It is currently Mon Sep 16, 2019 1:03 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 13 posts ] 
Author Message
PostPosted: Sat Aug 24, 2019 2:43 pm 
Offline

Joined: Sat Aug 24, 2019 2:21 pm
Posts: 5
I'm confused on how to read in a buffer of data for drawing. I'm mostly going off of how it is explained in the Frame and NMI article: https://wiki.nesdev.com/w/index.php/The_frame_and_NMIs

They explain that a great way to read in a drawing buffer would be to make a .db in the following structure:
Code:
 05 20 16 CA AB 00 EF 05 01 2C 01 00 00
  | \___/ \____________/  | \___/  |  |
  |   |         |         |   |    |  |
  |   |         |         |   |    |  length (0, so no more)
  |   |         |         |   |    byte to copy
  |   |         |         |   PPU Address $2C01
  |   |         |         length=1
  |   |         bytes to copy
  |   PPU Address $2016
  length=5


How would I read in the two hex numbers for the PPU address and store it in a single register to say I was going to load in the set of following bytes?

I keep coming back to trying to do something like this:
Code:
LoadBuffer:   
   LDA testbuffer, X
   CPX #$00      ;see where x is in the buffer
   BNE :+
   STX bufflength   ;iterate this many times through?
   JMP LoadBuffer
   :
   CPX #$01
;;etc... CPXing the entire buffer
;;then INX

;;;further down
testbuffer:
   .db $06,$01,$03,$00,$00,$01,$00


I want to load from the sprite sheet $00 just as a test.
I feel like my above example could be done, but I'd just be CPXing every single thing in the buffer to see where I was, which seems really inefficient.

What am I missing?

Secondly - if you happen to know, what does ">" or "<" do in an opcode? Does this shift right and left like ROL/ROR or LSR?
For example:
Code:
LDA needdma
   BEQ :+
   LDA #$00
   STA $2003
   LDA #$>oam   ;this line here - is this a ROR or LSR?
   STA $4014


Top
 Profile  
 
PostPosted: Sat Aug 24, 2019 3:46 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21591
Location: NE Indiana, USA (NTSC)
You can read one byte at time into A and write one byte at a time out to $2006.

_________________
Pin Eight | Twitter | GitHub | Patreon


Top
 Profile  
 
PostPosted: Sat Aug 24, 2019 5:40 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2559
Location: DIGDUG
Quote:
LDA #$>oam


This should throw an error. The syntax should be

LDA #>oam

LDA immediate mode, from the high byte of the 16 bit address of the label "oam". The > indicates high byte. If it were <, that would get the low byte.

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


Top
 Profile  
 
PostPosted: Sat Aug 24, 2019 7:02 pm 
Offline

Joined: Fri Feb 24, 2012 12:09 pm
Posts: 1003
Code:
LoadBuffer:   
   LDA testbuffer, X
   CPX #$00      ;see where x is in the buffer
   BNE :+
   STX bufflength   ;iterate this many times through?
   JMP LoadBuffer
   :
   CPX #$01
;;etc... CPXing the entire buffer
;;then INX

;;;further down
testbuffer:
   .db $06,$01,$03,$00,$00,$01,$00
