It is currently Sun Aug 25, 2019 9:48 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 13 posts ] 
Author Message
PostPosted: Sun Aug 11, 2019 6:43 pm 
Offline
User avatar

Joined: Sun Aug 11, 2019 4:36 pm
Posts: 6
Location: Trinidad and Tobago
Hello, I would appreciate any solutions to this.


I only implemented a nametable update. It basically writes a background tile to $2007
every 3 frames; to appear as animated text typing.

All code is in NMI and it works completly fine.

It resets fine when the animation is done but if it was RESET sometime during the animation,
it gives strange rows of $00 tiles on the upper part of the screen.

I used a couple macros to help conserve some development time.

I am also aware of the RESET issues for the NES hardware. Am I using my VBlank right? I just
can't seem to find the issue.

This is most of the important code. Also Forget about the Timer macro; I know its fine.



Code:
  .inesprg 1   ; 1x 16KB PRG code
  .ineschr 1   ; 1x  8KB CHR data
  .inesmap 4 ; Mapper 4 = MMC3
  .inesmir 3   ; Background Mirroring = Vertical with WRAM enabled



;=========VARIABLES==========
  .rsset $0000
backgroundMaxRLE     .rs 1

backgroundHi             .rs 1
backgroundLo             .rs 1
backgroundCounter     .rs 1

textHi                        .rs 1
textLo                        .rs 1
textCounter                .rs 1

textTimer                   .rs 1
textOnce                    .rs 1



  .bank 0
  .org $C000

;=========PALETTE DATA==========
starfoxesPalette:
  .db $3F,$04,$30,$24,  $3F,$3F,$3F,$3F,  $3F,$3F,$3F,$3F,  $3F,$3F,$3F,$3F
  .db $3F,$04,$24,$30,  $3F,$04,$30,$24,  $3F,$3F,$3F,$3F,  $3F,$3F,$3F,$3F



;=========TEXT DATA==========
introText:
.text0
  .db "STARFOXESoPRESENTS>>>"



;=========BACKGROUND DATA==========
backgroundRLE1:
  .db $FE,$2E,$FE,$2E,$FE,$2E,$C6,$2E ;8 BYTES

;ATTRIBUTES AS NORMAL
attributesRLE1:
  .db %00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000
  .db %00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000
  .db %00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000
  .db %00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000
  .db %00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000
  .db %00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000
  .db %00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000
  .db %00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000




;=========MACROS==========
;loadPalette (HIGH BYTE, LOW BYTE, PALETTE, MAX)
  .macro loadPalette
  LDA $2002
  LDA #\1
  STA $2006
  LDA #\2
  STA $2006
  LDX #$00
.Loop\@
  LDA \3,X
  STA $2007
  INX
  CPX #\4
  BNE .Loop\@
  .endm



;loadBackgroundRLE (HIGH BYTE, LOW BYTE, backgroundRLE, MAX BACKGROUND RLE, attributesRLE)
  .macro loadBackgroundRLE
.loadBackground\@
  LDA $2002
  LDA #\1
  STA $2006
  LDA #\2
  STA $2006

  LDX #$FF
.loadDataRLE\@
  INX
  LDA \3,X
  STA backgroundMaxRLE
  INX

  LDY #$00
.loadBackgroundLoop\@
  LDA \3,X
  STA $2007
  INY
  CPY backgroundMaxRLE
  BNE .loadBackgroundLoop\@

.checkEndOfDataRLE\@
  CPX #\4
  BCC .loadDataRLE\@

  LDX #$00
.loadAttributes\@
  LDA \5,X
  STA $2007
  INX
  CPX #$40
  BNE .loadAttributes\@
  .endm



;typeTextStart (HIGH BYTE, LOW BYTE)
  .macro typeTextStart

.checkTextOnce\@
  LDA textOnce
  BNE .typeTextStartDone\@

.setTextVariables\@
  LDA #\1
  STA textHi
  LDA #\2
  STA textLo
  LDA #$00
  STA textCounter
  LDA #$00
  STA textTimer

.switchTextOnce\@
  INC textOnce

.typeTextStartDone\@
  .endm



