Skip to content

Investigation: The Apple IIgs Memory Manager

Thomas Harte edited this page Feb 21, 2021 · 34 revisions

As of the first draft of this document, all parts are present for Apple IIgs emulation but bugs remain. At least one of those manifests as the in-ROM memory manager not responding as expected; in particular:

  • titles including Airball, Bouncing Bluster and Crystal Quest meet an unexpected BRK during startup, somewhere in the 00:BB or 00:BC page, dropping the user into the monitor; and
  • titles such as Defender of the Crown, Block Out and Bubble Ghost stop completely on an error of 'Unable to reserve memory segment $0201'.

The second set would appear overtly to be memory manager related; the former also seems to be.

Unexpected BRK

Digging down on Airball, the immediate progenitor of the error is this routine in the ROM's MoveUp (from mm.asm):

lda Count+2
adc Dest+2	;A = 00bank
xba	;A = bank00
ora #$44	;MVP opcode
pha
bra muFirst

i.e. the ROM is intending to assemble the sequence MVP, RTS on the stack, to which it later jumps. An ORA is used because a single-byte load isn't available. In my emulator the value at Dest+2 originates from whatever the contents of RAM were at power up rather than necessarily being 00 and as a result the generated opcode isn't MVP and the on-stack sequence that is jumped to runs off the rails.

Specifically the value at Dest+2 at that point in the code is 7f15.

Origin of the 7f15

By the time MoveUp is reached, the value is in Dest+2 (i.e. 00bcb1 and 00bcb2).

(Aside on cycle times below: they are relative to the 14Mhz bus, and depend on things like the exact disk rotation speeds I currently have, so should not be expected necessarily to be accurate in the future)

It was placed there by a STY at fc0d5f, 502081910 cycles since the machine launched. That immediately follows a load from 00bcc5/6.

The value was put into 00bcc5/6 by a STY at fc0d5f, 502078270 cycles since the machine launched. The value had been picked up by a LDY [$21] at fc0cb2, which read the value from 00bcc9/a.

It had been placed into 00bcc9/a by a STA at fc0fcb, 502077180 cycles since the machine launched. It picked the value up from from a LDA [$13],y with y=2 at fc0fc9, which read the value from e11806/7.

In context, that is likely to be the upper word of the block of memory pointed to by a memory handle; see précis on memory manager handles below.

The value was written to e11806/7 by a STA [$17],y at fc0dac with y=2, 497930810 cycles since machine launch. Its origin then was one instruction earlier, a LDA $03,x with x=2 and d=$bca4 at fc0daa, which accessed bca9/a.

7f15 had been stored to bca9/a at fc0da4 by a STA [$05], 497929740 cycles since machine launch, having been read from 00bcc1/2 one instruction earlier by a LDA [$1d].

It had been stored there by a STY [$1d] at fc0d5f, 497928970 cycles since machine launch, having been fetched by a LDY [$21] at fc0cb2, 497928450 cycles since machine launched, which fetched from 00bcc5/6.

That value was left by a STA [$21] at fc0fcb, 497927880 cycles since machine launch. It was fetched one instruction earlier by a LDA [$13], y with y=2, and d=$bca4 yielding a base for the long address of bcb7 which gave e119a8; that plus y leads to a two-byte fetch from e119aa/b.

Digging down on e119ab, logging reads and writes to it since launch until its relevant access at 497927875 cycles since launch shows:

e119ab <- 00 [15775430]
e119ab -> 00 [141578780]
e119ab -> 00 [141579715]
e119ab <- 00 [469272795]
e119ab -> 00 [469274435]
e119ab -> 00 [469274490]
e119ab -> 00 [473880845]
e119ab -> 00 [473881875]
e119ab -> 00 [473920325]
e119ab <- 00 [480022440]
e119ab <- 7f [495809345]
e119ab -> 7f [495811040]
e119ab -> 7f [495811095]
e119ab -> 7f [495820535]
e119ab -> 7f [495820590]
e119ab -> 7f [497927875]

Therefore: it looks like the handle map is properly cleared at startup, which at least takes guesses around detecting a cold start and with loop lengths off the table.

Digging down on cycle 495809345 after machine launch returns us to fc0dac, the same location that wrote to e11806/7 above; that instruction begins 495809310 cycles after machine launch.

This time the LDA $03,x at fc0daa with x=2 has d=$bc4a and therefore loads from 00bc4f and 00bc50.

The store at fc0da4 this time stores a value previously loaded from bc67.

bc67 was stored to by fc0d5f at 495807470, with the offending 7f15 having been picked up from bc6b/c by the LDY [$21] at fc0cb2 which read from bc6b. That instruction began at 495806950.

The fc0fc9/fc0fcb load and store which stored 7f15 to bc6b/bc6c occured from time 495806380, and specifically the LDA [$13], y at fc0fc9 occurred with d=$bc4a and y=2 so read from bc5d, bc5e and bc5f, which contained $755b4a. Which is not in the memory manager handle set.

I don't currently believe that 755b4c has been initialised.

Memory Manager Notes

The memory manager uses the region between e11700 and e11aff for a list of handles. Each handle is 20 bytes long and has the format:

  • 0–3: address of held memory;
  • 4–5: attributes;
  • 6–7: segment owner ID;
  • 8–11: size of held memory;
  • 12–15: pointer to next;
  • 16–19: pointer to previous.

e119aa/b is 682 bytes into the full list of handles; it is therefore the high two bytes of the first field — the address of the memory held — of the 34th handle.