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