Finish displaying the text

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Finish displaying the text
by on (#235038)
Hello! I've finally got sprites showing up on the screen! :D I also know how to make objects move on the screen as well, just by adding code in the VBlank. Now, I have a new problem; I have more than 8 sprites I want to display on the screen but the emulator I'm using won't display the last 8 sprites. I'm trying to have the emulator display the phrase, "A winner is you" based on an internet meme. However, so far, I've only got it to write "A winner" and that's it. Am I missing something? Here's the code for reference:
Code:
.segment "HEADER"
    .byte "NES"
    .byte $1a
    .byte $02
    .byte $01
    .byte %00000000
    .byte $00
    .byte $00
    .byte $00
    .byte $00
    .byte $00,$00,$00,$00,$00

.segment "STARTUP"
.segment "CODE"

WAITVBLANK:
:
    BIT $2002
    BPL :-
    RTS

RESET:
  SEI          ; disable IRQs
  CLD          ; disable decimal mode
  LDX #$40
  STX $4017    ; disable APU frame IRQ
  LDX #$FF
  TXS          ; Set up stack
  INX          ; now X = 0
  STX $2000    ; disable NMI
  STX $2001    ; disable rendering
  STX $4010    ; disable DMC IRQs

  JSR WAITVBLANK

clrmem:
  LDA #$00
  STA $0000, x
  STA $0100, x
  STA $0200, x
  STA $0400, x
  STA $0500, x
  STA $0600, x
  STA $0700, x
  STA $0300, x
  INX
  BNE clrmem

JSR WAITVBLANK
JSR TEXT
LDA #%10010000
STA $2000
LDA #%00010100
STA $2001


Forever:
JMP Forever

TEXT:
LDA #$08
STA $10

LDA #$00
STA $11

LDX #$00
LDY #$00

LoadTEXT:
JSR LoadPalette
LDA #$08
STA $0200,x      ;Y Position
INX
JSR LoadSpriteTile
LDA #$02
STA $0200,x      ;Palette Number
INX
JSR LoadXPosition
INY
CPY #$0F
BNE LoadTEXT
RTS


LoadSpriteTile:
LDA $11
STA $0200,x      ;Sprite Tile Number
INC $11
INX
RTS

LoadXPosition:
LDA $10
STA $0200,x      ;X Position
LDA $10
CLC
ADC #$08
STA $10
INX
RTS

LoadPalette:
LDA #$3F
STA $2006
LDA #$00
STA $2006
LDA #$0F
STA $2007
LDA #$27
STA $2007
LDA #$37
STA $2007
RTS

VBLANK:         ;What should happen during a v-blank or nmi?
LDA #$00      
STA $2003
LDA #$02
STA $4014
RTI


.segment "VECTORS"
    .word VBLANK
    .word RESET
    .word 0

.segment "CHARS" 
   .incbin "ball.chr"


the "ball.chr" now consists of text. The file is attached here.
Any ideas on how to get the emulator to display the rest of the text? Thank you!

P.S.
The emulator I'm using is FCEUX.
Re: Finish displaying the text
by on (#235039)
Text is almost never displayed with sprites on the NES, because the PPU can only show 8 sprites per scanline. If you put more than 8, only the ones with highest priorities (i.e. lower OAM indices) will show.

Some emulators have an option to display extra sprites, but the real PPU can never display more than 8 per scanline.

Games normally get around this limit by cycling sprite priorities each frame, so that different sprites are dropped each time, resulting in flickering rather than total drop out, but having text flicker wouldn't look good at all, so with the exception of short words like "pause", text is normally rendered on the background.
Re: Finish displaying the text
by on (#235040)
Are you borrowing code from a tutorial?

By the way, you can name variables, which I recommend. Using "$10" as a variable is error prone, is hard to read, and hard to search code to find uses of.

Also, there is an 8 Sprite limit, horizontally, on the NES. Any more than that on a given horizontal line, and they won't display (the 9th Sprite will disappear).

It's generally not a good idea to use sprites for text, for this reason.
Re: Finish displaying the text
by on (#235045)
The text "A winner" has exactly 8 characters (including the space), so I'm pretty sure that this is just a case of the PPU dropping the extra sprites. There's not much you can do to fix this... You can resort to sprite cycling, by (pseudo)randomizing the order in which the sprites are written to $0200-$02FF, which should make the entire text visible, but flickery, which is terrible for text.

The proper solution would be to write text using the background instead. Sprites are meant for moving objects (characters, projectiles, etc.).
Re: Finish displaying the text
by on (#235056)
You could just put the "is you" part on a second line.
Re: Finish displaying the text
by on (#235070)
tokumaru wrote:
The text "A winner" has exactly 8 characters (including the space), so I'm pretty sure that this is just a case of the PPU dropping the extra sprites. There's not much you can do to fix this... You can resort to sprite cycling, by (pseudo)randomizing the order in which the sprites are written to $0200-$02FF, which should make the entire text visible, but flickery, which is terrible for text.

The proper solution would be to write text using the background instead. Sprites are meant for moving objects (characters, projectiles, etc.).


Ah, I see. But, how do you make the emulator recognize the tiles as part of the background?
Re: Finish displaying the text
by on (#235071)
You write to a "nametable" in the PPU. I like to call it a tile map.

PPU addresses $2000 - 23ff are for nametable #0. This is the background you want to start with.

You would write to it the same way you write to the palette. 2 writes to register 2006, and then the tile numbers to 2007. This also needs to be done during v-blank, OR, while the screen is off.

lda #$21
sta $2006
lda #$40 ;ppu address 2140
sta $2006
lda #1 ;tile #1
sta $2007

Note that writing to the nametable will change the scroll registers, so after all changes are made, write 2x to the 2005 scroll registers, and once again to the 2000 register.

lda #0
sta $2005
sta $2005
lda #$90 ; or lda #$88 *
sta $2000

*note the 2 least significant bits 00 indicates that we want to see the #0 nametable.
Re: Finish displaying the text
by on (#235076)
I got it all now, thanks, you guys! :D
Re: Finish displaying the text
by on (#236139)
Uh oh. I suddenly forgot how to display the text via background sprites! This is my code:
Code:
.segment "HEADER"
    .byte "NES"
    .byte $1a
    .byte $02
    .byte $01
    .byte %00000000
    .byte $00
    .byte $00
    .byte $00
    .byte $00
    .byte $00,$00,$00,$00,$00
   
.segment "STARTUP"
.segment "BSS"
indexy_thing:
   .res 1
.segment "ZEROPAGE"
.segment "RODATA"
Text:
   .byte $04,$0F,$0E,$1B,$14,$00,$06,$0F,$12,$07,$05,$14,$1C
Palette:
   .byte $0F,$30,$31,$32

.segment "CODE"

WAITVBLANK:
   BIT $2002
   BPL WAITVBLANK
   RTS
   
RESET:
  SEI          ; disable IRQs
  CLD          ; disable decimal mode
  LDX #$40
  STX $4017    ; disable APU frame IRQ
  LDX #$FF
  TXS          ; Set up stack
  INX          ; now X = 0
  STX $2000    ; disable NMI
  STX $2001    ; disable rendering
  STX $4010    ; disable DMC IRQs

  JSR WAITVBLANK

clrmem:
  LDA #$00
  STA $0000, x
  STA $0100, x
  STA $0200, x
  STA $0400, x
  STA $0500, x
  STA $0600, x
  STA $0700, x
  STA $0300, x
  INX
  BNE clrmem

JSR WAITVBLANK

Main:
JSR LoadText
JSR TurnOnScreen
JSR Loopy

Loopy:
   JMP Loopy

TurnOnScreen:
   LDA #%10010000
   STA $2000
   LDA #%00011000
   STA $2001
   RTS

LoadText:
   LoadTextPalette:
      LDA #$3F
      STA $2006
      LDA #$00
      STA $2006
      STA indexy_thing
      PaletteLoop:
         LDA indexy_thing
         CMP #$04
         BCS LoadTextData
         LDY indexy_thing
         LDA Palette,y
         STA $2007
         INC indexy_thing
         JMP PaletteLoop
   LoadTextData:
      LDA #$21
      STA $2006
      LDA #$C1
      STA $2006
      LDA #$00
      STA indexy_thing
      TextDataLoop:
         LDA indexy_thing
         CMP #$0F
         BCS ResetScroll
         LDY indexy_thing
         LDA Text,y
         STA $2007
         INC indexy_thing
         JMP TextDataLoop

ResetScroll:
   LDA #$00
   STA $2006
   STA $2006
   STA $2005
   STA $2005
   RTS

VBLANK:
LDA #$00
STA $2003
LDA #$02
STA $4014
JSR ResetScroll
RTI

.segment "VECTORS"
    .word VBLANK
    .word RESET
    .word 0
   
.segment "CHARS"
   .incbin "ball.chr"   ;CHRS are files containing sprites


I need help again!
Re: Finish displaying the text
by on (#236144)
What's the error you're getting?

Also, let me help you clean up a portion of your code to make it more readable and efficient:
Code:
...

LoadText:

   LoadTextPalette:
      lda #$3f      ; the compiler is not case sensitive.
      sta $2006      ; writing in lowercase will save time, but in the end
      ldy #$00      ; it's just a preference.
      sty $2006      ; instead of using your variable "indexy_thing",
                  ; just use either register x or y.
                  ; they are meant to be used for this kind of stuff.
                  ; it will save you some RAM, ROM space and CPU cycles.
      PaletteLoop:
         lda Palette,y
         sta $2007
         iny         ; increment y directly and
         cpy #$04   ; compare y with the end value of your loop
         bne PaletteLoop

   LoadTextData:
      lda #$21
      sta $2006
      lda #$c1
      sta $2006
      ldy #$00
      TextDataLoop:
         lda Text,y
         sta $2007
         iny
         cpy #$0f
         bne TextDataLoop
         
   ResetScroll:
   ...


EDIT:

I just noticed a flaw in your NMI routine "VBLANK" that might not cause an issue now but surely will once you start adding code in your "Loopy" routine.
The fault is that you NMI does not push and restore register values. The reason why that is important is because the NMI can happen while any of your other code is running - it happens once every end of a frame. So if you have some code running in "Loopy" for instance when the end of a frame occurs, the CPU will run the code in your NMI and then return to where it left off in the code that was running before. That means whatever values you had loaded in your registers before NMI will most likely not be the same anymore, as your code in NMI will have altered them. To get around this you must put registers A, Y and X on the stack and then restore them at the end of NMI like this:

Code:
VBLANK:

PHA      ; first "push" register A (the accumulator) to the stack.
TXA      ; transfer x to a.
PHA      ; push what was in x.
TYA      ; transfer y to a.
PHA      ; push what was in y.

LDA #$00
STA $2003
LDA #$02
STA $4014
JSR ResetScroll

PLA      ; now we're retrieving ("pulling") the value that used to be in register Y
      ; pushing and pulling from the stack is like piling a
      ; bunch of papers on top of each other.
      ; So the last value pushed will be the first to be pulled.
TYA      ; transfer a to y (value ends up in a).
PLA      ; pull what used to be in x.
TXA      ; transfer a to x.
PLA      ; finally we pull the value that used to be in the accumulator (a).

RTI
Re: Finish displaying the text
by on (#236217)
I'm still having major difficulty pulling this off. I did it before, but I can't remember!
I've tried this code using the last person's suggestions:
Code:
.segment "HEADER"
    .byte "NES"
    .byte $1a
    .byte $02
    .byte $01
    .byte %00000000
    .byte $00
    .byte $00
    .byte $00
    .byte $00
    .byte $00,$00,$00,$00,$00
   
.segment "STARTUP"
.segment "BSS"
indexy_thing:
   .res 1
.segment "ZEROPAGE"
.segment "RODATA"
Text:
   .byte $04,$0F,$0E,$1B,$14,$00,$06,$0F,$12,$07,$05,$14,$1C
Palette:
   .byte $0F,$30,$31,$32

.segment "CODE"

WAITVBLANK:
   BIT $2002
   BPL WAITVBLANK
   RTS
   
RESET:
  SEI          ; disable IRQs
  CLD          ; disable decimal mode
  LDX #$40
  STX $4017    ; disable APU frame IRQ
  LDX #$FF
  TXS          ; Set up stack
  INX          ; now X = 0
  STX $2000    ; disable NMI
  STX $2001    ; disable rendering
  STX $4010    ; disable DMC IRQs

  JSR WAITVBLANK

clrmem:
  LDA #$00
  STA $0000, x
  STA $0100, x
  STA $0200, x
  STA $0400, x
  STA $0500, x
  STA $0600, x
  STA $0700, x
  STA $0300, x
  INX
  BNE clrmem

JSR WAITVBLANK

Main:
JSR LoadText
JSR TurnOnScreen
JSR Loopy

Loopy:
   JMP Loopy

TurnOnScreen:
   LDA #%10010000
   STA $2000
   LDA #%00011000
   STA $2001
   RTS

LoadText:

   LoadTextPalette:
      lda #$3f      ; the compiler is not case sensitive.
      sta $2006      ; writing in lowercase will save time, but in the end
      ldy #$00      ; it's just a preference.
      sty $2006      ; instead of using your variable "indexy_thing",
                  ; just use either register x or y.
                  ; they are meant to be used for this kind of stuff.
                  ; it will save you some RAM, ROM space and CPU cycles.
      PaletteLoop:
         lda Palette,y
         sta $2007
         iny         ; increment y directly and
         cpy #$04   ; compare y with the end value of your loop
         bne PaletteLoop

   LoadTextData:
      lda #$21
      sta $2006
      lda #$c1
      sta $2006
      ldy #$00
      TextDataLoop:
         lda Text,y
         sta $2007
         iny
         cpy #$0f
         bne TextDataLoop

ResetScroll:
   LDA #$00
   STA $2006
   STA $2006
   STA $2005
   STA $2005
   RTS

VBLANK:
PHA      ; first "push" register A (the accumulator) to the stack.
TXA      ; transfer x to a.
PHA      ; push what was in x.
TYA      ; transfer y to a.
PHA      ; push what was in y.

LDA #$00
STA $2003
LDA #$02
STA $4014
JSR ResetScroll

PLA      ; now we're retrieving ("pulling") the value that used to be in register Y
      ; pushing and pulling from the stack is like piling a
      ; bunch of papers on top of each other.
      ; So the last value pushed will be the first to be pulled.
TYA      ; transfer a to y (value ends up in a).
PLA      ; pull what used to be in x.
TXA      ; transfer a to x.
PLA      ; finally we pull the value that used to be in the accumulator (a).

RTI

.segment "VECTORS"
    .word VBLANK
    .word RESET
    .word 0
   
.segment "CHARS"
   .incbin "ball.chr"   ;CHRS are files containing sprites


and it still doesn't work. What am I doing wrong? Am I directing the system to the wrong nametable? Why can't it just simply put a single background sprite without so much arduousness?
Re: Finish displaying the text
by on (#236220)
Quote:
single background sprite


I know that on some modern platforms they refer to any 2d picture element as a "sprite", in the NES it has a specific use, which is not background tiles, but 8x8 foreground objects that move independently of the background.

I'm not sure what you are doing wrong. Maybe you should post the .nes file.
Re: Finish displaying the text
by on (#236221)
The "ball.chr" file you're using for tiles is almost all empty, that's the problem. When compiled with the chr-rom from smb1 tiles show up just fine. :)
Also, I think you might be confusing the tile set used for sprites with the one used for background. Your code is expecting background tiles to be in the second tile set, but that's where you have your ball sprite. You can fix this by changing some bits of what you write to $2000. https://wiki.nesdev.com/w/index.php/PPU_registers
Code:
7  bit  0
---- ----
VPHB SINN
|||| ||||
|||| ||++- Base nametable address
|||| ||    (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)
|||| |+--- VRAM address increment per CPU read/write of PPUDATA
|||| |     (0: add 1, going across; 1: add 32, going down)
|||| +---- Sprite pattern table address for 8x8 sprites
||||       (0: $0000; 1: $1000; ignored in 8x16 mode)
|||+------ Background pattern table address (0: $0000; 1: $1000)
||+------- Sprite size (0: 8x8 pixels; 1: 8x16 pixels)
|+-------- PPU master/slave select
|          (0: read backdrop from EXT pins; 1: output color on EXT pins)
+--------- Generate an NMI at the start of the
           vertical blanking interval (0: off; 1: on)

Tompis1995 wrote:
Why can't it just simply put a single background sprite without so much arduousness?

I totally feel your frustration but NES development is all about building things from scratch. You will have to get used to things being hard and slow most of the time and you will find yourself reading the nesdev wiki a lot. I know it can be super confusing when you first start out but the good thing is it gets easier... until you run into some aspects of the NES you haven't tried your hand at before. :D
Re: Finish displaying the text
by on (#236232)
pwnskar wrote:
The "ball.chr" file you're using for tiles is almost all empty, that's the problem. When compiled with the chr-rom from smb1 tiles show up just fine. :)
Also, I think you might be confusing the tile set used for sprites with the one used for background. Your code is expecting background tiles to be in the second tile set, but that's where you have your ball sprite. You can fix this by changing some bits of what you write to $2000. https://wiki.nesdev.com/w/index.php/PPU_registers
Code:
7  bit  0
---- ----
VPHB SINN
|||| ||||
|||| ||++- Base nametable address
|||| ||    (0 = $2000; 1 = $2400; 2 = $2800; 3 = $2C00)
|||| |+--- VRAM address increment per CPU read/write of PPUDATA
|||| |     (0: add 1, going across; 1: add 32, going down)
|||| +---- Sprite pattern table address for 8x8 sprites
||||       (0: $0000; 1: $1000; ignored in 8x16 mode)
|||+------ Background pattern table address (0: $0000; 1: $1000)
||+------- Sprite size (0: 8x8 pixels; 1: 8x16 pixels)
|+-------- PPU master/slave select
|          (0: read backdrop from EXT pins; 1: output color on EXT pins)
+--------- Generate an NMI at the start of the
           vertical blanking interval (0: off; 1: on)

Tompis1995 wrote:
Why can't it just simply put a single background sprite without so much arduousness?

I totally feel your frustration but NES development is all about building things from scratch. You will have to get used to things being hard and slow most of the time and you will find yourself reading the nesdev wiki a lot. I know it can be super confusing when you first start out but the good thing is it gets easier... until you run into some aspects of the NES you haven't tried your hand at before. :D


Thanks! And I finally figured it out! I moved the sprites in the chr file from $0000 to $1000 and it worked!