Port-based memory addressing

From Ninerpedia
Jump to navigation Jump to search

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.

Video memory

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
>8C02 >8C02 n/a >8C00 >8800 >8802

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.

GROM access

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
>9C02 >9802 >9C00 >9800
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