Finish displaying the text

A place for your artistic side. Discuss techniques and tools for pixel art on the NES, GBC, or similar platforms.

Moderator: Moderators

Post Reply
Tompis1995
Posts: 34
Joined: Fri Feb 22, 2019 10:05 am

Finish displaying the text

Post by Tompis1995 »

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: Select all

.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.
Attachments
ball.chr
(8 KiB) Downloaded 408 times
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Finish displaying the text

Post by tokumaru »

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.
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Finish displaying the text

Post by dougeff »

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.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Finish displaying the text

Post by tokumaru »

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.).
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Finish displaying the text

Post by rainwarrior »

You could just put the "is you" part on a second line.
Tompis1995
Posts: 34
Joined: Fri Feb 22, 2019 10:05 am

Re: Finish displaying the text

Post by Tompis1995 »

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?
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Finish displaying the text

Post by dougeff »

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.
nesdoug.com -- blog/tutorial on programming for the NES
Tompis1995
Posts: 34
Joined: Fri Feb 22, 2019 10:05 am

Re: Finish displaying the text

Post by Tompis1995 »

I got it all now, thanks, you guys! :D
Tompis1995
Posts: 34
Joined: Fri Feb 22, 2019 10:05 am

Re: Finish displaying the text

Post by Tompis1995 »

Uh oh. I suddenly forgot how to display the text via background sprites! This is my code:

Code: Select all

.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!
pwnskar
Posts: 119
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: Finish displaying the text

Post by pwnskar »

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: Select all

...

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: Select all

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
Tompis1995
Posts: 34
Joined: Fri Feb 22, 2019 10:05 am

Re: Finish displaying the text

Post by Tompis1995 »

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: Select all

.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?
User avatar
dougeff
Posts: 3078
Joined: Fri May 08, 2015 7:17 pm

Re: Finish displaying the text

Post by dougeff »

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.
nesdoug.com -- blog/tutorial on programming for the NES
pwnskar
Posts: 119
Joined: Tue Oct 16, 2018 5:46 am
Location: Gothenburg, Sweden

Re: Finish displaying the text

Post by pwnskar »

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: Select all

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
Attachments
2019-03-18 20_57_29-Window.png
2019-03-18 21_11_39-Window.png
Tompis1995
Posts: 34
Joined: Fri Feb 22, 2019 10:05 am

Re: Finish displaying the text

Post by Tompis1995 »

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: Select all

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!
Post Reply