It is currently Mon May 22, 2017 6:16 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 8 posts ] 
Author Message
PostPosted: Thu Jan 26, 2017 5:40 pm 
Offline

Joined: Sat Jan 14, 2017 8:40 am
Posts: 23
Hi all, its me again, Hundo!

So I find myself now on the advanced Nerdy Nights tutorial about horizontal scrolling. Step 4 of specifically, and there is a portion of that code that just doesn't make sense to me. I've debugged it by hand, while following the debugger in FCEUX and it just doesn't make sense to me what is happening. Would anyone here care to shed some light on my questions as to how this particular piece of code works? First I'll show the code and then I'll ask my question about it.

Code:
DrawNewColumn:
  LDA scroll       ; calculate new column address using scroll register
  LSR A
  LSR A
  LSR A            ; shift right 3 times = divide by 8
  STA columnLow    ; $00 to $1F, screen is 32 tiles wide

  LDA nametable     ; calculate new column address using current nametable
  EOR #$01          ; invert low bit, A = $00 or $01
  ASL A             ; shift up, A = $00 or $02
  ASL A             ; $00 or $04
  CLC
  ADC #$20          ; add high byte of nametable base address ($2000)
  STA columnHigh    ; now address = $20 or $24 for nametable 0 or 1

  LDA columnNumber  ; column number * 32 = column data offset
  ASL A
  ASL A
  ASL A
  ASL A
  ASL A
  STA sourceLow
  LDA columnNumber
  AND #%11111000
  LSR A
  LSR A
  LSR A
  STA sourceHigh
 
  LDA sourceLow       ; column data start + offset = address to load column data from
  CLC
  ADC #LOW(columnData)
  STA sourceLow
  LDA sourceHigh
  ADC #HIGH(columnData)
  STA sourceHigh


Here we have the code which draws a new column of background data every time the scroll counter reaches a multiple of 8 (tiles are 8 pixels wide...).

I'm pretty sure I understand the first 12 lines of code correctly. If I'm correct, here we are calculating what PPU memory address we will be writing the first byte of data too for our new off screen column. Is this correct? Pretty sure I am correct on that, but its the next piece of code that I really don't undrstand.

Its the code that start with "LDA ColumnNumber". Can anyone tell me exactly what this piece of code is doing? Finally the logic behind the code is destroying my brain. In the code bunny loads a column number. Lets say the column number is 24, arbitrarily. He then multiples that number by 32 with 5 ASL's. After that, he stores the result of this previous multiplication as the low byte of the variable "sourceLow". Now, if the column number is 24, (and it can be because he's loading 128 columns of data in this example), and you multiply 24 by 32, your decimal result is 1800. Now one byte of data only holds up to 255, so how can this value be placed into the variable "sourceLow"? When I follow the sequence of ASL's in the FCEUX debugger, the results that I'm expecting for the multiplies do not add up there either.

Bunny also AND's the "columnNumber" value with the number #%11111000. If I understand the logic right here, any 8-bit number AND'ed with 11111000 can only equal the values 24 or 00 (decimal). Why does he want one or the other of these two values as his High byte for the "sourceHigh" variable?

Any advice for this would be greatly appreciated. Thank you!

-Hundo


Top
 Profile  
 
PostPosted: Thu Jan 26, 2017 7:43 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1567
Location: DIGDUG
I am not looking at the nerdy nights code, but this looks to be selecting a column from the BG data.

Addresses in Nametable #0 go from $2000 to $23ff. So, $400 bytes of data...0-$3ff.

Thus the uncompressed data needs 3 bits for the high byte, and ...not 8 bits for the low, since he is always starting the column from the top, we only need 5 bits for the low byte...0 to $1f.

EDIT: He's multiplying the column # by 32 to get the correct offset in the data, since each chunk is 32 bytes apart from each other.

He's adding this to the start address of the data, to find the start address of the column within the data. I would have (if carry set on the low byte math) allow for adding 1 to the high byte, in the rare case of the data crossing a page...like...

(Math adding an offset to a start address, 16-bit)...
Ldx #highByteData
Lda #lowByteData
Clc
Adc offsetLow
Bcc +
Inx
+
Sta indirectAddressLow
Txa ;high byte
Clc
Adc offsetHigh
Sta indirectAddressHigh

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Last edited by dougeff on Thu Jan 26, 2017 8:57 pm, edited 2 times in total.

Top
 Profile  
 
PostPosted: Thu Jan 26, 2017 8:27 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1567
Location: DIGDUG
OK, so I'm looking at the Scrolling #4 code...

the BG data is not stored as I would expect...like, cut and pasted from a PPU dump...but rather the first 32 bytes represents the first column (30 + 2 bytes of padding)...