There are some bugs in there:
If you want to start at the begin of testbufffer, then you must first set X to zero.
If X is zero, then it nonsense to do CPX 0 and BNE because you already know that it is zero. Remove CPY and BNE.
If X is nonzero at that location, then I've no idea what you are trying to do there.
You might however want to compare if the length value in A is zero (and exit the function if so).
With STX you want to store the length value from A to the variable bufflength. The correct opcode for that is STA, not STX.
JMP LoadBuffer is causing your code to hang in an endless loop (assuming that X was zero). Remove that JMP.
CPX 1 looks nonsense, too. Normally you would use INX (to increment from 0 to 1), and then continue with the etc.
In the etc you say you are CPXing the entire buffer? What do you think what CPX does? Copy something? It means Compare X.
Don't do that CPXing, just go on with using LDA+STA+INX to copy the two address bytes.
Then use further LDA+STA in a loop to copy the data bytes from testbuffer to the address, with bufflength as loop counter.
For writing to PPU memory you will need to know how to write to PPU memory (if you are new to that, then you probably don't know how to do that?).
In the .db line, 06 means six data bytes, 01,03 would be your target address (that would be 0103, do you really want that???), then 03,00,01 would be your six data bytes (but that are only three bytes???), and the ending 00 is the end byte?
Don't say "a great way" (unless you somehow believe that it is white and male and superior to most or all other ways).

_________________
homepage - patreon


Top
 Profile  
 
PostPosted: Sat Aug 24, 2019 9:12 pm 
Offline

Joined: Sat Aug 24, 2019 2:21 pm
Posts: 5
Thanks for the help!
I've modified to this:
Code:
   LDX #$00
   LDY #$00
LoadBuffer:   
   LDA testbuffer, X
   STA bufflength
   INX
   LDA testbuffer, X
   STA $2006
   INX
   LDA testbuffer, X
   STA $2006
   INX
BuffLoop:   
   LDA testbuffer, X
   STA $2007
   INX
   CPX bufflength
   BNE BuffLoop
   INX
   LDA testbuffer, X   ;next data set in buffer (in test it is 0 and the end)
   STA bufflength
        ;do something else once at 0
DoDrawingDone:
   RTS

Quote:
Quote:
LDA #$>oam


This should throw an error. The syntax should be

LDA #>oam


dougeff - I tried switching my #$>oam to #>oam and it threw an error?

Quote:
Don't say "a great way" (unless you somehow believe that it is white and male and superior to most or all other ways).


nocash, your comment is confusing. Are you saying that people shouldn't refer to things as "great ways" because there is no such thing as a "great way" necessarily? And white males who think they are superior only refer to things like that? If it's an insult I don't get your teaching/belittling process?


Top
 Profile  
 
PostPosted: Sat Aug 24, 2019 11:49 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11412
Location: Rio de Janeiro - Brazil
mathe-matician wrote:
I tried switching my #$>oam to #>oam and it threw an error?

Are you by any chance using NESASM? Because NESASM uses the functions LOW() and HIGH() instead of < and > like most other assemblers.


Top
 Profile  
 
PostPosted: Sun Aug 25, 2019 7:20 am 
Offline

Joined: Sat Aug 24, 2019 2:21 pm
Posts: 5
tokumaru wrote:
mathe-matician wrote:
I tried switching my #$>oam to #>oam and it threw an error?

Are you by any chance using NESASM? Because NESASM uses the functions LOW() and HIGH() instead of < and > like most other assemblers.


I am using NESASM, that would make sense. Do you have a recommended assembler? asm6?


Top
 Profile  
 
PostPosted: Sun Aug 25, 2019 7:51 am 
Offline

Joined: Fri Feb 24, 2012 12:09 pm
Posts: 1003
mathe-matician wrote:
Code:
   LDX #$00
   LDY #$00
LoadBuffer:   
   LDA testbuffer, X
   STA bufflength
   INX
   LDA testbuffer, X
   STA $2006
   INX
   LDA testbuffer, X
   STA $2006
   INX
BuffLoop:   
   LDA testbuffer, X
   STA $2007
   INX
   CPX bufflength
   BNE BuffLoop
   INX
   LDA testbuffer, X   ;next data set in buffer (in test it is 0 and the end)
   STA bufflength
        ;do something else once at 0
DoDrawingDone:
   RTS
That looks much better!
As it is a function that's ends with RTS, it should also begin with a function name/label, eg. add "DrawingFunction:" in the first line, so you can call the function name via JSR from your main program.
The LDY doesn't do anything useful here.
If LoadBuffer: is meant to be the outer loop address (?) then I would rename it to "OuterLoop:" or the like.
After the first LDA, best "BEQ DoDrawingDone" (LDA does automatically set the zeroflag is A is (EQual to) zero, so you won't even need a separate compare opcode between LDA and BEQ).
CPX bufflength looks wrong (unless you are intentionally doing things differently than in the wiki.nesdev example).
First of, the wiki suggests length of data bytes (whilst X is the total length including the length byte and address bytes).
Second, if you loop to "OuterLoop", then X is the total length including all previous data blocks.
To fix both issues: Use "DEC bufflength" instead of "CPX bufflength".
The INX after BNE BuffLoop is nonsense (you had already used INX a few above, so the extra INX would skip another/unused byte).
Instead of the final LDA/STA, use "B OuterLoop".

I think then it should work and do what you want. Best use the Trace feature in a debugger to see what it is doing when executing the opcodes step by step, and to see if the loop(s) are executed the expected number of times.

I am not so familar with common NES ASM syntax(es), but the "$" in "#$>oam" looked mindblowing and probably wrong to me, too.
The code is probably supposed to use the upper 8bit of the 16bit "oam" address, so better try "#oam/256" or that HIGH/LOW thing, ie. "#HIGH(oam)".

What the code is currently doing, I guess it's "comparing if $ is greater than oam" and replacing the expression by zero (if false), and nonzero (if true).
Uhm, now I've used the word "great" myself, but I didn't mean that it is true or false that greater is better than less or equal. Yes, I am getting offtopic again, sorry. Greatness is a bit politically charged these days (and perhaps it's always been since before roman empire). Anyways, use whatever math operators you want to.
And the "$", your assembler is apparently using it as prefix for hexadecimal numbers. But $ without any following numeric digits is apparently treated as something else... some assemblers treat "$" as "address of the current opcode"... or maybe some assemblers treat anything like $000 and $00 and $0 and $ as "zero".

_________________
homepage - patreon


Top
 Profile  
 
PostPosted: Sun Aug 25, 2019 12:57 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11412
Location: Rio de Janeiro - Brazil
mathe-matician wrote:
I am using NESASM, that would make sense. Do you have a recommended assembler? asm6?

I do prefer ASM6 over NESASM, but for a good while I've been using ca65. All of those have problems though, and I'm not 100% satisfied with any of them.

NESASM has some cool features, but quite a few annoyances, such as the non-standard syntax and obligatory use of 8kb banks.

ASM6 has less features overall, but is pretty straightforward and versatile when it comes to defining any ROM structure and doesn't force you to do things in any particular way.

ca65 is a powerhouse compared to the other 2, with an insane amount of features, including a powerful macro system that makes the assembler highly customizable. One drawback is that you can't make assembly-time decisions based on not-yet-defined labels/symbols, because it assembles programs in a single pass. It also requires you to configure the whole memory layout in a separate file before coding anything, which can be inconvenient.

All things considered, I think ASM6 is the best one to start with if you don't plan on directly copying code from tutorials. If you do plan on copying code from tutorials, better use whatever assembler the tutorial uses (most likely NESASM). Then, when you're more experienced, you can make an educated decision on whether to switch to ca65.


Top
 Profile  
 
PostPosted: Mon Sep 02, 2019 4:09 pm 
Offline

Joined: Sat Aug 24, 2019 2:21 pm
Posts: 5
Thank you, that is all helpful information!
Right now I'm trying to load the first 3 sprites for the background, I can't seem to figure out why it won't load the palette info and the right background sprites? I keep getting this image below - which is just the first background sprite printed over and over in only gray.

Attachment:
can't figure it out.png
can't figure it out.png [ 23.38 KiB | Viewed 320 times ]


tokumaru, I'm using your template btw - thank you for those!
I'm not sure if I'm not using the correct ASM6 syntax coming from NESASM.
Do you have any suggestions on this?
Code:
;----------------------------------------------------------------
; constants
;----------------------------------------------------------------

PRG_COUNT = 1 ;1 = 16KB, 2 = 32KB
MIRRORING = %0001 ;%0000 = horizontal, %0001 = vertical, %1000 = four-screen

STATETITLE   = $00
STATEPLAYING   = $01
STATEGAMEOVER   = $02

;----------------------------------------------------------------
; variables
;----------------------------------------------------------------

   .enum $0000

   ;NOTE: declare variables using the DSB and DSW directives, like this:

   ;MyVariable0 .dsb 1
   ;MyVariable1 .dsb 3
oam         .dsw 1,$0200 ;shadow oam
gamestate     .dsb 1
soft2000     .dsb 1
soft2001     .dsb 1
sleeping     .dsb 1
needma        .dsb 1
needdraw     .dsb 1
needppureg     .dsb 1
needoam        .dsb 1   
buttons1     .dsb 1
buttons2     .dsb 1
bufflength     .dsb 1

   .ende

   ;NOTE: you can also split the variable declarations into individual pages, like this:

   ;.enum $0100
   ;.ende

   ;.enum $0200
   ;.ende

;----------------------------------------------------------------
; iNES header
;----------------------------------------------------------------

   .db "NES", $1a ;identification of the iNES header
   .db PRG_COUNT ;number of 16KB PRG-ROM pages
   .db $01 ;number of 8KB CHR-ROM pages
   .db $00|MIRRORING ;mapper 0 and mirroring
   .dsb 9, $00 ;clear the remaining bytes

;----------------------------------------------------------------
; program bank(s)
;----------------------------------------------------------------

   .base $10000-(PRG_COUNT*$4000)

   ;; .include "clearmem.asm"
   .include "macros.asm"
   ;; .include "colorbuffers.asm" ;palette & attribute data
   ;; .include "background.asm"

RESET:
   SEI         
   CLD         
   LDX #$40
   STX $4017      
   LDX #$FE
   TXS         
   INX
   STX $2000
   STX $2001
   STX $4010

   ;; JMP ClearMem
   ;; JMP LoadBackground
   
vblankwait1:
   BIT $2002
   BPL vblankwait1

clrmem:
   LDA #$00
   STA $0000, x
   STA $0100, x
   STA $0300, x      
   STA $0400, x
   STA $0500, x
   STA $0600, x
   STA $0700, x
   LDA #$FE
   STA $0200, x      ;shadow OAM
   INX
   BNE clrmem
   
vblankwait2:   
   BIT $2002
   BPL vblankwait2


LoadPalettes:
   LDA $2002             ; read PPU status to reset the high/low latch
   LDA #$3F
   STA $2006             ; write the high byte of $3F00 address
   LDA #$00
   STA $2006             ; write the low byte of $3F00 address
   LDX #$00              ; start out at 0
LoadPalettesLoop:
   LDA palette, x       
   STA $2007             ; write to PPU
   INX                   
   CPX #$20             
   BNE LoadPalettesLoop

LoadAttribute:
   LDA $2002             ; read PPU status to reset the high/low latch
   LDA #$23
   STA $2006             ; write the high byte of $23C0 address
   LDA #$C0
   STA $2006             ; write the low byte of $23C0 address
   LDX #$00              ; start out at 0
LoadAttributeLoop:
   LDA attribute, x      ; load data from address (attribute + the value in x)
   STA $2007             ; write to PPU
   INX                   ; X = X + 1
   CPX #$28              ; Compare X to hex $08, decimal 8 - copying 8 bytes
   BNE LoadAttributeLoop  ; Branch to LoadAttributeLoop if compare was Not Equal to zero
LoadBackground:
   LDA $2002             ; read PPU status to reset the high/low latch
   LDA #$20
   STA $2006             ; write the high byte of $2000 address
   LDA #$00
   STA $2006             ; write the low byte of $2000 address
   LDX #$00              ; start out at 0
LoadBackgroundLoop1:
   LDA background, x     
   STA $2007           
   INX                   
   CPX #$00             
   BNE LoadBackgroundLoop1 

   LDA #%10010000
   STA $2000
   LDA #%00011110
   STA $2001

GameEngineRunning:
   
Forever:
   JMP Forever
   
WaitFrame:
   inc sleeping
loop:
   lda sleeping
   bne loop
   rts

   ;--------------------------------------
   ; DoFrame - same idea as WaitFrame, but also does some other stuff
   ;   that the game logic will want done every frame.  Things that
   ;   shouldn't be put in NMI

DoFrame:
   lda #1
   sta needdraw
   sta needoam
   sta needppureg
   jsr WaitFrame
   jmp UpdateJoypadData
   RTS
;---------------------------------------
; NMI
;---------------------------------------
NMI:

   PushAll
   LDA needma
   BEQ NeedDraw
   LDA #$00
   STA $2003
   LDA oam
   STA $4014

NeedDraw:
   LDA needdraw
   BEQ NeedPpuReg
   BIT $2002
   JSR DoDrawing
   DEC needdraw
NeedPpuReg:
   LDA needppureg
   BEQ Next
   LDA soft2001
   STA $2001
   LDA soft2000
   STA $2000
Next:   
   BIT $2002
   LDA #$00
   STA $2005
   STA $2005

   ;; Optional
   ;; 7)run music code
   ;; 8)wait for sprite 0 hit and change the VRAM address
   ;; Optional

   LDA #0 ;reset sleeping to 0 so WaitFrame exits
   STA sleeping

   ;; 9)pull registers/status flags off stack
   PullAll

   RTI


