Darned NES Scrolling

Yesterday I decided to try and implement an intro for the story of Ardent Assault. I kind of wanted that Star Wars-esque scrolling the words up dealy. The only problem was, I have never scrolled more than one screen at a time! I thought it would be fairly easy to implement, and had hoped it would take only an hour. Unfortunately, it took up my entire day yesterday, plus a little time after I got home from work to get it to behave properly. Ugh.

But the way I have the scrolling now feels… I don’t know. Like I’m not doing something properly. I can’t seem to comprehend the relationship between $2000, $2005, and $2006 that I’m always hearing about. Anyway, here is the code that I came up with after all kinds of time spent, and some guidance in IRC:

intro_nmi:
	lda time_write_count			; Check if we should write words to background
	beq :+					;  if so, jump over a couple instructions
		dec time_write_count		;  if not, decrement time_write_count
		jmp @scroll			;  and continue to scroll the screen
:	ldx offset_x_reg			; Use RAM register for our X offset
	lda intro_ppu_addy_hi, x		;  and check if #$ff is loaded from the table
	cmp #$ff				;  if it isn't, jump over an instruction
	bne :+					;  if it is #$ff, jump to the end of NMI,
		jmp end_nmi			;  effectively freezing the program
:	ldy offset_y_reg			; Load the RAM register for Y offset
	lda intro_lo, y				;  and use it to setup the pointer. The pointer
	sta pointer1				;  points to words to be printed to the
	lda intro_hi, y				;  background
	sta pointer1+1				;
	lda intro_ppu_addy_hi, x		; Use X to select from a table of background
	sta $2006				;  addresses to which to write to. Hi and lo,
	lda intro_ppu_addy_lo, x		;  of course
	sta $2006				;
	ldy #$00				; Use Y _without_ the RAM register offset,
:	lda (pointer1), y			;  and use it to grab the letters of the words
	sta $2007				;  and write them to the background
	iny					;  increment Y
	cpy #$1a				;  and check it against #$1a (the number of
	bne :-					;  letters in each row). Branch back if needed
	inx					; Increment X and store it back in the RAM
	stx offset_x_reg			;  X offset
	inc offset_y_reg			;  Increment the Y RAM offset
	lda #$38				; Use #$38 as the amount of time that needs to
	sta time_write_count			;  countdown before writing to the background
	lda reg2006hi				; Select the base nametable. In this routine,
	sta $2006				;  reg2006hi will be either #$20 or #$28
	lda #$00				;
	sta $2006				;
@scroll:
	bit $2002
	lda scroll_x_hi				; scroll_x_hi will never be anything but zero
	sta $2005				;  in this routine
		lda scroll_y_lo			; We want to scroll slower than 1 pixel per
		clc				;  frame, so we have a hi and lo value for
		adc #<intro_scroll_speed	;  the Y scroll. intro_scroll_speed is set
		sta scroll_y_lo			;  at $0040 in the declarations
		lda scroll_y_hi			;
		adc #>intro_scroll_speed	;
		sta scroll_y_hi			;
		cmp #$ef			; Check if the scroll has reached #$ef
		bne @store_scroll		;  branch and store scroll_y_hi in 2005 if not
			lda reg2006hi		; If it did reach #$ef, check if reg2006hi is
			cmp #$20		;  #$20, and if it is...
			bne :+			;
				lda #$28	;  ... stick #$28 in there
				sta reg2006hi	;  to use as the base nametable next time
				jmp :++		;  then jump two instructions
:			lda #$20		; If reg2006hi was NOT #$20, then make it that
			sta reg2006hi		;  number
:			lda reg2000save		; Load the $2000 register we made in RAM, and
			eor #%00000010		;  flip the bit for which nametable to scroll
			sta reg2000save		;  on. Be sure to save it in both reg2000save
			sta $2000		;  and the $2000 register itself
			lda #$00		; Reset the scroll_y_hi to zero
			sta scroll_y_hi		;
@store_scroll:
		sta $2005			; The second and final write to the scroll
	jmp end_nmi

I know there is some clean-up that can be done, like the inx/stx thing, followed by an inc, but whatever. Anywheres, the main loop of the program does nothing but keep waiting for another NMI to fire.

But yeah, I’m feeling like it’s just not right. It works, but feels wrong. If anyone has a better approach for this, please let me know! If you just want to see the end result of this, here is a vid (ignore the font, it’s just a placeholder).

Leave a Reply