2424242424242424242424242424242424242424242424242430B4B6B4B60000

...so each column is 32 bytes offset from each other x 32 columns wide = 1024 bytes = $400 bytes x 4 nametables worth of data = 4k bytes of data = $1000.

Ok, so, seeing as each chunk is 32 bytes apart, we skip ahead 32 bytes for each step of the way (asl x 5 is the same as multiply by 32, in hex $20). columnNumber goes from 0-$7f...$80 x $20 = $1000 bytes.

All that bit shifting is just a way to multiply by 32.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Fri Jan 27, 2017 5:52 am 
Offline

Joined: Sat Jan 14, 2017 8:40 am
Posts: 23
Hi Doug! Thanks for the reply. I appreciate the help and I understand what the code is doing now conceptually, I'm still having trouble understanding one part though. When the code ASL's the A register 5 times to multiply by 32, how does it store the result in the "sourceLow" variable, since "sourceLow" allows for only one byte (0 - 255) of storage. Example, if the columnNumber is 20 and we multiply 20 by 32 we get 280 in hex. In binary form Hex 280 is 1010000000. This value is larger than an 8 bit number, so how can we store it into "sourceLow", if source low is only 1 byte worth of storage?


Top
 Profile  
 
PostPosted: Fri Jan 27, 2017 7:34 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 18332
Location: NE Indiana, USA (NTSC)
ASL forgets the old value of carry and moves bit 7 into carry. If you open the debugger in FCEUX for Windows and "Step Into" through shifting $14 to the left five times, you'll see this:

Initial: A=14 C=don't care
ASL 1: A=28 C=0
ASL 2: A=50 C=0
ASL 3: A=A0 C=0
ASL 4: A=40 C=1
ASL 5: A=80 C=0


Top
 Profile  
 
PostPosted: Fri Jan 27, 2017 4:39 pm 
Offline

Joined: Sat Jan 14, 2017 8:40 am
Posts: 23
tepples wrote:
ASL forgets the old value of carry and moves bit 7 into carry. If you open the debugger in FCEUX for Windows and "Step Into" through shifting $14 to the left five times, you'll see this:

Initial: A=14 C=don't care
ASL 1: A=28 C=0
ASL 2: A=50 C=0
ASL 3: A=A0 C=0
ASL 4: A=40 C=1
ASL 5: A=80 C=0


Thank you Tepples. I traced this in FCEUX to verify what you said is correct.

Does this mean we are using the resulting overflow as the value stored in "sourceLow"? for values over 256 (Decimal)?

For example, if we are starting with #$14 (20)

A = #$14 Decimal(20)
ASL 1: A = #$28 Decimal(40)
ASL 2: A = #$50 Decimal(80)
ASL 3: A = #$A0 Decimal160)
ASL 4: A = $140 or $40 C=1 - Decimal value is originally 320. After bit 7 has been shifted into carry, value is now Decimal 64. Are we using 64 as sourceLow byte?

Thank you. Sorry if I'm being annoying on this question, just want to make sure I understand the math correctly.

-Spencer


Top
 Profile  
 
PostPosted: Fri Jan 27, 2017 6:48 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 1567
Location: DIGDUG
Quote:
Are we using 64 as sourceLow byte


Yes. The upper bits are discarded. Then this math gets the upper byte...

LDA columnNumber
AND #%11111000
LSR A
LSR A
LSR A
STA sourceHigh


Another way, (16 bit shift) requires rolling the bits from carry to (a zeroed) upper byte.

Lda #0
Sta sourceHigh
Lda # someNumber
Sta sourceLow
Asl sourceLow (shifts high bit to carry)
Rol sourceHigh (shifts from carry into high byte)

Repeat asl/rol combo per # of shifts.

_________________
nesdoug.com -- blog/tutorial on programming for the NES


Top
 Profile  
 
PostPosted: Fri Jan 27, 2017 6:55 pm 
Offline

Joined: Sat Jan 14, 2017 8:40 am
Posts: 23
dougeff wrote:
Quote:
Are we using 64 as sourceLow byte


Yes. The upper bits are discarded. Then this math gets the upper byte...

LDA columnNumber
AND #%11111000
LSR A
LSR A
LSR A
STA sourceHigh


Another way, (16 bit shift) requires rolling the bits from carry to (a zeroed) upper byte.

Lda #0
Sta sourceHigh
Lda # someNumber
Sta sourceLow
Asl sourceLow (shifts high bit to carry)
Rol sourceHigh (shifts from carry into high byte)

Repeat asl/rol combo per # of shifts.


Thank you Dougeff and Tepples! Much appreciated. I had been struggling with that for two days : )


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 6 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group