DoDrawing:
   ;; draw the stuff needed to draw to the drawingbuf variable ($0300)
   ;; DoFrame to wait for the next VBLANK for it to write
;;    LDX #$00
;;    LDA $2002
;; OuterLoop:   
;;    LDA testbuffer, X
;;    BEQ DoDrawingDone
;;    STA bufflength
;;    INX
;;    LDA testbuffer, X
;;    STA $2006
;;    INX
;;    LDA testbuffer, X
;;    STA $2006
;;    INX
;;    ;X should be at #$03 right now (in bytes to load)
;; BuffLoop:   
;;    LDA testbuffer, X
;;    STA $2007
;;    INX
;;    CPX bufflength
;;    BNE BuffLoop
DoDrawingDone:
   RTS
UpdateJoypadData:
   LDA #$01
   STA $4016
   LDA #$00
   STA $4016
   LDX #$08
ReadController1Loop:
   LDA $4016
   LSR A            ; bit0 -> Carry
   ROL buttons1     ; bit0 <- Carry
   DEX
   BNE ReadController1Loop
   RTS

testbuffer:
   .db $03,$20,$00,$00,$01,$02

background:
   .db $00,$01,$02,$03
   
palette:
   .db $20,$29,$1A,$0F,  $20,$36,$17,$0F,  $20,$30,$21,$0F,  $20,$27,$17,$0F   ;;background palette
   .db $20,$16,$27,$18,  $20,$02,$38,$3C,  $20,$1C,$15,$14,  $20,$02,$38,$3C   ;;sprite palette
   