;typeTextUpdate (textData, FRAME, INDENT BYTE, MAX)
  .macro typeTextUpdate

.checkTextFrame\@
  LDA textTimer
  CMP #\2
  BNE .typeTextUpdateDone\@

.checkTextMax\@
  LDA textCounter
  CMP #\4
  BEQ .typeTextUpdateDone\@

.loadText\@
  LDA $2002
  LDA textHi
  STA $2006
  LDA textLo
  STA $2006

.transferCounterToX\@
  LDA textCounter
  TAX

.updateTextCounter\@
  INC textCounter

.convertByteToASCII\@
  LDA \1,X
  SEC
  SBC #$30
  STA \1,X

.writeTextByte\@
  STA $2007

.updateTextLowByte\@
  INC textLo
  LDA textLo
  AND #%00011111
  BNE .updateLowByteDone\@
  LDA textLo
  CLC
  ADC #\3
  STA textLo
.updateLowByteDone\@

.updateTextHighByte\@
  LDA textLo
  CMP #\3
  BNE .updateHighByteDone\@
  INC textHi
.updateHighByteDone\@

.resetTextTimer\@
  LDA #$00
  STA textTimer

.typeTextUpdateDone\@
  .endm



;Timer (INDEX, timer, START/STOP/RESET - 2/1/0 )
  .macro Timer

  LDX #\1
.checkTimerReset\@
  LDA #\3
  BEQ .resetTimer\@

.checkTimerStop\@
  LDA #\3
  CMP #$01
  BEQ .stopTimer\@

.checkTimerUpdate\@
  LDA #\3
  CMP #$02
  BEQ .updateTimer\@

.updateTimer\@
  INC \2,X
  JMP .stopTimer\@

.resetTimer\@
  LDA #$00
  STA \2,X

.stopTimer\@
  .endm



  .bank 1
  .org $E000

Reset:
  SEI
  CLD
  LDX #$40
  STX $4017
  LDX #$FF
  TXS
  INX
  STX $2000
  STX $2001
  STX $4010

vBlankWait0:
  BIT $2002
  BPL vBlankWait0

clearMemory:
  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
  INX
  BNE clearMemory

vBlankWait1:
  BIT $2002
  BPL vBlankWait1





INIT:

vBlankWait2:
  BIT $2002
  BPL vBlankWait2

  loadBackgroundRLE $20,$00,backgroundRLE1,$08,attributesRLE1 ;LOADS A FULL NAMETABLE OF BLACK TILES.
  loadPalette $3F,$00,starfoxesPalette,$20                                     ;LOADS THE PALETTE.

INITDONE:

vBlankWait3:
  BIT $2002
  BPL vBlankWait3

  LDA #%10010000
  STA $2000
  LDA #%00011110
  STA $2001
  LDA #$00
  STA $2006
  STA $2005
  STA $2005
  STA $2006

gameLoop:
  JMP gameLoop

NMI:
  LDA #$00
  STA $2003
  LDA #$02
  STA $4014

drawText:
  typeTextStart $22,$08
  typeTextUpdate introText,$03,$00,$15
  Timer $00,textTimer,$02

NMIDONE:
  LDA #%10010000
  STA $2000
  LDA #%00011110
  STA $2001
  LDA #$00
  STA $2006
  STA $2006
  STA $2005
  STA $2005

  .include "Controllers.asm"

  RTI

;===VECTORS===
  .org $FFFA
  .dw NMI
  .dw Reset
  .dw 0



  .bank 2
  .org $0000
  .include "Graphics.chr"


Attachments:
platformer2.gif
platformer2.gif [ 5 KiB | Viewed 704 times ]
platformer22.gif
platformer22.gif [ 6.24 KiB | Viewed 704 times ]

_________________
Shoot for the Stars***


Last edited by Starfoxes on Sun Aug 11, 2019 11:42 pm, edited 1 time in total.
Top
 Profile  
 
