how many scrolling layers did NES have again?

effowe

Diamond Member
Nov 1, 2004
6,012
18
81
Pretty cool, I loaded it up on my Powerpak and everything seemed to work fine. Can you provide the source / give some details on what's going on?
 

exdeath

Lifer
Jan 29, 2004
13,679
10
81
FCEUX emulator will show what is going on mostly. You can show PPU pattern buffers and name tables while it runs.

The BG rocks are just tile animations, 32 frames of the same 32 x 32 pixel repeating area with the image rotated/shifted 1 pixel.

Since the NES only has 1 layer, the BG tiles actually move with the foreground, but animate as they do giving the illusion of a separate scrolling layer. Since there are more BG tile indices than the NES can update per frame, the animation is achieved instantly by animating the actual tile graphics via MMC3 bank switching. A 2k chunk of video memory containing the tile bitmaps is swapped out with 1 of 32 copies containing frames of the BG tile animations. That's 2 layers.

The dialog box is using MMC3 IRQs to trigger a interrupt on a particular scanline, and having the interrupt handler use timed code to modify the scroll registers mid frame during hblank to split the screen without artifacts. This is what is normally used for status bars, etc on most commercial NES games. There's 3 layers.

The most challenging part of doing the split screen was getting the animated BG tiles to appear seamless and line up before and after the split. Because of the relevant motion between the fixed and moving halves of the screen, the animation of the background tiles had to be played in reverse of each other before and after the split. To see what I mean, observe the rock layer going right to left into the right side of the text box, but watch it move left to right away from the blocks as they scroll.

Code:
	.inesprg 2
	.ineschr 16
	.inesmap 4
	.inesmir 2	; H mirror + BATT SRAM



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	.bank 0
	.org $8000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

res:

	;; load palette ;;;;;;;;;;;;;;;;;;;;;;;;;;

	lda #$3F
	sta $2006
	lda #$00
	sta $2006
	
	ldx #$00

loadpal:
    
	lda pal, x
	sta $2007
	inx
	cpx #16
	bne loadpal

	;; load name table ;;;;;;;;;;;;;;;;;;;;;;

	lda #$20
	sta $2006
	lda #$00
	sta $2006

	lda #low(nam)
	sta $00
	lda #high(nam)
	sta $01

	ldx #4
	ldy #0

loadnam:
    
	lda [$00], y
	sta $2007
	iny
	bne loadnam

	inc $01
	dex
	bne loadnam

	;; enable drawing ;;;;;;;;;;;;;;;;;;;;;;;

	lda #%10001000
	sta $2000
	lda #%00001110
	sta $2001

	lda #$40	; disable built in frame counter IRQ
	sta $4017

	cli		; enable interrupts

	lda #$00		
	sta $10		; init frame counter = 0	

	lda #%00000001  ; configure mapper, 2k char pages at $0800
	sta $8000


	;; program main loop  ;;;;;;;;;;;;;;;;;;;

vblank:
	lda $2002
	bpl vblank


	;; mmc3 irq

	lda #$01	
	sta $E000
	lda #$4F
	sta $C000			;// setup MMC3 to generate an IRQ after
	sta $C001			;// 120 scanlines
	lda #$01
	sta $E000
	sta $E001

	;; mmc3 irq end


		

	lda #$00		; scroll screen to (0,0) for top half
	sta $2005
	lda #$00
	sta $2005



	; read controller

	lda #$01
	sta $4016
	lda #$00
	sta $4016	; strobe controllers 1 then 0 to init for read

	lda $4016	;a
	lda $4016	;b
	lda $4016	;SEL
	lda $4016	;START
	lda $4016	;up
	lda $4016	;down
	lda $4016	;left
	and #1
	bne skipleft
	inc $10
	inc $10

skipleft:
	lda $4016	;right
	and #1
	bne skipright
	dec $10
	dec $10
	
skipright:


	lda #$FF	; 
	sbc $10		; twos complement frame counter to animate bg tiles backwards on top half
	adc #1		; this is due to the scroll split causing apparent diff in relative movement

	and #$3E	; pick a page based on frame counter, 
			; by skipping bit 0 we get 2k aligned page (even page indexes) 
			; and half the frame rate for BG animation scroll rate
			; start at page 8 to skip over normal bg and spr
	
	adc #$8		; skip first 8 x 1k banks in CHR-ROM, they are the initial BG and SPR pages
	sta $8001	; bank switch the animated tiles in CHR-ROM to the selected bank


	jmp vblank

irq:
	pha	
	sta $E000	; acknowledge MMC3 interrupt to prevent multiple firings

	;2006/1 ---- NN-- (nametable select)
	;2005/2 VV-- -vvv (upper two bits of coarse V scroll, all bits of fine V scroll)
	;2005/1 ---- -hhh (fine horizontal scrolling) (takes effect immediately)
	;2006/2 VVVH HHHH (lower three bits of coarse V scroll, all bits of coarse H scroll)

	lda #$00
	sta $2006	; nametable select 

	lda #%01000000	
	sta $2005	; vscroll = 50 = 01 010 000 = 01xxx000
		
	nop	; cycle timed to start of next hblank since MM3 IRQ triggers late
	nop	; 3 dots per CPU cycle
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop
	nop	
	nop
	nop
	nop
	nop	
	nop
	nop
	nop
	nop	


	lda $10		; read frame counter
	and #$3E	; pick a page based on frame counter, 
			; by skipping bit 0 we get 2k aligned page (even page indexes) 
			; and half the frame rate for BG animation scroll rate
			; start at page 8 to skip over normal bg and spr
		
	adc #8		; skip first 8 x 1k banks in CHR-ROM, they are the initial BG and SPR pages
	sta $8001	; select tile animation in CHR-ROM and bank switch it	



	lda $10		; A = XXXXXxxx (all of X scroll value coarse and fine)
	sta $2005	; set fine x scroll (only lower 3 bits matter, takes effect instantly)

	lsr a
	lsr a
	lsr a
	ora #%01000000	; A = YYYXXXXX, shift right 3 to get course x value, OR in 3 bits Y course which is const

	sta $2006	

	pla
	rti		; return from interrupt