attribute:
   .db %00000000, %00010000, %01010000, %00010000, %00000000, %00000000, %00000000, %00110000
   
   .db $01,$01,$01,$01, $01,$01,$01,$01 ,$01,$01,$47,$47, $47,$47,$24,$24 ,$24,$24,$24,$24 ,$24,$24,$24,$24, $24,$24,$24,$24, $55,$56,$24,$24
   .db $01,$01,$01,$01, $24,$24,$24,$24 ,$24,$24,$24,$24, $24,$24,$24,$24 ,$24,$24,$24,$24 ,$24,$24,$24,$24, $24,$24,$24,$24, $24,$24,$24,$24

;----------------------------------------------------------------
; interrupt vectors
;----------------------------------------------------------------

   .org $fffa

   .dw NMI
   .dw RESET
   .dw 0

;----------------------------------------------------------------
; CHR-ROM bank
;----------------------------------------------------------------

   .incbin "gamer.chr"


Top
 Profile  
 
PostPosted: Mon Sep 02, 2019 6:28 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2559
Location: DIGDUG
Among other problems...

LDX #$FE ;!!!!!!!
TXS
INX
STX $2000
STX $2001
STX $4010

should be
LDX #$FF

Would you like me to go step by step through the code pointing out problems?

Edit.
Also significantly problematic...