PostPosted: Sun Aug 11, 2019 8:27 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4208
Location: A world gone mad
Work out what's going on in an emulator with a good debugger like Mesen. You can find out what's happening or not happening. You will eventually find the part where the PPU begins drawing the erroneous data. I suspect several things, but trying to cover them all is not particularly a good use of my time when you yourself should be able to debug this with good tooling. You will find the Event Viewer particularly helpful, combined with all the other PPU details. Maybe you're running out of VBlank time in NMI. Maybe you're tweaking PPU registers without resetting internal PPU latches (i.e. when tweaking $2000/2002/2005). It could also be something as simple as you not initialising ZP/RAM values on reset, thus subjecting yourself to your own bug (you have omitted a lot of code relevant to this).


Top
 Profile  
 
PostPosted: Sun Aug 11, 2019 8:38 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11403
Location: Rio de Janeiro - Brazil
Maybe I'm missing something, but where are your interrupt vectors?


Top
 Profile  
 
PostPosted: Sun Aug 11, 2019 8:48 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4208
Location: A world gone mad
All of his vectors were omitted as well. We have to assume Reset and NMI are pointed to by the correct vectors.


Top
 Profile  
 
PostPosted: Sun Aug 11, 2019 9:18 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11403
Location: Rio de Janeiro - Brazil
Oh, I realize now that some things from the main file were omitted. Well, I don't see a full name table wipe anywhere before the text is displayed, so I don't know if it's been omitted as well or if it's really missing. The problem with posting partial code because you think certain parts are "safe" or "fine" is that there are no guarantees of that. Bugs can be where we least expect. Anyway, if you're not clearing the name table during the initialization of your intro screen, it's bound to have garbage in it as you're basically displaying uninitialized RAM.

Aside from that, there's one other obvious issue I see (which isn't related to the bug you're trying to fix) with how A, X and Y are pushed to the stack and later restored in the NMI handler: this is supposed to be done in order to preserve the values of the 3 registers so that when the NMI or IRQ finishes and returns to the main thread, the logic there can pick up from where it left off, but you are altering the contents of the accumulator before saving it and after restoring it, effectively corrupting it. This isn't causing any problems for now because your main thread is just an empty loop that's not using the registers at all, but if you do put any code there in the future you may have problems.

So yeah, either fix it so that the register pushing/popping are the very first and very last actions in your NMI handler, respectively, or get rid of the pushing/popping altogether (and get a tiny bit of extra vblank time! :lol:) if you plan to continue with the "all in NMI" approach through the rest of the development and the main loop will remain empty forever.


Top
 Profile  
 
PostPosted: Mon Aug 12, 2019 12:01 am 
Offline
User avatar

Joined: Sun Aug 11, 2019 4:36 pm
Posts: 6
Location: Trinidad and Tobago
koitsu wrote:
Work out what's going on in an emulator with a good debugger like Mesen. You can find out what's happening or not happening. You will eventually find the part where the PPU begins drawing the erroneous data. I suspect several things, but trying to cover them all is not particularly a good use of my time when you yourself should be able to debug this with good tooling. You will find the Event Viewer particularly helpful, combined with all the other PPU details. Maybe you're running out of VBlank time in NMI. Maybe you're tweaking PPU registers without resetting internal PPU latches (i.e. when tweaking $2000/2002/2005). It could also be something as simple as you not initialising ZP/RAM values on reset, thus subjecting yourself to your own bug (you have omitted a lot of code relevant to this).


I have quite a low level understanding of the debugger tools and I'm glad you put that to my interest. And yes, my apologies to this confusion. I have edited it with all the code needed to run the same animation including Variables, Vectors, and other data. I'd zeroed out everything in RAM; I checked and it still had more cycles to execute in VBlank, I am confident I still have more time in NMI. Though I suspect it's something to do with the PPU registers what you just stated. I am using Fceux actually. The code runs fine; even when it's powered on/off, but the issue is this strange glitch that happens after a reset.

_________________
Shoot for the Stars***


Top
 Profile  
 
PostPosted: Mon Aug 12, 2019 12:25 am 
Offline
User avatar

