6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Mon May 06, 2024 7:04 pm

All times are UTC




Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Tue Sep 09, 2014 5:27 am 
Offline

Joined: Sun Feb 23, 2014 1:45 pm
Posts: 12
So, I'm trying to make a loop that counts to 960 (decimal), but I have never used 16 bit numbers so far, so I have no idea how to do it. What the loop is supposed to do is that it loads data from different location with indexed X addressing mode and stores it to $2007 (which automatically stores the data to pre-defined location and stores the next data to the next slot). Here is example (8 bit tough):

LDA $2002 ; Reset High/Low latch
LDA #$20 ; Defines high and low bytes
STA $2006
LDA #$00
STA $2006
LDX #$00 ; Load 0 to X for loop

DrawBackground: ; Loop for drawing background
LDA background, x
STA $2007
INX
CPX #$FF
BNE DrawBackground ; Branch to DrawBackground if X is less than $FF


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 6:21 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 1:09 am
Posts: 8432
Location: Southern California
Here's one way to do it:
Code:
        LDA  $2002            ; (Instead of a number, it's better to give addresses meaningful names.)
        LDA  $20
        STA  $2006
        STZ  $2006            ; 65c02 STZ allows you to skip the LDA #0.

        LDA  #<background     ; One way to do it is to put the address of "background" into a pair of ZP bytes
        STA  BG_ind           ; to use for indirect addressing, then increment it, instead of indexing by X
        LDA  #>background     ; (since X doesn't have enough bits).  Remember it's low-byte-first order.
        STA  BG_ind + 1

DrawBackground:
        LDA  (BG_ind)         ; Do the transfer.  (If you use an NMOS 6502, you'll have to use LDA (BG_ind),Y or
        STA  $2007            ; LDA (BG_ind,X), with Y or X zeroed.  65c02 allows the indirect without the X or Y.

        INC  BG_ind           ; Now increment and test your indirect number.
        BNE  db1
        INC  BG_ind + 1

db1:    LDA  BG_ind                   ; After the incr, compare to target value, starting with low byte.
        CMP  #<{background + $3C0}    ; $3C0 is the hex value of 960.  "<" is for low byte.
        BNE  DrawBackground

        LDA  BG_ind + 1               ; Now compare high byte (hence the "+1").
        CMP  #>{background + $3C0}    ; The ">" in many assemblers puts the high byte in the operand.
        BNE  DrawBackground

I didn't test it. Hopefully I don't have mistakes.

_________________
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 6:27 am 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1928
Location: Sacramento, CA, USA
You can easily loop 960 times by nesting loops. If the y register is available, you could just do 15 outer loops with y and 64 inner loops with x (15 * 64 == 960).

What about your background data? Is it repetitive in a Flintstones kind of way? If not, then you need to construct a 16-bit address pointer into your background data using the (dp),y addressing mode, and increment the high-byte of the pointer address when y wraps around.

Code:
        ...
DrawBackground:
        lda  #<background       ; init background pointer
        sta  zppointer
        lda  #>background
        sta  zppointer+1
        ldy  #0
DrawLoop:                       ; transfer 3 full pages of data
        lda  (zppointer),y
        sta  $2007
        iny
        bne  DrawLoop
        inc  zppointer+1        ; point to next page
        lda  zppointer+1
        cmp  #>background+3     ; last page of data?
        bne  Drawloop           ; No.  transfer another full page
Drawloop2:                      ; Yes.  transfer final (partial) page
        lda  (zppointer),y
        sta  $2007
        iny
        cpy  #960-768           ; done?
        bne  Drawloop2
        ...


Mike

[Edit: Darn! I see that Garth scooped me by a few minutes! My version is about 5 bytes longer, but works on any 6502, and is a tiny bit faster than Garth's version.]


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 9:13 am 
Offline

Joined: Sun Feb 23, 2014 1:45 pm
Posts: 12
GARTHWILSON wrote:
One way to do it is to put the address of "background" into a pair of ZP bytes


How do I know where the background is in the program?

The way I learned to load the background is doing this (not sure if this is how everybody does it):

Code:
background:
 .db $2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F ; Row 0 1/2 - Writes
 .db $2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F ; Row 0 2/2 - 'HELLO!
 .db $2F,$11,$0E,$15,$15,$18,$27,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F ; Row 1 1/2 - THIS IS A TEST
 .db $2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F ; Row 1 2/2 - FOR DRAWING
 .db $2F,$1D,$11,$12,$1C,$2F,$12,$1C,$2F,$0A,$2F,$1D,$0E,$1C,$1D,$2F ; Row 2 1/2 - BACKGROUND.'
 .db $2F,$0F,$18,$1B,$2F,$0D,$1B,$0A,$20,$12,$17,$10,$2F,$2F,$2F,$2F ; Row 2 2/2 - on screen
 .db $2F,$0B,$0A,$0C,$14,$10,$1B,$18,$1E,$17,$0D,$24,$2F,$2F,$2F,$2F ; Row 3 1/2
 .db $2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F ; Row 3 2/2


The rest is just blank tiles ($2F) for now, but I will change that once I get this working (undefined tiles display as a '0' since it's the first tile). So the loop should go through this list and take the tiles from the included .chr file which is in the chr data bank. (Hopefully that made sense, lol)


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 11:31 am 
Offline
User avatar

Joined: Tue Mar 02, 2004 8:55 am
Posts: 996
Location: Berkshire, UK
You can squeeze a few cycles (and one byte!) using X to count the pages
Code:
DrawBackground:
        lda  #<background       ; init background pointer
        sta  zppointer
        lda  #>background
        sta  zppointer+1
        ldx  #>960
        ldy  #0
DrawLoop:                       ; transfer 3 full pages of data
        lda  (zppointer),y
        sta  $2007
        iny
        bne  DrawLoop
        inc  zppointer+1        ; point to next page
        dex                     ; last page of data?
        bne  Drawloop           ; No.  transfer another full page
Drawloop2:                      ; Yes.  transfer final (partial) page
        lda  (zppointer),y
        sta  $2007
        iny
        cpy  #<960             ; done?
        bne  Drawloop2
        ...

It shouldn't matter where the data for the background is. The assembler will work out the actual address and use it in the <background and >background expressions used to initialise the pointer.

_________________
Andrew Jacobs
6502 & PIC Stuff - http://www.obelisk.me.uk/
Cross-Platform 6502/65C02/65816 Macro Assembler - http://www.obelisk.me.uk/dev65/
Open Source Projects - https://github.com/andrew-jacobs


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 11:50 am 
Offline

Joined: Sun Feb 23, 2014 1:45 pm
Posts: 12
BitWise wrote:
You can squeeze a few cycles (and one byte!) using X to count the pages
It shouldn't matter where the data for the background is. The assembler will work out the actual address and use it in the <background and >background expressions used to initialise the pointer.


So, using the < and > with a label tells the assembler to get the start and end point of the .db?


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 1:02 pm 
Offline

Joined: Wed Jan 08, 2014 3:31 pm
Posts: 575
Baka94 wrote:
So, using the < and > with a label tells the assembler to get the start and end point of the .db?


It's a way to indicate the address of the low (>) and high (<) bytes of a label.

I like the 16 bit value in the zero page. It's nice and clear and leaves X and Y free.


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 2:14 pm 
Offline

Joined: Sun Feb 23, 2014 1:45 pm
Posts: 12
Okay. I tried this code (I of course edited the pointer names and labels to fit in my program), but I got few syntax errors (marked red in the code)

barrym95838 wrote:
...
DrawBackground:
lda #<background ; init background pointer
sta zppointer
lda #>background
sta zppointer+1
ldy #0
DrawLoop: ; transfer 3 full pages of data
lda (zppointer),y
sta $2007
iny
bne DrawLoop
inc zppointer+1 ; point to next page
lda zppointer+1
cmp #>background+3 ; last page of data?
bne Drawloop ; No. transfer another full page
Drawloop2: ; Yes. transfer final (partial) page
lda (zppointer),y
sta $2007
iny
cpy #960-768 ; done?
bne Drawloop2
...


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 2:18 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 387
Location: Minnesota
Quote:
So, using the < and > with a label tells the assembler to get the start and end point of the .db?


No. "label" is the symbolic name of a 16-bit value. Typically the value denotes some location in the 16-bit 6502 address space. The "<" operator extracts the low eight bits of "label"'s value, ">" extracts the high eight bits.

So if, for example, the value of "label" is $1234, then "<label" has a value of $34 and "">label" has a value of $12.


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 2:25 pm 
Offline

Joined: Sun Nov 08, 2009 1:56 am
Posts: 387
Location: Minnesota
Quote:
but I got few syntax errors


That suggests the assembler you're using doesn't like/understand standard 6502 syntax. What assembler are you using?


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 2:28 pm 
Offline

Joined: Sun Feb 23, 2014 1:45 pm
Posts: 12
teamtempest wrote:
That suggests the assembler you're using doesn't like/understand standard 6502 syntax. What assembler are you using?


I'm using NESASM3.

EIDT: Also there is some weird issue that I forgot to mention that started around the time I started doing the 16 Bit counter stuff. It happens whenever I start up or reset
https://www.youtube.com/watch?v=HiNY6d4wd2U&feature=youtu.be


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 2:59 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1928
Location: Sacramento, CA, USA
BitWise wrote:
You can squeeze a few cycles (and one byte!) using X to count the pages

What do you think about this optimization? It's the smallest and fastest I can create at the moment.
Code:
        ...
DrawBackground:
        lda  #<(background-64)  ; init background pointer with offset
        sta  zppointer
        lda  #>(background-64)
        sta  zppointer+1
        ldx  #4
        ldy  #64                ; first page transfer is only 192 bytes
DrawLoop:                       ; transfer 3.75 pages of data (960 bytes)
        lda  (zppointer),y
        sta  $2007
        iny
        bne  DrawLoop
        inc  zppointer+1        ; point to next page
        dex                     ; are we done?
        bne  Drawloop           ; No.  transfer another full page
        ...

Mike
[Edit: added parentheses around initial address operands for safety. NESASM3 must have a way to select the upper and lower halves of a 16-bit value, but I'm not familiar with it.]


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 4:02 pm 
Offline

Joined: Sun Feb 23, 2014 1:45 pm
Posts: 12
I almost got it working. This is what I used:

Code:
DrawBackground:      ; Loop for drawing background
 LDA #$B3
 STA pointerLo
 LDA #$80
 STA pointerHi
 LDA #$00
 STA counterLo
 LDA #$04
 STA counterHi

BgLoop:
 LDA [pointerLo], y
 STA $2007
 LDA pointerLo
 CLC
 ADC #$01
 STA pointerLo
 LDA pointerHi
 ADC #$00
 STA pointerHi
 
 LDA counterLo
 SEC
 SBC #$01
 STA counterLo
 LDA counterHi
 SBC #$00
 STA counterHi
 
 LDA counterLo
 CMP #$00
 BNE BgLoop
 LDA counterHi
 CMP #$00
 BNE BgLoop


Now the problem is that the 'H' ($11) on the word 'Hello!' is replaced with a blank tile ($2F).

Here is the background tile address list:
http://pastebin.com/2MBdrRSN

Here is a list of the tiles in the .chr and their hex values:
http://pastebin.com/ssmABx7F

Hopefully with these someone can find what's going wrong.


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 6:04 pm 
Offline

Joined: Fri Nov 09, 2012 6:52 am
Posts: 16
You can directly INC or DEC a memory location without using a register.

Replace:
Code:
 LDA pointerLo
 CLC
 ADC #$01
 STA pointerLo


with:

Code:
INC pointerlo


But anyway, here is a suggested way of doing things that is simple.. if you make sure your data is a multiple of 4 bytes (4 bytes of data, 8 bytes of data, 12 bytes, etc..)
If you are writing the entire nametable it will work, otherwise you are going to have to decide on a terminating value and bail out of the loop.

Code:
   
    ; replace symbol names with your names..
    ; PPU_NAMETABLEADDRESS - where you want to write
    ; p - your pointer
    ; myData - data you want to write
    ; PPU_ADDRESS ; $2006
    ; PPU_DATA ; $2007

    ; set PPU address
    lda #>PPU_NAMETABLEADDRESS
    sta PPU_ADDRESS ; $2006
    lda #<PPU_NAMETABLEADDRESS
    sta PPU_ADDRESS

    ; nametable is 32 x 30 = 960
    ; this loop is 240 x 4 = 960
   
    lda #<mydata
    sta p
    lda #>mydata
    sta p+1

    ldy #0
    ldx #240 
 
    loop:
        lda (p), y
        sta PPU_DATA
        iny

        lda (p), y
        sta PPU_DATA
        iny

        lda (p), y
        sta PPU_DATA
        iny

        lda (p), y
        sta PPU_DATA
        iny
       
        bne loop
        inc p+1
    dex
    bne loop
   
    ; here you could set your attributes


Untested! .. barrym95838's code should work too, this code may be a touch faster to execute, but is slightly more bytes of code.


Top
 Profile  
Reply with quote  
PostPosted: Tue Sep 09, 2014 6:36 pm 
Offline

Joined: Sun Feb 23, 2014 1:45 pm
Posts: 12
Movax12 wrote:
You can directly INC or DEC a memory location without using a register.

Replace:
Code:
 LDA pointerLo
 CLC
 ADC #$01
 STA pointerLo


with:

Code:
INC pointerlo


But anyway, here is a suggested way of doing things that is simple.. if you make sure your data is a multiple of 4 bytes (4 bytes of data, 8 bytes of data, 12 bytes, etc..)
If you are writing the entire nametable it will work, otherwise you are going to have to decide on a terminating value and bail out of the loop.

Code:
   
    ; replace symbol names with your names..
    ; PPU_NAMETABLEADDRESS - where you want to write
    ; p - your pointer
    ; myData - data you want to write
    ; PPU_ADDRESS ; $2006
    ; PPU_DATA ; $2007

    ; set PPU address
    lda #>PPU_NAMETABLEADDRESS
    sta PPU_ADDRESS ; $2006
    lda #<PPU_NAMETABLEADDRESS
    sta PPU_ADDRESS

    ; nametable is 32 x 30 = 960
    ; this loop is 240 x 4 = 960
   
    lda #<mydata
    sta p
    lda #>mydata
    sta p+1

    ldy #0
    ldx #240 
 
    loop:
        lda (p), y
        sta PPU_DATA
        iny

        lda (p), y
        sta PPU_DATA
        iny

        lda (p), y
        sta PPU_DATA
        iny

        lda (p), y
        sta PPU_DATA
        iny
       
        bne loop
        inc p+1
    dex
    bne loop
   
    ; here you could set your attributes


Untested! .. barrym95838's code should work too, this code may be a touch faster to execute, but is slightly more bytes of code.


Unfortunately your code didn't work. I just got a messed up mix of 0 and blank tiles (like usually when it doesn't work). I was hoping it would have worked tough.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 20 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 7 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: