rchoudhary wrote:
Oh ok. Yeah we dealt with memory mapping a lot in my embedded systems courses. We'd read addresses in memory to like perform I/O with the GPIO pins, send data out with SSI and UART, etc. The part that confused me here was that we were writing 16 bits to an address that points to 8 bits.
Why not have say $2122 and $2123 write to CGRAM, put the accumulator in 16-bit mode, and then only do a single write operation to send the color data? Is there an advantage to splitting it up into two writes?
While I understand exactly what previous individuals have said to you (and they're right), I don't think the explanations are clear given your 2nd paragraph above. So let me try to phrase it differently. You have "some" of it right, but then you end up confusing yourself (which means you don't have it completely right).
$2122 is a single 8-bit MMIO register. It acts as a "gateway" to CGRAM, allowing you to write data to CGRAM one byte at a time. Every time you write to it, internally, the PPU auto-increments the CGRAM address (this way you don't have to keep setting $2121 when writing data linearly). The CGRAM data format itself is 16 bits (thus represented by 2 bytes), but I'll explain that format with some examples at the end of my post.
So, if you wanted to make the first 4 bytes of CGRAM values
$ff, $7f, $e0, $03 respectively (I'll explain the colour, just hold on!) -- to affect palette entries #0 and #1 -- you'd do this:
Code:
sep #$20
stz $2121 ; Start at palette entry #0 (CGRAM address $0000)
lda #$ff
sta $2122 ; 1st byte of CGRAM = $ff (%11111111) (low byte of CGRAM data palette entry #0)
lda #$7f
sta $2122 ; 2nd byte of CGRAM = $7f (%011111111) (high byte of CGRAM data palette entry #0)
lda #$e0
sta $2122 ; 3rd byte of CGRAM = $e0 (%11100000) (low byte of CGRAM data palette entry #1)
lda #$03
sta $2122 ; 4th byte of CGRAM = $03 (%00000011) (high byte of CGRAM data palette entry #1)
The reason the accumulator is set to 8-bit using
sep #$20 is because you're wanting to write a single byte of data to single MMIO register/address ($2122).
Now let's try it the way you described in your paragraph, using a 16-bit accumulator:
Code:
rep #$20
stz $2121 ; Mistake #1
lda #$7fff
sta $2122 ; Mistake #2
lda #$03e0
sta $2122 ; Mistake #3
First mistake: because the accumulator is 16 bits, the
stz $2121 writes $00 to $2121, and $00 to $2122. That means you'd be starting at palette entry #0 in CGRAM (good), and the 1st byte of CGRAM data would be $00 (bad).
Second mistake: because the accumulator is 16 bits, the
sta $2122 writes $ff to $2122, and $7f to $2123. That means the 2nd byte of CGRAM data would be $ff (bad), and you'd be tweaking $2123 which is the MMIO register for both window masks on BG1 and BG2. CGRAM palette entry #0 would then the CGRAM data/colour $ff00 (
%1111111100000000) -- not what you wanted!
Third mistake: because the accumulator is 16 bits, the
sta $2122 writes $e0 to $2122, and $03 to $2123. That means the 3rd byte of CGRAM data would be $e0 (bad), and you'd be again tweaking $2123 which is the MMIO register for both window masks on BG1 and BG2. CGRAM palette entry #1 would be "half written" at this point, thus value
%????????11100000 -- not what you wanted!
Now going back to my first code section/example (that one that works), explaining the colour itself:
When you write to $2122, you write it in the order of low byte of CGRAM data first, followed by the high byte. The colour data format is as follows, where
bbbbb,
ggggg, and
rrrrr are the intensities of blue/green/red (5 bits each, thus ranging from 0-31 each).
So let's take those values I wrote earlier and see what you get:
Code:
Highbyte Lowbyte
--------- --------
%0bbbbbgg gggrrrrr
%01111111 11111111 -- $7fff: blue=31, green=31, red=31: white
%00000011 11100000 -- $03e0: blue=0, green=31, red=0: green
All colours at full intensity (i.e. red=31, blue=31, green=31)? That'd be white!
Here's a "simple" chart of colours from my old SNES documentation to help:
Code:
----------------------------------------------------------------------------
|A quick colour chart could be the following: |
| $7FFF [0111 1111 1111 1111]: White. |
| $001F [0000 0000 0001 1111]: Red. |
| $03E0 [0000 0011 1110 0000]: Green. |
| $7C00 [0111 1100 0000 0000]: Blue. |
| $7C1F [0111 1100 0001 1111]: Purple. |
| $7FE0 [0111 1111 1110 0000]: Aqua. |
| $03FF [0000 0011 1111 1111]: Yellow. |
----------------------------------------------------------------------------
As for you asking, essentially, why didn't they just make the CGRAM MMIO register span two bytes in CPU address space (ex. $2122 and $2123), much like they did with $2116 and $2118? They probably had reasons. It usually has to do with busses, chip design, cost savings, etc.. I ask you seriously: does "why" matter? This is how the system is designed, it cannot be changed. If you think this is terrible and awful and confusing, wait until you get to OAM data, particularly where the 9th bit of the X (horizontal) position is, and how all that is arranged.
Finally, just in case you aren't aware: you don't need to keep doing
sep #$20 over and over. You WILL, however, find yourself changing register sizes fairly often given the design of all the MMIO registers and other whatnots; get used to seeing
sep/rep often. That's just how the 65816 is.
HTH.