Joined: Sun Aug 11, 2019 4:36 pm
Posts: 6
Location: Trinidad and Tobago
tokumaru wrote:
Oh, I realize now that some things from the main file were omitted. Well, I don't see a full name table wipe anywhere before the text is displayed, so I don't know if it's been omitted as well or if it's really missing. The problem with posting partial code because you think certain parts are "safe" or "fine" is that there are no guarantees of that. Bugs can be where we least expect. Anyway, if you're not clearing the name table during the initialization of your intro screen, it's bound to have garbage in it as you're basically displaying uninitialized RAM.

Aside from that, there's one other obvious issue I see (which isn't related to the bug you're trying to fix) with how A, X and Y are pushed to the stack and later restored in the NMI handler: this is supposed to be done in order to preserve the values of the 3 registers so that when the NMI or IRQ finishes and returns to the main thread, the logic there can pick up from where it left off, but you are altering the contents of the accumulator before saving it and after restoring it, effectively corrupting it. This isn't causing any problems for now because your main thread is just an empty loop that's not using the registers at all, but if you do put any code there in the future you may have problems.

So yeah, either fix it so that the register pushing/popping are the very first and very last actions in your NMI handler, respectively, or get rid of the pushing/popping altogether (and get a tiny bit of extra vblank time! :lol:) if you plan to continue with the "all in NMI" approach through the rest of the development and the main loop will remain empty forever.


I always wondered why to push all the registers onto the stack. I just followed it from an article that spoke about how registers can change during IRQ interrupts or so but I still didn't really catch the use; I removed it now, but thanks for clearing it up :) . And yes, you are right about how bugs can be almost anywhere in the code so its a good practice avoid omissions. Oh and I am testing my code in NMI first then seperate the logic code into the main excepting the graphic code. It would most likely appear to be an NMI problem but I know I am below the instruction limit. The code is edited now with the background data included but in an RLE format, it writes the 960 bytes to the screen along with 64 attribute bytes. Again it works fine but not when reset. Thanks in return for any observations.

_________________
Shoot for the Stars***


Top
 Profile  
 
PostPosted: Mon Aug 12, 2019 12:55 am 
Offline
User avatar

Joined: Sun Aug 11, 2019 4:36 pm
Posts: 6
Location: Trinidad and Tobago
Also, This is a pretty large chunk of code. It really don't seem practical for any individual could go through all of this especially for the macros that was written for I to mostly understand. I was expecting an answer like a hardware bug or something. But no worries, I will take the debugger approach and put an update on this weird problem in the mean while.


*Update

Okay nvm, I got it to work.

My data was initially at bank $A000; it worked when I moved it to $C000. :lol:
But how did this arrangement made a difference?

_________________
Shoot for the Stars***


Top
 Profile  
 
PostPosted: Mon Aug 12, 2019 4:01 am 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4208
Location: A world gone mad
Starfoxes wrote:
I have quite a low level understanding of the debugger tools and I'm glad you put that to my interest. And yes, my apologies to this confusion. I have edited it with all the code needed to run the same animation including Variables, Vectors, and other data. I'd zeroed out everything in RAM; I checked and it still had more cycles to execute in VBlank, I am confident I still have more time in NMI. Though I suspect it's something to do with the PPU registers what you just stated. I am using Fceux actually. The code runs fine; even when it's powered on/off, but the issue is this strange glitch that happens after a reset.

If you have low-level understanding of debugger tools, then you should be able to answer this one yourself. FCEUX can provide some good insights into what's going on by simply stepping through instructions, but Mesen provides some even more useful features that might help in cases like this. What you need is a tool that can show you what is going on on both a CPU instruction level as well as the PPU level -- and that doesn't mean "show me the nametable", it means showing actual PPU internals. Enjoy.

P.S. -- You should be able to answer your own question, re: why moving data around somehow caused your problem. Only you know (and have all of) your code.


Top
 Profile  
 
PostPosted: Mon Aug 12, 2019 7:55 pm 
Offline
User avatar

Joined: Sun Aug 11, 2019 4:36 pm
Posts: 6
Location: Trinidad and Tobago
Will check it out. Thanks for the link.

_________________
Shoot for the Stars***


Top
 Profile  
 
PostPosted: Mon Aug 12, 2019 9:08 pm 
Online
Site Admin
User avatar