oam .dsw 1,$0200

Does not do what you think it does.

oam = $200
should suffice. Move it outside the enum.

Then when you reference it later
LDA oam
STA $4014

should be changed to
LDA #>oam
STA $4014

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


Top
 Profile  
 
PostPosted: Mon Sep 02, 2019 7:26 pm 
Offline

Joined: Sat Aug 24, 2019 2:21 pm
Posts: 5
dougeff wrote:
Among other problems...

LDX #$FE ;!!!!!!!
TXS
INX
STX $2000
STX $2001
STX $4010

should be
LDX #$FF

Would you like me to go step by step through the code pointing out problems?

If you have the care and the will? Otherwise suggestions/direction would be more than fine and greatly appreciated!

I've also managed to get the attributes and background loaded properly. Very neat and exciting.

Thank you for your input so far too.


Top
 Profile  
 
PostPosted: Mon Sep 02, 2019 8:34 pm 
Offline
User avatar

Joined: Fri May 08, 2015 7:17 pm
Posts: 2559
Location: DIGDUG
load attributes

CPX #$28 ; Compare X to hex $08

I'm assuming you meant CPX #$08

Load background

LDA background, x
STA $2007
INX
CPX #$00
BNE...

This will load 256 bytes. It looks like you only have 4 in the array. The CPX is superfluous, when you are comparing zero. INX will set the z flag if it results in X becoming zero.


NMI:

PushAll
LDA needma
BEQ NeedDraw

PushAll is being treated as a label by the assembler. needma is currently unused. The next line is the OAM DMA, which honestly should be done every frame anyway, even if it hasn't changed. On a real NES, the OAM RAM can decay.

LDA soft2001
STA $2001
LDA soft2000
STA $2000

You haven't given values to soft2000 and soft2001 yet. This would turn the screen off and turn off NMIs and crash the game.

The rest seems ok. I'm a little worried about your commented out draw from a buffer routine, but I have a feeling you are still working on it.

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


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

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 1 guest


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