nmi:
	pha
	pla
	rti

;; data files


pal:	
	.incbin "gfx/test3.pal"		; 16 bytes
nam:
	.incbin "gfx/test_rocks3.nam" 	; 1024 bytes

	.bank 1
	.bank 2

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	.bank 3
	.org $FFFA	
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	
	.dw nmi
	.dw res
	.dw irq


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
	.bank 4
;	.org $0000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	.incbin "gfx/test3.chr"
;	.incbin "gfx/test3.chr"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;	
	.bank 5
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	.incbin "gfx/rock32.chr"	
	.incbin "gfx/rock31.chr"
	.incbin "gfx/rock30.chr"
	.incbin "gfx/rock29.chr"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	.bank 6
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	.incbin "gfx/rock28.chr"
	.incbin "gfx/rock27.chr"
	.incbin "gfx/rock26.chr"
	.incbin "gfx/rock25.chr"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	.bank 7
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	.incbin "gfx/rock24.chr"	
	.incbin "gfx/rock23.chr"
	.incbin "gfx/rock22.chr"
	.incbin "gfx/rock21.chr"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	.bank 8
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	.incbin "gfx/rock20.chr"
	.incbin "gfx/rock19.chr"
	.incbin "gfx/rock18.chr"
	.incbin "gfx/rock17.chr"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	.bank 9
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	.incbin "gfx/rock16.chr"
	.incbin "gfx/rock15.chr"
	.incbin "gfx/rock14.chr"
	.incbin "gfx/rock13.chr"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	.bank 10
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	.incbin "gfx/rock12.chr"
	.incbin "gfx/rock11.chr"
	.incbin "gfx/rock10.chr"
	.incbin "gfx/rock9.chr"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	.bank 11
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	.incbin "gfx/rock8.chr"
	.incbin "gfx/rock7.chr"
	.incbin "gfx/rock6.chr"
	.incbin "gfx/rock5.chr"

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	.bank 12
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

	.incbin "gfx/rock4.chr"
	.incbin "gfx/rock3.chr"
	.incbin "gfx/rock2.chr"
	.incbin "gfx/rock1.chr"
	

;; dummy banks to pad CHR-ROM to power 2, in this case 128k up from 72k
	.bank 13
	.bank 14
	.bank 15
	.bank 16
	.bank 17
	.bank 18
	.bank 19
 
Last edited:

effowe

Diamond Member
Nov 1, 2004
6,012
18
81

I tried running this on my Powerpak and it didn't work as I assume it should. I had one layer crushing from the top and another from the bottom, but the background layer seemed to flash between the different textures very quickly. It was almost like a strobe light effect. I can take a video if that didn't explain it properly.
 

exdeath

Lifer
Jan 29, 2004
13,679
10
81
Yeah video if you want, driving me nuts thinking about it.

Sounds like a mapper issue with the bank switching used to animate the BG.

But its no different from the first one. This doesn't even use timing sensitive code in hblank or anything fancy. Try it in Nestopia?
 
Last edited:

exdeath

Lifer
Jan 29, 2004
13,679
10
81
Definitely not right. I'll have to test when I get my PowerPak. Looks like possibly a mapper problem. Any way to get info from the cart that the header is correct or that it's mapper 4 / MMC3?

I don't recognize that boot image. Using any custom mappers or anything funky?

The only thing I changed is reconfigured the mapper to use 1k pages instead of 2k. Works in like 4 emulators. The page containing the BG tiles is alternated at 30 fps (every other blank NMI) with a MMC3 bank switch. That's what it would look like if it wasn't loading all the CHR-ROM or if the bank indices were wrong and it was flashing random or empty pages where the background tile is located instead of the 16 banks containing 1 of 16 tile animations.
 
Last edited:

exdeath

Lifer
Jan 29, 2004
13,679
10
81
I'm only a week new to NES. Don't know any specific sites, just Google results.

I've done SNES, GBA, DC, and PS2 before so I'm mostly only having to search for specific documentation like "NES memory map" or "NES PPU registers" etc.

nesdev.com is a good start though and there is a wiki I use alot: http://wiki.nesdev.com/w/index.php/Programming_guide
 

exdeath

Lifer
Jan 29, 2004
13,679
10
81
So I got my PowerPak and my demos didn't work!

First issue was that the mapper setting to control the name table mirror from the cart overrides the PPU setting, and the MAP04.MAP I had didn't have it's initial state to the the vertical mirroring for "test.nes" which resulted in half the map being garbage (repeated vertically instead of horizontally)

Fixed that by explicitly setting the mirror state in the mapper in software when I start up.

Next I've been tracking down a random jitter problem that turns out to be a specific mapper file for the PowerPak FPGA.

I mention this because thefox's save state mappers you use in your video defaults to the right mirror state and doesn't have the jitter problem.... so it worked perfectly for you all this time haha.