123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 |
- VRAM Addressing
- ===============
- This document describes a model for the way the V9938 VDP calculates
- addresses in VRAM using base registers and indices.
- General Formula
- ---------------
- General formula for VRAM addressing:
- vram_address = index1 & mask
- where:
- vram_address is the index in VRAM
- index1 is the index in the particular table, such as the name table or
- color table. The unused bits of index are all ones.
- mask is the value in the table's base register. The unused bits of mask
- are all ones.
- Examples:
- The name table base register is VDP R#2.
- It contains A16-A10 of the mask, the other bits are 1.
- If R#2 would contain 24h, the mask would be:
- (24h << 10) | ~(-1 << 10) = 093FFh
- In SCREEN1 (Graphic 1) there are usually 24 lines of 32 characters each.
- However, the actual screen height is 32 lines, as can be seen when the
- vertical adjust register R#23 is used. So the number of characters per
- screen is 32*32 = 1024 = 2^10. With one byte per character, the index is
- 10 bits wide.
- If the index would be 157h, this is extended with leading ones for A10 and
- higher (1FC00h) to become 1FD57h. Then the final address is:
- vram_address = 1FD57h & 093FFh = 09157h
- Note that in this case, the final address is equal to:
- R#2 * 2^10 + index = 09000h + 157h = 09157h
- This is true for all display modes in which the used bits in the base
- register (A16..A10 in this case) do not overlap the bits in the index
- (A9..A0 in this case). This is also the way application programmers
- typically use the table base address registers.
- In SCREEN5 (Graphic 4) there are 256 lines of 128 bytes each. Only 212 lines
- are visible at a time, but using the vertical adjust register R#23 all 256
- lines can be seen. A line is 256 pixels wide, with 4 bits per pixel that
- makes 128 bytes per line. So the total number of bytes per screen is
- 256 * 128 = 32768 = 2^15. Therefore the index is 15 bits wide.
- If the index would be 4521h, this is extended with leading ones for A16 and
- A15 (18000h) to become 1C521h. Using the same mask as before (093FFh), the
- final address is:
- vram_address = 1C521h & 093FFh = 08121h
- Note that in this case, the final address is NOT equal to:
- R#2 * 2^10 + index = 09000h + 4521h = 0D521h
- The VDP data book states that in SCREEN5 bit4 to bit0 should be 1, in those
- cases the general formula is equivalent to addition. But when some of those
- bits are zeroes, the general formula gives the same result the real VDP
- does, while addition does not. The many-scrollers part of Anma's "Source of
- Power" demo uses this feature to display the same scrolling text many times
- on the same screen.
- Range Checking
- --------------
- Different VDP subsystems are interested in different tables. For example,
- the sprite subsystem is only interested in the sprite name and pattern
- tables. On reads and writes to tables, interested subsystems must be
- informed so they can update their internal state. Therefore an algorithm is
- needed which calculates in which tables (if any) a given address is located.
- Here is the address calculation formula again:
- vram_address = index1 & mask
- The lowest address within the range is when the index is 0, index1 then
- contains only ones in unused bits. The highest address (inclusive) is when
- the index is all ones, then index1 is also all ones and the address is equal
- to the mask. However, not all addresses between the lowest and the highest
- address actually occur if the mask has one or more zeroes in the bits that
- overlap with the index.
- Let "inside : Int -> Bool" be the function that tells whether an address is
- inside a table:
- inside.addr = (Exist i : i in Indices : i & mask = addr)
- In English: there is an index i, for which the VRAM address is addr.
- A convenient formula for the inside function is this one:
- inside.addr =
- addr & (~mask | (-1 << index_width)) = mask & (-1 << index_width)
- Note that "addr" only occurs once and all other values are constant for each
- display mode, so the check takes two operations (one compare and one AND).
- This is probably the cheapest you can get.
- Index Width
- -----------
- Which address bits are stored in the table base address registers is
- documented in the VDP data book, but which address bits are produced by the
- index is not documented there. This section fills that gap.
- - Name Table -
- Name table base address is [A16..A10] (R#2).
- Display mode: Index width in bits:
- Text 1 12 (*1)
- Text 2 12
- Multicolor 10 (*2)
- Graphic 1 10 (*2)
- Graphic 2 10 (*2)
- Graphic 3 10 (*2)
- Graphic 4 15
- Graphic 5 15
- Graphic 6 15 + plane (*3)
- Graphic 7 15 + plane (*3)
- (*1) Text 1 index is calculated as 00C00h + position.
- On MSX1 it was 10 bits wide. For some reason, unifying address
- calculation with Text 2 must have been simpler than keeping it 10 bits
- wide, even though unification requires an add operation.
- (*2) Maybe this is 12 bits and index = 00C00h + position just like Text 1.
- That's equivalent to 10 bits though, because there are 1024 characters
- per screen.
- (*3) See "Planar Addressing" section for details.
- - Pattern Table -
- Pattern table base address is [A16..A11] (R#4).
- Display mode: Index width in bits:
- Text 1 11
- Text 2 11
- Multicolor 11
- Graphic 1 11
- Graphic 2 13
- Graphic 3 13
- Graphic 4 -
- Graphic 5 -
- Graphic 6 -
- Graphic 7 -
- - Color Table -
- Color table base address is [A16..A6] (R#3 and R#10).
- Display mode: Index width in bits:
- Text 1 -
- Text 2 9
- Multicolor -
- Graphic 1 6 (*1)
- Graphic 2 13
- Graphic 3 13
- Graphic 4 -
- Graphic 5 -
- Graphic 6 -
- Graphic 7 -
- (*1) Bit5 is fixed to zero.
- - Sprite Attribute Table -
- Sprite mode: Index width in bits:
- mode 1 7
- mode 2 10 (*1)
- (*1) Bit9 selects sprite color table (0) or sprite attribute table (1).
- Bit8 and Bit7 are fixed to zero for the attribute table.
- - Sprite Pattern Table -
- Sprite mode: Index width in bits:
- mode 1 11
- mode 2 11
- Index Calculation
- -----------------
- The following variables occur in every display mode:
- org_line: display line,
- 0 <= org_line < 256
- line: org_line after scroll adjustment (R#23 is applied),
- 0 <= line < 256
- name: value in VRAM at address (name_index & name_mask),
- 0 <= name < 256
- The "/" operation is integer division ("div").
- The "%" operation is modulo/remainder ("mod").
- - Text 1 -
- Aka SCREEN0, WIDTH40.
- 0 <= x < 40
- y = org_line / 8
- name_index = 00C00h + y * 40 + x
- pattern_index = name * 8 + line % 8
- Adding 00C00h was probably done to make the index 12 bits wide, just like
- in Text 2 mode. You can see this addition in action if you enable 212 lines
- display and then play with different values of R#2. Pay attention to the
- characters displayed in the lower few lines of the screen.
- - Text 2 -
- Aka SCREEN0, WIDTH80.
- 0 <= x < 80
- y = org_line / 8
- name_index = y * 80 + x
- pattern_index = name * 8 + line % 8
- - Multicolor -
- Aka SCREEN3.
- 0 <= x < 32
- y = line / 8
- name_index = y * 32 + x
- pattern_index = name * 8 + line % 4
- - Graphic 1 -
- Aka SCREEN1.
- 0 <= x < 32
- y = line / 8
- name_index = y * 32 + x
- pattern_index = name * 8 + line % 8
- color_index = name / 8
- - Graphic 2 and 3 -
- These modes are equivalent, except for the sprite mode.
- Aka SCREEN2 and SCREEN4.
- 0 <= x < 32
- y = line / 8
- name_index = y * 32 + x
- pattern_index = ((line / 64) * 256 + name) * 8 + line % 8
- color_index = ((line / 64) * 256 + name) * 8 + line % 8
- - Graphic 4 -
- 0 <= x < 256
- name_index = line * 128 + x / 2
- - Graphic 5 -
- 0 <= x < 512
- name_index = line * 128 + x / 4
- - Graphic 6 -
- 0 <= x < 512
- name_index = line * 128 + x / 4
- plane = (x / 2) % 2
- - Graphic 7 -
- 0 <= x < 256
- name_index = line * 128 + x / 2
- plane = x % 2
- - Sprite Mode 1 -
- 0 <= sprite_nr < 32
- attrib_index = sprite_nr * 4
- 0 <= pattern_nr < 256
- pattern_index = pattern_nr * 8
- When sprite size is 16x16, Bit1 and Bit0 of the index are ignored.
- Four patterns define the look of a single sprite, like this:
- 0 2
- 1 3
- - Sprite Mode 2 -
- 0 <= sprite_nr < 32
- attrib_index = 512 + sprite_nr * 4
- color_index = sprite_nr * 16
- 0 <= pattern_nr < 256
- pattern_index = pattern_nr * 8
- When sprite size is 16x16, Bit1 and Bit0 of the index are ignored.
- Four patterns define the look of a single sprite, like this:
- 0 2
- 1 3
- Planar Addressing
- -----------------
- The V9938 has 3 pins that select a group of VRAM ICs each: CAS0, CAS1 and CASX.
- CASX is used to select the expansion RAM, which can be accessed through the CPU
- interface and through commands, but cannot be displayed directly.
- Each VRAM group can be 0K, 16K or 64K. A group can consist of 8 chips of 1-bit
- wide RAM or 2 chips of 4-bit wide RAM, but for the purpose of addressing we can
- just pretend it is 1 chip with 8-bit wide RAM.
- In real MSX machines, the following group combinations are used:
- CAS0: CAS1: CASX: Description:
- 16K 0K 0K MSX1 with V9938 and 16K VRAM (*1)
- 64K 0K 0K MSX2 with 64K VRAM (*2)
- 64K 64K 0K MSX2 with 128K VRAM
- 64K 64K 64K MSX2 with 128K VRAM and 64K expansion (*3)
- *1: Examples are the Spectravideo SVI-738 X'PRESS and the Yamaha CX5MII/128.
- The only V9938-specific display mode that they can fully display is
- 80-columns text mode (TEXT2).
- *2: Known 64K VRAM machines: Canon V-25, Hitachi MB-H3, Talent TPP-311 and 312,
- Toshiba HX-23.
- *3: No known machines shipped with expansion VRAM. It was a popular amateur
- upgrade for users of FastCopy, to reduce the number of required disk swaps.
- In most display modes addressing is linear, meaning the lower 64K of address
- space uses the VRAM connected to CAS0 and the upper 64K of address space uses
- the VRAM connected to CAS1.
- In SCREEN7/8 (Graphic 6/7) the addressing is planar, meaning that even VRAM
- addresses are mapped to CAS0 and odd addresses are mapped to CAS1. This means
- that SCREEN 7/8 are only available on 128K VRAM machines. The most likely
- reason for using planar addressing is getting a higher bandwidth: even though
- the data lines are shared between all VRAM groups, the row select has to be
- done only once.
- VR=0 / VR=1 mode
- ----------------
- The VDP chip has 10 pins that are connected to the VRAM subsystem to control
- the read/write address. There are 8 address pins, these select the CAS/RAS
- address. And there is a CAS0 and CAS1 pin, these select one of two ram chip
- (groups). In total this allows for a 17-bit address space (=128kB). (I'm
- ignoring the extended VRAM here).
- When the VDP does a memory access, it's puts the 17-bit logical address on
- these pins. The way how this is done depends on the VR bit (bit 3 of register
- 8), see table below.
- In VR=1 mode, it first puts the bits A15-A8 on the address bus, followed by the
- bits A7-A0. The whole time either pin CAS0 or CAS1 is selected, depending on
- address bit A16. In VR=0 mode, .. well see table ;)
- VDP | VR=0 | VR=1 |
- address|----------------------|----------------------|
- pin | RAS(high) | CAS(low) | RAS(high) | CAS(low) |
- -------+-----------+----------|-----------+----------|
- AD0 | A6 #2| 1 #1| A8 | A0 |
- AD1 | A7 | A0 | A9 | A1 |
- AD2 | A8 | A1 | A10 | A2 |
- AD3 | A9 | A2 | A11 | A3 |
- AD4 | A10 | A3 | A12 | A4 |
- AD5 | A11 | A4 | A13 | A5 |
- AD6 | A12 | A5 | A14 | A6 |
- AD7 | A13 | A6 #2| A15 | A7 |
- -------+-----------+----------|-----------+----------|
- CAS0/1 | A14 | A16 |
- #1 this pin is always '1' in the CAS phase
- #2 A6 is output both in CAS and RAS phase, this allows to use both 8/6 and 7/7
- RAS/CAS configurations (see VDP data sheet for details)
- One important side-effect is that in VR=0 mode, the VDP can only address 32kB
- memory. This is what makes the demo 'syntax infinity' work even though R14 is
- programmed 'wrongly' (it writes to address 0x1????, but expects the data to end
- up at address 0x0????).
- Another side effect is that when switching between VR=0 and VR=1 modes, the
- address space gets shuffled around, much like bit 7 in VDP register 1 for
- TMS99x8 chips.
|