| e8555c89 Andy McCall |
1 |
VRAM Addressing |
| 2 |
=============== |
| 3 |
|
| 4 |
This document describes a model for the way the V9938 VDP calculates |
| 5 |
addresses in VRAM using base registers and indices. |
| 6 |
|
| 7 |
General Formula |
| 8 |
--------------- |
| 9 |
|
| 10 |
General formula for VRAM addressing: |
| 11 |
vram_address = index1 & mask |
| 12 |
where: |
| 13 |
vram_address is the index in VRAM |
| 14 |
index1 is the index in the particular table, such as the name table or |
| 15 |
color table. The unused bits of index are all ones. |
| 16 |
mask is the value in the table's base register. The unused bits of mask |
| 17 |
are all ones. |
| 18 |
|
| 19 |
Examples: |
| 20 |
|
| 21 |
The name table base register is VDP R#2. |
| 22 |
It contains A16-A10 of the mask, the other bits are 1. |
| 23 |
If R#2 would contain 24h, the mask would be: |
| 24 |
(24h << 10) | ~(-1 << 10) = 093FFh |
| 25 |
|
| 26 |
In SCREEN1 (Graphic 1) there are usually 24 lines of 32 characters each. |
| 27 |
However, the actual screen height is 32 lines, as can be seen when the |
| 28 |
vertical adjust register R#23 is used. So the number of characters per |
| 29 |
screen is 32*32 = 1024 = 2^10. With one byte per character, the index is |
| 30 |
10 bits wide. |
| 31 |
|
| 32 |
If the index would be 157h, this is extended with leading ones for A10 and |
| 33 |
higher (1FC00h) to become 1FD57h. Then the final address is: |
| 34 |
vram_address = 1FD57h & 093FFh = 09157h |
| 35 |
|
| 36 |
Note that in this case, the final address is equal to: |
| 37 |
R#2 * 2^10 + index = 09000h + 157h = 09157h |
| 38 |
This is true for all display modes in which the used bits in the base |
| 39 |
register (A16..A10 in this case) do not overlap the bits in the index |
| 40 |
(A9..A0 in this case). This is also the way application programmers |
| 41 |
typically use the table base address registers. |
| 42 |
|
| 43 |
In SCREEN5 (Graphic 4) there are 256 lines of 128 bytes each. Only 212 lines |
| 44 |
are visible at a time, but using the vertical adjust register R#23 all 256 |
| 45 |
lines can be seen. A line is 256 pixels wide, with 4 bits per pixel that |
| 46 |
makes 128 bytes per line. So the total number of bytes per screen is |
| 47 |
256 * 128 = 32768 = 2^15. Therefore the index is 15 bits wide. |
| 48 |
|
| 49 |
If the index would be 4521h, this is extended with leading ones for A16 and |
| 50 |
A15 (18000h) to become 1C521h. Using the same mask as before (093FFh), the |
| 51 |
final address is: |
| 52 |
vram_address = 1C521h & 093FFh = 08121h |
| 53 |
|
| 54 |
Note that in this case, the final address is NOT equal to: |
| 55 |
R#2 * 2^10 + index = 09000h + 4521h = 0D521h |
| 56 |
The VDP data book states that in SCREEN5 bit4 to bit0 should be 1, in those |
| 57 |
cases the general formula is equivalent to addition. But when some of those |
| 58 |
bits are zeroes, the general formula gives the same result the real VDP |
| 59 |
does, while addition does not. The many-scrollers part of Anma's "Source of |
| 60 |
Power" demo uses this feature to display the same scrolling text many times |
| 61 |
on the same screen. |
| 62 |
|
| 63 |
Range Checking |
| 64 |
-------------- |
| 65 |
|
| 66 |
Different VDP subsystems are interested in different tables. For example, |
| 67 |
the sprite subsystem is only interested in the sprite name and pattern |
| 68 |
tables. On reads and writes to tables, interested subsystems must be |
| 69 |
informed so they can update their internal state. Therefore an algorithm is |
| 70 |
needed which calculates in which tables (if any) a given address is located. |
| 71 |
|
| 72 |
Here is the address calculation formula again: |
| 73 |
vram_address = index1 & mask |
| 74 |
The lowest address within the range is when the index is 0, index1 then |
| 75 |
contains only ones in unused bits. The highest address (inclusive) is when |
| 76 |
the index is all ones, then index1 is also all ones and the address is equal |
| 77 |
to the mask. However, not all addresses between the lowest and the highest |
| 78 |
address actually occur if the mask has one or more zeroes in the bits that |
| 79 |
overlap with the index. |
| 80 |
|
| 81 |
Let "inside : Int -> Bool" be the function that tells whether an address is |
| 82 |
inside a table: |
| 83 |
inside.addr = (Exist i : i in Indices : i & mask = addr) |
| 84 |
In English: there is an index i, for which the VRAM address is addr. |
| 85 |
|
| 86 |
A convenient formula for the inside function is this one: |
| 87 |
inside.addr = |
| 88 |
addr & (~mask | (-1 << index_width)) = mask & (-1 << index_width) |
| 89 |
Note that "addr" only occurs once and all other values are constant for each |
| 90 |
display mode, so the check takes two operations (one compare and one AND). |
| 91 |
This is probably the cheapest you can get. |
| 92 |
|
| 93 |
Index Width |
| 94 |
----------- |
| 95 |
|
| 96 |
Which address bits are stored in the table base address registers is |
| 97 |
documented in the VDP data book, but which address bits are produced by the |
| 98 |
index is not documented there. This section fills that gap. |
| 99 |
|
| 100 |
- Name Table - |
| 101 |
|
| 102 |
Name table base address is [A16..A10] (R#2). |
| 103 |
|
| 104 |
Display mode: Index width in bits: |
| 105 |
Text 1 12 (*1) |
| 106 |
Text 2 12 |
| 107 |
Multicolor 10 (*2) |
| 108 |
Graphic 1 10 (*2) |
| 109 |
Graphic 2 10 (*2) |
| 110 |
Graphic 3 10 (*2) |
| 111 |
Graphic 4 15 |
| 112 |
Graphic 5 15 |
| 113 |
Graphic 6 15 + plane (*3) |
| 114 |
Graphic 7 15 + plane (*3) |
| 115 |
(*1) Text 1 index is calculated as 00C00h + position. |
| 116 |
On MSX1 it was 10 bits wide. For some reason, unifying address |
| 117 |
calculation with Text 2 must have been simpler than keeping it 10 bits |
| 118 |
wide, even though unification requires an add operation. |
| 119 |
(*2) Maybe this is 12 bits and index = 00C00h + position just like Text 1. |
| 120 |
That's equivalent to 10 bits though, because there are 1024 characters |
| 121 |
per screen. |
| 122 |
(*3) See "Planar Addressing" section for details. |
| 123 |
|
| 124 |
- Pattern Table - |
| 125 |
|
| 126 |
Pattern table base address is [A16..A11] (R#4). |
| 127 |
|
| 128 |
Display mode: Index width in bits: |
| 129 |
Text 1 11 |
| 130 |
Text 2 11 |
| 131 |
Multicolor 11 |
| 132 |
Graphic 1 11 |
| 133 |
Graphic 2 13 |
| 134 |
Graphic 3 13 |
| 135 |
Graphic 4 - |
| 136 |
Graphic 5 - |
| 137 |
Graphic 6 - |
| 138 |
Graphic 7 - |
| 139 |
|
| 140 |
- Color Table - |
| 141 |
|
| 142 |
Color table base address is [A16..A6] (R#3 and R#10). |
| 143 |
|
| 144 |
Display mode: Index width in bits: |
| 145 |
Text 1 - |
| 146 |
Text 2 9 |
| 147 |
Multicolor - |
| 148 |
Graphic 1 6 (*1) |
| 149 |
Graphic 2 13 |
| 150 |
Graphic 3 13 |
| 151 |
Graphic 4 - |
| 152 |
Graphic 5 - |
| 153 |
Graphic 6 - |
| 154 |
Graphic 7 - |
| 155 |
(*1) Bit5 is fixed to zero. |
| 156 |
|
| 157 |
- Sprite Attribute Table - |
| 158 |
|
| 159 |
Sprite mode: Index width in bits: |
| 160 |
mode 1 7 |
| 161 |
mode 2 10 (*1) |
| 162 |
(*1) Bit9 selects sprite color table (0) or sprite attribute table (1). |
| 163 |
Bit8 and Bit7 are fixed to zero for the attribute table. |
| 164 |
|
| 165 |
- Sprite Pattern Table - |
| 166 |
|
| 167 |
Sprite mode: Index width in bits: |
| 168 |
mode 1 11 |
| 169 |
mode 2 11 |
| 170 |
|
| 171 |
Index Calculation |
| 172 |
----------------- |
| 173 |
|
| 174 |
The following variables occur in every display mode: |
| 175 |
org_line: display line, |
| 176 |
0 <= org_line < 256 |
| 177 |
line: org_line after scroll adjustment (R#23 is applied), |
| 178 |
0 <= line < 256 |
| 179 |
name: value in VRAM at address (name_index & name_mask), |
| 180 |
0 <= name < 256 |
| 181 |
|
| 182 |
The "/" operation is integer division ("div"). |
| 183 |
The "%" operation is modulo/remainder ("mod"). |
| 184 |
|
| 185 |
- Text 1 - |
| 186 |
|
| 187 |
Aka SCREEN0, WIDTH40. |
| 188 |
0 <= x < 40 |
| 189 |
y = org_line / 8 |
| 190 |
name_index = 00C00h + y * 40 + x |
| 191 |
pattern_index = name * 8 + line % 8 |
| 192 |
|
| 193 |
Adding 00C00h was probably done to make the index 12 bits wide, just like |
| 194 |
in Text 2 mode. You can see this addition in action if you enable 212 lines |
| 195 |
display and then play with different values of R#2. Pay attention to the |
| 196 |
characters displayed in the lower few lines of the screen. |
| 197 |
|
| 198 |
- Text 2 - |
| 199 |
|
| 200 |
Aka SCREEN0, WIDTH80. |
| 201 |
0 <= x < 80 |
| 202 |
y = org_line / 8 |
| 203 |
name_index = y * 80 + x |
| 204 |
pattern_index = name * 8 + line % 8 |
| 205 |
|
| 206 |
- Multicolor - |
| 207 |
|
| 208 |
Aka SCREEN3. |
| 209 |
0 <= x < 32 |
| 210 |
y = line / 8 |
| 211 |
name_index = y * 32 + x |
| 212 |
pattern_index = name * 8 + line % 4 |
| 213 |
|
| 214 |
- Graphic 1 - |
| 215 |
|
| 216 |
Aka SCREEN1. |
| 217 |
0 <= x < 32 |
| 218 |
y = line / 8 |
| 219 |
name_index = y * 32 + x |
| 220 |
pattern_index = name * 8 + line % 8 |
| 221 |
color_index = name / 8 |
| 222 |
|
| 223 |
- Graphic 2 and 3 - |
| 224 |
|
| 225 |
These modes are equivalent, except for the sprite mode. |
| 226 |
Aka SCREEN2 and SCREEN4. |
| 227 |
0 <= x < 32 |
| 228 |
y = line / 8 |
| 229 |
name_index = y * 32 + x |
| 230 |
pattern_index = ((line / 64) * 256 + name) * 8 + line % 8 |
| 231 |
color_index = ((line / 64) * 256 + name) * 8 + line % 8 |
| 232 |
|
| 233 |
- Graphic 4 - |
| 234 |
|
| 235 |
0 <= x < 256 |
| 236 |
name_index = line * 128 + x / 2 |
| 237 |
|
| 238 |
- Graphic 5 - |
| 239 |
|
| 240 |
0 <= x < 512 |
| 241 |
name_index = line * 128 + x / 4 |
| 242 |
|
| 243 |
- Graphic 6 - |
| 244 |
|
| 245 |
0 <= x < 512 |
| 246 |
name_index = line * 128 + x / 4 |
| 247 |
plane = (x / 2) % 2 |
| 248 |
|
| 249 |
- Graphic 7 - |
| 250 |
|
| 251 |
0 <= x < 256 |
| 252 |
name_index = line * 128 + x / 2 |
| 253 |
plane = x % 2 |
| 254 |
|
| 255 |
- Sprite Mode 1 - |
| 256 |
|
| 257 |
0 <= sprite_nr < 32 |
| 258 |
attrib_index = sprite_nr * 4 |
| 259 |
|
| 260 |
0 <= pattern_nr < 256 |
| 261 |
pattern_index = pattern_nr * 8 |
| 262 |
|
| 263 |
When sprite size is 16x16, Bit1 and Bit0 of the index are ignored. |
| 264 |
Four patterns define the look of a single sprite, like this: |
| 265 |
0 2 |
| 266 |
1 3 |
| 267 |
|
| 268 |
- Sprite Mode 2 - |
| 269 |
|
| 270 |
0 <= sprite_nr < 32 |
| 271 |
attrib_index = 512 + sprite_nr * 4 |
| 272 |
color_index = sprite_nr * 16 |
| 273 |
|
| 274 |
0 <= pattern_nr < 256 |
| 275 |
pattern_index = pattern_nr * 8 |
| 276 |
|
| 277 |
When sprite size is 16x16, Bit1 and Bit0 of the index are ignored. |
| 278 |
Four patterns define the look of a single sprite, like this: |
| 279 |
0 2 |
| 280 |
1 3 |
| 281 |
|
| 282 |
Planar Addressing |
| 283 |
----------------- |
| 284 |
|
| 285 |
The V9938 has 3 pins that select a group of VRAM ICs each: CAS0, CAS1 and CASX. |
| 286 |
CASX is used to select the expansion RAM, which can be accessed through the CPU |
| 287 |
interface and through commands, but cannot be displayed directly. |
| 288 |
|
| 289 |
Each VRAM group can be 0K, 16K or 64K. A group can consist of 8 chips of 1-bit |
| 290 |
wide RAM or 2 chips of 4-bit wide RAM, but for the purpose of addressing we can |
| 291 |
just pretend it is 1 chip with 8-bit wide RAM. |
| 292 |
|
| 293 |
In real MSX machines, the following group combinations are used: |
| 294 |
|
| 295 |
CAS0: CAS1: CASX: Description: |
| 296 |
16K 0K 0K MSX1 with V9938 and 16K VRAM (*1) |
| 297 |
64K 0K 0K MSX2 with 64K VRAM (*2) |
| 298 |
64K 64K 0K MSX2 with 128K VRAM |
| 299 |
64K 64K 64K MSX2 with 128K VRAM and 64K expansion (*3) |
| 300 |
|
| 301 |
*1: Examples are the Spectravideo SVI-738 X'PRESS and the Yamaha CX5MII/128. |
| 302 |
The only V9938-specific display mode that they can fully display is |
| 303 |
80-columns text mode (TEXT2). |
| 304 |
*2: Known 64K VRAM machines: Canon V-25, Hitachi MB-H3, Talent TPP-311 and 312, |
| 305 |
Toshiba HX-23. |
| 306 |
*3: No known machines shipped with expansion VRAM. It was a popular amateur |
| 307 |
upgrade for users of FastCopy, to reduce the number of required disk swaps. |
| 308 |
|
| 309 |
In most display modes addressing is linear, meaning the lower 64K of address |
| 310 |
space uses the VRAM connected to CAS0 and the upper 64K of address space uses |
| 311 |
the VRAM connected to CAS1. |
| 312 |
|
| 313 |
In SCREEN7/8 (Graphic 6/7) the addressing is planar, meaning that even VRAM |
| 314 |
addresses are mapped to CAS0 and odd addresses are mapped to CAS1. This means |
| 315 |
that SCREEN 7/8 are only available on 128K VRAM machines. The most likely |
| 316 |
reason for using planar addressing is getting a higher bandwidth: even though |
| 317 |
the data lines are shared between all VRAM groups, the row select has to be |
| 318 |
done only once. |
| 319 |
|
| 320 |
VR=0 / VR=1 mode |
| 321 |
---------------- |
| 322 |
|
| 323 |
The VDP chip has 10 pins that are connected to the VRAM subsystem to control |
| 324 |
the read/write address. There are 8 address pins, these select the CAS/RAS |
| 325 |
address. And there is a CAS0 and CAS1 pin, these select one of two ram chip |
| 326 |
(groups). In total this allows for a 17-bit address space (=128kB). (I'm |
| 327 |
ignoring the extended VRAM here). |
| 328 |
|
| 329 |
When the VDP does a memory access, it's puts the 17-bit logical address on |
| 330 |
these pins. The way how this is done depends on the VR bit (bit 3 of register |
| 331 |
8), see table below. |
| 332 |
|
| 333 |
In VR=1 mode, it first puts the bits A15-A8 on the address bus, followed by the |
| 334 |
bits A7-A0. The whole time either pin CAS0 or CAS1 is selected, depending on |
| 335 |
address bit A16. In VR=0 mode, .. well see table ;) |
| 336 |
|
| 337 |
|
| 338 |
VDP | VR=0 | VR=1 | |
| 339 |
address|----------------------|----------------------| |
| 340 |
pin | RAS(high) | CAS(low) | RAS(high) | CAS(low) | |
| 341 |
-------+-----------+----------|-----------+----------| |
| 342 |
AD0 | A6 #2| 1 #1| A8 | A0 | |
| 343 |
AD1 | A7 | A0 | A9 | A1 | |
| 344 |
AD2 | A8 | A1 | A10 | A2 | |
| 345 |
AD3 | A9 | A2 | A11 | A3 | |
| 346 |
AD4 | A10 | A3 | A12 | A4 | |
| 347 |
AD5 | A11 | A4 | A13 | A5 | |
| 348 |
AD6 | A12 | A5 | A14 | A6 | |
| 349 |
AD7 | A13 | A6 #2| A15 | A7 | |
| 350 |
-------+-----------+----------|-----------+----------| |
| 351 |
CAS0/1 | A14 | A16 | |
| 352 |
|
| 353 |
#1 this pin is always '1' in the CAS phase |
| 354 |
#2 A6 is output both in CAS and RAS phase, this allows to use both 8/6 and 7/7 |
| 355 |
RAS/CAS configurations (see VDP data sheet for details) |
| 356 |
|
| 357 |
|
| 358 |
One important side-effect is that in VR=0 mode, the VDP can only address 32kB |
| 359 |
memory. This is what makes the demo 'syntax infinity' work even though R14 is |
| 360 |
programmed 'wrongly' (it writes to address 0x1????, but expects the data to end |
| 361 |
up at address 0x0????). |
| 362 |
|
| 363 |
Another side effect is that when switching between VR=0 and VR=1 modes, the |
| 364 |
address space gets shuffled around, much like bit 7 in VDP register 1 for |
| 365 |
TMS99x8 chips. |