Port-based memory addressing
Usually, memory is accessed by putting a value on the address bus, triggering the read cycle, and reading the value from the data bus. This is called direct memory addressing, and without any "tricks" like banking, this allows to access 2^(#address lines) = 65536 bytes for a 16-bit address bus. Within a 16 bit system, the least-significant bit of the address is usually unused as memory is organized as words (in the width of the data bus), so this would be 32 KiW (32768 words). However, as already said, the external devices of the TI computer system are connected by a 16/8 bit converter, turning a 16 bit word into two consecutive 8 bit words, so we usually talk about addressible bytes.
Direct addressing is indispendible for highest possible performance. However, in the base configuration of the TI, only BASIC programming was available, while games or other applications came on cartridges. On the other side, the TI computer system uses a video display processor (VDP), the TMS9918A (or 9928A for European models) with its own video memory of 16 KiB size.
The video memory was chosen to allow for the maximum resolution mode. Within BASIC, this mode was deemed uninteresting for simple applications (the way you expect BASIC programs to be), so the high resolution modes are unaccessible, and a lot of memory become available. This memory is reachable via the video processor, using ports in the directly accessible address space.
Note that hexadecimal notation in the TI world is indicated by ">". That is, >1234 is 0x1234.
|Set address||Set video register||Read address||Write byte||Read byte||Read video status|
As the video memory is limited to 16 KiB (ranging from address >0000 to >3fff), there is room for two flag bits in the address. Setting the address to >4000 and higher indicated to the VDP that data is about to be written to the corresponding address, masking away the >4000 (otherwise, data shall be read). Setting an address with the most-significant bit set (>8000 and higher) indicated that a VDP register shall be set.
In order to write a byte to VDP memory, you have to first set the address on the address port. This must be performed by first writing the least significant byte (odd position), then the most significant byte (even position). After that, a short delay must be respected. Now each byte that is writted to the data port will be written to VDP memory in consecutive locations. Example:
BYTES BYTE 'H','E','L','L','O','!' EXPL LI R0,12*32+13 Address (middle of the 32*24 text screen) SWPB R0 swap bytes MOVB R0,@>8C02 set address (lower part) SWPB R0 swap bytes ORI R0,>4000 set write flag MOVB R0,@>8C02 set address (higher part) NOP wait LI R1,BYTES load pointer to bytes LI R2,6 counter WRLP MOVB *R1+,@>8C00 write to VDP, increase pointer DEC R2 decrease counter JNE WRLP if not zero, jump to WRLP
Notice that you cannot read the current VDP address pointer. This may cause quite some inconvenience when programming with interrupts. Therefore you must inhibit interrupts to occur during VDP accesses.
A similar system is the GROM memory system in the console and in cartridges. GROMs are special devices with a small package (16 pin) and a comparatively large memory of 6 KiB. GROMs were exclusively licensed by Texas Instruments. Third-party producers usually had no chance to use GROMs for their own cartridges.
This was not a big problem, for TI originally allowed third-party manufacturers to build modules with direct memory (ROM) only. The console had a routine to query the module pointer table within GROM or within ROM. Later consoles (close to TI's market withdrawal) had an updated ROM which disabled the table search in ROM, allowing only cartridges with GROM to be mounted, so effectively excluding all third-party vendors.
GROMs have an address space of 64KiB, with some limitation: As each GROM only contains 6 KiB, the addresses at offset >1800 to >1fff mirror the region from >0800 to >0fff. That is, the GROM address space can at most hold 48 KiB.
Accessing the GROM address space is done similarly to video memory access, with the exception that here the most-significant byte is set first. Also, no read/write flags have to be set.
|Set address||Read address||Write byte||Read byte|
BUFFER DATA 0,0,0,0 Buffer of 8 bytes EXPL LI R0,>6000 Address (beginning of cartridge space) MOVB R0,@>9C02 set address (higher part) SWPB R0 swap bytes MOVB R0,@>9C02 set address (lower part) NOP wait LI R1,BUFFER load pointer to buffer LI R2,8 counter WRLP MOVB @>9800,*R1+ read from GROM to buffer, increase pointer DEC R2 decrease counter JNE WRLP if not zero, jump to WRLP