Joined: Mon Sep 20, 2004 6:04 am
Posts: 3712
Location: Indianapolis
Starfoxes wrote:
My data was initially at bank $A000; it worked when I moved it to $C000. :lol:
But how did this arrangement made a difference?


My guess from looking at the header is because it's a 16kB program using MMC3 mapper. In that case you better stick to $C000+. Unless there are mapper writes in there, and I didn't see them, the bank selects for $8000-$BFFF would be in an undefined state.


Top
 Profile  
 
PostPosted: Mon Aug 12, 2019 10:04 pm 
Offline
User avatar

Joined: Sat Feb 12, 2005 9:43 pm
Posts: 11403
Location: Rio de Janeiro - Brazil
Keep in mind that with the MMC3, the only bank guaranteed to be mapped to a fixed location on power up and reset is the last bank, mapped to $E000-$FFFF, where the initialization code and CPU vectors must reside. The other "fixed" bank (the second to last one) can be mapped to either $8000-$9FFF or $C000-$DFFF depending on the PRG-ROM banking mode of your choice, so until you've properly set that (via a $8000 write), you shouldn't try to access that bank. Always initialize all relevant mapper registers, because they boot up with unknown values and also have unknown values on reset due to you using the mapper before the reset.

Also note that simply writing ".org $C000" in the source file doesn't cause that bank to actually be mapped to the specified address. What decides which banks are mapped where is the mapper, not the assembler, which uses the .org directive only to assign the correct values to labels so that the assembled code works when mapped to where the mapper will put it. If there's a mismatch between the address where the assembler was told the code would be and the address where it will actually be mapped to, the CPU will certainly try to access the wrong bank(s), causing the program to crash or use garbage data.


Top
 Profile  
 
PostPosted: Tue Aug 13, 2019 9:26 pm 
Offline
User avatar

Joined: Sun Aug 11, 2019 4:36 pm
Posts: 6
Location: Trinidad and Tobago
Memblers wrote:
Starfoxes wrote:
My data was initially at bank $A000; it worked when I moved it to $C000. :lol:
But how did this arrangement made a difference?


My guess from looking at the header is because it's a 16kB program using MMC3 mapper. In that case you better stick to $C000+. Unless there are mapper writes in there, and I didn't see them, the bank selects for $8000-$BFFF would be in an undefined state.


I know NESASM arranges data into 8K banks and you will have to remember to divide or multiply some mapper functions by 2. That was a foolish mistake for I to ignore and thanks for this observation. :lol:


tokumaru wrote:
Keep in mind that with the MMC3, the only bank guaranteed to be mapped to a fixed location on power up and reset is the last bank, mapped to $E000-$FFFF, where the initialization code and CPU vectors must reside. The other "fixed" bank (the second to last one) can be mapped to either $8000-$9FFF or $C000-$DFFF depending on the PRG-ROM banking mode of your choice, so until you've properly set that (via a $8000 write), you shouldn't try to access that bank. Always initialize all relevant mapper registers, because they boot up with unknown values and also have unknown values on reset due to you using the mapper before the reset.

Also note that simply writing ".org $C000" in the source file doesn't cause that bank to actually be mapped to the specified address. What decides which banks are mapped where is the mapper, not the assembler, which uses the .org directive only to assign the correct values to labels so that the assembled code works when mapped to where the mapper will put it. If there's a mismatch between the address where the assembler was told the code would be and the address where it will actually be mapped to, the CPU will certainly try to access the wrong bank(s), causing the program to crash or use garbage data.


This was quite some handy info once again tokumaru. It explained why I ended up with those graphical glitches; most likely I would turn my attention to the PPU and my Vertical Blanking time but it was not so in my case. It revealed that my main problem was to properly initialize this mapper to avoid this garbage data in my banks. Those were quite simple but very important issues I wasn't aware of, but I'm seeing that they could cost good development time during a dry run.

Thanks to the three of you for checking under the hood in all of this :D . From this topic; I learnt the importance of debugging and initializing mappers. It helps in making my homebrew (Cave Story/IWBTG like) flawless. I'm making sure there is minimal lag and no graphical glitches (Like background jumping etc.).

_________________
Shoot for the Stars***


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: Bing [Bot], Google [Bot] and 5 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