Syntax Error (I hope...)

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
StabbyMcStabbStabb
Posts: 3
Joined: Fri May 19, 2023 4:11 pm

Syntax Error (I hope...)

Post by StabbyMcStabbStabb »

So, I am VERY new to 6502 assembly code but I found a rather informative YouTube channel that's helped out a bit:
The Zero Pages (https://www.youtube.com/playlist?list=P ... PqwliHEuEk)
I've been following along, making notes, creating bookmarks, downloading programs, etc. and I'm beginning to understand things (sort of). Well, in "Episode 4 - Hello Mario" I downloaded the files to follow along with the video (except I started with blank files for the coding bit). I got to the very end of the video and ran my newly created ROM (which should just have a blue background and a jumping Mario sprite on screen) and I missed something. It runs... but all I get is a blue screen. I don't know if that's supposed to be right (his screen looked a bit darker than mine). I have went over my code a few times (even comparing it side by side with the originally downloaded file) and I can't find what I've done wrong.

WARNING: I put comments on every line because I didn't want to forget what something does later on.... prepare yourself for ALOT of green (or whatever your highlight color is....

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.segment "HEADER" ;must have for emulator
.byte "NES" ;structures ROM to resemble physical cartridge to emulator

.byte $1a ;signature for verification by emulator
.byte $02 ;two 16KB PRG-ROM
.byte $01 ;one 8KB CHR-ROM

.byte %00000000 ;mapper and mirroring - in binary for ease of reference/modification

.byte $00 ;
.byte $00 ;
.byte $00 ;
.byte $00 ;

.byte $00, $00, $00, $00, $00 ;filler bytes for complete header
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.segment "ZEROPAGE" ;sets most significant bit to zero
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.segment "STARTUP" ;
Reset: ;
SEI ;disable all interrupts
CLD ;disable decimal mode
LDX #$40 ;disable sound IRQ - load value $40 (%0100 0000 / 64) into X register
STX $4017 ;disable sound IRQ - store value in memory address $4017

LDX #$FF ;initialize the stack register - load value #FF into X register
TXS ;initialize the stack register - transfer X value to stack

INX ;increment X value by one to cause rollover to zero (#$FF / %1111 1111 / 255 + 1 => #$00 / %0000 0000 / 0)

STX $2000 ;zero out the PPU registers
STX $2001 ;zero out the PPU registers

STX $4010 ;disable PCM channel

: ;wait for V-Blank - anonymous label
BIT $2002 ;wait for V-Blank - check value of bit seven in $2002 byte
BPL :- ;wait for V-Blank - jump to anonymous label if BIT is 0 (- jump to previous anonymous label, + jump to next anonymous label)

TXA ;transfer value of X register to A register

CLEARMEM: ;clear memory - label for loop to clear memory

STA $0000, X ;clear memory - X register index $0000 => 00FF
STA $0100, X ;clear memory - X register index $0100 => 01FF
;SKIP ADRESSES -- will be used for sprite data
STA $0300, X ;clear memory - X register index $0300 => 03FF
STA $0400, X ;clear memory - X register index $0400 => 04FF
STA $0500, X ;clear memory - X register index $0500 => 05FF
STA $0600, X ;clear memory - X register index $0600 => 06FF
STA $0700, X ;clear memory - X register index $0700 => 07FF

LDA #$FF ;sprite data - load #FF into A register
STA $0200, X ;sprite data - store value in X register index $0200 => $02FF

LDA #$00 ;clear memory - reset A register back to #00 for clear nemory loop

INX ;zero flag - increment X register (#$FF + 1 = #$00 with zero flag set to 1 / otherwise zero flag zet to 0)
BNE CLEARMEM ;zero flag - branch if not equal

: ;wait for V-Blank - anonymous label
BIT $2002 ;wait for V-Blank - check value of bit seven in $2002 byte
BPL :- ;wait for V-Blank - jump to anonymous label if BIT is 0 (- jump to previous anonymous label, + jump to next anonymous label)

LDA #$02 ;load sprite data - load A register with value from MSB (most significant byte)
STA $4014 ;load sprite data - store A register data in adress $4014 (OAM-DMA [Object Attribute Memory])
NOP ;load sprite data - no operation (wait command to allow PPU time to load and store data)

LDA #$3F ;access PPU - load MSD into A register
STA $2006 ;access PPU - store value in PPU address register (PPU-ADDR)
LDA #$00 ;access PPU - load LSD (least significant digit) of zero into A register
STA $2006 ;access PPU - store value in PPU address register (PPU-ADDR)

LDX #$00 ;load sprite data - initialize X register (prepare for sprite palette data loop)

LoadPalettes: ;load palette colors - label for palette loop
LDA PaletteData, X ;load palette colors - load "PaletteData" into A register then increment X register (PaletteData Array)
STA $2007 ;load palette colors - store A register to PPUDATA that auto increments ($3F00 => $3F1F)
INX ;load palette colors - increment X register
CPX #$20 ;load palette colors - compare value X to value 32 (decimal)
BNE LoadPalettes ;load palette colors - BNE to label

LDX #$00 ;load sprite data - initialize X register (prepare for sprite palette data loop)

LoadSprites: ;load sprites - label for sprite loop
LDA SpriteData, X ;load sprites - load "SpriteData" into A register then increment X register (SpriteData Array)
STA $0200 ;load sprites - store value in $0200
INX ;load sprites - increment X register
CPX #$20 ;load sprites - compare value X to value 32 (decimal)
BNE LoadSprites ;load sprites - BNE to label

CLI ;enable interrupts

LDA #%10010000 ;VPHB SINN -- Enable NMI / Background uses second sprite set
;----------------------------------------------------------------------------------------;
;V -- Generate an NMI at V-Blank (0:off, 1:on)
;P -- PPU master/slave select (0:read backdrop from EXT pins, 1:output color on EXT pins)
;H -- Sprite Size (0:8x8 pixels, 1:8x16 pixels)
;B -- Background table address (0:$0000, 1:$1000)
;S -- Sprite Pattern Table for 8x8 sprites (0:$0000, 1:$1000) [ignore if 8x16 pixels]
;I -- VRAM adderss increment per CPU read/write of PPUDATA
;N -- Base Nametable Address (0:$2000, 1:$2400, 2:$2800, 3:$2C00)
;N -- Base Nametable Address (0:$2000, 1:$2400, 2:$2800, 3:$2C00)
;----------------------------------------------------------------------------------------;
STA $2000 ;

LDA #%00011110 ;BGRs bMmG (Toggle 0:off, 1:on) -- enable sprites and background and show both in leftmost 8 pixels
;----------------------------------------------------------------------------------------;
;B -- Emphasize Blue
;G -- Emphasize Green
;R -- Emphasize Red
;s -- Show sprites
;b -- Show background
;M -- Show sprites in leftmost 8 pixels of screen
;m -- Show background in leftmost 8 pixels of screen
;G -- Grayscale
;----------------------------------------------------------------------------------------;
STA $2001 ;

Loop: ;USED ONLY FOR TUTORIAL PURPOSES --- infinite loop to prevent accidental access to NMI
JMP Loop ;USED ONLY FOR TUTORIAL PURPOSES --- infinite loop to prevent accidental access to NMI

NMI: ;NMI label
LDA #$02 ;copy sprite data from $0200
STA $4014 ;store copied data in PPU memory for display
RTI ;return from interrupt

PaletteData: ;
;----------------------------------------------------------------------;
;SPRITE PALETTE ADRESSES -- $3F00 -- Universal Background Color
;SPRITE PALETTE ADRESSES -- $3F01 - $3F03 -- Background Palette Color 0
;SPRITE PALETTE ADRESSES -- $3F05 - $3F07 -- Background Palette Color 1
;SPRITE PALETTE ADRESSES -- $3F09 - $3F0B -- Background Palette Color 2
;SPRITE PALETTE ADRESSES -- $3F0D - $3F0F -- Background Palette Color 3
;SPRITE PALETTE ADRESSES -- $3F11 - $3F13 -- Sprite Palatte Color 0
;SPRITE PALETTE ADRESSES -- $3F15 - $3F17 -- Sprite Palatte Color 1
;SPRITE PALETTE ADRESSES -- $3F19 - $3F1B -- Sprite Palatte Color 2
;SPRITE PALETTE ADRESSES -- $3F1D - $3F1F -- Sprite Palatte Color 3
;----------------------------------------------------------------------;
.byte $22, $29, $1A, $0F, $22, $36, $17, $0F, $22, $30, $21, $0F, $22, $27, $17, $0F ;Background Palette Color Data
.byte $22, $16, $27, $18, $22, $1A, $30, $27, $22, $16, $30, $27, $22, $0F, $36, $17 ;Sprite Palette Color Data

SpriteData: ;
;----------------------------------------;
;SPRITE BYTE DATA (4 BYTES)
;1ST BYTE -- Y coordinate on screen
;2ND BYTE -- Tile number on sprite sheet
;3RD BYTE -- !!! UNKNOWN !!!
;4TH BYTE -- X coordinate on screen
;----------------------------------------;
.byte $08, $00, $00, $08 ;
.byte $08, $01, $00, $10 ;
.byte $10, $02, $00, $08 ;
.byte $10, $03, $00, $10 ;
.byte $18, $04, $00, $08 ;
.byte $18, $05, $00, $10 ;
.byte $20, $06, $00, $08 ;
.byte $20, $07, $00, $10 ;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.segment "VECTORS" ;interrupt handlers
.word NMI ;non-maskable interrupt handler
.word Reset ;handle RESET being pressed
;handles special hardware interrupts
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.segment "CHARS" ;where pretty s#%t lives (sprite data)
.incbin "hellomario.chr" ;name of YY-CHR file (*.chr)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Re: Syntax Error (I hope...)

Post by Memblers »

Welcome!

I see a few (potential) problems.

1. CLI is used, but there is no IRQ vector specified. NMI is used, however SEI/CLI has no effect on that. When IRQ is unused, often people either set it to point to an RTI instruction, or point it to the reset vector. The IRQ vector will be used if the CPU happens to run a BRK instruction (SEI/CLI has no effect on that, either).

2. Before you enter Loop, safe practice would have the STA $2000 be the last instruction. As it is, the next instruction is LDA # / STA $2001. Since this NMI routine doesn't save/preserve the registers in this example, if an NMI were to happen, then you may end up writing garbage ($02, in this case) to $2001.

If you can post the .NES file, that would help with debugging. Without knowing exactly what the CHR file has, I can't tell. It could be working but simply showing blank tiles. In that case, check it in Mesen with the sprite viewer.

--- edit ---

OK, I now see the problem! I missed it at first glance, too.

Code: Select all

STA $0200 ;load sprites - store value in $0200
Indexing was forgotten. STA $0200, X is the intended instruction.
StabbyMcStabbStabb
Posts: 3
Joined: Fri May 19, 2023 4:11 pm

Re: Syntax Error (I hope...)

Post by StabbyMcStabbStabb »

I didn't know I could post the actual file. I tried using the debugger but I really don't know what I am doing yet. I'll do that next time. The missing X was the problem. You fixed it. Thank you. As for the stuff that is used that doesn't point to anything.... The narrator of the videos I've been watching said something about that but he said that even though he's not writing that stuff in, it would be good practice to get used to including that stuff (I'm working on learning what all of that means. I don't even know what SEI and that does... it's been too long since i've used hex on anything and having to remember when i see the number 20 (for example) it's most likely 32 (depending on the symbol) is throwing me off. I do have one question though... Too many comments or no?

hellomario.nes
Thank You Again
(40.02 KiB) Downloaded 21 times
hellomario.chr
sprites
(8 KiB) Downloaded 20 times
hellomario.asm
fixed file
(10.23 KiB) Downloaded 20 times
User avatar
Memblers
Site Admin
Posts: 4044
Joined: Mon Sep 20, 2004 6:04 am
Location: Indianapolis
Contact:

Re: Syntax Error (I hope...)

Post by Memblers »

I'd say that is fully commented, heheh. When you're just starting out, one can never have too many comments, if you're referring to them. I think the natural progression is starting out commenting what the code is doing. As you gain experience the what becomes self-evident and can mostly be described with descriptive label and variable names, then comments end up being more about why the code is doing something.
StabbyMcStabbStabb
Posts: 3
Joined: Fri May 19, 2023 4:11 pm

Re: Syntax Error (I hope...)

Post by StabbyMcStabbStabb »

When I first learned C++ I was an arrogant little s#%t and didn't use comments at all (well, rarely, at least). I fell on hard times and... skipping over a woe-is-me story... I didn't code for about 3 years (I was still learning when I stopped). Using the comments as "notes for the big test on Friday" helps me learn and remember. I learned that much when I had to start from almost scratch learning C++ again. Once it's nature to me, my comments will shift to help other programmers get what I was trying to do and what direction I took.

Side Note... I've got a whole new respect for programmers from the NES era. I used to think things would have been simpler then (less bytes, less graphics, etc.) so the code must be super simple..... Haha. NO! 6502 is like trying to decipher the rosetta stone - written backwards - under water - in pitch darkness... Baby want baba :shock:
unregistered
Posts: 1318
Joined: Thu Apr 23, 2009 11:21 pm
Location: cypress, texas

Re: Syntax Error (I hope...)

Post by unregistered »

LDX #$00 ;load sprite data - initialize X register (prepare for sprite palette data loop)

LoadSprites: ;load sprites - label for sprite loop
LDA SpriteData, X ;load sprites - load "SpriteData" into A register then increment X register (SpriteData Array)
STA $0200 ;load sprites - store value in $0200
INX ;load sprites - increment X register
CPX #$20 ;load sprites - compare value X to value 32 (decimal)
BNE LoadSprites ;load sprites - BNE to label
This may help you later…

If you run your loops in reverse, that saves space and time.

i.e. this accomplishes the same thing:

Code: Select all

LDX #$20 ;load sprite data - initialize X register to 32 (decimal) (prepare for sprite palette data loop)

LoadSprites: ;load sprites - label for sprite loop
LDA SpriteData, X ;load sprites - load "SpriteData" into A register then decrement X register (SpriteData Array)
STA $0200, X ;load sprites - store value in $0200
DEX ;load sprites - decrement X register
BNE LoadSprites ;load sprites - BNE to label
See, that code is faster AND takes less ROM space. Since, it’s running in reverse, it’s missing the CMP that was placed before the BNE. A CMP takes 3 cycles of time and 2 bytes of space. You never need a CMP #0 bc a register’s 0 value automatically sets the z flag and so BNE works appropriately.

Yes, 3 cycles is almost 0 cycles, but if you make these small improvements throughout your game, you’ll be very happy. :) I realize that you don’t have a game yet, but learning this early should benefit you.


NOTE: Be careful when reversing loops; this will not work with LoadPalette bc that loop stores to $2007 and $2007 must be written to sequentially bc the PPU always increments $2007 after each write. (Or vertically, if you ever set your PPU to increment the $2007 address vertically after each write.) $0200 is just RAM and therefore, has no preference about writes to it. :)
Fiskbit
Posts: 891
Joined: Sat Nov 18, 2017 9:15 pm

Re: Syntax Error (I hope...)

Post by Fiskbit »

Immediate compares are 2 cycles, not 3, but the in general, saving cycles inside of a loop can be substantial. Just saving a few cycles here or there often isn't worth it because it's so small compared to the 30,000 cycle frame budget, the amount of work to make these tiny optimizations may be a bad trade-off compared to other improvements you could make to your game, and optimizations sometimes reduce clarity and add risk to the code. However, a few cycles across 32 iterations starts to be meaningful and is worth the squeeze.

Over time, though, you'll likely pick up on these strategies to improve performance and start writing things in a more efficient way the first time. In particular, iterating downward where possible is usually the way to go because of the free zero or negative checks.
Post Reply