Expanding the QL's address space beyond 1M (+primer on building QL compatible machines)
Posted: Tue Aug 17, 2021 8:51 pm
Here is a little topic which may prove useful. The idea is to explain how the QL's addressing capability has been expanded from the original 1Mbytes addressable, to more, as done by several expansion cards or alternative hardware and indeed emulators.
To start off, however, first a small re-cap on how the memory map was originally structured:
Traditionally, several well known address ranges were defined in the original literature:
$00000..$0BFFF 48k ROM
$0C000..$0FFFF 16k ROM slot
$10000..$1FFFF 64k on-board IO
$20000..$3FFFF 128k on-board RAM
$40000..$BFFFF 512k expansion RAM
$C0000..$FFFFF 256k expansion cards (ROM plus on-card IO)
Having a look at the hardware and OS code however gives us more details on how the usage of the areas was intended, given how it uses them:
$00000..$0FFFF is actually treated as a single 64k ROM area, which is decoded by a single active high signal from the 8301 ULA, ROMCS. This signal goes high whenever data is read from these addresses, but not when written. Writes here are ignored by the motherboard hardware. External hardware is expected to decode the appropriate internal ROMs or the ROM slot, which is why the original ROM chip set uses multiple chip selects to do this without requiring extra logic chips. The ROM slot hardware however must include some logic to decode address lines A14=1 and A15=1 and ROMCS=1, which completes the decode for the top 16k of the ROM space. This is also the reason why the entire ROM can be located in a ROM cartridge if the internal ROM chips are removed.
There is a special difference in how the OS treats the ROM slot area ($0C000..$0FFFF) and that is by looking for an add-on ROM flag ($4AFB0001) at the beginning of that area. This is required for any expansion ROM to be detected.
$10000..$1FFFF seem like a waste of space to address only the small amount of on-board IO addresses. There are really only 17 total locations used in there. While the hardware decodes this differently depending on the version of the 8301 ULA, the software only uses a very small portion of the total address space. All IO locations for all versions are enclosed within the 128 bytes at $18000..$18007F. Even then only the 17 addresses are used.
This resulted in a slightly different usage of the 64k area, like this:
$10000..$17FFF is normally not used in an unexpanded QL but the Minerva OS will also look for up to two 16k expansion ROMs, at address $10000 and $14000.
$18000..$1BFFF is designated as the on-board IO area. Still, only the aforementioned 17 addresses are used except if QIMI is fitted, in which the last 128 bytes are also used, as in old PTR_GEN will look for QIMI hardware there.
$1C000..$1FFFF is not specifically defined for any use.
The next area is RAM, which is, as far as the OS goes, treated as a single contiguous section of the address map. So, $20000 to $BFFFF, 640k in total, is designated as RAM, of which the initial 128k is expected to be populated on the motherboard.
Finally there is the expansion area from $C0000 to the end of address space, $FFFFF, which rounds out the total 1M of addressable by the 68008. This is expected to be structured as up to 16 slots of 16k each, containing ROMs which supply the necessary OS extensions to use the hardware on the expansion card. If any addresses are used to control this hardware, it is expected that they reside within the same 16k, which requires the software to always reference them relatively from the start address of the ROM. It should be noted that this is not an absolute requirement - addresses that control expansion hardware could in principle be located anywhere that is not already populated, say in the unused addresses of the on-board IO area or the undefined area at $1C000..$1FFFF.
One more thing that has to be mentioned is that the QL bus provides a signal that can be used to defeat any oy-motherboard decoding and replace motherboard hardware by other hardware on the bus. This can also be used to do other tricks, and in fact has been in the past.
Initial state, the bare QL
The bare QL motherboard completely disregards the top two address lines coming out of the CPU and the above mentioned mechanism is expected to take over in order to implement decoding of extra stuff like expansion RAM.
So, initially, the motherboard implements only the first 256k of the address map, which then repeats an additional 3 times to fill up the entire 1M address space, since address lines A18 and A19 are 'don't care', there are 4 states they can have (00, 01, 10, 11) and therefore 4 copies of the initial 256k.
Since the OS looks for RAM starting at $20000, once it hits $40000, it will actually address another copy of the ROM and not getting a proper readback of data it attempted to write, will stop looking for extra RAM, establishing the total RAM at the on-board 128k.
In a similar manner, the OS looks for expansion cards starting at $C0000, which is again at the start of a 256k boundary, so in a bare QL it will find the ROM there, and depending on ROM version may continue finding other parts of the ROM, IO and RAM. If I remember correctly, JM ROMs had a bug which only looked for the first expansion ROM at $C0000, which Toolkit 2 would fix. The proper sequence was ultimately built into Minerva. This was a happy circumstance because doe to the 4 copies of the forst 256k, looking for all possible expansion ROMs would end up looking at 16k boundaries in the forst 256k, eventually double linking the ROM slot (which would also appear at $CC000), and also having the obscure consequence that it could falsly detect a ROM if the clock seconds counter register happened to hold a value equal to the ROM flag, $4AFB0001, because looking at $D8000 for it would actually read the clock register.
The special case: TrumpCard
The first expansion to break the addressing convention was the TrumpCard, which added up to 768k of additional RAM instead of the previous maximum of 512k. It did this by using the 256k of expansion IO addresses for RAM. As it happens, the OS will normally continue to search for RAM until it finds an area that does not read back what is written, or the end of the CPU address range is encountered. If RAM is extended within the expansion IO area, the system will happily use it as such, though there are/were limits to this due to some interesting bugs. However without any addresses left for expansions, Miracle had to devise a way to implement what was standard at the time within the available pool of addresses and it cleverly used $10000..$17FFF for the ROM with extra software needed for Trumpcard features, and $1C000..$1FFFF for control locations for the extra hardware on it. To do it and get around OS limitations, it had to do a clever dance with relocating the ROM in order to get the system to see it in it's true address. As an aside, the original GoldCard would do a double initialization to achieve the same, but with different means. In any case, this provides us with the first clue on how the available addressing range was expanded using the original OS addressing framework, and more about that in the next post.
To start off, however, first a small re-cap on how the memory map was originally structured:
Traditionally, several well known address ranges were defined in the original literature:
$00000..$0BFFF 48k ROM
$0C000..$0FFFF 16k ROM slot
$10000..$1FFFF 64k on-board IO
$20000..$3FFFF 128k on-board RAM
$40000..$BFFFF 512k expansion RAM
$C0000..$FFFFF 256k expansion cards (ROM plus on-card IO)
Having a look at the hardware and OS code however gives us more details on how the usage of the areas was intended, given how it uses them:
$00000..$0FFFF is actually treated as a single 64k ROM area, which is decoded by a single active high signal from the 8301 ULA, ROMCS. This signal goes high whenever data is read from these addresses, but not when written. Writes here are ignored by the motherboard hardware. External hardware is expected to decode the appropriate internal ROMs or the ROM slot, which is why the original ROM chip set uses multiple chip selects to do this without requiring extra logic chips. The ROM slot hardware however must include some logic to decode address lines A14=1 and A15=1 and ROMCS=1, which completes the decode for the top 16k of the ROM space. This is also the reason why the entire ROM can be located in a ROM cartridge if the internal ROM chips are removed.
There is a special difference in how the OS treats the ROM slot area ($0C000..$0FFFF) and that is by looking for an add-on ROM flag ($4AFB0001) at the beginning of that area. This is required for any expansion ROM to be detected.
$10000..$1FFFF seem like a waste of space to address only the small amount of on-board IO addresses. There are really only 17 total locations used in there. While the hardware decodes this differently depending on the version of the 8301 ULA, the software only uses a very small portion of the total address space. All IO locations for all versions are enclosed within the 128 bytes at $18000..$18007F. Even then only the 17 addresses are used.
This resulted in a slightly different usage of the 64k area, like this:
$10000..$17FFF is normally not used in an unexpanded QL but the Minerva OS will also look for up to two 16k expansion ROMs, at address $10000 and $14000.
$18000..$1BFFF is designated as the on-board IO area. Still, only the aforementioned 17 addresses are used except if QIMI is fitted, in which the last 128 bytes are also used, as in old PTR_GEN will look for QIMI hardware there.
$1C000..$1FFFF is not specifically defined for any use.
The next area is RAM, which is, as far as the OS goes, treated as a single contiguous section of the address map. So, $20000 to $BFFFF, 640k in total, is designated as RAM, of which the initial 128k is expected to be populated on the motherboard.
Finally there is the expansion area from $C0000 to the end of address space, $FFFFF, which rounds out the total 1M of addressable by the 68008. This is expected to be structured as up to 16 slots of 16k each, containing ROMs which supply the necessary OS extensions to use the hardware on the expansion card. If any addresses are used to control this hardware, it is expected that they reside within the same 16k, which requires the software to always reference them relatively from the start address of the ROM. It should be noted that this is not an absolute requirement - addresses that control expansion hardware could in principle be located anywhere that is not already populated, say in the unused addresses of the on-board IO area or the undefined area at $1C000..$1FFFF.
One more thing that has to be mentioned is that the QL bus provides a signal that can be used to defeat any oy-motherboard decoding and replace motherboard hardware by other hardware on the bus. This can also be used to do other tricks, and in fact has been in the past.
Initial state, the bare QL
The bare QL motherboard completely disregards the top two address lines coming out of the CPU and the above mentioned mechanism is expected to take over in order to implement decoding of extra stuff like expansion RAM.
So, initially, the motherboard implements only the first 256k of the address map, which then repeats an additional 3 times to fill up the entire 1M address space, since address lines A18 and A19 are 'don't care', there are 4 states they can have (00, 01, 10, 11) and therefore 4 copies of the initial 256k.
Since the OS looks for RAM starting at $20000, once it hits $40000, it will actually address another copy of the ROM and not getting a proper readback of data it attempted to write, will stop looking for extra RAM, establishing the total RAM at the on-board 128k.
In a similar manner, the OS looks for expansion cards starting at $C0000, which is again at the start of a 256k boundary, so in a bare QL it will find the ROM there, and depending on ROM version may continue finding other parts of the ROM, IO and RAM. If I remember correctly, JM ROMs had a bug which only looked for the first expansion ROM at $C0000, which Toolkit 2 would fix. The proper sequence was ultimately built into Minerva. This was a happy circumstance because doe to the 4 copies of the forst 256k, looking for all possible expansion ROMs would end up looking at 16k boundaries in the forst 256k, eventually double linking the ROM slot (which would also appear at $CC000), and also having the obscure consequence that it could falsly detect a ROM if the clock seconds counter register happened to hold a value equal to the ROM flag, $4AFB0001, because looking at $D8000 for it would actually read the clock register.
The special case: TrumpCard
The first expansion to break the addressing convention was the TrumpCard, which added up to 768k of additional RAM instead of the previous maximum of 512k. It did this by using the 256k of expansion IO addresses for RAM. As it happens, the OS will normally continue to search for RAM until it finds an area that does not read back what is written, or the end of the CPU address range is encountered. If RAM is extended within the expansion IO area, the system will happily use it as such, though there are/were limits to this due to some interesting bugs. However without any addresses left for expansions, Miracle had to devise a way to implement what was standard at the time within the available pool of addresses and it cleverly used $10000..$17FFF for the ROM with extra software needed for Trumpcard features, and $1C000..$1FFFF for control locations for the extra hardware on it. To do it and get around OS limitations, it had to do a clever dance with relocating the ROM in order to get the system to see it in it's true address. As an aside, the original GoldCard would do a double initialization to achieve the same, but with different means. In any case, this provides us with the first clue on how the available addressing range was expanded using the original OS addressing framework, and more about that in the next post.