diff --git a/MMC1_A12/MMC1.asm b/MMC1_A12/MMC1.asm new file mode 100644 index 0000000..5759b41 --- /dev/null +++ b/MMC1_A12/MMC1.asm @@ -0,0 +1,66 @@ +;MMC1 write handler +.define R0 $9fff +.define R1 $bfff +.define R2 $dfff +.define R3 $f000 + +.section "MMC1" FREE +WriteR0 + sta R0 + lsr A + sta R0 + lsr A + sta R0 + lsr A + sta R0 + lsr A + sta R0 + rts + +WriteR1 + sta R1 + lsr A + sta R1 + lsr A + sta R1 + lsr A + sta R1 + lsr A + sta R1 + rts + +WriteR2 + sta R2 + lsr A + sta R2 + lsr A + sta R2 + lsr A + sta R2 + lsr A + sta R2 + rts + +ProgramSwitch1 + sta BankLatch1 +ProgramSwitch2 + sta BankLatch2 +WriteR3 + tax + lda M2000 + and #$7f + sta $2000 + txa + sta R3 + lsr A + sta R3 + lsr A + sta R3 + lsr A + sta R3 + lsr A + sta R3 + lda M2000 + sta $2000 + rts +.ends \ No newline at end of file diff --git a/MMC1_A12/alphabet.chr b/MMC1_A12/alphabet.chr new file mode 100644 index 0000000..dc27ce9 Binary files /dev/null and b/MMC1_A12/alphabet.chr differ diff --git a/MMC1_A12/joypad.asm b/MMC1_A12/joypad.asm new file mode 100644 index 0000000..a151447 --- /dev/null +++ b/MMC1_A12/joypad.asm @@ -0,0 +1,68 @@ +;Joypad handling + +.section "ReadnLockPads" FREE +ReadLockJoypad + jsr ReadJoypad + ldx #$01 +- lda Pad1,X + tay + eor Pad1Locked,X ;This detects the key who had a '0' to '1' transition + and Pad1,X + sta Pad1,X + sty Pad1Locked,X + dex + bpl - + rts +.ends + +.section "ReadLockPadsDPCM" FREE ;To be used when the DPCM may occasionally glitch the reads +ReadLockJoypadDPCM + jsr ReadJoypad +- ldx Pad1 + ldy Pad2 ;Read until 2 consecutive reads returns the same data + jsr ReadJoypad + cpx Pad1 + bne - + cpy Pad2 + bne - + + ldx #$01 +- lda Pad1,X + tay + eor Pad1Locked,X ;This detects the key who had a '0' to '1' transition + and Pad1,X + sta Pad1,X + sty Pad1Locked,X + dex + bpl - + rts +.ends + +.section "ReadPad" FREE +;Do read the hardware joypads ports +;Exit with JoyData=Joypad value +;X and Y unchanged + +ReadJoypad + lda #$01 + sta $4016 ;Be sure to reset the shift counters + sta Pad1 + sta Pad2 + lsr A ;Simple trick to get a 0, heh + sta $4016 + +- lda $4016 ;Read the value of JoyPad 1 + lsr A + bcs + + lsr A ++ rol Pad1 + bcc - ;Carry will be set when all 8 keys are read + +- lda $4017 ;Read the value of JoyPad 1 + lsr A + bcs + + lsr A ++ rol Pad2 + bcc - ;Carry will be set when all 8 keys are read + rts +.ends \ No newline at end of file diff --git a/MMC1_A12/joypad.lib b/MMC1_A12/joypad.lib new file mode 100644 index 0000000..18e405c Binary files /dev/null and b/MMC1_A12/joypad.lib differ diff --git a/MMC1_A12/linkfile.txt b/MMC1_A12/linkfile.txt new file mode 100644 index 0000000..7b6603e --- /dev/null +++ b/MMC1_A12/linkfile.txt @@ -0,0 +1,9 @@ +[objects] +mmc1_a12.o + +[libraries] +BANK 7 SLOT 3 mmc1.lib +BANK 7 SLOT 3 joypad.lib + +[header] +snrom.bin \ No newline at end of file diff --git a/MMC1_A12/maping.asm b/MMC1_A12/maping.asm new file mode 100644 index 0000000..e9672be --- /dev/null +++ b/MMC1_A12/maping.asm @@ -0,0 +1,23 @@ +;Defines NES's RAM for any games witout SRAM +;Stack isn't defined, it's use is reserved +;Also $200-$2ff is reserved for SpriteRam and isn't defined here + +.memorymap +defaultslot 0 +slotsize $100 +slot 0 $0 ;0 page RAM +slotsize $500 +slot 1 $300 ;BSS RAM +slotsize $4000 ;PRG ROM slot (16kb) +slot 2 $8000 +slotsize $4000 +slot 3 $c000 ;Resitant PRG slot (16kb) +.endme + +;Define a CNROM structure with 32kb PRG and 32kb CHR + +.rombankmap +bankstotal 8 +banksize $4000 ;4x 32kb PRG +banks 8 +.endro \ No newline at end of file diff --git a/MMC1_A12/mmc1.lib b/MMC1_A12/mmc1.lib new file mode 100644 index 0000000..05dd74d Binary files /dev/null and b/MMC1_A12/mmc1.lib differ diff --git a/MMC1_A12/mmc1_a12.asm b/MMC1_A12/mmc1_a12.asm new file mode 100644 index 0000000..2f2ea12 --- /dev/null +++ b/MMC1_A12/mmc1_a12.asm @@ -0,0 +1,552 @@ +**** PRIOR DEFINITIONS **** + +.include "maping.asm" ;This tells the ROM definition for the hardware +;.define PAL + +.asctable +MAP "1" TO "9" = $10 +MAP "A" TO "Z" = $20 +MAP "=" = $1c +MAP "?" = $1e +MAP " " = $1f +MAP "/" = $3b +.enda + +.ramsection "NMIVars" SLOT 0 + ;Thoose variables are used in the NMI routine + ;Don't do calculations with them if a VBlank is pending + ;set them after the calculation +SpriteDMAFlag db ;This tells if sprite DMA is ready in a frame +PalFlag db ;Check if palette uploaded is needed +NMIFlag db ;Check if a NMI is pending or not +FrameCounter db +NMITemp db +NMITemp2 db +NMITemp3 db +NMITemp4 db +ScrollV db +ScrollH db +M2001 db +M2000 db + +.ends + +.ramsection "Palette" SLOT 1 +PaletteBuffer dsb $20 +.ends + +.ramsection "Main Vars" SLOT 0 +Pointer .dw +PointerL db +PointerH db +Temp db +Temp2 db +Temp3 db +Temp4 db +EffectCtr db +DataCtr db +VRAMPointerH db +VRAMPointerL db +.ends + +.ramsection "JoypadVar" SLOT 0 +Pad1 db +Pad2 db +Pad1Locked db +Pad2Locked db +.ends + +.ramsection "PlayerVars" SLOT 0 +Delay db +.ends +.define DelayMax = $50 + +.struct SpriteDMA ;Structure for the Sprites +PosV db +TileNmr db +Palette db +PosH db +.endst + ;$200-$2ff is reserved for sprite DMA +.enum $200 +SpriteBuffer INSTANCEOF SpriteDMA 64 +.ende + +.ramsection "MMC1" SLOT 0 +BankLatch1 db +BankLatch2 db +.ends + +****** ROM aera begins here ******* + +.bank 0 SLOT 2 +.orga $8000 +.bank 1 SLOT 2 +.orga $8000 +.bank 3 SLOT 2 +.orga $8000 +.bank 4 SLOT 2 +.orga $8000 +.bank 5 SLOT 2 +.orga $8000 +.bank 6 SLOT 2 +.orga $8000 + +.bank 7 SLOT 3 +.orga $c000 + +*********************** +*** Wait for a VBlank *** +*********************** + +.section "WaitNMI" FREE +WaitNMI + lda #$01 + sta NMIFlag +- lda NMIFlag + bne - + rts + +;Wait multiple NMIs in function of the value in X +WaitNMIs +- jsr WaitNMI + dex + bne - + rts +.ends + +.section "NMIRoutine" FREE + +NMI + pha + txa + pha + tya + pha + bit $2002 + lda M2001 + sta $2001 + lda SpriteDMAFlag + beq _skipSpriteDMA ;Is the sprite buffer ready ? + lda #$00 + sta SpriteDMAFlag ;Sprite buffer is read + sta $2003.w + lda #$02 + sta $4014.w ;Do sprite DMA at $200 +_skipSpriteDMA + lda PalFlag + beq + ;Do we need to update tiles ? + lda #$00 + sta PalFlag + jsr WritePalette + ++ lda #$22 + sta $2006.w + lda #$1d + sta $2006.w + + ldx #$00 + lda Delay +- cmp #$0a ;Display units and tens of delay to NT + bcc + + sbc #$0a + inx + jmp - ++ tay + txa + clc + bne + + lda #$3f + bne ++ ++ adc #$0f +++ sta $2007.w + tya + bne + + lda #$2e + bne ++ ++ adc #$0f +++ sta $2007.w + + lda ScrollH + sta $2005.w + lda ScrollV + sta $2005.w ;Upload scrolling regs + lda M2000 + sta $2000.w + lda FrameCounter + eor #$01 + sta FrameCounter + lda #$00 ;Clears the flag to stop waiting the frame + sta NMIFlag + pla ;Restore registers + tay + pla + tax + pla +IRQ rti +.ends + +****************** +* Graphics Stuff ** +****************** + +;then clear all nametables and attribute tables with spaces ($60) +.section "ClearVRAM" FREE +ClearVRAM + lda M2000 ;Enable NMIs + ora #$80 + sta M2000 + sta $2000 + jsr PaletteFadeOut ;Fade out palette (trick to save a routine to clear it) + lda #$00 + sta $2000 + lda #$06 + sta M2001 + sta $2001 ;Rendering off (so that nametables can be cleared) + jsr Clr2ndNamTbl ;Clear 2 active name tables + jmp ClrNamTbl +.ends + +.section "PaletteFadeOut" FREE +PaletteFadeOut ;Fade out the palette + lda #$04 + sta EffectCtr +_palfadeLoop + ldy #$1f +- lda PaletteBuffer.w,Y + sec + sbc #$10 ;Remove $10 from the color + bpl + + lda #$0f ;"Negative" colors forced to black ++ sta PaletteBuffer.w,Y + dey + bpl - + jsr WritePalette ;Palette will be uploaded at next NMI + ldx #$05 + jsr WaitNMIs ;Wait 5 NMIs + dec EffectCtr + bne _palfadeLoop + rts +.ends + +.section "ClrNamTbl" FREE +Clr2ndNamTbl + lda #$24 ;Clears the name tables + .db $cd +ClrNamTbl + lda #$20 +SetNamAdress + sta $2006 ;Begin at $2000/$2400 (vertical mirroring) + lda #$00 + sta $2006 + lda #$0f + ldx #$1e ;Clears 30 rows with spaces ($60) +_screenloop + ldy #$20 +_rowloop + sta $2007 + dey + bne _rowloop + dex + bne _screenloop + lda #$00 + ldx #$40 ;Clear attribute table with color $0 +_attribloop + sta $2007 + dex + bne _attribloop + rts +.ends + +.section "ClrSprRam" FREE +ClrSprRam ;Clears the whole OAM buffer at $200 + ldx #$00 + beq _ClearRemainingSprites + +FillBlankSprites ;This does just clears the unused sprites in $200-$2ff + lda SpriteDMAFlag ;For proper filling before sprite DMA + bne _endClrSprRam +_ClearRemainingSprites + lda #$f0 +- sta SpriteBuffer.w,X + inx + inx + inx + inx + bne - +_endClrSprRam + inc SpriteDMAFlag + rts +.ends + +;Turn the screen back on without scrolling glitches +;Set nametable 0 and scrolling to 0,0 +;To be used only when the screen is disabled +.section "ResetScreen" FREE +ResetScreen +- bit $2002 + bpl - ;Wait for VBlank and acknownledge it (no NMI) + lda #$88 + sta M2000 + sta $2000 + lda #$00 + sta ScrollV ;Enable NMI, enable endering, reset scrolling + sta ScrollH + lda M2001 + ora #$1e + sta M2001 ;Enable rendering + sta $2001 + rts +.ends + +***************** +* Main programm * +***************** + +.section "RESET" FREE +RESET ;The programm will start here + sei + ldx #$ff + txs ;Init stack pointer + inx + stx $2000.w + stx $2001.w ;Turn off rendering and interrupts + lda #$40 + sta $4017.w ;Set sound clock, IRQ off + txa + sta PointerL + tay + ldx #$07 +_initRAMloop + stx PointerH +- dey + sta [Pointer],Y ;Clear RAM + bne - + dex + bpl _initRAMloop + +- bit $2002.w + bpl - +- bit $2002.w ;Wait for several VBlanks + bpl - +_MMC1 + inc _MMC1.w + jsr ClearVRAM ;Clear the whole PPU + jsr LoadAlphabet ;Load character set + lda #$1e + jsr WriteR0 ;Initialise mapper + lda #$00 + jsr WriteR1 + lda #$00 + jsr WriteR2 ;WRAM always enabled (for now) + lda #$06 + jsr WriteR3 + + lda #$00 + sta $2000.w + sta $2001.w + jsr PaletteInit + jsr PrintMsg + jsr ResetScreen ;This will make sprites use right pattern table (important !) + lda #$10 + sta Delay ;Initial delay of $10 + +Loop + jsr ClrSprRam + lda #$85 + sta $200 + lda #$0f + sta $201 + lda #$0f + sta $202 + lda #$de + sta $203 ;Setup sprite zero hit + inc SpriteDMAFlag + jsr WaitNMI ;Wait a frame + + jsr ReadLockJoypad + lda Pad1 + lsr A ;Increase delay if right is pressed + bcc _noRight + lda Delay + cmp #DelayMax + bcs _noRight + inc Delay + +_noRight + lda #$02 + and Pad1 + beq _noLeft + lda Delay ;Decrease delay if left is pressed + cmp #$01 + beq _noLeft + dec Delay + +_noLeft + lda #$04 + and Pad1 + beq _noDown + lda Delay ;Substract 10 to delay if down is pressed + sec + sbc #$0a + bcc + + bne ++ ++ lda #$01 +++ sta Delay + +_noDown + lda #$08 + and Pad1 + beq _noUp ;Add 10 to delay if up is pressed + lda Delay + clc + adc #$0a + cmp #DelayMax + bcc + + lda #DelayMax ++ sta Delay +_noUp + jsr DisplayTestBar ;Display the grayscale test area + jmp Loop +.ends + + +.section "PaletteInit" FREE +PaletteInit + ldx #$00 +- lda PalData.w,X + sta PaletteBuffer.w,X + inx + cpx #$20 + bne - + inc PalFlag + rts + +PalData + .db $01, $06, $26, $36 + .db $01, $06, $27, $36 + .db $01, $06, $0a, $0a + .db $01, $06, $0a, $0a + .db $01, $06, $0a, $0a + .db $01, $06, $0a, $0a + .db $01, $06, $0a, $0a + .db $01, $06, $0a, $0a +.ends + +.section "WritePalette" FREE +WritePalette + lda #$3f + sta $2006.w + lda #$00 + sta $2006.w + tay +_palLoop + lda PaletteBuffer.w,Y + sta $2007.w + iny + cpy #$20 + bne _palLoop + rts +.ends + +.section "LoadAlphabet" FREE + +LoadAlphabet + ldy #$00 + jsr + ;Load tileset into first pattern table + ldy #$10 ;Load identical tileset into second pattern table ++ sty $2006.w + ldy #$00 + sty $2006.w + sty PointerL + ldx #$04 + lda #>Alphabet + sta PointerH +- lda [Pointer],Y + sta $2007 + iny + bne - + inc PointerH + dex + bne - + rts +.ends + +.section "Alphabet" ALIGN $100 + +Alphabet +.incbin "alphabet.chr" + +.ends + +.section "PrintMsg" FREE +PrintMsg + lda #$21 + sta $2006 + lda #$a0 + sta $2006 + ldy #$00 +- lda Msg.w,Y + sta $2007 + iny + bpl - + rts + +Msg +.asc " MMC1 WRAM DISABLE SCANLINE " +.asc " COUNTER TEST " +.asc " C 2O1O BREGALAD " +.asc " USE U/D L/R TO ADJUST DELAY= " + +.ends + +.section "DisplayTestBar" FREE +.define TestConst = $60~$ff ;Use the value oposite as the open bus value. + +DisplayTestBar + lda #TestConst + sta $6000 ;Write a test constant in PRG RAM + + lda #$10 + jsr WriteR2 ;WRAM disabled when fecthing sprites, but enabled when fetchin BG (!) + ;Now we can read $6000 as if we were reading PPU A12 directly... + ;We can detect +- bit $2002 + bvs - +- bit $2002 ;Wait for sprite zero hit + bvc - + + lda #$1f + sta $2001 ;Turn on grayscale mode + ldx Delay +_delayLoop +- lda $6000 + cmp #TestConst ;Wait until PRG RAM is disabled (emulators will freeze here) + beq - + + ldy #$05 +- dey ;Delay while sprites are being fetched + bne - + + dex + bne _delayLoop ;Loop until the delay is expired + + + lda #$1e + sta $2001 + lda #$00 + jsr WriteR2 ;WRAM always enabled + sta $6000 + rts +.ends + +************************* +**** INTERUPTS VECTORS **** +************************* + +.orga $fffa +.section "vectors" FORCE +.dw NMI +.dw RESET ;Thoose are the actual interupts vectors +.dw IRQ +.ends diff --git a/MMC1_A12/mmc1_a12.nes b/MMC1_A12/mmc1_a12.nes new file mode 100644 index 0000000..ff3de91 Binary files /dev/null and b/MMC1_A12/mmc1_a12.nes differ diff --git a/MMC1_A12/snrom.bin b/MMC1_A12/snrom.bin new file mode 100644 index 0000000..d58f77b Binary files /dev/null and b/MMC1_A12/snrom.bin differ diff --git a/PaddleTest3/Info.txt b/PaddleTest3/Info.txt new file mode 100644 index 0000000..0805430 --- /dev/null +++ b/PaddleTest3/Info.txt @@ -0,0 +1,3 @@ +Use NESASM3 to assemble code. + +Program made by Aaron Bottegal (3GenGames) and finished on 3/17/2011, at 2:25AM. No warranty implied on this program. Use at your own risk. Don't steal my code! If you want to use my program, contact me please. Some of this is still a template from the Nerdy Nights tutorials that I've never really had to change yet, thanks bunnyboy. (www.retrousb.com) \ No newline at end of file diff --git a/PaddleTest3/KaboomASM.bat b/PaddleTest3/KaboomASM.bat new file mode 100644 index 0000000..2f4ec93 --- /dev/null +++ b/PaddleTest3/KaboomASM.bat @@ -0,0 +1,2 @@ +NESASM3 PaddleTest.asm +pause \ No newline at end of file diff --git a/PaddleTest3/PaddleTest.asm b/PaddleTest3/PaddleTest.asm new file mode 100644 index 0000000..e53d238 --- /dev/null +++ b/PaddleTest3/PaddleTest.asm @@ -0,0 +1,287 @@ + .inesprg 1 ; 1x 16KB PRG code + .ineschr 1 ; 1x 8KB CHR data + .inesmap 0 ; mapper 0 = NROM, no bank swapping + .inesmir 1 ; background mirroring + + +;;;;;;;;;;;;;;; + + +;$0000-$00FF:Zeropage +;$0100-$01FF:Stack +;$0200-$02FF:Sprites +;$0300-$07FF:RAM + + + .org $0000 ;Zeropage Variables +SixteenBitBackgroundPointer: .rs $02 + + .org $0300 ;Normal RAM +Frame: .rs 1 +PaddleButtons: .rs 1 +GreenFlag: .rs 1 + + + .bank 0 + .org $C000 +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 + +vblankwait1: ; First wait for vblank to make sure PPU is ready + BIT $2002 + BPL vblankwait1 + +clrmem: + LDA #$00 + STA $0000, x + STA $0100, x + STA $0300, x + STA $0400, x + STA $0500, x + STA $0600, x + STA $0700, x + LDA #$FF + STA $0200, x + INX + BNE clrmem + TAX + TXS +vblankwait2: ; Second wait for vblank, PPU is ready after this + BIT $2002 + BPL vblankwait2 +LoadPalettes: + LDA $2002 ; read PPU status to reset the high/low latch + LDA #$3F + STA $2006 ; write the high byte of $3F00 address + LDA #$00 + STA $2006 ; write the low byte of $3F00 address + LDX #$20 +LoadPalettesLoop: + LDA Palette-1,x ;Run pallete loading from +$1F to +$0. + STA $2007 + DEX + BNE LoadPalettesLoop + LDA Palette + STA $2007 + LDA $2002 ;Starts background loading. + LDA #$20 + STA $2006 + LDA #$00 + STA $2006 + LDA #LOW(BackgroundPage) + STA SixteenBitBackgroundPointer + LDA #HIGH(BackgroundPage) + STA SixteenBitBackgroundPointer+1 + LDX #$04 + LDY #$00 +BackgroundLoop: ;Loop to upload 1K of data to the PPU as pointed at by the pointer in zeropage. + LDA [SixteenBitBackgroundPointer],Y + STA $2007 + INY + BNE BackgroundLoop + INC SixteenBitBackgroundPointer+1 + DEX + BNE BackgroundLoop + LDA Sprite ;Put scale on screen. + STA $200 + LDA Sprite+1 + STA $201 + LDA Sprite+2 + STA $202 + LDA Sprite+3 + STA $203 + +LoopUpOne: + BIT $2002 + BPL LoopUpOne + + LDA #%10001000 ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1 + STA $2000 + + LDA #%00011110 ; enable sprites, enable background + STA $2001 + JSR ReadPaddle + + FOREVER: + LDA Frame ;Get Frame number. + CMP Frame ;Wait for it to change. + BEQ FOREVER ;Still waiting for frame to end, will change when NMI is done. + JSR ReadPaddle ;NMI is done, read paddle outside of NMI to save NMI resources. + JMP FOREVER ;Wait again. + + + +ReadPaddle: + LDA #$01 + STA $4016 + LDA #$00 + STA $4016 + LDA $4016 + AND #$10 + ASL A + ASL A + ASL A + ASL A + ROL PaddleButtons + LDA $4016 + AND #$10 + ASL A + ASL A + ASL A + ASL A + ROL PaddleButtons + LDA $4016 + AND #$10 + ASL A + ASL A + ASL A + ASL A + ROL PaddleButtons + LDA $4016 + AND #$10 + ASL A + ASL A + ASL A + ASL A + ROL PaddleButtons + LDA $4016 + AND #$10 + ASL A + ASL A + ASL A + ASL A + ROL PaddleButtons + LDA $4016 + AND #$10 + ASL A + ASL A + ASL A + ASL A + ROL PaddleButtons + LDA $4016 + AND #$10 + ASL A + ASL A + ASL A + ASL A + ROL PaddleButtons + LDA $4016 + AND #$10 + ASL A + ASL A + ASL A + ASL A + ROL PaddleButtons + LDA PaddleButtons + EOR #$FF + STA PaddleButtons + RTS + +NMI: + PHA + LDA $2002 ;Recognize interrupt + LDA PaddleButtons + CMP #$FF ;Paddle plugged in? + BEQ NoController ;No, take the square off the scale. + LDA PaddleYWhenPluggedIn ;Put square on screen, paddle is plugged in. + STA $200 + LDA PaddleButtons ;Get paddle value. + SEC + SBC #$30 ;Put in range of the bar on screen. + BCS QuickJump ;Just check for subtracting wrap. + LDA #$00 ;If wraps for some reason, put on leftmost side of screen. +QuickJump: + JMP SkipTextBelow +NoController: + LDA PaddleYWhenNotPluggedIn + STA $200 ;Paddle not reading if this runs, so put it a different sprite Y position off graph. + LDA #$7D +SkipTextBelow: + STA $203 ;Assign X value, either on graph value if plugged in, or middle if not plugged in. + LDA $4016 + AND #$08 ;Button pressed? + BEQ Red ;Nope, Red Block. + LDA #$02 ;Yes, Green Block to show paddle button is pressed is loaded. + JMP SetBackgroundValue +Red: + LDA #$01 ;Red Block to show paddle button isn't pressed is loaded. +SetBackgroundValue: + STA $201 ;Set Block color via sprite block change. + LDA $2002 ;Reset latch to write value in text to the screen. + LDA #$21 + STA $2006 + LDA #$72 + STA $2006 + LDA PaddleButtons + CMP #$FF + BEQ NotConnectedText ;Not connected/readable, put letters ND on screen. + LSR A + LSR A + LSR A + LSR A + CLC + ADC #$10 + STA $2007 + LDA PaddleButtons + AND #$0F + CLC + ADC #$10 + STA $2007 + JMP SpritesToScreen +NotConnectedText: + LDA #$4E + STA $2007 + LDA #$44 + STA $2007 +SpritesToScreen: + LDA #$00 + STA $2003 ; set the low byte (00) of the RAM address + LDA #$02 + STA $4014 ; set the high byte (02) of the RAM address, start the transfer + LDA #$00 + STA $2005 + STA $2005 ;Reset scroll. + INC Frame ;Tell main engine NMI is done. + PLA + RTI ;Return to the infinite loop. + +;;;;;;;;;;;;;; + + + .bank 1 + .org $E000 + + +Palette: + .incbin "Palette.bin" +Sprite: + .db $FF,$01,$00,$FF +PaddleYWhenPluggedIn: + .db $47 +PaddleYWhenNotPluggedIn: + .db $37 +BackgroundPage: + .incbin "PaddleTestScreen.bin" + + + .org $FFFA + .dw NMI + .dw RESET + .dw 0 + + +;;;;;;;;;;;;;; + + + .bank 2 + .org $0000 + .incbin "PaddleTestGraphics.chr" ;includes 8KB graphics file for game. \ No newline at end of file diff --git a/PaddleTest3/PaddleTest.nes b/PaddleTest3/PaddleTest.nes new file mode 100644 index 0000000..f9e8b9f Binary files /dev/null and b/PaddleTest3/PaddleTest.nes differ diff --git a/PaddleTest3/PaddleTestGraphics.chr b/PaddleTest3/PaddleTestGraphics.chr new file mode 100644 index 0000000..9290c14 Binary files /dev/null and b/PaddleTest3/PaddleTestGraphics.chr differ diff --git a/PaddleTest3/PaddleTestScreen.bin b/PaddleTest3/PaddleTestScreen.bin new file mode 100644 index 0000000..6aa5a3a Binary files /dev/null and b/PaddleTest3/PaddleTestScreen.bin differ diff --git a/PaddleTest3/Palette.bin b/PaddleTest3/Palette.bin new file mode 100644 index 0000000..cc38cda --- /dev/null +++ b/PaddleTest3/Palette.bin @@ -0,0 +1 @@ +0 \ No newline at end of file diff --git a/apu_mixer/dmc.nes b/apu_mixer/dmc.nes new file mode 100644 index 0000000..50c0c6f Binary files /dev/null and b/apu_mixer/dmc.nes differ diff --git a/apu_mixer/noise.nes b/apu_mixer/noise.nes new file mode 100644 index 0000000..1dd65cf Binary files /dev/null and b/apu_mixer/noise.nes differ diff --git a/apu_mixer/readme.txt b/apu_mixer/readme.txt new file mode 100644 index 0000000..74c8f2a --- /dev/null +++ b/apu_mixer/readme.txt @@ -0,0 +1,94 @@ +NES APU Mixer Tests +------------------- +These tests verify proper operation of the NES APU's sound channel +mixer, including relative volumes of channels and non-linear mixing. +Tests MUST be run from a freshly-powered NES, as this is the only way to +ensure that the triangle wave doesn't interfere. + +All tests beep, play a test sound, then beep again. For all but the +noise test, there should be near silence between the beeps. For the +noise test, noise will fade in and out. There shouldn't be any +noticeable tone when heard through a speaker (through headphones, faint +tones might be audible). + + +Internal operation +------------------ +The tests have the channel under test generate a tone, then generate the +inverse waveform using the DMC DAC, canceling to (near) silence if +everything is correct. + +The DMC test verifies that non-linearity of the DMC DAC. The noise and +triangle tests verify relative volume of the noise and triangle to the +DMC, and that the DMC DAC affects attenuation of them properly. Finally, +the square test verifies relative volume of the squares to the DMC, +non-linearity of the square DACs, how one square affects the other +(slightly), and that the square DAC non-linearity is separate from the +DMC. + + +Flashes, clicks, other glitches +------------------------------- +If a test prints "passed", it passed, even if there were some flashes or +odd sounds. Only a test which prints "done" at the end requires that you +watch/listen while it runs in order to determine whether it passed. Such +tests involve things which the CPU cannot directly test. + + +Alternate output +---------------- +Tests generally print information on screen, but also report the final +result audibly, and output text to memory, in case the PPU doesn't work +or there isn't one, as in an NSF or a NES emulator early in development. + +After the tests are done, the final result is reported as a series of +beeps (see below). For NSF builds, any important diagnostic bytes are +also reported as beeps, before the final result. + + +Output at $6000 +--------------- +All text output is written starting at $6004, with a zero-byte +terminator at the end. As more text is written, the terminator is moved +forward, so an emulator can print the current text at any time. + +The test status is written to $6000. $80 means the test is running, $81 +means the test needs the reset button pressed, but delayed by at least +100 msec from now. $00-$7F means the test has completed and given that +result code. + +To allow an emulator to know when one of these tests is running and the +data at $6000+ is valid, as opposed to some other NES program, $DE $B0 +$G1 is written to $6001-$6003. + + +Audible output +-------------- +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason. See the source +code of the test for more information about the meaning of a test code. +They are found after the set_test macro. For example, the cause of test +code 3 would be found in a line containing set_test 3. Examples: + + Tones Binary Decimal Meaning + - - - - - - - - - - - - - - - - - - - - + low 0 0 passed + low high 01 1 failed + low high low 010 2 error 2 + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to prevent the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +-- +Shay Green diff --git a/apu_mixer/source/common/ascii.chr b/apu_mixer/source/common/ascii.chr new file mode 100644 index 0000000..2c5b26b Binary files /dev/null and b/apu_mixer/source/common/ascii.chr differ diff --git a/apu_mixer/source/common/build_rom.s b/apu_mixer/source/common/build_rom.s new file mode 100644 index 0000000..38eab29 --- /dev/null +++ b/apu_mixer/source/common/build_rom.s @@ -0,0 +1,92 @@ +; Builds program as iNES ROM + +; Default is 32K PRG and 8K CHR ROM, NROM (0) + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM=1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM=1 ; Use mapper that supports 8K WRAM in cart +CUSTOM_MAPPER=n ; Specify mapper number +.endif + +.ifndef CUSTOM_MAPPER + .ifdef CART_WRAM + CUSTOM_MAPPER = 2 ; UNROM + .else + CUSTOM_MAPPER = 0 ; NROM + .endif +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte $4E,$45,$53,26 ; "NES" EOF + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte CUSTOM_MAPPER*$10+$01 ; vertical mirroring +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word -1,-1,-1, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .define CHARS "CHARS_PRG" + .segment CHARS + ascii_chr: + + .segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: +.else + .define CHARS "CHARS" + .segment "CHARS_ASCII" + .align $200 + .incbin "ascii.chr" + .res $1800 +.endif + +.segment CHARS + .res $10,0 + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +.segment "CODE" + .res $4000 + +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + load_chr_ram + .endif + rts + +post_exit: + jsr set_final_result + jsr play_hex + jmp forever + +; This helps devcart recover after running test. +; It is never executed by test ROM. +.segment "LOADER" + .incbin "devcart.bin" + +.code +.align 256 diff --git a/apu_mixer/source/common/console.s b/apu_mixer/source/common/console.s new file mode 100644 index 0000000..508898b --- /dev/null +++ b/apu_mixer/source/common/console.s @@ -0,0 +1,328 @@ +; Scrolling text console with word wrapping, 30x29 characters. +; +; * Defers PPU initialization until first flush/ newline. +; * Works even if PPU doesn't support scrolling. +; * Keeps border around edge of screen for TV overscan. +; * Requires vertical or single-screen mirroring. +; * Requires ASCII font in CHR. + +.ifndef CONSOLE_COLOR + CONSOLE_COLOR = $30 ; white +.endif + +console_screen_width = 32 ; if lower than 32, left-justifies + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs. OK if either/both are 0. +console_left_margin = 1 +console_right_margin = 1 + +console_width = console_screen_width - console_left_margin - console_right_margin + +zp_byte console_pos ; 0 to console_width +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_width + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + + setb console_pos,0 + rts + + +; Hides console by disabling PPU rendering and blacking out +; first four entries of palette. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +; Shows console display +; Preserved: A, X, Y +console_show: + pha + lda #CONSOLE_COLOR + jsr console_show_custom_color_ + pla + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + sty console_temp + + ldy console_pos + cpy #console_width + beq console_full_ + sta console_buf,y + iny + sty console_pos + + ldy console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_scroll_up_ + setb console_pos,0 + pla + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_apply_scroll_ + pla + rts + + +;**** Internal routines **** + +console_full_: + ldy console_temp + + ; Line is full + + ; If space, treat as newline + cmp #' ' + beq console_newline + + ; Wrap current line at appropriate point + pha + tya + pha + jsr console_wrap_ + pla + tay + pla + + jmp console_print + + +; Inserts newline into buffer at appropriate position, leaving +; next line ready in buffer +; Preserved: X, console_temp +console_wrap_: + ; Find beginning of last word + ldy #console_width + lda #' ' +: dey + bmi console_newline + cmp console_buf,y + bne :- + + ; y = 0 to console_width-1 + + ; Flush through current word and put remaining + ; in buffer for next line + jsr console_wait_vbl_ + + ; Time to last PPU write: 207 + 32*(26 + 10) + + lda console_scroll + jsr console_set_ppuaddr_ + + stx console_pos ; save X + + ldx #0 + + ; Print everything before last word +: lda console_buf,x + sta PPUDATA + inx + dey + bpl :- + + ; x = 1 to console_width + + ; Move last word to beginning of buffer, and + ; print spaces for rest of line + ldy #0 + beq :++ +: lda #' ' + sta PPUDATA + lda console_buf,x + inx + sta console_buf,y + iny +: cpx #console_width + bne :-- + + ldx console_pos ; restore X + + ; Append new text after that + sty console_pos + + ; FALL THROUGH + + +; Scrolls up 8 pixels and clears one line BELOW new line +; Preserved: X, console_temp +console_scroll_up_: + ; Scroll up 8 pixels + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + + ; Clear line AFTER that on screen + jsr console_add_8_to_scroll_ + jsr console_set_ppuaddr_ + + ldy #console_width + lda #' ' +: sta PPUDATA + dey + bne :- + ; FALL THROUGH + + +; Applies current scrolling position to PPU +; Preserved: X, Y, console_temp +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + rts + + +; Sets PPU address for row +; In: A = scroll position +; Preserved: X, Y +console_set_ppuaddr_: + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + ora #console_left_margin + sta PPUADDR + rts + + +; A = (A + 8) % 240 +; Preserved: X, Y +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_show_custom_color_: + pha + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + pla + jsr console_load_palette_ + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + rts + + +; Initializes PPU if necessary, then waits for VBL +; Preserved: A, X, Y, console_temp +console_wait_vbl_: + lda console_scroll + cmp #-1 + bne @already_initialized + + ; Deferred initialization of PPU until first use of console + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + tya + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldy #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dey + bne :- + + ; Clear attributes + lda #0 + ldy #$40 +: sta PPUDATA + dey + bne :- + + pla + tay + + jsr console_show +@already_initialized: + jmp wait_vbl_optional + + +; Flushes current line +; Preserved: X, Y +console_flush_: + lda console_scroll + jsr console_set_ppuaddr_ + + sty console_temp + + ; Copy line + ldy #0 + beq :++ +: lda console_buf,y + sta PPUDATA + iny +: cpy console_pos + bne :-- + + ldy console_temp + rts diff --git a/apu_mixer/source/common/crc.s b/apu_mixer/source/common/crc.s new file mode 100644 index 0000000..de96c2a --- /dev/null +++ b/apu_mixer/source/common/crc.s @@ -0,0 +1,118 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on + + +; EQ if checksum matches CRC +; Out: A=0 and EQ if match, A>0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/apu_mixer/source/common/delay.s b/apu_mixer/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/apu_mixer/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/apu_mixer/source/common/devcart.bin b/apu_mixer/source/common/devcart.bin new file mode 100644 index 0000000..7180661 Binary files /dev/null and b/apu_mixer/source/common/devcart.bin differ diff --git a/apu_mixer/source/common/macros.inc b/apu_mixer/source/common/macros.inc new file mode 100644 index 0000000..4307c66 --- /dev/null +++ b/apu_mixer/source/common/macros.inc @@ -0,0 +1,188 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro inxy + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro + +; Negates A and adds it to operand +.macro subaf Operand + eor #$FF + sec + adc Operand +.endmacro + +; Initializes CPU registers to reasonable values +.macro init_cpu_regs + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + .ifndef BUILD_NSF + inx + stx PPUCTRL + .endif +.endmacro diff --git a/apu_mixer/source/common/neshw.inc b/apu_mixer/source/common/neshw.inc new file mode 100644 index 0000000..d636a97 --- /dev/null +++ b/apu_mixer/source/common/neshw.inc @@ -0,0 +1,56 @@ +; NES I/O locations and masks + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 + +.ifndef REGION_FREE + .ifndef PAL_ONLY + .ifndef NTSC_ONLY + NTSC_ONLY = 1 + .endif + .endif +.else + .ifdef NTSC_ONLY + .error "NTSC_ONLY and REGION_FREE defined" + .endif + .ifdef PAL_ONLY + .error "PAL_ONLY and REGION_FREE defined" + .endif +.endif + +.ifdef NTSC_ONLY + CLOCK_RATE = 1789773 + PPU_FRAMELEN = 29781 +.endif + +.ifdef PAL_ONLY + CLOCK_RATE = 1662607 + PPU_FRAMELEN = 33248 +.endif diff --git a/apu_mixer/source/common/ppu.s b/apu_mixer/source/common/ppu.s new file mode 100644 index 0000000..4827b3e --- /dev/null +++ b/apu_mixer/source/common/ppu.s @@ -0,0 +1,203 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts + + +.macro check_ppu_region_ Len + ; Delays since VBL began + jsr wait_vbl_optional ; 10 average + delay Len - 18 - 200 + lda PPUSTATUS ; 4 + bmi @ok ; 2 + delay 200 + ; Next VBL should roughly begin here if it's the + ; one we are detecting + delay 200 + lda PPUSTATUS ; 2 + bpl @ok +.endmacro + +check_ppu_region: + +.ifndef REGION_FREE +.ifdef PAL_ONLY + check_ppu_region_ 29781 + print_str {newline,"Note: This test is meant for PAL NES only.",newline,newline} +.endif + +.ifdef NTSC_ONLY + check_ppu_region_ 33248 + print_str {newline,"Note: This test is meant for NTSC NES only.",newline,newline} +.endif +.endif +@ok: rts + + +; Loads ASCII font into CHR RAM and fills rest with $FF +.macro load_chr_ram + bit PPUSTATUS + setb PPUADDR,0 + setb PPUADDR,0 + + ; Copy ascii_chr to 0 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page + + ; Fill rest + lda #$FF +: sta PPUDATA + iny + bne :- + inx + cpx #$20 + bne :- +.endmacro diff --git a/apu_mixer/source/common/print.s b/apu_mixer/source/common/print.s new file mode 100644 index 0000000..ab8a308 --- /dev/null +++ b/apu_mixer/source/common/print.s @@ -0,0 +1,247 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr print_nibble_ + pla + + pha + and #$0F + jsr print_nibble_ + pla + rts + +print_nibble_: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints low 4 bits of A as single hex character +; Preserved: A, X, Y +print_nibble: + pha + and #$0F + jsr update_crc + jsr print_nibble_ + pla + rts + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: A, X, Y +print_dec: + pha + sta print_temp_ + jsr update_crc + txa + pha + lda print_temp_ + + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jsr print_char + pla + tax + pla + rts + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/apu_mixer/source/common/shell.inc b/apu_mixer/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/apu_mixer/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/apu_mixer/source/common/shell.s b/apu_mixer/source/common/shell.s new file mode 100644 index 0000000..d458c9b --- /dev/null +++ b/apu_mixer/source/common/shell.s @@ -0,0 +1,180 @@ +; Shell that sets up testing framework and calls main + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +; Move code from $C000 to $E200, to accommodate my devcarts +.segment "CODE" + .res $2200 + +; Put shell code after user code, so user code is in more +; consistent environment +.segment "CODE2" + + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + init_cpu_regs + + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell without affecting current set_test values +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + ; Clear APU registers + lda #0 + sta $4015 + ldx #$13 +: sta $4000,x + dex + bpl :- + + ; CPU registers + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sta temp + init_cpu_regs + setb SNDCHN,0 + lda temp + + jsr report_result + pha + jsr check_ppu_region + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +.include "shell_misc.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts +.endif diff --git a/apu_mixer/source/common/shell_misc.s b/apu_mixer/source/common/shell_misc.s new file mode 100644 index 0000000..7e7d4e9 --- /dev/null +++ b/apu_mixer/source/common/shell_misc.s @@ -0,0 +1,211 @@ +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +.macro fill_ram_ Begin, End + ; Simpler to count from negative size up to 0, + ; and adjust address downward to compensate + ; for initial low byte in Y index + .local Neg_size + Neg_size = (Begin) - (End) + ldxy #(Begin) - = 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/apu_mixer/source/common/text_out.s b/apu_mixer/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/apu_mixer/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +The first time a line of text is completed, the console initializes the +PPU. Most print routines update a running CRC-32 checksum which can be +checked with check_crc. This allows ALL the output to be checked very +easily. If the checksum doesn't match, it is printed, so during +development the code can be run on a NES to get the correct checksum, +which is then typed into the test code. The checksum is also useful when +comparing different outputs; rather than having to examine all the +output, one need only compare checksums. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported by the included code, due to +their complexity. + + +Macros +------ +Some macros are used to make common operations more convenient, defined +in common/macros.inc. The left is equivalent to the right: + + Macro Equivalent + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + setb addr,byte lda #byte + sta addr + + setw addr,word lda #word + sta addr+1 + + ldxy #value ldx #>value + ldy # diff --git a/apu_mixer/source/square.s b/apu_mixer/source/square.s new file mode 100644 index 0000000..9e8c24a --- /dev/null +++ b/apu_mixer/source/square.s @@ -0,0 +1,88 @@ +; Verifies square DACs and non-linear mixing +; +; Plays two square waves in all totals from 0 to 31. +; Cancels this to silence with inverse wave generated +; using DMC DAC. + +.include "vol_shell.inc" + +test_main: + setb $4001,$7F ; disable sweep + setb $4005,$7F + setb $4002,0 ; period = 0 + setb $4003,0 + setb $4006,0 + setb $4007,0 + delay 5000 ; allow period to settle in + setb $4015,$03 + + ldx #$6F ; period = 896*2 + ldy #$00 + stx $4002 + stx $4006 + sty $4003 + sty $4007 + delay 175 + + extra = 27-1 + ldx #-1 + ldy #0 + jmp @first + +@1: delay extra +@2: lda dmc,x + sta $4011 + delay 896-4-2 + lda #127 + sta $4011 + delay 896-4-5-extra-4 + dey + bne @1 +@first: inx + lda sq1,x ; update square volumes + sta $4000 + lda sq2,x + sta $4004 + ldy #80 + lda dmc,x + bne @2 + + rts + +.align 256 +dmc: +.byte $7F,$7B,$77,$77,$74,$74,$70,$70,$70,$6D,$6D,$6D,$6A,$6A,$6A,$6A +.byte $67,$67,$67,$67,$64,$64,$64,$64,$64,$61,$61,$61,$61,$61,$5E,$5E +.byte $5E,$5E,$5E,$5E,$5C,$5C,$5C,$5C,$5C,$5C,$59,$59,$59,$59,$59,$59 +.byte $59,$57,$57,$57,$57,$57,$57,$57,$54,$54,$54,$54,$54,$54,$54,$54 +.byte $52,$52,$52,$52,$52,$52,$52,$52,$50,$50,$50,$50,$50,$50,$50,$50 +.byte $4E,$4E,$4E,$4E,$4E,$4E,$4E,$4C,$4C,$4C,$4C,$4C,$4C,$4C,$4A,$4A +.byte $4A,$4A,$4A,$4A,$48,$48,$48,$48,$48,$48,$46,$46,$46,$46,$46,$44 +.byte $44,$44,$44,$44,$42,$42,$42,$42,$41,$41,$41,$41,$3F,$3F,$3F,$3E +.byte $3E,$3E,$3C,$3C,$3B,$3B,$39,$38,$00 + +.align 256 +sq1: +.byte $B0,$B0,$B0,$B1,$B0,$B1,$B0,$B1,$B2,$B0,$B1,$B2,$B0,$B1,$B2,$B3 +.byte $B0,$B1,$B2,$B3,$B0,$B1,$B2,$B3,$B4,$B0,$B1,$B2,$B3,$B4,$B0,$B1 +.byte $B2,$B3,$B4,$B5,$B0,$B1,$B2,$B3,$B4,$B5,$B0,$B1,$B2,$B3,$B4,$B5 +.byte $B6,$B0,$B1,$B2,$B3,$B4,$B5,$B6,$B0,$B1,$B2,$B3,$B4,$B5,$B6,$B7 +.byte $B0,$B1,$B2,$B3,$B4,$B5,$B6,$B7,$B1,$B2,$B3,$B4,$B5,$B6,$B7,$B8 +.byte $B2,$B3,$B4,$B5,$B6,$B7,$B8,$B3,$B4,$B5,$B6,$B7,$B8,$B9,$B4,$B5 +.byte $B6,$B7,$B8,$B9,$B5,$B6,$B7,$B8,$B9,$BA,$B6,$B7,$B8,$B9,$BA,$B7 +.byte $B8,$B9,$BA,$BB,$B8,$B9,$BA,$BB,$B9,$BA,$BB,$BC,$BA,$BB,$BC,$BB +.byte $BC,$BD,$BC,$BD,$BD,$BE,$BE,$BF + +.align 256 +sq2: +.byte $B0,$B1,$B2,$B1,$B3,$B2,$B4,$B3,$B2,$B5,$B4,$B3,$B6,$B5,$B4,$B3 +.byte $B7,$B6,$B5,$B4,$B8,$B7,$B6,$B5,$B4,$B9,$B8,$B7,$B6,$B5,$BA,$B9 +.byte $B8,$B7,$B6,$B5,$BB,$BA,$B9,$B8,$B7,$B6,$BC,$BB,$BA,$B9,$B8,$B7 +.byte $B6,$BD,$BC,$BB,$BA,$B9,$B8,$B7,$BE,$BD,$BC,$BB,$BA,$B9,$B8,$B7 +.byte $BF,$BE,$BD,$BC,$BB,$BA,$B9,$B8,$BF,$BE,$BD,$BC,$BB,$BA,$B9,$B8 +.byte $BF,$BE,$BD,$BC,$BB,$BA,$B9,$BF,$BE,$BD,$BC,$BB,$BA,$B9,$BF,$BE +.byte $BD,$BC,$BB,$BA,$BF,$BE,$BD,$BC,$BB,$BA,$BF,$BE,$BD,$BC,$BB,$BF +.byte $BE,$BD,$BC,$BB,$BF,$BE,$BD,$BC,$BF,$BE,$BD,$BC,$BF,$BE,$BD,$BF +.byte $BE,$BD,$BF,$BE,$BF,$BE,$BF,$BF + + diff --git a/apu_mixer/source/triangle.s b/apu_mixer/source/triangle.s new file mode 100644 index 0000000..8a54232 --- /dev/null +++ b/apu_mixer/source/triangle.s @@ -0,0 +1,54 @@ +; Verifies triangle DAC and non-linear mixing +; +; Plays triangle wave. Cancels this to silence with inverse +; wave generated using DMC DAC. Scans over range of DMC DAC +; to test non-linearity. + +.include "vol_shell.inc" + +test_main: + setb $4008,$FF + setb $400A,0 + setb $400B,0 + delay_msec 20 + setb $4015,$04 + setb $4009,0 + + wait = 8 + + lda #wait + sta temp + sta temp+1 + ldy #127-41 + + extra = 13-1 + + setb $400A,$37 + setb $400B,0 + delay 28-extra + + ldx #16 +@1: delay extra +@2: inx + txa + and #$1F + tax + tya + clc + adc table,x + sta $4011 + delay 3 + dec_tempw + bne @1 + lda #wait + sta temp + sta temp+1 + dey + bne @2 + + rts + +.align 32 +table: + .byte 0,3,6,8,11,14,17,19,22,25,28,30,33,35,38,41 + .byte 41,38,35,33,30,28,25,22,19,17,14,11,8,6,3,0 diff --git a/apu_mixer/square.nes b/apu_mixer/square.nes new file mode 100644 index 0000000..08a514a Binary files /dev/null and b/apu_mixer/square.nes differ diff --git a/apu_mixer/triangle.nes b/apu_mixer/triangle.nes new file mode 100644 index 0000000..923349c Binary files /dev/null and b/apu_mixer/triangle.nes differ diff --git a/apu_mixer_recordings/dmc.mp3 b/apu_mixer_recordings/dmc.mp3 new file mode 100644 index 0000000..8a28e2a Binary files /dev/null and b/apu_mixer_recordings/dmc.mp3 differ diff --git a/apu_mixer_recordings/noise.mp3 b/apu_mixer_recordings/noise.mp3 new file mode 100644 index 0000000..2ecefa8 Binary files /dev/null and b/apu_mixer_recordings/noise.mp3 differ diff --git a/apu_mixer_recordings/square.mp3 b/apu_mixer_recordings/square.mp3 new file mode 100644 index 0000000..033908e Binary files /dev/null and b/apu_mixer_recordings/square.mp3 differ diff --git a/apu_mixer_recordings/triangle.mp3 b/apu_mixer_recordings/triangle.mp3 new file mode 100644 index 0000000..e135f7a Binary files /dev/null and b/apu_mixer_recordings/triangle.mp3 differ diff --git a/apu_reset/4015_cleared.nes b/apu_reset/4015_cleared.nes new file mode 100644 index 0000000..5874a6d Binary files /dev/null and b/apu_reset/4015_cleared.nes differ diff --git a/apu_reset/4017_timing.nes b/apu_reset/4017_timing.nes new file mode 100644 index 0000000..6e09d03 Binary files /dev/null and b/apu_reset/4017_timing.nes differ diff --git a/apu_reset/4017_written.nes b/apu_reset/4017_written.nes new file mode 100644 index 0000000..816a821 Binary files /dev/null and b/apu_reset/4017_written.nes differ diff --git a/apu_reset/irq_flag_cleared.nes b/apu_reset/irq_flag_cleared.nes new file mode 100644 index 0000000..e624a25 Binary files /dev/null and b/apu_reset/irq_flag_cleared.nes differ diff --git a/apu_reset/len_ctrs_enabled.nes b/apu_reset/len_ctrs_enabled.nes new file mode 100644 index 0000000..df7a005 Binary files /dev/null and b/apu_reset/len_ctrs_enabled.nes differ diff --git a/apu_reset/readme.txt b/apu_reset/readme.txt new file mode 100644 index 0000000..b201fa3 --- /dev/null +++ b/apu_reset/readme.txt @@ -0,0 +1,129 @@ +NES APU Reset Tests +-------------------- +These tests verify initial APU state at power, and the effect of reset. + + +4015_cleared +------------ +At power and reset, $4015 is cleared. + +2) At power, $4015 should be cleared +3) At reset, $4015 should be cleared + + +4017_timing +----------- +At power, it is as if $00 were written to $4017, +then a 9-12 clock delay, then execution from address +in reset vector. + +At reset, same as above, except last value written +to $4017 is written again, rather than $00. + +The delay from when $00 was written to $4017 is +printed. Delay after NES being powered off for a +minute is usually 9. + +2) Frame IRQ flag should be set later after power/reset +3) Frame IRQ flag should be set sooner after power/reset + + +4017_written +------------ +At power, $4017 = $00. +At reset, $4017 mode is unchanged, but IRQ inhibit +flag is sometimes cleared. + +2) At power, $4017 should be written with $00 +3) At reset, $4017 should should be rewritten with last value written + + +irq_flag_cleared +---------------- +At power and reset, IRQ flag is clear. + +2) At power, flag should be clear +3) At reset, flag should be clear + + +len_ctrs_enabled +---------------- +At power and reset, length counters are enabled. + +2) At power, length counters should be enabled +3) At reset, length counters should be enabled, triangle unaffected + + +works_immediately +----------------- +At power and reset, $4017, $4015, and length counters work +immediately. + +2) At power, writes should work immediately +3) At reset, writes should work immediately + +Flashes, clicks, other glitches +------------------------------- +If a test prints "passed", it passed, even if there were some flashes or +odd sounds. Only a test which prints "done" at the end requires that you +watch/listen while it runs in order to determine whether it passed. Such +tests involve things which the CPU cannot directly test. + + +Alternate output +---------------- +Tests generally print information on screen, but also report the final +result audibly, and output text to memory, in case the PPU doesn't work +or there isn't one, as in an NSF or a NES emulator early in development. + +After the tests are done, the final result is reported as a series of +beeps (see below). For NSF builds, any important diagnostic bytes are +also reported as beeps, before the final result. + + +Output at $6000 +--------------- +All text output is written starting at $6004, with a zero-byte +terminator at the end. As more text is written, the terminator is moved +forward, so an emulator can print the current text at any time. + +The test status is written to $6000. $80 means the test is running, $81 +means the test needs the reset button pressed, but delayed by at least +100 msec from now. $00-$7F means the test has completed and given that +result code. + +To allow an emulator to know when one of these tests is running and the +data at $6000+ is valid, as opposed to some other NES program, $DE $B0 +$G1 is written to $6001-$6003. + + +Audible output +-------------- +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason. See the source +code of the test for more information about the meaning of a test code. +They are found after the set_test macro. For example, the cause of test +code 3 would be found in a line containing set_test 3. Examples: + + Tones Binary Decimal Meaning + - - - - - - - - - - - - - - - - - - - - + low 0 0 passed + low high 01 1 failed + low high low 010 2 error 2 + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to prevent the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +-- +Shay Green diff --git a/apu_reset/source/4015_cleared.s b/apu_reset/source/4015_cleared.s new file mode 100644 index 0000000..67e24e2 --- /dev/null +++ b/apu_reset/source/4015_cleared.s @@ -0,0 +1,60 @@ +; At power and reset, $4015 is cleared. + +CUSTOM_RESET=1 +.include "shell.inc" +.include "run_at_reset.s" + +nv_res log + +reset: + ; Begin mode 0 + setb $4017,$00 + + ; Start channels with zero volume + + setb $4000,$00 + setb $4001,$7F + setb $4002,$FF + setb $4003,$28 + + setb $4004,$00 + setb $4005,$7F + setb $4006,$FF + setb $4007,$28 + + setb $4008,$7F + setb $400A,$FF + setb $400B,$28 + + setb $400C,$00 + setb $400E,$00 + setb $400F,$28 + + ; If any of the low 4 bits of $4015 were set, then + ; length counter of that channel would be non-zero, + ; and that bit would read back as non-zero here: + + lda $4015 + sta log + + jmp std_reset + +main: jsr num_resets + bne first_reset + +power: set_test 2,"At power, $4015 should be cleared" + lda log + and #$0F + jne test_failed + + jsr prompt_to_reset + setb $4015,$0F + jmp wait_reset + +first_reset: + set_test 3,"At reset, $4015 should be cleared" + lda log + and #$0F + jne test_failed + + jmp tests_passed diff --git a/apu_reset/source/4017_timing.s b/apu_reset/source/4017_timing.s new file mode 100644 index 0000000..5d59f78 --- /dev/null +++ b/apu_reset/source/4017_timing.s @@ -0,0 +1,49 @@ +; At power, it is as if $00 were written to $4017, +; then a 9-12 clock delay, then execution from address +; in reset vector. +; +; At reset, same as above, except last value written +; to $4017 is written again, rather than $00. +; +; The delay from when $00 was written to $4017 is +; printed. Delay after NES being powered off for a +; minute is usually 9. + +CUSTOM_RESET=1 +.include "shell.inc" +.include "run_at_reset.s" + +nv_res count + +reset: delay 29814-2 + ldx #14 +: lda $4015 + and #$40 + bne :+ + delay 29831-4-2-2-4-2-3 + lda $4015 + dex + bne :- +: stx count + jmp std_reset + +main: print_str "Delay after effective $4017 write: " + lda count + jsr print_dec + jsr print_newline + + set_test 2,"Frame IRQ flag should be set later after power/reset" + lda count + cmp #13 + jge test_failed + + set_test 3,"Frame IRQ flag should be set sooner after power/reset" + lda count + cmp #6 + jlt test_failed + + jsr num_resets + jne tests_passed + + jsr prompt_to_reset + jmp wait_reset diff --git a/apu_reset/source/4017_written.s b/apu_reset/source/4017_written.s new file mode 100644 index 0000000..fc4fbec --- /dev/null +++ b/apu_reset/source/4017_written.s @@ -0,0 +1,73 @@ +; At power, $4017 = $00. +; At reset, $4017 mode is unchanged, but IRQ inhibit +; flag is sometimes cleared. + +CUSTOM_RESET=1 +.include "shell.inc" +.include "run_at_reset.s" + +nv_res log,4 + +reset: + setb $4015,$01 + setb $4000,0 + setb $4001,$7F + setb $4002,$FF + setb $4003,$28 + + delay 29831*2-5*6-9-7 + lda $4015 + sta log+0 + lda $4015 + sta log+1 + + delay 14887 + lda $4015 + sta log+2 + lda $4015 + sta log+3 + + jmp std_reset + +main: jsr num_resets + bne first_reset + +power: + set_test 2,"At power, $4017 should be written with $00" + lda log+0 + and #$41 + cmp #$41 + jne test_failed + lda log+1 + and #$01 + jne test_failed + + jsr prompt_to_reset + setb $4017,$40 + jmp wait_reset + +first_reset: + set_test 3,"At reset, $4017 should should be rewritten with last value written" + cmp #2 + beq second_reset + + lda log+0 + and #$01 + jeq test_failed + lda log+1 + and #$01 + jne test_failed + + jsr prompt_to_reset + setb $4017,$80 ; put in other mode this time + jmp wait_reset + +second_reset: + lda log+2 + and #$01 + jeq test_failed + lda log+3 + and #$01 + jne test_failed + + jmp tests_passed diff --git a/apu_reset/source/common/ascii.chr b/apu_reset/source/common/ascii.chr new file mode 100644 index 0000000..2c5b26b Binary files /dev/null and b/apu_reset/source/common/ascii.chr differ diff --git a/apu_reset/source/common/build_rom.s b/apu_reset/source/common/build_rom.s new file mode 100644 index 0000000..38eab29 --- /dev/null +++ b/apu_reset/source/common/build_rom.s @@ -0,0 +1,92 @@ +; Builds program as iNES ROM + +; Default is 32K PRG and 8K CHR ROM, NROM (0) + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM=1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM=1 ; Use mapper that supports 8K WRAM in cart +CUSTOM_MAPPER=n ; Specify mapper number +.endif + +.ifndef CUSTOM_MAPPER + .ifdef CART_WRAM + CUSTOM_MAPPER = 2 ; UNROM + .else + CUSTOM_MAPPER = 0 ; NROM + .endif +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte $4E,$45,$53,26 ; "NES" EOF + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte CUSTOM_MAPPER*$10+$01 ; vertical mirroring +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word -1,-1,-1, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .define CHARS "CHARS_PRG" + .segment CHARS + ascii_chr: + + .segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: +.else + .define CHARS "CHARS" + .segment "CHARS_ASCII" + .align $200 + .incbin "ascii.chr" + .res $1800 +.endif + +.segment CHARS + .res $10,0 + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +.segment "CODE" + .res $4000 + +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + load_chr_ram + .endif + rts + +post_exit: + jsr set_final_result + jsr play_hex + jmp forever + +; This helps devcart recover after running test. +; It is never executed by test ROM. +.segment "LOADER" + .incbin "devcart.bin" + +.code +.align 256 diff --git a/apu_reset/source/common/console.s b/apu_reset/source/common/console.s new file mode 100644 index 0000000..508898b --- /dev/null +++ b/apu_reset/source/common/console.s @@ -0,0 +1,328 @@ +; Scrolling text console with word wrapping, 30x29 characters. +; +; * Defers PPU initialization until first flush/ newline. +; * Works even if PPU doesn't support scrolling. +; * Keeps border around edge of screen for TV overscan. +; * Requires vertical or single-screen mirroring. +; * Requires ASCII font in CHR. + +.ifndef CONSOLE_COLOR + CONSOLE_COLOR = $30 ; white +.endif + +console_screen_width = 32 ; if lower than 32, left-justifies + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs. OK if either/both are 0. +console_left_margin = 1 +console_right_margin = 1 + +console_width = console_screen_width - console_left_margin - console_right_margin + +zp_byte console_pos ; 0 to console_width +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_width + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + + setb console_pos,0 + rts + + +; Hides console by disabling PPU rendering and blacking out +; first four entries of palette. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +; Shows console display +; Preserved: A, X, Y +console_show: + pha + lda #CONSOLE_COLOR + jsr console_show_custom_color_ + pla + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + sty console_temp + + ldy console_pos + cpy #console_width + beq console_full_ + sta console_buf,y + iny + sty console_pos + + ldy console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_scroll_up_ + setb console_pos,0 + pla + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_apply_scroll_ + pla + rts + + +;**** Internal routines **** + +console_full_: + ldy console_temp + + ; Line is full + + ; If space, treat as newline + cmp #' ' + beq console_newline + + ; Wrap current line at appropriate point + pha + tya + pha + jsr console_wrap_ + pla + tay + pla + + jmp console_print + + +; Inserts newline into buffer at appropriate position, leaving +; next line ready in buffer +; Preserved: X, console_temp +console_wrap_: + ; Find beginning of last word + ldy #console_width + lda #' ' +: dey + bmi console_newline + cmp console_buf,y + bne :- + + ; y = 0 to console_width-1 + + ; Flush through current word and put remaining + ; in buffer for next line + jsr console_wait_vbl_ + + ; Time to last PPU write: 207 + 32*(26 + 10) + + lda console_scroll + jsr console_set_ppuaddr_ + + stx console_pos ; save X + + ldx #0 + + ; Print everything before last word +: lda console_buf,x + sta PPUDATA + inx + dey + bpl :- + + ; x = 1 to console_width + + ; Move last word to beginning of buffer, and + ; print spaces for rest of line + ldy #0 + beq :++ +: lda #' ' + sta PPUDATA + lda console_buf,x + inx + sta console_buf,y + iny +: cpx #console_width + bne :-- + + ldx console_pos ; restore X + + ; Append new text after that + sty console_pos + + ; FALL THROUGH + + +; Scrolls up 8 pixels and clears one line BELOW new line +; Preserved: X, console_temp +console_scroll_up_: + ; Scroll up 8 pixels + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + + ; Clear line AFTER that on screen + jsr console_add_8_to_scroll_ + jsr console_set_ppuaddr_ + + ldy #console_width + lda #' ' +: sta PPUDATA + dey + bne :- + ; FALL THROUGH + + +; Applies current scrolling position to PPU +; Preserved: X, Y, console_temp +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + rts + + +; Sets PPU address for row +; In: A = scroll position +; Preserved: X, Y +console_set_ppuaddr_: + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + ora #console_left_margin + sta PPUADDR + rts + + +; A = (A + 8) % 240 +; Preserved: X, Y +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_show_custom_color_: + pha + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + pla + jsr console_load_palette_ + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + rts + + +; Initializes PPU if necessary, then waits for VBL +; Preserved: A, X, Y, console_temp +console_wait_vbl_: + lda console_scroll + cmp #-1 + bne @already_initialized + + ; Deferred initialization of PPU until first use of console + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + tya + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldy #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dey + bne :- + + ; Clear attributes + lda #0 + ldy #$40 +: sta PPUDATA + dey + bne :- + + pla + tay + + jsr console_show +@already_initialized: + jmp wait_vbl_optional + + +; Flushes current line +; Preserved: X, Y +console_flush_: + lda console_scroll + jsr console_set_ppuaddr_ + + sty console_temp + + ; Copy line + ldy #0 + beq :++ +: lda console_buf,y + sta PPUDATA + iny +: cpy console_pos + bne :-- + + ldy console_temp + rts diff --git a/apu_reset/source/common/crc.s b/apu_reset/source/common/crc.s new file mode 100644 index 0000000..de96c2a --- /dev/null +++ b/apu_reset/source/common/crc.s @@ -0,0 +1,118 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on + + +; EQ if checksum matches CRC +; Out: A=0 and EQ if match, A>0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/apu_reset/source/common/delay.s b/apu_reset/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/apu_reset/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/apu_reset/source/common/devcart.bin b/apu_reset/source/common/devcart.bin new file mode 100644 index 0000000..7180661 Binary files /dev/null and b/apu_reset/source/common/devcart.bin differ diff --git a/apu_reset/source/common/macros.inc b/apu_reset/source/common/macros.inc new file mode 100644 index 0000000..4307c66 --- /dev/null +++ b/apu_reset/source/common/macros.inc @@ -0,0 +1,188 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro inxy + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro + +; Negates A and adds it to operand +.macro subaf Operand + eor #$FF + sec + adc Operand +.endmacro + +; Initializes CPU registers to reasonable values +.macro init_cpu_regs + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + .ifndef BUILD_NSF + inx + stx PPUCTRL + .endif +.endmacro diff --git a/apu_reset/source/common/neshw.inc b/apu_reset/source/common/neshw.inc new file mode 100644 index 0000000..d636a97 --- /dev/null +++ b/apu_reset/source/common/neshw.inc @@ -0,0 +1,56 @@ +; NES I/O locations and masks + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 + +.ifndef REGION_FREE + .ifndef PAL_ONLY + .ifndef NTSC_ONLY + NTSC_ONLY = 1 + .endif + .endif +.else + .ifdef NTSC_ONLY + .error "NTSC_ONLY and REGION_FREE defined" + .endif + .ifdef PAL_ONLY + .error "PAL_ONLY and REGION_FREE defined" + .endif +.endif + +.ifdef NTSC_ONLY + CLOCK_RATE = 1789773 + PPU_FRAMELEN = 29781 +.endif + +.ifdef PAL_ONLY + CLOCK_RATE = 1662607 + PPU_FRAMELEN = 33248 +.endif diff --git a/apu_reset/source/common/ppu.s b/apu_reset/source/common/ppu.s new file mode 100644 index 0000000..4827b3e --- /dev/null +++ b/apu_reset/source/common/ppu.s @@ -0,0 +1,203 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts + + +.macro check_ppu_region_ Len + ; Delays since VBL began + jsr wait_vbl_optional ; 10 average + delay Len - 18 - 200 + lda PPUSTATUS ; 4 + bmi @ok ; 2 + delay 200 + ; Next VBL should roughly begin here if it's the + ; one we are detecting + delay 200 + lda PPUSTATUS ; 2 + bpl @ok +.endmacro + +check_ppu_region: + +.ifndef REGION_FREE +.ifdef PAL_ONLY + check_ppu_region_ 29781 + print_str {newline,"Note: This test is meant for PAL NES only.",newline,newline} +.endif + +.ifdef NTSC_ONLY + check_ppu_region_ 33248 + print_str {newline,"Note: This test is meant for NTSC NES only.",newline,newline} +.endif +.endif +@ok: rts + + +; Loads ASCII font into CHR RAM and fills rest with $FF +.macro load_chr_ram + bit PPUSTATUS + setb PPUADDR,0 + setb PPUADDR,0 + + ; Copy ascii_chr to 0 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page + + ; Fill rest + lda #$FF +: sta PPUDATA + iny + bne :- + inx + cpx #$20 + bne :- +.endmacro diff --git a/apu_reset/source/common/print.s b/apu_reset/source/common/print.s new file mode 100644 index 0000000..ab8a308 --- /dev/null +++ b/apu_reset/source/common/print.s @@ -0,0 +1,247 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr print_nibble_ + pla + + pha + and #$0F + jsr print_nibble_ + pla + rts + +print_nibble_: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints low 4 bits of A as single hex character +; Preserved: A, X, Y +print_nibble: + pha + and #$0F + jsr update_crc + jsr print_nibble_ + pla + rts + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: A, X, Y +print_dec: + pha + sta print_temp_ + jsr update_crc + txa + pha + lda print_temp_ + + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jsr print_char + pla + tax + pla + rts + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/apu_reset/source/common/run_at_reset.s b/apu_reset/source/common/run_at_reset.s new file mode 100644 index 0000000..17425b2 --- /dev/null +++ b/apu_reset/source/common/run_at_reset.s @@ -0,0 +1,64 @@ +; Keeps track of number of times reset, and prompts user. + +power_flag_value = $42 + +nv_res power_flag_ +nv_res num_resets_ + +; Out: A = number of times NES has been reset since turned on +; Preserved: X, Y +num_resets: + lda power_flag_ + cmp #power_flag_value + bne :+ + lda num_resets_ + rts +: lda #0 + rts + + +; Prompts user to press reset after message disappears, +; then hides message, increments reset count, and asks +; emulator to reset NES. +; Preserved: X, Y +prompt_to_reset: + print_str {newline,newline,"Press RESET"} + + ; Add "again" if this isn't first requested reset + jsr num_resets + beq :+ + print_str " again" +: + print_str newline,newline + + jsr inc_reset_count + + ; Show message + jsr console_show + + rts + + +; Increments reset count and marks it as valid +; Preserved: X, Y +inc_reset_count: + jsr num_resets + clc + adc #1 + bcc :+ + lda #$FF ; don't wrap around +: sta num_resets_ + + setb power_flag_,power_flag_value + + ; Tell emulator that NES should be reset now + lda #$81 + jsr set_final_result + + rts + + +; Waits in infinite loop for reset +; Preserved: A, X, Y, flags +wait_reset: + jmp wait_reset diff --git a/apu_reset/source/common/shell.inc b/apu_reset/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/apu_reset/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/apu_reset/source/common/shell.s b/apu_reset/source/common/shell.s new file mode 100644 index 0000000..d458c9b --- /dev/null +++ b/apu_reset/source/common/shell.s @@ -0,0 +1,180 @@ +; Shell that sets up testing framework and calls main + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +; Move code from $C000 to $E200, to accommodate my devcarts +.segment "CODE" + .res $2200 + +; Put shell code after user code, so user code is in more +; consistent environment +.segment "CODE2" + + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + init_cpu_regs + + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell without affecting current set_test values +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + ; Clear APU registers + lda #0 + sta $4015 + ldx #$13 +: sta $4000,x + dex + bpl :- + + ; CPU registers + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sta temp + init_cpu_regs + setb SNDCHN,0 + lda temp + + jsr report_result + pha + jsr check_ppu_region + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +.include "shell_misc.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts +.endif diff --git a/apu_reset/source/common/shell_misc.s b/apu_reset/source/common/shell_misc.s new file mode 100644 index 0000000..7e7d4e9 --- /dev/null +++ b/apu_reset/source/common/shell_misc.s @@ -0,0 +1,211 @@ +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +.macro fill_ram_ Begin, End + ; Simpler to count from negative size up to 0, + ; and adjust address downward to compensate + ; for initial low byte in Y index + .local Neg_size + Neg_size = (Begin) - (End) + ldxy #(Begin) - = 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/apu_reset/source/common/text_out.s b/apu_reset/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/apu_reset/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +The first time a line of text is completed, the console initializes the +PPU. Most print routines update a running CRC-32 checksum which can be +checked with check_crc. This allows ALL the output to be checked very +easily. If the checksum doesn't match, it is printed, so during +development the code can be run on a NES to get the correct checksum, +which is then typed into the test code. The checksum is also useful when +comparing different outputs; rather than having to examine all the +output, one need only compare checksums. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported by the included code, due to +their complexity. + + +Macros +------ +Some macros are used to make common operations more convenient, defined +in common/macros.inc. The left is equivalent to the right: + + Macro Equivalent + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + setb addr,byte lda #byte + sta addr + + setw addr,word lda #word + sta addr+1 + + ldxy #value ldx #>value + ldy # diff --git a/apu_reset/source/works_immediately.s b/apu_reset/source/works_immediately.s new file mode 100644 index 0000000..edb0c93 --- /dev/null +++ b/apu_reset/source/works_immediately.s @@ -0,0 +1,90 @@ +; At power and reset, $4017, $4015, and length counters work +; immediately. + +CUSTOM_RESET=1 +.include "shell.inc" +.include "run_at_reset.s" + +nv_res log,4 + +reset: + ; Triangle linear counter + setb $4008,$FF + setb $4017,$80 + + setb $4015,$0F + + ; Setup channels + setb $4000,$00 + setb $4001,$7F + setb $4002,$FF + setb $4003,$28 + + setb $4004,$00 + setb $4005,$7F + setb $4006,$FF + setb $4007,$28 + + setb $4008,$7F + setb $400A,$FF + setb $400B,$28 + + setb $400C,$00 + setb $400E,$00 + setb $400F,$28 + + setb $4010,$8F + setb $4013,1 + setb $4015,$1F + + delay 6000 + lda $4015 + sta log+0 + delay 2000 + lda $4015 + sta log+1 + + delay 29831*2 - 18*6 - 8016 + 14907 - 180 + lda $4015 + sta log+2 + delay 200 + lda $4015 + sta log+3 + + jmp std_reset + +main: jsr num_resets + bne first_reset + +power: set_test 2,"At power, writes should work immediately" + jsr test + + jsr prompt_to_reset + setb $4013,2 + setb $4015,$00 + setb $4017,$00 + jmp wait_reset + +first_reset: + set_test 3,"At reset, writes should work immediately" + jsr test + + jmp tests_passed + +test: + lda log+0 + and #$DF + cmp #$1F + jne test_failed + lda log+1 + and #$DF + cmp #$8F + jne test_failed + lda log+2 + and #$4F + cmp #$0F + jne test_failed + lda log+3 + and #$0F + jne test_failed + rts diff --git a/apu_reset/works_immediately.nes b/apu_reset/works_immediately.nes new file mode 100644 index 0000000..2fd3f39 Binary files /dev/null and b/apu_reset/works_immediately.nes differ diff --git a/apu_test/apu_test.nes b/apu_test/apu_test.nes new file mode 100644 index 0000000..257f227 Binary files /dev/null and b/apu_test/apu_test.nes differ diff --git a/apu_test/readme.txt b/apu_test/readme.txt new file mode 100644 index 0000000..85e4bda --- /dev/null +++ b/apu_test/readme.txt @@ -0,0 +1,169 @@ +NES APU Tests +------------- +These ROMs test many aspects of the APU that are visible to the CPU. +Really obsucre things are not tested here. + + +1-len_ctr +--------- +Tests length counter operation for the four main channels + +2) Problem with length counter load or $4015 +3) Problem with length table, timing, or $4015 +4) Writing $80 to $4017 should clock length immediately +5) Writing 0 to $4017 shouldn't clock length immediately +6) Disabling via $4015 should clear length counter +7) When disabled via $4015, length shouldn't allow reloading +8) Halt bit should suspend length clocking + + +2-len_table +----------- +Verifies all length table entries + + +3-irq_flag +---------- +Verifies basic operation of frame irq flag + +2) Flag shouldn't be set in $4017 mode $40 +3) Flag shouldn't be set in $4017 mode $80 +4) Flag should be set in $4017 mode $00 +5) Reading flag should clear it +6) Writing $00 or $80 to $4017 shouldn't affect flag +7) Writing $40 or $C0 to $4017 should clear flag + + +4-jitter +-------- +Tests for APU clock jitter. Also tests basic timing of frame irq flag +since it's needed to determine jitter. + +3) Frame irq is set too late +4) Even jitter not handled properly +5) Odd jitter not handled properly + + +5-len_timing +------------ +Verifies timing of length counter clocks in both modes + +2) First length of mode 0 is too soon +3) First length of mode 0 is too late +4) Second length of mode 0 is too soon +5) Second length of mode 0 is too late +6) Third length of mode 0 is too soon +7) Third length of mode 0 is too late +8) First length of mode 1 is too soon +9) First length of mode 1 is too late +10) Second length of mode 1 is too soon +11) Second length of mode 1 is too late +12) Third length of mode 1 is too soon +13) Third length of mode 1 is too late + + +6-irq_flag_timing +----------------- +Frame interrupt flag is set three times in a row 29831 clocks after +writing $00 to $4017. + +3) Flag first set too late +4) Flag last set too soon +5) Flag last set too late + + +7-dmc_basics +------------ +Verifies basic DMC operation + +2) DMC isn't working well enough to test further +3) Starting DMC should reload length from $4013 +4) Writing $10 to $4015 should restart DMC if previous sample finished +5) Writing $10 to $4015 should not affect DMC if previous sample is +still playing +6) Writing $00 to $4015 should stop current sample +7) Changing $4013 shouldn't affect current sample length +8) Shouldn't set DMC IRQ flag when flag is disabled +9) Should set IRQ flag when enabled and sample ends +10) Reading IRQ flag shouldn't clear it +11) Writing to $4015 should clear IRQ flag +12) Disabling IRQ flag should clear it +13) Looped sample shouldn't end until $00 is written to $4015 +14) Looped sample shouldn't ever set IRQ flag +15) Clearing loop flag and then setting again shouldn't stop loop +16) Clearing loop flag should end sample once it reaches end +17) Looped sample should reload length from $4013 each time it reaches +end +18) $4013=0 should give 1-byte sample +19) There should be a one-byte buffer that's filled immediately if empty + + +8-dmc_rates +----------- +Verifies the DMC's 16 rates + +Flashes, clicks, other glitches +------------------------------- +If a test prints "passed", it passed, even if there were some flashes or +odd sounds. Only a test which prints "done" at the end requires that you +watch/listen while it runs in order to determine whether it passed. Such +tests involve things which the CPU cannot directly test. + + +Alternate output +---------------- +Tests generally print information on screen, but also report the final +result audibly, and output text to memory, in case the PPU doesn't work +or there isn't one, as in an NSF or a NES emulator early in development. + +After the tests are done, the final result is reported as a series of +beeps (see below). For NSF builds, any important diagnostic bytes are +also reported as beeps, before the final result. + + +Output at $6000 +--------------- +All text output is written starting at $6004, with a zero-byte +terminator at the end. As more text is written, the terminator is moved +forward, so an emulator can print the current text at any time. + +The test status is written to $6000. $80 means the test is running, $81 +means the test needs the reset button pressed, but delayed by at least +100 msec from now. $00-$7F means the test has completed and given that +result code. + +To allow an emulator to know when one of these tests is running and the +data at $6000+ is valid, as opposed to some other NES program, $DE $B0 +$G1 is written to $6001-$6003. + + +Audible output +-------------- +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason. See the source +code of the test for more information about the meaning of a test code. +They are found after the set_test macro. For example, the cause of test +code 3 would be found in a line containing set_test 3. Examples: + + Tones Binary Decimal Meaning + - - - - - - - - - - - - - - - - - - - - + low 0 0 passed + low high 01 1 failed + low high low 010 2 error 2 + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to prevent the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +-- +Shay Green diff --git a/apu_test/rom_singles/1-len_ctr.nes b/apu_test/rom_singles/1-len_ctr.nes new file mode 100644 index 0000000..8f82d13 Binary files /dev/null and b/apu_test/rom_singles/1-len_ctr.nes differ diff --git a/apu_test/rom_singles/2-len_table.nes b/apu_test/rom_singles/2-len_table.nes new file mode 100644 index 0000000..9dfd09b Binary files /dev/null and b/apu_test/rom_singles/2-len_table.nes differ diff --git a/apu_test/rom_singles/3-irq_flag.nes b/apu_test/rom_singles/3-irq_flag.nes new file mode 100644 index 0000000..3e91c17 Binary files /dev/null and b/apu_test/rom_singles/3-irq_flag.nes differ diff --git a/apu_test/rom_singles/4-jitter.nes b/apu_test/rom_singles/4-jitter.nes new file mode 100644 index 0000000..93f219d Binary files /dev/null and b/apu_test/rom_singles/4-jitter.nes differ diff --git a/apu_test/rom_singles/5-len_timing.nes b/apu_test/rom_singles/5-len_timing.nes new file mode 100644 index 0000000..e6c3c33 Binary files /dev/null and b/apu_test/rom_singles/5-len_timing.nes differ diff --git a/apu_test/rom_singles/6-irq_flag_timing.nes b/apu_test/rom_singles/6-irq_flag_timing.nes new file mode 100644 index 0000000..c705de4 Binary files /dev/null and b/apu_test/rom_singles/6-irq_flag_timing.nes differ diff --git a/apu_test/rom_singles/7-dmc_basics.nes b/apu_test/rom_singles/7-dmc_basics.nes new file mode 100644 index 0000000..17ba304 Binary files /dev/null and b/apu_test/rom_singles/7-dmc_basics.nes differ diff --git a/apu_test/rom_singles/8-dmc_rates.nes b/apu_test/rom_singles/8-dmc_rates.nes new file mode 100644 index 0000000..0e72d6d Binary files /dev/null and b/apu_test/rom_singles/8-dmc_rates.nes differ diff --git a/apu_test/source/1-len_ctr.s b/apu_test/source/1-len_ctr.s new file mode 100644 index 0000000..c1018d5 --- /dev/null +++ b/apu_test/source/1-len_ctr.s @@ -0,0 +1,52 @@ +; Tests length counter operation for the four main channels + +.include "apu_shell.inc" + +main: test_main_chans test + jmp tests_passed + +test: + set_test 2,"Problem with length counter load or $4015" + mov $4015,chan_bit ; enable channel + setb {$4003,x},len_2 ; load length + jsr should_be_playing + + set_test 3,"Problem with length table, timing, or $4015" + delay_msec 30 + jsr should_be_silent ; length should have reached 0 by now + + set_test 4,"Writing $80 to $4017 should clock length immediately" + setb $4017,0 + setb {$4003,x},len_2 + setb $4017,$80 ; should clock length immediately + setb $4017,$80 ; should clock length immediately + jsr should_be_silent + + set_test 5,"Writing 0 to $4017 shouldn't clock length immediately" + setb {$4003,x},len_2 + setb $4017,0 ; shouldn't clock length + setb $4017,0 ; shouldn't clock length + jsr should_be_playing + + set_test 6,"Disabling via $4015 should clear length counter" + setb {$4003,x},len_2 + setb $4015,0 ; should clear length immediately + jsr should_be_silent + mov $4015,chan_bit + jsr should_be_silent ; length should still be clear + + set_test 7,"When disabled via $4015, length shouldn't allow reloading" + setb $4015,0 + setb {$4003,x},len_2 ; shouldn't reload length + jsr should_be_silent + mov $4015,chan_bit + jsr should_be_silent ; length should still be clear + + set_test 8,"Halt bit should suspend length clocking" + setb {$4000,x},halt ; halt length + setb {$4003,x},len_2 + setb $4017,$80 ; attempt to clock length twice + setb $4017,$80 + jsr should_be_playing + + rts diff --git a/apu_test/source/2-len_table.s b/apu_test/source/2-len_table.s new file mode 100644 index 0000000..a5bde05 --- /dev/null +++ b/apu_test/source/2-len_table.s @@ -0,0 +1,36 @@ +; Verifies all length table entries + +.include "apu_shell.inc" + +main: test_main_chans test + jmp tests_passed + +test: loop_n_times test_entry,32 + rts + +test_entry: + tay + setb SNDMODE,0 + + ; Load + tya + asl a + asl a + asl a + ldx chan_off + sta $4003,x + + ; Verify + lda table,y + tax + dex +: setb SNDMODE,$C0 ; clock length counter + dex + bne :- + jsr len_should_be_1 + rts + +table: .byte 10, 254, 20, 2, 40, 4, 80, 6 + .byte 160, 8, 60, 10, 14, 12, 26, 14 + .byte 12, 16, 24, 18, 48, 20, 96, 22 + .byte 192, 24, 72, 26, 16, 28, 32, 30 diff --git a/apu_test/source/3-irq_flag.s b/apu_test/source/3-irq_flag.s new file mode 100644 index 0000000..be6b96e --- /dev/null +++ b/apu_test/source/3-irq_flag.s @@ -0,0 +1,53 @@ +; Verifies basic operation of frame irq flag + +.include "shell.inc" + +should_be_clear: + lda $4015 + and #$40 + jne test_failed + rts + +should_be_set: + lda $4015 + and #$40 + jeq test_failed + rts + +main: + set_test 2,"Flag shouldn't be set in $4017 mode $40" + setb $4017,$40 + delay_msec 20 + jsr should_be_clear + + set_test 3,"Flag shouldn't be set in $4017 mode $80" + setb $4017,$80 + delay_msec 20 + jsr should_be_clear + + set_test 4,"Flag should be set in $4017 mode $00" + setb $4017,0 + delay_msec 20 + jsr should_be_set + + set_test 5,"Reading flag should clear it" + setb $4017,0 + delay_msec 20 + jsr should_be_set + jsr should_be_clear + + set_test 6,"Writing $00 or $80 to $4017 shouldn't affect flag" + setb $4017,0 + delay_msec 20 + setb $4017,0 + setb $4017,$80 + jsr should_be_set + + set_test 7,"Writing $40 or $C0 to $4017 should clear flag" + setb $4017,0 + delay_msec 20 + setb $4017,$40 + setb $4017,0 + jsr should_be_clear + + jmp tests_passed diff --git a/apu_test/source/4-jitter.s b/apu_test/source/4-jitter.s new file mode 100644 index 0000000..111ee53 --- /dev/null +++ b/apu_test/source/4-jitter.s @@ -0,0 +1,47 @@ +; Tests for APU clock jitter. Also tests basic timing of frame irq flag +; since it's needed to determine jitter. + +.include "shell.inc" + +; Returns current jitter in A. Takes an even number of clocks. +get_jitter: + delay 3 ; make routine an even number of clocks + setb SNDMODE,$40 ; clear frame irq flag + setb SNDMODE,$00 ; begin mode 0, frame irq enabled + delay 29827 + lda SNDCHN ; read at 29831 + and #$40 + rts + +main: set_test 2,"Frame irq is set too soon" + setb SNDMODE,$40 ; clear frame irq flag + setb SNDMODE,$00 ; begin mode 0, frame irq enabled + delay 29826 + lda SNDCHN ; read at 29830 + and #$40 + jne test_failed + + set_test 3,"Frame irq is set too late" + setb SNDMODE,$40 ; clear frame irq flag + setb SNDMODE,$00 ; begin mode 0, frame irq enabled + delay 29828 + lda SNDCHN ; read at 29832 + and #$40 + jeq test_failed + + set_test 4,"Even jitter not handled properly" + jsr get_jitter + sta 0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/apu_test/source/common/delay.s b/apu_test/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/apu_test/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/apu_test/source/common/devcart.bin b/apu_test/source/common/devcart.bin new file mode 100644 index 0000000..7180661 Binary files /dev/null and b/apu_test/source/common/devcart.bin differ diff --git a/apu_test/source/common/macros.inc b/apu_test/source/common/macros.inc new file mode 100644 index 0000000..4307c66 --- /dev/null +++ b/apu_test/source/common/macros.inc @@ -0,0 +1,188 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro inxy + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro + +; Negates A and adds it to operand +.macro subaf Operand + eor #$FF + sec + adc Operand +.endmacro + +; Initializes CPU registers to reasonable values +.macro init_cpu_regs + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + .ifndef BUILD_NSF + inx + stx PPUCTRL + .endif +.endmacro diff --git a/apu_test/source/common/neshw.inc b/apu_test/source/common/neshw.inc new file mode 100644 index 0000000..d636a97 --- /dev/null +++ b/apu_test/source/common/neshw.inc @@ -0,0 +1,56 @@ +; NES I/O locations and masks + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 + +.ifndef REGION_FREE + .ifndef PAL_ONLY + .ifndef NTSC_ONLY + NTSC_ONLY = 1 + .endif + .endif +.else + .ifdef NTSC_ONLY + .error "NTSC_ONLY and REGION_FREE defined" + .endif + .ifdef PAL_ONLY + .error "PAL_ONLY and REGION_FREE defined" + .endif +.endif + +.ifdef NTSC_ONLY + CLOCK_RATE = 1789773 + PPU_FRAMELEN = 29781 +.endif + +.ifdef PAL_ONLY + CLOCK_RATE = 1662607 + PPU_FRAMELEN = 33248 +.endif diff --git a/apu_test/source/common/ppu.s b/apu_test/source/common/ppu.s new file mode 100644 index 0000000..4827b3e --- /dev/null +++ b/apu_test/source/common/ppu.s @@ -0,0 +1,203 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts + + +.macro check_ppu_region_ Len + ; Delays since VBL began + jsr wait_vbl_optional ; 10 average + delay Len - 18 - 200 + lda PPUSTATUS ; 4 + bmi @ok ; 2 + delay 200 + ; Next VBL should roughly begin here if it's the + ; one we are detecting + delay 200 + lda PPUSTATUS ; 2 + bpl @ok +.endmacro + +check_ppu_region: + +.ifndef REGION_FREE +.ifdef PAL_ONLY + check_ppu_region_ 29781 + print_str {newline,"Note: This test is meant for PAL NES only.",newline,newline} +.endif + +.ifdef NTSC_ONLY + check_ppu_region_ 33248 + print_str {newline,"Note: This test is meant for NTSC NES only.",newline,newline} +.endif +.endif +@ok: rts + + +; Loads ASCII font into CHR RAM and fills rest with $FF +.macro load_chr_ram + bit PPUSTATUS + setb PPUADDR,0 + setb PPUADDR,0 + + ; Copy ascii_chr to 0 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page + + ; Fill rest + lda #$FF +: sta PPUDATA + iny + bne :- + inx + cpx #$20 + bne :- +.endmacro diff --git a/apu_test/source/common/print.s b/apu_test/source/common/print.s new file mode 100644 index 0000000..ab8a308 --- /dev/null +++ b/apu_test/source/common/print.s @@ -0,0 +1,247 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr print_nibble_ + pla + + pha + and #$0F + jsr print_nibble_ + pla + rts + +print_nibble_: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints low 4 bits of A as single hex character +; Preserved: A, X, Y +print_nibble: + pha + and #$0F + jsr update_crc + jsr print_nibble_ + pla + rts + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: A, X, Y +print_dec: + pha + sta print_temp_ + jsr update_crc + txa + pha + lda print_temp_ + + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jsr print_char + pla + tax + pla + rts + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/apu_test/source/common/shell.inc b/apu_test/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/apu_test/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/apu_test/source/common/shell.s b/apu_test/source/common/shell.s new file mode 100644 index 0000000..d458c9b --- /dev/null +++ b/apu_test/source/common/shell.s @@ -0,0 +1,180 @@ +; Shell that sets up testing framework and calls main + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +; Move code from $C000 to $E200, to accommodate my devcarts +.segment "CODE" + .res $2200 + +; Put shell code after user code, so user code is in more +; consistent environment +.segment "CODE2" + + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + init_cpu_regs + + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell without affecting current set_test values +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + ; Clear APU registers + lda #0 + sta $4015 + ldx #$13 +: sta $4000,x + dex + bpl :- + + ; CPU registers + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sta temp + init_cpu_regs + setb SNDCHN,0 + lda temp + + jsr report_result + pha + jsr check_ppu_region + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +.include "shell_misc.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts +.endif diff --git a/apu_test/source/common/shell_misc.s b/apu_test/source/common/shell_misc.s new file mode 100644 index 0000000..7e7d4e9 --- /dev/null +++ b/apu_test/source/common/shell_misc.s @@ -0,0 +1,211 @@ +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +.macro fill_ram_ Begin, End + ; Simpler to count from negative size up to 0, + ; and adjust address downward to compensate + ; for initial low byte in Y index + .local Neg_size + Neg_size = (Begin) - (End) + ldxy #(Begin) - = 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/apu_test/source/common/text_out.s b/apu_test/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/apu_test/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +The first time a line of text is completed, the console initializes the +PPU. Most print routines update a running CRC-32 checksum which can be +checked with check_crc. This allows ALL the output to be checked very +easily. If the checksum doesn't match, it is printed, so during +development the code can be run on a NES to get the correct checksum, +which is then typed into the test code. The checksum is also useful when +comparing different outputs; rather than having to examine all the +output, one need only compare checksums. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported by the included code, due to +their complexity. + + +Macros +------ +Some macros are used to make common operations more convenient, defined +in common/macros.inc. The left is equivalent to the right: + + Macro Equivalent + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + setb addr,byte lda #byte + sta addr + + setw addr,word lda #word + sta addr+1 + + ldxy #value ldx #>value + ldy # diff --git a/blargg_apu_2005.07.30/01.len_ctr.nes b/blargg_apu_2005.07.30/01.len_ctr.nes new file mode 100644 index 0000000..60de6d9 Binary files /dev/null and b/blargg_apu_2005.07.30/01.len_ctr.nes differ diff --git a/blargg_apu_2005.07.30/02.len_table.nes b/blargg_apu_2005.07.30/02.len_table.nes new file mode 100644 index 0000000..a084a14 Binary files /dev/null and b/blargg_apu_2005.07.30/02.len_table.nes differ diff --git a/blargg_apu_2005.07.30/03.irq_flag.nes b/blargg_apu_2005.07.30/03.irq_flag.nes new file mode 100644 index 0000000..3d5cb4e Binary files /dev/null and b/blargg_apu_2005.07.30/03.irq_flag.nes differ diff --git a/blargg_apu_2005.07.30/04.clock_jitter.nes b/blargg_apu_2005.07.30/04.clock_jitter.nes new file mode 100644 index 0000000..8c45d02 Binary files /dev/null and b/blargg_apu_2005.07.30/04.clock_jitter.nes differ diff --git a/blargg_apu_2005.07.30/05.len_timing_mode0.nes b/blargg_apu_2005.07.30/05.len_timing_mode0.nes new file mode 100644 index 0000000..1b878e6 Binary files /dev/null and b/blargg_apu_2005.07.30/05.len_timing_mode0.nes differ diff --git a/blargg_apu_2005.07.30/06.len_timing_mode1.nes b/blargg_apu_2005.07.30/06.len_timing_mode1.nes new file mode 100644 index 0000000..5aea9d2 Binary files /dev/null and b/blargg_apu_2005.07.30/06.len_timing_mode1.nes differ diff --git a/blargg_apu_2005.07.30/07.irq_flag_timing.nes b/blargg_apu_2005.07.30/07.irq_flag_timing.nes new file mode 100644 index 0000000..1ec9b88 Binary files /dev/null and b/blargg_apu_2005.07.30/07.irq_flag_timing.nes differ diff --git a/blargg_apu_2005.07.30/08.irq_timing.nes b/blargg_apu_2005.07.30/08.irq_timing.nes new file mode 100644 index 0000000..56f1887 Binary files /dev/null and b/blargg_apu_2005.07.30/08.irq_timing.nes differ diff --git a/blargg_apu_2005.07.30/09.reset_timing.nes b/blargg_apu_2005.07.30/09.reset_timing.nes new file mode 100644 index 0000000..0ea3fc6 Binary files /dev/null and b/blargg_apu_2005.07.30/09.reset_timing.nes differ diff --git a/blargg_apu_2005.07.30/10.len_halt_timing.nes b/blargg_apu_2005.07.30/10.len_halt_timing.nes new file mode 100644 index 0000000..d64a74b Binary files /dev/null and b/blargg_apu_2005.07.30/10.len_halt_timing.nes differ diff --git a/blargg_apu_2005.07.30/11.len_reload_timing.nes b/blargg_apu_2005.07.30/11.len_reload_timing.nes new file mode 100644 index 0000000..8030fc9 Binary files /dev/null and b/blargg_apu_2005.07.30/11.len_reload_timing.nes differ diff --git a/blargg_apu_2005.07.30/readme.txt b/blargg_apu_2005.07.30/readme.txt new file mode 100644 index 0000000..47887c8 --- /dev/null +++ b/blargg_apu_2005.07.30/readme.txt @@ -0,0 +1,187 @@ +NES APU Frame Counter Update +---------------------------- + +I have run more tests on the NES APU and come up with new information +about the exact timing of the frame counter and length counter, and some +subtle behavior. The information here either extends or contradicts what +is stated in the NES APU reference and on the nesdev wiki. + +Not documented here is a delay when changing modes by writing to $4017. +This is quite complex and I haven't fully worked out its exact +operation. Once determined, documented, and tested, the information here +should still be valid. This delay when changing modes involves the +current mode running a few clocks before switching to the new mode, so +it only affects the rare case where $4017 is written within a few clocks +of a frame counter step. This delay does not cause the steps to occur +any later than shown below; it only causes the first few clocks of the +new mode to be transparent, allowing the previous mode to "show +through". + +Also not documented is the exact operation of the envelope, sweep, and +triangle's linear counter when register writes occur close to clocking. + +Refer to tests.txt for a description of the test ROMs included. + +I have not yet fully updated my APU emulator and tested it with this +information, so report any problems you have with implementation. + +Shay (swap to e-mail) + + +Clock Jitter +------------ +Changes to the mode by writing to $4017 only occur on *even* internal +APU clocks; if written on an odd clock, the first step of the mode is +delayed by one clock. At power-up and reset, the APU is randomly in an +odd or even cycle with respect to the first clock of the first +instruction executed by the CPU. + + ; assume even APU and CPU clocks occur together + lda #$00 + sta $4017 ; mode begins in one clock + sta <0 ; delay 3 clocks + sta $4017 ; mode begins immediately + + +Mode 0 Timing +------------- +-5 lda #$00 +-3 sta $4017 +0 (write occurs here) +1 +2 +3 +... + Step 1 +7459 Clock linear +... + Step 2 +14915 Clock linear & length +... + Step 3 +22373 Clock linear +... + Step 4 +29830 Set frame irq +29831 Clock linear & length and set frame irq +29832 Set frame irq +... + Step 1 +37289 Clock linear +... +etc. + + +Mode 1 Timing +------------- +-5 lda #$80 +-3 sta $4017 +0 (write occurs here) + Step 0 +1 Clock linear & length +2 +... + Step 1 +7459 Clock linear +... + Step 2 +14915 Clock linear & length +... + Step 3 +22373 Clock linear +... + Step 4 +29829 (do nothing) +... + Step 0 +37283 Clock linear & length +... +etc. + + +Length Halt +----------- +Write to halt flag is delayed by one clock: + + $10->$4000 clear halt flag +0 $00->$4017 begin mode 0 +14914 $30->$4000 set halt flag +14915 Length not clocked + + $10->$4000 clear halt flag +0 $00->$4017 begin mode 0 +14915 $30->$4000 set halt flag + Length clocked + + $30->$4000 set halt flag +0 $00->$4017 begin mode 0 +14914 $10->$4000 clear halt flag +14915 Length clocked + + $30->$4000 set halt flag +0 $00->$4017 begin mode 0 +14915 $10->$4000 clear halt flag + Length not clocked + + + +Length Reload +------------- +Length reload is completely ignored if written during length clocking +and length counter is non-zero before clocking: + + $38->$4003 make length non-zero +0 $00->$4017 +14914 Write to $4003 + Length reloaded +14915 Length clocked + + $38->$4003 make length non-zero +0 $00->$4017 +14915 Write to $4003 + Length not reloaded + Length clocked + + $00->$4015 clear length counter + $01->$4015 +0 $00->$4017 +14915 Write to $4003 + Length reloaded + Length not clocked + +Misc +---- +- The frame IRQ flag is cleared only when $4015 is read or $4017 is +written with bit 6 set ($40 or $c0). + +- The IRQ handler is invoked at minimum 29833 clocks after writing $00 +to $4017 (assuming the frame IRQ flag isn't already set, and nothing +else generates an IRQ during that time). + +- After reset or power-up, APU acts as if $4017 were written with $00 +from 9 to 12 clocks before first instruction begins. It is as if this +occurs (this generates a 10 clock delay): + + lda #$00 + sta $4017 ; 1 + lda <0 ; 9 delay + nop + nop + nop +reset: + ... + +- As shown, the frame irq flag is set three times in a row. Thus when +polling it, always read $4015 an extra time after the flag is found to +be set, to be sure it's clear afterwards, + +wait: bit $4015 ; V flag reflects frame IRQ flag + bvc wait + bit $4015 ; be sure irq flag is clear + +or better yet, clear it before polling it: + + bit $4015 ; clear flag first +wait: bit $4015 ; V flag reflects frame IRQ flag + bvc wait + diff --git a/blargg_apu_2005.07.30/source/01.len_ctr.asm b/blargg_apu_2005.07.30/source/01.len_ctr.asm new file mode 100644 index 0000000..e241e27 --- /dev/null +++ b/blargg_apu_2005.07.30/source/01.len_ctr.asm @@ -0,0 +1,90 @@ +; Tests basic length counter operation + + .include "prefix_apu.a" + +; to do: test triangle channel's differing halt bit position + +reset: + jsr setup_apu + + lda #2;) Problem with length counter load or $4015 + sta result + lda #$18 ; length = 2 + sta $4003 + jsr should_be_playing + + lda #3;) Problem with length table, timing, or $4015 + sta result + lda #250 ; delay 250 msec + jsr delay_msec + jsr should_be_silent + + lda #4;) Writing $80 to $4017 should clock length immediately + sta result + lda #$00 ; mode 0 + sta $4017 + lda #$18 ; length = 2 + sta $4003 + lda #$80 ; clock length twice + sta $4017 + sta $4017 + jsr should_be_silent + + lda #5;) Writing $00 to $4017 shouldn't clock length immediately + sta result + lda #$00 ; mode 0 + sta $4017 + lda #$18 ; length = 2 + sta $4003 + lda #$00 ; write mode twice + sta $4017 + sta $4017 + jsr should_be_playing + + lda #6;) Clearing enable bit in $4015 should clear length counter + sta result + lda #$18 ; length = 2 + sta $4003 + lda #$00 + sta $4015 + lda #$01 + sta $4015 + jsr should_be_silent + + lda #7;) When disabled via $4015, length shouldn't allow reloading + sta result + lda #$00 + sta $4015 + lda #$18 ; attempt to reload + sta $4003 + lda #$01 + sta $4015 + jsr should_be_silent + + lda #8;) Halt bit should suspend length clocking + sta result + lda #$30 ; halt length + sta $4000 + lda #$18 ; length = 2 + sta $4003 + lda #$80 ; attempt to clock length twice + sta $4017 + sta $4017 + jsr should_be_playing + + lda #1;) Passed tests + sta result +error: + jmp report_final_result + +should_be_playing: + lda $4015 + and #$01 + beq error + rts + +should_be_silent: + lda $4015 + and #$01 + bne error + rts diff --git a/blargg_apu_2005.07.30/source/02.len_table.asm b/blargg_apu_2005.07.30/source/02.len_table.asm new file mode 100644 index 0000000..4080688 --- /dev/null +++ b/blargg_apu_2005.07.30/source/02.len_table.asm @@ -0,0 +1,57 @@ +; Tests all length table entries. +; +; 1) Passed +; 2) Failed. Prints four bytes $II $ee $cc $02 that indicate the length +; load value written (ll), the value that the emulator uses ($ee), and the +; correct value ($cc). + + .include "prefix_apu.a" + +; Zero-page +entry = 10 + +reset: + jsr setup_apu + + lda #31 + sta entry +loop: lda #$00 ; sync apu + sta $4017 + lda entry + asl a + asl a + asl a + sta $4003 ; load length + lda #$01 ; check resulting length + jsr count_length + ldx entry + cmp table,x + bne error + dec entry + bpl loop + + lda #1;) Passed +report: + sta result + jmp report_final_result + +error: + pha + lda entry + asl a + asl a + asl a + jsr debug_byte ; entry + pla + jsr debug_byte ; value detected + ldx entry + lda table,x + jsr debug_byte ; correct value + lda #2 + jmp report + +table: + .byte 10, 254, 20, 2, 40, 4, 80, 6 + .byte 160, 8, 60, 10, 14, 12, 26, 14 + .byte 12, 16, 24, 18, 48, 20, 96, 22 + .byte 192, 24, 72, 26, 16, 28, 32, 30 diff --git a/blargg_apu_2005.07.30/source/03.irq_flag.asm b/blargg_apu_2005.07.30/source/03.irq_flag.asm new file mode 100644 index 0000000..dce4e3e --- /dev/null +++ b/blargg_apu_2005.07.30/source/03.irq_flag.asm @@ -0,0 +1,82 @@ +; Tests basic operation of frame irq flag. + + .include "prefix_apu.a" + +reset: + jsr setup_apu + + lda #2;) Flag shouldn't be set in $4017 mode $40 + sta result + lda #$40 + sta $4017 + lda #20 + jsr delay_msec + jsr should_be_clear + + lda #3;) Flag shouldn't be set in $4017 mode $80 + sta result + lda #$80 + sta $4017 + lda #20 + jsr delay_msec + jsr should_be_clear + + lda #4;) Flag should be set in $4017 mode $00 + sta result + lda #$00 + sta $4017 + lda #20 + jsr delay_msec + jsr should_be_set + + lda #5;) Reading flag clears it + sta result + lda #$00 + sta $4017 + lda #20 + jsr delay_msec + lda $4015 + jsr should_be_clear + + lda #6;) Writing $00 or $80 to $4017 doesn't affect flag + sta result + lda #$00 + sta $4017 + lda #20 + jsr delay_msec + lda #$00 + sta $4017 + lda #$80 + sta $4017 + jsr should_be_set + + lda #7;) Writing $40 or $c0 to $4017 clears flag + sta result + lda #$00 + sta $4017 + lda #20 + jsr delay_msec + lda #$40 + sta $4017 + lda #$00 + sta $4017 + jsr should_be_clear + + lda #1;) Tests passed + sta result +error: + jmp report_final_result + +; Report error if flag isn't clear +should_be_clear: + lda $4015 + and #$40 + bne error + rts + +; Report error if flag isn't set +should_be_set: + lda $4015 + and #$40 + beq error + rts diff --git a/blargg_apu_2005.07.30/source/04.clock_jitter.asm b/blargg_apu_2005.07.30/source/04.clock_jitter.asm new file mode 100644 index 0000000..d364596 --- /dev/null +++ b/blargg_apu_2005.07.30/source/04.clock_jitter.asm @@ -0,0 +1,71 @@ +; Tests for APU clock jitter. Also tests basic timing of frame irq flag +; since it's needed to determine jitter. + + .include "prefix_apu.a" + +jitter = 1 + +reset: + jsr setup_apu + + lda #2;) Frame irq is set too soon + sta result + lda #$40 ; clear frame irq flag + sta $4017 + lda #$00 ; begin mode 0, frame irq enabled + sta $4017 + ldy #48 ; 29826 delay + lda #123 + jsr delay_ya1 + lda $4015 ; read at 29830 + and #$40 + bne error + + lda #3;) Frame irq is set too late + sta result + lda #$40 ; clear frame irq flag + sta $4017 + lda #$00 ; begin mode 0, frame irq enabled + sta $4017 + ldy #48 ; 29828 delay + lda #123 + jsr delay_ya3 + lda $4015 ; read at 29832 + and #$40 + beq error + + lda #4;) Even jitter not handled properly + sta result + jsr get_jitter + sta 0 should be ignored + sta result + jsr sync_apu + lda #$38 ; length = 6 + sta reload + lda #$40 ; begin mode 0 + sta $4017 + ldy #244 ; 14909 delay + lda #11 + jsr delay_ya8 + lda #$18 ; try to reload counter with 2 + sta reload ; write at 14915 + lda #mask + jsr count_length + cmp #5 ; should have ignored reload + bne error + + lda #1;) Passed tests + sta result +error: + jmp report_final_result diff --git a/blargg_apu_2005.07.30/source/apu_util.asm b/blargg_apu_2005.07.30/source/apu_util.asm new file mode 100644 index 0000000..5748f53 --- /dev/null +++ b/blargg_apu_2005.07.30/source/apu_util.asm @@ -0,0 +1,57 @@ +; Set up APU with square 1 enabled and unhalted + .code +setup_apu: + sei + lda #$40 ; mode 0, interrupt disabled + sta $4017 + lda #$01 ; enable square 1 + sta $4015 + lda #$10 ; unhalt length + sta $4000 + lda #$7f ; sweep off + sta $4001 + lda #$ff ; period + sta $4002 + rts + +; Synchronize APU divide-by-two so that an sta $4017 will +; start the frame counter without an extra clock delay. +; Takes 16 msec to execute. + .code +sync_apu: + sei + lda #$40 ; clear irq flag + sta $4017 + lda #$00 ; mode 0, frame irq enabled + sta $4017 + ldy #48 ; 29827 delay + lda #123 + jsr delay_ya2 + lda $4015 + and #$40 + bne + ; delay extra clock if odd jitter +: lda #$40 ; clear irq flag + sta $4017 + rts + + .code + +; Count number of length clocks required until $4015 AND A becomes 0 +; then return result in A. + .code +count_length: + ldy #0 + bit $4015 + beq count_length_end + ldx #$c0 +: stx $4017 + iny + beq count_length_overflow + bit $4015 + bne - +count_length_end: + tya + rts +count_length_overflow: + lda #$ff + rts diff --git a/blargg_apu_2005.07.30/source/prefix_apu.asm b/blargg_apu_2005.07.30/source/prefix_apu.asm new file mode 100644 index 0000000..a6f3a4e --- /dev/null +++ b/blargg_apu_2005.07.30/source/prefix_apu.asm @@ -0,0 +1,40 @@ +; Prefix included at the beginning of each test. Defines vectors +; and other things for custom devcart loader. +; +; Refer to apu_util.asm for APU test-specific routines: + + .include "apu_util.asm" + +; Reset vector points to "reset". +; NMI points to "nmi" if defined, otherwise default_nmi. +; IRQ points to "irq" if defined, otherwise default_irq. + +default_nmi: + rti + +default_irq: + bit $4015 + rti + +; Delays for almost A milliseconds (A * 0.999009524 msec) +; Preserved: X, Y +delay_msec: + +; Delays for almost 'A / 10' milliseconds (A * 0.099453968 msec) +; Preserved: X, Y +delay_01_msec: + +; Variable delay. All calls include comment stating number of clocks +; used, including the setup code in the caller. +delay_yaNN: + +; Print byte A to console as $hh +; Preserved: A, X, Y +debug_byte: + +; Report "result" as number of beeps and code printed to console, +; then jump to forever. +report_final_result: + +; Disable IRQ and NMI then loop endlessly. +forever: diff --git a/blargg_apu_2005.07.30/tests.txt b/blargg_apu_2005.07.30/tests.txt new file mode 100644 index 0000000..7616131 --- /dev/null +++ b/blargg_apu_2005.07.30/tests.txt @@ -0,0 +1,160 @@ +NES APU Frame Counter Test ROMs +------------------------------- + +These ROMs test the APU frame counter operation and length counter +clocking. They have been tested on an actual NES and all give a passing +result. They can't catch every possible bug in an emulator, but they +check for likely problems like one-off errors. + +The tests do not test clocking of the envelope, sweep, or triangle's +linear counter. Also, only the length counter on the first square wave +($4000-$4003) is tested. + +The main source code for each test is included. In the tests, remember +that the actual read/write of sta/lda $xxxx occurs on the fourth (last) +clock if the instruction. Some of the common support code is included, +but not all, since it runs on a custom setup. Contact me if you want to +assemble the tests yourself. + +Each ROM only gives meaningful results if all previous test ROMs have +passed without error. + +Each ROM runs several tests and reports a result code on screen and by +beeping a number of times. A result code of 1 always indicates that all +tests were passed. Other result codes indicate a problem or behavior +that isn't implemented as stated: + +Shay (swap to e-mail) + + +01.len_ctr +---------- +Tests basic length counter operation + +1) Passed tests +2) Problem with length counter load or $4015 +3) Problem with length table, timing, or $4015 +4) Writing $80 to $4017 should clock length immediately +5) Writing $00 to $4017 shouldn't clock length immediately +6) Clearing enable bit in $4015 should clear length counter +7) When disabled via $4015, length shouldn't allow reloading +8) Halt bit should suspend length clocking + + +02.len_table +------------ +Tests all length table entries. + +1) Passed +2) Failed. Prints four bytes $II $ee $cc $02 that indicate the length +load value written (ll), the value that the emulator uses ($ee), and the +correct value ($cc). + + +03.irq_flag +----------- +Tests basic operation of frame irq flag. + +1) Tests passed +2) Flag shouldn't be set in $4017 mode $40 +3) Flag shouldn't be set in $4017 mode $80 +4) Flag should be set in $4017 mode $00 +5) Reading flag clears it +6) Writing $00 or $80 to $4017 doesn't affect flag +7) Writing $40 or $c0 to $4017 clears flag + + +04.clock_jitter +--------------- +Tests for APU clock jitter. Also tests basic timing of frame irq flag +since it's needed to determine jitter. + +1) Passed tests +2) Frame irq is set too soon +3) Frame irq is set too late +4) Even jitter not handled properly +5) Odd jitter not handled properly + + +05.len_timing_mode0 +------------------- +Return current jitter in A. Takes an even number of clocks. Tests length +counter timing in mode 0. + +1) Passed tests +2) First length is clocked too soon +3) First length is clocked too late +4) Second length is clocked too soon +5) Second length is clocked too late +6) Third length is clocked too soon +7) Third length is clocked too late + + +06.len_timing_mode1 +------------------- +Tests length counter timing in mode 1. + +1) Passed tests +2) First length is clocked too soon +3) First length is clocked too late +4) Second length is clocked too soon +5) Second length is clocked too late +6) Third length is clocked too soon +7) Third length is clocked too late + + +07.irq_flag_timing +------------------ +Frame interrupt flag is set three times in a row 29831 clocks after +writing $4017 with $00. + +1) Success +2) Flag first set too soon +3) Flag first set too late +4) Flag last set too soon +5) Flag last set too late + + +08.irq_timing +------------- +IRQ handler is invoked at minimum 29833 clocks after writing $00 to +$4017. + +1) Passed tests +2) Too soon +3) Too late +4) Never occurred + + +09.reset_timing +--------------- +After reset or power-up, APU acts as if $4017 were written with $00 from +9 to 12 clocks before first instruction begins. + +1) Success +2) $4015 didn't read back as $00 at power-up +3) Fourth step occurs too soon +4) Fourth step occurs too late + + +10.len_halt_timing +------------------ +Changes to length counter halt occur after clocking length, not before. + +1) Passed tests +2) Length shouldn't be clocked when halted at 14914 +3) Length should be clocked when halted at 14915 +4) Length should be clocked when unhalted at 14914 +5) Length shouldn't be clocked when unhalted at 14915 + + +11.len_reload_timing +-------------------- +Write to length counter reload should be ignored when made during length +counter clocking and the length counter is not zero. + +1) Passed tests +2) Reload just before length clock should work normally +3) Reload just after length clock should work normally +4) Reload during length clock when ctr = 0 should work normally +5) Reload during length clock when ctr > 0 should be ignored diff --git a/blargg_nes_cpu_test5/cpu.nes b/blargg_nes_cpu_test5/cpu.nes new file mode 100644 index 0000000..63cb98d Binary files /dev/null and b/blargg_nes_cpu_test5/cpu.nes differ diff --git a/blargg_nes_cpu_test5/official.nes b/blargg_nes_cpu_test5/official.nes new file mode 100644 index 0000000..5103f0f Binary files /dev/null and b/blargg_nes_cpu_test5/official.nes differ diff --git a/blargg_nes_cpu_test5/readme.txt b/blargg_nes_cpu_test5/readme.txt new file mode 100644 index 0000000..85e3e06 --- /dev/null +++ b/blargg_nes_cpu_test5/readme.txt @@ -0,0 +1,278 @@ +NES CPU Tests +------------- +These test most of the instruction behavior fairly thoroughly, including unofficial instructions. Failing instructions are listed by their opcode and name. Serious errors in basic opcodes might cause massive erroneous errors. + +cpu.nes Tests all instructions +official.nes Tests official only + +Most instructions are tested by setting many combinations of input values for registers, flags, and memory, running the instruction under test, then updating a running checksum with the resulting values. After trying all interesting input combinations, the checksum is compared with the correct one (what a NES gives) to find if the instruction passed. + +This approach makes it very easy to write the tests, since the instructions don't have to be each coded for separately; instead, only the different addressing modes need separate tests. + + +Instructions +------------ +U = Unofficial +- = Not tested yet +X = Freezes CPU +? = Unreliable + +00 - BRK #n +01 ORA (z,X) +02 X KIL +03 U SLO (z,X) +04 U DOP z +05 ORA z +06 ASL z +07 U SLO z +08 PHP +09 ORA #n +0A ASL A +0B U AAC #n +0C U TOP abs +0D ORA a +0E ASL a +0F U SLO abs +10 BPL r +11 ORA (z),Y +12 X KIL +13 U SLO (z),Y +14 U DOP z,X +15 ORA z,X +16 ASL z,X +17 U SLO z,X +18 CLC +19 ORA a,Y +1A U NOP +1B U SLO abs,Y +1C U TOP abs,X +1D ORA a,X +1E ASL a,X +1F U SLO abs,X +20 - JSR a +21 AND (z,X) +22 X KIL +23 U RLA (z,X) +24 BIT z +25 AND z +26 ROL z +27 U RLA z +28 PLP +29 AND #n +2A ROL A +2B U AAC #n +2C BIT a +2D AND a +2E ROL a +2F U RLA abs +30 BMI r +31 AND (z),Y +32 X KIL +33 U RLA (z),Y +34 U DOP z,X +35 AND z,X +36 ROL z,X +37 U RLA z,X +38 SEC +39 AND a,Y +3A U NOP +3B U RLA abs,Y +3C U TOP abs,X +3D AND a,X +3E ROL a,X +3F U RLA abs,X +40 - RTI +41 EOR (z,X) +42 X KIL +43 U SRE (z,X) +44 U DOP z +45 EOR z +46 LSR z +47 U SRE z +48 PHA +49 EOR #n +4A LSR A +4B U ASR #n +4C - JMP a +4D EOR a +4E LSR a +4F U SRE abs +50 BVC r +51 EOR (z),Y +52 X KIL +53 U SRE (z),Y +54 U DOP z,X +55 EOR z,X +56 LSR z,X +57 U SRE z,X +58 CLI +59 EOR a,Y +5A U NOP +5B U SRE abs,Y +5C U TOP abs,X +5D EOR a,X +5E LSR a,X +5F U SRE abs,X +60 - RTS +61 ADC (z,X) +62 X KIL +63 U RRA (z,X) +64 U DOP z +65 ADC z +66 ROR z +67 U RRA z +68 PLA +69 ADC #n +6A ROR A +6B U ARR #n +6C - JMP (a) +6D ADC a +6E ROR a +6F U RRA abs +70 BVS r +71 ADC (z),Y +72 X KIL +73 U RRA (z),Y +74 U DOP z,X +75 ADC z,X +76 ROR z,X +77 U RRA z,X +78 SEI +79 ADC a,Y +7A U NOP +7B U RRA abs,Y +7C U TOP abs,X +7D ADC a,X +7E ROR a,X +7F U RRA abs,X +80 U DOP #n +81 STA (z,X) +82 U DOP #n +83 U AAX (z,X) +84 STY z +85 STA z +86 STX z +87 U AAX z +88 DEY +89 U DOP #n +8A TXA +8B ? XAA #n +8C STY a +8D STA a +8E STX a +8F U AAX abs +90 BCC r +91 STA (z),Y +92 X KIL +93 ? AXA (z),Y +94 STY z,X +95 STA z,X +96 STX z,Y +97 U AAX z,Y +98 TYA +99 STA a,Y +9A TXS +9B ? XAS abs,Y +9C U SYA abs,X +9D STA a,X +9E U SXA abs,Y +9F ? AXA abs,Y +A0 LDY #n +A1 LDA (z,X) +A2 LDX #n +A3 U LAX (z,X) +A4 LDY z +A5 LDA z +A6 LDX z +A7 U LAX z +A8 TAY +A9 LDA #n +AA TAX +AB U ATX #n +AC LDY a +AD LDA a +AE LDX a +AF U LAX abs +B0 BCS r +B1 LDA (z),Y +B2 X KIL +B3 U LAX (z),Y +B4 LDY z,X +B5 LDA z,X +B6 LDX z,Y +B7 U LAX z,Y +B8 CLV +B9 LDA a,Y +BA TSX +BB ? LAR abs,Y +BC LDY a,X +BD LDA a,X +BE LDX a,Y +BF U LAX abs,Y +C0 CPY #n +C1 CMP (z,X) +C2 U DOP #n +C3 U DCP (z,X) +C4 CPY z +C5 CMP z +C6 DEC z +C7 U DCP z +C8 INY +C9 CMP #n +CA DEX +CB U AXS #n +CC CPY a +CD CMP a +CE DEC a +CF U DCP abs +D0 BNE r +D1 CMP (z),Y +D2 X KIL +D3 U DCP (z),Y +D4 U DOP z,X +D5 CMP z,X +D6 DEC z,X +D7 U DCP z,X +D8 CLD +D9 CMP a,Y +DA U NOP +DB U DCP abs,Y +DC U TOP abs,X +DD CMP a,X +DE DEC a,X +DF U DCP abs,X +E0 CPX #n +E1 SBC (z,X) +E2 U DOP #n +E3 U ISC (z,X) +E4 CPX z +E5 SBC z +E6 INC z +E7 U ISC z +E8 INX +E9 SBC #n +EA NOP +EB U SBC #n +EC CPX a +ED SBC a +EE INC a +EF U ISC abs +F0 BEQ r +F1 SBC (z),Y +F2 X KIL +F3 U ISC (z),Y +F4 U DOP z,X +F5 SBC z,X +F6 INC z,X +F7 U ISC z,X +F8 SED +F9 SBC a,Y +FA U NOP +FB U ISC abs,Y +FC U TOP abs,X +FD SBC a,X +FE INC a,X +FF U ISC abs,X + +-- +Shay Green diff --git a/blargg_nes_cpu_test5/source/01-implied.a b/blargg_nes_cpu_test5/source/01-implied.a new file mode 100644 index 0000000..f57eb10 --- /dev/null +++ b/blargg_nes_cpu_test5/source/01-implied.a @@ -0,0 +1,81 @@ +;CALIBRATE=1 +.include "instr_test.a" +instrs: + entry $2A,"ROL A" ; A = op A + entry $0A,"ASL A" + entry $6A,"ROR A" + entry $4A,"LSR A" + + entry $8A,"TXA" ; AXY = AXY + entry $98,"TYA" + entry $AA,"TAX" + entry $A8,"TAY" + + entry $E8,"INX" ; XY = op XY + entry $C8,"INY" + entry $CA,"DEX" + entry $88,"DEY" + + entry $38,"SEC" ; flags = op flags + entry $18,"CLC" + entry $F8,"SED" + entry $D8,"CLD" + entry $78,"SEI" + entry $58,"CLI" + entry $B8,"CLV" + + entry $EA,"NOP" + +.ifndef OFFICIAL_ONLY + entry $1A,"NOP" + entry $3A,"NOP" + entry $5A,"NOP" + entry $7A,"NOP" + entry $DA,"NOP" + entry $FA,"NOP" +.endif +instrs_size = * - instrs + +instr_template: + nop + jmp instr_done +instr_template_size = * - instr_template + +operand = in_a + +.define set_in set_paxyso +.define check_out check_paxyso + +.include "instr_test_end.a" + +test_values: + test_normal + rts + +correct_checksums: +.dword $013A2933 +.dword $A38733B0 +.dword $6EC2BCA6 +.dword $763FEBC5 +.dword $0FF1C1E6 +.dword $5B2EB5B7 +.dword $1D8ACEF5 +.dword $83DC03F9 +.dword $8EBDF63B +.dword $F34CAA18 +.dword $9123FF08 +.dword $48897445 +.dword $4BE14840 +.dword $E7C7ECC0 +.dword $408EF097 +.dword $A6AEF749 +.dword $8F06AD7B +.dword $FC96AE14 +.dword $28F10ADA +.dword $CA7E6620 +.dword $CA7E6620 +.dword $CA7E6620 +.dword $CA7E6620 +.dword $CA7E6620 +.dword $CA7E6620 +.dword $CA7E6620 diff --git a/blargg_nes_cpu_test5/source/02-immediate.a b/blargg_nes_cpu_test5/source/02-immediate.a new file mode 100644 index 0000000..b687934 --- /dev/null +++ b/blargg_nes_cpu_test5/source/02-immediate.a @@ -0,0 +1,75 @@ +;CALIBRATE=1 +.include "instr_test.a" + +instrs: + entry $A9,"LDA #n" ; AXY = #n + entry $A2,"LDX #n" + entry $A0,"LDY #n" + + entry $69,"ADC #n" ; A = A op #n + entry $E9,"SBC #n" + entry $09,"ORA #n" + entry $29,"AND #n" + entry $49,"EOR #n" + + entry $C9,"CMP #n" ; AXY op #n + entry $E0,"CPX #n" + entry $C0,"CPY #n" +.ifndef OFFICIAL_ONLY + entry $EB,"SBC #n" + + entry $80,"DOP #n" + entry $82,"DOP #n" + entry $89,"DOP #n" + entry $C2,"DOP #n" + entry $E2,"DOP #n" + + entry $0B,"AAC #n" + entry $2B,"AAC #n" + entry $4B,"ASR #n" + entry $6B,"ARR #n" + entry $AB,"ATX #n" + entry $CB,"AXS #n" +.endif +instrs_size = * - instrs + +operand = instr+1 + +instr_template: + lda #0 + jmp instr_done +instr_template_size = * - instr_template + +.define set_in set_paxyso +.define check_out check_paxyso + +.include "instr_test_end.a" + +test_values: + test_normal + rts + +correct_checksums: +.dword $AB3E4F82 +.dword $7B121231 +.dword $E544DF3D +.dword $86046BF5 +.dword $999E9E48 +.dword $DC562F7E +.dword $6BF08A00 +.dword $D2A32FD6 +.dword $7DF1D50B +.dword $751D8339 +.dword $A451BD7A +.dword $999E9E48 +.dword $ACE6BAE4 +.dword $ACE6BAE4 +.dword $ACE6BAE4 +.dword $ACE6BAE4 +.dword $ACE6BAE4 +.dword $DC37BE89 +.dword $DC37BE89 +.dword $C07C3593 +.dword $49618FA8 +.dword $1D8ACEF5 +.dword $1240499F diff --git a/blargg_nes_cpu_test5/source/03-zero_page.a b/blargg_nes_cpu_test5/source/03-zero_page.a new file mode 100644 index 0000000..e591500 --- /dev/null +++ b/blargg_nes_cpu_test5/source/03-zero_page.a @@ -0,0 +1,94 @@ +;CALIBRATE=1 +.include "instr_test.a" + +instrs: + entry $A5,"LDA z" ; AXY = z + entry $A6,"LDX z" + entry $A4,"LDY z" + + entry $85,"STA z" ; z = AXY + entry $86,"STX z" + entry $84,"STY z" + + entry $E6,"INC z" ; z = op z + entry $C6,"DEC z" + entry $06,"ASL z" + entry $46,"LSR z" + entry $26,"ROL z" + entry $66,"ROR z" + + entry $65,"ADC z" ; A = A op z + entry $E5,"SBC z" + entry $05,"ORA z" + entry $25,"AND z" + entry $45,"EOR z" + + entry $24,"BIT z" ; AXY op z + entry $C5,"CMP z" + entry $E4,"CPX z" + entry $C4,"CPY z" +.ifndef OFFICIAL_ONLY + entry $04,"DOP z" + entry $44,"DOP z" + entry $64,"DOP z" + + entry $07,"SLO z" + entry $27,"RLA z" + entry $47,"SRE z" + entry $67,"RRA z" + entry $87,"AAX z" + entry $A7,"LAX z" + entry $C7,"DCP z" + entry $E7,"ISC z" +.endif +instrs_size = * - instrs + +operand = $FE + +instr_template: + lda operand + sta <(address+1) + + lda #<(operand+1) + sta <(address+2) + lda #>(operand+1) + sta <(address+3) + + lda #0 + jsr :+ + lda #2 +: sta in_x + test_normal + rts + +correct_checksums: +.dword $B9D16BC6 +.dword $DBC21F73 +.dword $84827E50 +.dword $FE9A8B04 +.dword $9EEFAAD8 +.dword $65F6C5BB +.dword $82C41B16 +.dword $DC68A9E8 +.dword $04A09668 +.dword $417BDD05 +.dword $9A40C4E4 +.dword $0CB8C16E +.dword $EC8492F8 +.dword $AFC77201 +.dword $5BFDAB74 +.dword $C62D3147 diff --git a/blargg_nes_cpu_test5/source/08-ind_y.a b/blargg_nes_cpu_test5/source/08-ind_y.a new file mode 100644 index 0000000..0f8c833 --- /dev/null +++ b/blargg_nes_cpu_test5/source/08-ind_y.a @@ -0,0 +1,88 @@ +;CALIBRATE=1 +.include "instr_test.a" + +instrs: + entry $B1,"LDA (z),Y" ; A = (z),Y + + entry $91,"STA (z),Y" ; (z),Y = A + + entry $D1,"CMP (z),Y" ; A op (z),Y + + entry $11,"ORA (z),Y" ; A = A op (z),Y + entry $F1,"SBC (z),Y" + entry $71,"ADC (z),Y" + entry $31,"AND (z),Y" + entry $51,"EOR (z),Y" +.ifndef OFFICIAL_ONLY + entry $13,"SLO (z),Y" + entry $33,"RLA (z),Y" + entry $53,"SRE (z),Y" + entry $73,"RRA (z),Y" + entry $B3,"LAX (z),Y" + entry $D3,"DCP (z),Y" + entry $F3,"ISC (z),Y" +.endif +instrs_size = * - instrs + +address = $FF +operand = $2FF + +instr_template: + lda (address),y + jmp instr_done +instr_template_size = * - instr_template + +.macro set_in + lda values+1,y + sta operand+1 + + lda values+2,y + sta operand+2 + + set_paxyso +.endmacro + +.macro check_out + check_paxyso + + lda operand+1 + jsr update_crc_fast + + lda operand+2 + jsr update_crc_fast + + lda address + jsr update_crc_fast +.endmacro + +.include "instr_test_end.a" + +test_values: + lda #operand + sta <(address+1) + + lda #0 + jsr :+ + lda #1 +: sta in_y + test_normal + rts + +correct_checksums: +.dword $A70CC617 +.dword $C51FB2A2 +.dword $9A5FD381 +.dword $7B2B686A +.dword $80320709 +.dword $E04726D5 +.dword $9C19B6C7 +.dword $C2B50439 +.dword $1A7D3BB9 +.dword $5FA670D4 +.dword $849D6935 +.dword $12656CBF +.dword $964F3A4A +.dword $452006A5 +.dword $D8F09C96 diff --git a/blargg_nes_cpu_test5/source/09-branches.a b/blargg_nes_cpu_test5/source/09-branches.a new file mode 100644 index 0000000..05ff36f --- /dev/null +++ b/blargg_nes_cpu_test5/source/09-branches.a @@ -0,0 +1,48 @@ +;CALIBRATE=1 +.include "instr_test.a" + +instrs: + entry $90,"BCC r" ; PC = PC op flags + entry $50,"BVC r" + entry $D0,"BNE r" + entry $30,"BMI r" + entry $10,"BPL r" + entry $F0,"BEQ r" + entry $B0,"BCS r" + entry $70,"BVS r" +instrs_size = * - instrs + +zp_byte operand + +instr_template: + bne :+ + sta operand +: jmp instr_done +instr_template_size = * - instr_template + +values2: + .byte 0,$FF,$01,$02,$04,$08,$10,$20,$40,$80 +values2_size = * - values2 + +.macro set_in + sta in_p + set_paxyso +.endmacro + +.define check_out check_paxyso + +.include "instr_test_end.a" + +test_values: + test_normal + rts + +correct_checksums: +.dword $D5B22EE0 +.dword $9CEB0A7E +.dword $DE8DEFE5 +.dword $D704F89C +.dword $AD3F7EB0 +.dword $A4B669C9 +.dword $AF89A8CC +.dword $E6D08C52 diff --git a/blargg_nes_cpu_test5/source/10-stack.a b/blargg_nes_cpu_test5/source/10-stack.a new file mode 100644 index 0000000..60f97ab --- /dev/null +++ b/blargg_nes_cpu_test5/source/10-stack.a @@ -0,0 +1,118 @@ +;CALIBRATE=1 +.include "instr_test.a" + +instrs: + entry $48,"PHA" + entry $08,"PHP" + + entry $68,"PLA" + entry $28,"PLP" + + entry $9A,"TXS" + entry $BA,"TSX" +instrs_size = * - instrs + +instr_template: + pha + jmp instr_done +instr_template_size = * - instr_template + +values2: + .byte 0,$FF,$01,$02,$04,$08,$10,$20,$40,$80 +values2_size = * - values2 + +zp_byte operand + +.macro set_in + sta in_p + set_paxyso + + ; Clear bytes on stack + stx $17F + sty $180 + stx $181 + + sty $1FE + stx $1FF + sty $100 + stx $101 + sty $102 +.endmacro + +zp_byte save +zp_byte save2 +zp_byte save3 +zp_byte save4 +zp_byte save5 + +.macro check_out + php + sta save ; A + pla + sta save2 ; P + pla + sta save3 ; PLA + stx save4 ; X + tsx + stx save5 ; S + + ldx saved_s + txs + + ; Output + tya + jsr update_crc_fast + + lda save + jsr update_crc_fast + + lda save2 + jsr update_crc_fast + + lda save3 + jsr update_crc_fast + + lda save4 + jsr update_crc_fast + + lda save5 + jsr update_crc_fast + + ldx in_s + dex + lda $100,x + jsr update_crc_fast + + inx + lda $100,x + jsr update_crc_fast + + inx + lda $100,x + jsr update_crc_fast +.endmacro + +.include "instr_test_end.a" + +test_values: + ; Values for SP + lda #$80 + jsr :+ + lda #$00 + jsr :+ + lda #$01 + jsr :+ + lda #$FF + jsr :+ + lda #$FE +: sta in_s + test_normal + rts + +correct_checksums: +.dword $AA53E72F +.dword $F46D6C3F +.dword $4B0D5E27 +.dword $A1AB7B53 +.dword $8A5B86A7 +.dword $6157E3AF diff --git a/blargg_nes_cpu_test5/source/11-special.a b/blargg_nes_cpu_test5/source/11-special.a new file mode 100644 index 0000000..271a856 --- /dev/null +++ b/blargg_nes_cpu_test5/source/11-special.a @@ -0,0 +1,68 @@ +.include "common.a" +.include "testing.a" + +jmp_6ff: + .byte $6C ; JMP ($6FF) (to avoid warning) + .word $6FF + +main: + set_test 3,"JMP ($6FF) should get high byte from $600" + lda #$F0 + sta $6FF + lda #$07 + sta $600 + lda #$06 + sta $700 + lda #$E8 ; INX + sta $7F0 + lda #$60 ; RTS + sta $7F1 + sta $6F0 + ldx #0 + jsr jmp_6ff + cpx #1 + jne test_failed + + set_test 4,"RTS should return to addr+1" + lda #>:+ + pha + lda #<:+ + pha + ldx #0 + rts + inx +: inx + inx + cpx #1 + jne test_failed + + set_test 5,"RTI should return to addr" + lda #>:+ + pha + lda #<:+ + pha + ldx #0 + php + rti + inx +: inx + inx + cpx #2 + jne test_failed + + set_test 6,"JSR should push addr of next instr - 1" + lda #$20 ; JSR + sta $6FE + lda #<:+ + sta $6FF + lda #>:+ + sta $700 + jmp $6FE +: pla + cmp #$00 + jne test_failed + pla + cmp #$07 + jne test_failed + + jmp tests_passed diff --git a/blargg_nes_cpu_test5/source/common/ascii.chr b/blargg_nes_cpu_test5/source/common/ascii.chr new file mode 100644 index 0000000..6cbf587 Binary files /dev/null and b/blargg_nes_cpu_test5/source/common/ascii.chr differ diff --git a/blargg_nes_cpu_test5/source/common/common.a b/blargg_nes_cpu_test5/source/common/common.a new file mode 100644 index 0000000..4118bfb --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/common.a @@ -0,0 +1,205 @@ +; Most programs include this file first + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef COMMON_A + .error "common.a included twice" + .end +.endif +COMMON_A = 1 + +.include "macros.a" + +SET_DEFAULT BUILD_NSF,0 +SET_DEFAULT BUILD_MULTI,0 +SET_DEFAULT BUILD_DEVCART,0 +SET_DEFAULT CUSTOM_HEADER,0 +SET_FLAG BUILD_ROM,(!BUILD_NSF) && (!BUILD_MULTI) + +.include "nes.a" + + +;**** Header and reset handler +.if (!CUSTOM_HEADER) && (!BUILD_MULTI) + +; iNES ROM +.if BUILD_ROM +.segment "HEADER" + .byte "NES",26 + .byte 2,1 ; 32K PRG, 8K CHR + .byte 1 ; vertical mirroring +.segment "VECTORS" + .word 0,0,0,nmi,reset,irq +.segment "CHARS" + .incbin "ascii.chr" + + +; NSF +.elseif BUILD_NSF +.segment "HEADER" + .byte "NESM",26,1,1,1 + .word $E000,reset,nsf_play +.segment "VECTORS" + .word 0,0,0,0,0,irq +.endif + + +; Reset handler +.segment "CODE" +; At beginning so devcart program can patch this to +; re-run bootloader instead. + nop +forever: + jmp forever +reset: init_nes + jmp startup + +.endif + +.if !CUSTOM_HEADER + .define BEGIN_NMI nmi: + .define BEGIN_IRQ irq: +.endif + + +;**** Common modules + +.include "delay.a" ; keep first since it can't cross page +.include "crc.a" +.include "print.a" +.include "shell.a" + +.if !BUILD_MULTI + .include "serial.a" +.endif + +.if !BUILD_NSF + .include "ppu.a" +.endif + + +;**** Building normal ROM or NSF +.if BUILD_NSF || (BUILD_ROM && (!BUILD_DEVCART)) + +.if !BUILD_NSF + .include "console.a" +.else + NO_CONSOLE = 1 + +wait_vbl: +nsf_play: + rts +.endif + +startup: + jsr console_init + jsr serial_init + jmp run_main + +print_char_: + jsr console_print + jmp serial_write + +exit_: tax + + ; Be sure output is visible + print_str {newline,newline,newline} + jsr console_show ; in case it was hidden + + ; Report audibly as well + txa + jsr beep_bits + + jmp forever + +; Reports byte A to user by printing it in hex and +; reporting its bits via sound. +; Preserved: X, Y +report_value: + jsr print_a + jmp beep_bits + + +;**** Building devcart +.elseif BUILD_DEVCART +NO_CONSOLE = 1 +print_char_ = serial_write +report_value = print_a +startup: + jsr serial_init + delay_msec 45 + jmp run_main + +exit_: ; Tell host to stop capturing serial + lda #$1A + jsr serial_write + + ; Run bootloader + delay_msec 400 + jsr wait_vbl + jmp $FF04 + + +;**** Building multi ROM +.elseif BUILD_MULTI + +.segment "VECTORS" + .word 0,0,0,nmi,reset,irq +.segment "HOOKS" +SET_DEFAULT MULTI_TYPE,$56 + .byte MULTI_TYPE,0 + .word filename,run_main,0 +reset: + ; Reset MMC1, map shell to $8000, then run it + + ; write $80 to reset shift register, + ; then write $08 to reg 0 + lda #$90 +: sta $8000 + lsr a + cmp #$02 + bne :- + + jmp $8000 +.code + +; Entry points +exit_ = $BFD2 +print_char_ = $BFD5 + + +.endif + +;**** NO_CONSOLE + +.ifdef NO_CONSOLE +console_init: +console_hide: +console_show: +console_print: + rts +.endif + + +;**** print_filename + +.if .defined(FILENAME_KNOWN) && (!BUILD_DEVCART) +print_filename: + print_str {newline,"Test:"} + lda #filename + sta addr+1 + jsr print_str_addr + jsr print_newline + rts + +filename: + .incbin "ram:rom.nes" + .byte 0 +.else +print_filename: + rts + +filename: + .byte 0 +.endif diff --git a/blargg_nes_cpu_test5/source/common/console.a b/blargg_nes_cpu_test5/source/common/console.a new file mode 100644 index 0000000..333acaf --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/console.a @@ -0,0 +1 @@ +; Scrolling text console with line wrapping, 30x30 characters. ; Buffers lines for speed. Will work even if PPU doesn't ; support scrolling. ; ** ASCII font must already be in CHR, and mirroring ; must be vertical or single-screen. ; Number of characters of margin on left and right, to avoid ; text getting cut off by common TVs console_margin = 1 console_buf_size = 32 console_width = console_buf_size - (console_margin*2) zp_byte console_pos ram_res console_scroll,1 ram_res console_temp,1 bss_res console_buf,console_buf_size ; Clears then waits for VBL ; Preserved: A, X, Y console_wait_vbl: bit PPUSTATUS : bit PPUSTATUS bpl :- rts ; Initializes console console_init: jsr console_hide sta PPUCTRL ; Load palette ldx #$3F stx PPUADDR sta PPUADDR ldx #$0F ; black background stx PPUDATA ldx #$30 ; white text stx PPUDATA stx PPUDATA stx PPUDATA ; Fill nametable with spaces ldx #$20 stx PPUADDR sta PPUADDR txa ldx #240 : sta PPUDATA sta PPUDATA sta PPUDATA sta PPUDATA dex bne :- ; Clear attributes txa ldx #$40 : sta PPUDATA dex bne :- ; In case PPU doesn't support scrolling, start a ; couple of lines down lda #16 sta console_scroll ; FALL THROUGH ; Shows console display ; Preserved: X, Y console_show: pha stx console_temp jsr console_wait_vbl lda #$0A sta PPUMASK jmp console_apply_scroll_ ; Hides console display ; Preserved: X, Y console_hide: jsr console_wait_vbl lda #0 sta PPUMASK rts ; Prints char A to console. Will not appear until a ; newline is printed. ; Preserved: A, X, Y console_print: cmp #10 beq console_newline ; Write to buffer stx console_temp ldx console_pos sta console_buf+console_margin,x dec console_pos bmi :+ ; reached end of line ldx console_temp rts ; Prints new line ; Preserved: A, X, Y console_newline: stx console_temp : pha jsr console_wait_vbl ; Address line in nametable lda console_scroll sta console_pos lda #$08 asl console_pos rol a asl console_pos rol a sta PPUADDR lda console_pos sta PPUADDR ; Copy line ldx #console_buf_size-1 : lda console_buf,x sta PPUDATA dex bpl :- ; Scroll up 8 pixels lda console_scroll cmp #240-8 bcc :+ adc #16-1;+1 for set carry : adc #8 sta console_scroll ; Apply new scroll console_apply_scroll_: lda #0 sta PPUADDR sta PPUADDR sta PPUSCROLL lda console_scroll sta PPUSCROLL ; Start new clear line lda #' ' ldx #console_buf_size-1 : sta console_buf,x dex bpl :- ldx #console_width-1 stx console_pos ldx console_temp pla rts \ No newline at end of file diff --git a/blargg_nes_cpu_test5/source/common/crc.a b/blargg_nes_cpu_test5/source/common/crc.a new file mode 100644 index 0000000..c57c044 --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/crc.a @@ -0,0 +1 @@ +; CRC-32 checksum calculation zp_res checksum,4 zp_byte checksum_temp zp_byte checksum_off_ ; Turns CRC updating on/off. Allows nesting. ; Preserved: X, Y .macro crc_off inc checksum_off_ .endmacro .macro crc_on jsr crc_on_ .endmacro crc_on_: dec checksum_off_ jmi internal_error ; catch unbalanced crc calls rts ; Initializes checksum module init_crc: ; FALL THROUGH ; Clears checksum and turns it on ; Preserved: X, Y reset_crc: lda #0 sta checksum_off_ lda #$FF sta checksum sta checksum + 1 sta checksum + 2 sta checksum + 3 rts ; If enabled, updates checksum with byte in A ; Preserved: X, Y ; Time: 350 clocks average update_crc_: stx checksum_temp jmp :+ update_crc: stx checksum_temp ldx checksum_off_ bne @off : eor checksum ldx #8 @bit: lsr checksum+3 ror checksum+2 ror checksum+1 ror a bcc :+ sta checksum lda checksum+3 eor #$ED sta checksum+3 lda checksum+2 eor #$B8 sta checksum+2 lda checksum+1 eor #$83 sta checksum+1 lda checksum eor #$20 : dex bne @bit sta checksum @off: ldx checksum_temp rts ; Prints CRC-32 checksum as 8-character hex value print_crc: crc_off ; Print complement ldx #3 : lda checksum,x eor #$FF jsr print_hex dex bpl :- crc_on rts \ No newline at end of file diff --git a/blargg_nes_cpu_test5/source/common/crc_fast.a b/blargg_nes_cpu_test5/source/common/crc_fast.a new file mode 100644 index 0000000..0f9751a --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/crc_fast.a @@ -0,0 +1,64 @@ +; Fast table-based CRC-32 + +; Uses 1K of RAM +checksum_t0 = $400 +checksum_t1 = $500 +checksum_t2 = $600 +checksum_t3 = $700 + +; Initializes fast CRC tables and resets checksum. +; Preserved: Y +; Time: 57 msec +init_crc_fast: + ldx #0 +@byte: ; Calculate CRC for this byte + lda #0 + sta checksum+3 + sta checksum+2 + sta checksum+1 + stx checksum + jsr update_crc_ + + ; Write in table + sta checksum_t0,x + lda checksum+1 + sta checksum_t1,x + lda checksum+2 + sta checksum_t2,x + lda checksum+3 + sta checksum_t3,x + + inx + bne @byte + + jmp reset_crc + + +; Updates checksum with byte from A +; Preserved: X, Y +; Time: 54 clocks +update_crc_fast: + stx checksum_temp + +; Updates checksum with byte from A +; Preserved: Y +; Time: 42 clocks +.macro update_crc_fast + eor checksum + tax + lda checksum+1 + eor checksum_t0,x + sta checksum + lda checksum+2 + eor checksum_t1,x + sta checksum+1 + lda checksum+3 + eor checksum_t2,x + sta checksum+2 + lda checksum_t3,x + sta checksum+3 +.endmacro + + update_crc_fast + ldx checksum_temp + rts diff --git a/blargg_nes_cpu_test5/source/common/delay.a b/blargg_nes_cpu_test5/source/common/delay.a new file mode 100644 index 0000000..e0add47 --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/delay.a @@ -0,0 +1 @@ +; Delays in clocks and milliseconds. All routines re-entrant ; (no global data). ; Delays n milliseconds (1/1000 second) ; n can range from 0 to 1100. ; Preserved: X, Y .macro delay_msec n .if (n) < 0 .or (n) > 1100 .error "time out of range" .endif delay ((n)*1789773+500)/1000 .endmacro ; Delays n microseconds (1/1000000 second). ; n can range from 0 to 100000. ; Preserved: X, Y .macro delay_usec n .if (n) < 0 .or (n) > 100000 .error "time out of range" .endif delay ((n)*17898+5000)/10000 .endmacro ; Delays n clocks, from 2 to 16777215 ; Preserved: X, Y .macro delay n .if (n) < 0 .or (n) = 1 .or (n) > 16777215 .error "Delay out of range" .endif .if (n) < 14 .and (n) <> 12 delay_inline (n) .elseif (n) < 27 delay_unrolled (n) .elseif <(n) = 0 delay_256 (n) .else lda #<((n)-27) jsr delay_a19_clocks delay_256 ((n)-27) .endif .endmacro ; Delays A+19 clocks (excluding JSR) ; Preserved: X, Y : sbc #7 ; carry set by CMP delay_a19_clocks: cmp #7 bcs :- ; do multiples of 7 lsr a ; bit 0 bcs :+ : ; A=clocks/2, either 0,1,2,3 beq @zero ; 0: 5 lsr a beq :+ ; 1: 7 bcc :+ ; 2: 9 @zero: bne :+ ; 3: 11 : rts ; (thanks to dclxvi for the algorithm) ; Delays A*256+10 clocks (excluding JSR) ; Preserved: X, Y delay_256a_clocks: cmp #0 bne :+ rts delay_256a_clocks_: pha lda #256-19-22-16 bne @first ; always branches : pha lda #256-19-22 @first: jsr delay_a19_clocks pla clc adc #-1 bne :- rts ; Delays A*65536+10 clocks (excluding JSR) ; Preserved: X, Y delay_65536a_clocks: cmp #0 bne :+ rts delay_65536a_clocks_: pha lda #256-19-22-16 bne @first : pha lda #256-19-22 @first: jsr delay_a19_clocks lda #255 jsr delay_256a_clocks_ pla clc adc #-1 bne :- rts .macro delay_inline n .if n = 7 .or n >= 9 pha pla delay_inline (n-7) .elseif n >= 3 .and n & 1 lda <0 delay_inline (n-3) .elseif n >= 2 nop delay_inline (n-2) .elseif n > 0 .error "delay_short internal error" .endif .endmacro .macro delay_unrolled n .if n & 1 lda <0 jsr delay_unrolled_-((n-15)/2) .else jsr delay_unrolled_-((n-12)/2) .endif .endmacro .res 7,$EA ; NOP delay_unrolled_: rts .macro delay_256 n .if >n lda #>n jsr delay_256a_clocks_ .endif .if ^n lda #^n jsr delay_65536a_clocks_ .endif .endmacro \ No newline at end of file diff --git a/blargg_nes_cpu_test5/source/common/instr_test.a b/blargg_nes_cpu_test5/source/common/instr_test.a new file mode 100644 index 0000000..e85daab --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/instr_test.a @@ -0,0 +1,50 @@ +.include "common.a" +.include "testing.a" +.include "crc_fast.a" + +; Instructions to test +.macro entry op,name + .byte op,0 + str_addr name +.endmacro + +zp_byte in_p +zp_byte in_a +zp_byte in_x +zp_byte in_y +zp_byte in_s + +values: + .byte 0,1,2,$40,$7F,$80,$81,$FF +values_size = * - values + .byte 0,1,2,$40,$7F,$80,$81,$FF + +.macro set_paxyso + ldx in_s + txs + lda values,y + sta operand + lda in_p + pha + lda in_a + ldx in_x + ldy in_y + plp +.endmacro + +.macro check_paxyso + php + cld ; limits effect of buggy emulator + jsr update_crc_fast + pla + jsr update_crc_fast + txa + jsr update_crc_fast + tya + jsr update_crc_fast + tsx + txa + jsr update_crc_fast + lda operand + jsr update_crc_fast +.endmacro diff --git a/blargg_nes_cpu_test5/source/common/instr_test_end.a b/blargg_nes_cpu_test5/source/common/instr_test_end.a new file mode 100644 index 0000000..85c17d5 --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/instr_test_end.a @@ -0,0 +1,178 @@ +zp_byte instrs_idx +zp_byte failed_count + +main: ldx #$A2 + txs + + jsr init_crc_fast + + ; Test each instruction + lda #0 +@loop: sta instrs_idx + tay + + jsr reset_crc + lda instrs,y + jsr test_instr + jsr check_result + + lda instrs_idx + clc + adc #4 + cmp #instrs_size + bne @loop + + lda failed_count + jne test_failed + jmp tests_passed + +; Check result of test +check_result: +.ifdef CALIBRATE + ; Print correct CRC + crc_off + print_str ".dword $" + ldx #0 +: lda checksum,x + jsr print_hex + inx + cpx #4 + bne :- + jsr print_newline + crc_on +.else + ; Verify CRC + ldx #3 + ldy instrs_idx +: lda checksum,x + cmp correct_checksums,y + bne @wrong + iny + dex + bpl :- +.endif + rts + +; Print failed opcode and name +@wrong: + ldy instrs_idx + lda instrs,y + jsr print_a + lda instrs+2,y + sta addr + lda instrs+3,y + sta addr+1 + jsr print_str_addr + jsr print_newline + inc failed_count + rts + +bss_res instr,instr_template_size + +; Tests instr A +test_instr: + sta instr + + ; Copy rest of template + ldx #instr_template_size - 1 +: lda instr_template,x + sta instr,x + dex + bne :- + + ; Disable and clear frame IRQ + lda #$40 + sta SNDMODE + + ; Default stack + lda #$90 + sta in_s + + ; Test with different flags + lda #$00 + jsr test_flags + lda #$FF + jsr test_flags + + rts + +zp_byte operand_idx + +test_flags: + sta in_p + + ldy #values_size-1 +: sty operand_idx + + lda values,y + sta in_a + + lda values+1,y + sta in_x + + lda values+2,y + sta in_y + + jsr test_values + + ldy operand_idx + dey + bpl :- + + rts + +.ifndef values2 + values2 = values + values2_size = values_size +.endif + +.macro test_normal +zp_byte a_idx +zp_byte saved_s + + tsx + stx saved_s + + ldy #values2_size-1 +inner: sty a_idx + + lda values2,y + sta operand + + set_in + +.if 0 + ; P A X Y S O (z,x) (z),y + jsr print_p + jsr print_a + jsr print_x + jsr print_y + jsr print_s + lda operand + jsr print_a +.ifdef address + lda (address,x) + jsr print_a + lda (address),y + jsr print_a +.else + lda operand,x + jsr print_a + lda operand,y + jsr print_a +.endif + jsr print_newline +.endif + + jmp instr +instr_done: + + check_out + + ldy a_idx + dey + bpl inner + + ldx saved_s + txs +.endmacro diff --git a/blargg_nes_cpu_test5/source/common/macros.a b/blargg_nes_cpu_test5/source/common/macros.a new file mode 100644 index 0000000..089739c --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/macros.a @@ -0,0 +1 @@ +; If name isn't yet defined, defines it with value .macro SET_DEFAULT name,value .ifndef name name=value .endif .endmacro ; Sets name to 1 if value is non-zero, 0 otherwise .macro SET_FLAG name,value .if value name=1 .else name=0 .endif .endmacro .macro PRINT_FLAG flag .if flag .error "true" .else .error "false" .endif .end .endmacro ; jxx equivalents to bxx .macpack longbranch ; blt, bge equivalents to bcc, bcs .macro blt Target bcc Target .endmacro .macro bge Target bcs Target .endmacro .macro jge Target jcs Target .endmacro .macro jlt Target jcc Target .endmacro ; Generates string in RODATA and inserts address in code .macro str_addr text .local Addr .rodata Addr: .ifnblank text .byte text .endif .byte 0 .code .word Addr .endmacro ; Simplified declaration of variable in RAM ; Reserves size bytes in zeropage for name .macro zp_res name,size .zeropage name: .res size .code .endmacro ; Reserves size bytes in bss for name .macro bss_res name,size .bss name: .res size .code .endmacro ; Reserves size bytes in zeropage or bss .macro ram_res name,size .zeropage name: .res size .code .endmacro ; Reserves byte in zeropage for name .macro zp_byte name zp_res name,1 .endmacro \ No newline at end of file diff --git a/blargg_nes_cpu_test5/source/common/nes.a b/blargg_nes_cpu_test5/source/common/nes.a new file mode 100644 index 0000000..e0481e8 --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/nes.a @@ -0,0 +1 @@ +.if !BUILD_NSF ; PPU PPUCTRL = $2000 PPUMASK = $2001 PPUSTATUS = $2002 SPRADDR = $2003 SPRDATA = $2004 PPUSCROLL = $2005 PPUADDR = $2006 PPUDATA = $2007 SPRDMA = $4014 PPUCTRL_NMI = $80 PPUMASK_BG0 = $0A PPUCTRL_8X8 = $00 PPUCTRL_8X16 = $20 PPUMASK_SPR = $14 PPUMASK_BG0CLIP = $08 .endif ; APU SNDCHN = $4015 JOY1 = $4016 JOY2 = $4017 SNDMODE = $4017 SNDMODE_NOIRQ = $40 ; Temporaries temp = $A temp2 = $B temp3 = $C addr = $E .macro init_nes ; Init NES sei cld ldx #$FF txs lda #0 .if !BUILD_NSF ; Init PPU sta PPUCTRL sta PPUMASK .endif ; Clear RAM ldx #7 ; last page tay ; offset in last page+1 sta <0 @clear_page: stx <1 : dey sta (0),y bne :- dex bpl @clear_page .if !BUILD_NSF ; Let PPU initialize : bit PPUSTATUS bpl :- : bit PPUSTATUS bpl :- .endif .endmacro \ No newline at end of file diff --git a/blargg_nes_cpu_test5/source/common/ppu.a b/blargg_nes_cpu_test5/source/common/ppu.a new file mode 100644 index 0000000..08f2b7a --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/ppu.a @@ -0,0 +1 @@ +; PPU utilities ; Clears VBL flag then waits for it to be set ; Preserved: A, X, Y wait_vbl: bit PPUSTATUS : bit PPUSTATUS bpl :- rts ; Turns NMI on ; Preserved: X, Y nmi_on: lda #PPUCTRL_NMI sta PPUCTRL rts ; Turns NMI off ; Preserved: X, Y nmi_off: lda #0 sta PPUCTRL rts ; Sets VRAM address to A * $100 ; Preserved: X, Y set_vpage: bit PPUSTATUS sta PPUADDR lda #0 sta PPUADDR rts ; Sets VRAM address to A * $100 + X ; Preserved: A, X, Y set_vaddr: bit PPUSTATUS sta PPUADDR stx PPUADDR rts ; Sets X and Y scroll ; Preserved: A, X, Y set_vscroll: bit PPUSTATUS stx PPUSCROLL sty PPUSCROLL rts ; Turns off NMI and disables BG and sprites ; Preserved: A, X, Y disable_ppu: pha lda #0 sta PPUCTRL sta PPUMASK bit PPUSTATUS sta PPUADDR sta PPUADDR pla rts ; Sets sprite memory to $FF ; Preserved: Y clear_sprites: lda #$ff ldx #0 : sta SPRDATA dex bne :- rts ; Clears/fills nametable with 0/A and clear attributes to 0 clear_nametable: lda #0 fill_nametable: pha lda #$20 jsr set_vpage pla ldx #240 : sta PPUDATA sta PPUDATA sta PPUDATA sta PPUDATA dex bne :- lda #0 ldx #64 : sta PPUDATA dex bne :- rts ; Clears/fills VRAM with 0/A clear_vram: lda #0 fill_vram: ldx #0 ldy #$24 bne fill_vram_ ; Clears/fills CHR with 0/A fill_chr1: ldx #$10 ldy #$10 bne fill_vram_ fill_chr0: ldx #0 ldy #$10 bne fill_vram_ clear_chr: lda #0 fill_chr: ldx #0 ldy #$20 ; Fill Y*$100 bytes of VRAM with A, starting at X*$100 fill_vram_: bit PPUSTATUS stx PPUADDR ldx #0 stx PPUADDR : sta PPUDATA dex bne :- dey bne :- rts \ No newline at end of file diff --git a/blargg_nes_cpu_test5/source/common/print.a b/blargg_nes_cpu_test5/source/common/print.a new file mode 100644 index 0000000..741c19e --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/print.a @@ -0,0 +1 @@ +; Prints values in various ways to output, including numbers and strings. newline = 10 ; Prints indicated register to console as two hex chars and space ; Preserved: A, X, Y, P print_a: php pha print_reg_: jsr print_hex lda #' ' jsr print_char_ pla plp rts print_x: php pha txa jmp print_reg_ print_y: php pha tya jmp print_reg_ print_p: php pha php pla jmp print_reg_ print_s: php pha txa tsx pha inx inx inx inx jsr print_x pla tax pla plp rts ; Prints A as two hex characters, NO space after ; Preserved: X, Y print_hex: ; Update checksum pha jsr update_crc pla ; Print high nibble pha lsr a lsr a lsr a lsr a jsr @nibble jsr print_char_ pla ; Print low nibble and #$0F jsr @nibble jmp print_char_ @nibble: cmp #10 blt @digit adc #6;+1 since carry is set @digit: adc #$30 rts ; Prints character and updates checksum UNLESS it's a newline. ; Preserved: X, Y print_char: cmp #newline beq :+ pha jsr update_crc pla : jmp print_char_ ; Prints space. Doesn't affect checksum. ; Preserved: A, X, Y print_space: pha lda #' ' bne print_char_pla_rts ; always branches ; Advances to next line. Doesn't affect checksum. ; Preserved: A, X, Y print_newline: pha lda #newline print_char_pla_rts: jsr print_char_ pla rts ; Prints zero-terminated string after JSR that called this routine. ; Preserved: A, X, X print_str: sta temp ; Get addr of string pla sta addr pla sta addr+1 jsr inc_addr lda temp jsr print_str_addr jmp (addr) ; Prints string ; Preserved: A, X, Y .macro print_str str jsr print_str .byte str,0 .endmacro ; Prints string at addr and leaves addr pointing to ; byte AFTER zero terminator. ; Preserved: A, X, Y print_str_addr: pha tya pha ldy #0 beq :+ ; always taken @loop: jsr print_char jsr inc_addr : lda (addr),y bne @loop pla tay pla ; FALL THROUGH ; Increments 16-bit value in addr. ; Preserved: A, X, Y inc_addr: inc addr beq :+ rts : inc addr+1 rts ; Prints A as 1-3 digit decimal value, NO space after. ; Preserved: Y print_dec: cmp #100 blt @tens ldx #'0' : sbc #100 inx cmp #100 bge :- jsr @digit @tens: cmp #10 blt @ones ldx #'0' : sbc #10 inx cmp #10 bge :- jsr @digit @ones: clc adc #'0' jmp print_char @digit: pha txa jsr print_char pla rts ; Reports value of A via low/high beeps. ; Preserved: X, Y beep_bits: ; Make reference low beep clc jsr @beep ; End marker sec ; Remove high zero bits : rol a beq @zero bcc :- ; Play remaining bits @loop: php jsr @beep plp asl a bne @loop @zero: rts @beep: pha ; Set LSB of pitch based on carry lda #0 adc #$FF sta $4002 ; Set up square lda #1 sta SNDCHN sta $4003 sta $4001 ; Fade volume lda #15 : pha eor #$30 sta $4000 delay_msec 8 pla clc adc #-1 bne :- ; Silence sta SNDCHN delay_msec 120 pla rts ; Reports internal error and exits program internal_error: print_str "Internal error" lda #1 jmp exit \ No newline at end of file diff --git a/blargg_nes_cpu_test5/source/common/serial.a b/blargg_nes_cpu_test5/source/common/serial.a new file mode 100644 index 0000000..e96d5de --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/serial.a @@ -0,0 +1 @@ +; Serial I/O at 57600 bits/sec on controller port 2 ; ; Uses stack and register A only, and doesn't mind page crossing ; (uses subroutines instead of loops). ; Initialize serial and wait so PC won't receive junk for the first byte. ; Preserved: A, X, Y serial_init: pha lda #1 sta JOY1 delay_msec 10 pla rts ; Write byte A to serial ; Preserved: X, Y serial_write: clc jsr serial_write_bit ; start nop ; TODO: why this extra delay of 6? jsr serial_write_bit2 ; bit 0 jsr serial_write_bit ; bit 1 jsr serial_write_bit ; bit 2 jsr serial_write_bit ; bit 3 jsr serial_write_bit ; bit 4 jsr serial_write_bit ; bit 5 jsr serial_write_bit ; bit 6 jsr serial_write_bit ; bit 7 sec ; 2 stop bit serial_write_bit2: nop ; 2 nop ; 2 serial_write_bit: ; 6 jsr pha ; 3 rol a ; 2 and #1 ; 2 sta JOY1 ; 4 pla ; 4 ror a ; 2 nop ; 2 rts ; 6 ; Wait for and read byte from serial into A ; Preserved: X, Y serial_read: lda #1 ; 2 wait_start_bit: bit JOY2 ; 4 beq wait_start_bit ; 3 ; 4-10 latency (average 7) jsr serial_start_bit ; 38 delay jsr serial_read_bit ; bit 0 jsr serial_read_bit ; bit 1 jsr serial_read_bit ; bit 2 jsr serial_read_bit ; bit 3 jsr serial_read_bit ; bit 4 jsr serial_read_bit ; bit 5 jsr serial_read_bit ; bit 6 nop ; bit 7 serial_start_bit: nop nop serial_read_bit: ; 6 jsr pha ; 3 sec ; 2 rol a ; 2 eor JOY2 ; 4 lsr a ; 2 pla ; 4 ror a ; 2 rts ; 6 \ No newline at end of file diff --git a/blargg_nes_cpu_test5/source/common/shell.a b/blargg_nes_cpu_test5/source/common/shell.a new file mode 100644 index 0000000..f8a7102 --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/shell.a @@ -0,0 +1,82 @@ +; Shell that sets up common services, calls main, +; and handles exit result + +; Sets up environment, calls main, then exit 0 +run_main: + ; Initialize libraries + jsr init_crc + + ; Establish consistent environment before + ; running main + jsr wait_vbl + lda #0 + sta SNDMODE + tax + tay + clc + clv + + jsr main + + ; Default to silent exit if main returns + lda #0 + +; Exits program and prints result code if non-zero +exit: + ; Disable IRQ and NMI + sei +.if !BUILD_NSF + ldx #0 + stx PPUCTRL +.endif + + ; Reset stack + dex + txs + +.if !BUILD_MULTI + ; 0: "" + cmp #1 + jlt exit_ + + ; 1: "Failed" + bne :+ + print_str {newline,"Failed"} + jmp exit_ + + ; n: "Error n" +: pha + print_str {newline,"Error "} + jsr print_dec + pla +.endif + jmp exit_ + + +;;;; Default NMI/IRQ + +; Default NMI and IRQ handlers + +.ifndef CUSTOM_NMI +zp_byte nmi_count + +BEGIN_NMI + inc nmi_count + rti +.code + +; Waits for NMI +; Preserved: X, Y +wait_nmi: + lda nmi_count +: cmp nmi_count + beq :- + rts +.endif + +.ifndef CUSTOM_IRQ +BEGIN_IRQ + bit SNDCHN ; clear APU IRQ flag + rti +.code +.endif diff --git a/blargg_nes_cpu_test5/source/common/testing.a b/blargg_nes_cpu_test5/source/common/testing.a new file mode 100644 index 0000000..308e3ec --- /dev/null +++ b/blargg_nes_cpu_test5/source/common/testing.a @@ -0,0 +1,158 @@ +; Utilities for writing test ROMs + +ram_res test_code,1 ; code of current test +ram_res test_name,2 ; address of name of current test, or 0 of none + + +; Reports that all tests passed +tests_passed: +.if !BUILD_MULTI + jsr print_filename + print_str "Passed" +.endif + lda #0 + jmp exit + + +; Reports test failure +test_failed: + lda test_code + + ; Treat 0 as 1, in case it wasn't ever set + bne :+ + lda #1 + sta test_code +: + ; If code >= 2, print name + cmp #2 + blt :+ + lda test_name+1 + beq :+ + sta addr+1 + lda test_name + sta addr + jsr print_newline + jsr print_str_addr + jsr print_newline +: +.if !BUILD_MULTI + jsr print_filename +.endif + + ; End program + lda test_code + jmp exit + + +; Sets current test code and optionally name. Also resets +; checksum. +.macro set_test code,name + jsr set_test_ + .byte code +.if (!BUILD_DEVCART) || .blank(name) + .addr 0 +.else + str_addr name +.endif +.endmacro + +set_test_: + ; Get return addr + sta test_code + pla + sta addr + pla + sta addr+1 + lda test_code + + pha + tya + pha + + jsr reset_crc + + ldy #1 + + ; Copy code + lda (addr),y + sta test_code + iny + + ; Get addr of string + lda (addr),y + sta test_name + iny + lda (addr),y + sta test_name+1 + + lda #3 + ; FALL THROUGH + + ; Restore Y and A, then jump to addr+A+1 +jmp_adj_addr: + ; Increment return addr + sec + adc addr + sta addr + bcc :+ + inc addr+1 +: + pla + tya + pla + jmp (addr) + + +; If checksum doesn't match expected, reports failed test +; Preserved: A, X, Y +.macro check_crc expected +.if expected + jsr check_crc_ + .dword expected +.else + ; print checksum if you pass 0 + jsr print_newline + jsr print_crc + jsr print_newline +.endif +.endmacro + + +; Compares CRC at addr to current CRC. +; A and Z reflect comparison. +; Preserved: X +cmp_crc_addr: + ; Compare with checksum + ldy #0 +: lda checksum,y + eor #$FF + eor (addr),y + bne @wrong + iny + cpy #4 + bne :- +@wrong: rts + +check_crc_: + ; Get return addr + sta temp + pla + sta addr + pla + sta addr+1 + jsr inc_addr + lda temp + + pha + tya + pha + + jsr cmp_crc_addr + bne :+ + lda #3 + jmp jmp_adj_addr + +: jsr print_newline + jsr print_crc + jsr print_newline + jmp test_failed diff --git a/blargg_nes_cpu_test5/source/nes.cfg b/blargg_nes_cpu_test5/source/nes.cfg new file mode 100644 index 0000000..6661c9d --- /dev/null +++ b/blargg_nes_cpu_test5/source/nes.cfg @@ -0,0 +1,28 @@ +# ca65 configuration for NROM with first $6000 bytes empty + +# fill=yes forces area to be padded to specified size in output +MEMORY { + ZP: start = $10, size = $F0, type = rw; + HEADER: start = 0, size = 16, type = ro, fill = yes; + + # my devcart only has memory from $E000-$FFFF + ROMX: start = $8000, size = $6000, type = ro, fill = yes; + ROM0: start = $E000, size = $1FF4, type = ro, fill = yes; + ROMV: start = $FFF4, size = $C, type = ro, fill = yes; + + ROM2: start = 0, size = $2000, type = ro, fill = yes; + + SRAM: start = $0200, size = $0600, type = rw; +} + +# align=$100 allows use of .align directive with a value up to $100 +SEGMENTS { + HEADER: load = HEADER, type = ro; + #STARTUP: load = ROMX, type = ro; + CODE: load = ROM0, type = ro, align = $100; + RODATA: load = ROM0, type = ro; + VECTORS: load = ROMV, type = ro; + CHARS: load = ROM2, type = ro; + BSS: load = SRAM, type = bss; + ZEROPAGE: load = ZP, type = zp; +} diff --git a/blargg_nes_cpu_test5/source/readme.txt b/blargg_nes_cpu_test5/source/readme.txt new file mode 100644 index 0000000..b9be231 --- /dev/null +++ b/blargg_nes_cpu_test5/source/readme.txt @@ -0,0 +1,60 @@ +NES Tests Source Code +--------------------- + +Building with ca65 +------------------ +To assemble a test with ca65, use the following commands: + + ca65 -I common -o rom.o source_filename_here.a + ld65 -C nes.cfg rom.o -o rom.nes + your_favorite_nes_emulator rom.nes + +Don't bother trying to build a multi-test ROM, since it's not worth the complexity. Also, tests you build won't print their name if they fail, since that requires special arrangements. + + +Framework +--------- +Each test is in a single source file, and makes use of several library source files from common/. This framework provides common services and reduces code to only that which performs the actual test. Virtually all tests include "common.a" at the beginning, which sets things up and includes all the appropriate library files. + +The reset handler does minimal NES hardware initialization, clears RAM, sets up the text console, then runs main. Main can exit by returning or jumping to "exit" with a code in A. Exit reports the code then goes into an infinite loop. If the code is 0, it doesn't do anything, otherwise it reports the code. Code 1 is reported as "Failed", and the rest as "Error ". + +Several routines are available to print values and text to the console. Most update a running CRC-32 checksum which can be checked with check_crc, allowing ALL the output to be checked very easily. If the checksum doesn't match, it is printed, so you can run the code on a NES and paste the correct checksum into your code. + +The default is to build a normal iNES ROM, with other options build types not documented well. My nes.cfg file puts the code at $E000 since my devcart requires it, and I don't want the normal ROM to differ in any way from what I've tested. + +I use symbolic constants for NES registers. Use of these raises the level of the code, reduces room for error, and makes it easier for less-experienced NES programmers to understand. I recently switched to using them and have no regrets. + +Library routines are organized by function into several files, each with short documentation. Each routine may also optionally list registers which are preserved, rather than those which are modified (trashed) as is more commonly done. This is because it's best for the caller to assume that ALL registers are NOT preserved unless noted. Also, preserving registers is part of the interface, and making that guarantee requires extra effort that isn't warranted except for often-used small routines that would be tedious to use if they didn't preserve registers. Also, it's easier for the caller to save and restore A, so it's best if a routine only modifies A, if possible. + +I couldn't help using some macros for common operations. The left is equivalent to the right: + + Macro Equivalent + ------------------------------------- + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + zp_byte name .zeropage + name: .res 1 + .code + + zp_res name,n .zeropage + name: .res n + .code + + bss_res name,n .bss + name: .res n + .code + + ram_res name,n .zeropage or .bss (unspecified) + name: .res n + .code + + +-- +Shay Green diff --git a/blargg_ppu_tests_2005.09.15b/palette_ram.nes b/blargg_ppu_tests_2005.09.15b/palette_ram.nes new file mode 100644 index 0000000..878b321 Binary files /dev/null and b/blargg_ppu_tests_2005.09.15b/palette_ram.nes differ diff --git a/blargg_ppu_tests_2005.09.15b/power_up_palette.nes b/blargg_ppu_tests_2005.09.15b/power_up_palette.nes new file mode 100644 index 0000000..8964506 Binary files /dev/null and b/blargg_ppu_tests_2005.09.15b/power_up_palette.nes differ diff --git a/blargg_ppu_tests_2005.09.15b/readme.txt b/blargg_ppu_tests_2005.09.15b/readme.txt new file mode 100644 index 0000000..82f2dd5 --- /dev/null +++ b/blargg_ppu_tests_2005.09.15b/readme.txt @@ -0,0 +1,74 @@ +NTSC NES PPU Tests +------------------ +These ROMs test a few aspects of the NTSC NES PPU operation. They have been +tested on an actual NES and all give a passing result. I wrote them to verify +that my NES emulator's PPU was working properly. + +Each ROM runs several tests and reports a result code on screen and by beeping +a number of times. A result code of 1 always indicates that all tests were +passed; see below for the meaning of other codes for each test. + +The main source code for each test is included, and most tests are clearly +divided into sections. Some of the common support code is included, but not +all, since it runs on a custom setup. Contact me if you want to assemble the +tests yourself. + +Shay Green (swap to e-mail) + + +palette_ram +----------- +PPU palette RAM read/write and mirroring test + +1) Tests passed +2) Palette read shouldn't be buffered like other VRAM +3) Palette write/read doesn't work +4) Palette should be mirrored within $3f00-$3fff +5) Write to $10 should be mirrored at $00 +6) Write to $00 should be mirrored at $10 + + +power_up_palette +---------------- +Reports whether initial values in palette at power-up match those +that my NES has. These values are probably unique to my NES. + +1) Palette matches +2) Palette differs from table + + +sprite_ram +---------- +Tests sprite RAM access via $2003, $2004, and $4014 + +1) Tests passed +2) Basic read/write doesn't work +3) Address should increment on $2004 write +4) Address should not increment on $2004 read +5) Third sprite bytes should be masked with $e3 on read +6) $4014 DMA copy doesn't work at all +7) $4014 DMA copy should start at value in $2003 and wrap +8) $4014 DMA copy should leave value in $2003 intact + + +vbl_clear_time +-------------- +The VBL flag ($2002.7) is cleared by the PPU around 2270 CPU clocks +after NMI occurs. + +1) Tests passed +2) VBL flag cleared too soon +3) VBL flag cleared too late + + +vram_access +----------- +Tests PPU VRAM read/write and internal read buffer operation + +1) Tests passed +2) VRAM reads should be delayed in a buffer +3) Basic Write/read doesn't work +4) Read buffer shouldn't be affected by VRAM write +5) Read buffer shouldn't be affected by palette write +6) Palette read should also read VRAM into read buffer +7) "Shadow" VRAM read unaffected by palette transparent color mirroring diff --git a/blargg_ppu_tests_2005.09.15b/source/palette_ram.asm b/blargg_ppu_tests_2005.09.15b/source/palette_ram.asm new file mode 100644 index 0000000..d65e956 --- /dev/null +++ b/blargg_ppu_tests_2005.09.15b/source/palette_ram.asm @@ -0,0 +1,106 @@ +; PPU palette RAM read/write and mirroring test +; to do: check that upper two bits aren't stored + + .include "prefix_ppu.a" + +; Set VRAM address to $3f00 + X +; Preserved: A, X, Y +set_pal_addr: + pha + bit $2002 + lda #$3f + sta $2006 + txa + sta $2006 + pla + rts + +; Set palette entry X to A +; Preserved: A, X, Y +set_pal_entry: + jsr set_pal_addr + sta $2007 + rts + +; Read palette entry X into A +; Preserved: X, Y +get_pal_entry: + jsr set_pal_addr + lda $2007 + and #$3f + rts + +reset: + lda #50 + jsr delay_msec + + jsr wait_vbl + lda #0 + sta $2000 + sta $2001 + + lda #2;) Palette read shouldn't be buffered like other VRAM + sta result + ldx #$00 + lda #$12 + jsr set_pal_entry + lda #$34 + sta $2007 + jsr get_pal_entry + lda $2007 + cmp #$12 + jsr error_if_eq + + lda #3;) Palette write/read doesn't work + sta result + ldx #$00 + lda #$34 + jsr set_pal_entry + jsr get_pal_entry + lda $2007 + cmp #$34 + jsr error_if_ne + + lda #4;) Palette should be mirrored within $3f00-$3fff + sta result + ldx #$00 + lda #$12 + jsr set_pal_entry + ldx #$e0 + lda #$34 + jsr set_pal_entry + ldx #$00 + jsr get_pal_entry + cmp #$34 + jsr error_if_ne + + lda #5;) Write to $10 should be mirrored at $00 + sta result + ldx #$00 + lda #$12 + jsr set_pal_entry + ldx #$10 + lda #$34 + jsr set_pal_entry + ldx #$00 + jsr get_pal_entry + cmp #$34 + jsr error_if_ne + + lda #6;) Write to $00 should be mirrored at $10 + sta result + ldx #$10 + lda #$12 + jsr set_pal_entry + ldx #$00 + lda #$34 + jsr set_pal_entry + ldx #$10 + jsr get_pal_entry + cmp #$34 + jsr error_if_ne + + lda #1;) Tests passed + sta result + jmp report_final_result + diff --git a/blargg_ppu_tests_2005.09.15b/source/power_up_palette.asm b/blargg_ppu_tests_2005.09.15b/source/power_up_palette.asm new file mode 100644 index 0000000..1fe0307 --- /dev/null +++ b/blargg_ppu_tests_2005.09.15b/source/power_up_palette.asm @@ -0,0 +1,35 @@ +; Reports whether initial values in palette at power-up match those +; that my NES has. These values are probably unique to my NES. + + .include "prefix_ppu.a" + +reset: + lda #50 + jsr delay_msec + + jsr wait_vbl + lda #0 + sta $2000 + sta $2001 + + lda #2;) Palette differs from table + sta result + lda #$3f + sta $2006 + lda #$00 + sta $2006 + ldx #0 +: lda $2007 + cmp table,x + jsr error_if_ne + inx + cpx #$20 + bne - + + lda #1;) Palette matches + sta result + jmp report_final_result + +table: + .db $09,$01,$00,$01,$00,$02,$02,$0D,$08,$10,$08,$24,$00,$00,$04,$2C + .db $09,$01,$34,$03,$00,$04,$00,$14,$08,$3A,$00,$02,$00,$20,$2C,$08 diff --git a/blargg_ppu_tests_2005.09.15b/source/prefix_ppu.asm b/blargg_ppu_tests_2005.09.15b/source/prefix_ppu.asm new file mode 100644 index 0000000..a93e211 --- /dev/null +++ b/blargg_ppu_tests_2005.09.15b/source/prefix_ppu.asm @@ -0,0 +1,48 @@ +; Prefix included at the beginning of each test. Defines vectors +; and other things for custom devcart loader. +; +; Reset vector points to "reset". +; NMI points to "nmi" if defined, otherwise default_nmi. +; IRQ points to "irq" if defined, otherwise default_irq. + +default_nmi: + rti + +default_irq: + bit $4015 + rti + +; Delays for almost A milliseconds (A * 0.999009524 msec) +; Preserved: X, Y +delay_msec: + +; Delays for almost 'A / 10' milliseconds (A * 0.099453968 msec) +; Preserved: X, Y +delay_01_msec: + +; Variable delay. All calls include comment stating number of clocks +; used, including the setup code in the caller. +delay_yaNN: + +; Report value in low-mem variable 'result' as number of beeps and +; code printed to console, then jump to forever. +report_final_result: + +; Disable IRQ and NMI then loop endlessly. +forever: + + +; Report error if last result was non-zero +error_if_ne: + bne error_if_ + rts + +; Report error if last result was zero +error_if_eq: + beq error_if_ + rts + +; Report error +error_if_: + jmp report_final_result + diff --git a/blargg_ppu_tests_2005.09.15b/source/sprite_ram.asm b/blargg_ppu_tests_2005.09.15b/source/sprite_ram.asm new file mode 100644 index 0000000..b6685ee --- /dev/null +++ b/blargg_ppu_tests_2005.09.15b/source/sprite_ram.asm @@ -0,0 +1,131 @@ +; Tests sprite RAM access via $2003, $2004, and $4014 + + .include "prefix_ppu.a" + +sprites = $200 + +reset: + lda #50 + jsr delay_msec + + jsr wait_vbl + lda #0 + sta $2000 + sta $2001 + + lda #2;) Basic read/write doesn't work + sta result + lda #0 + sta $2003 + lda #$12 + sta $2004 + lda #0 + sta $2003 + lda $2004 + cmp #$12 + jsr error_if_ne + + lda #3;) Address should increment on $2004 write + sta result + lda #0 + sta $2003 + lda #$12 + sta $2004 + lda #$34 + sta $2004 + lda #1 + sta $2003 + lda $2004 + cmp #$34 + jsr error_if_ne + + lda #4;) Address should not increment on $2004 read + sta result + lda #0 + sta $2003 + lda #$12 + sta $2004 + lda #$34 + sta $2004 + lda #0 + sta $2003 + lda $2004 + lda $2004 + cmp #$34 + jsr error_if_eq + + lda #5;) Third sprite bytes should be masked with $e3 on read + sta result + lda #3 + sta $2003 + lda #$ff + sta $2004 + lda #3 + sta $2003 + lda $2004 + cmp #$e3 + jsr error_if_eq + + lda #6;) $4014 DMA copy doesn't work at all + sta result + ldx #0 ; set up data to copy from +: lda test_data,x + sta sprites,x + inx + cpx #4 + bne - + lda #0 ; dma copy + sta $2003 + lda #$02 + sta $4014 + ldx #0 ; set up data to copy from +: stx $2003 + lda $2004 + cmp test_data,x + jsr error_if_ne + inx + cpx #4 + bne - + + lda #7;) $4014 DMA copy should start at value in $2003 and wrap + sta result + ldx #0 ; set up data to copy from +: lda test_data,x + sta sprites,x + inx + cpx #4 + bne - + lda #1 ; dma copy + sta $2003 + lda #$02 + sta $4014 + ldx #1 ; set up data to copy from +: stx $2003 + lda $2004 + cmp sprites - 1,x + jsr error_if_ne + inx + cpx #5 + bne - + + lda #8;) $4014 DMA copy should leave value in $2003 intact + sta result + lda #1 ; dma copy + sta $2003 + lda #$02 + sta $4014 + lda #$ff + sta $2004 + lda #1 + sta $2003 + lda $2004 + cmp #$ff + jsr error_if_ne + + lda #1;) Tests passed + sta result + jmp report_final_result + +test_data: + .db $12,$82,$e3,$78 + \ No newline at end of file diff --git a/blargg_ppu_tests_2005.09.15b/source/vbl_clear_time.asm b/blargg_ppu_tests_2005.09.15b/source/vbl_clear_time.asm new file mode 100644 index 0000000..dc3b22e --- /dev/null +++ b/blargg_ppu_tests_2005.09.15b/source/vbl_clear_time.asm @@ -0,0 +1,48 @@ +; The VBL flag ($2002.7) is cleared by the PPU around 2270 CPU clocks +; after NMI occurs. + + .include "prefix_ppu.a" + +phase = 10 + +reset: + lda #100 + jsr delay_msec + + lda #1 + sta phase + + jsr wait_vbl + lda #$80 + sta $2000 + lda #$00 + sta $2001 + +wait: jmp wait + +nmi: ; 7 clocks for NMI vectoring + ldy #203 ; 2251 delay + lda #1 + jsr delay_ya1 + + dec phase ; 5 + bne + ; 3 + ; -1 + + lda $2002 ; read at 2268 + ldx #2;) VBL flag cleared too soon + stx result + and #$80 + jsr error_if_eq + jmp wait + +: bit <0 + lda $2002 ; read at 2272 + ldx #3;) VBL flag cleared too late + stx result + and #$80 + jsr error_if_ne + + lda #1;) Tests passed + sta result + jmp report_final_result diff --git a/blargg_ppu_tests_2005.09.15b/source/vram_access.asm b/blargg_ppu_tests_2005.09.15b/source/vram_access.asm new file mode 100644 index 0000000..a7c9a50 --- /dev/null +++ b/blargg_ppu_tests_2005.09.15b/source/vram_access.asm @@ -0,0 +1,143 @@ +; Tests PPU VRAM read/write and internal read buffer operation + + .include "prefix_ppu.a" + +; Set VRAM addr to $2f00 + A +; Preserved: A, X, Y +set_vram_pos: + pha + lda #$2f + sta $2006 + pla + sta $2006 + rts + +reset: + lda #50 + jsr delay_msec + + jsr wait_vbl + lda #0 + sta $2000 + sta $2001 + + lda #2;) VRAM reads should be delayed in a buffer + sta result + lda #$00 + jsr set_vram_pos + lda #$12 + sta $2007 + lda #$34 + sta $2007 + lda #$00 + jsr set_vram_pos + lda $2007 + lda $2007 + cmp #$34 + jsr error_if_eq + + lda #3;) Basic Write/read doesn't work + sta result + lda #$00 + jsr set_vram_pos + lda #$56 + sta $2007 + lda #$00 + jsr set_vram_pos + lda $2007 + lda $2007 + cmp #$56 + jsr error_if_ne + + lda #4;) Read buffer shouldn't be affected by VRAM write + sta result + lda #$00 + jsr set_vram_pos + lda #$78 + sta $2007 + lda #$00 + jsr set_vram_pos + lda #$00 + lda $2007 ; buffer now contains $78 + lda #$12 + sta $2007 ; shouldn't affect buffer + lda $2007 + cmp #$78 + jsr error_if_ne + + lda #5;) Read buffer shouldn't be affected by palette write + sta result + lda #$00 + jsr set_vram_pos + lda #$9a + sta $2007 + lda #$00 + jsr set_vram_pos + lda $2007 ; buffer now contains $9a + lda #$3f + sta $2006 + lda #$00 + sta $2006 + lda #$34 + sta $2007 ; shouldn't affect buffer + lda #$01 ; change back to non-palette addr to enable buffer + jsr set_vram_pos + lda $2007 + cmp #$9a + jsr error_if_ne + + lda #6;) Palette read should also read VRAM into read buffer + sta result + lda #$12 + jsr set_vram_pos + lda #$9a + sta $2007 + lda $2007 + lda #$3f + sta $2006 + lda #$12 + sta $2006 + lda $2007 ; fills buffer with VRAM hidden by palette + lda #$13 ; change back to non-palette addr to enable buffer + jsr set_vram_pos + lda $2007 + cmp #$9a + jsr error_if_ne + + lda #7;) "Shadow" VRAM read unaffected by palette mirroring + sta result + lda #$04 + jsr set_vram_pos + lda #$12 + sta $2007 + lda #$14 + jsr set_vram_pos + lda #$34 + sta $2007 + lda #$3f + sta $2006 + lda #$04 + sta $2006 + lda $2007 ; fills buffer with VRAM hidden by palette + lda #$13 ; change back to non-palette addr to enable buffer + jsr set_vram_pos + lda $2007 + cmp #$12 + jsr error_if_ne + lda #$34 + sta $2007 + lda #$3f + sta $2006 + lda #$14 + sta $2006 + lda $2007 ; fills buffer with VRAM hidden by palette + lda #$13 ; change back to non-palette addr to enable buffer + jsr set_vram_pos + lda $2007 + cmp #$34 + jsr error_if_ne + + lda #1;) Tests passed + sta result + jmp report_final_result + diff --git a/blargg_ppu_tests_2005.09.15b/sprite_ram.nes b/blargg_ppu_tests_2005.09.15b/sprite_ram.nes new file mode 100644 index 0000000..dfbb613 Binary files /dev/null and b/blargg_ppu_tests_2005.09.15b/sprite_ram.nes differ diff --git a/blargg_ppu_tests_2005.09.15b/vbl_clear_time.nes b/blargg_ppu_tests_2005.09.15b/vbl_clear_time.nes new file mode 100644 index 0000000..41c2450 Binary files /dev/null and b/blargg_ppu_tests_2005.09.15b/vbl_clear_time.nes differ diff --git a/blargg_ppu_tests_2005.09.15b/vram_access.nes b/blargg_ppu_tests_2005.09.15b/vram_access.nes new file mode 100644 index 0000000..301518b Binary files /dev/null and b/blargg_ppu_tests_2005.09.15b/vram_access.nes differ diff --git a/branch_timing_tests/1.Branch_Basics.nes b/branch_timing_tests/1.Branch_Basics.nes new file mode 100644 index 0000000..e7f81f0 Binary files /dev/null and b/branch_timing_tests/1.Branch_Basics.nes differ diff --git a/branch_timing_tests/2.Backward_Branch.nes b/branch_timing_tests/2.Backward_Branch.nes new file mode 100644 index 0000000..6e02064 Binary files /dev/null and b/branch_timing_tests/2.Backward_Branch.nes differ diff --git a/branch_timing_tests/3.Forward_Branch.nes b/branch_timing_tests/3.Forward_Branch.nes new file mode 100644 index 0000000..4e701b2 Binary files /dev/null and b/branch_timing_tests/3.Forward_Branch.nes differ diff --git a/branch_timing_tests/readme.txt b/branch_timing_tests/readme.txt new file mode 100644 index 0000000..1f05e8e --- /dev/null +++ b/branch_timing_tests/readme.txt @@ -0,0 +1,74 @@ +NES 6502 Branch Timing Test ROMs +-------------------------------- +These ROMs test timing of the branch instruction, including edge cases +which an emulator might get wrong. When run on a NES they all give a +passing result. Each ROM runs several tests and reports the result on +screen and by beeping a number of times. See below for the meaning of +failure codes for each test. THE TESTS MUST BE RUN (*AND* *PASS*) IN +ORDER, because some earlier ROMs test things that later ones assume will +work properly. + +Source code for each test is included, and most tests are clearly +divided into sections. Support code is also included, but it runs on a +custom devcart and assembler so it will require some effort to assemble. +Contact me if you'd like assistance porting them to your setup. + + +Branch Timing Summary +--------------------- +An untaken branch takes 2 clocks. A taken branch takes 3 clocks. A taken +branch that crosses a page takes 4 clocks. Page crossing occurs when the +high byte of the branch target address is different than the high byte +of address of the next instruction: + +branch_target: + ... + bne branch_target +next_instruction: + nop + ... +branch_target: + + +1.Branch_Basics +--------------- +Tests branch timing basics and PPU NMI timing, which is needed for the +tests + +2) NMI period is too short +3) NMI period is too too long +4) Branch not taken is too long +5) Branch not taken is too short +6) Branch taken is too long +7) Branch taken is too short + + +2.Backward_Branch +----------------- +Tests backward (negative) branch timing. + +2) Branch from $E4FD to $E4FC is too long +3) Branch from $E4FD to $E4FC is too short +4) Branch from $E5FE to $E5FD is too long +5) Branch from $E5FE to $E5FD is too short +6) Branch from $E700 to $E6FF is too long +7) Branch from $E700 to $E6FF is too short +8) Branch from $E801 to $E800 is too long +9) Branch from $E801 to $E800 is too short + + +3.Forward_Branch +---------------- +Tests forward (positive) branch timing. + +2) Branch from $E5FC to $E5FF is too long +3) Branch from $E5FC to $E5FF is too short +4) Branch from $E6FD to $E700 is too long +5) Branch from $E6FD to $E700 is too short +6) Branch from $E7FE to $E801 is too long +7) Branch from $E7FE to $E801 is too short +8) Branch from $E8FF to $E902 is too long +9) Branch from $E8FF to $E902 is too short + +-- +Shay Green diff --git a/branch_timing_tests/source/1.Branch_Basics.a b/branch_timing_tests/source/1.Branch_Basics.a new file mode 100644 index 0000000..dc0a778 --- /dev/null +++ b/branch_timing_tests/source/1.Branch_Basics.a @@ -0,0 +1,78 @@ +; Tests branch timing basics + + .include "prefix.a" + +test_name: + .db "BRANCH TIMING BASICS",0 + .code + +delay7 = $20 + +delay1: + jsr delay2 + jsr delay2 +delay2: + jsr delay3 + jsr delay3 +delay3: + jsr delay4 + jsr delay4 +delay4: + jsr delay5 + jsr delay5 +delay5: + jsr delay6 + jsr delay6 +delay6: + jsr delay7 + jsr delay7 + jmp delay7 + .code + +reset: + jsr init_tests + + lda #$EA ; nop + sta delay7 + lda #$60 ; rts + sta delay7 + 1 + lda #2;) NMI period is too short/3)too long + jsr begin_test + jsr delay7 + jsr delay7 + jsr delay5 + jsr delay4 + jsr delay2 + jsr delay2 + jsr delay1 + jsr delay1 + jsr end_test + + lda #$B0 ; bcs + sta delay7 + lda #$60 ; rts + sta delay7 + 2 + lda #4;) Branch not taken is too long/5)too short + jsr begin_test + clc + jsr delay7 + jsr delay7 + jsr delay5 + jsr delay4 + jsr delay2 + jsr delay2 + jsr delay1 + jsr delay1 + jsr end_test + + lda #6;) Branch taken is too long/7)too short + jsr begin_test + ldy #24 + ldx #39 +: dex + bne - + dey + bne - + jsr end_test + + jmp tests_passed diff --git a/branch_timing_tests/source/2.Backward_Branch.a b/branch_timing_tests/source/2.Backward_Branch.a new file mode 100644 index 0000000..3b84995 --- /dev/null +++ b/branch_timing_tests/source/2.Backward_Branch.a @@ -0,0 +1,76 @@ +; Tests backward (negative) branch timing. +; "Branch from $XX to $YY" means that the low byte of the +; address of the branch opcode is $XX and the low byte of +; the address of the branch destination is $YY. + + .include "prefix.a" + +test_name: + .db "BACKWARD BRANCH TIMING",0 + .code + +reset: + jsr init_tests + + lda #2;) Branch from $E4FD to $E4FC is too long/3)short + jsr begin_test + ldy #23 + ldx #240 +: jsr loop_fc + dey + bne - + jsr end_test + + lda #4;) Branch from $E5FE to $E5FD is too long/5)short + jsr begin_test + ldy #20 + ldx #41 +: jsr loop_fd + dey + bne - + jsr end_test + + lda #6;) Branch from $E700 to $E6FF is too long/7)short + jsr begin_test + ldy #20 + ldx #41 +: jsr loop_ff + dey + bne - + jsr end_test + + lda #8;) Branch from $E801 to $E800 is too long/9)short + jsr begin_test + ldy #23 + ldx #240 +: jsr loop_00 + dey + bne - + jsr end_test + + jmp tests_passed + + .org $E4FC +loop_fc: +: dex + bne - + rts + + .org $E5FD +loop_fd: +: dex + bne - + rts + + .org $E6FF +loop_ff: +: dex + bne - + rts + + .org $E800 +loop_00: +: dex + bne - + rts + diff --git a/branch_timing_tests/source/3.Forward_Branch.a b/branch_timing_tests/source/3.Forward_Branch.a new file mode 100644 index 0000000..e04cf86 --- /dev/null +++ b/branch_timing_tests/source/3.Forward_Branch.a @@ -0,0 +1,82 @@ +; Tests forward (positive) branch timing. +; "Branch from $XX to $YY" means that the low byte of the +; address of the branch opcode is $XX and the low byte of +; the address of the branch destination is $YY. + + .include "prefix.a" + +test_name: + .db "FORWARD BRANCH TIMING",0 + .code + +reset: + jsr init_tests + + lda #2;) Branch from $E5FC to $E5FF is too long/3)short + jsr begin_test + ldy #15 + ldx #97 +: jsr loop_fc + dey + bne - + jsr end_test + + lda #4;) Branch from $E6FD to $E700 is too long/5)short + jsr begin_test + ldy #13 + ldx #203 +: jsr loop_fd + dey + bne - + nop + nop + jsr end_test + + lda #6;) Branch from $E7FE to $E801 is too long/7)short + jsr begin_test + ldy #15 + ldx #97 +: jsr loop_fe + dey + bne - + jsr end_test + + lda #8;) Branch from $E8FF to $E902 is too long/9)short + jsr begin_test + ldy #15 + ldx #97 +: jsr loop_ff + dey + bne - + jsr end_test + + jmp tests_passed + + .org $E5FC +loop_fc: + bne + + rts +: dex + jmp loop_fc + + .org $E6FD +loop_fd: + bne + + rts +: dex + jmp loop_fd + + .org $E7FE +loop_fe: + bne + + rts +: dex + jmp loop_fe + + .org $E8FF +loop_ff: + bne + + rts +: dex + jmp loop_ff + diff --git a/branch_timing_tests/source/console.a b/branch_timing_tests/source/console.a new file mode 100644 index 0000000..946b07f --- /dev/null +++ b/branch_timing_tests/source/console.a @@ -0,0 +1,107 @@ + +console_pos = $7f2 +console_pos_h = $7f3 + +; Print char A to console +; Preserved: A, X, Y +print_char: + jsr wait_vbl ; wait for safe access +print_char_no_wait: + pha + lda console_pos_h + sta $2006 + inc console_pos + lda console_pos + sta $2006 + lda #0 ; restore scroll + sta $2005 + sta $2005 + pla + sta $2007 + rts + .code + +; Go to next line +; Preserved: A, X, Y +console_newline: + pha + lda console_pos + and #$e0 + clc + adc #$21 + sta console_pos + lda console_pos_h + adc #0 + sta console_pos_h + pla + rts + .code + +; Initialize console +init_console: + lda #$81 + sta console_pos + lda #$20 + sta console_pos_h + + jsr wait_vbl ; init ppu + lda #0 + sta $2000 + sta $2001 + + lda #$3f ; load palette + jsr set_vpage + lda #15 ; bg + ldx #48 ; fg + ldy #8 +pal: sta $2007 + stx $2007 + stx $2007 + stx $2007 + dey + bne pal + + lda #$02 ; load tiles + jsr set_vpage + lda #chr_data.lsb + sta <$f0 + lda #chr_data.msb + sta <$f1 + ldy #0 + lda #59 ; 59 chars in data + sta <$f2 +chr_loop: + ldx #8 + lda #0 +: sta $2007 + dex + bne - + + ldx #8 +: lda ($f0),y + iny + sta $2007 + dex + bne - + + tya + bne + + inc <$f1 +: dec <$f2 + bne chr_loop + + lda #32 + jsr fill_nametable + + jsr wait_vbl ; enable ppu + lda #0 + sta $2005 + sta $2005 + lda #$0a + sta $2001 + rts + .code + +chr_data: + .incbin "chr.bin" + diff --git a/branch_timing_tests/source/debug.a b/branch_timing_tests/source/debug.a new file mode 100644 index 0000000..fff98dc --- /dev/null +++ b/branch_timing_tests/source/debug.a @@ -0,0 +1,154 @@ + +; Beep A times. Made to require minimal features from APU. +debug_beeps: +beep_loop: + pha + + lda #1 ; set up square 1 + sta $4015 + sta $4003 + sta $4001 + sta $4002 + + lda #$0f ; fade volume +: pha + eor #$30 + sta $4000 + lda #8 + jsr delay_msec + pla + clc + adc #-1 + bpl - + + lda #0 + sta $4015 ; silence square for a bit + lda #120 + jsr delay_msec + + pla + clc + adc #-1 + bne beep_loop + rts + .code + +; Print indicated register to console as $hh +; Preserved: A, X, Y, P +print_a: + php + jsr debug_byte + plp + rts + .code + +print_x: + php + pha + txa + jsr debug_byte + pla + plp + rts + .code + +print_y: + php + pha + tya + jsr debug_byte + pla + plp + rts + .code + +print_s: + php + pha + txa + pha + tsx + txa + jsr debug_byte + pla + tax + pla + plp + rts + .code + +print_p: + php + pha + php + pla + jsr debug_byte + pla + plp + rts + .code + +print_ay: + php + pha + lda #36 + jsr debug_char + pla + pha + jsr hex_byte + tya + jsr hex_byte + lda #32 ; ' ' + jsr debug_char_no_wait + pla + plp + rts + .code + +print_newline: + jmp debug_newline + .code + +; Print address YA to console as $hhhh +; Preserved: A, X, Y +debug_addr: + pha + lda #36 ; '$' + jsr debug_char + tya + jsr hex_byte + jmp debug_byte_impl + .code + +; Print byte A to console as $hh +; Preserved: A, X, Y +debug_byte: + pha + lda #36 ; '$' + jsr debug_char +debug_byte_impl: + pla + pha + jsr hex_byte + lda #32 ; ' ' + jsr debug_char_no_wait + pla + rts + +hex_byte: + pha + lsr a + lsr a + lsr a + lsr a + jsr nybble + pla + and #$0f +nybble: + cmp #10 + bcc not_letter + adc #6 ; relies on carry being set +not_letter: + adc #$30 + jmp debug_char_no_wait + .code diff --git a/branch_timing_tests/source/delays.a b/branch_timing_tests/source/delays.a new file mode 100644 index 0000000..72d62c8 --- /dev/null +++ b/branch_timing_tests/source/delays.a @@ -0,0 +1,118 @@ +; to do: delay loops that take only a single count + +; Delay for almost A milliseconds (A * 0.999009524 msec) +; Preserved: X, Y +delay_msec: + pha ; 3 + lda #253 ; 2 + sec ; 2 +delay_msec_: + nop ; 2 + adc #-2 ; 2 + bne delay_msec_ ; 3 + ; -1 + pla ; 4 + clc ; 2 + adc #-1 ; 2 + bne delay_msec ; 3 + rts + .code + +; Delay for almost 'A / 10' milliseconds (A * 0.099453968 msec) +; Preserved: X, Y +delay_01msec: + pha ; 3 + lda #18 ; 2 + sec ; 2 +delay_01msec_: + nop ; 2 + nop ; 2 + adc #-2 ; 2 + bne delay_01msec_ ; 3 + ; -1 + pla ; 4 + clc ; 2 + adc #-1 ; 2 + bne delay_01msec ; 3 + rts + .code + +; Delay for almost A*10 milliseconds +; Preserved: X, Y +delay_10msec: + pha + lda #10 + jsr delay_msec + pla + clc + adc #-1 + bne delay_10msec + rts + .code + +; Delay n clocks +; Preserved: P, A, X, Y +delay_32: nop +delay_30: nop +delay_28: nop +delay_26: nop +delay_24: nop +delay_22: nop +delay_20: nop +delay_18: nop +delay_16: nop +delay_14: nop +delay_12: rts + +delay_33: nop +delay_31: nop +delay_29: nop +delay_27: nop +delay_25: nop +delay_23: nop +delay_21: nop +delay_19: nop +delay_17: beq + ; 5 +: bne + +: rts ; 6 + .code + +; Delay (5 * A + 6) * Y + 7 + n clocks +delay_ya11: + .db $a2 ; 1 ldx #imm +delay_ya10: + .db $a2 ; 1 ldx #imm +delay_ya9: + .db $a2 ; 1 ldx #imm +delay_ya8: + .db $a2 ; 1 ldx #imm +delay_ya7: + .db $a2 ; 1 ldx #imm +delay_ya6: + .db $a2 ; 1 ldx #imm +delay_ya5: + .db $a2 ; 1 ldx #imm +delay_ya4: + .db $a2 ; 1 ldx #imm +delay_ya3: + .db $a2 ; 1 ldx #imm +delay_ya2: + .db $a2 ; 1 ldx #imm +delay_ya1: + .db $a6 ; 1 ldx zp +delay_ya0: + nop ; 2 +delay_ya: + ; 2 lda # + ; 2 ldy # + ; 6 jsr + tax ; *2 +delay_yax: + dex ; **2 + bne delay_yax ; **3 + ; *-1 + dey ; *2 + bne delay_ya ; *3 + ; -1 + rts ; 6 + .code diff --git a/branch_timing_tests/source/ppu_util.a b/branch_timing_tests/source/ppu_util.a new file mode 100644 index 0000000..f004227 --- /dev/null +++ b/branch_timing_tests/source/ppu_util.a @@ -0,0 +1,123 @@ + +; Clear VBL flag then wait for it to be set +; Preserved: A, X, Y +wait_vbl: + bit $2002 +: bit $2002 + bpl - + rts + .code + +; Set VRAM address to A * $100 +; Preserved: X, Y +set_vpage: + bit $2002 + sta $2006 + lda #0 + sta $2006 + rts + .code + +; Set VRAM address to A * $100 + X +; Preserved: A, X, Y +set_vaddr: + bit $2002 + sta $2006 + stx $2006 + rts + .code + +; Set X and Y scroll +; Preserved: A, X, Y +set_vscroll: + bit $2002 + stx $2005 + sty $2005 + rts + .code + +; Turn off NMI and disable BG and sprites +; Preserved: A, X, Y +disable_ppu: + pha + lda #0 + sta $2000 + sta $2001 + bit $2002 + sta $2006 + sta $2006 + pla + rts + .code + +; Set sprite memory to $ff +; Preserved: Y +clear_sprites: + lda #$ff + ldx #0 +: sta $2004 + dex + bne - + rts + .code + +; Clear/fill nametable with 0/A and clear attributes to 0 +clear_nametable: + lda #0 +fill_nametable: + pha + lda #$20 + jsr set_vpage + pla + ldx #240 +: sta $2007 + sta $2007 + sta $2007 + sta $2007 + dex + bne - + lda #0 + ldx #64 +: sta $2007 + dex + bne - + rts + .code + +; Clear/fill VRAM with 0/A +clear_vram: + lda #0 +fill_vram: + ldx #0 + ldy #$24 + bne fill_vram_ + +; Clear/fill CHR with 0/A +fill_chr1: + ldx #$10 + ldy #$10 + bne fill_vram_ + +fill_chr0: + ldx #0 + ldy #$10 + bne fill_vram_ + +clear_chr: + lda #0 +fill_chr: + ldx #0 + ldy #$20 +; Fill VRAM Y*$100 bytes of VRAM with A, starting at X*$100 +fill_vram_: + bit $2002 + stx $2006 + ldx #0 + stx $2006 +: sta $2007 + dex + bne - + dey + bne - + rts + .code diff --git a/branch_timing_tests/source/prefix.a b/branch_timing_tests/source/prefix.a new file mode 100644 index 0000000..724fdd5 --- /dev/null +++ b/branch_timing_tests/source/prefix.a @@ -0,0 +1,40 @@ + .include "validation.a" + +nmi_count = 10 + +nmi: inc 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: X, Y +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + + +; Delays n clocks, from 2 to 16777215 +; Preserved: X, Y +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + .if (n) < 14 .and (n) <> 12 + delay_inline (n) + .elseif (n) < 27 + delay_unrolled (n) + .elseif <(n) = 0 + delay_256 (n) + .else + lda #<((n)-27) + jsr delay_a_25_clocks + delay_256 ((n)-27) + .endif +.endmacro + + +; Delays A+25 clocks (including JSR) +; Preserved: X, Y +.align 64 +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256+16 clocks (including JSR) +; Preserved: X, Y +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_clocks_: + pha + lda #256-19-22-16 + bne @first ; always branches +: pha + lda #256-19-22 +@first: jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536+16 clocks (including JSR) +; Preserved: X, Y +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_clocks_: + pha + lda #256-19-22-16 + bne @first +: pha + lda #256-19-22 +@first: jsr delay_a_25_clocks + lda #255 + jsr delay_256a_clocks_ + pla + clc + adc #-1 + bne :- + rts + +.macro delay_inline n + .if n = 7 .or n >= 9 + pha + pla + delay_inline (n-7) + .elseif n >= 3 .and n & 1 + lda <0 + delay_inline (n-3) + .elseif n >= 2 + nop + delay_inline (n-2) + .elseif n > 0 + .error "delay_short internal error" + .endif +.endmacro + +.macro delay_unrolled n + .if n & 1 + lda <0 + jsr delay_unrolled_-((n-15)/2) + .else + jsr delay_unrolled_-((n-12)/2) + .endif +.endmacro + + .res 7,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_256 n + .if >n + lda #>n + jsr delay_256a_clocks_ + .endif + .if ^n + lda #^n + jsr delay_65536a_clocks_ + .endif +.endmacro + diff --git a/cpu_dummy_reads/source/common/macros.inc b/cpu_dummy_reads/source/common/macros.inc new file mode 100644 index 0000000..ea54ba6 --- /dev/null +++ b/cpu_dummy_reads/source/common/macros.inc @@ -0,0 +1,65 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name +.macro zp_res name,size + seg_data "ZEROPAGE",{name: .res size} +.endmacro + +.macro bss_res name,size + seg_data "BSS",{name: .res size} +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "STRINGS",{Addr: data} +.endmacro + +; If name isn't yet defined, defines it with value +.macro SET_DEFAULT name,value + .ifndef name + name=value + .endif +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro diff --git a/cpu_dummy_reads/source/common/nes.inc b/cpu_dummy_reads/source/common/nes.inc new file mode 100644 index 0000000..9959b3b --- /dev/null +++ b/cpu_dummy_reads/source/common/nes.inc @@ -0,0 +1,34 @@ +; NES I/O locations and masks + +; Clocks per second (NTSC) +CLOCK_RATE = 1789773 + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 diff --git a/cpu_dummy_reads/source/common/print.s b/cpu_dummy_reads/source/common/print.s new file mode 100644 index 0000000..60d1f8e --- /dev/null +++ b/cpu_dummy_reads/source/common/print.s @@ -0,0 +1,194 @@ +; Prints values in various ways to output, including numbers and strings. + +newline = 10 + +; Prints indicated register to console as two hex chars and space +; Preserved: A, X, Y, P +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: X, Y +print_hex: + ; Update checksum + pha + jsr update_crc + pla + + ; Print high nibble + pha + lsr a + lsr a + lsr a + lsr a + jsr @nibble + pla + + ; Print low nibble + and #$0F +@nibble: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints character and updates checksum UNLESS it's a newline. +; Preserved: X, Y +print_char: + cmp #newline + beq :+ + pha + jsr update_crc + pla +: jmp print_char_ + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str + jsr_with_addr print_str_addr,{.byte str,0} +.endmacro + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: Y +print_dec: + ; Hundreds + cmp #100 + blt @tens + ldx #'0' +: sbc #100 + inx + cmp #100 + bge :- + jsr @digit + + ; Tens +@tens: cmp #10 + blt @ones + ldx #'0' +: sbc #10 + inx + cmp #10 + bge :- + jsr @digit + + ; Ones +@ones: ora #'0' + jmp print_char + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y +.macro print_cc cond,yes,no + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla +.endmacro diff --git a/cpu_dummy_reads/source/common/serial.s b/cpu_dummy_reads/source/common/serial.s new file mode 100644 index 0000000..26b2897 --- /dev/null +++ b/cpu_dummy_reads/source/common/serial.s @@ -0,0 +1,41 @@ +; Serial output at 57600 bits/sec on controller port 2 +; +; Uses stack and register A only, and doesn't mind page crossing +; (uses subroutines instead of loops). + +; Initializes serial. If this isn't done, first byte sent to +; PC might be corrupt. +; Preserved: X, Y +serial_init: + sec + lda #$FF + bne serial_write_ ; always branches + + +; Writes byte A to serial +; Preserved: X, Y +serial_write: + clc +serial_write_: + jsr @bit ; start + nop ; TODO: why the extra delay? + jsr @first ; bit 0 + jsr @bit ; bit 1 + jsr @bit ; bit 2 + jsr @bit ; bit 3 + jsr @bit ; bit 4 + jsr @bit ; bit 5 + jsr @bit ; bit 6 + jsr @bit ; bit 7 + sec ; 2 stop bit +@first: nop ; 4 + nop +@bit: ; 6 jsr + pha ; 3 + rol a ; 2 + and #1 ; 2 + sta JOY1 ; 4 + pla ; 4 + ror a ; 2 + nop ; 2 + rts ; 6 diff --git a/cpu_dummy_reads/source/common/shell.inc b/cpu_dummy_reads/source/common/shell.inc new file mode 100644 index 0000000..61fe108 --- /dev/null +++ b/cpu_dummy_reads/source/common/shell.inc @@ -0,0 +1,434 @@ +; Program shell with text console. Included before user code. + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INC + .error "File included twice" + .end +.endif +SHELL_INC = 1 + + +; ******************************************* Prefix + +.segment "CODE2" + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E + +; RAM that isn't cleared by init routine +nv_ram = $7F0 + +; Macros and constants +.include "macros.inc" +.include "nes.inc" + +; Interrupt handlers are wrapped with these +.define BEGIN_NMI nmi: +.define END_NMI + +.define BEGIN_IRQ irq: +.define END_IRQ + +; Set undefined flags to 0, allowing simpler .if statements +SET_DEFAULT BUILD_NSF,0 +SET_DEFAULT BUILD_MULTI,0 +SET_DEFAULT BUILD_DEVCART,0 + +; Number of clocks devcart takes to jump to user reset +SET_DEFAULT DEVCART_DELAY,0 + + +; ******************************************* Libraries + +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +.if !BUILD_MULTI + .include "serial.s" +.endif + +; Sets up environment, calls main, then exits with code 0 +run_main: + ; Initialize libraries + jsr init_crc + + ; Establish consistent environment before + ; running main + jsr wait_vbl + lda #PPUMASK_BG0 + sta PPUMASK + delay 2370+24 + lda #$34 + pha + lda #0 + sta SNDMODE + tax + tay + clc + clv + plp + + jsr main + + ; Default to silent exit if main returns + lda #0 + ; FALL THROUGH + +; Exits program and prints result code if non-zero +exit: + ; Reset stack + ldx #$FF + txs + + ; Disable interrupts + sei + pha + jsr nmi_off + pla + + jmp exit_ + +; Reports internal error and exits program +internal_error: + print_str "Internal error" + lda #1 + jmp exit + + +.if BUILD_NSF || BUILD_MULTI || BUILD_DEVCART + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.else + .include "console.s" +.endif + + +; ******************************************* Single Test + +.if !BUILD_MULTI + +print_char_: + jsr console_print + jmp serial_write + +; Reset handler +.ifndef CUSTOM_RESET + reset = std_reset +.endif + +.macro init_nes + sei + cld + ldx #$FF + txs + + .if !BUILD_NSF + ; Init PPU + lda #0 + sta PPUCTRL + sta PPUMASK + .endif + + ; Clear RAM + lda #0 + ldx #7 ; last page + ldy #@ascii +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>@ascii_end + bne @page + +.pushseg +.rodata +@ascii: + .incbin "ascii.chr" +@ascii_end: +.popseg +.endif + + jsr console_init + jsr serial_init + jmp run_main + + +; Exit handler +exit_: + ; 0: "" + cmp #1 + jlt exit2 + + ; 1: "Failed" + bne :+ + print_str {newline,"Failed"} + jmp exit2 + + ; n: "Error n" +: pha + print_str {newline,"Error "} + jsr print_dec + pla + +exit2: +.if !BUILD_DEVCART + ; Be sure output is visible + pha + print_str {newline,newline,newline} + jsr console_show + pla + + ; Report audibly as well + jsr beep_bits +.else + ; Tell host to stop capturing serial + lda #$1A + jsr serial_write + delay_msec 400 +.endif + + ; Clear nv_ram + lda #0 + ldx #filename + sta addr+1 + jsr print_str_addr + jsr print_newline + rts + +.pushseg +.segment "STRINGS" +; Filename terminated with zero byte, or just zero byte +; if filename isn't available. +filename: + .incbin "ram:rom.nes" + .byte 0 +.popseg +.else +print_filename: + rts + +filename: + .byte 0 +.endif + + +; User code goes in main code segment +.segment "CODE" + nop diff --git a/cpu_dummy_reads/source/common/testing.s b/cpu_dummy_reads/source/common/testing.s new file mode 100644 index 0000000..8bb0741 --- /dev/null +++ b/cpu_dummy_reads/source/common/testing.s @@ -0,0 +1,162 @@ +; Utilities for writing test ROMs + +zp_res test_code,1 ; code of current test +zp_res test_name,2 ; address of name of current test, or 0 of none + + +; Reports that all tests passed +tests_passed: +.if !BUILD_MULTI + jsr print_filename + print_str "Passed" +.endif + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + lda test_code + + ; Treat 0 as 1, in case it wasn't ever set + bne :+ + lda #1 + sta test_code +: + ; If code >= 2, print name + cmp #2 + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: +.if !BUILD_MULTI + jsr print_filename +.endif + ; End program + lda test_code + jmp exit + + +; Sets current test code and optional name. Also resets +; checksum. +.macro set_test code,name + pha + lda #code + jsr set_test_ + .local Addr + lda #Addr + sta n + sta addr+1 +.endmacro + +begin: jsr wait_vbl + delay 29800 + rts + +main: set_test 2,"$2002 must be mirrored every 8 bytes to $3FFA" + jsr begin + lda $3FFA + jpl test_failed + lda $3FFA + jmi test_failed + + set_test 3,"LDA abs,x" + jsr begin + ldx #$22 + lda $2000,x ; no dummy read + jpl test_failed + lda $2002 + jmi test_failed + + jsr begin + ldx #$22 + lda $20E0,x ; dummy read from $2002 + jmi test_failed + lda $2002 + jmi test_failed + + jsr begin + ldx #$22 + lda $20E2,x ; dummy read from $2004 + lda $2002 + jpl test_failed + + jsr begin + ldx #$22 + lda $3FE0,x ; dummy read from $3F02 + lda $2002 + jmi test_failed + + set_test 4,"STA abs,x" + jsr begin + sta $2002 ; no dummy read + lda $2002 + jpl test_failed + + jsr begin + ldx #$22 + sta $20E0,x ; dummy read from $2002 + jmi test_failed + lda $2002 + jmi test_failed + + jsr begin + ldx #$22 + sta $20E2,x ; dummy read from $2004 + lda $2002 + jpl test_failed + + jsr begin + ldx #$22 + sta $3FE0,x ; dummy read from $3F02 + lda $2002 + jmi test_failed + + set_test 5,"LDA (z),y" + jsr begin + ldy #$22 + set_addr $2000 + lda (addr),y ; no dummy read + jpl test_failed + lda $2002 + jmi test_failed + + jsr begin + ldy #$22 + set_addr $20E0 + lda (addr),y ; dummy read from $2002 + jmi test_failed + lda $2002 + jmi test_failed + + jsr begin + ldy #$22 + set_addr $20E2 + lda (addr),y ; dummy read from $2004 + lda $2002 + jpl test_failed + + jsr begin + ldy #$22 + set_addr $3FE0 + lda (addr),y ; dummy read from $3F02 + lda $2002 + jmi test_failed + + set_test 6,"STA (z),y" + jsr begin + set_addr $20E0 + ldy #$22 + sta (addr),y ; dummy read from $2002 + jmi test_failed + lda $2002 + jmi test_failed + + jsr begin + set_addr $20E2 + ldy #$22 + sta (addr),y ; dummy read from $2004 + lda $2002 + jpl test_failed + + jsr begin + set_addr $3FE0 + ldy #$22 + sta (addr),y ; dummy read from $3F02 + lda $2002 + jmi test_failed + + set_test 7,"LDA (z,x)" + jsr begin + ldx #0 + set_addr $2002 + lda (addr,x) ; no dummy read + jpl test_failed + + set_test 8,"STA (z,x)" + jsr begin + ldx #0 + set_addr $2002 + sta (addr,x) ; no dummy read + lda $2002 + jpl test_failed + + jmp tests_passed diff --git a/cpu_dummy_reads/source/nes.cfg b/cpu_dummy_reads/source/nes.cfg new file mode 100644 index 0000000..1848af5 --- /dev/null +++ b/cpu_dummy_reads/source/nes.cfg @@ -0,0 +1,43 @@ +# ca65 configuration for CNROM with code at $E000 + +# fill=yes forces area to be padded to specified size in output +MEMORY +{ + ZP: start = $10, size = $F0, type = rw; + RAM: start = $0200, size = $0600, type = rw; + + # My devcart only has memory from $E000-$FFFF + HEADER: start = 0, size = $10, type = ro, fill=yes; + UNUSED: start = $8000, size = $6000, type = ro, fill=yes; + ROM: start = $E000, size = $1FF4, type = ro, fill=yes; + + # Extra 6 bytes in vectors because built-in NES configuration + # does the same. Stupid, but better to keep compatible with it + # so small examples can use the built-in configuration. + VECTORS:start = $FFF4, size = $C, type = ro, fill=yes; + + CHARS: start = 0, size = $2000, type = ro; +} + +# align=$100 allows use of .align directive with a value up to $100 +# optional=yes avoids warning if segment is never used +SEGMENTS +{ + ZEROPAGE: load = ZP, type = zp; + BSS: load = RAM, type = bss; + + HEADER: load = HEADER, type = ro; + CODE: load = ROM, type = ro, align=$100; + + # Library code goes into this segment, keeping user code same + # length regardless of runtime: devcart, ROM, NSF, etc. + CODE2: load = ROM, type = ro, align=$100, optional=yes; + RODATA: load = ROM, type = ro; + + # Separate segment for strings so RODATA can have pointers to + # strings + STRINGS: load = ROM, type = ro, optional=yes; + VECTORS: load = VECTORS,type = ro; + + CHARS: load = CHARS, type = ro, align=$2000, optional=yes; +} diff --git a/cpu_dummy_reads/source/readme.txt b/cpu_dummy_reads/source/readme.txt new file mode 100644 index 0000000..589ffb1 --- /dev/null +++ b/cpu_dummy_reads/source/readme.txt @@ -0,0 +1,100 @@ +NES Tests Source Code +--------------------- + +Building with ca65 +------------------ +To assemble a test with ca65, use the following commands: + + ca65 -I common -o rom.o source_filename_here.s + ld65 -C nes.cfg rom.o -o rom.nes + your_favorite_nes_emulator rom.nes + +Don't bother trying to build a multi-test ROM, since it's not worth the +complexity. Also, tests you build won't print their name if they fail, +since that requires special arrangements. + + +Framework +--------- +Each test is in a single source file, and makes use of several library +source files from common/. This framework provides common services and +reduces code to only that which performs the actual test. Virtually all +tests include "shell.inc" at the beginning, which sets things up and +includes all the appropriate library files. + +The reset handler does minimal NES hardware initialization, clears RAM, +sets up the text console, then runs main. Main can exit by returning or +jumping to "exit" with an error code in A. Exit reports the code then +goes into an infinite loop. If the code is 0, it doesn't do anything, +otherwise it reports the code. Code 1 is reported as "Failed", and the +rest as "Error ". + +Several routines are available to print values and text to the console. +Most update a running CRC-32 checksum which can be checked with +check_crc, allowing ALL the output to be checked very easily. If the +checksum doesn't match, it is printed, so you can run the code on a NES +and paste the correct checksum into your code. + +The default is to build an iNES ROM, with other build types that I +haven't documented (devcart, sub-test of a multi-test ROM, NSF music +file). My nes.cfg file puts the code at $E000 since my devcart requires +it, and I don't want the normal ROM to differ in any way from what I've +tested. + +Library routines are organized by function into several files, each with +short documentation. Each routine may also optionally list registers +which are preserved, rather than those which are modified (trashed) as +is more commonly done. This is because it's best for the caller to +assume that ALL registers are NOT preserved unless noted. + +Some macros are used to make common operations more convenient. The left +is equivalent to the right: + + Macro Equivalent + ------------------------------------- + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + zp_byte name .zeropage + name: .res 1 + .code + + zp_res name,n .zeropage + name: .res n + .code + + bss_res name,n .bss + name: .res n + .code + + for_loop r,b,e,s calls a routine with A set to successive values + +-- +Shay Green + +Some tests might turn the screen off and on, since that affects the +behavior being tested. This does not indicate failure, and should be +ignored. Only the test result reported at the end is important. + +The error code at the end is also reported audibly with a series of +tones, in case the picture isn't visible for some reason. The code is in +binary, with a low tone indicating 0 and a high tone 1. The first tone +is always a zero, so you can tell the difference. A code of 0 means +passed, 1 means failure, and 2 or higher indicates a specific reason as +listed in the source code by the corresponding set_code line. Examples: + +low = 0 = passed +low high = 1 = failed +low high low = 2 = error 2 +low high high = 3 = error 3 + +See the source code for more information about a particular test and why +it might be failing. Each test has comments and correct output at top. +-- +Shay Green diff --git a/cpu_interrupts_v2/cpu_interrupts.nes b/cpu_interrupts_v2/cpu_interrupts.nes new file mode 100644 index 0000000..c157e56 Binary files /dev/null and b/cpu_interrupts_v2/cpu_interrupts.nes differ diff --git a/cpu_interrupts_v2/readme.txt b/cpu_interrupts_v2/readme.txt new file mode 100644 index 0000000..5df7469 --- /dev/null +++ b/cpu_interrupts_v2/readme.txt @@ -0,0 +1,251 @@ +NES CPU Interrupt Tests +----------------------- +Tests behavior and timing of CPU in the presence of interrupts, both IRQ +and NMI. + + +CLI Latency Summary +------------------- +The RTI instruction affects IRQ inhibition immediately. If an IRQ is +pending and an RTI is executed that clears the I flag, the CPU will +invoke the IRQ handler immediately after RTI finishes executing. + +The CLI, SEI, and PLP instructions effectively delay changes to the I +flag until after the next instruction. For example, if an interrupt is +pending and the I flag is currently set, executing CLI will execute the +next instruction before the CPU invokes the IRQ handler. This delay only +affects inhibition, not the value of the I flag itself; CLI followed by +PHP will leave the I flag cleared in the saved status byte on the stack +(bit 2), as expected. + + +1-cli_latency +------------- +Tests the delay in CLI taking effect, and some basic aspects of IRQ +handling and the APU frame IRQ (needed by the tests). It uses the APU's +frame IRQ and first verifies that it works well enough for the tests. + +The later tests execute CLI followed by SEI and equivalent pairs of +instructions (CLI, PLP, where the PLP sets the I flag). These should +only allow at most one invocation of the IRQ handler, even if it doesn't +acknowledge the source of the IRQ. RTI is also tested, which behaves +differently. These tests also *don't* disable interrupts after the first +IRQ, in order to test whether a pair of instructions allows only one +interrupt or causes continuous interrupts that block the main code from +continuing. + +2) RTI should not adjust return address (as RTS does) +3) APU should generate IRQ when $4017 = $00 +4) Exactly one instruction after CLI should execute before IRQ is taken +5) CLI SEI should allow only one IRQ just after SEI +6) In IRQ allowed by CLI SEI, I flag should be set in saved status flags +7) CLI PLP should allow only one IRQ just after PLP +8) PLP SEI should allow only one IRQ just after SEI +9) PLP PLP should allow only one IRQ just after PLP +10) CLI RTI should not allow any IRQs +11) Unacknowledged IRQ shouldn't let any mainline code run +12) RTI RTI shouldn't let any mainline code run + + +2-nmi_and_brk +------------- +NMI behavior when it interrupts BRK. Occasionally fails on +NES due to PPU-CPU synchronization. + +Result when run: +NMI BRK -- +27 36 00 NMI before CLC +26 36 00 NMI after CLC +26 36 00 +36 00 00 NMI interrupting BRK, with B bit set on stack +36 00 00 +36 00 00 +36 00 00 +36 00 00 +27 36 00 NMI after SEC at beginning of IRQ handler +27 36 00 + + +3-nmi_and_irq +------------- +NMI behavior when it interrupts IRQ vectoring. + +Result when run: +NMI IRQ +23 00 NMI occurs before LDA #1 +21 00 NMI occurs after LDA #1 (Z flag clear) +21 00 +20 00 NMI occurs after CLC, interrupting IRQ +20 00 +20 00 +20 00 +20 00 +20 00 +20 00 Same result for 7 clocks before IRQ is vectored +25 20 IRQ occurs, then NMI occurs after SEC in IRQ handler +25 20 + + +4-irq_and_dma +------------- +Has IRQ occur at various times around sprite DMA. +First column refers to what instruction IRQ occurred +after. Second column is time of IRQ, in CPU clocks relative +to some arbitrary starting point. + +0 +0 +1 +1 +1 +2 +2 +3 +2 +4 +4 +5 +4 +6 +7 +7 +7 +8 +7 +9 +7 +10 +8 +11 +8 +12 +8 +13 +... +8 +524 +8 +525 +8 +526 +9 +527 + + +5-branch_delays_irq +------------------- +A taken non-page-crossing branch ignores IRQ during +its last clock, so that next instruction executes +before the IRQ. Other instructions would execute the +NMI before the next instruction. + +The same occurs for NMI, though that's not tested here. + +test_jmp +T+ CK PC +00 02 04 NOP +01 01 04 +02 03 07 JMP +03 02 07 +04 01 07 +05 02 08 NOP +06 01 08 +07 03 08 JMP +08 02 08 +09 01 08 + +test_branch_not_taken +T+ CK PC +00 02 04 CLC +01 01 04 +02 02 06 BCS +03 01 06 +04 02 07 NOP +05 01 07 +06 04 0A JMP +07 03 0A +08 02 0A +09 01 0A JMP + +test_branch_taken_pagecross +T+ CK PC +00 02 0D CLC +01 01 0D +02 04 00 BCC +03 03 00 +04 02 00 +05 01 00 +06 04 03 LDA $100 +07 03 03 +08 02 03 +09 01 03 + +test_branch_taken +T+ CK PC +00 02 04 CLC +01 01 04 +02 03 07 BCC +03 02 07 +04 05 0A LDA $100 *** This is the special case +05 04 0A +06 03 0A +07 02 0A +08 01 0A +09 03 0A JMP + +Multi-tests +----------- +The NES/NSF builds in the main directory consist of multiple sub-tests. +When run, they list the subtests as they are run. The final result code +refers to the first sub-test that failed. For more information about any +failed subtests, run them individually from rom_singles/ and +nsf_singles/. + + +Flashes, clicks, other glitches +------------------------------- +If a test prints "passed", it passed, even if there were some flashes or +odd sounds. Only a test which prints "done" at the end requires that you +watch/listen while it runs in order to determine whether it passed. Such +tests involve things which the CPU cannot directly test. + + +Alternate output +---------------- +Tests generally print information on screen, but also report the final +result audibly, and output text to memory, in case the PPU doesn't work +or there isn't one, as in an NSF or a NES emulator early in development. + +After the tests are done, the final result is reported as a series of +beeps (see below). For NSF builds, any important diagnostic bytes are +also reported as beeps, before the final result. + + +Output at $6000 +--------------- +All text output is written starting at $6004, with a zero-byte +terminator at the end. As more text is written, the terminator is moved +forward, so an emulator can print the current text at any time. + +The test status is written to $6000. $80 means the test is running, $81 +means the test needs the reset button pressed, but delayed by at least +100 msec from now. $00-$7F means the test has completed and given that +result code. + +To allow an emulator to know when one of these tests is running and the +data at $6000+ is valid, as opposed to some other NES program, $DE $B0 +$G1 is written to $6001-$6003. + + +Audible output +-------------- +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason. See the source +code of the test for more information about the meaning of a test code. +They are found after the set_test macro. For example, the cause of test +code 3 would be found in a line containing set_test 3. Examples: + + Tones Binary Decimal Meaning + - - - - - - - - - - - - - - - - - - - - + low 0 0 passed + low high 01 1 failed + low high low 010 2 error 2 + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to prevent the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +-- +Shay Green diff --git a/cpu_interrupts_v2/rom_singles/1-cli_latency.nes b/cpu_interrupts_v2/rom_singles/1-cli_latency.nes new file mode 100644 index 0000000..534f1ac Binary files /dev/null and b/cpu_interrupts_v2/rom_singles/1-cli_latency.nes differ diff --git a/cpu_interrupts_v2/rom_singles/2-nmi_and_brk.nes b/cpu_interrupts_v2/rom_singles/2-nmi_and_brk.nes new file mode 100644 index 0000000..670d8f2 Binary files /dev/null and b/cpu_interrupts_v2/rom_singles/2-nmi_and_brk.nes differ diff --git a/cpu_interrupts_v2/rom_singles/3-nmi_and_irq.nes b/cpu_interrupts_v2/rom_singles/3-nmi_and_irq.nes new file mode 100644 index 0000000..5a27e7b Binary files /dev/null and b/cpu_interrupts_v2/rom_singles/3-nmi_and_irq.nes differ diff --git a/cpu_interrupts_v2/rom_singles/4-irq_and_dma.nes b/cpu_interrupts_v2/rom_singles/4-irq_and_dma.nes new file mode 100644 index 0000000..2dac739 Binary files /dev/null and b/cpu_interrupts_v2/rom_singles/4-irq_and_dma.nes differ diff --git a/cpu_interrupts_v2/rom_singles/5-branch_delays_irq.nes b/cpu_interrupts_v2/rom_singles/5-branch_delays_irq.nes new file mode 100644 index 0000000..36cde94 Binary files /dev/null and b/cpu_interrupts_v2/rom_singles/5-branch_delays_irq.nes differ diff --git a/cpu_interrupts_v2/source/1-cli_latency.s b/cpu_interrupts_v2/source/1-cli_latency.s new file mode 100644 index 0000000..7c5732d --- /dev/null +++ b/cpu_interrupts_v2/source/1-cli_latency.s @@ -0,0 +1,214 @@ +; Tests the delay in CLI taking effect, and some basic aspects of IRQ +; handling and the APU frame IRQ (needed by the tests). It uses the APU's +; frame IRQ and first verifies that it works well enough for the tests. +; +; The later tests execute CLI followed by SEI and equivalent pairs of +; instructions (CLI, PLP, where the PLP sets the I flag). These should +; only allow at most one invocation of the IRQ handler, even if it doesn't +; acknowledge the source of the IRQ. RTI is also tested, which behaves +; differently. These tests also *don't* disable interrupts after the first +; IRQ, in order to test whether a pair of instructions allows only one +; interrupt or causes continuous interrupts that block the main code from +; continuing. + +CUSTOM_IRQ=1 +.include "shell.inc" + +zp_byte irq_count +zp_byte irq_flags +zp_res irq_addr,2 +zp_byte irq_data + +begin_test: + sei + lda #0 + sta irq_count + sta irq_flags + sta irq_addr + sta irq_addr + 1 + rts + +irq: sta irq_data + pla ; save status flags and return addr from stack + sta irq_flags + pla + sta irq_addr + pla + sta irq_addr + 1 + pha ; restore return addr and status flags on stack + lda irq_addr + pha + lda irq_flags + pha + inc irq_count + bpl :+ + pla + ora #$04 ; set I flag in saved status to disable IRQ + pha +: lda irq_data + rti + +; Reports error if none or more than one interrupt occurred, +; or if return address within handler doesn't match YX. +end_test: + sei + nop + cmp irq_count + jne test_failed + cpx irq_addr + jne test_failed + cpy irq_addr + 1 + jne test_failed + rts + + +.align 256 +main: + setb SNDMODE,0 + delay_msec 40 + ; APU frame IRQ should be active by now + + set_test 2,"RTI should not adjust return address (as RTS does)" + lda #>addr2 + pha + lda #irq3 + lda #$81 + jsr end_test + + set_test 5,"CLI SEI should allow only one IRQ just after SEI" + jsr begin_test + lda #$80 ; have IRQ handler set I flag after first invocation + sta irq_count + cli + sei +irq4: + ldx #irq4 + lda #$81 + jsr end_test + + set_test 6,"In IRQ allowed by CLI SEI, I flag should be set in saved status flags" + jsr begin_test + cli + sei + nop + nop + lda irq_flags + and #$04 + jeq test_failed + + set_test 7,"CLI PLP should allow only one IRQ just after PLP" + jsr begin_test + php + cli + plp +irq5: + ldx #irq5 + lda #1 + jsr end_test + + set_test 8,"PLP SEI should allow only one IRQ just after SEI" + jsr begin_test + lda #0 + pha + plp + sei +irq6: + ldx #irq6 + lda #1 + jsr end_test + + set_test 9,"PLP PLP should allow only one IRQ just after PLP" + jsr begin_test + php + lda #0 + pha + plp + plp +irq7: + ldx #irq7 + lda #1 + jsr end_test + + set_test 10,"CLI RTI should not allow any IRQs" + jsr begin_test + lda #>rti1 + pha + lda #rti3 + pha + lda #rti2 + pha + lda #0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/cpu_interrupts_v2/source/common/delay.s b/cpu_interrupts_v2/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/cpu_interrupts_v2/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/cpu_interrupts_v2/source/common/devcart.bin b/cpu_interrupts_v2/source/common/devcart.bin new file mode 100644 index 0000000..faad421 Binary files /dev/null and b/cpu_interrupts_v2/source/common/devcart.bin differ diff --git a/cpu_interrupts_v2/source/common/macros.inc b/cpu_interrupts_v2/source/common/macros.inc new file mode 100644 index 0000000..4307c66 --- /dev/null +++ b/cpu_interrupts_v2/source/common/macros.inc @@ -0,0 +1,188 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro inxy + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro + +; Negates A and adds it to operand +.macro subaf Operand + eor #$FF + sec + adc Operand +.endmacro + +; Initializes CPU registers to reasonable values +.macro init_cpu_regs + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + .ifndef BUILD_NSF + inx + stx PPUCTRL + .endif +.endmacro diff --git a/cpu_interrupts_v2/source/common/neshw.inc b/cpu_interrupts_v2/source/common/neshw.inc new file mode 100644 index 0000000..d636a97 --- /dev/null +++ b/cpu_interrupts_v2/source/common/neshw.inc @@ -0,0 +1,56 @@ +; NES I/O locations and masks + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 + +.ifndef REGION_FREE + .ifndef PAL_ONLY + .ifndef NTSC_ONLY + NTSC_ONLY = 1 + .endif + .endif +.else + .ifdef NTSC_ONLY + .error "NTSC_ONLY and REGION_FREE defined" + .endif + .ifdef PAL_ONLY + .error "PAL_ONLY and REGION_FREE defined" + .endif +.endif + +.ifdef NTSC_ONLY + CLOCK_RATE = 1789773 + PPU_FRAMELEN = 29781 +.endif + +.ifdef PAL_ONLY + CLOCK_RATE = 1662607 + PPU_FRAMELEN = 33248 +.endif diff --git a/cpu_interrupts_v2/source/common/ppu.s b/cpu_interrupts_v2/source/common/ppu.s new file mode 100644 index 0000000..4827b3e --- /dev/null +++ b/cpu_interrupts_v2/source/common/ppu.s @@ -0,0 +1,203 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts + + +.macro check_ppu_region_ Len + ; Delays since VBL began + jsr wait_vbl_optional ; 10 average + delay Len - 18 - 200 + lda PPUSTATUS ; 4 + bmi @ok ; 2 + delay 200 + ; Next VBL should roughly begin here if it's the + ; one we are detecting + delay 200 + lda PPUSTATUS ; 2 + bpl @ok +.endmacro + +check_ppu_region: + +.ifndef REGION_FREE +.ifdef PAL_ONLY + check_ppu_region_ 29781 + print_str {newline,"Note: This test is meant for PAL NES only.",newline,newline} +.endif + +.ifdef NTSC_ONLY + check_ppu_region_ 33248 + print_str {newline,"Note: This test is meant for NTSC NES only.",newline,newline} +.endif +.endif +@ok: rts + + +; Loads ASCII font into CHR RAM and fills rest with $FF +.macro load_chr_ram + bit PPUSTATUS + setb PPUADDR,0 + setb PPUADDR,0 + + ; Copy ascii_chr to 0 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page + + ; Fill rest + lda #$FF +: sta PPUDATA + iny + bne :- + inx + cpx #$20 + bne :- +.endmacro diff --git a/cpu_interrupts_v2/source/common/print.s b/cpu_interrupts_v2/source/common/print.s new file mode 100644 index 0000000..281491f --- /dev/null +++ b/cpu_interrupts_v2/source/common/print.s @@ -0,0 +1,277 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr print_nibble_ + pla + + pha + and #$0F + jsr print_nibble_ + pla + rts + +print_nibble_: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints low 4 bits of A as single hex character +; Preserved: A, X, Y +print_nibble: + pha + and #$0F + jsr update_crc + jsr print_nibble_ + pla + rts + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + bne :+ + inc addr+1 +: rts + + +; Prints A as 1-3 digit decimal. +; In: A = MSB +; Preserved: A, X, Y +print_dec: + sta print_temp_ + pha + txa + pha + tya + pha + ldy print_temp_ + lda #0 + sta print_temp_ + tya + jmp :+ + + +; Prints 16-bit AY as 1-5 digit decimal. +; Preserved: A, X, Y +print_ay_dec: + jsr update_crc + sta print_temp_ + pha + txa + pha + tya + pha +: jsr update_crc + + ; Strip leading zeroes + ldx #6 +: dex + cmp @lsb-1,x + lda print_temp_ + sbc @msb-1,x + tya + bcc :- + bcs @non_zero + + ; Print remaining digits + +@more: ; Commit subtraction + iny + sta print_temp_ + pla + + ; Subtract +@digit: sbc @lsb,x + pha + lda print_temp_ + sbc @msb,x + bcs @more + + ; Print digit and undo subtraction + tya + jsr print_char_ + pla + adc @lsb,x +@non_zero: + sec + ldy #'0' + dex + bne @digit + + ora #'0' + jsr print_char_ + + pla + tay + pla + tax + pla + rts + +@lsb: .byte 0,<10,<100,<1000,<10000 +@msb: .byte 0,>10,>100,>1000,>10000 + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/cpu_interrupts_v2/source/common/shell.inc b/cpu_interrupts_v2/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/cpu_interrupts_v2/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/cpu_interrupts_v2/source/common/shell.s b/cpu_interrupts_v2/source/common/shell.s new file mode 100644 index 0000000..0d4b396 --- /dev/null +++ b/cpu_interrupts_v2/source/common/shell.s @@ -0,0 +1,182 @@ +; Shell that sets up testing framework and calls main + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +; Move code from $C000 to $E200, to accommodate my devcarts +.ifndef LARGER_ROM_HACK +.segment "CODE" + .res $2200 +.endif + +; Put shell code after user code, so user code is in more +; consistent environment +.segment "CODE2" + + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + init_cpu_regs + + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell without affecting current set_test values +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + ; Clear APU registers + lda #0 + sta $4015 + ldx #$13 +: sta $4000,x + dex + bpl :- + + ; CPU registers + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sta temp + init_cpu_regs + setb SNDCHN,0 + lda temp + + jsr report_result + pha + jsr check_ppu_region + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +.include "shell_misc.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts +.endif diff --git a/cpu_interrupts_v2/source/common/shell_misc.s b/cpu_interrupts_v2/source/common/shell_misc.s new file mode 100644 index 0000000..4b34de8 --- /dev/null +++ b/cpu_interrupts_v2/source/common/shell_misc.s @@ -0,0 +1,212 @@ +; Reports internal error and exits program +internal_error: + init_cpu_regs + print_str newline,"Internal error" + lda #255 + jmp exit + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +.macro fill_ram_ Begin, End + ; Simpler to count from negative size up to 0, + ; and adjust address downward to compensate + ; for initial low byte in Y index + .local Neg_size + Neg_size = (Begin) - (End) + ldxy #(Begin) - 4 + delay (n)/3-1 + .endif + delay 29781*4 + .elseif (n) .MOD 3 = 2 + delay (n)/3 + delay 29781*2 + .else + delay (n)/3 + .endif +.endmacro diff --git a/cpu_interrupts_v2/source/common/testing.s b/cpu_interrupts_v2/source/common/testing.s new file mode 100644 index 0000000..6bb852a --- /dev/null +++ b/cpu_interrupts_v2/source/common/testing.s @@ -0,0 +1,105 @@ +; Utilities for writing test ROMs + +; In NVRAM so these can be used before initializing runtime, +; then runtime initialized without clearing them +nv_res test_code ; code of current test +nv_res test_name,2 ; address of name of current test, or 0 of none + + +; Sets current test code and optional name. Also resets +; checksum. +; Preserved: A, X, Y +.macro set_test code,name + pha + lda #code + jsr set_test_ + .ifblank name + setb test_name+1,0 + .else + .local Addr + setw test_name,Addr + seg_data "RODATA",{Addr: .byte name,0} + .endif + pla +.endmacro + +set_test_: + sta test_code + jmp reset_crc + + +; Initializes testing module +init_testing = init_crc + + +; Reports that all tests passed +tests_passed: + jsr print_filename + print_str newline,"Passed" + lda #0 + jmp exit + + +; Reports "Done" if set_test has never been used, +; "Passed" if set_test 0 was last used, or +; failure if set_test n was last used. +tests_done: + ldx test_code + jeq tests_passed + inx + bne test_failed + jsr print_filename + print_str newline,"Done" + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + ldx test_code + + ; Treat $FF as 1, in case it wasn't ever set + inx + bne :+ + inx + stx test_code +: + ; If code >= 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/cpu_interrupts_v2/source/common/text_out.s b/cpu_interrupts_v2/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/cpu_interrupts_v2/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +The first time a line of text is completed, the console initializes the +PPU. Most print routines update a running CRC-32 checksum which can be +checked with check_crc. This allows ALL the output to be checked very +easily. If the checksum doesn't match, it is printed, so during +development the code can be run on a NES to get the correct checksum, +which is then typed into the test code. The checksum is also useful when +comparing different outputs; rather than having to examine all the +output, one need only compare checksums. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported by the included code, due to +their complexity. + + +Macros +------ +Some macros are used to make common operations more convenient, defined +in common/macros.inc. The left is equivalent to the right: + + Macro Equivalent + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + setb addr,byte lda #byte + sta addr + + setw addr,word lda #word + sta addr+1 + + ldxy #value ldx #>value + ldy # diff --git a/cpu_reset/ram_after_reset.nes b/cpu_reset/ram_after_reset.nes new file mode 100644 index 0000000..cb223a0 Binary files /dev/null and b/cpu_reset/ram_after_reset.nes differ diff --git a/cpu_reset/readme.txt b/cpu_reset/readme.txt new file mode 100644 index 0000000..e8f9ebc --- /dev/null +++ b/cpu_reset/readme.txt @@ -0,0 +1,93 @@ +CPU Power/Reset Tests +--------------------- +Verifies CPU register values at power, and changes that occur during +reset. Also verifies that RAM isn't modified during reset. + + +Expected behavior +----------------- +At power: + A, X, Y = 0 + P = $34 + S = $FD + +After reset: + A, X, Y unchanged + I flag set (P ORed with $04) + S decremented by 3, but nothing written to stack + + +Multi-tests +----------- +The NES/NSF builds in the main directory consist of multiple sub-tests. +When run, they list the subtests as they are run. The final result code +refers to the first sub-test that failed. For more information about any +failed subtests, run them individually from rom_singles/ and +nsf_singles/. + + +Flashes, clicks, other glitches +------------------------------- +If a test prints "passed", it passed, even if there were some flashes or +odd sounds. Only a test which prints "done" at the end requires that you +watch/listen while it runs in order to determine whether it passed. Such +tests involve things which the CPU cannot directly test. + + +Alternate output +---------------- +Tests generally print information on screen, but also report the final +result audibly, and output text to memory, in case the PPU doesn't work +or there isn't one, as in an NSF or a NES emulator early in development. + +After the tests are done, the final result is reported as a series of +beeps (see below). For NSF builds, any important diagnostic bytes are +also reported as beeps, before the final result. + + +Output at $6000 +--------------- +All text output is written starting at $6004, with a zero-byte +terminator at the end. As more text is written, the terminator is moved +forward, so an emulator can print the current text at any time. + +The test status is written to $6000. $80 means the test is running, $81 +means the test needs the reset button pressed, but delayed by at least +100 msec from now. $00-$7F means the test has completed and given that +result code. + +To allow an emulator to know when one of these tests is running and the +data at $6000+ is valid, as opposed to some other NES program, $DE $B0 +$G1 is written to $6001-$6003. + + +Audible output +-------------- +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason. See the source +code of the test for more information about the meaning of a test code. +They are found after the set_test macro. For example, the cause of test +code 3 would be found in a line containing set_test 3. Examples: + + Tones Binary Decimal Meaning + - - - - - - - - - - - - - - - - - - - - + low 0 0 passed + low high 01 1 failed + low high low 010 2 error 2 + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to prevent the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +-- +Shay Green diff --git a/cpu_reset/registers.nes b/cpu_reset/registers.nes new file mode 100644 index 0000000..5408f76 Binary files /dev/null and b/cpu_reset/registers.nes differ diff --git a/cpu_reset/source/common/ascii.chr b/cpu_reset/source/common/ascii.chr new file mode 100644 index 0000000..2c5b26b Binary files /dev/null and b/cpu_reset/source/common/ascii.chr differ diff --git a/cpu_reset/source/common/build_rom.s b/cpu_reset/source/common/build_rom.s new file mode 100644 index 0000000..f3d50cd --- /dev/null +++ b/cpu_reset/source/common/build_rom.s @@ -0,0 +1,93 @@ +; Builds program as iNES ROM + +; Default is 32K PRG and 8K CHR ROM, NROM (0) + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM=1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM=1 ; Use mapper that supports 8K WRAM in cart +CUSTOM_MAPPER=n ; Specify mapper number +.endif + +.ifndef CUSTOM_MAPPER + .ifdef CART_WRAM + CUSTOM_MAPPER = 2 ; UNROM + .else + CUSTOM_MAPPER = 0 ; NROM + .endif +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte $4E,$45,$53,26 ; "NES" EOF + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte CUSTOM_MAPPER*$10+$01 ; vertical mirroring +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word -1,-1,-1, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .define CHARS "CHARS_PRG" + .segment CHARS + ascii_chr: + + .segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: +.else + .define CHARS "CHARS" + .segment "CHARS_ASCII" + .align $200 + .incbin "ascii.chr" + .res $1800 +.endif + +.segment CHARS + .res $10,0 + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +; Move code to $C000 +.segment "CODE" + .res $4000 + +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + load_ascii_chr + .endif + rts + +post_exit: + jsr set_final_result + jsr play_hex + jmp forever + +; This helps devcart recover after running test. +; It is never executed by test ROM. +.segment "LOADER" + .incbin "devcart.bin" + +.code +.align 256 diff --git a/cpu_reset/source/common/console.s b/cpu_reset/source/common/console.s new file mode 100644 index 0000000..036553c --- /dev/null +++ b/cpu_reset/source/common/console.s @@ -0,0 +1,331 @@ +; Scrolling text console with line wrapping, 30x29 characters. +; Buffers lines for speed. Will work even if PPU doesn't +; support scrolling (until text reaches bottom). Keeps border +; along bottom in case TV cuts it off. +; +; Defers most initialization until first newline, at which +; point it clears nametable and makes palette non-black. +; +; ** ASCII font must already be in CHR, and mirroring +; must be vertical or single-screen. + +.ifndef CONSOLE_COLOR + CONSOLE_COLOR = $30 ; white +.endif + +console_screen_width = 32 ; if lower than 32, left-justifies + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs. OK if either/both are 0. +console_left_margin = 1 +console_right_margin = 1 + +console_width = console_screen_width - console_left_margin - console_right_margin + +zp_byte console_pos ; 0 to console_width +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_width + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + + setb console_pos,0 + rts + + +; Hides console by disabling PPU rendering and blacking out +; first four entries of palette. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +; Shows console display +; Preserved: X, Y +console_show: + pha + lda #CONSOLE_COLOR + jsr console_show_custom_color_ + pla + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + sty console_temp + + ldy console_pos + cpy #console_width + beq console_full_ + sta console_buf,y + iny + sty console_pos + + ldy console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_scroll_up_ + setb console_pos,0 + pla + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_apply_scroll_ + pla + rts + + +;**** Internal routines **** + +console_full_: + ldy console_temp + + ; Line is full + + ; If space, treat as newline + cmp #' ' + beq console_newline + + ; Wrap current line at appropriate point + pha + tya + pha + jsr console_wrap_ + pla + tay + pla + + jmp console_print + + +; Inserts newline into buffer at appropriate position, leaving +; next line ready in buffer +; Preserved: X, console_temp +console_wrap_: + ; Find beginning of last word + ldy #console_width + lda #' ' +: dey + bmi console_newline + cmp console_buf,y + bne :- + + ; y = 0 to console_width-1 + + ; Flush through current word and put remaining + ; in buffer for next line + jsr console_wait_vbl_ + + ; Time to last PPU write: 207 + 32*(26 + 10) + + lda console_scroll + jsr console_set_ppuaddr_ + + stx console_pos ; save X + + ldx #0 + + ; Print everything before last word +: lda console_buf,x + sta PPUDATA + inx + dey + bpl :- + + ; x = 1 to console_width + + ; Move last word to beginning of buffer, and + ; print spaces for rest of line + ldy #0 + beq :++ +: lda #' ' + sta PPUDATA + lda console_buf,x + inx + sta console_buf,y + iny +: cpx #console_width + bne :-- + + ldx console_pos ; restore X + + ; Append new text after that + sty console_pos + + ; FALL THROUGH + + +; Scrolls up 8 pixels and clears one line BELOW new line +; Preserved: X, console_temp +console_scroll_up_: + ; Scroll up 8 pixels + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + + ; Clear line AFTER that on screen + jsr console_add_8_to_scroll_ + jsr console_set_ppuaddr_ + + ldy #console_width + lda #' ' +: sta PPUDATA + dey + bne :- + ; FALL THROUGH + + +; Applies current scrolling position to PPU +; Preserved: X, Y, console_temp +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + rts + + +; Sets PPU address for row +; In: A = scroll position +; Preserved: X, Y +console_set_ppuaddr_: + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + ora #console_left_margin + sta PPUADDR + rts + + +; A = (A + 8) % 240 +; Preserved: X, Y +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_show_custom_color_: + pha + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + pla + jsr console_load_palette_ + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + rts + + +; Initializes PPU if necessary, then waits for VBL +; Preserved: A, X, Y, console_temp +console_wait_vbl_: + lda console_scroll + cmp #-1 + bne @already_initialized + + ; Deferred initialization of PPU until first use of console + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + tya + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldy #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dey + bne :- + + ; Clear attributes + lda #0 + ldy #$40 +: sta PPUDATA + dey + bne :- + + pla + tay + + jsr console_show +@already_initialized: + jmp wait_vbl_optional + + +; Flushes current line +; Preserved: X, Y +console_flush_: + lda console_scroll + jsr console_set_ppuaddr_ + + sty console_temp + + ; Copy line + ldy #0 + beq :++ +: lda console_buf,y + sta PPUDATA + iny +: cpy console_pos + bne :-- + + ldy console_temp + rts diff --git a/cpu_reset/source/common/crc.s b/cpu_reset/source/common/crc.s new file mode 100644 index 0000000..de96c2a --- /dev/null +++ b/cpu_reset/source/common/crc.s @@ -0,0 +1,118 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on + + +; EQ if checksum matches CRC +; Out: A=0 and EQ if match, A>0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/cpu_reset/source/common/delay.s b/cpu_reset/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/cpu_reset/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/cpu_reset/source/common/devcart.bin b/cpu_reset/source/common/devcart.bin new file mode 100644 index 0000000..684fbb9 Binary files /dev/null and b/cpu_reset/source/common/devcart.bin differ diff --git a/cpu_reset/source/common/macros.inc b/cpu_reset/source/common/macros.inc new file mode 100644 index 0000000..d4cb72e --- /dev/null +++ b/cpu_reset/source/common/macros.inc @@ -0,0 +1,169 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro inxy + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro diff --git a/cpu_reset/source/common/neshw.inc b/cpu_reset/source/common/neshw.inc new file mode 100644 index 0000000..814d772 --- /dev/null +++ b/cpu_reset/source/common/neshw.inc @@ -0,0 +1,43 @@ +; NES I/O locations and masks + +; Clocks per second +.ifndef CLOCK_RATE + CLOCK_RATE = 1789773 ; NTSC +; CLOCK_RATE = 1662607 ; PAL +.endif + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 + +.if CLOCK_RATE = 1789773 + PPU_FRAMELEN = 29781 +.elseif CLOCK_RATE = 1662607 + PPU_FRAMELEN = 33248 +.endif diff --git a/cpu_reset/source/common/ppu.s b/cpu_reset/source/common/ppu.s new file mode 100644 index 0000000..21cad12 --- /dev/null +++ b/cpu_reset/source/common/ppu.s @@ -0,0 +1,142 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts diff --git a/cpu_reset/source/common/print.s b/cpu_reset/source/common/print.s new file mode 100644 index 0000000..29fbc23 --- /dev/null +++ b/cpu_reset/source/common/print.s @@ -0,0 +1,235 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr @nibble + pla + + pha + and #$0F + jsr @nibble + pla + rts + +@nibble: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: A, X, Y +print_dec: + pha + sta print_temp_ + txa + pha + lda print_temp_ + + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jsr print_char + pla + tax + pla + rts + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/cpu_reset/source/common/run_at_reset.s b/cpu_reset/source/common/run_at_reset.s new file mode 100644 index 0000000..aeb09f1 --- /dev/null +++ b/cpu_reset/source/common/run_at_reset.s @@ -0,0 +1,64 @@ +; Keeps track of number of times reset, and prompts user. + +power_flag_value = $42 + +nv_res power_flag_ +nv_res num_resets_ + +; Out: A = number of times NES has been reset since turned on +; Preserved: X, Y +num_resets: + lda power_flag_ + cmp #power_flag_value + bne :+ + lda num_resets_ + rts +: lda #0 + rts + + +; Prompts user to press reset after message disappears, +; then hides message, increments reset count, and asks +; emulator to reset NES. +; Preserved: X, Y +prompt_to_reset: + print_str {newline,newline,"Press reset AFTER this message",newline,"disappears"} + + ; Add "again" if this isn't first requested reset + jsr num_resets + beq :+ + print_str ", again" +: + ; Show for a few seconds + print_str {newline,newline,newline} + jsr console_show + delay_msec 1000 + delay_msec 1000 + + jsr inc_reset_count + + ; Tell emulator that NES should be reset now + lda #$81 + jsr set_final_result + + jsr console_hide + rts + + +; Increments reset count and marks it as valid +; Preserved: X, Y +inc_reset_count: + jsr num_resets + clc + adc #1 + bcc :+ + lda #$FF ; don't wrap around +: sta num_resets_ + setb power_flag_,power_flag_value + rts + + +; Waits in infinite loop for reset +; Preserved: A, X, Y, flags +wait_reset: + jmp wait_reset diff --git a/cpu_reset/source/common/shell.inc b/cpu_reset/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/cpu_reset/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/cpu_reset/source/common/shell.s b/cpu_reset/source/common/shell.s new file mode 100644 index 0000000..358cddf --- /dev/null +++ b/cpu_reset/source/common/shell.s @@ -0,0 +1,382 @@ +; Common routines and runtime + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +; Move code to $E200 ($200 bytes for text output in devcarts +; where WRAM is mirrored to $E000) +.segment "CODE" + .res $2200 + +; Put shell code after user code, so user code is in more +; consistent environment +.segment "CODE2" + + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts +.endif + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sei + cld + ldx #$FF + txs + + ldx #0 + stx SNDCHN + .ifndef BUILD_NSF + stx PPUCTRL + .endif + + jsr report_result + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +.macro fill_ram_ Begin, End + .local Neg_size + Neg_size = (Begin) - (End) + ldxy #(Begin) - ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page +.endmacro + +; Disables interrupts and loops forever +.ifndef CUSTOM_FOREVER +forever: + sei + lda #0 + sta PPUCTRL +: beq :- + .res $10,$EA ; room for code to run loader +.endif + + +; Default NMI +.ifndef CUSTOM_NMI + zp_byte nmi_count + + nmi: + inc nmi_count + rti + + ; Waits for NMI. Must be using NMI handler that increments + ; nmi_count, with NMI enabled. + ; Preserved: X, Y + wait_nmi: + lda nmi_count + : cmp nmi_count + beq :- + rts +.endif + + +; Default IRQ +.ifndef CUSTOM_IRQ + irq: + bit SNDCHN ; clear APU IRQ flag + rti +.endif + +.endif + + +; Reports A in binary as high and low tones, with +; leading low tone for reference. Omits leading +; zeroes. Doesn't hang if no APU is present. +; Preserved: A, X, Y +play_hex: + pha + + ; Make low reference beep + clc + jsr @beep + + ; Remove high zero bits + sec +: rol a + bcc :- + + ; Play remaining bits + beq @zero +: jsr @beep + asl a + bne :- +@zero: + + delay_msec 300 + pla + rts + +; Plays low/high beep based on carry +; Preserved: A, X, Y +@beep: + pha + + ; Set up square + lda #1 + sta SNDCHN + sta $4001 + sta $4003 + adc #$FE ; period=$100 if carry, $1FF if none + sta $4002 + + ; Fade volume + lda #$0F +: ora #$30 + sta $4000 + delay_msec 8 + sec + sbc #$31 + bpl :- + + ; Silence + sta SNDCHN + delay_msec 160 + + pla + rts diff --git a/cpu_reset/source/common/testing.s b/cpu_reset/source/common/testing.s new file mode 100644 index 0000000..ba41f03 --- /dev/null +++ b/cpu_reset/source/common/testing.s @@ -0,0 +1,106 @@ +; Utilities for writing test ROMs + +; In NVRAM so these can be used before initializing runtime, +; then runtime initialized without clearing them +nv_res test_code ; code of current test +nv_res test_name,2 ; address of name of current test, or 0 of none + + +; Sets current test code and optional name. Also resets +; checksum. +; Preserved: A, X, Y +.macro set_test code,name + pha + lda #code + jsr set_test_ + .ifblank name + setb test_name+1,0 + .else + .local Addr + setw test_name,Addr + seg_data "RODATA",{Addr: .byte name,0} + .endif + pla +.endmacro + +set_test_: + sta test_code + jmp reset_crc + + +; Initializes testing module +init_testing: + jmp init_crc + + +; Reports that all tests passed +tests_passed: + jsr print_filename + print_str newline,"Passed" + lda #0 + jmp exit + + +; Reports "Done" if set_test has never been used, +; "Passed" if set_test 0 was last used, or +; failure if set_test n was last used. +tests_done: + ldx test_code + jeq tests_passed + inx + bne test_failed + jsr print_filename + print_str newline,"Done" + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + ldx test_code + + ; Treat $FF as 1, in case it wasn't ever set + inx + bne :+ + inx + stx test_code +: + ; If code >= 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/cpu_reset/source/common/text_out.s b/cpu_reset/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/cpu_reset/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +The first time a line of text is completed, the console initializes the +PPU. Most print routines update a running CRC-32 checksum which can be +checked with check_crc. This allows ALL the output to be checked very +easily. If the checksum doesn't match, it is printed, so during +development the code can be run on a NES to get the correct checksum, +which is then typed into the test code. The checksum is also useful when +comparing different outputs; rather than having to examine all the +output, one need only compare checksums. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported by the included code, due to +their complexity. + + +Macros +------ +Some macros are used to make common operations more convenient, defined +in common/macros.inc. The left is equivalent to the right: + + Macro Equivalent + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + setb addr,byte lda #byte + sta addr + + setw addr,word lda #word + sta addr+1 + + ldxy #value ldx #>value + ldy # diff --git a/cpu_reset/source/registers.s b/cpu_reset/source/registers.s new file mode 100644 index 0000000..73f748e --- /dev/null +++ b/cpu_reset/source/registers.s @@ -0,0 +1,85 @@ +; At power, A,X,Y=0 P=$34 S=$FD +; At reset, I flag set, S decreased by 3, no other change + +CUSTOM_RESET=1 +.include "shell.inc" +.include "run_at_reset.s" + +nv_res log,8 + +reset: ; Save current registers and stack + php + sta log+0 + stx log+1 + sty log+2 + pla + sta log+3 + tsx + stx log+4 + lda $112 + sta log+5 + lda $111 + sta log+6 + lda $110 + sta log+7 + + jmp std_reset + +main: jsr num_resets + beq power + + set_test 3,"Reset should set I flag, subtract 3 from S, nothing more" + jsr print_log + check_crc $717C409F + + set_test 4,"Reset shouldn't write to stack" + lda log+5 + cmp #$FB + jne test_failed + lda log+6 + cmp #$9A + jne test_failed + lda log+7 + cmp #$BC + jne test_failed + + jmp tests_passed + +power: set_test 2,"At power A,X,Y=0 P=$34 S=$FD" + jsr print_log + check_crc $EAE4AAFA + + jsr prompt_to_reset + + ; Clear interrupt sources + setb SNDCHN,0 + setb SNDMODE,$C0 + setb PPUCTRL,0 + bit SNDCHN + + ; Set initial stack bytes and registers + setb $111,$9A + setb $110,$BC + ldx #$12 + txs + lda #$FB + pha + lda #$34 + ldx #$56 + ldy #$78 + plp + + jmp wait_reset + +print_log: + print_str "A X Y P S",newline + ldx #0 +: lda log,x + jsr print_a + inx + cpx #5 + bne :- + + jsr print_newline + + rts diff --git a/cpu_timing_test6/cpu_timing_test.nes b/cpu_timing_test6/cpu_timing_test.nes new file mode 100644 index 0000000..e20bca4 Binary files /dev/null and b/cpu_timing_test6/cpu_timing_test.nes differ diff --git a/cpu_timing_test6/readme.txt b/cpu_timing_test6/readme.txt new file mode 100644 index 0000000..e73de19 --- /dev/null +++ b/cpu_timing_test6/readme.txt @@ -0,0 +1,147 @@ +NES 6502 Timing Test +-------------------- +This program tests instruction timing for all official and unofficial +NES 6502 instructions except the 8 branch instructions (Bxx) and the 12 +halt instructions (HLT). It tests normal and page crossing cases of all +instructions (including instructions that should not take longer due to +a page crossing). It passes when run on an NTSC NES (it will not work on +a PAL NES due to the differing refresh rate). + +The test takes up to 16 seconds to complete. If everything passes, it +prints a message and beeps twice. If it fails, it prints an error +message and beeps once. + +The test can be restricted to only official instructions or test some or +all of the unofficial instructions, in case your emulator doesn't +emulate all the unofficial instructions. Select between these by holding +the following buttons on controller #1 when starting the test: + +(nothing) Official instructions only +B Official + all unofficial instructions +A Official + $EB (equivalent to $E9) + unofficial NOPs: + +1-byte NOPs: $1A $3A $5A $7A $DA $FA +2-byte NOPs: $04 $14 $34 $44 $54 $64 $74 $80 $82 $89 $C2 $D4 $E2 $F4 +3-byte NOPs: $0C $1C $3C $5C $7C $DC $FC + +The 12 halt instructions are never tested, as they freeze the NES and +require a reset: + +HLT: $02 $12 $22 $32 $42 $52 $62 $72 $92 $B2 $D2 $F2 + +The 8 branch instructions aren't tested since they have more subtle +page-crossing behavior. Use my branch_timing_tests for these. + +Source code is included. Support code is also included, but it runs on a +custom devcart and assembler so it will require some effort to assemble. +Contact me if you'd like assistance porting them to your setup. I really +do plan on making my source work with ca65 eventually. + + +Errors +------ +All instructions are first tested without a page crossing, then with a +page crossing, allowing you to more easily debug timing problems. + +FAIL OP: The indicated opcode failed. If it was being timed where a page +crossing should occur, that will be noted. The number of clocks will be +shown that the emulator used, and the correct number of clocks it should +have used. + +UNKNOWN ERROR: Occurs if the instruction timing fails or NMI +unexpectedly returns. Prints the opcode and a hex value. Post to the +Nesdev forum if you get this error. + +BASIC TIMING WRONG: If you get this error, then the loop that tests NMI +and basic instruction timing (below) ran too many/too few times. If this +occurs, verify the timing of the following instructions and your PPU's +NMI interrupt timing. + +loop: + cpx zero-page + bne stop + inc zero-page + bne loop + inc zero-page + jmp loop +stop: + + +How the tests work +------------------ +All instructions are tested using a common framework which runs the +instruction in an infinite loop. Once the loop is eventually interrupted +by NMI, the number of times the loop ran is cross-referenced with a +table to determine how many clocks the instruction used. For normal +timing, instructions which use some form of indexed addressing reference +address $0xFD and X and Y are set to 2, which is just shy of a page +cross ($FD+2=$FF). To test page crossing timing, X and Y are set to 3, +causing a page crossing for relevant instructions ($FD+3=$100). Not all +instructions add an extra clock when a page is crossed, so this test +reveals missing and extra page crossing penalties. + +Some instructions require special handling. JMP and JSR are tested by +jumping to the next instruction. RTS and RTI are handled by filling the +stack with the value $02 and having the next instruction of the loop be +at address $0202 (for RTI) or $0203 (for RTS, since it adds one to the +return address). BRK is handled by setting the IRQ vector to $0202, the +address of the next instruction in the loop after BRK. The trickiness of +these special cases might reveal non-timing problems in an emulator. + + +Instruction timing +------------------ +The following unofficial instructions have an extra clock added for page +crossing and use the indicated addressing mode: + +Absolute, X: $1C $3C $5C $7C $DC $FC +Absolute, Y: $BB $BF +Indirect, Y: $B3 + +These are the tables the test uses. Since it passes on a NES, the values +are pretty much guaranteed correct. + +No page crossing: + + 0 1 2 3 4 5 6 7 8 9 A B C D E F + -------------------------------- + 7,6,0,8,3,3,5,5,3,2,2,2,4,4,6,6 | 0 + 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 | 1 + 6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6 | 2 + 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 | 3 + 6,6,0,8,3,3,5,5,3,2,2,2,3,4,6,6 | 4 + 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 | 5 + 6,6,0,8,3,3,5,5,4,2,2,2,5,4,6,6 | 6 + 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 | 7 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 | 8 + 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 | 9 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 | A + 0,5,0,5,4,4,4,4,2,4,2,4,4,4,4,4 | B + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 | C + 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 | D + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 | E + 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 | F + +Page crossing: + + 0 1 2 3 4 5 6 7 8 9 A B C D E F + -------------------------------- + 7,6,0,8,3,3,5,5,3,2,2,2,4,4,6,6 | 0 + 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 | 1 + 6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6 | 2 + 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 | 3 + 6,6,0,8,3,3,5,5,3,2,2,2,3,4,6,6 | 4 + 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 | 5 + 6,6,0,8,3,3,5,5,4,2,2,2,5,4,6,6 | 6 + 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 | 7 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 | 8 + 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 | 9 + 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 | A + 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 | B + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 | C + 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 | D + 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 | E + 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 | F + +-- +Shay Green diff --git a/cpu_timing_test6/source/console.a b/cpu_timing_test6/source/console.a new file mode 100644 index 0000000..38cdc5b --- /dev/null +++ b/cpu_timing_test6/source/console.a @@ -0,0 +1,108 @@ +; Simple text console using NES PPU. + +console_pos = $7f2 +console_pos_h = $7f3 + +; Print char A to console +; Preserved: A, X, Y +print_char: + jsr wait_vbl ; wait for safe access +print_char_no_wait: + pha + lda console_pos_h + sta $2006 + inc console_pos + lda console_pos + sta $2006 + lda #0 ; restore scroll + sta $2005 + sta $2005 + pla + sta $2007 + rts + .code + +; Go to next line +; Preserved: A, X, Y +console_newline: + pha + lda console_pos + and #$e0 + clc + adc #$21 + sta console_pos + lda console_pos_h + adc #0 + sta console_pos_h + pla + rts + .code + +; Initialize console +init_console: + lda #$81 + sta console_pos + lda #$20 + sta console_pos_h + + jsr wait_vbl ; init ppu + lda #0 + sta $2000 + sta $2001 + + lda #$3f ; load palette + jsr set_vpage + lda #15 ; bg + ldx #48 ; fg + ldy #8 +pal: sta $2007 + stx $2007 + stx $2007 + stx $2007 + dey + bne pal + + lda #$02 ; load tiles + jsr set_vpage + lda #chr_data.lsb + sta <$f0 + lda #chr_data.msb + sta <$f1 + ldy #0 + lda #59 ; 59 chars in data + sta <$f2 +chr_loop: + ldx #8 + lda #0 +: sta $2007 + dex + bne - + + ldx #8 +: lda ($f0),y + iny + sta $2007 + dex + bne - + + tya + bne + + inc <$f1 +: dec <$f2 + bne chr_loop + + lda #32 + jsr fill_nametable + + jsr wait_vbl ; enable ppu + lda #0 + sta $2005 + sta $2005 + lda #$0a + sta $2001 + rts + .code + +chr_data: + .incbin "chr.bin" + diff --git a/cpu_timing_test6/source/cpu_timing_test.asm b/cpu_timing_test6/source/cpu_timing_test.asm new file mode 100644 index 0000000..f5fd555 --- /dev/null +++ b/cpu_timing_test6/source/cpu_timing_test.asm @@ -0,0 +1,471 @@ +; Times all official and unofficial 6502 instructions except the +; 8 branch instructions (Bxx) and the 12 halt instructions (HLT). +; See readme.txt for operation and overview of algorithm used. + +counter = $10 ; number of iterations of test loop +nmi_flag = $12 ; tells NMI what to do +test_index = $13 ; opcode being tested +test_mode = $14 ; 0 = normal, $FE = page crossing +type_mask = $15 ; which subset of instructions to test +test_code = $200 ; test loop is copied here +test_code_page = $02 + +.org $202 + irq: + .code + +reset: + ; Standard NES init + sei + lda #0 + sta $2000 + sta $2001 + jsr wait_vbl + jsr wait_vbl + cld + + jsr init_console + lda #$A5 ; tell runtime that console is inited + sta $7F1 + + ; Print title + ldx #title_str.msb + ldy #title_str.lsb + jsr print_str + jsr console_newline + jsr console_newline + + ; Poll joypad to determine which tests to run + lda #1 + sta $4016 + lda #0 + sta $4016 + lda $4016 ; A button status + and #$01 + beq + + ldx #official_nop_str.msb + ldy #official_nop_str.lsb + lda #$70 + jmp set_type +: lda $4016 ; B button status + and #$01 + beq + + ldx #all_instrs_str.msb + ldy #all_instrs_str.lsb + lda #$30 + jmp set_type +: lda #$F0 + ldx #official_str.msb + ldy #official_str.lsb +set_type: + sta type_mask + jsr print_str + jsr console_newline + jsr console_newline + + ; Disable APU IRQ + lda #$C0 + sta $4017 + + ; Start NMI + lda #0 + sta nmi_flag + lda #$80 + sta $2000 + + ; Be sure basic NMI timing is correct + lda #$F7 + sta counter + 1 + lda #$18 + sta counter + ldx #255 + stx nmi_flag +: cpx nmi_flag + beq - + dex +: cpx nmi_flag + bne check_nmi_time + inc counter + bne - + inc counter + 1 + jmp - +check_nmi_time: + lda counter + 1 + bne nmi_time_wrong + lda counter + cmp #12 + bcs nmi_time_wrong + + ; Begin tests + lda #2 + sta test_mode + ldx #0 + jmp test_loop + +nmi_time_wrong: + lda #0 + sta $2000 + ldx #nmi_time_str.msb + ldy #nmi_time_str.lsb + jsr print_str +error_beep_exit: + lda #1 + jsr debug_beeps + jmp forever + .code + + ; Print current opcode +print_opcode: + ldx #error_str.msb + ldy #error_str.lsb + jsr print_str + lda test_index + jsr print_a + lda test_mode + cmp #2 + beq + + ldx #cross_str.msb + ldy #cross_str.lsb + jsr print_str +: jsr console_newline + jsr console_newline + rts + .code + + ; NMI handler +: rti +nmi: dec nmi_flag + bmi - + beq check_time + + ; Restore overwritten stack values to $02 + pla + pla + pla + lda #$02 + pha + pha + pha + + ; Run test loop until next NMI interrupts it + lda test_mode + tay + tax + jmp test_code + +error: + lda #0 + sta $2000 + jsr print_opcode + ldx #unknown_str.msb + ldy #unknown_str.lsb + jsr print_str + jsr console_newline + jsr console_newline + lda counter + 1 + ldy counter + jsr print_ay + jmp error_beep_exit + +clock_table: ; doubled entries are to subtract value greater than $FF + .db $A5, $A5, $87, $86, $E1, $BE, $A2, $8D, $7C, $6D, $08 +clock_times: + .db $01, $00, $02, $00, $03, $04, $05, $06, $07, $08, $09 + + ; Calculate number of clocks instruction took +check_time: + cld ; avoid buggy NES emulator breaking test + lda counter + ldx counter + 1 + ldy #11 +clock_loop: + dey + bmi error + sec + sbc clock_table,y + bcs no_carry + dex +no_carry: + cpx #0 + bmi error + bne clock_loop + cmp #12 + bcs clock_loop + + lda clock_times,y + beq error + + ; Compare result with correct timing + ldx test_index + ldy test_mode + cpy #2 + bne + + cmp instr_times,x + beq next_instr + ldy instr_times,x + jmp print_error +: cmp instr_times_cross,x + beq next_instr + ldy instr_times_cross,x + + ; Print measured and correct times +print_error: + sta <2 + sty <3 + lda #0 + sta $2000 + + jsr print_opcode + + ldx #time_str.msb + ldy #time_str.lsb + jsr print_str + lda <2 + ora #$30 + jsr print_char + jsr console_newline + jsr console_newline + + ldx #correct_str.msb + ldy #correct_str.lsb + jsr print_str + lda <3 + ora #$30 + jsr print_char + jmp error_beep_exit + + ; Report success +done: lda #0 + sta $2000 + ldx #passed_str.msb + ldy #passed_str.lsb + jsr print_str + lda #2 + jsr debug_beeps + jmp forever + +next_instr: + ; Next instruction +: inx + bne test_loop + + ; Toggle between normal and page crossing + lda test_mode + eor #1 + sta test_mode + cmp #2 + beq done +test_loop: + lda instr_types,x + and type_mask ; skip instructions not being tested + bne - + stx test_index + + ; Copy test_code to RAM + lda instr_types,x ; get instr length + and #$07 + tay + lda test_addrs_lsb - 1,y + sta <0 + lda test_addrs_msb - 1,y + sta <1 + ldy #0 + txa +: sta test_code,y + iny + lda (0),y + cpy #16 + bne - + + ; Special case for JSR and JMP + cpx #$20 + beq jsr_jmp + cpx #$4C + bne + +jsr_jmp: + lda #$02 ; change address to $0203 + sta test_code + 2 + lda #$03 + sta test_code + 1 +: + ; Set zero-page values so the addressing modes use the following + ; when X and Y = $02/$03 + ; zp $FD + ; zp,x $FF/$00 + ; abs $03FD + ; abs,x/y $03FF/$0400 + ; (ind,x) $02FD/$0302 + ; (ind),y $02FF/$0300 + ; JMP (ind) $0203 + lda #$02 + sta <$00 + lda #$03 + sta <$01 + lda #$FD + sta <$FD + lda #$02 + sta <$FE + lda #$FD + sta <$FF + lda test_mode ; $A3 LAX (ab,X) will load X from these two + sta $02FD + sta $0302 + lda #$03 ; JMP ($03FD) will use this address + sta $03FD + lda #$02 + sta $03FE + + ; Fill stack with the value $02 (for RTS/RTI test) + ldx #$FF + txs + inx + lda #$02 +: pha + inx + bne - + + ; Load timer with -$6C4 + lda #$F9 + sta counter + 1 + lda #$3C + sta counter + + ; Tell NMI to run test code + lda #2 + sta nmi_flag +: cmp nmi_flag + beq - + jmp error + .code + +test_1: +: nop + inc counter + bne - + inc counter + 1 + jmp test_code + +test_2: +: sta <$FD + inc counter + bne - + inc counter + 1 + jmp test_code + +test_3: +: sta $03FD + inc counter + bne - + inc counter + 1 + jmp test_code + +test_addrs_lsb: + .db test_1.lsb, test_2.lsb, test_3.lsb +test_addrs_msb: + .db test_1.msb, test_2.msb, test_3.msb + .code + +; $4n = unofficial NOPs and $EB equivalent of SBC #imm +; $4n = all other unofficial opcodes +; $0n = official opcodes +; -1 = not tested +; n = instruction length +instr_types: + ; 0 1 2 3 4 5 6 7 8 9 A B C D E F + .db 2, 2, -1,$42,$82, 2, 2,$42, 1, 2, 1,$42,$83, 3, 3,$43 ; 0 + .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 1 + .db 3, 2, -1,$42, 2, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; 2 + .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 3 + .db 2, 2, -1,$42,$82, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; 4 + .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 5 + .db 3, 2, -1,$42,$82, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; 6 + .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 7 + .db $82, 2,$82,$42, 2, 2, 2,$42, 1,$82, 1,$42, 3, 3, 3,$43 ; 8 + .db -1, 2, -1,$42, 2, 2, 2,$42, 1, 3, 1,$43,$43, 3,$43,$43 ; 9 + .db 2, 2, 2,$42, 2, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; A + .db -1, 2, -1,$42, 2, 2, 2,$42, 1, 3, 1,$43, 3, 3, 3,$43 ; B + .db 2, 2,$82,$42, 2, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; C + .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; D + .db 2, 2,$82,$42, 2, 2, 2,$42, 1, 2, 1,$82, 3, 3, 3,$43 ; E + .db -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; F + ; Bxx HLT + +; Clocks when no page crossing occurs +instr_times: + ; 0 1 2 3 4 5 6 7 8 9 A B C D E F + .db 7,6,0,8,3,3,5,5,3,2,2,2,4,4,6,6 ; 0 + .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 1 + .db 6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6 ; 2 + .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 3 + .db 6,6,0,8,3,3,5,5,3,2,2,2,3,4,6,6 ; 4 + .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 5 + .db 6,6,0,8,3,3,5,5,4,2,2,2,5,4,6,6 ; 6 + .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 7 + .db 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; 8 + .db 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 ; 9 + .db 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; A + .db 0,5,0,5,4,4,4,4,2,4,2,4,4,4,4,4 ; B + .db 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; C + .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; D + .db 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; E + .db 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; F + +; Clocks when page crossing occurs +instr_times_cross: + ; 0 1 2 3 4 5 6 7 8 9 A B C D E F + .db 7,6,0,8,3,3,5,5,3,2,2,2,4,4,6,6 ; 0 + .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 1 + .db 6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6 ; 2 + .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 3 + .db 6,6,0,8,3,3,5,5,3,2,2,2,3,4,6,6 ; 4 + .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 5 + .db 6,6,0,8,3,3,5,5,4,2,2,2,5,4,6,6 ; 6 + .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 7 + .db 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; 8 + .db 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 ; 9 + .db 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; A + .db 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 ; B + .db 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; C + .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; D + .db 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; E + .db 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; F + +title_str: + .db "6502 TIMING TEST (16 SECONDS)",0 +official_str: + .db "OFFICIAL INSTRUCTIONS ONLY",0 +official_nop_str: + .db "OFFICIAL + NOP",0 +all_instrs_str: + .db "OFFICIAL + UNDOCUMENTED",0 +passed_str: + .db "PASSED",0 +error_str: + .db "FAIL OP :",0 +cross_str: + .db "WITH PAGE CROSS",0 +time_str: + .db "EMULATOR: ",0 +correct_str: + .db "CORRECT : ",0 +unknown_str: + .db "UNKNOWN ERROR",0 +nmi_time_str: + .db "BASIC TIMING WRONG",0 + +; Prints string at address XY +print_str: + stx <1 + sty <0 + ldy #0 + lda (0),y + jsr print_char + iny + lda (0),y +: jsr print_char_no_wait + iny + lda (0),y + bne - + rts + .code + diff --git a/cpu_timing_test6/source/debug.a b/cpu_timing_test6/source/debug.a new file mode 100644 index 0000000..b53f49b --- /dev/null +++ b/cpu_timing_test6/source/debug.a @@ -0,0 +1,156 @@ +; Simple debugging utilities for reporting events and state. +; Text output is sent to debug_char and debug_char_nowait. + +; Beep A times. Made to require minimal features from APU. +debug_beeps: +beep_loop: + pha + + lda #1 ; set up square 1 + sta $4015 + sta $4003 + sta $4001 + sta $4002 + + lda #$0f ; fade volume +: pha + eor #$30 + sta $4000 + lda #8 + jsr delay_msec + pla + clc + adc #-1 + bpl - + + lda #0 + sta $4015 ; silence square for a bit + lda #120 + jsr delay_msec + + pla + clc + adc #-1 + bne beep_loop + rts + .code + +; Print indicated register to console as $hh +; Preserved: A, X, Y, P +print_a: + php + jsr debug_byte + plp + rts + .code + +print_x: + php + pha + txa + jsr debug_byte + pla + plp + rts + .code + +print_y: + php + pha + tya + jsr debug_byte + pla + plp + rts + .code + +print_s: + php + pha + txa + pha + tsx + txa + jsr debug_byte + pla + tax + pla + plp + rts + .code + +print_p: + php + pha + php + pla + jsr debug_byte + pla + plp + rts + .code + +print_ay: + php + pha + lda #36 + jsr debug_char + pla + pha + jsr hex_byte + tya + jsr hex_byte + lda #32 ; ' ' + jsr debug_char_no_wait + pla + plp + rts + .code + +print_newline: + jmp debug_newline + .code + +; Print address YA to console as $hhhh +; Preserved: A, X, Y +debug_addr: + pha + lda #36 ; '$' + jsr debug_char + tya + jsr hex_byte + jmp debug_byte_impl + .code + +; Print byte A to console as $hh +; Preserved: A, X, Y +debug_byte: + pha + lda #36 ; '$' + jsr debug_char +debug_byte_impl: + pla + pha + jsr hex_byte + lda #32 ; ' ' + jsr debug_char_no_wait + pla + rts + +hex_byte: + pha + lsr a + lsr a + lsr a + lsr a + jsr nybble + pla + and #$0f +nybble: + cmp #10 + bcc not_letter + adc #6 ; relies on carry being set +not_letter: + adc #$30 + jmp debug_char_no_wait + .code diff --git a/cpu_timing_test6/source/delays.a b/cpu_timing_test6/source/delays.a new file mode 100644 index 0000000..0a5f2cb --- /dev/null +++ b/cpu_timing_test6/source/delays.a @@ -0,0 +1,122 @@ +; Delays specified in milliseconds, tenths of a millisecond, +; tens of milliseconds, and CPU clocks. + +; TODO: delay loops that take only a single count + +; Delay for almost A milliseconds (A * 0.999009524 msec) +; Preserved: X, Y +delay_msec: + pha ; 3 + lda #253 ; 2 + sec ; 2 +delay_msec_: + nop ; 2 + adc #-2 ; 2 + bne delay_msec_ ; 3 + ; -1 + pla ; 4 + clc ; 2 + adc #-1 ; 2 + bne delay_msec ; 3 + rts + .code + +; Delay for almost 'A / 10' milliseconds (A * 0.099453968 msec) +; Preserved: X, Y +delay_01msec: + pha ; 3 + lda #18 ; 2 + sec ; 2 +delay_01msec_: + nop ; 2 + nop ; 2 + adc #-2 ; 2 + bne delay_01msec_ ; 3 + ; -1 + pla ; 4 + clc ; 2 + adc #-1 ; 2 + bne delay_01msec ; 3 + rts + .code + +; Delay for almost A*10 milliseconds +; Preserved: X, Y +delay_10msec: + pha + lda #10 + jsr delay_msec + pla + clc + adc #-1 + bne delay_10msec + rts + .code + +; Delay n clocks +; Preserved: P, A, X, Y +delay_32: nop +delay_30: nop +delay_28: nop +delay_26: nop +delay_24: nop +delay_22: nop +delay_20: nop +delay_18: nop +delay_16: nop +delay_14: nop +delay_12: rts + +delay_33: nop +delay_31: nop +delay_29: nop +delay_27: nop +delay_25: nop +delay_23: nop +delay_21: nop +delay_19: nop +delay_17: beq + ; 5 +: bne + +: rts ; 6 + .code + +; Delay (5 * A + 6) * Y + 7 + n clocks. Use delay_ya +; command-line tool to generate proper code to call this. +delay_ya11: + .db $a2 ; 1 ldx #imm +delay_ya10: + .db $a2 ; 1 ldx #imm +delay_ya9: + .db $a2 ; 1 ldx #imm +delay_ya8: + .db $a2 ; 1 ldx #imm +delay_ya7: + .db $a2 ; 1 ldx #imm +delay_ya6: + .db $a2 ; 1 ldx #imm +delay_ya5: + .db $a2 ; 1 ldx #imm +delay_ya4: + .db $a2 ; 1 ldx #imm +delay_ya3: + .db $a2 ; 1 ldx #imm +delay_ya2: + .db $a2 ; 1 ldx #imm +delay_ya1: + .db $a6 ; 1 ldx zp +delay_ya0: + nop ; 2 +delay_ya: + ; 2 lda # + ; 2 ldy # + ; 6 jsr + tax ; *2 +delay_yax: + dex ; **2 + bne delay_yax ; **3 + ; *-1 + dey ; *2 + bne delay_ya ; *3 + ; -1 + rts ; 6 + .code diff --git a/cpu_timing_test6/source/ppu_util.a b/cpu_timing_test6/source/ppu_util.a new file mode 100644 index 0000000..9529a1e --- /dev/null +++ b/cpu_timing_test6/source/ppu_util.a @@ -0,0 +1,123 @@ + +; Clear VBL flag then wait for it to be set +; Preserved: A, X, Y +wait_vbl: + bit $2002 +: bit $2002 + bpl - + rts + .code + +; Set VRAM address to A * $100 +; Preserved: X, Y +set_vpage: + bit $2002 + sta $2006 + lda #0 + sta $2006 + rts + .code + +; Set VRAM address to A * $100 + X +; Preserved: A, X, Y +set_vaddr: + bit $2002 + sta $2006 + stx $2006 + rts + .code + +; Set X and Y scroll +; Preserved: A, X, Y +set_vscroll: + bit $2002 + stx $2005 + sty $2005 + rts + .code + +; Turn off NMI and disable BG and sprites +; Preserved: A, X, Y +disable_ppu: + pha + lda #0 + sta $2000 + sta $2001 + bit $2002 + sta $2006 + sta $2006 + pla + rts + .code + +; Set sprite memory to $ff +; Preserved: Y +clear_sprites: + lda #$ff + ldx #0 +: sta $2004 + dex + bne - + rts + .code + +; Clear/fill nametable with 0/A and clear attributes to 0 +clear_nametable: + lda #0 +fill_nametable: + pha + lda #$20 + jsr set_vpage + pla + ldx #240 +: sta $2007 + sta $2007 + sta $2007 + sta $2007 + dex + bne - + lda #0 + ldx #64 +: sta $2007 + dex + bne - + rts + .code + +; Clear/fill VRAM with 0/A +clear_vram: + lda #0 +fill_vram: + ldx #0 + ldy #$24 + bne fill_vram_ + +; Clear/fill CHR with 0/A +fill_chr1: + ldx #$10 + ldy #$10 + bne fill_vram_ + +fill_chr0: + ldx #0 + ldy #$10 + bne fill_vram_ + +clear_chr: + lda #0 +fill_chr: + ldx #0 + ldy #$20 +; Fill Y*$100 bytes of VRAM with A, starting at X*$100 +fill_vram_: + bit $2002 + stx $2006 + ldx #0 + stx $2006 +: sta $2007 + dex + bne - + dey + bne - + rts + .code diff --git a/cpu_timing_test6/source/runtime_rom.a b/cpu_timing_test6/source/runtime_rom.a new file mode 100644 index 0000000..9aa1ca8 --- /dev/null +++ b/cpu_timing_test6/source/runtime_rom.a @@ -0,0 +1,13 @@ +; Build as standalone NES ROM using console for output + + .include "runtime_rom_common.a" + +patch_reset_then_wait: +exit: jmp exit + + .default reset = main + + .org $fffa + .dw nmi + .dw reset + .dw irq diff --git a/cpu_timing_test6/source/runtime_rom_common.a b/cpu_timing_test6/source/runtime_rom_common.a new file mode 100644 index 0000000..321d462 --- /dev/null +++ b/cpu_timing_test6/source/runtime_rom_common.a @@ -0,0 +1,45 @@ + + .include "delays.a" + .include "console.a" + .include "debug.a" + .include "ppu_util.a" + +console_ready = $7f1 + +debug_char: + pha + lda #$a5 + cmp console_ready + beq + + sta console_ready + txa + pha + tya + pha + jsr init_console + pla + tay + pla + tax +: pla + jmp print_char + +debug_newline: + jsr console_newline + jmp console_newline + +debug_char_no_wait: + jmp print_char_no_wait + +init_runtime: +clear_console_ready: + lda #0 + sta console_ready + rts + +forever: + sei ; disable interrupts + lda #0 + sta $2000 + jsr clear_console_ready + jmp exit diff --git a/dmc_dma_during_read4/dma_2007_read.nes b/dmc_dma_during_read4/dma_2007_read.nes new file mode 100644 index 0000000..845d36e Binary files /dev/null and b/dmc_dma_during_read4/dma_2007_read.nes differ diff --git a/dmc_dma_during_read4/dma_2007_write.nes b/dmc_dma_during_read4/dma_2007_write.nes new file mode 100644 index 0000000..68215a6 Binary files /dev/null and b/dmc_dma_during_read4/dma_2007_write.nes differ diff --git a/dmc_dma_during_read4/dma_4016_read.nes b/dmc_dma_during_read4/dma_4016_read.nes new file mode 100644 index 0000000..e9cc257 Binary files /dev/null and b/dmc_dma_during_read4/dma_4016_read.nes differ diff --git a/dmc_dma_during_read4/double_2007_read.nes b/dmc_dma_during_read4/double_2007_read.nes new file mode 100644 index 0000000..8180194 Binary files /dev/null and b/dmc_dma_during_read4/double_2007_read.nes differ diff --git a/dmc_dma_during_read4/read_write_2007.nes b/dmc_dma_during_read4/read_write_2007.nes new file mode 100644 index 0000000..4b49fd3 Binary files /dev/null and b/dmc_dma_during_read4/read_write_2007.nes differ diff --git a/dmc_dma_during_read4/source/common/ascii.chr b/dmc_dma_during_read4/source/common/ascii.chr new file mode 100644 index 0000000..0687d4e Binary files /dev/null and b/dmc_dma_during_read4/source/common/ascii.chr differ diff --git a/dmc_dma_during_read4/source/common/common.inc b/dmc_dma_during_read4/source/common/common.inc new file mode 100644 index 0000000..c781bc6 --- /dev/null +++ b/dmc_dma_during_read4/source/common/common.inc @@ -0,0 +1,54 @@ +; Shell that calls begin, synchronizes with DMC, then +; calls test with DMC DMA occurring during its +; execution. Then stops DMC and calls end. Repeats +; above, each with the DMA one clock later than the +; previous. +; +; The following constants must be set before including +; this file: +; +;iter = n ; how many times the test is run +;time = n ; adjusts time of first DMA +;dma = 1 ; set to 0 to disable DMA + +CHR_RAM=1 +.include "shell.inc" +.include "sync_dmc.s" + +run_tests: + for_loop dmc_test,iter,1,-1 + rts + +dmc_test: + pha + + jsr begin + + ; Synchronize with DMC + jsr time_code_begin + + ; Start DMC + lda #$10 + sta $4015 + lda #dma*$10 + sta $4015 + + ; Delay + pla + jsr delay_a_25_clocks + delay 3309+33+time-iter + + ; DMC DMA occurs during this code + jsr test + + ; Stop DMC + pha + lda #0 + sta $4015 + pla + + jsr end + + rts + +.align 256 diff --git a/dmc_dma_during_read4/source/common/console.s b/dmc_dma_during_read4/source/common/console.s new file mode 100644 index 0000000..9d8f559 --- /dev/null +++ b/dmc_dma_during_read4/source/common/console.s @@ -0,0 +1,196 @@ +; Scrolling text console with line wrapping, 30x30 characters. +; Buffers lines for speed. Will work even if PPU doesn't +; support scrolling (until text reaches bottom). +; ** ASCII font must already be in CHR, and mirroring +; must be vertical or single-screen. + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs +console_margin = 1 + +console_buf_size = 32 +console_width = console_buf_size - (console_margin*2) + +zp_byte console_pos +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_buf_size + + +; Waits for beginning of VBL +; Preserved: A, X, Y +console_wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- + rts + + +; Initializes console +console_init: + jsr console_hide + lda #0 + sta PPUCTRL + + ; Load palette + lda #$3F + sta PPUADDR + lda #0 + sta PPUADDR + lda #$0F ; black background + sta PPUDATA + lda #$30 ; white text + sta PPUDATA + sta PPUDATA + sta PPUDATA + + ; Fill nametable with spaces + lda #$20 + sta PPUADDR + ldx #0 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + + ; Clear attributes + lda #0 + ldx #$40 +: sta PPUDATA + dex + bne :- + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + lda #8 + sta console_scroll + jsr console_scroll_up_ + jmp console_show + + +; Shows console display +; Preserved: X, Y +console_show: + pha + jsr console_wait_vbl + lda #PPUMASK_BG0 + sta PPUMASK + jmp console_apply_scroll_ + + +; Hides console display and makes screen black +; Preserved: X, Y +console_hide: + jsr console_wait_vbl + lda #0 + sta PPUMASK + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + ; Write to buffer + stx console_temp + ldx console_pos + sta console_buf+console_margin,x + ldx console_temp + + ; Update pos and print newline if buf full + dec console_pos + bmi console_newline ; reached end of line + + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl + jsr console_flush_ + jsr console_scroll_up_ + jsr console_flush_ + jmp console_apply_scroll_ + + +console_get_scroll_: + ; A = (console_scroll+8)%240 + lda console_scroll + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_scroll_up_: + ; Scroll up 8 pixels + jsr console_get_scroll_ + sta console_scroll + + stx console_temp + + ; Start new clear line + lda #' ' + ldx #console_buf_size-1 +: sta console_buf,x + dex + bpl :- + ldx #console_width-1 + stx console_pos + + ldx console_temp + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl + jsr console_flush_ +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + jsr console_get_scroll_ + sta PPUSCROLL + + pla + rts + +console_flush_: + ; Address line in nametable + lda console_scroll + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + sta PPUADDR + + ; Copy line + stx console_temp + ldx #console_buf_size-1 +: lda console_buf,x + sta PPUDATA + dex + bpl :- + ldx console_temp + + rts + diff --git a/dmc_dma_during_read4/source/common/crc.s b/dmc_dma_during_read4/source/common/crc.s new file mode 100644 index 0000000..195491e --- /dev/null +++ b/dmc_dma_during_read4/source/common/crc.s @@ -0,0 +1,83 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + ; FALL THROUGH +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: X, Y +; Time: 350 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on diff --git a/dmc_dma_during_read4/source/common/delay.s b/dmc_dma_during_read4/source/common/delay.s new file mode 100644 index 0000000..ffccc5b --- /dev/null +++ b/dmc_dma_during_read4/source/common/delay.s @@ -0,0 +1,144 @@ +; Delays in clocks and milliseconds. All routines re-entrant +; (no global data). + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: X, Y +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: X, Y +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + + +; Delays n clocks, from 2 to 16777215 +; Preserved: X, Y +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + .if (n) < 14 .and (n) <> 12 + delay_inline (n) + .elseif (n) < 27 + delay_unrolled (n) + .elseif <(n) = 0 + delay_256 (n) + .else + lda #<((n)-27) + jsr delay_a_25_clocks + delay_256 ((n)-27) + .endif +.endmacro + + +; Delays A+25 clocks (including JSR) +; Preserved: X, Y +.align 64 +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256+16 clocks (including JSR) +; Preserved: X, Y +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_clocks_: + pha + lda #256-19-22-16 + bne @first ; always branches +: pha + lda #256-19-22 +@first: jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536+16 clocks (including JSR) +; Preserved: X, Y +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_clocks_: + pha + lda #256-19-22-16 + bne @first +: pha + lda #256-19-22 +@first: jsr delay_a_25_clocks + lda #255 + jsr delay_256a_clocks_ + pla + clc + adc #-1 + bne :- + rts + +.macro delay_inline n + .if n = 7 .or n >= 9 + pha + pla + delay_inline (n-7) + .elseif n >= 3 .and n & 1 + lda <0 + delay_inline (n-3) + .elseif n >= 2 + nop + delay_inline (n-2) + .elseif n > 0 + .error "delay_short internal error" + .endif +.endmacro + +.macro delay_unrolled n + .if n & 1 + lda <0 + jsr delay_unrolled_-((n-15)/2) + .else + jsr delay_unrolled_-((n-12)/2) + .endif +.endmacro + + .res 7,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_256 n + .if >n + lda #>n + jsr delay_256a_clocks_ + .endif + .if ^n + lda #^n + jsr delay_65536a_clocks_ + .endif +.endmacro + diff --git a/dmc_dma_during_read4/source/common/macros.inc b/dmc_dma_during_read4/source/common/macros.inc new file mode 100644 index 0000000..ea54ba6 --- /dev/null +++ b/dmc_dma_during_read4/source/common/macros.inc @@ -0,0 +1,65 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name +.macro zp_res name,size + seg_data "ZEROPAGE",{name: .res size} +.endmacro + +.macro bss_res name,size + seg_data "BSS",{name: .res size} +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "STRINGS",{Addr: data} +.endmacro + +; If name isn't yet defined, defines it with value +.macro SET_DEFAULT name,value + .ifndef name + name=value + .endif +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro diff --git a/dmc_dma_during_read4/source/common/nes.inc b/dmc_dma_during_read4/source/common/nes.inc new file mode 100644 index 0000000..aff4389 --- /dev/null +++ b/dmc_dma_during_read4/source/common/nes.inc @@ -0,0 +1,35 @@ +; NES I/O locations and masks + +; Clocks per second +CLOCK_RATE = 1789773 +;CLOCK_RATE = 1662607 + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 diff --git a/dmc_dma_during_read4/source/common/print.s b/dmc_dma_during_read4/source/common/print.s new file mode 100644 index 0000000..60d1f8e --- /dev/null +++ b/dmc_dma_during_read4/source/common/print.s @@ -0,0 +1,194 @@ +; Prints values in various ways to output, including numbers and strings. + +newline = 10 + +; Prints indicated register to console as two hex chars and space +; Preserved: A, X, Y, P +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: X, Y +print_hex: + ; Update checksum + pha + jsr update_crc + pla + + ; Print high nibble + pha + lsr a + lsr a + lsr a + lsr a + jsr @nibble + pla + + ; Print low nibble + and #$0F +@nibble: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints character and updates checksum UNLESS it's a newline. +; Preserved: X, Y +print_char: + cmp #newline + beq :+ + pha + jsr update_crc + pla +: jmp print_char_ + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str + jsr_with_addr print_str_addr,{.byte str,0} +.endmacro + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: Y +print_dec: + ; Hundreds + cmp #100 + blt @tens + ldx #'0' +: sbc #100 + inx + cmp #100 + bge :- + jsr @digit + + ; Tens +@tens: cmp #10 + blt @ones + ldx #'0' +: sbc #10 + inx + cmp #10 + bge :- + jsr @digit + + ; Ones +@ones: ora #'0' + jmp print_char + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y +.macro print_cc cond,yes,no + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla +.endmacro diff --git a/dmc_dma_during_read4/source/common/serial.s b/dmc_dma_during_read4/source/common/serial.s new file mode 100644 index 0000000..59fbebd --- /dev/null +++ b/dmc_dma_during_read4/source/common/serial.s @@ -0,0 +1,43 @@ +; Serial output at 57600 bits/sec on controller port 2 +; +; Uses stack and register A only, and doesn't mind page crossing +; (uses subroutines instead of loops). + +; Initializes serial. If this isn't done, first byte sent to +; PC might be corrupt. +; Preserved: X, Y +serial_init: + sec + lda #$FF + bne serial_write_ ; always branches + + +; Writes byte A to serial +; Preserved: X, Y +serial_write: + clc +serial_write_: + jsr @bit ; start + nop ; TODO: why the extra delay? + jsr @first ; bit 0 + jsr @bit ; bit 1 + jsr @bit ; bit 2 + jsr @bit ; bit 3 + jsr @bit ; bit 4 + jsr @bit ; bit 5 + jsr @bit ; bit 6 + jsr @bit ; bit 7 + sec ; 2 stop bit +@first: nop ; 4 + nop +@bit: ; 6 jsr + pha ; 3 + rol a ; 2 + and #1 ; 2 + sta JOY1 ; 4 + pla ; 4 + ror a ; 2 +.if CLOCK_RATE = 1789773 + nop ; 2 +.endif + rts ; 6 diff --git a/dmc_dma_during_read4/source/common/shell.inc b/dmc_dma_during_read4/source/common/shell.inc new file mode 100644 index 0000000..61fe108 --- /dev/null +++ b/dmc_dma_during_read4/source/common/shell.inc @@ -0,0 +1,434 @@ +; Program shell with text console. Included before user code. + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INC + .error "File included twice" + .end +.endif +SHELL_INC = 1 + + +; ******************************************* Prefix + +.segment "CODE2" + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E + +; RAM that isn't cleared by init routine +nv_ram = $7F0 + +; Macros and constants +.include "macros.inc" +.include "nes.inc" + +; Interrupt handlers are wrapped with these +.define BEGIN_NMI nmi: +.define END_NMI + +.define BEGIN_IRQ irq: +.define END_IRQ + +; Set undefined flags to 0, allowing simpler .if statements +SET_DEFAULT BUILD_NSF,0 +SET_DEFAULT BUILD_MULTI,0 +SET_DEFAULT BUILD_DEVCART,0 + +; Number of clocks devcart takes to jump to user reset +SET_DEFAULT DEVCART_DELAY,0 + + +; ******************************************* Libraries + +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +.if !BUILD_MULTI + .include "serial.s" +.endif + +; Sets up environment, calls main, then exits with code 0 +run_main: + ; Initialize libraries + jsr init_crc + + ; Establish consistent environment before + ; running main + jsr wait_vbl + lda #PPUMASK_BG0 + sta PPUMASK + delay 2370+24 + lda #$34 + pha + lda #0 + sta SNDMODE + tax + tay + clc + clv + plp + + jsr main + + ; Default to silent exit if main returns + lda #0 + ; FALL THROUGH + +; Exits program and prints result code if non-zero +exit: + ; Reset stack + ldx #$FF + txs + + ; Disable interrupts + sei + pha + jsr nmi_off + pla + + jmp exit_ + +; Reports internal error and exits program +internal_error: + print_str "Internal error" + lda #1 + jmp exit + + +.if BUILD_NSF || BUILD_MULTI || BUILD_DEVCART + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.else + .include "console.s" +.endif + + +; ******************************************* Single Test + +.if !BUILD_MULTI + +print_char_: + jsr console_print + jmp serial_write + +; Reset handler +.ifndef CUSTOM_RESET + reset = std_reset +.endif + +.macro init_nes + sei + cld + ldx #$FF + txs + + .if !BUILD_NSF + ; Init PPU + lda #0 + sta PPUCTRL + sta PPUMASK + .endif + + ; Clear RAM + lda #0 + ldx #7 ; last page + ldy #@ascii +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>@ascii_end + bne @page + +.pushseg +.rodata +@ascii: + .incbin "ascii.chr" +@ascii_end: +.popseg +.endif + + jsr console_init + jsr serial_init + jmp run_main + + +; Exit handler +exit_: + ; 0: "" + cmp #1 + jlt exit2 + + ; 1: "Failed" + bne :+ + print_str {newline,"Failed"} + jmp exit2 + + ; n: "Error n" +: pha + print_str {newline,"Error "} + jsr print_dec + pla + +exit2: +.if !BUILD_DEVCART + ; Be sure output is visible + pha + print_str {newline,newline,newline} + jsr console_show + pla + + ; Report audibly as well + jsr beep_bits +.else + ; Tell host to stop capturing serial + lda #$1A + jsr serial_write + delay_msec 400 +.endif + + ; Clear nv_ram + lda #0 + ldx #filename + sta addr+1 + jsr print_str_addr + jsr print_newline + rts + +.pushseg +.segment "STRINGS" +; Filename terminated with zero byte, or just zero byte +; if filename isn't available. +filename: + .incbin "ram:rom.nes" + .byte 0 +.popseg +.else +print_filename: + rts + +filename: + .byte 0 +.endif + + +; User code goes in main code segment +.segment "CODE" + nop diff --git a/dmc_dma_during_read4/source/common/sync_dmc.s b/dmc_dma_during_read4/source/common/sync_dmc.s new file mode 100644 index 0000000..157bda7 --- /dev/null +++ b/dmc_dma_during_read4/source/common/sync_dmc.s @@ -0,0 +1,159 @@ +; Synchronizes to DMC and times a piece of code + +; Synchronizes precisely with DMC timer +; Preserved: X, Y +; Time: 8 msec avg, 17 msec max +.align 64 +sync_dmc: + ; Setup + lda #$80 + sta $4010 + lda #0 + sta $4013 + sta SNDCHN + + ; Start twice (first will clear immediately) + lda #$10 + sta SNDCHN + nop + sta SNDCHN + + ; Coarse synchronize +: bit SNDCHN + bne :- + + ; DO NOT write to memory. It affects timing. + + nop ; 2 fine-tune: 2=OK 3=OK 4=fail + + ; Fine synchronize. 3421+4 clocks per iter + nop ; 2 + nop ; 2 + lda #226 ; 3391 delay + bne @first ; 3 +@wait: + lda #227 ; 3406 delay +@first: +: nop + nop + nop + nop + sec + sbc #1 + bne :- + ; 4 DMC wait-states + lda #$10 ; 2 + sta SNDCHN ; 4 + nop ; 2 + bit SNDCHN ; 4 + bne @wait ; 3 + + rts + + +; Returns in XA number of clocks elapsed since call to +; time_code_begin, MOD 3424. Unreliable if result is +; 3387 or greater. +; Time: 33 msec max +.align 64 +time_code_end: + ; Restart + lda #$10 + sta SNDCHN + nop + sta SNDCHN + + ; Rough sync + ldy #-$2C +@coarse: + nop + nop + bne :+ +: dey + bit SNDCHN + bne @coarse + + ; DO NOT write to memory. It affects timing. + + ; Fine sync + ldx #-$2 +@wait: + lda #$10 ; 2 + sta SNDCHN ; 4 + + lda #179 ; delay 3402 +: nop + nop + nop + nop + nop + nop + sec + sbc #1 + bne :- + + inx ; 2 + lda #$10 ; 2 + bit SNDCHN ; 4 + beq @wait ; 3 + + ;jsr print_y + ;jsr print_x + + ; Calculate result + ; XA = Y << 4 | X + stx <0 + + tya + lsr a + lsr a + lsr a + lsr a + tax + + tya + asl a + asl a + asl a + asl a + + clc + adc <0 + bcc :+ + inx +: + rts + + +; Begins timing section of code +; Preserved: A, X, Y, flags +; Time: 9 msec avg, 17 msec max +.align 32 +time_code_begin: + php + pha + txa + pha + tya + pha + + jsr sync_dmc + + nop + + ldx #163 ; 3396 delay + lda #3 +: dex + bne :- + sec + sbc #1 + bne :- + + pla + tay + pla + tax + pla + plp + + rts diff --git a/dmc_dma_during_read4/source/common/testing.s b/dmc_dma_during_read4/source/common/testing.s new file mode 100644 index 0000000..8bb0741 --- /dev/null +++ b/dmc_dma_during_read4/source/common/testing.s @@ -0,0 +1,162 @@ +; Utilities for writing test ROMs + +zp_res test_code,1 ; code of current test +zp_res test_name,2 ; address of name of current test, or 0 of none + + +; Reports that all tests passed +tests_passed: +.if !BUILD_MULTI + jsr print_filename + print_str "Passed" +.endif + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + lda test_code + + ; Treat 0 as 1, in case it wasn't ever set + bne :+ + lda #1 + sta test_code +: + ; If code >= 2, print name + cmp #2 + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: +.if !BUILD_MULTI + jsr print_filename +.endif + ; End program + lda test_code + jmp exit + + +; Sets current test code and optional name. Also resets +; checksum. +.macro set_test code,name + pha + lda #code + jsr set_test_ + .local Addr + lda #Addr + sta ". + +Several routines are available to print values and text to the console. +Most update a running CRC-32 checksum which can be checked with +check_crc, allowing ALL the output to be checked very easily. If the +checksum doesn't match, it is printed, so you can run the code on a NES +and paste the correct checksum into your code. + +The default is to build an iNES ROM, with other build types that I +haven't documented (devcart, sub-test of a multi-test ROM, NSF music +file). My nes.cfg file puts the code at $E000 since my devcart requires +it, and I don't want the normal ROM to differ in any way from what I've +tested. + +Library routines are organized by function into several files, each with +short documentation. Each routine may also optionally list registers +which are preserved, rather than those which are modified (trashed) as +is more commonly done. This is because it's best for the caller to +assume that ALL registers are NOT preserved unless noted. + +Some macros are used to make common operations more convenient. The left +is equivalent to the right: + + Macro Equivalent + ------------------------------------- + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + zp_byte name .zeropage + name: .res 1 + .code + + zp_res name,n .zeropage + name: .res n + .code + + bss_res name,n .bss + name: .res n + .code + + for_loop routine,begin,end,step + calls a routine with A set to successive values diff --git a/dmc_tests/buffer_retained.nes b/dmc_tests/buffer_retained.nes new file mode 100644 index 0000000..e205aea Binary files /dev/null and b/dmc_tests/buffer_retained.nes differ diff --git a/dmc_tests/latency.nes b/dmc_tests/latency.nes new file mode 100644 index 0000000..5aeb407 Binary files /dev/null and b/dmc_tests/latency.nes differ diff --git a/dmc_tests/status.nes b/dmc_tests/status.nes new file mode 100644 index 0000000..541ce6f Binary files /dev/null and b/dmc_tests/status.nes differ diff --git a/dmc_tests/status_irq.nes b/dmc_tests/status_irq.nes new file mode 100644 index 0000000..1a31ad5 Binary files /dev/null and b/dmc_tests/status_irq.nes differ diff --git a/dpcmletterbox/CHANGES.txt b/dpcmletterbox/CHANGES.txt new file mode 100644 index 0000000..e6ae9cb --- /dev/null +++ b/dpcmletterbox/CHANGES.txt @@ -0,0 +1,16 @@ +_____________________________________________________________________ +v2 (2010-08-16): + * NMI handler no longer uses the Y register without saving it. + * Lots more comments in the NMI handler. + * Vertical mirroring to illustrate how this effect would fit into a + real game. + * Reads the controllers during a period that is guaranteed + glitch-free, then lets the player move the camera. + * Actually sets the sample address so that the length-17 loop + isn't playing garbage. + * Priming playback no longer raises an IRQ. + +_____________________________________________________________________ +v1 (2010-08-12): + * initial release + diff --git a/dpcmletterbox/NROM.ini b/dpcmletterbox/NROM.ini new file mode 100644 index 0000000..0cee01a --- /dev/null +++ b/dpcmletterbox/NROM.ini @@ -0,0 +1,31 @@ +# +# Linker script for NROM games +# Copyright 2010 Damian Yerrick +# +# Copying and distribution of this file, with or without +# modification, are permitted in any medium without royalty +# provided the copyright notice and this notice are preserved. +# This file is offered as-is, without any warranty. +# +MEMORY { + ZP: start = $10, size = $f0, type = rw; + # use first $10 zeropage locations as locals + HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00; + RAM: start = $0300, size = $0500, type = rw; + ROM7: start = $C000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF; +} + +SEGMENTS { + INESHDR: load = HEADER, type = ro, align = $10; + ZEROPAGE: load = ZP, type = zp; + BSS: load = RAM, type = bss, define = yes, align = $100; + DMC: load = ROM7, type = ro, align = 64, optional = yes; + CODE: load = ROM7, type = ro, align = $100; + RODATA: load = ROM7, type = ro, align = $100; + VECTORS: load = ROM7, type = ro, start = $FFFA; +} + +FILES { + %O: format = bin; +} + diff --git a/dpcmletterbox/README.txt b/dpcmletterbox/README.txt new file mode 100644 index 0000000..075c08f --- /dev/null +++ b/dpcmletterbox/README.txt @@ -0,0 +1,66 @@ +DPCM Letterbox + +This NES program demonstrates abusing the NTSC NES's sampled sound +playback hardware as a scanline timer to split the screen twice +without needing to use a mapper-generated IRQ. + +== How it works == + +The NES has sample playback hardware that can trigger when it +finishes playing a differential pulse code modulated (DPCM) waveform. +There are eight samples to a byte, and a waveform is 16n + 1 bytes +long. There are sixteen valid rates for sample playback, numbered 0 +to 15, and DPCM rate 15 has 54 CPU cycles per sample, or 54*3*8=1296 +PPU dots per byte, or 3.8 scanlines per byte. But the time between +NMI and the first sample data fetch drifts from frame to frame. +So at the start of the frame, the program has to measure exactly +how far apart the CPU and PPU are, and then the IRQ handler has to +waste a corresponding amount of time before doing raster effects. + +This version has a slight visual artifact in the top overscan region +because it uses sprite 0 as a timing reference so that it can be +used even with an NMI handler whose execution time in CPU cycles +varies. But a game with a cycle-timed NMI handler would not need +sprite 0; it could use the end of the NMI handler as a reference. + +Reset handler: +Set up screen data +Enable IRQ +Wait forever + +NMI handler: +Set up sprite 0 hit at top of screen +Disable sample playback +Turn on background rendering at a fixed scroll position +Wait for Sprite 0 off and on +Turn off background rendering +Clear number of elapsed IRQs +Enable playback and IRQ +Measure time until IRQ in 8-cycle units +Convert this to an amount of time to waste +Read the controllers +Compute next scroll value +Return + +IRQ handler: +Add 1 to elapsed IRQs +Restart sample playback +If elapsed IRQs is at first threshold: + Waste time in 8-cycle units + Turn on background rendering +If elapsed IRQs is at second threshold: + Switch next sample playback to 17-byte mode +If elapsed IRQs is at third threshold: + Waste time in 8-cycle units + Turn off background rendering + +== Legal == + +The following license applies to the source code, binary code, and +this manual: + +Copyright 2010 Damian Yerrick +Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without any warranty. diff --git a/dpcmletterbox/dpcmletterbox.nes b/dpcmletterbox/dpcmletterbox.nes new file mode 100644 index 0000000..b8945b1 Binary files /dev/null and b/dpcmletterbox/dpcmletterbox.nes differ diff --git a/dpcmletterbox/makefile b/dpcmletterbox/makefile new file mode 100644 index 0000000..26e2e32 --- /dev/null +++ b/dpcmletterbox/makefile @@ -0,0 +1,52 @@ +#!/usr/bin/make -f +# +# Makefile for DPCM Letterbox demo +# Copyright 2010 Damian Yerrick +# +# Copying and distribution of this file, with or without +# modification, are permitted in any medium without royalty +# provided the copyright notice and this notice are preserved. +# This file is offered as-is, without any warranty. +# +CC65 = /usr/local/bin +AS65 = $(CC65)/ca65 +LD65 = $(CC65)/ld65 +EMU := fceu -slstart 0 -slend 239 -xscale 2 -yscale 2 -opengl 0 +CC = gcc +ifdef COMSPEC +DOTEXE=.exe +else +DOTEXE= +endif +CFLAGS = -std=gnu99 -Wall -DNDEBUG -O +CFLAGS65 = +objdir = obj/nes +srcdir = src +imgdir = tilesets + +objlist = reset +objlistntsc = $(foreach o,$(objlist),$(objdir)/$(o).o) + +.PHONY: run + +run: dpcmletterbox.nes + $(EMU) $< + +$(objdir)/%.o: $(srcdir)/%.s + $(AS65) $(CFLAGS65) $< -o $@ + +$(objdir)/%.o: $(objdir)/%.s + $(AS65) $(CFLAGS65) $< -o $@ + +map.txt dpcmletterbox.prg: NROM.ini $(objlistntsc) + $(LD65) -C $^ -m map.txt -o dpcmletterbox.prg + +$(objdir)/ac16.chr: $(imgdir)/ac16.png + tools/pilbmp2nes.py -H 16 $< $@ + +%.nes: %.prg %.chr + cat $^ > $@ + +dpcmletterbox.chr: $(objdir)/ac16.chr + cat $(objdir)/ac16.chr $(objdir)/ac16.chr > $@ + diff --git a/dpcmletterbox/obj/nes/index.txt b/dpcmletterbox/obj/nes/index.txt new file mode 100644 index 0000000..4ff5b7e --- /dev/null +++ b/dpcmletterbox/obj/nes/index.txt @@ -0,0 +1 @@ +Compiled files get put here. diff --git a/dpcmletterbox/src/nes.h b/dpcmletterbox/src/nes.h new file mode 100644 index 0000000..f4ee2d0 --- /dev/null +++ b/dpcmletterbox/src/nes.h @@ -0,0 +1,38 @@ +; Copyright 2010 Damian Yerrick +; +; Copying and distribution of this file, with or without +; modification, are permitted in any medium without royalty +; provided the copyright notice and this notice are preserved. +; This file is offered as-is, without any warranty. + +PPUCTRL = $2000 + VBLANK_NMI = $80 +PPUMASK = $2001 + TINT_B = $80 + TINT_G = $40 + TINT_R = $20 + OBJ_CLIP = $10 + OBJ_ON = $14 + BG_CLIP = $08 + BG_ON = $0A + LIGHTGRAY = $01 +PPUSTATUS = $2002 + ; N is NMI_occurred, used primarily for PPU warmup wait after reset + ; V is sprite 0 hit status, cleared at end of vblank +OAMADDR = $2003 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +DMCFREQ = $4010 + DMC_IRQMODE = $80 + DMC_LOOPMODE = $40 +DMCADDR = $4012 +DMCLEN = $4013 +OAM_DMA = $4014 +SNDCHN = $4015 + SNDCHN_PSGS = $0F + SNDCHN_DMC = $10 +P1 = $4016 +P2 = $4017 + + diff --git a/dpcmletterbox/src/reset.s b/dpcmletterbox/src/reset.s new file mode 100644 index 0000000..7bf591c --- /dev/null +++ b/dpcmletterbox/src/reset.s @@ -0,0 +1,539 @@ +; nes sample timer abuse +; +; Copyright 2010 Damian Yerrick +; +; Copying and distribution of this file, with or without +; modification, are permitted in any medium without royalty +; provided the copyright notice and this notice are preserved. +; This file is offered as-is, without any warranty. + +.p02 +.include "src/nes.h" + +WITH_CONTROLLERS = 1 + +; Possible playback rates are +; 428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54 + + +OAM = $200 + +.segment "INESHDR" +.byt 'N','E','S',$1A +.byt 1 ; 1 prg page +.byt 1 ; 1 chr page +.byt $01 ; mirror type 1 (vertical mirroring), mapper low nibble = 0 +.byt $00 ; mapper high nibble = 0 + +.segment "VECTORS" +.addr nmi_handler, reset_handler, irq_handler + +.segment "ZEROPAGE" +irqs: .res 1 +nmis: .res 1 +camera_x: .res 1 +camera_y: .res 1 +lastPPUCTRL: .res 1 +irq_lag: .res 1 +val_4015: .res 1 +.ifdef WITH_CONTROLLERS + cur_keys: .res 2 +.endif + +.segment "CODE" +.proc irq_handler + bit $4015 + inc irqs + + ; restart IRQ + pha + lda val_4015 + sta $4015 + lda irqs + cmp #8 + beq irq_on + cmp #16 + beq irq_slowdown + cmp #19 + beq irq_off +irq_done: + pla + rti + +irq_on: + txa + pha + jsr compensate + jsr waste36dots + jsr waste36dots + jsr waste36dots + bit $00 + nop + + ; Based on an example by tokumaru + ; http://nesdev.parodius.com/bbs/viewtopic.php?p=64111#64111 + ; start setting the scroll before the horizontal blank + lda lastPPUCTRL + asl a + asl a + sta PPUADDR + lda camera_y + sta PPUSCROLL + asl a + asl a + and #%11100000 + ldx camera_x + sta camera_x + txa + lsr a + lsr a + lsr a + ora camera_x + + ;finish setting the scroll during HBlank (11 cycles) + stx PPUSCROLL + sta PPUADDR + stx camera_x + + lda #BG_ON|OBJ_ON + sta PPUMASK + pla + tax + jmp irq_done + +irq_slowdown: + lda #1 + sta DMCLEN + jmp irq_done + +irq_off: + txa + pha + jsr compensate + jsr waste36dots + jsr waste36dots + bit OAM + bit OAM + lda #OBJ_ON|LIGHTGRAY + sta PPUMASK + pla + tax + jmp irq_done + +compensate: + ldx irq_lag +: + lda $00 + inx + bne :- +waste36dots: + rts +.endproc + +.proc nmi_handler + bit PPUSTATUS + inc nmis + pha + txa + pha + + ; Set sample channel to high frequency and turn off playback. + ; This makes sure that 1. the sample buffer is empty, and 2. all + ; CPU cycles are available for VRAM transfer. + lda #$0F + sta DMCFREQ ; fastest pitch, no loop, no irq + sta SNDCHN ; tones on, sample off + lda #$00 + sta irqs ; init irq counter to distinguish single from double + sta DMCLEN ; sample length: 1 byte + + ; OMITTED: Write to VRAM and stuff here. + + ; After writing to VRAM, we need some sort of raster time + ; reference. If your VRAM update code executes in constant time, + ; you can use that. But instead, we're using sprite 0, so we + ; scroll the background to guarantee that a sprite 0 hit will + ; trigger. + + sta PPUSCROLL + sta PPUSCROLL + sta OAMADDR + lda #>OAM + sta OAM_DMA + lda #BG_ON|OBJ_ON|LIGHTGRAY + sta PPUMASK +wait_s0_off: + bit PPUSTATUS + bvs wait_s0_off +wait_s0_on: + bit PPUSTATUS + bvc wait_s0_on + lda #OBJ_ON|LIGHTGRAY + sta PPUMASK + + ; *** Start of timer setup code *** + + ; There are two buffers in the sample playback chain. One is an + ; 8-bit buffer that holds bytes fetched from memory; for reasons + ; related to audio hardware on another Nintendo platform, I call + ; this buffer the "FIFO". The other is an 8-bit shift register + ; from which delta values are shifted. Every 8 sample periods, the + ; data in the FIFO gets moved to the shift register, and the FIFO + ; becomes empty. Whenever the FIFO is empty, and at least one + ; byte remains in the wave data, one byte gets read into the FIFO. + ; If this is the last byte of the wave, the APU pulls /IRQ low. + ; + ; This first playback starts with an empty FIFO and a sample of + ; length 1. It will finish instantly and cause an IRQ, which we + ; ignore, but it + ; will also fill the sample buffer. + lda #SNDCHN_PSGS|SNDCHN_DMC + sta val_4015 + sta $4015 + nop + + ; By that time, 'irqs' should + ; have become 1. + + ; Now we start using IRQs on this one. + ldx #$0F | DMC_IRQMODE + stx DMCFREQ + sta $4015 + cli + ldx #0 + + ; The IRQ handler has started a second playback, and we + ; measure this one. + lda irqs +: + inx + cmp irqs + beq :- + stx irq_lag + + ; irq_lag measurements should ordinarily be in the range of 1 to 54 + ; or thereabouts. But occasionally, the second playback triggers + ; *during* the IRQ handler after the first, and 'irqs' will be 2 at + ; the start of the measurement loop. So at the end of the loop, + ; if the value of 'irqs' was 2 at the start, we were measuring the + ; *third* playback, so we have to compensate for time spent in the + ; second playback's IRQ handler by adding a few eight-cycle units + ; to the elapsed time. + + cmp #1 + bcc not_double_irq + lda #6 ; Increase this or decrease this based on the length of + ; the IRQ handler. +not_double_irq: + + ; Inside the IRQ handler, the compensate subroutine counts up from + ; irq_lag to 256. So we add about 180 or so to convert irq_lag to + ; work as an up-counter. (Other examples of up-counters include + ; the frequency timers on the Game Boy tone generators and the + ; Game Boy Advance PCM channels.) + adc #188 + adc irq_lag + + ; For robustness: counting once is better than counting 250 times. + bcc not_excessive_lag + lda #255 +not_excessive_lag: + sta irq_lag + + ; At this point, we're at a predictable position relative to the + ; phase of the sample clock. + lda #0 + sta irqs + + ; *** End of timer setup code *** + ; At this point, we have about 400 cycles until the next sample + ; fetch. Take advantage of this time to read the controllers + ; without any double clocking interference. +.if ::WITH_CONTROLLERS + ldx #1 + stx cur_keys+1 ; when this is shifted left 8 times, carry will be set + stx $4016 + dex + stx $4016 +padsloop: + ; bit 0: famicom hardwired controller or nes controller + ; bit 1: famicom expansion port controller + ; if either is set, consider the button pressed + lda $4016 + and #$03 + cmp #$01 + rol cur_keys + lda $4017 + and #$03 + cmp #$01 + rol cur_keys+1 + bcc padsloop + + ; Let player 1 control the camera position. The position will + ; actually get written to the PPU during the IRQ handler. + + lda cur_keys + and #$08 + beq not_up + lda camera_y + beq not_up + dec camera_y +not_up: + + lda cur_keys + and #$04 + beq not_down + lda camera_y + cmp #80 + beq not_down + inc camera_y +not_down: + + lda cur_keys + and #$02 + beq not_left + lda camera_x + beq not_left + dec camera_x +not_left: + + lda cur_keys + and #$01 + beq not_right + lda camera_x + cmp #216 + beq not_right + inc camera_x +not_right: +.else + + ; If we have the demo configured not to read the controllers, + ; move the camera in a diamond pattern instead. + lda #$80 ; nametable select bits go here + sta lastPPUCTRL + lda nmis + cmp #128 + bcc :+ + eor #$FF +: + lsr a + sta camera_y + + lda nmis + clc + adc #64 + cmp #128 + bcc :+ + eor #$FF +: + lsr a + sta camera_x +.endif + + pla + tax + pla + rti +.endproc + +.proc reset_handler + sei ; IRQs while initializing hardware: OFF! + ldx #$00 + stx PPUCTRL ; Vblank NMI: OFF! + stx PPUMASK ; Rendering: OFF! + stx $4015 ; DPCM and tone generators: OFF! + stx val_4015 ; ISR functionality: OFF! + lda #$40 + sta $4017 ; APU IRQ: OFF! + lda $4015 ; APU IRQ: ACK! + cld ; Decimal mode on famiclones: OFF! + lda PPUSTATUS ; Vblank NMI: ACK! + dex + txs ; Stack: TOP! + +vwait1: + lda PPUSTATUS + bpl vwait1 + + ; Clear zero page and sprite page + ; (the demo doesn't use anything else) + ldx #0 + ldy #$00 + lda #$f0 + tay +clear_zeropage: + sty $00,x + sta OAM,x + inx + bne clear_zeropage + + ; Set up those variables that are actually used + lda #0 + sta camera_x + lda #40 + sta camera_y + lda #<(sampledata >> 6) + sta DMCADDR +vwait2: + lda PPUSTATUS + bpl vwait2 + + ; Clear the nametables + lda #$24 + sta PPUADDR + stx PPUADDR + txa +clear_vram: + .repeat 8 + sta PPUDATA + .endrepeat + inx + bne clear_vram + + lda #$20 + ldy #$00 + sty OAM + sty OAM+2 + iny + sta PPUADDR + sty PPUADDR + iny + sty PPUDATA + sty OAM+1 + lda #8 + sta OAM+3 + sta OAM+2 + + ; Write a green-tinted palette to the PPU to show that the program + ; counter has reached this point + lda #$3F + sta PPUADDR + ldx #$00 + stx PPUADDR +set_initial_palette: + lda initial_palette,x + sta PPUDATA + inx + cpx #32 + bcc set_initial_palette + + jsr sayHello + cli +: + jmp :- + +.endproc + +.proc sayHello + lineIdx = 2 + vramDstHi = 3 + vramDstLo = 4 + + lda #0 + sta lineIdx + lda #$20 + sta vramDstHi + lda #$A2 + sta vramDstLo + +lineloop: + ldx lineIdx + lda helloLines,x + sta 0 + inx + lda helloLines,x + sta 1 + inx + stx lineIdx + ora #0 ; skip null pointers + beq skipLine + lda vramDstHi + ldx vramDstLo + jsr puts +skipLine: + + lda vramDstLo + clc + adc #64 + sta vramDstLo + lda vramDstHi + adc #0 + sta vramDstHi + lda lineIdx + cmp #20 + bcc lineloop + + lda PPUSTATUS + lda #$80 + sta PPUCTRL + lda nmis +: + cmp nmis + beq :- + lda PPUSTATUS + + rts +.endproc + +.proc puts + sta PPUADDR + stx PPUADDR + pha + txa + pha + ldy #0 +copyloop1: + lda (0),y + beq after_copyloop1 + asl a + sta PPUDATA + iny + bne copyloop1 +after_copyloop1: + + pla + clc + adc #32 + tax + pla + adc #0 + sta PPUADDR + stx PPUADDR + ldy #0 +copyloop2: + lda (0),y + beq after_copyloop2 + sec + rol a + sta PPUDATA + iny + bne copyloop2 +after_copyloop2: + rts +.endproc + +.segment "RODATA" +initial_palette: + .byt $0A,$1A,$2A,$3A,$0A,$1A,$2A,$3A,$0A,$1A,$2A,$3A,$0A,$1A,$2A,$3A + .byt $0A,$16,$26,$36,$0A,$1A,$2A,$3A,$0A,$1A,$2A,$3A,$0A,$1A,$2A,$3A +helloLines: + .addr hello1, hello2, 0, hello4, hello5, hello6, hello7 + .addr hello8, hello9, hello10 + +; [ ] +hello1: .byt "DPCM Letterbox Demo v2",0 +hello2: .byt "Copr. 2010 Damian Yerrick",0 +hello4: .byt "This 16:9 window (scroll it",0 +hello5: .byt "with the Control Pad) uses",0 +hello6: .byt "no mapper IRQ. Instead, it",0 +hello7: .byt "uses the DMC IRQ as a crude",0 +hello8: .byt "timer to hide the top and",0 +hello9: .byt "bottom of the background.",0 +hello10: .byt "Too bad it can't do 720p :(",0 + + +.segment "DMC" +.align 64 +sampledata: + .res 17,$00 + diff --git a/dpcmletterbox/tilesets/ac16.png b/dpcmletterbox/tilesets/ac16.png new file mode 100644 index 0000000..05a32c4 Binary files /dev/null and b/dpcmletterbox/tilesets/ac16.png differ diff --git a/dpcmletterbox/tools/pilbmp2nes.py b/dpcmletterbox/tools/pilbmp2nes.py new file mode 100644 index 0000000..fd0ad32 --- /dev/null +++ b/dpcmletterbox/tools/pilbmp2nes.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python +from __future__ import with_statement +from PIL import Image +from time import sleep + +def formatTilePlanar(tile, nPlanes): + import array + if (tile.size != (8, 8)): + return None + pixels = iter(tile.getdata()) + outplanes = [array.array('B') + for i in range(nPlanes)] + for y in range(8): + slivers = [0 for i in range(nPlanes)] + for x in range(8): + px = pixels.next() + for i in range(nPlanes): + slivers[i] = slivers[i] << 1 + if px & 0x01: + slivers[i] = slivers[i] | 1 + px >>= 1 + for i in range(nPlanes): + outplanes[i].append(slivers[i]) + out = "".join(plane.tostring() for plane in outplanes) + return out + +def parse_argv(argv): + from optparse import OptionParser + parser = OptionParser(usage="usage: %prog [options] [-i] INFILE [-o] OUTFILE") + parser.add_option("-i", "--image", dest="infilename", + help="read image from INFILE", metavar="INFILE") + parser.add_option("-o", "--output", dest="outfilename", + help="write CHR data to OUTFILE", metavar="OUTFILE") + parser.add_option("-W", "--tile-width", dest="tileWidth", + help="set width of metatiles", metavar="HEIGHT", + type="int", default=8) + parser.add_option("-H", "--tile-height", dest="tileHeight", + help="set height of metatiles", metavar="HEIGHT", + type="int", default=8) + (options, args) = parser.parse_args(argv[1:]) + + tileWidth = int(options.tileWidth) + if tileWidth <= 0: + raise ValueError("tile width '%d' must be positive" % tileWidth) + + tileHeight = int(options.tileHeight) + if tileHeight <= 0: + raise ValueError("tile height '%d' must be positive" % tileHeight) + + # Fill unfilled roles with positional arguments + argsreader = iter(args) + try: + infilename = options.infilename + if infilename is None: + infilename = argsreader.next() + outfilename = options.infilename + if outfilename is None: + outfilename = argsreader.next() + except StopIteration: + raise ValueError("not enough filenames") + try: + argsreader.next() + raise ValueError("too many filenames") + except StopIteration: + pass + + return (infilename, outfilename, tileWidth, tileHeight) + +argvTestingMode = True + +def main(argv=None): + if argv is None: + import sys + import os + argv = sys.argv + print argv + if (argvTestingMode and len(argv) < 2 + and sys.stdin.isatty() and sys.stdout.isatty()): + argv.extend(raw_input('args:').split()) + try: + (infilename, outfilename, tileWidth, tileHeight) = parse_argv(argv) + except StandardError, e: + import sys + sys.stderr.write("%s: %s\n" % (argv[0], str(e))) + sys.exit(1) + + print "loading", infilename + im = Image.open(infilename) + im.load() + (w, h) = im.size + + out = [] + for mt_y in range(0, h, tileHeight): + for mt_x in range(0, w, tileWidth): + metatile = im.crop((mt_x, mt_y, + mt_x + tileWidth, mt_y + tileHeight)) + for tile_y in range(0, tileHeight, 8): + for tile_x in range(0, tileWidth, 8): + tile = metatile.crop((tile_x, tile_y, + tile_x + 8, tile_y + 8)) + data = formatTilePlanar(tile, 2) + out.append(data) + with open(outfilename, 'wb') as outfp: + outfp.writelines(out) + +if __name__=='__main__': + main() diff --git a/exram/mmc5exram.asm b/exram/mmc5exram.asm new file mode 100644 index 0000000..c9eb821 --- /dev/null +++ b/exram/mmc5exram.asm @@ -0,0 +1,379 @@ +;NES Copper Bars demo +;Run program for description +;Quietust, 2004/10/06 + +.org $0000 +ptr: .block 2 ;for initializing VRAM +vbflag: .block 1 ;signal when we've gotten an NMI +overflow: .block 1 ;1/3 cycle counter + +rbars: .block $06 ;this holds the position of each bar +ctabl: .block $02 ;(padding) + .block $C0 ;this stores the colour of each scanline + +.org $C000 +.fill $E000-*,$FF +text: +.db " " +.db " MMC5 Executable ExRAM Test " +.db "________________________________" +.db " " +.db " This is basically just my " +.db " 'copper bars' test program, " +.db " except I've modified it so " +.db " the code that determines " +.db " where each color bar should " +.db " be displayed during each " +.db " frame is not executed from " +.db " ROM, but is copied into the " +.db " MMC5's ExRAM during startup " +.db " and then executed from there " +.db " during each VBLANK. " +.db " " +.db " A proper emulator will be " +.db " able to handle this without " +.db " any problems, including both " +.db " Nintendulator and Nestopia. " +.db " In Nintendulator, though, " +.db " the debugger will display FF " +.db " even though it's executing " +.db " real code, since it thinks " +.db " the region contains I/O " +.db " registers rather than RAM. " +.db "________________________________" +.db " " +.db " Written by Quietust " +.db " " +.db $00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00 + + +rast_start1: +.org $5C00 +rast_start2: + ;Raster bar table generator + ;written by Kevin Horton + +do_rast: LDX #$00 ;updates the raster buffer + TXA + +clrast: STA ctabl,x ;clear out all rasters + INX + CPX #$C2 + BNE clrast + + CLC ;only have to do this once here + LDX #$05 ;# of raster bars to do (minus 1) + +dr_loop: LDA rbars,x + INC rbars,x ;get & inc raster bar + TAY + LDA sinetab,y ;get position + TAY + LDA coltab,x ;get starting colour + ORA #$80 + STA ctabl+0,y ;store the 1s + STA ctabl+4,y + ORA #$40 + STA ctabl+1,y ;then the 2s + STA ctabl+3,y + ORA #$20 + STA ctabl+2,y ;then 3, for "12321" + DEX + BPL dr_loop + RTS + +rast_init: LDX #$05 ;initalizes the raster bar positions +ri1: TXA + ASL A + ASL A + ASL A + ASL A + STA rbars,x + DEX + BPL ri1 + + LDX #$00 + TXA + +ri2: STA ctabl,x ;clear out all raster bars + INX + BPL ri2 + RTS +rast_end2: +.org rast_start1 + (rast_end2 - rast_start2) +rast_end1: + +coltab: +.db $14,$10,$18,$08,$0C,$04 + +sinetab: +.db $5F,$61,$64,$66,$68,$6B,$6D,$6F,$72,$74,$76,$78,$7B,$7D,$7F,$81 +.db $83,$85,$88,$8A,$8C,$8E,$90,$92,$94,$96,$98,$99,$9B,$9D,$9F,$A1 +.db $A2,$A4,$A5,$A7,$A8,$AA,$AB,$AD,$AE,$AF,$B0,$B2,$B3,$B4,$B5,$B6 +.db $B7,$B8,$B8,$B9,$BA,$BB,$BB,$BC,$BC,$BD,$BD,$BD,$BE,$BE,$BE,$BE +.db $BE,$BE,$BE,$BE,$BE,$BD,$BD,$BD,$BC,$BC,$BB,$BB,$BA,$B9,$B8,$B8 +.db $B7,$B6,$B5,$B4,$B3,$B2,$B0,$AF,$AE,$AD,$AB,$AA,$A8,$A7,$A5,$A4 +.db $A2,$A1,$9F,$9D,$9B,$99,$98,$96,$94,$92,$90,$8E,$8C,$8A,$88,$85 +.db $83,$81,$7F,$7D,$7B,$78,$76,$74,$72,$6F,$6D,$6B,$68,$66,$64,$61 +.db $5F,$5D,$5A,$58,$56,$53,$51,$4F,$4C,$4A,$48,$46,$43,$41,$3F,$3D +.db $3B,$39,$36,$34,$32,$30,$2E,$2C,$2A,$28,$26,$25,$23,$21,$1F,$1D +.db $1C,$1A,$19,$17,$16,$14,$13,$11,$10,$0F,$0E,$0C,$0B,$0A,$09,$08 +.db $07,$06,$06,$05,$04,$03,$03,$02,$02,$01,$01,$01,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$01,$01,$01,$02,$02,$03,$03,$04,$05,$06,$06 +.db $07,$08,$09,$0A,$0B,$0C,$0E,$0F,$10,$11,$13,$14,$16,$17,$19,$1A +.db $1C,$1D,$1F,$21,$23,$25,$26,$28,$2A,$2C,$2E,$30,$32,$34,$36,$39 +.db $3B,$3D,$3F,$41,$43,$46,$48,$4A,$4C,$4F,$51,$53,$56,$58,$5A,$5D + +nmi: PHA + TXA + PHA + TYA + PHA + LDA #$00 + STA $2003 + LDA #$02 + STA $4014 + LDA #$01 + STA vbflag + LDA #$98 + STA $2000 + LDA #$1E + STA $2001 + LDA #$00 + STA $2005 + STA $2005 + JSR do_rast + PLA + TAY + PLA + TAX + PLA +irq: RTI + +reset: SEI + CLD + LDA #$00 + STA $2000 + STA $2001 + LDX #$02 +waitframe: LDA $2002 + BPL waitframe + DEX + BPL waitframe + TXS + + LDA #$00 + TAX +clr_ram: STA $00,X + STA $0200,X + STA $0300,X + STA $0400,X + STA $0500,X + STA $0600,X + STA $0700,X + INX + BNE clr_ram + + LDA #$03 + STA $5100 + LDA #$00 + STA $5101 + LDA #$02 + STA $5104 + LDA #$44 + STA $5105 + LDA #$00 + STA $5127 + STA $512B + STA $5200 + STA $5204 + + LDX #0 +exram_init: LDA rast_start1,X + STA rast_start2,X + INX + CPX #rast_end2-rast_start2 + BNE exram_init + + LDX #$20 + STX $2006 + LDY #$00 + STY $2006 + + LDA #text & $FF + STA ptr + LDA #text >> 8 + STA ptr+1 + + LDX #$04 +init_vram1: LDA (ptr),Y ;load text for primary NT + STA $2007 + INY + BNE init_vram1 + INC ptr+1 + DEX + BNE init_vram1 + + LDX #$04 + LDA #$FF +init_vram2: STA $2007 ;load white for secondary NT + INY + BNE init_vram2 + DEX + BNE init_vram2 + + LDX #$00 + LDA #$F8 +spr_init: STA $0200,X ;move all sprites offscreen + INX + INX + INX + INX + BNE spr_init + + LDA #$0F ;except sprite 0, which we'll be using for hit + STA $0200 + LDA #'_' + STA $0201 ;$0202 and $0203 are already zero, which is just the way I want them + + LDX #$3F + LDY #$00 + STX $2006 + STY $2006 + + LDX #$08 +palloop: LDA #$1F + STA $2007 + LDA #$1F + STA $2007 + LDY #$00 + STY $2007 + LDY #$30 + STY $2007 + DEX + BNE palloop + + LDA #$3F + STA $2006 + LDA #$00 + STA $2006 + STA $2006 + STA $2006 + + STA $2005 + STA $2005 ;clear scroll regs + STA $4015 ;kill sound + STA vbflag + LDA #$C0 + STA $4017 ;disable frame IRQs + JSR rast_init + LDA #$98 + STA $2000 + CLI + +loop: LDA vbflag + BEQ loop + LDA #$00 + STA vbflag + +spr0off: BIT $2002 + BVS spr0off +spr0on: BIT $2002 + BVC spr0on + LDX #$FF + STX overflow + INX + LDY #$13 +sync: DEY + BNE sync +scanloop: LDA ctabl+2,x ;4 + ;cut 6 cycles + ASL A ;12 + BCC color0 ;14 + ASL A ;16 + BCC color1 ;18 + ASL A ;20 + BCC color2 ;22 + NOP ;24 + NOP ;26 + NOP ;28 + ORA #$01 ;30 + NOP ;32 + NOP ;34 + LDY #$0A ;36 +c3w: DEY ;56 + BNE c3w ;85 + LDY #$99 ;insert them + STY $2000 ;here + JMP endloop ;88 + +color2: ;23 + ORA #$01 ;25 + NOP ;27 + NOP ;29 + LDY #$0B ;31 +c2w: DEY ;53 + BNE c2w ;85 + LDY #$98 ;and + STY $2000 ;here + JMP endloop ;88 + +color1: ;19 + ASL A ;21 + NOP ;23 + NOP ;25 + NOP ;27 + NOP ;29 + LDY #$0B ;31 +c1w: DEY ;53 + BNE c1w ;85 + LDY #$80 ;and + STY $2000 ;here + JMP endloop ;88 + +color0: ;15 + LDA #$00 ;17 + LDY #$0E ;19 +c0w: DEY ;47 + BNE c0w ;88 + LDY #$98 ;and + STY $2000 ;here + +endloop: ;88 + ORA #$1E ;90 + STA $2001 ;94 + + CLC ;96 + LDA overflow ;99 + ADC #$55 ;101 + STA overflow ;104 + BCC skip ;106+2/3 +skip: + + INX ;108+2/3 + CPX #$BF ;110+2/3 + BNE scanloop ;113+2/3 + + LDY #$11 +endw: DEY + BNE endw + + LDX #$1E + LDY #$98 + STX $2001 + STY $2000 + JMP loop + +.fill $FFFA-*,$FF +.org $FFFA +.dw nmi +.dw reset +.dw irq +.end diff --git a/exram/mmc5exram.chr b/exram/mmc5exram.chr new file mode 100644 index 0000000..9ee9ec6 Binary files /dev/null and b/exram/mmc5exram.chr differ diff --git a/exram/mmc5exram.imk b/exram/mmc5exram.imk new file mode 100644 index 0000000..a7d2335 --- /dev/null +++ b/exram/mmc5exram.imk @@ -0,0 +1,6 @@ +MAKEINES +mmc5exram.nes +MAPR 5 +VMIR 1 +PROG mmc5exram.obj +CHAR mmc5exram.chr diff --git a/exram/mmc5exram.nes b/exram/mmc5exram.nes new file mode 100644 index 0000000..5287c77 Binary files /dev/null and b/exram/mmc5exram.nes differ diff --git a/full_palette/flowing_palette.nes b/full_palette/flowing_palette.nes new file mode 100644 index 0000000..af370d7 Binary files /dev/null and b/full_palette/flowing_palette.nes differ diff --git a/full_palette/full_palette.nes b/full_palette/full_palette.nes new file mode 100644 index 0000000..eb4f841 Binary files /dev/null and b/full_palette/full_palette.nes differ diff --git a/full_palette/full_palette.s b/full_palette/full_palette.s new file mode 100644 index 0000000..e4a3e1a --- /dev/null +++ b/full_palette/full_palette.s @@ -0,0 +1 @@ +; Displays entire 400+ color NTSC NES palette on screen. ; Disables PPU rendering so that current scanline color can be ; set directly by VRAM address, then uses cycle-timed code to ; cycle through all colors in a clean grid. ; ; ca65 -o full_palette.o full_palette.s ; ld65 -t nes full_palette.o -o full_palette.nes ; ; Shay Green .segment "HEADER" .byte "NES",26, 2,1, 0,0 .segment "VECTORS" .word 0,0,0, nmi, reset, irq .segment "CHARS" .res 8192 .segment "STARTUP" ; avoids warning .segment "CODE" even_frame = $200 irq: nmi: rti wait_vbl: bit $2002 : bit $2002 bpl :- rts blacken_palette: ; Fill palette with black. Starts at $3FE0 so that VRAM ; address will wrap around to 0 afterwards, so that BG ; rendering will work correctly. lda #$3F sta $2006 lda #$E0 sta $2006 lda #$0F ldy #$20 : sta $2007 dey bne :- rts reset: sei ldx #$FF txs ; Init PPU jsr wait_vbl jsr wait_vbl lda #0 sta $2000 sta $2001 jsr blacken_palette ; Clear nametable lda #$20 sta $2006 lda #$00 sta $2006 ldx #4 ldy #0 : sta $2007 iny bne :- dex bne :- ; Synchronize precisely to VBL. VBL occurs every 29780.67 ; CPU clocks. Loop takes 27 clocks. Every 1103 iterations, ; the second LDA $2002 will read exactly 29781 clocks ; after a previous read. Thus, the loop will effectively ; read $2002 one PPU clock later each frame. It starts out ; with VBL beginning sometime after this read, so that ; eventually VBL will begin just before the $2002 read, ; and thus leave CPU exactly synchronized to VBL. jsr wait_vbl nop : nop lda $2002 lda $2002 pha pla pha pla bpl :- lda #0 sta even_frame begin_frame: jsr blacken_palette ; Enable BG so that PPU will make every other frame ; shorter by one PPU clock. This allows our code to ; synchronize better and reduce horizontal shaking. lda #$08 sta $2001 ; Delay 4739 cycles, well into frame ldx #4 ldy #176 : dey bne :- dex bne :- nop ; Disable BG. Now electron beam color can be set by ; VRAM address pointing into palette. lda #0 sta $2001 ; Draw palette ldy #0 ; Y = color triplet: ; Draws one scanline of palette. Takes 106 cycles. .macro draw_row nop nop nop tya and #$18 asl a ldx #$3F stx $2006 stx $2006 tax stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 .endmacro draw_row ; Palette writes are delayed a line, since VRAM address ; increments just after $2007 write. So we don't set ; color tint until after first row of triplet tya and #$E0 sta $2001 draw_row iny iny iny nop draw_row iny beq :+ ; loop is more than 128 bytes, argh jmp triplet : nop ; Delay 2869 cycles ldy #239 : pha pla dey bne :- ; Delay extra cycle every other frame inc even_frame lda even_frame lsr a bcs :+ : jmp begin_frame \ No newline at end of file diff --git a/full_palette/full_palette_smooth.nes b/full_palette/full_palette_smooth.nes new file mode 100644 index 0000000..2b276e1 Binary files /dev/null and b/full_palette/full_palette_smooth.nes differ diff --git a/full_palette/full_palette_smooth.s b/full_palette/full_palette_smooth.s new file mode 100644 index 0000000..a3d0173 --- /dev/null +++ b/full_palette/full_palette_smooth.s @@ -0,0 +1 @@ +; Displays entire 400+ color NTSC NES palette on screen. ; Disables PPU rendering so that current scanline color can be ; set directly by VRAM address, then uses cycle-timed code to ; cycle through all colors in a clean grid. ; ; ca65 -o full_palette_smooth.o full_palette_smooth.s ; ld65 -t nes full_palette_smooth.o -o full_palette_smooth.nes ; ; Shay Green .segment "HEADER" .byte "NES",26, 2,1, 0,0 .segment "VECTORS" .word 0,0,0, nmi, reset, irq .segment "CHARS" .res 8192 .segment "STARTUP" ; avoids warning .segment "CODE" even_frame = $200 irq: nmi: rti wait_vbl: bit $2002 : bit $2002 bpl :- rts blacken_palette: ; Fill palette with black. Starts at $3FE0 so that VRAM ; address will wrap around to 0 afterwards, so that BG ; rendering will work correctly. lda #$3F sta $2006 lda #$E0 sta $2006 lda #$0F ldy #$20 : sta $2007 dey bne :- rts reset: sei ldx #$FF txs ; Init PPU jsr wait_vbl jsr wait_vbl lda #0 sta $2000 sta $2001 jsr blacken_palette ; Clear nametable lda #$20 sta $2006 lda #$00 sta $2006 ldx #4 ldy #0 : sta $2007 iny bne :- dex bne :- ; Synchronize precisely to VBL. VBL occurs every 29780.67 ; CPU clocks. Loop takes 27 clocks. Every 1103 iterations, ; the second LDA $2002 will read exactly 29781 clocks ; after a previous read. Thus, the loop will effectively ; read $2002 one PPU clock later each frame. It starts out ; with VBL beginning sometime after this read, so that ; eventually VBL will begin just before the $2002 read, ; and thus leave CPU exactly synchronized to VBL. jsr wait_vbl nop : nop lda $2002 lda $2002 pha pla pha pla bpl :- lda #0 sta even_frame begin_frame: jsr blacken_palette ; Enable BG so that PPU will make every other frame ; shorter by one PPU clock. This allows our code to ; synchronize better and reduce horizontal shaking. lda #$08 sta $2001 ; Delay 4739 cycles, well into frame ldx #4 ldy #176 : dey bne :- dex bne :- nop ; Disable BG. Now electron beam color can be set by ; VRAM address pointing into palette. lda #0 sta $2001 ; Draw palette ldy #$C0 ; Y = color triplet: ; Draws one scanline of palette. Takes 98 cycles. .macro draw_row tya and #$30 ldx #$3F stx $2006 stx $2006 tax stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 inx stx $2007 .endmacro nop nop nop nop draw_row ; Palette writes are delayed a line, since VRAM address ; increments just after $2007 write. So we don't set ; color tint until after first row of triplet tya lsr a and #$07 tax lda tints,x sta $2001 draw_row pha pla pha pla nop draw_row iny beq :+ ; loop is more than 128 bytes, argh jmp triplet : nop ; Delay 2869 cycles ldy #239 : pha pla dey bne :- ; Delay extra cycle every other frame inc even_frame lda even_frame lsr a bcs :+ : jmp begin_frame ; Reorder color tints to be most gradual tints: .byte $E0,$C0,$A0,$60,$20,$40,$80,$00 \ No newline at end of file diff --git a/instr_misc/instr_misc.nes b/instr_misc/instr_misc.nes new file mode 100644 index 0000000..5db3286 Binary files /dev/null and b/instr_misc/instr_misc.nes differ diff --git a/instr_misc/readme.txt b/instr_misc/readme.txt new file mode 100644 index 0000000..a3c0b84 --- /dev/null +++ b/instr_misc/readme.txt @@ -0,0 +1,107 @@ +NES CPU Instruction Behavior Misc Tests +---------------------------------------- +These tests verify miscellaneous instruction behavior. + + +01-abs_x_wrap +------------- +Verifies that $FFFF wraps around to 0 for STA abs,X and LDA abs,X. + + +02-branch_wrap +-------------- +Verifies that branching past end or before beginning of RAM wraps +around. + + +03-dummy_reads +-------------- +Tests some instructions that do dummy reads before the real read/write. +Doesn't test all instructions. + +Tests LDA and STA with modes (ZP,X), (ZP),Y and ABS,X +Dummy reads for the following cases are tested: + +LDA ABS,X or (ZP),Y when carry is generated from low byte +STA ABS,X or (ZP),Y +ROL ABS,X always + + +04-dummy_reads_apu +------------------ +Tests dummy reads for (hopefully) ALL instructions which do them, +including unofficial ones. Prints opcode(s) of failed instructions. +Requires that APU implement $4015 IRQ flag reading. + + +Multi-tests +----------- +The NES/NSF builds in the main directory consist of multiple sub-tests. +When run, they list the subtests as they are run. The final result code +refers to the first sub-test that failed. For more information about any +failed subtests, run them individually from rom_singles/ and +nsf_singles/. + + +Flashes, clicks, other glitches +------------------------------- +If a test prints "passed", it passed, even if there were some flashes or +odd sounds. Only a test which prints "done" at the end requires that you +watch/listen while it runs in order to determine whether it passed. Such +tests involve things which the CPU cannot directly test. + + +Text output +----------- +Tests generally print information on screen, but also output information +in other ways, in case the PPU doesn't work or there isn't one, as in an +NSF or a NES emulator early in development. + +When building as an NSF, the final result is reported as a series of +beeps (see below). Any important diagnostic bytes are also reported as +beeps, before the final result. + +All text output is written starting at $6004, with a zero-byte +terminator at the end. As more text is written, the terminator is moved +forward, so an emulator can print the current text at any time. + +The test status is written to $6000. $80 means the test is running, $81 +means the test needs the reset button pressed, but delayed by at least +100 msec from now. $00-$7F means the test has completed and given that +result code. + +To allow an emulator to know when one of these tests is running and the +data at $6000+ is valid, as opposed to some other NES program, $DE $B0 +$G1 is written to $6001-$6003. + +See the source code for more information about a particular test and why +it might be failing. Each test has comments anout its operation. + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to prevent the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +In addition to the other text output methods described above, NSF builds +report essential information bytes audibly, including the final result. +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason as listed in the +source code by the corresponding set_code line. Examples: + + Tones Binary Decimal Meaning + - - - - - - - - - - - - - - - - - - - - + low 0 0 passed + low high 01 1 failed + low high low 010 2 error 2 + +-- +Shay Green diff --git a/instr_misc/rom_singles/01-abs_x_wrap.nes b/instr_misc/rom_singles/01-abs_x_wrap.nes new file mode 100644 index 0000000..2f4fb2a Binary files /dev/null and b/instr_misc/rom_singles/01-abs_x_wrap.nes differ diff --git a/instr_misc/rom_singles/02-branch_wrap.nes b/instr_misc/rom_singles/02-branch_wrap.nes new file mode 100644 index 0000000..fcffab2 Binary files /dev/null and b/instr_misc/rom_singles/02-branch_wrap.nes differ diff --git a/instr_misc/rom_singles/03-dummy_reads.nes b/instr_misc/rom_singles/03-dummy_reads.nes new file mode 100644 index 0000000..e11faab Binary files /dev/null and b/instr_misc/rom_singles/03-dummy_reads.nes differ diff --git a/instr_misc/rom_singles/04-dummy_reads_apu.nes b/instr_misc/rom_singles/04-dummy_reads_apu.nes new file mode 100644 index 0000000..5cbbecb Binary files /dev/null and b/instr_misc/rom_singles/04-dummy_reads_apu.nes differ diff --git a/instr_misc/source/01-abs_x_wrap.s b/instr_misc/source/01-abs_x_wrap.s new file mode 100644 index 0000000..2198050 --- /dev/null +++ b/instr_misc/source/01-abs_x_wrap.s @@ -0,0 +1,40 @@ +; Verifies that $FFFF wraps around to 0 for +; STA abs,X and LDA abs,X + +.include "shell.inc" + +main: + setb <0,0 + setb <1,0 + + ldx #1 + lda #$12 + sta $FFFF,x + + ldx #2 + lda #$34 + sta $FFFF,x + + set_test 2,"Write wrap-around failed" + + lda <$00 + cmp #$12 + jne test_failed + + lda <$01 + cmp #$34 + jne test_failed + + set_test 3,"Read wrap-around failed" + + ldx #1 + lda $FFFF,x + cmp #$12 + jne test_failed + + ldx #2 + lda $FFFF,x + cmp #$34 + jne test_failed + + jmp tests_passed diff --git a/instr_misc/source/02-branch_wrap.s b/instr_misc/source/02-branch_wrap.s new file mode 100644 index 0000000..a839cf2 --- /dev/null +++ b/instr_misc/source/02-branch_wrap.s @@ -0,0 +1,37 @@ +; Verifies that branching past end or before beginning +; of RAM wraps around + +.include "shell.inc" + +main: + set_test 2,"Branch from $FF8x to $000x" + setb <0,$E8 ; INX + setb <1,$E8 ; INX + setb <2,$60 ; RTS + ldx #0 + clc + jsr forward + cpx #1 + jne test_failed + + set_test 3,"Branch from $000x to $FFFx" + setb <0,$90 ; BCC + setb <1,-$3F + ldx #0 + clc + jsr <0 + cpx #1 + jne test_failed + + jmp tests_passed + + +.segment "FF80" + .res $40 +; $FFC0 +forward: + .byte $90,$3F ; BCC $3F + inx +return: + inx + rts diff --git a/instr_misc/source/03-dummy_reads.s b/instr_misc/source/03-dummy_reads.s new file mode 100644 index 0000000..4272ece --- /dev/null +++ b/instr_misc/source/03-dummy_reads.s @@ -0,0 +1,104 @@ +; Tests some instructions that do dummy reads before the real read/write. +; Doesn't test all instructions. +; +; Tests LDA and STA with modes (ZP,X), (ZP),Y and ABS,X +; Dummy reads for the following cases are tested: +; +; LDA ABS,X or (ZP),Y when carry is generated from low byte +; STA ABS,X or (ZP),Y +; ROL ABS,X always + +.include "shell.inc" + +no_read: + lda PPUSTATUS +single_read: + jpl test_failed +dummy_read: + lda PPUSTATUS +double_read: + jmi test_failed + lda PPUSTATUS + jmi test_failed +begin: jsr wait_vbl + delay PPU_FRAMELEN + 20 + ldx #$22 + ldy #$22 + rts + +main: jsr begin + + set_test 2,"Test requires $2002 mirroring every 8 bytes to $3FFA" + lda $3FFA + jsr single_read + + set_test 3,"LDA abs,x" + lda $2000,x ; $2022 + jsr single_read + lda $20E0,x ; $2002, $2102 + jsr double_read + lda $20E2,x ; $2004, $2104 + jsr no_read + lda $3FE0,x ; $3F02, $4002 + jsr dummy_read + + set_test 4,"STA abs,x" + sta $2002 + jsr no_read + sta $20E0,x ; $2002, $2102 (write) + jsr dummy_read + sta $20E2,x ; $2004, $2104 (write) + jsr no_read + sta $3FE0,x ; $3F02, $4002 (write) + jsr dummy_read + + set_test 5,"LDA (z),y" + setw addr,$2000 + lda (addr),y ; $2022 + jsr single_read + setw addr,$20E0 + lda (addr),y ; $2002, $2102 + jsr double_read + setw addr,$20E2 + lda (addr),y ; $2004, $2104 + jsr no_read + setw addr,$3FE0 + lda (addr),y ; $3F02, $4002 + jsr dummy_read + + set_test 6,"STA (z),y" + setw addr,$20E0 + sta (addr),y ; $2002, $2102 (write) + jsr dummy_read + setw addr,$20E2 + sta (addr),y ; $2004, $2104 (write) + jsr no_read + setw addr,$3FE0 + sta (addr),y ; $3F02, $4002 (write) + jsr dummy_read + + set_test 7,"LDA (z,x)" + ldx #0 + setw addr,$2002 + lda (addr,x) ; no dummy read + jsr single_read + + set_test 8,"STA (z,x)" + ldx #0 + setw addr,$2002 + sta (addr,x) ; no dummy read + jsr no_read + + set_test 9,"ROL abs" + rol $2022 ; $2022 + ror a + jsr single_read + + set_test 10,"ROL abs,x" + rol $2000,x ; $2022, $2022 + ror a + jsr double_read + rol $3FE0,x ; $3F02, $4002 + jsr dummy_read + + jmp tests_passed diff --git a/instr_misc/source/04-dummy_reads_apu.s b/instr_misc/source/04-dummy_reads_apu.s new file mode 100644 index 0000000..0b3ede0 --- /dev/null +++ b/instr_misc/source/04-dummy_reads_apu.s @@ -0,0 +1,124 @@ +; Tests dummy reads for (hopefully) ALL instructions which do them, +; including unofficial ones. Prints opcode(s) of failed +; instructions. Requires that APU implement $4015 IRQ flag reading. + +.include "shell.inc" + +zp_byte opcode +zp_byte errors + +begin: setb SNDMODE,0 + lda SNDCHN + delay 30000 + setw addr,SNDCHN+3 + rts + +failed: + inc errors + lda opcode + jsr print_a + jsr play_byte + rts + +.macro test_ x_, y_, instr + .local instr_ + lda instr_ + sta opcode + jsr begin + ldx #x_ + ldy #y_ +instr_: instr + lda SNDCHN + and #$40 +.endmacro + +.macro test_x opcode + test_ 0,-3,{.byte opcode,SNDCHN} + beq :+ + test_ -3,0,{.byte opcode,SNDCHN} + beq :++ +: jsr failed +: +.endmacro + +.macro test_y opcode + test_ -3,0,{.byte opcode,SNDCHN} + beq :+ + test_ 0,-3,{.byte opcode,SNDCHN} + beq :++ +: jsr failed +: +.endmacro + +.macro test_i opcode + test_ -3,0,{.byte opcode,addr} + beq :+ + test_ 0,-3,{.byte opcode,addr} + beq :++ +: jsr failed +: +.endmacro + +.macro test_xyi opcode + test_x opcode + test_y opcode-4 + test_i opcode-12 +.endmacro + +main: + set_test 2,"Official opcodes failed" + + test_xyi $1D ; ORA + test_xyi $3D ; AND + test_xyi $5D ; EOR + test_xyi $7D ; ADC + test_xyi $9D ; STA + test_xyi $BD ; LDA + test_xyi $DD ; CMP + test_xyi $FD ; SBC + + test_x $1E ; ASL + test_x $3E ; ROL + test_x $5E ; LSR + test_x $7E ; ROR + test_x $DE ; DEC + test_x $FE ; INC + + test_x $BC ; LDY + + test_y $BE ; LDX + + lda errors + jne test_failed + + set_test 2,"Unofficial opcodes failed" + + test_x $1C ; SKW + test_x $3C ; SKW + test_x $5C ; SKW + test_x $7C ; SKW + test_x $DC ; SKW + test_x $FC ; SKW + + test_xyi $1F ; ASO + test_xyi $3F ; RLA + test_xyi $5F ; LSE + test_xyi $7F ; RRA + test_xyi $DF ; DCM + test_xyi $FF ; INS + + test_x $9C ; SAY + + test_y $BF ; LAX + test_y $9B ; TAS + test_y $9E ; XAS + test_y $9F ; AXA + test_y $BB ; LAS + + test_i $93 ; AXA + test_i $B3 ; LAX + + lda errors + jne test_failed + + jmp tests_passed diff --git a/instr_misc/source/common/ascii.chr b/instr_misc/source/common/ascii.chr new file mode 100644 index 0000000..2c5b26b Binary files /dev/null and b/instr_misc/source/common/ascii.chr differ diff --git a/instr_misc/source/common/build_rom.s b/instr_misc/source/common/build_rom.s new file mode 100644 index 0000000..dcb7df5 --- /dev/null +++ b/instr_misc/source/common/build_rom.s @@ -0,0 +1,92 @@ +; Builds program as iNES ROM + +; Default is 16K PRG and 8K CHR ROM, NROM (0) + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM=1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM=1 ; Use mapper that supports 8K WRAM in cart +CUSTOM_MAPPER=n ; Specify mapper number +.endif + +.ifndef CUSTOM_MAPPER + .ifdef CART_WRAM + CUSTOM_MAPPER = 2 ; UNROM + .else + CUSTOM_MAPPER = 0 ; NROM + .endif +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte $4E,$45,$53,26 ; "NES" EOF + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte CUSTOM_MAPPER*$10+$01 ; vertical mirroring +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word -1,-1,-1, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .define CHARS "CHARS_PRG" + .segment CHARS + ascii_chr: + + .segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: +.else + .define CHARS "CHARS" + .segment "CHARS_ASCII" + .align $200 + .incbin "ascii.chr" + .res $1800 +.endif + +.segment CHARS + .res $10,0 + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +; Move code to $C000 +.segment "DMC" + .res $4000 + +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + load_ascii_chr + .endif + rts + +post_exit: + jsr set_final_result + jmp forever + +; This helps devcart recover after running test. +; It is never executed by test ROM. +.segment "LOADER" + .incbin "devcart.bin" + +.code +.align 256 diff --git a/instr_misc/source/common/console.s b/instr_misc/source/common/console.s new file mode 100644 index 0000000..036553c --- /dev/null +++ b/instr_misc/source/common/console.s @@ -0,0 +1,331 @@ +; Scrolling text console with line wrapping, 30x29 characters. +; Buffers lines for speed. Will work even if PPU doesn't +; support scrolling (until text reaches bottom). Keeps border +; along bottom in case TV cuts it off. +; +; Defers most initialization until first newline, at which +; point it clears nametable and makes palette non-black. +; +; ** ASCII font must already be in CHR, and mirroring +; must be vertical or single-screen. + +.ifndef CONSOLE_COLOR + CONSOLE_COLOR = $30 ; white +.endif + +console_screen_width = 32 ; if lower than 32, left-justifies + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs. OK if either/both are 0. +console_left_margin = 1 +console_right_margin = 1 + +console_width = console_screen_width - console_left_margin - console_right_margin + +zp_byte console_pos ; 0 to console_width +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_width + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + + setb console_pos,0 + rts + + +; Hides console by disabling PPU rendering and blacking out +; first four entries of palette. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +; Shows console display +; Preserved: X, Y +console_show: + pha + lda #CONSOLE_COLOR + jsr console_show_custom_color_ + pla + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + sty console_temp + + ldy console_pos + cpy #console_width + beq console_full_ + sta console_buf,y + iny + sty console_pos + + ldy console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_scroll_up_ + setb console_pos,0 + pla + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_apply_scroll_ + pla + rts + + +;**** Internal routines **** + +console_full_: + ldy console_temp + + ; Line is full + + ; If space, treat as newline + cmp #' ' + beq console_newline + + ; Wrap current line at appropriate point + pha + tya + pha + jsr console_wrap_ + pla + tay + pla + + jmp console_print + + +; Inserts newline into buffer at appropriate position, leaving +; next line ready in buffer +; Preserved: X, console_temp +console_wrap_: + ; Find beginning of last word + ldy #console_width + lda #' ' +: dey + bmi console_newline + cmp console_buf,y + bne :- + + ; y = 0 to console_width-1 + + ; Flush through current word and put remaining + ; in buffer for next line + jsr console_wait_vbl_ + + ; Time to last PPU write: 207 + 32*(26 + 10) + + lda console_scroll + jsr console_set_ppuaddr_ + + stx console_pos ; save X + + ldx #0 + + ; Print everything before last word +: lda console_buf,x + sta PPUDATA + inx + dey + bpl :- + + ; x = 1 to console_width + + ; Move last word to beginning of buffer, and + ; print spaces for rest of line + ldy #0 + beq :++ +: lda #' ' + sta PPUDATA + lda console_buf,x + inx + sta console_buf,y + iny +: cpx #console_width + bne :-- + + ldx console_pos ; restore X + + ; Append new text after that + sty console_pos + + ; FALL THROUGH + + +; Scrolls up 8 pixels and clears one line BELOW new line +; Preserved: X, console_temp +console_scroll_up_: + ; Scroll up 8 pixels + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + + ; Clear line AFTER that on screen + jsr console_add_8_to_scroll_ + jsr console_set_ppuaddr_ + + ldy #console_width + lda #' ' +: sta PPUDATA + dey + bne :- + ; FALL THROUGH + + +; Applies current scrolling position to PPU +; Preserved: X, Y, console_temp +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + rts + + +; Sets PPU address for row +; In: A = scroll position +; Preserved: X, Y +console_set_ppuaddr_: + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + ora #console_left_margin + sta PPUADDR + rts + + +; A = (A + 8) % 240 +; Preserved: X, Y +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_show_custom_color_: + pha + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + pla + jsr console_load_palette_ + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + rts + + +; Initializes PPU if necessary, then waits for VBL +; Preserved: A, X, Y, console_temp +console_wait_vbl_: + lda console_scroll + cmp #-1 + bne @already_initialized + + ; Deferred initialization of PPU until first use of console + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + tya + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldy #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dey + bne :- + + ; Clear attributes + lda #0 + ldy #$40 +: sta PPUDATA + dey + bne :- + + pla + tay + + jsr console_show +@already_initialized: + jmp wait_vbl_optional + + +; Flushes current line +; Preserved: X, Y +console_flush_: + lda console_scroll + jsr console_set_ppuaddr_ + + sty console_temp + + ; Copy line + ldy #0 + beq :++ +: lda console_buf,y + sta PPUDATA + iny +: cpy console_pos + bne :-- + + ldy console_temp + rts diff --git a/instr_misc/source/common/crc.s b/instr_misc/source/common/crc.s new file mode 100644 index 0000000..de96c2a --- /dev/null +++ b/instr_misc/source/common/crc.s @@ -0,0 +1,118 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on + + +; EQ if checksum matches CRC +; Out: A=0 and EQ if match, A>0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/instr_misc/source/common/delay.s b/instr_misc/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/instr_misc/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/instr_misc/source/common/devcart.bin b/instr_misc/source/common/devcart.bin new file mode 100644 index 0000000..6c515be Binary files /dev/null and b/instr_misc/source/common/devcart.bin differ diff --git a/instr_misc/source/common/macros.inc b/instr_misc/source/common/macros.inc new file mode 100644 index 0000000..d4cb72e --- /dev/null +++ b/instr_misc/source/common/macros.inc @@ -0,0 +1,169 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro inxy + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro diff --git a/instr_misc/source/common/neshw.inc b/instr_misc/source/common/neshw.inc new file mode 100644 index 0000000..814d772 --- /dev/null +++ b/instr_misc/source/common/neshw.inc @@ -0,0 +1,43 @@ +; NES I/O locations and masks + +; Clocks per second +.ifndef CLOCK_RATE + CLOCK_RATE = 1789773 ; NTSC +; CLOCK_RATE = 1662607 ; PAL +.endif + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 + +.if CLOCK_RATE = 1789773 + PPU_FRAMELEN = 29781 +.elseif CLOCK_RATE = 1662607 + PPU_FRAMELEN = 33248 +.endif diff --git a/instr_misc/source/common/ppu.s b/instr_misc/source/common/ppu.s new file mode 100644 index 0000000..21cad12 --- /dev/null +++ b/instr_misc/source/common/ppu.s @@ -0,0 +1,142 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts diff --git a/instr_misc/source/common/print.s b/instr_misc/source/common/print.s new file mode 100644 index 0000000..29fbc23 --- /dev/null +++ b/instr_misc/source/common/print.s @@ -0,0 +1,235 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr @nibble + pla + + pha + and #$0F + jsr @nibble + pla + rts + +@nibble: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: A, X, Y +print_dec: + pha + sta print_temp_ + txa + pha + lda print_temp_ + + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jsr print_char + pla + tax + pla + rts + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/instr_misc/source/common/shell.inc b/instr_misc/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/instr_misc/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/instr_misc/source/common/shell.s b/instr_misc/source/common/shell.s new file mode 100644 index 0000000..fcd49a9 --- /dev/null +++ b/instr_misc/source/common/shell.s @@ -0,0 +1,331 @@ +; Common routines and runtime + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +;**** Special globals **** + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +.segment "NVRAM" + ; Beginning of variables not cleared at startup + nvram_begin: + +;**** Code segment setup **** + +.segment "RODATA" + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +; Move code to $E200 ($200 bytes for text output) +.segment "DMC" + .res $2200 + +; Devcart corrupts byte at $E000 when powering off +.segment "CODE" + nop + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "print.s" +.include "delay.s" +.include "crc.s" +.include "testing.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts + +.endif + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sei + cld + ldx #$FF + txs + pha + + setb SNDCHN,0 + .ifndef BUILD_NSF + setb PPUCTRL,0 + .endif + + pla + pha + jsr report_result + ;jsr clear_nvram ; TODO: was this needed for anything? + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +; Clears $0-($100+S) and nv_ram_end-$7FF +clear_ram: + lda #0 + + ; Main pages + tax +: sta 0,x + sta $300,x + sta $400,x + sta $500,x + sta $600,x + sta $700,x + inx + bne :- + + ; Stack except that above stack pointer + tsx + inx +: dex + sta $100,x + bne :- + + ; BSS except nvram + ldx #<__NVRAM_SIZE__ +: sta __NVRAM_LOAD__,x + inx + bne :- + + rts + + +; Clears nvram +clear_nvram: + ldx #<__NVRAM_SIZE__ + beq @empty + lda #0 +: dex + sta __NVRAM_LOAD__,x + bne :- +@empty: + rts + + +; Prints filename and newline, if available, otherwise nothing. +; Preserved: A, X, Y +print_filename: + .ifdef FILENAME_KNOWN + pha + jsr print_newline + setw addr,filename + jsr print_str_addr + jsr print_newline + pla + .endif + rts + +.pushseg +.segment "RODATA" + ; Filename terminated with zero byte. + filename: + .ifdef FILENAME_KNOWN + .incbin "ram:nes_temp" + .endif + .byte 0 +.popseg + + +;**** ROM-specific **** +.ifndef BUILD_NSF + +.include "ppu.s" + +avoid_silent_nsf: +play_byte: + rts + +; Loads ASCII font into CHR RAM +.macro load_ascii_chr + bit PPUSTATUS + setb PPUADDR,$00 + setb PPUADDR,$00 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page +.endmacro + +; Disables interrupts and loops forever +.ifndef CUSTOM_FOREVER +forever: + sei + lda #0 + sta PPUCTRL +: beq :- + .res $10,$EA ; room for code to run loader +.endif + + +; Default NMI +.ifndef CUSTOM_NMI + zp_byte nmi_count + + nmi: + inc nmi_count + rti + + ; Waits for NMI. Must be using NMI handler that increments + ; nmi_count, with NMI enabled. + ; Preserved: X, Y + wait_nmi: + lda nmi_count + : cmp nmi_count + beq :- + rts +.endif + + +; Default IRQ +.ifndef CUSTOM_IRQ + irq: + bit SNDCHN ; clear APU IRQ flag + rti +.endif + +.endif diff --git a/instr_misc/source/common/testing.s b/instr_misc/source/common/testing.s new file mode 100644 index 0000000..ba41f03 --- /dev/null +++ b/instr_misc/source/common/testing.s @@ -0,0 +1,106 @@ +; Utilities for writing test ROMs + +; In NVRAM so these can be used before initializing runtime, +; then runtime initialized without clearing them +nv_res test_code ; code of current test +nv_res test_name,2 ; address of name of current test, or 0 of none + + +; Sets current test code and optional name. Also resets +; checksum. +; Preserved: A, X, Y +.macro set_test code,name + pha + lda #code + jsr set_test_ + .ifblank name + setb test_name+1,0 + .else + .local Addr + setw test_name,Addr + seg_data "RODATA",{Addr: .byte name,0} + .endif + pla +.endmacro + +set_test_: + sta test_code + jmp reset_crc + + +; Initializes testing module +init_testing: + jmp init_crc + + +; Reports that all tests passed +tests_passed: + jsr print_filename + print_str newline,"Passed" + lda #0 + jmp exit + + +; Reports "Done" if set_test has never been used, +; "Passed" if set_test 0 was last used, or +; failure if set_test n was last used. +tests_done: + ldx test_code + jeq tests_passed + inx + bne test_failed + jsr print_filename + print_str newline,"Done" + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + ldx test_code + + ; Treat $FF as 1, in case it wasn't ever set + inx + bne :+ + inx + stx test_code +: + ; If code >= 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/instr_misc/source/common/text_out.s b/instr_misc/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/instr_misc/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +The first time a line of text is completed, the console initializes the +PPU. Most print routines update a running CRC-32 checksum which can be +checked with check_crc. This allows ALL the output to be checked very +easily. If the checksum doesn't match, it is printed, so during +development the code can be run on a NES to get the correct checksum, +which is then typed into the test code. The checksum is also useful when +comparing different outputs; rather than having to examine all the +output, one need only compare checksums. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported by the included code, due to +their complexity. + + +Macros +------ +Some macros are used to make common operations more convenient, defined +in common/macros.inc. The left is equivalent to the right: + + Macro Equivalent + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + setb addr,byte lda #byte + sta addr + + setw addr,word lda #word + sta addr+1 + + ldxy #value ldx #>value + ldy # diff --git a/instr_test-v3/all_instrs.nes b/instr_test-v3/all_instrs.nes new file mode 100644 index 0000000..ff39446 Binary files /dev/null and b/instr_test-v3/all_instrs.nes differ diff --git a/instr_test-v3/official_only.nes b/instr_test-v3/official_only.nes new file mode 100644 index 0000000..5eb86d4 Binary files /dev/null and b/instr_test-v3/official_only.nes differ diff --git a/instr_test-v3/readme.txt b/instr_test-v3/readme.txt new file mode 100644 index 0000000..48f07b4 --- /dev/null +++ b/instr_test-v3/readme.txt @@ -0,0 +1,387 @@ +NES CPU Instruction Behavior Tests +---------------------------------- +These tests verify most instruction behavior fairly thoroughly, +including unofficial instructions. Failing instructions are listed by +their opcode and name. Serious errors in behavior of basic opcodes might +cause many false errors. These tests will NOT help you figure out what +is wrong with your implementation of the failed instructions, simply +whether you are failing any, and which those are. + +all_instrs.nes tests (almost) all instructions, including unofficial +ones, while official_only.nes tests only official ("documented") +instructions. The *_singles/ test all instructions, but test the +official ones first, so you can tell whether you pass those even if your +emulator hangs on the unofficial ones. + +The nsf_singles builds audibly report the opcodes of any failed +instructions before the final result. + + +Internal operation +------------------ +Instructions are tested by setting many combinations of input values for +registers, flags, and memory, running the instruction under test, then +updating a running checksum with the resulting values. After trying all +interesting input combinations, the checksum is compared with the +correct one to find whether the instruction passed. + +This approach is used for all instructions, even those that shouldn't +care the value of any registers or modify them. This catches an emulator +incorrectly looking at or modifying registers in those instructions. + +This approach makes it very easy to write the tests, since the +instructions don't have to be each coded for separately; instead, only +the different addressing modes need separate tests. + +instrs: what opcodes to test, along with their names. + +instr_template: template for instructions. First byte is replaced with +opcode. After executing instruction and anything after, it should jump +to instr_done. + +operand: where to place byte operand for instruction. This value comes +from the table of values to test, using an index separate from that used +to set the other registers before executing the instruction. + +set_in: things to execute before the instruction. On entry, A is value +put in operand, and Y is index used in table. + +check_out: things to execute after the instruction. + +values2: if defined, set of values to use for operand. Default uses same +set as for other registers. + +test_values: routine to actually run the tests. test_normal does what's +described above. + +correct_checksums: list of checksums for each instruction. Generated +when CALIBRATE=1 is uncommented. + + +Instructions +------------ +U = Unofficial +X = Freezes CPU, so not tested +? = Inconsistent/unknown behavior, so not tested + +00 BRK #n +01 ORA (z,X) +02 X KIL +03 U SLO (z,X) +04 U DOP z +05 ORA z +06 ASL z +07 U SLO z +08 PHP +09 ORA #n +0A ASL A +0B U AAC #n +0C U TOP abs +0D ORA a +0E ASL a +0F U SLO abs +10 BPL r +11 ORA (z),Y +12 X KIL +13 U SLO (z),Y +14 U DOP z,X +15 ORA z,X +16 ASL z,X +17 U SLO z,X +18 CLC +19 ORA a,Y +1A U NOP +1B U SLO abs,Y +1C U TOP abs,X +1D ORA a,X +1E ASL a,X +1F U SLO abs,X +20 JSR a +21 AND (z,X) +22 X KIL +23 U RLA (z,X) +24 BIT z +25 AND z +26 ROL z +27 U RLA z +28 PLP +29 AND #n +2A ROL A +2B U AAC #n +2C BIT a +2D AND a +2E ROL a +2F U RLA abs +30 BMI r +31 AND (z),Y +32 X KIL +33 U RLA (z),Y +34 U DOP z,X +35 AND z,X +36 ROL z,X +37 U RLA z,X +38 SEC +39 AND a,Y +3A U NOP +3B U RLA abs,Y +3C U TOP abs,X +3D AND a,X +3E ROL a,X +3F U RLA abs,X +40 RTI +41 EOR (z,X) +42 X KIL +43 U SRE (z,X) +44 U DOP z +45 EOR z +46 LSR z +47 U SRE z +48 PHA +49 EOR #n +4A LSR A +4B U ASR #n +4C JMP a +4D EOR a +4E LSR a +4F U SRE abs +50 BVC r +51 EOR (z),Y +52 X KIL +53 U SRE (z),Y +54 U DOP z,X +55 EOR z,X +56 LSR z,X +57 U SRE z,X +58 CLI +59 EOR a,Y +5A U NOP +5B U SRE abs,Y +5C U TOP abs,X +5D EOR a,X +5E LSR a,X +5F U SRE abs,X +60 RTS +61 ADC (z,X) +62 X KIL +63 U RRA (z,X) +64 U DOP z +65 ADC z +66 ROR z +67 U RRA z +68 PLA +69 ADC #n +6A ROR A +6B U ARR #n +6C JMP (a) +6D ADC a +6E ROR a +6F U RRA abs +70 BVS r +71 ADC (z),Y +72 X KIL +73 U RRA (z),Y +74 U DOP z,X +75 ADC z,X +76 ROR z,X +77 U RRA z,X +78 SEI +79 ADC a,Y +7A U NOP +7B U RRA abs,Y +7C U TOP abs,X +7D ADC a,X +7E ROR a,X +7F U RRA abs,X +80 U DOP #n +81 STA (z,X) +82 U DOP #n +83 U AAX (z,X) +84 STY z +85 STA z +86 STX z +87 U AAX z +88 DEY +89 U DOP #n +8A TXA +8B ? XAA #n +8C STY a +8D STA a +8E STX a +8F U AAX abs +90 BCC r +91 STA (z),Y +92 X KIL +93 ? AXA (z),Y +94 STY z,X +95 STA z,X +96 STX z,Y +97 U AAX z,Y +98 TYA +99 STA a,Y +9A TXS +9B ? XAS abs,Y +9C U SYA abs,X +9D STA a,X +9E U SXA abs,Y +9F ? AXA abs,Y +A0 LDY #n +A1 LDA (z,X) +A2 LDX #n +A3 U LAX (z,X) +A4 LDY z +A5 LDA z +A6 LDX z +A7 U LAX z +A8 TAY +A9 LDA #n +AA TAX +AB U ATX #n +AC LDY a +AD LDA a +AE LDX a +AF U LAX abs +B0 BCS r +B1 LDA (z),Y +B2 X KIL +B3 U LAX (z),Y +B4 LDY z,X +B5 LDA z,X +B6 LDX z,Y +B7 U LAX z,Y +B8 CLV +B9 LDA a,Y +BA TSX +BB ? LAR abs,Y +BC LDY a,X +BD LDA a,X +BE LDX a,Y +BF U LAX abs,Y +C0 CPY #n +C1 CMP (z,X) +C2 U DOP #n +C3 U DCP (z,X) +C4 CPY z +C5 CMP z +C6 DEC z +C7 U DCP z +C8 INY +C9 CMP #n +CA DEX +CB U AXS #n +CC CPY a +CD CMP a +CE DEC a +CF U DCP abs +D0 BNE r +D1 CMP (z),Y +D2 X KIL +D3 U DCP (z),Y +D4 U DOP z,X +D5 CMP z,X +D6 DEC z,X +D7 U DCP z,X +D8 CLD +D9 CMP a,Y +DA U NOP +DB U DCP abs,Y +DC U TOP abs,X +DD CMP a,X +DE DEC a,X +DF U DCP abs,X +E0 CPX #n +E1 SBC (z,X) +E2 U DOP #n +E3 U ISC (z,X) +E4 CPX z +E5 SBC z +E6 INC z +E7 U ISC z +E8 INX +E9 SBC #n +EA NOP +EB U SBC #n +EC CPX a +ED SBC a +EE INC a +EF U ISC abs +F0 BEQ r +F1 SBC (z),Y +F2 X KIL +F3 U ISC (z),Y +F4 U DOP z,X +F5 SBC z,X +F6 INC z,X +F7 U ISC z,X +F8 SED +F9 SBC a,Y +FA U NOP +FB U ISC abs,Y +FC U TOP abs,X +FD SBC a,X +FE INC a,X +FF U ISC abs,X + + +Multi-tests +----------- +The NES/NSF builds in the main directory consist of multiple sub-tests. +When run, they list the subtests as they are run. The final result code +refers to the first sub-test that failed. For more information about any +failed subtests, run them individually from rom_singles/ and +nsf_singles/. + + +Flashes, clicks, other glitches +------------------------------- +If a test prints "passed", it passed, even if there were some flashes or +odd sounds. Only a test which prints "done" at the end requires that you +watch/listen while it runs in order to determine whether it passed. Such +tests involve things which the CPU cannot directly test. + + +Alternate output +---------------- +Tests generally print information on screen, but also report the final +result audibly, and output text to memory, in case the PPU doesn't work +or there isn't one, as in an NSF or a NES emulator early in development. + +After the tests are done, the final result is reported as a series of +beeps (see below). For NSF builds, any important diagnostic bytes are +also reported as beeps, before the final result. + + +Output at $6000 +--------------- +All text output is written starting at $6004, with a zero-byte +terminator at the end. As more text is written, the terminator is moved +forward, so an emulator can print the current text at any time. + +The test status is written to $6000. $80 means the test is running, $81 +means the test needs the reset button pressed, but delayed by at least +100 msec from now. $00-$7F means the test has completed and given that +result code. + +To allow an emulator to know when one of these tests is running and the +data at $6000+ is valid, as opposed to some other NES program, $DE $B0 +$G1 is written to $6001-$6003. + + +Audible output +-------------- +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason. See the source +code of the test for more information about the meaning of a test code. +They are found after the set_test macro. For example, the cause of test +code 3 would be found in a line containing set_test 3. Examples: + + Tones Binary Decimal Meaning + - - - - - - - - - - - - - - - - - - - - + low 0 0 passed + low high 01 1 failed + low high low 010 2 error 2 + + +-- +Shay Green diff --git a/instr_test-v3/rom_singles/01-implied.nes b/instr_test-v3/rom_singles/01-implied.nes new file mode 100644 index 0000000..6bddb18 Binary files /dev/null and b/instr_test-v3/rom_singles/01-implied.nes differ diff --git a/instr_test-v3/rom_singles/02-immediate.nes b/instr_test-v3/rom_singles/02-immediate.nes new file mode 100644 index 0000000..7244a44 Binary files /dev/null and b/instr_test-v3/rom_singles/02-immediate.nes differ diff --git a/instr_test-v3/rom_singles/03-zero_page.nes b/instr_test-v3/rom_singles/03-zero_page.nes new file mode 100644 index 0000000..2ea31f7 Binary files /dev/null and b/instr_test-v3/rom_singles/03-zero_page.nes differ diff --git a/instr_test-v3/rom_singles/04-zp_xy.nes b/instr_test-v3/rom_singles/04-zp_xy.nes new file mode 100644 index 0000000..89b7eac Binary files /dev/null and b/instr_test-v3/rom_singles/04-zp_xy.nes differ diff --git a/instr_test-v3/rom_singles/05-absolute.nes b/instr_test-v3/rom_singles/05-absolute.nes new file mode 100644 index 0000000..a14419f Binary files /dev/null and b/instr_test-v3/rom_singles/05-absolute.nes differ diff --git a/instr_test-v3/rom_singles/06-abs_xy.nes b/instr_test-v3/rom_singles/06-abs_xy.nes new file mode 100644 index 0000000..d34e417 Binary files /dev/null and b/instr_test-v3/rom_singles/06-abs_xy.nes differ diff --git a/instr_test-v3/rom_singles/07-ind_x.nes b/instr_test-v3/rom_singles/07-ind_x.nes new file mode 100644 index 0000000..56a0ccc Binary files /dev/null and b/instr_test-v3/rom_singles/07-ind_x.nes differ diff --git a/instr_test-v3/rom_singles/08-ind_y.nes b/instr_test-v3/rom_singles/08-ind_y.nes new file mode 100644 index 0000000..d4ea1bb Binary files /dev/null and b/instr_test-v3/rom_singles/08-ind_y.nes differ diff --git a/instr_test-v3/rom_singles/09-branches.nes b/instr_test-v3/rom_singles/09-branches.nes new file mode 100644 index 0000000..047b5c9 Binary files /dev/null and b/instr_test-v3/rom_singles/09-branches.nes differ diff --git a/instr_test-v3/rom_singles/10-stack.nes b/instr_test-v3/rom_singles/10-stack.nes new file mode 100644 index 0000000..0483966 Binary files /dev/null and b/instr_test-v3/rom_singles/10-stack.nes differ diff --git a/instr_test-v3/rom_singles/11-jmp_jsr.nes b/instr_test-v3/rom_singles/11-jmp_jsr.nes new file mode 100644 index 0000000..e4c1884 Binary files /dev/null and b/instr_test-v3/rom_singles/11-jmp_jsr.nes differ diff --git a/instr_test-v3/rom_singles/12-rts.nes b/instr_test-v3/rom_singles/12-rts.nes new file mode 100644 index 0000000..2f308be Binary files /dev/null and b/instr_test-v3/rom_singles/12-rts.nes differ diff --git a/instr_test-v3/rom_singles/13-rti.nes b/instr_test-v3/rom_singles/13-rti.nes new file mode 100644 index 0000000..d8f090b Binary files /dev/null and b/instr_test-v3/rom_singles/13-rti.nes differ diff --git a/instr_test-v3/rom_singles/14-brk.nes b/instr_test-v3/rom_singles/14-brk.nes new file mode 100644 index 0000000..c61f876 Binary files /dev/null and b/instr_test-v3/rom_singles/14-brk.nes differ diff --git a/instr_test-v3/rom_singles/15-special.nes b/instr_test-v3/rom_singles/15-special.nes new file mode 100644 index 0000000..2fa10df Binary files /dev/null and b/instr_test-v3/rom_singles/15-special.nes differ diff --git a/instr_test-v3/source/01-implied.s b/instr_test-v3/source/01-implied.s new file mode 100644 index 0000000..f6ba50d --- /dev/null +++ b/instr_test-v3/source/01-implied.s @@ -0,0 +1,80 @@ +.include "instr_test.inc" +instrs: + entry $2A,"ROL A" ; A = op A + entry $0A,"ASL A" + entry $6A,"ROR A" + entry $4A,"LSR A" + + entry $8A,"TXA" ; AXY = AXY + entry $98,"TYA" + entry $AA,"TAX" + entry $A8,"TAY" + + entry $E8,"INX" ; XY = op XY + entry $C8,"INY" + entry $CA,"DEX" + entry $88,"DEY" + + entry $38,"SEC" ; flags = op flags + entry $18,"CLC" + entry $F8,"SED" + entry $D8,"CLD" + entry $78,"SEI" + entry $58,"CLI" + entry $B8,"CLV" + + entry $EA,"NOP" + +.ifndef OFFICIAL_ONLY + entry $1A,"NOP" + entry $3A,"NOP" + entry $5A,"NOP" + entry $7A,"NOP" + entry $DA,"NOP" + entry $FA,"NOP" +.endif +instrs_size = * - instrs + +instr_template: + nop + jmp instr_done +instr_template_size = * - instr_template + +operand = in_a + +.define set_in set_paxyso +.define check_out check_paxyso + +.include "instr_test_end.s" + +test_values: + test_normal + rts + +correct_checksums: +.dword $B129E6BE +.dword $965A320E +.dword $905D41EE +.dword $51FA7AD7 +.dword $A60AE5B1 +.dword $8FA16B44 +.dword $D311C870 +.dword $453F27CD +.dword $4F91B466 +.dword $604DB29C +.dword $4BCFE982 +.dword $8E0D1602 +.dword $26DBEBEC +.dword $49214BA2 +.dword $8C4FB749 +.dword $37962351 +.dword $99E7216C +.dword $6408D38D +.dword $C334A2A7 +.dword $55827CC6 +.dword $55827CC6 +.dword $55827CC6 +.dword $55827CC6 +.dword $55827CC6 +.dword $55827CC6 +.dword $55827CC6 diff --git a/instr_test-v3/source/02-immediate.s b/instr_test-v3/source/02-immediate.s new file mode 100644 index 0000000..b286e7d --- /dev/null +++ b/instr_test-v3/source/02-immediate.s @@ -0,0 +1,74 @@ +.include "instr_test.inc" + +instrs: + entry $A9,"LDA #n" ; AXY = #n + entry $A2,"LDX #n" + entry $A0,"LDY #n" + + entry $69,"ADC #n" ; A = A op #n + entry $E9,"SBC #n" + entry $09,"ORA #n" + entry $29,"AND #n" + entry $49,"EOR #n" + + entry $C9,"CMP #n" ; AXY op #n + entry $E0,"CPX #n" + entry $C0,"CPY #n" +.ifndef OFFICIAL_ONLY + entry $EB,"SBC #n" + + entry $80,"DOP #n" + entry $82,"DOP #n" + entry $89,"DOP #n" + entry $C2,"DOP #n" + entry $E2,"DOP #n" + + entry $0B,"AAC #n" + entry $2B,"AAC #n" + entry $4B,"ASR #n" + entry $6B,"ARR #n" + entry $AB,"ATX #n" + entry $CB,"AXS #n" +.endif +instrs_size = * - instrs + +operand = instr+1 + +instr_template: + lda #0 + jmp instr_done +instr_template_size = * - instr_template + +.define set_in set_paxyso +.define check_out check_paxyso + +.include "instr_test_end.s" + +test_values: + test_normal + rts + +correct_checksums: +.dword $5D5728B8 +.dword $EA228F76 +.dword $7C0C60CB +.dword $49288BFC +.dword $14C7EA46 +.dword $42684E66 +.dword $EA1D7F06 +.dword $512F9D2A +.dword $70AA1B34 +.dword $D3DC4002 +.dword $6675067C +.dword $14C7EA46 +.dword $6CB13BC0 +.dword $6CB13BC0 +.dword $6CB13BC0 +.dword $6CB13BC0 +.dword $6CB13BC0 +.dword $FE191060 +.dword $FE191060 +.dword $27355577 +.dword $C6B8642B +.dword $D311C870 +.dword $EE21BFAD diff --git a/instr_test-v3/source/03-zero_page.s b/instr_test-v3/source/03-zero_page.s new file mode 100644 index 0000000..8b8cf76 --- /dev/null +++ b/instr_test-v3/source/03-zero_page.s @@ -0,0 +1,93 @@ +.include "instr_test.inc" + +instrs: + entry $A5,"LDA z" ; AXY = z + entry $A6,"LDX z" + entry $A4,"LDY z" + + entry $85,"STA z" ; z = AXY + entry $86,"STX z" + entry $84,"STY z" + + entry $E6,"INC z" ; z = op z + entry $C6,"DEC z" + entry $06,"ASL z" + entry $46,"LSR z" + entry $26,"ROL z" + entry $66,"ROR z" + + entry $65,"ADC z" ; A = A op z + entry $E5,"SBC z" + entry $05,"ORA z" + entry $25,"AND z" + entry $45,"EOR z" + + entry $24,"BIT z" ; AXY op z + entry $C5,"CMP z" + entry $E4,"CPX z" + entry $C4,"CPY z" +.ifndef OFFICIAL_ONLY + entry $04,"DOP z" + entry $44,"DOP z" + entry $64,"DOP z" + + entry $07,"SLO z" + entry $27,"RLA z" + entry $47,"SRE z" + entry $67,"RRA z" + entry $87,"AAX z" + entry $A7,"LAX z" + entry $C7,"DCP z" + entry $E7,"ISC z" +.endif +instrs_size = * - instrs + +operand = <$FE + +instr_template: + lda operand + sta <(address+1) + + lda #<(operand+1) + sta <(address+2) + lda #>(operand+1) + sta <(address+3) + + ; Be sure X doesn't have values other than + ; 0 or 2 + lda #0 + jsr :+ + lda #2 +: sta in_x + lda #$A5 + sta address+1 + sta address+2 + sta address+3 + test_normal + rts + +correct_checksums: +.dword $C7123EFB +.dword $A914111E +.dword $78FDC202 +.dword $727A1EC0 +.dword $0CCBE904 +.dword $918A9806 +.dword $47A2405D +.dword $9D5AE8F0 +.dword $57CC5810 +.dword $686F6585 +.dword $41CCD775 +.dword $1CCC0373 +.dword $54931D9E +.dword $D221ACE3 +.dword $2F5C514E +.dword $47A96694 diff --git a/instr_test-v3/source/08-ind_y.s b/instr_test-v3/source/08-ind_y.s new file mode 100644 index 0000000..cbfa3bd --- /dev/null +++ b/instr_test-v3/source/08-ind_y.s @@ -0,0 +1,87 @@ +.include "instr_test.inc" + +instrs: + entry $B1,"LDA (z),Y" ; A = (z),Y + + entry $91,"STA (z),Y" ; (z),Y = A + + entry $D1,"CMP (z),Y" ; A op (z),Y + + entry $11,"ORA (z),Y" ; A = A op (z),Y + entry $F1,"SBC (z),Y" + entry $71,"ADC (z),Y" + entry $31,"AND (z),Y" + entry $51,"EOR (z),Y" +.ifndef OFFICIAL_ONLY + entry $13,"SLO (z),Y" + entry $33,"RLA (z),Y" + entry $53,"SRE (z),Y" + entry $73,"RRA (z),Y" + entry $B3,"LAX (z),Y" + entry $D3,"DCP (z),Y" + entry $F3,"ISC (z),Y" +.endif +instrs_size = * - instrs + +address = <$FF +operand = $2FF + +instr_template: + lda (address),y + jmp instr_done +instr_template_size = * - instr_template + +.macro set_in + lda values+1,y + sta operand+1 + + lda values+2,y + sta operand+2 + + set_paxyso +.endmacro + +.macro check_out + check_paxyso + + lda operand+1 + jsr update_crc_fast + + lda operand+2 + jsr update_crc_fast + + lda address + jsr update_crc_fast +.endmacro + +.include "instr_test_end.s" + +test_values: + lda #operand + sta <(address+1) + + lda #0 + jsr :+ + lda #1 +: sta in_y + test_normal + rts + +correct_checksums: +.dword $C34014B1 +.dword $AD463B54 +.dword $7CAFE848 +.dword $95D8B24C +.dword $0899C34E +.dword $7628348A +.dword $43F06A17 +.dword $9908C2BA +.dword $539E725A +.dword $6C3D4FCF +.dword $459EFD3F +.dword $189E2939 +.dword $B1EC2D77 +.dword $2B0E7B04 +.dword $43FB4CDE diff --git a/instr_test-v3/source/09-branches.s b/instr_test-v3/source/09-branches.s new file mode 100644 index 0000000..0d9b825 --- /dev/null +++ b/instr_test-v3/source/09-branches.s @@ -0,0 +1,47 @@ +.include "instr_test.inc" + +instrs: + entry $90,"BCC r" ; PC = PC op flags + entry $50,"BVC r" + entry $D0,"BNE r" + entry $30,"BMI r" + entry $10,"BPL r" + entry $F0,"BEQ r" + entry $B0,"BCS r" + entry $70,"BVS r" +instrs_size = * - instrs + +zp_byte operand + +instr_template: + bne :+ + sta operand +: jmp instr_done +instr_template_size = * - instr_template + +values2: + .byte 0,$FF,$01,$02,$04,$08,$10,$20,$40,$80 +values2_size = * - values2 + +.macro set_in + sta in_p + set_paxyso +.endmacro + +.define check_out check_paxyso + +.include "instr_test_end.s" + +test_values: + test_normal + rts + +correct_checksums: +.dword $70FED976 +.dword $423DD402 +.dword $EFA864F5 +.dword $987425C4 +.dword $3095ECE9 +.dword $4749ADD8 +.dword $D81F105B +.dword $EADC1D2F diff --git a/instr_test-v3/source/10-stack.s b/instr_test-v3/source/10-stack.s new file mode 100644 index 0000000..d7f72a7 --- /dev/null +++ b/instr_test-v3/source/10-stack.s @@ -0,0 +1,117 @@ +.include "instr_test.inc" + +instrs: + entry $48,"PHA" + entry $08,"PHP" + + entry $68,"PLA" + entry $28,"PLP" + + entry $9A,"TXS" + entry $BA,"TSX" +instrs_size = * - instrs + +instr_template: + pha + jmp instr_done +instr_template_size = * - instr_template + +values2: + .byte 0,$FF,$01,$02,$04,$08,$10,$20,$40,$80 +values2_size = * - values2 + +zp_byte operand + +.macro set_in + sta in_p + set_paxyso + + ; Clear bytes on stack + stx $17F + sty $180 + stx $181 + + sty $1FE + stx $1FF + sty $100 + stx $101 + sty $102 +.endmacro + +zp_byte save +zp_byte save2 +zp_byte save3 +zp_byte save4 +zp_byte save5 + +.macro check_out + php + sta save ; A + pla + sta save2 ; P + pla + sta save3 ; PLA + stx save4 ; X + tsx + stx save5 ; S + + ldx saved_s + txs + + ; Output + tya + jsr update_crc_fast + + lda save + jsr update_crc_fast + + lda save2 + jsr update_crc_fast + + lda save3 + jsr update_crc_fast + + lda save4 + jsr update_crc_fast + + lda save5 + jsr update_crc_fast + + ldx in_s + dex + lda $100,x + jsr update_crc_fast + + inx + lda $100,x + jsr update_crc_fast + + inx + lda $100,x + jsr update_crc_fast +.endmacro + +.include "instr_test_end.s" + +test_values: + ; Values for SP + lda #$80 + jsr :+ + lda #$00 + jsr :+ + lda #$01 + jsr :+ + lda #$FF + jsr :+ + lda #$FE +: sta in_s + test_normal + rts + +correct_checksums: +.dword $798D2DB5 +.dword $0CA6FC29 +.dword $68C636F1 +.dword $D35DB3D5 +.dword $F1159742 +.dword $A3EBB2D7 diff --git a/instr_test-v3/source/11-jmp_jsr.s b/instr_test-v3/source/11-jmp_jsr.s new file mode 100644 index 0000000..293e78a --- /dev/null +++ b/instr_test-v3/source/11-jmp_jsr.s @@ -0,0 +1,28 @@ +.include "instr_test.inc" + +instrs: + entry $4C,"JMP" + entry $20,"JSR" +instrs_size = * - instrs + +instr_template: + jsr :+ + inx +: inx + jmp instr_done +instr_template_size = * - instr_template + +operand = in_a + +.define set_in set_paxyso +.define check_out check_paxyso + +.include "instr_test_end.s" + +test_values: + test_normal + rts + +correct_checksums: +.dword $4F91B466 +.dword $D794E018 diff --git a/instr_test-v3/source/12-rts.s b/instr_test-v3/source/12-rts.s new file mode 100644 index 0000000..d4b93e5 --- /dev/null +++ b/instr_test-v3/source/12-rts.s @@ -0,0 +1,38 @@ +.include "instr_test.inc" + +instrs: + entry $60,"RTS" +instrs_size = * - instrs + +instr_template: + rts + inx + inx + jmp instr_done +instr_template_size = * - instr_template + +operand = in_a + +.define check_out check_paxyso + +.macro set_in + ; Put return address on stack + ldx in_s + inx + lda #<(instr+1) + sta $100,x + inx + lda #>(instr+1) + sta $100,x + + set_paxyso +.endmacro + +.include "instr_test_end.s" + +test_values: + test_normal + rts + +correct_checksums: +.dword $E1EB954A diff --git a/instr_test-v3/source/13-rti.s b/instr_test-v3/source/13-rti.s new file mode 100644 index 0000000..854db70 --- /dev/null +++ b/instr_test-v3/source/13-rti.s @@ -0,0 +1,41 @@ +.include "instr_test.inc" + +instrs: + entry $40,"RTI" +instrs_size = * - instrs + +instr_template: + rti + inx + inx + jmp instr_done +instr_template_size = * - instr_template + +zp_res operand + +.define check_out check_paxyso + +.macro set_in + ; Put return address and P on stack + ldx in_s + inx + lda operand + sta $100,x + inx + lda #<(instr+1) + sta $100,x + inx + lda #>(instr+1) + sta $100,x + + set_paxyso +.endmacro + +.include "instr_test_end.s" + +test_values: + test_normal + rts + +correct_checksums: +.dword $F4B30222 diff --git a/instr_test-v3/source/14-brk.s b/instr_test-v3/source/14-brk.s new file mode 100644 index 0000000..b40084f --- /dev/null +++ b/instr_test-v3/source/14-brk.s @@ -0,0 +1,47 @@ +CUSTOM_IRQ = 1 +.include "instr_test.inc" + +zp_byte p_inside_brk + +irq: pha + php + pla + sta p_inside_brk + pla + rti + +instrs: + entry $00,"BRK" +instrs_size = * - instrs + +instr_template: + brk + inx + inx + jmp instr_done +instr_template_size = * - instr_template + +operand = in_a + +.macro set_in + set_stack + set_paxyso +.endmacro + +.macro check_out + ; By looking at stack, we verify + ; values BRK pushed on it + check_paxyso + check_stack + lda p_inside_brk + jsr update_crc_fast +.endmacro + +.include "instr_test_end.s" + +test_values: + test_normal + rts + +correct_checksums: +.dword $1392F39C diff --git a/instr_test-v3/source/15-special.s b/instr_test-v3/source/15-special.s new file mode 100644 index 0000000..dc3534e --- /dev/null +++ b/instr_test-v3/source/15-special.s @@ -0,0 +1,90 @@ +CUSTOM_IRQ=1 +.include "shell.inc" + +irq: pla + pha + rti + +jmp_6ff: + .byte $6C ; JMP ($6FF) (to avoid warning) + .word $6FF + +main: + setb SNDMODE,$40 ; disable frame IRQ + + set_test 3,"JMP ($6FF) should get high byte from $600" + setb $6FF,$F0 + setb $600,$07 + setb $700,$06 + setb $7F0,$E8 ; INX + setb $7F1,$60 ; RTS + setb $6F0,$60 ; RTS + ldx #0 + jsr jmp_6ff + cpx #1 + jne test_failed + + set_test 4,"RTS should return to addr+1" + lda #>:+ + pha + lda #<:+ + pha + ldx #0 + rts + inx +: inx + inx + cpx #1 + jne test_failed + + set_test 5,"RTI should return to addr" + lda #>:+ + pha + lda #<:+ + pha + ldx #0 + php + rti + inx +: inx + inx + cpx #2 + jne test_failed + + set_test 6,"JSR should push addr of next instr - 1" + setb $6FE,$20 ; JSR + setb $6FF,<:+ + setb $700,>:+ + jmp $6FE +: pla + cmp #$00 + jne test_failed + pla + cmp #$07 + jne test_failed + + set_test 7,"BRK should push status with bits 4 and 5 set" + lda #$00 + pha + plp + brk + nop + cmp #$30 + jne test_failed + lda #$FF + pha + plp + brk + nop + cmp #$FF + jne test_failed + + set_test 8,"BRK should push address BRK + 2" + ldx #1 + brk + inx + inx + cpx #2 + jne test_failed + + jmp tests_passed diff --git a/instr_test-v3/source/common/ascii.chr b/instr_test-v3/source/common/ascii.chr new file mode 100644 index 0000000..2c5b26b Binary files /dev/null and b/instr_test-v3/source/common/ascii.chr differ diff --git a/instr_test-v3/source/common/bootloader.bin b/instr_test-v3/source/common/bootloader.bin new file mode 100644 index 0000000..b5ca2b3 Binary files /dev/null and b/instr_test-v3/source/common/bootloader.bin differ diff --git a/instr_test-v3/source/common/build_rom.s b/instr_test-v3/source/common/build_rom.s new file mode 100644 index 0000000..b89aaa8 --- /dev/null +++ b/instr_test-v3/source/common/build_rom.s @@ -0,0 +1,131 @@ +; Builds program as iNES ROM + +; Default is 32K PRG and 8K CHR ROM, NROM (0) +; CHR_RAM selects UNROM (2) +; CART_WRAM selects MMC1 (1) + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM = 1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM = 1 ; Use mapper that supports 8K WRAM in cart +MAPPER = n ; Specify mapper number +.endif + +.ifndef MAPPER + .ifdef CART_WRAM + MAPPER = 1 ; MMC1 + .elseif .defined(CHR_RAM) + MAPPER = 2 ; UNROM + .else + MAPPER = 0 ; NROM + .endif +.endif + +.ifndef V_MIRRORING + V_MIRRORING = 1 ; since text console needs it +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte "NES",$1A + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte MAPPER*$10 + V_MIRRORING +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word -1,-1,-1, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .define CHARS "CHARS_PRG" + .segment CHARS + ascii_chr: + + .segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: +.else + .define CHARS "CHARS" + .segment "CHARS_ASCII" + .align $200 + .incbin "ascii.chr" + .res $1800 +.endif + +.segment CHARS + .res $10,0 + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +.ifndef LARGER_ROM_HACK +.segment "CODE" + .res $4000 +.endif + +.include "shell.s" + +std_reset: +.if MAPPER = 1 + ; Some writes to odd addresses to work + ; with my Ultima devcart + lda #$80 + sta $8001 + + ; Vertical mirroring, 8K CHR, WRAM enabled, all 32K mapped + lda #$0E<<1 ; $0E +: lsr a + sta $8000 ; 0E 07 03 01 00 + bne :- + + lda #04 ; $00 +: sta $A001 ; 04 08 10 20 40 + asl a + bpl :- + + lda #$05 ; $01 +: sta $C001 ; 05 0A 14 28 50 + asl a + bpl :- + + lda #04 ; $00 +: sta $E000 ; 04 08 10 20 40 + asl a + bpl :- + +.endif + + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + load_chr_ram + .endif + rts + +post_exit: + jsr set_final_result + jsr play_hex + jmp forever + +; Standard NES bootloader to help with devcart. +; It is never executed by test ROM. +.segment "LOADER" + .incbin "bootloader.bin" + +.code +.align 256 diff --git a/instr_test-v3/source/common/console.s b/instr_test-v3/source/common/console.s new file mode 100644 index 0000000..5820b3c --- /dev/null +++ b/instr_test-v3/source/common/console.s @@ -0,0 +1,331 @@ +; Scrolling text console with word wrapping, 30x29 characters. +; +; * Defers PPU initialization until first flush/ newline. +; * Works even if PPU doesn't support scrolling. +; * Keeps border around edge of screen for TV overscan. +; * Requires vertical or single-screen mirroring. +; * Requires ASCII font in CHR. + +.ifndef CONSOLE_COLOR + CONSOLE_COLOR = $30 ; white +.endif + +console_screen_width = 32 ; if lower than 32, left-justifies + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs. OK if either/both are 0. +console_left_margin = 1 +console_right_margin = 1 + +console_width = console_screen_width - console_left_margin - console_right_margin + +zp_byte console_pos ; 0 to console_width +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_width + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + + setb console_pos,0 + rts + + +; Hides console by disabling PPU rendering and blacking out +; first four entries of palette. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +; Shows console display +; Preserved: A, X, Y +console_show: + pha + lda #CONSOLE_COLOR + jsr console_show_custom_color_ + pla + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + sty console_temp + + ldy console_pos + cpy #console_width + beq console_full_ + sta console_buf,y + iny + sty console_pos + + ldy console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_scroll_up_ + setb console_pos,0 + pla + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_apply_scroll_ + pla + rts + + +;**** Internal routines **** + +console_full_: + ldy console_temp + + ; Line is full + + ; If space, treat as newline + cmp #' ' + beq console_newline + + ; Wrap current line at appropriate point + pha + tya + pha + jsr console_wrap_ + pla + tay + pla + + jmp console_print + + +; Inserts newline into buffer at appropriate position, leaving +; next line ready in buffer +; Preserved: X, console_temp +console_wrap_: + ; Find beginning of last word + ldy #console_width + lda #' ' +: dey + bmi console_newline + cmp console_buf,y + bne :- + + ; y = 0 to console_width-1 + + ; Flush through current word and put remaining + ; in buffer for next line + jsr console_wait_vbl_ + + ; Time to last PPU write: 207 + 32*(26 + 10) + + lda console_scroll + jsr console_set_ppuaddr_ + + stx console_pos ; save X + + ldx #0 + + ; Print everything before last word +: lda console_buf,x + sta PPUDATA + inx + dey + bpl :- + + ; x = 1 to console_width + + ; Move last word to beginning of buffer, and + ; print spaces for rest of line + ldy #0 + beq :++ +: lda #' ' + sta PPUDATA + lda console_buf,x + inx + sta console_buf,y + iny +: cpx #console_width + bne :-- + + ldx console_pos ; restore X + + ; Append new text after that + sty console_pos + + ; FALL THROUGH + + +; Scrolls up 8 pixels and clears one line BELOW new line +; Preserved: X, console_temp +console_scroll_up_: + ; Scroll up 8 pixels + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + + ; Clear line AFTER that on screen + jsr console_add_8_to_scroll_ + jsr console_set_ppuaddr_ + + ldy #console_width + lda #' ' +: sta PPUDATA + dey + bne :- + ; FALL THROUGH + + +; Applies current scrolling position to PPU +; Preserved: X, Y, console_temp +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + rts + + +; Sets PPU address for row +; In: A = scroll position +; Preserved: X, Y +console_set_ppuaddr_: + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + ora #console_left_margin + sta PPUADDR + rts + + +; A = (A + 8) % 240 +; Preserved: X, Y +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_show_custom_color_: + pha + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + pla + jsr console_load_palette_ + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + lda #0 + sta PPUADDR + sta PPUADDR + rts + + +; Initializes PPU if necessary, then waits for VBL +; Preserved: A, X, Y, console_temp +console_wait_vbl_: + lda console_scroll + cmp #-1 + bne @already_initialized + + ; Deferred initialization of PPU until first use of console + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + tya + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldy #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dey + bne :- + + ; Clear attributes + lda #0 + ldy #$40 +: sta PPUDATA + dey + bne :- + + pla + tay + + jsr console_show +@already_initialized: + jmp wait_vbl_optional + + +; Flushes current line +; Preserved: X, Y +console_flush_: + lda console_scroll + jsr console_set_ppuaddr_ + + sty console_temp + + ; Copy line + ldy #0 + beq :++ +: lda console_buf,y + sta PPUDATA + iny +: cpy console_pos + bne :-- + + ldy console_temp + rts diff --git a/instr_test-v3/source/common/crc.s b/instr_test-v3/source/common/crc.s new file mode 100644 index 0000000..625bf1e --- /dev/null +++ b/instr_test-v3/source/common/crc.s @@ -0,0 +1,115 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 ; Current CRC-32; no need to invert +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 360 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 + sec +@bit: ror checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcs :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 + sec +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ldx #3 +: lda checksum,x + jsr print_hex + dex + bpl :- + + jmp crc_on + + +; EQ if checksum matches CRC +; Out: A=0 and EQ if match, A>0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ldy #3 +: lda (ptr),y + cmp checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/instr_test-v3/source/common/crc_fast.s b/instr_test-v3/source/common/crc_fast.s new file mode 100644 index 0000000..94abad7 --- /dev/null +++ b/instr_test-v3/source/common/crc_fast.s @@ -0,0 +1,110 @@ +; Fast table-based CRC-32 + +; Initializes fast CRC tables and resets checksum. +; Preserved: Y +init_crc_fast = reset_crc + +; Updates checksum with byte from A +; Preserved: X, Y +; Time: 54 clocks +update_crc_fast: + stx checksum_temp + +; Updates checksum with byte from A +; Preserved: Y +; Time: 42 clocks +.macro update_crc_fast + eor checksum + tax + lda checksum+1 + eor checksum_t0,x + sta checksum + lda checksum+2 + eor checksum_t1,x + sta checksum+1 + lda checksum+3 + eor checksum_t2,x + sta checksum+2 + lda checksum_t3,x + sta checksum+3 +.endmacro + + update_crc_fast + ldx checksum_temp + rts + +.pushseg +.segment "RODATA" +.align 256 +checksum_t0: + .byte $8D,$1B,$A1,$37,$94,$02,$B8,$2E,$BF,$29,$93,$05,$A6,$30,$8A,$1C + .byte $E9,$7F,$C5,$53,$F0,$66,$DC,$4A,$DB,$4D,$F7,$61,$C2,$54,$EE,$78 + .byte $45,$D3,$69,$FF,$5C,$CA,$70,$E6,$77,$E1,$5B,$CD,$6E,$F8,$42,$D4 + .byte $21,$B7,$0D,$9B,$38,$AE,$14,$82,$13,$85,$3F,$A9,$0A,$9C,$26,$B0 + .byte $1D,$8B,$31,$A7,$04,$92,$28,$BE,$2F,$B9,$03,$95,$36,$A0,$1A,$8C + .byte $79,$EF,$55,$C3,$60,$F6,$4C,$DA,$4B,$DD,$67,$F1,$52,$C4,$7E,$E8 + .byte $D5,$43,$F9,$6F,$CC,$5A,$E0,$76,$E7,$71,$CB,$5D,$FE,$68,$D2,$44 + .byte $B1,$27,$9D,$0B,$A8,$3E,$84,$12,$83,$15,$AF,$39,$9A,$0C,$B6,$20 + .byte $AD,$3B,$81,$17,$B4,$22,$98,$0E,$9F,$09,$B3,$25,$86,$10,$AA,$3C + .byte $C9,$5F,$E5,$73,$D0,$46,$FC,$6A,$FB,$6D,$D7,$41,$E2,$74,$CE,$58 + .byte $65,$F3,$49,$DF,$7C,$EA,$50,$C6,$57,$C1,$7B,$ED,$4E,$D8,$62,$F4 + .byte $01,$97,$2D,$BB,$18,$8E,$34,$A2,$33,$A5,$1F,$89,$2A,$BC,$06,$90 + .byte $3D,$AB,$11,$87,$24,$B2,$08,$9E,$0F,$99,$23,$B5,$16,$80,$3A,$AC + .byte $59,$CF,$75,$E3,$40,$D6,$6C,$FA,$6B,$FD,$47,$D1,$72,$E4,$5E,$C8 + .byte $F5,$63,$D9,$4F,$EC,$7A,$C0,$56,$C7,$51,$EB,$7D,$DE,$48,$F2,$64 + .byte $91,$07,$BD,$2B,$88,$1E,$A4,$32,$A3,$35,$8F,$19,$BA,$2C,$96,$00 + +checksum_t1: + .byte $EF,$DF,$8E,$BE,$2B,$1B,$4A,$7A,$67,$57,$06,$36,$A3,$93,$C2,$F2 + .byte $FF,$CF,$9E,$AE,$3B,$0B,$5A,$6A,$77,$47,$16,$26,$B3,$83,$D2,$E2 + .byte $CF,$FF,$AE,$9E,$0B,$3B,$6A,$5A,$47,$77,$26,$16,$83,$B3,$E2,$D2 + .byte $DF,$EF,$BE,$8E,$1B,$2B,$7A,$4A,$57,$67,$36,$06,$93,$A3,$F2,$C2 + .byte $AE,$9E,$CF,$FF,$6A,$5A,$0B,$3B,$26,$16,$47,$77,$E2,$D2,$83,$B3 + .byte $BE,$8E,$DF,$EF,$7A,$4A,$1B,$2B,$36,$06,$57,$67,$F2,$C2,$93,$A3 + .byte $8E,$BE,$EF,$DF,$4A,$7A,$2B,$1B,$06,$36,$67,$57,$C2,$F2,$A3,$93 + .byte $9E,$AE,$FF,$CF,$5A,$6A,$3B,$0B,$16,$26,$77,$47,$D2,$E2,$B3,$83 + .byte $6C,$5C,$0D,$3D,$A8,$98,$C9,$F9,$E4,$D4,$85,$B5,$20,$10,$41,$71 + .byte $7C,$4C,$1D,$2D,$B8,$88,$D9,$E9,$F4,$C4,$95,$A5,$30,$00,$51,$61 + .byte $4C,$7C,$2D,$1D,$88,$B8,$E9,$D9,$C4,$F4,$A5,$95,$00,$30,$61,$51 + .byte $5C,$6C,$3D,$0D,$98,$A8,$F9,$C9,$D4,$E4,$B5,$85,$10,$20,$71,$41 + .byte $2D,$1D,$4C,$7C,$E9,$D9,$88,$B8,$A5,$95,$C4,$F4,$61,$51,$00,$30 + .byte $3D,$0D,$5C,$6C,$F9,$C9,$98,$A8,$B5,$85,$D4,$E4,$71,$41,$10,$20 + .byte $0D,$3D,$6C,$5C,$C9,$F9,$A8,$98,$85,$B5,$E4,$D4,$41,$71,$20,$10 + .byte $1D,$2D,$7C,$4C,$D9,$E9,$B8,$88,$95,$A5,$F4,$C4,$51,$61,$30,$00 + +checksum_t2: + .byte $02,$05,$0C,$0B,$6F,$68,$61,$66,$D9,$DE,$D7,$D0,$B4,$B3,$BA,$BD + .byte $B5,$B2,$BB,$BC,$D8,$DF,$D6,$D1,$6E,$69,$60,$67,$03,$04,$0D,$0A + .byte $6C,$6B,$62,$65,$01,$06,$0F,$08,$B7,$B0,$B9,$BE,$DA,$DD,$D4,$D3 + .byte $DB,$DC,$D5,$D2,$B6,$B1,$B8,$BF,$00,$07,$0E,$09,$6D,$6A,$63,$64 + .byte $DE,$D9,$D0,$D7,$B3,$B4,$BD,$BA,$05,$02,$0B,$0C,$68,$6F,$66,$61 + .byte $69,$6E,$67,$60,$04,$03,$0A,$0D,$B2,$B5,$BC,$BB,$DF,$D8,$D1,$D6 + .byte $B0,$B7,$BE,$B9,$DD,$DA,$D3,$D4,$6B,$6C,$65,$62,$06,$01,$08,$0F + .byte $07,$00,$09,$0E,$6A,$6D,$64,$63,$DC,$DB,$D2,$D5,$B1,$B6,$BF,$B8 + .byte $BA,$BD,$B4,$B3,$D7,$D0,$D9,$DE,$61,$66,$6F,$68,$0C,$0B,$02,$05 + .byte $0D,$0A,$03,$04,$60,$67,$6E,$69,$D6,$D1,$D8,$DF,$BB,$BC,$B5,$B2 + .byte $D4,$D3,$DA,$DD,$B9,$BE,$B7,$B0,$0F,$08,$01,$06,$62,$65,$6C,$6B + .byte $63,$64,$6D,$6A,$0E,$09,$00,$07,$B8,$BF,$B6,$B1,$D5,$D2,$DB,$DC + .byte $66,$61,$68,$6F,$0B,$0C,$05,$02,$BD,$BA,$B3,$B4,$D0,$D7,$DE,$D9 + .byte $D1,$D6,$DF,$D8,$BC,$BB,$B2,$B5,$0A,$0D,$04,$03,$67,$60,$69,$6E + .byte $08,$0F,$06,$01,$65,$62,$6B,$6C,$D3,$D4,$DD,$DA,$BE,$B9,$B0,$B7 + .byte $BF,$B8,$B1,$B6,$D2,$D5,$DC,$DB,$64,$63,$6A,$6D,$09,$0E,$07,$00 + +checksum_t3: + .byte $D2,$A5,$3C,$4B,$D5,$A2,$3B,$4C,$DC,$AB,$32,$45,$DB,$AC,$35,$42 + .byte $CF,$B8,$21,$56,$C8,$BF,$26,$51,$C1,$B6,$2F,$58,$C6,$B1,$28,$5F + .byte $E9,$9E,$07,$70,$EE,$99,$00,$77,$E7,$90,$09,$7E,$E0,$97,$0E,$79 + .byte $F4,$83,$1A,$6D,$F3,$84,$1D,$6A,$FA,$8D,$14,$63,$FD,$8A,$13,$64 + .byte $A4,$D3,$4A,$3D,$A3,$D4,$4D,$3A,$AA,$DD,$44,$33,$AD,$DA,$43,$34 + .byte $B9,$CE,$57,$20,$BE,$C9,$50,$27,$B7,$C0,$59,$2E,$B0,$C7,$5E,$29 + .byte $9F,$E8,$71,$06,$98,$EF,$76,$01,$91,$E6,$7F,$08,$96,$E1,$78,$0F + .byte $82,$F5,$6C,$1B,$85,$F2,$6B,$1C,$8C,$FB,$62,$15,$8B,$FC,$65,$12 + .byte $3F,$48,$D1,$A6,$38,$4F,$D6,$A1,$31,$46,$DF,$A8,$36,$41,$D8,$AF + .byte $22,$55,$CC,$BB,$25,$52,$CB,$BC,$2C,$5B,$C2,$B5,$2B,$5C,$C5,$B2 + .byte $04,$73,$EA,$9D,$03,$74,$ED,$9A,$0A,$7D,$E4,$93,$0D,$7A,$E3,$94 + .byte $19,$6E,$F7,$80,$1E,$69,$F0,$87,$17,$60,$F9,$8E,$10,$67,$FE,$89 + .byte $49,$3E,$A7,$D0,$4E,$39,$A0,$D7,$47,$30,$A9,$DE,$40,$37,$AE,$D9 + .byte $54,$23,$BA,$CD,$53,$24,$BD,$CA,$5A,$2D,$B4,$C3,$5D,$2A,$B3,$C4 + .byte $72,$05,$9C,$EB,$75,$02,$9B,$EC,$7C,$0B,$92,$E5,$7B,$0C,$95,$E2 + .byte $6F,$18,$81,$F6,$68,$1F,$86,$F1,$61,$16,$8F,$F8,$66,$11,$88,$FF +.popseg diff --git a/instr_test-v3/source/common/delay.s b/instr_test-v3/source/common/delay.s new file mode 100644 index 0000000..e8d7278 --- /dev/null +++ b/instr_test-v3/source/common/delay.s @@ -0,0 +1,202 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_res delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +; Delays approximately n milliseconds (1/1000 second), +; without caring whether it's NTSC or PAL. +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec_approx n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*1726190+500)/1000 +.endmacro + + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/instr_test-v3/source/common/instr_test.inc b/instr_test-v3/source/common/instr_test.inc new file mode 100644 index 0000000..9faa4ce --- /dev/null +++ b/instr_test-v3/source/common/instr_test.inc @@ -0,0 +1,99 @@ +REGION_FREE = 1 +.include "shell.inc" +.include "crc_fast.s" + +; Defines instruction to test +.macro entry op,name + .byte op,0 + .local Addr + .word Addr + seg_data RODATA,{Addr: .byte name,0} +.endmacro + +; Values set_paxyso sets registers to. Set +; before calling test_values, which can then +; overwrite them if desired. +zp_byte in_p +zp_byte in_a +zp_byte in_x +zp_byte in_y +zp_byte in_s + +; Temporary space for check_paxyso +zp_byte out_a +zp_byte out_x +zp_byte out_s + +; Values to cycle through for registers +values: + .byte 0,1,2,$40,$7F,$80,$81,$FF +values_size = * - values + .byte 0,1,2,$40,$7F,$80,$81,$FF + +; Sets bytes on stack around in_s +.macro set_stack + ldx in_s + inx + inx + ldy #6 +: txa + asl + eor #$A5 + sta $100,x + dex + dey + bne :- +.endmacro + +; Checksums bytes on stack around in_s +.macro check_stack + ldx in_s + inx + inx + ldy #6 +: lda $100,x + dex + jsr update_crc + dey + bne :- +.endmacro + +; Sets P, A, X, Y, S, and operand +.macro set_paxyso + ldx in_s + txs + lda values,y + sta operand + lda in_p + pha + lda in_a + ldx in_x + ldy in_y + plp +.endmacro + +; Checksums P, A, X, Y, S, and operand +.macro check_paxyso + php + sta out_a + pla + + stx out_x + tsx + stx out_s + ldx saved_s + txs + + cld + jsr update_crc_fast + lda out_a + jsr update_crc_fast + lda out_x + jsr update_crc_fast + tya + jsr update_crc_fast + lda out_s + jsr update_crc_fast + lda operand + jsr update_crc_fast +.endmacro diff --git a/instr_test-v3/source/common/instr_test_end.s b/instr_test-v3/source/common/instr_test_end.s new file mode 100644 index 0000000..07fca18 --- /dev/null +++ b/instr_test-v3/source/common/instr_test_end.s @@ -0,0 +1,199 @@ +; Offset of current instruction +zp_byte instrs_idx + +zp_byte failed_count + +main: + ; Stack slightly lower than top + ldx #$A2 + txs + + jsr init_crc_fast + + ; Test each instruction + lda #0 +@loop: sta instrs_idx + tay + + jsr reset_crc + lda instrs,y + jsr test_instr + jsr check_result + + lda instrs_idx + clc + adc #4 + cmp #instrs_size + bne @loop + +.ifdef BUILD_DEVCART + lda #0 + jmp exit +.endif + + lda failed_count + jne test_failed + jmp tests_passed + +; Check result of test +check_result: +.ifdef BUILD_DEVCART + ; Print correct CRC + jsr crc_off + print_str ".dword $" + ldx #0 +: lda checksum,x + jsr print_hex + inx + cpx #4 + bne :- + jsr print_newline + jsr crc_on +.else + ; Verify CRC + ldx #3 + ldy instrs_idx +: lda checksum,x + cmp correct_checksums,y + bne @wrong + iny + dex + bpl :- +.endif + rts + +; Print failed opcode and name +@wrong: + ldy instrs_idx + lda instrs,y + jsr print_a + jsr play_byte + lda instrs+2,y + sta addr + lda instrs+3,y + sta addr+1 + jsr print_str_addr + jsr print_newline + inc failed_count + rts + +; Place where instruction is executed +instr = $3A0 + +; Tests instr A +test_instr: + sta instr + jsr avoid_silent_nsf + + ; Copy rest of template + ldx #instr_template_size - 1 +: lda instr_template,x + sta instr,x + dex + bne :- + + ; Disable and be sure APU IRQs are clear, since + ; I flag gets cleared during testing. + setb SNDMODE,$C0 + setb $4010,0 + nop + lda SNDCHN + + ; Default stack + lda #$90 + sta in_s + + ; Test with different flags + lda #$00 + jsr test_flags + lda #$FF + jsr test_flags + + rts + +; Position in operand table +zp_byte operand_idx + +test_flags: + sta in_p + + ldy #values_size-1 +: sty operand_idx + + lda values,y + sta in_a + + lda values+1,y + sta in_x + + lda values+2,y + sta in_y + + jsr test_values + + ldy operand_idx + dey + bpl :- + + rts + +.ifndef values2 + values2 = values + values2_size = values_size +.endif + +.macro test_normal +zp_byte a_idx +zp_byte saved_s + + tsx + stx saved_s + + set_stack + + ldy #values2_size-1 +inner: sty a_idx + + lda values2,y + sta operand + + set_in + +; For debugging +.if 0 + ; P A X Y S O (z,x) (z),y + jsr print_p + jsr print_a + jsr print_x + jsr print_y + jsr print_s + lda operand + jsr print_a +.ifdef address + lda (address,x) + jsr print_a + lda (address),y + jsr print_a +.else + lda operand,x + jsr print_a + lda operand,y + jsr print_a +.endif + jsr print_newline +.endif + + jmp instr +instr_done: + + check_out + + ldy a_idx + dey + bpl inner + + check_stack + + ldx saved_s + txs +.endmacro diff --git a/instr_test-v3/source/common/macros.inc b/instr_test-v3/source/common/macros.inc new file mode 100644 index 0000000..dc968ad --- /dev/null +++ b/instr_test-v3/source/common/macros.inc @@ -0,0 +1,321 @@ +BLARGG_MACROS_INCLUDED = 1 + +; Allows extra error checking with modified version +; of ca65. Otherwise acts like a constant of 0. +ADDR = 0 + +; Switches to Segment and places Line there. +; Line can be an .align directive, .res, .byte, etc. +; Examples: +; seg_data BSS, .align 256 +; seg_data RODATA, {message: .byte "Test",0} +.macro seg_data Segment, Line + .pushseg + .segment .string(Segment) + Line + .popseg +.endmacro + +; Reserves Size bytes in Segment for Name. +; If Size is omitted, reserves one byte. +.macro seg_res Segment, Name, Size + .ifblank Size + seg_data Segment, Name: .res 1 + .else + seg_data Segment, Name: .res Size + .endif +.endmacro + +; Shortcuts for zeropage, bss, and stack +.define zp_res seg_res ZEROPAGE, +.define nv_res seg_res NVRAM, +.define bss_res seg_res BSS, +.define sp_res seg_res STACK, +.define zp_byte zp_res + +; Copies byte from Src to Addr. If Src begins with #, +; it sets Addr to the immediate value. +; Out: A = byte copied +; Preserved: X, Y +.macro mov Addr, Src + lda Src + sta Addr +.endmacro + +; Copies word from Src to Addr. If Src begins with #, +; it sets Addr the immediate value. +; Out: A = high byte of word +; Preserved: X, Y +.macro movw Addr, Src + .if .match( .left( 1, {Src} ), # ) + lda #<(.right( .tcount( {Src} )-1, {Src} )) + sta Addr + lda #>(.right( .tcount( {Src} )-1, {Src} )) + sta 1+(Addr) + .else + lda Src + sta Addr + lda 1+(Src) + sta 1+(Addr) + .endif +.endmacro + +; Increments 16-bit value at Addr. +; Out: EQ/NE based on resulting 16-bit value +; Preserved: A, X, Y +.macro incw Addr + .local @Skip + inc Addr + bne @Skip + inc 1+(Addr) +@Skip: +.endmacro + +; Adds Src to word at Addr. +; Out: A = high byte of result, carry set appropriately +; Preserved: X, Y +.macro addw Addr, Src + .if .match( .left( 1, {Src} ), # ) + addw_ Addr,(.right( .tcount( {Src} )-1, {Src} )) + .else + lda Addr + clc + adc Src + sta Addr + + lda 1+(Addr) + adc 1+(Src) + sta 1+(Addr) + .endif +.endmacro +.macro addw_ Addr, Imm + lda Addr + clc + adc #> 8) <> 0 + lda 1+(Addr) + adc #>Imm + sta 1+(Addr) + ;.else + ; .local @Skip + ; bcc @Skip + ; inc 1+(Addr) + ;@Skip: + ;.endif +.endmacro + +; Splits list of words into tables of low and high bytes +; Example: split_words foo, {$1234, $5678} +; expands to: +; foo_l: $34, $78 +; foo_h: $12, $56 +; foo_count = 2 +.macro split_words Label, Words + .ident (.concat (.string(Label), "_l")): .lobytes Words + .ident (.concat (.string(Label), "_h")): .hibytes Words + .ident (.concat (.string(Label), "_count")) = * - .ident (.concat (.string(Label), "_h")) +.endmacro + +.macro SELECT Bool, True, False, Extra + .ifndef Bool + False Extra + .elseif Bool <> 0 + True Extra + .else + False Extra + .endif +.endmacro + +.macro DEFAULT Name, Value + .ifndef Name + Name = Value + .endif +.endmacro + +.ifp02 + ; 6502 doesn't define these alternate names + .define blt bcc + .define bge bcs +.endif +.define jlt jcc +.define jge jcs + +; Jxx Target = Bxx Target, except it can go farther than +; 128 bytes. Implemented via branch around a JMP. + +; Don't use ca65's longbranch, because they fail for @labels +;.macpack longbranch + +.macro jeq Target + bne *+5 + jmp Target +.endmacro + +.macro jne Target + beq *+5 + jmp Target +.endmacro + +.macro jmi Target + bpl *+5 + jmp Target +.endmacro + +.macro jpl Target + bmi *+5 + jmp Target +.endmacro + +.macro jcs Target + bcc *+5 + jmp Target +.endmacro + +.macro jcc Target + bcs *+5 + jmp Target +.endmacro + +.macro jvs Target + bvc *+5 + jmp Target +.endmacro + +.macro jvc Target + bvs *+5 + jmp Target +.endmacro + + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data RODATA,{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + .local @for_loop + lda #start +@for_loop: + pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne @for_loop +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + .local @for_loop_skip + .local @for_loop + ldy #>(start) + lda #<(start) +@for_loop: + tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc @for_loop_skip + iny +.else + bcs @for_loop_skip + dey +.endif +@for_loop_skip: + cmp #<((end)+(step)) + bne @for_loop + cpy #>((end)+(step)) + bne @for_loop +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro inxy + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro + +; Negates A and adds it to operand +.macro subaf Operand + eor #$FF + sec + adc Operand +.endmacro + +; Initializes CPU registers to reasonable values +; Preserved: A, Y +.macro init_cpu_regs + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + .ifndef BUILD_NSF + inx + stx PPUCTRL + .endif +.endmacro diff --git a/instr_test-v3/source/common/neshw.inc b/instr_test-v3/source/common/neshw.inc new file mode 100644 index 0000000..d636a97 --- /dev/null +++ b/instr_test-v3/source/common/neshw.inc @@ -0,0 +1,56 @@ +; NES I/O locations and masks + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 + +.ifndef REGION_FREE + .ifndef PAL_ONLY + .ifndef NTSC_ONLY + NTSC_ONLY = 1 + .endif + .endif +.else + .ifdef NTSC_ONLY + .error "NTSC_ONLY and REGION_FREE defined" + .endif + .ifdef PAL_ONLY + .error "PAL_ONLY and REGION_FREE defined" + .endif +.endif + +.ifdef NTSC_ONLY + CLOCK_RATE = 1789773 + PPU_FRAMELEN = 29781 +.endif + +.ifdef PAL_ONLY + CLOCK_RATE = 1662607 + PPU_FRAMELEN = 33248 +.endif diff --git a/instr_test-v3/source/common/ppu.s b/instr_test-v3/source/common/ppu.s new file mode 100644 index 0000000..0e6587a --- /dev/null +++ b/instr_test-v3/source/common/ppu.s @@ -0,0 +1,205 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts + + +.macro check_ppu_region_ Len + ; Delays since VBL began + jsr wait_vbl_optional ; 10 average + delay Len - 18 - 200 + lda PPUSTATUS ; 4 + bmi @ok ; 2 + delay 200 + ; Next VBL should roughly begin here if it's the + ; one we are detecting + delay 200 + lda PPUSTATUS ; 2 + bpl @ok +.endmacro + +check_ppu_region: + +.ifndef REGION_FREE +.ifdef PAL_ONLY + check_ppu_region_ 29781 + print_str {newline,"Note: This test is meant for PAL NES only.",newline,newline} +.endif + +.ifdef NTSC_ONLY + check_ppu_region_ 33248 + print_str {newline,"Note: This test is meant for NTSC NES only.",newline,newline} +.endif +.endif +@ok: rts + + +; Loads ASCII font into CHR RAM and fills rest with $FF +.macro load_chr_ram + jsr wait_vbl_optional + mov PPUCTRL,#0 + mov PPUMASK,#0 + mov PPUADDR,#0 + mov PPUADDR,#0 + + ; Copy ascii_chr to 0 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page + + ; Fill rest + lda #$FF +: sta PPUDATA + iny + bne :- + inx + cpx #$20 + bne :- +.endmacro diff --git a/instr_test-v3/source/common/print.s b/instr_test-v3/source/common/print.s new file mode 100644 index 0000000..976fd9c --- /dev/null +++ b/instr_test-v3/source/common/print.s @@ -0,0 +1,278 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr print_nibble_ + pla + + pha + and #$0F + jsr print_nibble_ + pla + rts + +print_nibble_: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints low 4 bits of A as single hex character +; Preserved: A, X, Y +print_nibble: + pha + and #$0F + jsr update_crc + jsr print_nibble_ + pla + rts + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + bne :+ + inc addr+1 +: rts + + +; Prints A as 1-3 digit decimal. +; In: A = MSB +; Preserved: A, X, Y +print_dec: + sta print_temp_ + pha + txa + pha + tya + pha + ldy print_temp_ + lda #0 + sta print_temp_ + tya + jmp :+ + + +; Prints 16-bit AY as 1-5 digit decimal. +; Preserved: A, X, Y +print_ay_dec: + jsr update_crc + sta print_temp_ + pha + txa + pha + tya + pha +: jsr update_crc + + ; Strip leading zeroes + ldx #6 +: dex + cmp @lsb-1,x + lda print_temp_ + sbc @msb-1,x + tya + bcc :- + bcs @non_zero + + ; Print remaining digits + +@more: ; Commit subtraction + iny + sta print_temp_ + pla + + ; Subtract +@digit: sbc @lsb,x + pha + lda print_temp_ + sbc @msb,x + bcs @more + + ; Print digit and undo subtraction + tya + jsr print_char_ + pla + clc + adc @lsb,x +@non_zero: + sec + ldy #'0' + dex + bne @digit + + ora #'0' + jsr print_char_ + + pla + tay + pla + tax + pla + rts + +@lsb: .byte 0,<10,<100,<1000,<10000 +@msb: .byte 0,>10,>100,>1000,>10000 + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/instr_test-v3/source/common/shell.inc b/instr_test-v3/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/instr_test-v3/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/instr_test-v3/source/common/shell.s b/instr_test-v3/source/common/shell.s new file mode 100644 index 0000000..0ddd572 --- /dev/null +++ b/instr_test-v3/source/common/shell.s @@ -0,0 +1,184 @@ +; Shell that sets up testing framework and calls main + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +; Move code from $C000 to $E200, to accommodate my devcarts +.ifndef LARGER_ROM_HACK +.segment "CODE" + .res $2200 +.endif + +; Put shell code after user code, so user code is in more +; consistent environment +.ifndef NO_CODE2 +.segment "CODE2" +.endif + + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + init_cpu_regs + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell without affecting current set_test values +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + ; Clear APU registers + lda #0 + sta $4015 + ldx #$13 +: sta $4000,x + dex + bpl :- + + ; CPU registers + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sta temp + init_cpu_regs + setb SNDCHN,0 + lda temp + + jsr report_result + pha + jsr check_ppu_region + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + + +;**** Other routines **** + +.include "shell_misc.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts +.endif diff --git a/instr_test-v3/source/common/shell_misc.s b/instr_test-v3/source/common/shell_misc.s new file mode 100644 index 0000000..cf95e5a --- /dev/null +++ b/instr_test-v3/source/common/shell_misc.s @@ -0,0 +1,226 @@ +; Reports internal error and exits program +internal_error: +assert_failed: + pla + tay + pla + init_cpu_regs + print_str newline,"internal error, PC=" + jsr print_hex + jsr print_y + lda #255 + jmp exit + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +.macro fill_ram_ Begin, End + ; Simpler to count from negative size up to 0, + ; and adjust address downward to compensate + ; for initial low byte in Y index + .local Neg_size + Neg_size = (Begin) - (End) + ldxy #(Begin) - = 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/instr_test-v3/source/common/text_out.s b/instr_test-v3/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/instr_test-v3/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +The first time a line of text is completed, the console initializes the +PPU. Most print routines update a running CRC-32 checksum which can be +checked with check_crc. This allows ALL the output to be checked very +easily. If the checksum doesn't match, it is printed, so during +development the code can be run on a NES to get the correct checksum, +which is then typed into the test code. The checksum is also useful when +comparing different outputs; rather than having to examine all the +output, one need only compare checksums. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported by the included code, due to +their complexity. + + +Macros +------ +Some macros are used to make common operations more convenient, defined +in common/macros.inc. The left is equivalent to the right: + + Macro Equivalent + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + setb addr,byte lda #byte + sta addr + + setw addr,word lda #word + sta addr+1 + + ldxy #value ldx #>value + ldy # diff --git a/instr_timing/instr_timing.nes b/instr_timing/instr_timing.nes new file mode 100644 index 0000000..48e9227 Binary files /dev/null and b/instr_timing/instr_timing.nes differ diff --git a/instr_timing/readme.txt b/instr_timing/readme.txt new file mode 100644 index 0000000..9f5100c --- /dev/null +++ b/instr_timing/readme.txt @@ -0,0 +1,101 @@ +NES CPU Instruction Timing Test +------------------------------- +These tests verify timing of all NES CPU instructions, except the 12 +that freeze the CPU. + +The individual tests report the opcode of any failed instructions. +instr_timing prints the measured and correct times. branch_timing runs +the branch instruction in 8 different situations: four not taken, and +four taken. For each of these four, the first two are for a +non-page-cross both negative and positive, and the second two cross a +page. The correct times are 2 2 2 2 3 3 4 4. + + +Requirements +------------ +- Basic CPU instruction behavior +- Basic APU length counter operation + + +Internal operation +------------------ +Each instruction is timed by setting up appropriate conditions, +synchronizing to the APU length counter and then loading it with 2, +executing the instruction in a loop that stops once the length counter +expires. The number of loop iterations indicates how many clocks the +instruction took. + +Multi-tests +----------- +The NES/NSF builds in the main directory consist of multiple sub-tests. +When run, they list the subtests as they are run. The final result code +refers to the first sub-test that failed. For more information about any +failed subtests, run them individually from rom_singles/ and +nsf_singles/. + + +Flashes, clicks, other glitches +------------------------------- +If a test prints "passed", it passed, even if there were some flashes or +odd sounds. Only a test which prints "done" at the end requires that you +watch/listen while it runs in order to determine whether it passed. Such +tests involve things which the CPU cannot directly test. + + +Alternate output +---------------- +Tests generally print information on screen, but also report the final +result audibly, and output text to memory, in case the PPU doesn't work +or there isn't one, as in an NSF or a NES emulator early in development. + +After the tests are done, the final result is reported as a series of +beeps (see below). For NSF builds, any important diagnostic bytes are +also reported as beeps, before the final result. + + +Output at $6000 +--------------- +All text output is written starting at $6004, with a zero-byte +terminator at the end. As more text is written, the terminator is moved +forward, so an emulator can print the current text at any time. + +The test status is written to $6000. $80 means the test is running, $81 +means the test needs the reset button pressed, but delayed by at least +100 msec from now. $00-$7F means the test has completed and given that +result code. + +To allow an emulator to know when one of these tests is running and the +data at $6000+ is valid, as opposed to some other NES program, $DE $B0 +$G1 is written to $6001-$6003. + + +Audible output +-------------- +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason. See the source +code of the test for more information about the meaning of a test code. +They are found after the set_test macro. For example, the cause of test +code 3 would be found in a line containing set_test 3. Examples: + + Tones Binary Decimal Meaning + - - - - - - - - - - - - - - - - - - - - + low 0 0 passed + low high 01 1 failed + low high low 010 2 error 2 + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to prevent the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +-- +Shay Green diff --git a/instr_timing/rom_singles/1-instr_timing.nes b/instr_timing/rom_singles/1-instr_timing.nes new file mode 100644 index 0000000..51d445f Binary files /dev/null and b/instr_timing/rom_singles/1-instr_timing.nes differ diff --git a/instr_timing/rom_singles/2-branch_timing.nes b/instr_timing/rom_singles/2-branch_timing.nes new file mode 100644 index 0000000..5205014 Binary files /dev/null and b/instr_timing/rom_singles/2-branch_timing.nes differ diff --git a/instr_timing/source/1-instr_timing.s b/instr_timing/source/1-instr_timing.s new file mode 100644 index 0000000..be3fe5f --- /dev/null +++ b/instr_timing/source/1-instr_timing.s @@ -0,0 +1,331 @@ +; Tests instruction timing for all except the 8 branches and 12 halts +; +; Times each instruction by putting it in a loop that counts iterations +; and waits for APU length counter to expire. + +CUSTOM_IRQ=1 +.include "shell.inc" + +zp_byte nothing_failed +zp_byte test_index +zp_byte type_mask +zp_byte saved_sp +stack_copy = $600 ; stack is copied here during instruction test +test_opcode = $705 ; test loop is copied here +irq = test_opcode+2 + +main: + print_str "Instruction timing test",{newline,newline} + print_str "Takes about 25 seconds. " + print_str "Doesn't time the 8 branches " + print_str "and 12 illegal instructions.",{newline,newline} + + set_test 5,"Timing of APU length period, INC zp, LDA abs, AND #imm, or BNE (taken) is wrong." + lda #$29 + sta test_index + jsr time_instr + cmp #2 + jne test_failed + + set_test 0 + + print_str "Official instructions...",newline + lda #0 + jsr test_instrs + bne :+ + set_test 2,"Official instruction timing is wrong" +: + print_str {newline,"NOPs and alternate SBC...",newline} + lda #$80 + jsr test_instrs + bne :+ + set_test 3,"NOPs and alternate SBC timing is wrong" +: + print_str {newline,"Unofficial instructions...",newline} + lda #$40 + jsr test_instrs + bne :+ + set_test 4,"Unofficial instruction timing is wrong" +: + jmp tests_done + + +; A -> Type to test +test_instrs: + sta type_mask + setb nothing_failed,1 + + setb test_index,0 +@loop: ldx test_index + lda instr_types,x + and #$F0 + cmp type_mask + bne :+ + jsr avoid_silent_nsf + jsr @test_instr +: inc test_index + bne @loop + + lda nothing_failed + ora test_code + rts + + +; Tests current instruction in normal and page cross cases +@test_instr: + ; No page crossing + lda #2 + jsr time_instr + ldy test_index + eor instr_times,y + beq :+ + + jsr print_failed_instr + jsr print_newline + + lda instr_times,y + cmp instr_times_cross,y + bne :+ ; test again only if page crossing time is different + rts + + ; Page crossing +: lda #3 + jsr time_instr + ldy test_index + eor instr_times_cross,y + beq :+ + + jsr print_failed_instr + print_str " (cross)",newline +: rts + + +; Prints failed instruction times +; A -> Actual XOR correct +; X -> Actual +; Preserved: Y +print_failed_instr: + pha + lda test_index + jsr print_hex + jsr play_byte + print_str " was " + txa + jsr print_dec + pla + print_str ", should be " + stx temp + eor temp + jsr print_dec + setb nothing_failed,0 + rts + + +; Times a single instruction +; test_index -> Opcode of instruction +; A -> Byte to load into X and Y before running instruction +; X, A <- Cycles instruction took +.align 256 +time_instr: + sta temp + ldy test_index + sty test_opcode + + ; Copy test_opcode to RAM + lda instr_types,y + and #$07 + asl a + tay + lda test_addrs-2,y + sta addr + lda test_addrs-1,y + sta addr+1 + ldy #1 +: lda (addr),y + sta test_opcode,y + iny + cpy #16 + bne :- + + ; Save copy of stack + tsx + stx saved_sp +: lda $100,x + sta stack_copy,x + inx + bne :- + + ; Set zero-page values so the addressing modes use the following + ; when X and Y = $02/$03 + ; zp $FD + ; zp,x $FF/$00 + ; abs $03FD + ; abs,x/y $03FF/$0400 + ; (ind,x) $02FD/$0302 + ; (ind),y $02FF/$0300 + ; JMP (ind) test_opcode+3 + setb <$00,$02 + setb <$01,$03 + setb <$FD,$FD + setb <$FE,$02 + setb <$FF,$FD + lda temp ; $A3 LAX (ab,X) will load X from these two + sta $02FD + sta $0302 + setw $03FD,test_opcode+3 ; JMP ($03FD) will use this address + + ; Fill stack for RTS/RTI test + ldx #$FF + txs + inx + lda #>test_opcode +: pha + inx + bne :- + + ; Setup registers + lda temp + tay + tax + setb temp,0 + + ; Synchronize with APU length counter + setb SNDMODE,$40 + setb SNDCHN,$01 + setb $4000,$10 + setb $4001,$7F + setb $4002,$FF + setb $4003,$18 + lda #$01 +: and SNDCHN + bne :- + + ; Setup length counter + setb $4003,$18 + + ; ~26323 delay, so length counter will be ~3500 cycles from expiring + setb temp3,-13 + setb temp2,-207 +: inc temp2 + bne :- + inc temp3 + bne :- + + ; Run instruction + jmp test_opcode + +raw_to_cycles: ; entry i is lowest value that qualifies for i cycles + .byte 241, 226, 212, 200, 189, 179, 171, 163, 156, 149, 0 + +; Jumps here when instruction has been timed +instr_done: + ; Restore things in case instruction affected them + sei + cld + + ; Convert iteration count to cycle count + lda temp + ldy #-1 +: iny + cmp raw_to_cycles,y + blt :- + + ; Convert 10+ to 0 + cpy #10 + blt :+ + ldy #0 +: + ; Restore stack + ldx saved_sp + txs +: lda stack_copy,x + sta $100,x + inx + bne :- + + tya + tax + + rts + +.macro instr_template instr +: instr + inc temp + lda SNDCHN + and #$01 + bne :- + jmp instr_done +.endmacro + +test_1: instr_template nop +test_2: instr_template sta <$FD +test_3: instr_template sta $03FD +test_4: instr_template jmp test_opcode+3 + +test_addrs: + .word test_1, test_2, test_3, test_4 + +; $8n = unofficial NOPs and $EB equivalent of SBC #imm +; $4n = all other unofficial opcodes +; $0n = official opcodes +; -1 = not tested +; n = instruction length +instr_types: + ; 0 1 2 3 4 5 6 7 8 9 A B C D E F + .byte 2, 2, -1,$42,$82, 2, 2,$42, 1, 2, 1,$42,$83, 3, 3,$43 ; 0 + .byte -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 1 + .byte 4, 2, -1,$42, 2, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; 2 + .byte -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 3 + .byte 2, 2, -1,$42,$82, 2, 2,$42, 1, 2, 1,$42, 4, 3, 3,$43 ; 4 + .byte -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 5 + .byte 3, 2, -1,$42,$82, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; 6 + .byte -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; 7 + .byte $82, 2,$82,$42, 2, 2, 2,$42, 1,$82, 1,$42, 3, 3, 3,$43 ; 8 + .byte -1, 2, -1,$42, 2, 2, 2,$42, 1, 3, 1,$43,$43, 3,$43,$43 ; 9 + .byte 2, 2, 2,$42, 2, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; A + .byte -1, 2, -1,$42, 2, 2, 2,$42, 1, 3, 1,$43, 3, 3, 3,$43 ; B + .byte 2, 2,$82,$42, 2, 2, 2,$42, 1, 2, 1,$42, 3, 3, 3,$43 ; C + .byte -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; D + .byte 2, 2,$82,$42, 2, 2, 2,$42, 1, 2, 1,$82, 3, 3, 3,$43 ; E + .byte -1, 2, -1,$42,$82, 2, 2,$42, 1, 3,$81,$43,$83, 3, 3,$43 ; F + ; Bxx HLT + +; Clocks when no page crossing occurs +instr_times: + ; 0 1 2 3 4 5 6 7 8 9 A B C D E F + .byte 7,6,0,8,3,3,5,5,3,2,2,2,4,4,6,6 ; 0 + .byte 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 1 + .byte 6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6 ; 2 + .byte 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 3 + .byte 6,6,0,8,3,3,5,5,3,2,2,2,3,4,6,6 ; 4 + .byte 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 5 + .byte 6,6,0,8,3,3,5,5,4,2,2,2,5,4,6,6 ; 6 + .byte 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; 7 + .byte 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; 8 + .byte 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 ; 9 + .byte 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; A + .byte 0,5,0,5,4,4,4,4,2,4,2,4,4,4,4,4 ; B + .byte 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; C + .byte 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; D + .byte 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; E + .byte 0,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 ; F + +; Clocks when page crossing occurs +instr_times_cross: + ; 0 1 2 3 4 5 6 7 8 9 A B C D E F + .byte 7,6,0,8,3,3,5,5,3,2,2,2,4,4,6,6 ; 0 + .byte 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 1 + .byte 6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6 ; 2 + .byte 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 3 + .byte 6,6,0,8,3,3,5,5,3,2,2,2,3,4,6,6 ; 4 + .byte 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 5 + .byte 6,6,0,8,3,3,5,5,4,2,2,2,5,4,6,6 ; 6 + .byte 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; 7 + .byte 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; 8 + .byte 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 ; 9 + .byte 2,6,2,6,3,3,3,3,2,2,2,2,4,4,4,4 ; A + .byte 0,6,0,6,4,4,4,4,2,5,2,5,5,5,5,5 ; B + .byte 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; C + .byte 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; D + .byte 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6 ; E + .byte 0,6,0,8,4,4,6,6,2,5,2,7,5,5,7,7 ; F diff --git a/instr_timing/source/2-branch_timing.s b/instr_timing/source/2-branch_timing.s new file mode 100644 index 0000000..d372fef --- /dev/null +++ b/instr_timing/source/2-branch_timing.s @@ -0,0 +1,186 @@ +; Verifies timing of branch instructions +; +; Runs branch instruction in loop that counts iterations +; until APU length counter expires. Moves the loop around +; in memory to trigger page cross/no cross cases. + +.include "shell.inc" + +zp_byte opcode +zp_byte flags +zp_byte time_ptr +bss_res times,8 + +main: + set_test 0 + for_loop test_opcode,$10,$F0,$20 + jmp tests_done + +log_time: + ldx time_ptr + sta times,x + inc time_ptr + rts + +test_opcode: + sta opcode + + ; Not taken + ldx #$FF + and #$20 + beq :+ + inx +: stx flags + setb time_ptr,0 + jsr test_addrs + + ; Taken + lda flags + eor #$FF + sta flags + jsr test_addrs + + ; Verify times + ldx #8 - 1 +: lda times,x + cmp @correct_times,x + bne @error + dex + bpl :- + + rts + +@correct_times: + .byte 2,2,2,2,3,3,4,4 + +@error: lda opcode + jsr print_a + jsr play_byte + ldy #0 +: lda times,y + jsr print_dec + jsr print_space + iny + cpy #8 + bne :- + jsr print_newline + set_test 1 + rts + +; Tests instruction with page cross/no cross cases +test_addrs: + setw addr,$6EA + jsr test_forward + + setw addr,$700 + jsr test_reverse + + setw addr,$6EB + jsr test_forward + + setw addr,$6FF + jsr test_reverse + + rts + +; Times code at addr +time_code: + pha + + ; Synchronize with APU length counter + setb SNDMODE,$40 + setb SNDCHN,$01 + setb $4000,$10 + setb $4001,$7F + setb $4002,$FF + setb $4003,$18 + lda #$01 +: and SNDCHN + bne :- + + ; Setup length counter + setb $4003,$18 + + delay 29830-7120 + + ; Run instruction + setb temp,0 + pla + jmp (addr) + +raw_to_cycles: ; entry i is lowest value that qualifies for i cycles + .byte 250, 241, 233, 226, 219, 213, 206, 201, 195, 190, 0 + +; Jumps here when instruction has been timed +instr_done: + ; Convert iteration count to cycle count + lda temp + ldy #-1 +: iny + cmp raw_to_cycles,y + blt :- + + ; Convert 10+ to 0 + cpy #10 + blt :+ + ldy #0 +: + tya + jsr log_time + + rts + +.macro test_dir + ; Copy code + ldy #40 +: lda @code,y + sta (addr),y + dey + bpl :- + + ; Patch branch opcode + ldy #@branch - @code + lda opcode + sta (addr),y + + ; Calculate address of @loop + lda addr + clc + adc #@loop - @code + sta addr + lda addr+1 + adc #0 + sta addr+1 + + jmp time_code +@code: +.endmacro + +.macro instr_loop + inc temp + lda SNDCHN + and #$01 + beq *+5 + jmp (addr) + jmp instr_done +.endmacro + +test_reverse: + test_dir +: instr_loop +@loop: lda flags + pha + plp +@branch: + bmi :- + instr_loop + +test_forward: + test_dir +@loop: lda flags + pha + plp +@branch: + bmi :+ + instr_loop +: instr_loop diff --git a/instr_timing/source/common/ascii.chr b/instr_timing/source/common/ascii.chr new file mode 100644 index 0000000..2c5b26b Binary files /dev/null and b/instr_timing/source/common/ascii.chr differ diff --git a/instr_timing/source/common/build_rom.s b/instr_timing/source/common/build_rom.s new file mode 100644 index 0000000..f3d50cd --- /dev/null +++ b/instr_timing/source/common/build_rom.s @@ -0,0 +1,93 @@ +; Builds program as iNES ROM + +; Default is 32K PRG and 8K CHR ROM, NROM (0) + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM=1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM=1 ; Use mapper that supports 8K WRAM in cart +CUSTOM_MAPPER=n ; Specify mapper number +.endif + +.ifndef CUSTOM_MAPPER + .ifdef CART_WRAM + CUSTOM_MAPPER = 2 ; UNROM + .else + CUSTOM_MAPPER = 0 ; NROM + .endif +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte $4E,$45,$53,26 ; "NES" EOF + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte CUSTOM_MAPPER*$10+$01 ; vertical mirroring +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word -1,-1,-1, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .define CHARS "CHARS_PRG" + .segment CHARS + ascii_chr: + + .segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: +.else + .define CHARS "CHARS" + .segment "CHARS_ASCII" + .align $200 + .incbin "ascii.chr" + .res $1800 +.endif + +.segment CHARS + .res $10,0 + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +; Move code to $C000 +.segment "CODE" + .res $4000 + +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + load_ascii_chr + .endif + rts + +post_exit: + jsr set_final_result + jsr play_hex + jmp forever + +; This helps devcart recover after running test. +; It is never executed by test ROM. +.segment "LOADER" + .incbin "devcart.bin" + +.code +.align 256 diff --git a/instr_timing/source/common/console.s b/instr_timing/source/common/console.s new file mode 100644 index 0000000..036553c --- /dev/null +++ b/instr_timing/source/common/console.s @@ -0,0 +1,331 @@ +; Scrolling text console with line wrapping, 30x29 characters. +; Buffers lines for speed. Will work even if PPU doesn't +; support scrolling (until text reaches bottom). Keeps border +; along bottom in case TV cuts it off. +; +; Defers most initialization until first newline, at which +; point it clears nametable and makes palette non-black. +; +; ** ASCII font must already be in CHR, and mirroring +; must be vertical or single-screen. + +.ifndef CONSOLE_COLOR + CONSOLE_COLOR = $30 ; white +.endif + +console_screen_width = 32 ; if lower than 32, left-justifies + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs. OK if either/both are 0. +console_left_margin = 1 +console_right_margin = 1 + +console_width = console_screen_width - console_left_margin - console_right_margin + +zp_byte console_pos ; 0 to console_width +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_width + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + + setb console_pos,0 + rts + + +; Hides console by disabling PPU rendering and blacking out +; first four entries of palette. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +; Shows console display +; Preserved: X, Y +console_show: + pha + lda #CONSOLE_COLOR + jsr console_show_custom_color_ + pla + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + sty console_temp + + ldy console_pos + cpy #console_width + beq console_full_ + sta console_buf,y + iny + sty console_pos + + ldy console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_scroll_up_ + setb console_pos,0 + pla + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_apply_scroll_ + pla + rts + + +;**** Internal routines **** + +console_full_: + ldy console_temp + + ; Line is full + + ; If space, treat as newline + cmp #' ' + beq console_newline + + ; Wrap current line at appropriate point + pha + tya + pha + jsr console_wrap_ + pla + tay + pla + + jmp console_print + + +; Inserts newline into buffer at appropriate position, leaving +; next line ready in buffer +; Preserved: X, console_temp +console_wrap_: + ; Find beginning of last word + ldy #console_width + lda #' ' +: dey + bmi console_newline + cmp console_buf,y + bne :- + + ; y = 0 to console_width-1 + + ; Flush through current word and put remaining + ; in buffer for next line + jsr console_wait_vbl_ + + ; Time to last PPU write: 207 + 32*(26 + 10) + + lda console_scroll + jsr console_set_ppuaddr_ + + stx console_pos ; save X + + ldx #0 + + ; Print everything before last word +: lda console_buf,x + sta PPUDATA + inx + dey + bpl :- + + ; x = 1 to console_width + + ; Move last word to beginning of buffer, and + ; print spaces for rest of line + ldy #0 + beq :++ +: lda #' ' + sta PPUDATA + lda console_buf,x + inx + sta console_buf,y + iny +: cpx #console_width + bne :-- + + ldx console_pos ; restore X + + ; Append new text after that + sty console_pos + + ; FALL THROUGH + + +; Scrolls up 8 pixels and clears one line BELOW new line +; Preserved: X, console_temp +console_scroll_up_: + ; Scroll up 8 pixels + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + + ; Clear line AFTER that on screen + jsr console_add_8_to_scroll_ + jsr console_set_ppuaddr_ + + ldy #console_width + lda #' ' +: sta PPUDATA + dey + bne :- + ; FALL THROUGH + + +; Applies current scrolling position to PPU +; Preserved: X, Y, console_temp +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + rts + + +; Sets PPU address for row +; In: A = scroll position +; Preserved: X, Y +console_set_ppuaddr_: + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + ora #console_left_margin + sta PPUADDR + rts + + +; A = (A + 8) % 240 +; Preserved: X, Y +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_show_custom_color_: + pha + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + pla + jsr console_load_palette_ + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + rts + + +; Initializes PPU if necessary, then waits for VBL +; Preserved: A, X, Y, console_temp +console_wait_vbl_: + lda console_scroll + cmp #-1 + bne @already_initialized + + ; Deferred initialization of PPU until first use of console + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + tya + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldy #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dey + bne :- + + ; Clear attributes + lda #0 + ldy #$40 +: sta PPUDATA + dey + bne :- + + pla + tay + + jsr console_show +@already_initialized: + jmp wait_vbl_optional + + +; Flushes current line +; Preserved: X, Y +console_flush_: + lda console_scroll + jsr console_set_ppuaddr_ + + sty console_temp + + ; Copy line + ldy #0 + beq :++ +: lda console_buf,y + sta PPUDATA + iny +: cpy console_pos + bne :-- + + ldy console_temp + rts diff --git a/instr_timing/source/common/crc.s b/instr_timing/source/common/crc.s new file mode 100644 index 0000000..de96c2a --- /dev/null +++ b/instr_timing/source/common/crc.s @@ -0,0 +1,118 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on + + +; EQ if checksum matches CRC +; Out: A=0 and EQ if match, A>0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/instr_timing/source/common/delay.s b/instr_timing/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/instr_timing/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/instr_timing/source/common/devcart.bin b/instr_timing/source/common/devcart.bin new file mode 100644 index 0000000..684fbb9 Binary files /dev/null and b/instr_timing/source/common/devcart.bin differ diff --git a/instr_timing/source/common/macros.inc b/instr_timing/source/common/macros.inc new file mode 100644 index 0000000..d4cb72e --- /dev/null +++ b/instr_timing/source/common/macros.inc @@ -0,0 +1,169 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro inxy + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro diff --git a/instr_timing/source/common/neshw.inc b/instr_timing/source/common/neshw.inc new file mode 100644 index 0000000..814d772 --- /dev/null +++ b/instr_timing/source/common/neshw.inc @@ -0,0 +1,43 @@ +; NES I/O locations and masks + +; Clocks per second +.ifndef CLOCK_RATE + CLOCK_RATE = 1789773 ; NTSC +; CLOCK_RATE = 1662607 ; PAL +.endif + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 + +.if CLOCK_RATE = 1789773 + PPU_FRAMELEN = 29781 +.elseif CLOCK_RATE = 1662607 + PPU_FRAMELEN = 33248 +.endif diff --git a/instr_timing/source/common/ppu.s b/instr_timing/source/common/ppu.s new file mode 100644 index 0000000..21cad12 --- /dev/null +++ b/instr_timing/source/common/ppu.s @@ -0,0 +1,142 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts diff --git a/instr_timing/source/common/print.s b/instr_timing/source/common/print.s new file mode 100644 index 0000000..29fbc23 --- /dev/null +++ b/instr_timing/source/common/print.s @@ -0,0 +1,235 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr @nibble + pla + + pha + and #$0F + jsr @nibble + pla + rts + +@nibble: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: A, X, Y +print_dec: + pha + sta print_temp_ + txa + pha + lda print_temp_ + + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jsr print_char + pla + tax + pla + rts + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/instr_timing/source/common/shell.inc b/instr_timing/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/instr_timing/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/instr_timing/source/common/shell.s b/instr_timing/source/common/shell.s new file mode 100644 index 0000000..358cddf --- /dev/null +++ b/instr_timing/source/common/shell.s @@ -0,0 +1,382 @@ +; Common routines and runtime + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +; Move code to $E200 ($200 bytes for text output in devcarts +; where WRAM is mirrored to $E000) +.segment "CODE" + .res $2200 + +; Put shell code after user code, so user code is in more +; consistent environment +.segment "CODE2" + + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts +.endif + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sei + cld + ldx #$FF + txs + + ldx #0 + stx SNDCHN + .ifndef BUILD_NSF + stx PPUCTRL + .endif + + jsr report_result + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +.macro fill_ram_ Begin, End + .local Neg_size + Neg_size = (Begin) - (End) + ldxy #(Begin) - ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page +.endmacro + +; Disables interrupts and loops forever +.ifndef CUSTOM_FOREVER +forever: + sei + lda #0 + sta PPUCTRL +: beq :- + .res $10,$EA ; room for code to run loader +.endif + + +; Default NMI +.ifndef CUSTOM_NMI + zp_byte nmi_count + + nmi: + inc nmi_count + rti + + ; Waits for NMI. Must be using NMI handler that increments + ; nmi_count, with NMI enabled. + ; Preserved: X, Y + wait_nmi: + lda nmi_count + : cmp nmi_count + beq :- + rts +.endif + + +; Default IRQ +.ifndef CUSTOM_IRQ + irq: + bit SNDCHN ; clear APU IRQ flag + rti +.endif + +.endif + + +; Reports A in binary as high and low tones, with +; leading low tone for reference. Omits leading +; zeroes. Doesn't hang if no APU is present. +; Preserved: A, X, Y +play_hex: + pha + + ; Make low reference beep + clc + jsr @beep + + ; Remove high zero bits + sec +: rol a + bcc :- + + ; Play remaining bits + beq @zero +: jsr @beep + asl a + bne :- +@zero: + + delay_msec 300 + pla + rts + +; Plays low/high beep based on carry +; Preserved: A, X, Y +@beep: + pha + + ; Set up square + lda #1 + sta SNDCHN + sta $4001 + sta $4003 + adc #$FE ; period=$100 if carry, $1FF if none + sta $4002 + + ; Fade volume + lda #$0F +: ora #$30 + sta $4000 + delay_msec 8 + sec + sbc #$31 + bpl :- + + ; Silence + sta SNDCHN + delay_msec 160 + + pla + rts diff --git a/instr_timing/source/common/testing.s b/instr_timing/source/common/testing.s new file mode 100644 index 0000000..ba41f03 --- /dev/null +++ b/instr_timing/source/common/testing.s @@ -0,0 +1,106 @@ +; Utilities for writing test ROMs + +; In NVRAM so these can be used before initializing runtime, +; then runtime initialized without clearing them +nv_res test_code ; code of current test +nv_res test_name,2 ; address of name of current test, or 0 of none + + +; Sets current test code and optional name. Also resets +; checksum. +; Preserved: A, X, Y +.macro set_test code,name + pha + lda #code + jsr set_test_ + .ifblank name + setb test_name+1,0 + .else + .local Addr + setw test_name,Addr + seg_data "RODATA",{Addr: .byte name,0} + .endif + pla +.endmacro + +set_test_: + sta test_code + jmp reset_crc + + +; Initializes testing module +init_testing: + jmp init_crc + + +; Reports that all tests passed +tests_passed: + jsr print_filename + print_str newline,"Passed" + lda #0 + jmp exit + + +; Reports "Done" if set_test has never been used, +; "Passed" if set_test 0 was last used, or +; failure if set_test n was last used. +tests_done: + ldx test_code + jeq tests_passed + inx + bne test_failed + jsr print_filename + print_str newline,"Done" + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + ldx test_code + + ; Treat $FF as 1, in case it wasn't ever set + inx + bne :+ + inx + stx test_code +: + ; If code >= 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/instr_timing/source/common/text_out.s b/instr_timing/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/instr_timing/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +The first time a line of text is completed, the console initializes the +PPU. Most print routines update a running CRC-32 checksum which can be +checked with check_crc. This allows ALL the output to be checked very +easily. If the checksum doesn't match, it is printed, so during +development the code can be run on a NES to get the correct checksum, +which is then typed into the test code. The checksum is also useful when +comparing different outputs; rather than having to examine all the +output, one need only compare checksums. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported by the included code, due to +their complexity. + + +Macros +------ +Some macros are used to make common operations more convenient, defined +in common/macros.inc. The left is equivalent to the right: + + Macro Equivalent + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + setb addr,byte lda #byte + sta addr + + setw addr,word lda #word + sta addr+1 + + ldxy #value ldx #>value + ldy # diff --git a/mmc3_irq_tests/1.Clocking.nes b/mmc3_irq_tests/1.Clocking.nes new file mode 100644 index 0000000..47ed2dd Binary files /dev/null and b/mmc3_irq_tests/1.Clocking.nes differ diff --git a/mmc3_irq_tests/2.Details.nes b/mmc3_irq_tests/2.Details.nes new file mode 100644 index 0000000..842e26f Binary files /dev/null and b/mmc3_irq_tests/2.Details.nes differ diff --git a/mmc3_irq_tests/3.A12_clocking.nes b/mmc3_irq_tests/3.A12_clocking.nes new file mode 100644 index 0000000..00094de Binary files /dev/null and b/mmc3_irq_tests/3.A12_clocking.nes differ diff --git a/mmc3_irq_tests/4.Scanline_timing.nes b/mmc3_irq_tests/4.Scanline_timing.nes new file mode 100644 index 0000000..202d04f Binary files /dev/null and b/mmc3_irq_tests/4.Scanline_timing.nes differ diff --git a/mmc3_irq_tests/5.MMC3_rev_A.nes b/mmc3_irq_tests/5.MMC3_rev_A.nes new file mode 100644 index 0000000..4881ee6 Binary files /dev/null and b/mmc3_irq_tests/5.MMC3_rev_A.nes differ diff --git a/mmc3_irq_tests/5.MMC3_rev_A.sav b/mmc3_irq_tests/5.MMC3_rev_A.sav new file mode 100644 index 0000000..4960062 --- /dev/null +++ b/mmc3_irq_tests/5.MMC3_rev_A.sav @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/mmc3_irq_tests/6.MMC3_rev_B.nes b/mmc3_irq_tests/6.MMC3_rev_B.nes new file mode 100644 index 0000000..c58b30c Binary files /dev/null and b/mmc3_irq_tests/6.MMC3_rev_B.nes differ diff --git a/mmc3_irq_tests/readme.txt b/mmc3_irq_tests/readme.txt new file mode 100644 index 0000000..7b0fd2d --- /dev/null +++ b/mmc3_irq_tests/readme.txt @@ -0,0 +1,128 @@ +NTSC NES MMC3 IRQ Counter Test ROMs +----------------------------------- +These ROMs test much of MMC3 IRQ counter behavior on an NTSC NES PPU. +They have been tested on an actual NES with on several MMC3 cartridges +and all give a passing result. Many tests are written specifically to +catch likely errors in an emulator. + +Each ROM runs several tests and reports the result on screen and by +beeping a number of times. Failure codes for each ROM are listed below. +It's best to run the tests in order, because some earlier ROMs test +things that later ones assume will work properly. + +The ROMs mainly test behavior by manually clocking the MMC3's IRQ +counter by writing to $2006 to change the current VRAM address. The last +two ROMs test different revisions of the MMC3, so at most only one will +pass on a particular emulator. + +All the asm source is included, and most tests are clearly divided into +sections. The code runs on a custom devcart and assembler so it will +require some effort to assemble. Contact me if you'd like assistance +porting them to your setup. + + +MMC3 Operation +-------------- +I have fairly thoroughly tested MMC3 IRQ counter operation and found the +following behaviors that differ as described in kevtris's (draft?) MMC3 +documentation: + +- The counter can be clocked manually via bit 12 of the VRAM address +even when $2000 = $00 (bg and sprites both use tiles from $0xxx). + +- The IRQ flag is not set when the counter is cleared by writing to +$C001. + +- I uncovered some pathological behavior that isn't covered by the test +ROMs. If $C001 is written, the counter clocked, then $C001 written +again, on the next counter clock the counter will be ORed with $80 +(revision B)/frozen (revision A) and neither decremented nor reloaded. +If $C001 is written again at this point, on the next counter clock it +will be reloaded normally. I put a check in my emulator and none of the +several games I tested ever caused this situation to occur, so it's +probably not a good idea to implement this. + +The MMC3 in Crystalis (referred to here as revision A) worked as +described in kevtris's document, with the above changes. The MMC3 in +Super Mario Bros. 3 and Mega Man 3 (I think revision B, but I don't have +the special screw driver) further differed when $C000 was written with +0: + +- Writing 0 to $C000 works no differently than any other value written; +it will cause the counter to be reloaded every time it is clocked (once +it reaches zero). + +- When the counter is clocked, if it's not zero, it is decremented, +otherwise it is reloaded with the last value written to $C000. *After* +decrementing/reloading, if the counter is zero and IRQ is enabled via +$E001, the IRQ flag is set. + + +1.Clocking +---------- +Tests counter operation. Requires support for clocking via manual +toggling of VRAM address. + +2) Counter/IRQ/A12 clocking isn't working at all +3) Should decrement when A12 is toggled via $2006 +4) Writing to $C000 shouldn't cause reload +5) Writing to $C001 shouldn't cause immediate reload +6) Should reload (no decrement) on first clock after clear +7) IRQ should be set when counter is decremented to 0 +8) IRQ should never be set when disabled +9) Should reload when clocked when counter is 0 + + +2.Details +--------- +Tests counter details. + +2) Counter isn't working when reloaded with 255 +3) Counter should run even when IRQ is disabled +4) Counter should run even after IRQ flag has been set +5) IRQ should not be set when counter reloads with non-zero +6) IRQ should not be set when counter is cleared via $C001 +7) Counter should be clocked 241 times in PPU frame + + +3.A12 Clocking +-------------- +Tests clocking via bit 12 of VRAM address. + +2) Shouldn't be clocked when A12 doesn't change +3) Shouldn't be clocked when A12 changes to 0 +4) Should be clocked when A12 changes to 1 via $2006 write +5) Should be clocked when A12 changes to 1 via $2007 read +6) Should be clocked when A12 changes to 1 via $2007 write + + +4.Scanline Timing +----------------- +Tests basic timing for scanlines 0, 1, and 240. + +2) Scanline 0 time is too soon +3) Scanline 0 time is too late +4) Scanline 1 time is too soon +5) Scanline 1 time is too late +6) Scanline 239 time is too soon +7) Scanline 239 time is too late + + +5.MMC3 Rev A +------------ +Tests MMC3 revision A differences (tested with Crystalis board). + +2) IRQ should be set when reloading to 0 after clear +3) IRQ shouldn't occur when reloading after counter normally reaches 0 + + +6.MMC3 Rev B +------------ +Tests MMC3 revision B differences (tested with Super Mario Bros. 3 and +Mega Man 3 boards). + +2) Should reload and set IRQ every clock when reload is 0 +3) IRQ should be set when counter is 0 after reloading + +-- +Shay Green (swap to e-mail) diff --git a/mmc3_irq_tests/source/1.Clocking.asm b/mmc3_irq_tests/source/1.Clocking.asm new file mode 100644 index 0000000..cc14f14 --- /dev/null +++ b/mmc3_irq_tests/source/1.Clocking.asm @@ -0,0 +1,85 @@ +; Tests MMC3 IRQ counter operation. Requires support for clocking via +; manual toggling of VRAM address. + + .include "prefix_mmc3_validation.a" + +test_name: + .db "MMC3 IRQ COUNTER",0 + +reset: + jsr begin_mmc3_tests + + lda #2;) Counter/IRQ/A12 clocking isn't working at all + ldx #10 + jsr begin_counter_test + jsr clock_counter + jsr clock_counter + jsr should_be_clear + + lda #3;) Should decrement when A12 is toggled via $2006 + ldx #2 + jsr begin_counter_test + ldx #9 + jsr clock_counter_x + jsr should_be_set + + lda #4;) Writing to $C000 shouldn't cause reload + ldx #2 + jsr begin_counter_test + jsr clock_counter ; reloads with 2 + lda #100 + jsr set_reload + ldx #8 + jsr clock_counter_x + jsr should_be_set + + lda #5;) Writing to $C001 shouldn't cause immediate reload + ldx #1 + jsr begin_counter_test + lda #1 + jsr set_reload + jsr clear_counter + lda #4 + jsr set_reload + jsr clock_counter ; 4 + jsr clock_counter ; 3 + jsr should_be_clear + + lda #6;) Should reload (no decrement) on first clock after clear + ldx #2 + jsr begin_counter_test + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr should_be_clear + + lda #7;) IRQ should be set when counter is decremented to 0 + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr should_be_set + + lda #8;) IRQ should never be set when disabled + ldx #1 + jsr begin_counter_test + jsr disable_irq + ldx #10 + jsr clock_counter_x + jsr should_be_clear + + lda #9;) Should reload when clocked when counter is 0 + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + lda #10 + jsr set_reload + jsr clock_counter ; 0 + lda #2 + jsr set_reload + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clear_irq + jsr clock_counter ; 0 + jsr should_be_set + + jmp tests_passed diff --git a/mmc3_irq_tests/source/2.Details.asm b/mmc3_irq_tests/source/2.Details.asm new file mode 100644 index 0000000..84aed36 --- /dev/null +++ b/mmc3_irq_tests/source/2.Details.asm @@ -0,0 +1,88 @@ +; Tests MMC3 IRQ counter details + + .include "prefix_mmc3_validation.a" + +test_name: + .db "MMC3 IRQ COUNTER DETAILS",0 + +reset: + jsr begin_mmc3_tests + + lda #2;) Counter isn't working when reloaded with 255 + ldx #255 + jsr begin_counter_test + ldx #255 + jsr clock_counter_x + jsr should_be_clear + jsr clock_counter + jsr should_be_set + + lda #3;) Counter should run even when IRQ is disabled + ldx #2 + jsr begin_counter_test + jsr disable_irq + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr enable_irq + jsr should_be_clear + jsr clock_counter ; 0 + jsr should_be_set + + lda #4;) Counter should run even after IRQ flag has been set + ldx #2 + jsr begin_counter_test + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clock_counter ; 2 + jsr clear_irq + jsr clock_counter ; 1 + jsr should_be_clear + jsr clock_counter ; 0 + jsr should_be_set + + lda #5;) IRQ should not be set when counter reloads with non-zero + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clear_irq + jsr clock_counter ; 1 + jsr should_be_clear + + lda #6;) IRQ should not be set when counter is cleared via $C001 + ldx #2 + jsr begin_counter_test + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clear_counter + jsr should_be_clear + + lda #0 + jsr clear_vram + jsr clear_sprites + lda #7;) Counter should be clocked 241 times in PPU frame + ldx #241 + jsr begin_counter_test + jsr wait_vbl + lda #0 + sta $2005 + sta $2005 + lda #$08 ; sprites use tiles at $1xxx + sta $2000 + lda #$18 ; enable bg and sprites + sta $2001 + ldy #25 ; 29800 delay + lda #237 + jsr delay_ya8 + lda #$00 ; disable rendering + sta $2001 + jsr should_be_clear + jsr clock_counter + jsr should_be_set + + jmp tests_passed + diff --git a/mmc3_irq_tests/source/3.A12_clocking.asm b/mmc3_irq_tests/source/3.A12_clocking.asm new file mode 100644 index 0000000..63de7d0 --- /dev/null +++ b/mmc3_irq_tests/source/3.A12_clocking.asm @@ -0,0 +1,88 @@ +; Tests MMC3 IRQ clocking via bit 12 of VRAM address + + .include "prefix_mmc3_validation.a" + +test_name: + .db "MMC3 IRQ COUNTER A12",0 + +reset: + jsr begin_mmc3_tests + + lda #0 ; disable PPU, sprites and bg use $0xxx patterns + sta $2000 + sta $2001 + + lda #2;) Shouldn't be clocked when A12 doesn't change + ldx #1 + jsr begin_counter_test + lda #$00 ; transition everything but A12 + ldx #$ef + ldy #$ff + sta $2006 + sta $2006 + stx $2006 + sty $2006 + sta $2006 + sta $2006 + stx $2006 + sty $2006 + sta $2006 + sta $2006 + jsr should_be_clear + + lda #3;) Shouldn't be clocked when A12 changes to 0 + ldx #1 + jsr begin_counter_test + jsr clock_counter ; avoid pathological behavior + lda #$10 + sta $2006 + sta $2006 + jsr clear_counter + jsr clear_irq + ldx #$00 + ldy #$10 + stx $2006 + stx $2006 + sty $2006 ; counter = 1 + stx $2006 + stx $2006 ; second 1 to 0 transition + stx $2006 + jsr should_be_clear + + lda #4;) Should be clocked when A12 changes to 1 via $2006 write + ldx #1 + jsr begin_counter_test + jsr clock_counter + lda #$00 ; transition A12 from 0 to 1 + sta $2006 + sta $2006 + lda #$10 + sta $2006 + sta $2006 + jsr should_be_set + + lda #5;) Should be clocked when A12 changes to 1 via $2007 read + ldx #1 + jsr begin_counter_test + jsr clock_counter + lda #$0f ; vaddr = $0fff + sta $2006 + lda #$ff + sta $2006 + jsr should_be_clear + bit $2007 + jsr should_be_set + + lda #6;) Should be clocked when A12 changes to 1 via $2007 write + ldx #1 + jsr begin_counter_test + jsr clock_counter + lda #$0f ; vaddr = $0fff + sta $2006 + lda #$ff + sta $2006 + jsr should_be_clear + sta $2007 + jsr should_be_set + + jmp tests_passed diff --git a/mmc3_irq_tests/source/4.Scanline_timing.asm b/mmc3_irq_tests/source/4.Scanline_timing.asm new file mode 100644 index 0000000..4415abe --- /dev/null +++ b/mmc3_irq_tests/source/4.Scanline_timing.asm @@ -0,0 +1,77 @@ +; Tests basic MMC3 IRQ timing for scanlines 0, 1, and 240 + + .include "prefix_mmc3_validation.a" + +test_name: + .db "MMC3 IRQ TIMING",0 + +reset: + jsr begin_mmc3_tests + + lda #0 + jsr clear_vram + jsr clear_sprites + + lda #0 + jsr begin_test + ldy #5 ; 2327 delay + lda #91 + jsr delay_ya5 + lda #2;) Scanline 0 time is too soon/late + jsr check_irq_time + + lda #1 + jsr begin_test + ldy #20 ; 2440 delay + lda #23 + jsr delay_ya3 + lda #4;) Scanline 1 time is too soon/late + jsr check_irq_time + + lda #240 + jsr begin_test + ldy #173 ; 29606 delay + lda #33 + jsr delay_ya6 + lda #6;) Scanline 239 time is too soon/late + jsr check_irq_time + + jmp tests_passed + .code + +begin_test: + pha + jsr sync_ppu_align2_30 + lda #$08 ; 12 + sta $2000 + lda #$18 + sta $2001 + pla ; 20 + sta r_set_reload + sta r_clear_counter + sta r_disable_irq + sta r_enable_irq + rts ; 6 + .code + +get_irq_time: + ldx #0 + cli + nop + ldx #1 + nop + sei ; IRQ causes return from this routine + ldx #2 + rts + .code + +check_irq_time: + sta result + jsr get_irq_time + dex + bne + + rts +: bmi + + inc result +: jmp report_final_result + .code diff --git a/mmc3_irq_tests/source/5.MMC3_rev_A.asm b/mmc3_irq_tests/source/5.MMC3_rev_A.asm new file mode 100644 index 0000000..6fd41bc --- /dev/null +++ b/mmc3_irq_tests/source/5.MMC3_rev_A.asm @@ -0,0 +1,30 @@ +; Tests MMC3 revision A differences + + .include "prefix_mmc3_validation.a" + +test_name: + .db "MMC3 IRQ COUNTER REVISION A",0 + +reset: + jsr begin_mmc3_tests + + lda #2;) IRQ should be set when reloading to 0 after clear + ldx #0 + jsr begin_counter_test + jsr clock_counter ; 0 + jsr should_be_set + + lda #3;) IRQ shouldn't occur when reloading after counter normally reaches 0 + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + lda #0 + jsr set_reload + jsr clock_counter ; 0 + jsr clear_irq + jsr clock_counter ; 0 + jsr should_be_clear + ldx #255 + + jmp tests_passed + diff --git a/mmc3_irq_tests/source/6.MMC3_rev_B.asm b/mmc3_irq_tests/source/6.MMC3_rev_B.asm new file mode 100644 index 0000000..636e693 --- /dev/null +++ b/mmc3_irq_tests/source/6.MMC3_rev_B.asm @@ -0,0 +1,34 @@ +; Tests MMC3 revision B differences + + .include "prefix_mmc3_validation.a" + +test_name: + .db "MMC3 IRQ COUNTER REVISION B",0 + +reset: + jsr begin_mmc3_tests + + lda #2;) Should reload and set IRQ every clock when reload is 0 + ldx #0 + jsr begin_counter_test + jsr clock_counter ; 0 + jsr should_be_set + jsr clock_counter ; 0 + jsr should_be_set + jsr clock_counter ; 0 + jsr should_be_set + + lda #3;) IRQ should be set when counter is 0 after reloading + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clear_irq + lda #0 + jsr set_reload + jsr clock_counter ; 0 + jsr should_be_set + + + jmp tests_passed + diff --git a/mmc3_irq_tests/source/console.asm b/mmc3_irq_tests/source/console.asm new file mode 100644 index 0000000..793d9c7 --- /dev/null +++ b/mmc3_irq_tests/source/console.asm @@ -0,0 +1,101 @@ + +console_pos = $7f0 + +; Print char A to console +; Preserved: A, X, Y +print_char: + jsr wait_vbl ; wait for safe access +print_char_no_wait: + pha + lda #$20 + sta $2006 + inc console_pos + lda console_pos + sta $2006 + lda #0 ; restore scroll + sta $2005 + sta $2005 + pla + sta $2007 + rts + .code + +; Go to next line +; Preserved: A, X, Y +console_newline: + pha + lda console_pos + and #$e0 + clc + adc #$21 + sta console_pos + pla + rts + .code + +; Initialize console +init_console: + lda #$81 + sta console_pos + + jsr wait_vbl ; init ppu + lda #0 + sta $2000 + sta $2001 + + lda #$3f ; load palette + jsr set_vpage + lda #15 ; bg + ldx #48 ; fg + ldy #8 +pal: sta $2007 + stx $2007 + stx $2007 + stx $2007 + dey + bne pal + + lda #$02 ; load tiles + jsr set_vpage + lda #chr_data.lsb + sta <$f0 + lda #chr_data.msb + sta <$f1 + ldy #0 + lda #59 ; 59 chars in data + sta <$f2 +chr_loop: + ldx #8 + lda #0 +: sta $2007 + dex + bne - + + ldx #8 +: lda ($f0),y + iny + sta $2007 + dex + bne - + + tya + bne + + inc <$f1 +: dec <$f2 + bne chr_loop + + lda #32 + jsr fill_nametable + + jsr wait_vbl ; enable ppu + lda #0 + sta $2005 + sta $2005 + lda #$08 + sta $2001 + rts + .code + +chr_data: + .incbin "chr.bin" + diff --git a/mmc3_irq_tests/source/debug.asm b/mmc3_irq_tests/source/debug.asm new file mode 100644 index 0000000..903bf97 --- /dev/null +++ b/mmc3_irq_tests/source/debug.asm @@ -0,0 +1,149 @@ + +; Beep A times. Made to require minimal features from APU. +debug_beeps: +beep_loop: + pha + + lda #1 ; set up square 1 + sta $4015 + sta $4003 + sta $4001 + sta $4002 + + lda #$0f ; fade volume +: pha + eor #$30 + sta $4000 + lda #8 + jsr delay_msec + pla + clc + adc #-1 + bpl - + + sta $4015 ; silence square for a bit + lda #120 + jsr delay_msec + + pla + clc + adc #-1 + bne beep_loop + rts + .code + +; Print indicated register to console as $hh +; Preserved: A, X, Y, P +print_a: + php + jsr debug_byte + plp + rts + .code + +print_x: + php + pha + txa + jsr debug_byte + pla + plp + rts + .code + +print_y: + php + pha + tya + jsr debug_byte + pla + plp + rts + .code + +print_s: + php + pha + txa + pha + tsx + txa + jsr debug_byte + pla + tax + pla + plp + rts + .code + +print_p: + php + pha + php + pla + jsr debug_byte + pla + plp + rts + .code + +print_ay: + php + pha + lda #36 + jsr debug_char + pla + pha + jsr hex_byte + tya + jsr hex_byte + lda #32 ; ' ' + jsr debug_char_no_wait + pla + plp + rts + .code + +; Print address YA to console as $hhhh +; Preserved: A, X, Y +debug_addr: + pha + lda #36 ; '$' + jsr debug_char + tya + jsr hex_byte + jmp debug_byte_impl + .code + +; Print byte A to console as $hh +; Preserved: A, X, Y +debug_byte: + pha + lda #36 ; '$' + jsr debug_char +debug_byte_impl: + pla + pha + jsr hex_byte + lda #32 ; ' ' + jsr debug_char_no_wait + pla + rts + +hex_byte: + pha + lsr a + lsr a + lsr a + lsr a + jsr nybble + pla + and #$0f +nybble: + cmp #10 + bcc not_letter + adc #6 ; relies on carry being set +not_letter: + adc #$30 + jmp debug_char_no_wait + .code diff --git a/mmc3_irq_tests/source/delays.asm b/mmc3_irq_tests/source/delays.asm new file mode 100644 index 0000000..db4f23e --- /dev/null +++ b/mmc3_irq_tests/source/delays.asm @@ -0,0 +1,118 @@ +; to do: delay loops that take only a single count + +; Delay for almost A milliseconds (A * 0.999009524 msec) +; Preserved: X, Y +delay_msec: + pha ; 3 + lda #253 ; 2 + sec ; 2 +delay_msec_: + nop ; 2 + adc #-2 ; 2 + bne delay_msec_ ; 3 + ; -1 + pla ; 4 + clc ; 2 + adc #-1 ; 2 + bne delay_msec ; 3 + rts + .code + +; Delay for almost 'A / 10' milliseconds (A * 0.099453968 msec) +; Preserved: X, Y +delay_01msec: + pha ; 3 + lda #18 ; 2 + sec ; 2 +delay_01msec_: + nop ; 2 + nop ; 2 + adc #-2 ; 2 + bne delay_01msec_ ; 3 + ; -1 + pla ; 4 + clc ; 2 + adc #-1 ; 2 + bne delay_01msec ; 3 + rts + .code + +; Delay for almost A*10 milliseconds +; Preserved: X, Y +delay_10msec: + pha + lda #10 + jsr delay_msec + pla + clc + adc #-1 + bne delay_10msec + rts + .code + +; Delay n clocks +; Preserved: P, A, X, Y +delay_32: nop +delay_30: nop +delay_28: nop +delay_26: nop +delay_24: nop +delay_22: nop +delay_20: nop +delay_18: nop +delay_16: nop +delay_14: nop +delay_12: rts + +delay_33: nop +delay_31: nop +delay_29: nop +delay_27: nop +delay_25: nop +delay_23: nop +delay_21: nop +delay_19: nop +delay_17: beq + ; 5 +: bne + +: rts ; 6 + .code + +; Delay (5 * A + 6) * Y + 7 + n clocks +delay_ya11: + .db $a2 ; 1 ldx #imm +delay_ya10: + .db $a2 ; 1 ldx #imm +delay_ya9: + .db $a2 ; 1 ldx #imm +delay_ya8: + .db $a2 ; 1 ldx #imm +delay_ya7: + .db $a2 ; 1 ldx #imm +delay_ya6: + .db $a2 ; 1 ldx #imm +delay_ya5: + .db $a2 ; 1 ldx #imm +delay_ya4: + .db $a2 ; 1 ldx #imm +delay_ya3: + .db $a2 ; 1 ldx #imm +delay_ya2: + .db $a2 ; 1 ldx #imm +delay_ya1: + .db $a6 ; 1 ldx zp +delay_ya0: + nop ; 2 +delay_ya: + ; 2 lda # + ; 2 ldy # + ; 6 jsr + tax ; *2 +delay_yax: + dex ; **2 + bne delay_yax ; **3 + ; *-1 + dey ; *2 + bne delay_ya ; *3 + ; -1 + rts ; 6 + .code diff --git a/mmc3_irq_tests/source/ppu_sync.asm b/mmc3_irq_tests/source/ppu_sync.asm new file mode 100644 index 0000000..832eef3 --- /dev/null +++ b/mmc3_irq_tests/source/ppu_sync.asm @@ -0,0 +1,124 @@ + +; Same as sync_ppu_20 plus next frame is odd (clock subtracted) or even +; (no clock subtracted). +sync_ppu_odd_20: + lda #$80 + bne sync_ppu_frame_ +sync_ppu_even_20: + lda #$00 +sync_ppu_frame_: + pha + ; Synchronize with PPU + jsr sync_ppu_20 ; synchronize with PPU + + ; Run for two frames with BG enabled. One of the two frames will + ; be one PPU clock shorter. Note whether the first frame was the + ; shorter one. + + lda #$08 ; 6 enable bg + sta $2001 + ldy #41 ; 29785 delay + lda #144 + jsr delay_ya2 + nop ; 2 delay + pla ; 4 + eor $2002 ; 4 find whether frame was odd or even + pha ; 3 + ; run another enabled frame so that clock will + ; have been subtracted on one of the two frames + ldy #43 ; 29730+ delay + lda #137 + jsr delay_ya1 + lda #$00 ; 6 disable bg + sta $2001 + + ; If the first frame was shorter, wait three frames to switch + ; the even/odd synchronization without changing the CPU clock's + ; synchronization with the PPU clock. + + pla ; 4 + pha ; 3 + bpl + ; 3 + ; -1 + ldy #75 ; 89343 delay + lda #237 + jsr delay_ya1 +: pla ; 4 + rts ; 6 + .code + +; After return, 30 clocks until VBL flag will read +; as set, then 29781, then 29780. Turns PPU +; rendering, NMI, IRQ, and DMC off. +sync_ppu_align2_30: + pha + txa + pha + tya + pha + lda #0 ; disable dmc and irq + sta $4015 + sei + jsr wait_vbl + lda #0 ; disable bg and nmi + sta $2000 + sta $2001 + + bit $2002 +: bit $2002 ; 1 + bpl - ; 2 + + ldy #141 ; 29774 delay + lda #41 + jsr delay_ya6 + +: ldy #86 ; 29774 delay + lda #68 + jsr delay_ya1 + + bit $2002 ; 1 + bpl - ; 2 + + ldy #28 ; 29726 delay + lda #211 + jsr delay_ya1 + + pla ; 16 + tay + pla + tax + pla + + rts ; 6 + .code + +sync_ppu_align1_30: + jsr sync_ppu_align2_30 + ldy #86 ; 29775 delay + lda #68 + jsr delay_ya2 + rts + .code + +sync_ppu_align1_31: + jsr sync_ppu_align2_30 + ldy #86 ; 29774 delay + lda #68 + jsr delay_ya1 + rts + .code + +sync_ppu_align0_30: + jsr sync_ppu_align1_30 + ldy #86 ; 29774 delay + lda #68 + jsr delay_ya1 + rts + .code + +sync_ppu_20: + jsr sync_ppu_align2_30 + nop ; 4 + nop + rts ; 6 + .code diff --git a/mmc3_irq_tests/source/ppu_util.asm b/mmc3_irq_tests/source/ppu_util.asm new file mode 100644 index 0000000..51baa8f --- /dev/null +++ b/mmc3_irq_tests/source/ppu_util.asm @@ -0,0 +1,112 @@ + +; Clear VBL flag then wait for it to be set +; Preserved: A, X, Y +wait_vbl: + bit $2002 +: bit $2002 + bpl - + rts + .code + +; Set VRAM address to A * $100 +; Preserved: X, Y +set_vpage: + bit $2002 + sta $2006 + lda #0 + sta $2006 + rts + .code + +; Set VRAM address to A * $100 + X +; Preserved: A, X, Y +set_vaddr: + bit $2002 + sta $2006 + stx $2006 + rts + .code + +; Set X and Y scroll +; Preserved: A, X, Y +set_vscroll: + bit $2002 + stx $2005 + sty $2005 + rts + .code + +; Turn off NMI and disable BG and sprites +; Preserved: A, X, Y +disable_ppu: + pha + lda #0 + sta $2000 + sta $2001 + bit $2002 + sta $2006 + sta $2006 + pla + rts + .code + +; Set sprite memory to $ff +; Preserved: Y +clear_sprites: + lda #$ff + ldx #0 +: sta $2004 + dex + bne - + rts + .code + +; Clear/fill nametable with 0/A and clear attributes to 0 +clear_nametable: + lda #0 +fill_nametable: + pha + lda #$20 + jsr set_vpage + pla + ldx #240 +: sta $2007 + sta $2007 + sta $2007 + sta $2007 + dex + bne - + lda #0 + ldx #32 +: sta $2007 + dex + bne - + rts + .code + +; Clear/fill VRAM with 0/A +clear_vram: + lda #0 +fill_vram: + ldy #$24 + jmp fill_vram_ + .code + +; Clear/fill CHR with 0/A +clear_chr: + lda #0 +fill_chr: + ldy #$20 +fill_vram_: + tax + lda #0 + jsr set_vpage + txa + ldx #0 +: sta $2007 + dex + bne - + dey + bne - + rts + .code diff --git a/mmc3_irq_tests/source/prefix_cpu.asm b/mmc3_irq_tests/source/prefix_cpu.asm new file mode 100644 index 0000000..4ab7a81 --- /dev/null +++ b/mmc3_irq_tests/source/prefix_cpu.asm @@ -0,0 +1,18 @@ + .include "validation.a" + +; Arrange for IRQ to be pending on return. +; Preserved: A, X, Y +setup_pending_irq: + sei + pha + lda #$40 ; clear frame irq flag + sta $4017 + lda #$00 ; begin mode 0 + sta $4017 + + lda #40 ; wait for irq flag to be set + jsr delay_msec + pla + rts + .code + diff --git a/mmc3_irq_tests/source/prefix_mmc3.asm b/mmc3_irq_tests/source/prefix_mmc3.asm new file mode 100644 index 0000000..bd0d9aa --- /dev/null +++ b/mmc3_irq_tests/source/prefix_mmc3.asm @@ -0,0 +1,162 @@ + .include "prefix_swap.a" + +r_set_reload = $c000 +r_clear_counter = $c001 +r_disable_irq = $e000 +r_enable_irq = $e001 + +begin_mmc3_tests: + lda #200 + jsr delay_msec + lda #$c0 ; disable frame irq + sta $4017 + lda #0 ; disable PPU + sta $2001 + sta $2000 + bit $2002 ; vaddr = 0 + sta $2006 + sta $2006 + + ; Setup RESET handler + ldx $fffc + lda #$4c + sta 0,x + lda #$00 + sta 1,x + lda #$07 + sta 2,x + + ; Setup IRQ handler + ldx $fffe + lda #$4c + sta 0,x + lda #irq.lsb + sta 1,x + lda #irq.msb + sta 2,x + + lda #1 + jsr set_reload + jsr clear_counter + ldx #0 + jsr clock_counter_x + jsr clear_counter + ldx #0 + jsr clock_counter_x + jsr clear_irq + rts + + rts + .code + +; Decrement counter until IRQ occurs, then +; print how many decrements were required +print_count: + jsr get_count + jsr print_x + jsr clear_irq + rts + .code + +; Print $01 if IRQ is pending, $00 if not +print_pending: + jsr get_pending + lda #'P' + dex + beq + + lda #'-' +: jsr debug_char + lda #32 + jmp debug_char + .code + +set_reload: + sta r_set_reload + rts + +clear_counter: + lda #123 + sta r_clear_counter + rts + +disable_irq: + lda #123 + sta r_disable_irq + rts + +; Disable then re-enable IRQ +clear_irq: + lda #123 + sta r_disable_irq +enable_irq: + lda #123 + sta r_enable_irq + rts + .code + +; Clock counter X times +; Preserved: A, Y +clock_counter_x: +: jsr clock_counter + dex + bne - + rts + .code + +; Clock counter once +; Preserved: A, X, Y +clock_counter: + pha + lda #0 + sta $2006 + sta $2006 + lda #$10 + sta $2006 + sta $2006 + lda #0 + sta $2006 + sta $2006 + pla + rts + .code + +; Return X=1 if IRQ is pending, otherwise X=0 +; Preserved: Y +get_pending: + ldx #1 + cli + nop + sei ; IRQ causes return from this routine + nop + ldx #0 + rts + .code + +; Decrement counter until IRQ occurs and return +; number of decrements required in A. +get_count: + ldx #0 + cli + nop + nop +: lda #0 + sta $2006 + sta $2006 + lda #$10 + inx + sta $2006 + sta $2006 + bne - + sei + ldx #$ff + rts + +; Return from get_count +irq: pla + pla + pla + lda #0 + sta $2006 + sta $2006 + rts + .code diff --git a/mmc3_irq_tests/source/prefix_mmc3_validation.asm b/mmc3_irq_tests/source/prefix_mmc3_validation.asm new file mode 100644 index 0000000..526b456 --- /dev/null +++ b/mmc3_irq_tests/source/prefix_mmc3_validation.asm @@ -0,0 +1,27 @@ + .include "prefix_mmc3.a" + +begin_counter_test: + sta result + jsr clock_counter ; avoid pathological reload behavior + jsr clock_counter + txa + jsr set_reload + jsr clear_counter + jsr clear_irq + rts + .code + +should_be_clear: + jsr get_pending + cpx #0 + jsr error_if_ne + jsr clear_irq + rts + +should_be_set: + jsr get_pending + cpx #1 + jsr error_if_ne + jsr clear_irq + rts + diff --git a/mmc3_irq_tests/source/prefix_swap.asm b/mmc3_irq_tests/source/prefix_swap.asm new file mode 100644 index 0000000..e023946 --- /dev/null +++ b/mmc3_irq_tests/source/prefix_swap.asm @@ -0,0 +1,3 @@ + .include "prefix_cpu.a" + .include "util.a" + .include "ppu_sync.a" diff --git a/mmc3_irq_tests/source/runtime_swapcart.asm b/mmc3_irq_tests/source/runtime_swapcart.asm new file mode 100644 index 0000000..3dbb1cf --- /dev/null +++ b/mmc3_irq_tests/source/runtime_swapcart.asm @@ -0,0 +1,26 @@ + .include "delays.a" + .include "serial.a" + .include "debug.a" + .include "ppu_util.a" + +debug_newline: + lda #13 +debug_char: +debug_char_no_wait: + jmp serial_write + .code + +init_runtime: + rts + .code + +forever: + jmp $0700 + .code + + .default main = reset + + .org $fffa + .dw nmi + .dw main + .dw irq diff --git a/mmc3_irq_tests/source/validation.asm b/mmc3_irq_tests/source/validation.asm new file mode 100644 index 0000000..180d7ea --- /dev/null +++ b/mmc3_irq_tests/source/validation.asm @@ -0,0 +1,120 @@ + +result = $f8 + +default_test_name: + .db 0 + .default test_name = default_test_name + +; Report error if branch with given condition would be taken +error_if_ne: + bne report_final_result_ + rts +error_if_eq: + beq report_final_result_ + rts +error_if_mi: + bmi report_final_result_ + rts +error_if_pl: + bpl report_final_result_ + rts +error_if_cc: + bcc report_final_result_ + rts +error_if_cs: + bcs report_final_result_ + rts +error_if_vc: + bvc report_final_result_ + rts +error_if_vs: + bvs report_final_result_ + rts + +; Report passing result +tests_passed: + lda #1 + sta result +; Report final result code and wait forever +report_final_result: +report_final_result_: + sei ; disable interrupts + lda #0 + sta $2000 + jsr init_runtime + + lda test_name + beq no_name + jsr debug_char + ldx #1 +: lda test_name,x + beq no_name + jsr debug_char_no_wait + inx + jmp - + +no_name: + jsr debug_newline + + lda result + cmp #1 + beq test_passed + + lda #'F' + jsr debug_char + lda #'A' + jsr debug_char_no_wait + lda #'I' + jsr debug_char_no_wait + lda #'L' + jsr debug_char_no_wait + lda #'E' + jsr debug_char_no_wait + lda #'D' + jsr debug_char_no_wait + lda #32 ; ' ' + jsr debug_char_no_wait + lda #'#' + jsr debug_char_no_wait + + lda result + cmp #10 + bcc + + clc + adc #-10 + pha + lda #'1' + jsr debug_char_no_wait + pla +: clc + adc #'0' + jsr debug_char_no_wait +report_beeps: + lda result + jsr debug_beeps + jmp forever + +test_passed: + lda #'P' + jsr debug_char + lda #'A' + jsr debug_char_no_wait + lda #'S' + jsr debug_char_no_wait + lda #'S' + jsr debug_char_no_wait + lda #'E' + jsr debug_char_no_wait + lda #'D' + jsr debug_char_no_wait + jmp report_beeps + .code + + .default nmi = default_nmi + .default irq = default_irq + .code +default_irq: + bit $4015 +default_nmi: + rti + diff --git a/mmc3_test/1-clocking.nes b/mmc3_test/1-clocking.nes new file mode 100644 index 0000000..0c0d98a Binary files /dev/null and b/mmc3_test/1-clocking.nes differ diff --git a/mmc3_test/2-details.nes b/mmc3_test/2-details.nes new file mode 100644 index 0000000..44134a6 Binary files /dev/null and b/mmc3_test/2-details.nes differ diff --git a/mmc3_test/3-A12_clocking.nes b/mmc3_test/3-A12_clocking.nes new file mode 100644 index 0000000..8b40e5a Binary files /dev/null and b/mmc3_test/3-A12_clocking.nes differ diff --git a/mmc3_test/4-scanline_timing.nes b/mmc3_test/4-scanline_timing.nes new file mode 100644 index 0000000..81fc3e0 Binary files /dev/null and b/mmc3_test/4-scanline_timing.nes differ diff --git a/mmc3_test/5-MMC3.nes b/mmc3_test/5-MMC3.nes new file mode 100644 index 0000000..03d3565 Binary files /dev/null and b/mmc3_test/5-MMC3.nes differ diff --git a/mmc3_test/6-MMC6.nes b/mmc3_test/6-MMC6.nes new file mode 100644 index 0000000..844ffd7 Binary files /dev/null and b/mmc3_test/6-MMC6.nes differ diff --git a/mmc3_test/readme.txt b/mmc3_test/readme.txt new file mode 100644 index 0000000..ca6596c --- /dev/null +++ b/mmc3_test/readme.txt @@ -0,0 +1,80 @@ +NES MMC3 Tests +-------------- +These tests verify a small part of MMC3 (and some MMC6) behavior, mostly +related to the scanline counter and IRQ. They should be run in order. + + +Multi-tests +----------- +The NES/NSF builds in the main directory consist of multiple sub-tests. +When run, they list the subtests as they are run. The final result code +refers to the first sub-test that failed. For more information about any +failed subtests, run them individually from rom_singles/ and +nsf_singles/. + + +Flashes, clicks, other glitches +------------------------------- +If a test prints "passed", it passed, even if there were some flashes or +odd sounds. Only a test which prints "done" at the end requires that you +watch/listen while it runs in order to determine whether it passed. Such +tests involve things which the CPU cannot directly test. + + +Alternate output +---------------- +Tests generally print information on screen, but also report the final +result audibly, and output text to memory, in case the PPU doesn't work +or there isn't one, as in an NSF or a NES emulator early in development. + +After the tests are done, the final result is reported as a series of +beeps (see below). For NSF builds, any important diagnostic bytes are +also reported as beeps, before the final result. + + +Output at $6000 +--------------- +All text output is written starting at $6004, with a zero-byte +terminator at the end. As more text is written, the terminator is moved +forward, so an emulator can print the current text at any time. + +The test status is written to $6000. $80 means the test is running, $81 +means the test needs the reset button pressed, but delayed by at least +100 msec from now. $00-$7F means the test has completed and given that +result code. + +To allow an emulator to know when one of these tests is running and the +data at $6000+ is valid, as opposed to some other NES program, $DE $B0 +$G1 is written to $6001-$6003. + + +Audible output +-------------- +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason. See the source +code of the test for more information about the meaning of a test code. +They are found after the set_test macro. For example, the cause of test +code 3 would be found in a line containing set_test 3. Examples: + + Tones Binary Decimal Meaning + - - - - - - - - - - - - - - - - - - - - + low 0 0 passed + low high 01 1 failed + low high low 010 2 error 2 + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to prevent the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +-- +Shay Green diff --git a/mmc3_test/source/1-clocking.s b/mmc3_test/source/1-clocking.s new file mode 100644 index 0000000..62d24e2 --- /dev/null +++ b/mmc3_test/source/1-clocking.s @@ -0,0 +1,79 @@ +.include "test_mmc3.inc" + +main: + jsr begin_mmc3_tests + + set_test 2,"Counter/IRQ/A12 clocking isn't working at all" + ldx #10 + jsr begin_counter_test + jsr clock_counter + jsr clock_counter + jsr should_be_clear + + set_test 3,"Should decrement when A12 is toggled via PPUADDR" + ldx #2 + jsr begin_counter_test + ldx #9 + jsr clock_counter_x + jsr should_be_set + + set_test 4,"Writing to $C000 shouldn't cause reload" + ldx #2 + jsr begin_counter_test + jsr clock_counter ; reloads with 2 + lda #100 + jsr set_reload + ldx #8 + jsr clock_counter_x + jsr should_be_set + + set_test 5,"Writing to $C001 shouldn't cause immediate reload" + ldx #1 + jsr begin_counter_test + lda #1 + jsr set_reload + jsr clear_counter + lda #4 + jsr set_reload + jsr clock_counter ; 4 + jsr clock_counter ; 3 + jsr should_be_clear + + set_test 6,"Should reload (no decrement) on first clock after clear" + ldx #2 + jsr begin_counter_test + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr should_be_clear + + set_test 7,"IRQ should be set when counter is decremented to 0" + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr should_be_set + + set_test 8,"IRQ should never be set when disabled" + ldx #1 + jsr begin_counter_test + jsr disable_irq + ldx #10 + jsr clock_counter_x + jsr should_be_clear + + set_test 9,"Should reload when clocked when counter is 0" + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + lda #10 + jsr set_reload + jsr clock_counter ; 0 + lda #2 + jsr set_reload + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clear_irq + jsr clock_counter ; 0 + jsr should_be_set + + jmp tests_passed diff --git a/mmc3_test/source/2-details.s b/mmc3_test/source/2-details.s new file mode 100644 index 0000000..0812e05 --- /dev/null +++ b/mmc3_test/source/2-details.s @@ -0,0 +1,79 @@ +; Tests MMC3 IRQ counter details + +.include "test_mmc3.inc" + +main: + jsr begin_mmc3_tests + + set_test 2,"Counter isn't working when reloaded with 255" + ldx #255 + jsr begin_counter_test + ldx #255 + jsr clock_counter_x + jsr should_be_clear + jsr clock_counter + jsr should_be_set + + set_test 3,"Counter should run even when IRQ is disabled" + ldx #2 + jsr begin_counter_test + jsr disable_irq + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr enable_irq + jsr should_be_clear + jsr clock_counter ; 0 + jsr should_be_set + + set_test 4,"Counter should run even after IRQ flag has been set" + ldx #2 + jsr begin_counter_test + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clock_counter ; 2 + jsr clear_irq + jsr clock_counter ; 1 + jsr should_be_clear + jsr clock_counter ; 0 + jsr should_be_set + + set_test 5,"IRQ should not be set when counter reloads with non-zero" + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clear_irq + jsr clock_counter ; 1 + jsr should_be_clear + + set_test 6,"IRQ should not be set when counter is cleared via $C001" + ldx #2 + jsr begin_counter_test + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clear_counter + jsr should_be_clear + + lda #0 + ;jsr clear_vram + jsr clear_oam + set_test 7,"Counter should be clocked 241 times in PPU frame" + ldx #241 + jsr begin_counter_test + jsr wait_vbl + setb PPUSCROLL,0 + sta PPUSCROLL + setb PPUCTRL,$08 ; sprites use tiles at $1xxx + setb PPUMASK,$18 ; enable bg and sprites + delay 29800 + setb PPUMASK,$00 ; disable rendering + jsr should_be_clear + jsr clock_counter + jsr should_be_set + + jmp tests_passed + diff --git a/mmc3_test/source/3-A12_clocking.s b/mmc3_test/source/3-A12_clocking.s new file mode 100644 index 0000000..0d37c6f --- /dev/null +++ b/mmc3_test/source/3-A12_clocking.s @@ -0,0 +1,77 @@ +; Tests MMC3 IRQ clocking via bit 12 of VRAM address + +.include "test_mmc3.inc" + +main: + jsr begin_mmc3_tests + + setb PPUCTRL,0 ; disable PPU, sprites and bg use $0xxx patterns + sta PPUMASK + + set_test 2,"Shouldn't be clocked when A12 doesn't change" + ldx #1 + jsr begin_counter_test + lda #$00 ; transition everything but A12 + ldx #$ef + ldy #$ff + sta PPUADDR + sta PPUADDR + stx PPUADDR + sty PPUADDR + sta PPUADDR + sta PPUADDR + stx PPUADDR + sty PPUADDR + sta PPUADDR + sta PPUADDR + jsr should_be_clear + + set_test 3,"Shouldn't be clocked when A12 changes to 0" + ldx #1 + jsr begin_counter_test + jsr clock_counter ; avoid pathological behavior + setb PPUADDR,$10 + sta PPUADDR + jsr clear_counter + jsr clear_irq + ldx #$00 + ldy #$10 + stx PPUADDR + stx PPUADDR + sty PPUADDR ; counter = 1 + stx PPUADDR + stx PPUADDR ; second 1 to 0 transition + stx PPUADDR + jsr should_be_clear + + set_test 4,"Should be clocked when A12 changes to 1 via PPUADDR write" + ldx #1 + jsr begin_counter_test + jsr clock_counter + setb PPUADDR,$00 ; transition A12 from 0 to 1 + sta PPUADDR + setb PPUADDR,$10 + sta PPUADDR + jsr should_be_set + + set_test 5,"Should be clocked when A12 changes to 1 via PPUDATA read" + ldx #1 + jsr begin_counter_test + jsr clock_counter + setb PPUADDR,$0f ; vaddr = $0fff + setb PPUADDR,$ff + jsr should_be_clear + bit PPUDATA + jsr should_be_set + + set_test 6,"Should be clocked when A12 changes to 1 via PPUDATA write" + ldx #1 + jsr begin_counter_test + jsr clock_counter + setb PPUADDR,$0f ; vaddr = $0fff + setb PPUADDR,$ff + jsr should_be_clear + sta PPUDATA + jsr should_be_set + + jmp tests_passed diff --git a/mmc3_test/source/4-scanline_timing.s b/mmc3_test/source/4-scanline_timing.s new file mode 100644 index 0000000..fb36737 --- /dev/null +++ b/mmc3_test/source/4-scanline_timing.s @@ -0,0 +1,123 @@ +; Tests MMC3 IRQ timing to PPU clock accuracy. Tests both modes, +; $2000=$08 and $2000=$10. +; +; Timing tested is between $2002 reads of VBL flag first set, +; and IRQ occurring. + +.include "test_mmc3.inc" +.include "sync_vbl.s" + +.macro test mode, count, n + .local n_ + n_ = (n) + 1 + setb PPUMASK,0 + jsr sync_vbl_even + delay_ppu_even (n_ .MOD 3) + 9 + setb PPUCTRL,mode + lda #count + jsr begin_ + delay n_/3 - 3 - 43 + jsr end_ +.endmacro + +begin_: + pha + setb PPUMASK,$18 + pla + sta r_set_reload + sta r_clear_counter + sta r_disable_irq + sta r_enable_irq + rts + +end_: + setb irq_flag,$10 + cli + nop + nop + inc irq_flag + delay 1000 + sei + nop + lda irq_flag + cmp #$11 + beq @no_irq + rts + +@no_irq: + set_test 14,"IRQ never occurred" + jmp test_failed + +main: + jsr begin_mmc3_tests + + jsr clear_oam + + scanline_0_08 = 6976 + scanline_1_08 = scanline_0_08 + + set_test 2,"Scanline 0 IRQ should occur later when $2000=$08" + test $08, 0, scanline_0_08 - 1 + cmp #$22 + jne test_failed + + set_test 3,"Scanline 0 IRQ should occur sooner when $2000=$08" + test $08, 0, scanline_0_08 + cmp #$21 + jne test_failed + + set_test 4,"Scanline 1 IRQ should occur later when $2000=$08" + test $08, 1, scanline_1_08 + 341 - 1 + cmp #$22 + jne test_failed + + set_test 5,"Scanline 1 IRQ should occur sooner when $2000=$08" + test $08, 1, scanline_1_08 + 341 + cmp #$21 + jne test_failed + + set_test 6,"Scanline 239 IRQ should occur later when $2000=$08" + test $08, 239, scanline_1_08 + 239*341 - 1 + cmp #$22 + jne test_failed + + set_test 7,"Scanline 239 IRQ should occur sooner when $2000=$08" + test $08, 239, scanline_1_08 + 239*341 + cmp #$21 + jne test_failed + + + scanline_0_10 = 6976 - 256 + scanline_1_10 = scanline_0_10 - 21 + + set_test 8,"Scanline 0 IRQ should occur later when $2000=$10" + test $10, 0, scanline_0_10 - 1 + cmp #$22 + jne test_failed + + set_test 9,"Scanline 0 IRQ should occur sooner when $2000=$10" + test $10, 0, scanline_0_10 + cmp #$21 + jne test_failed + + set_test 10,"Scanline 1 IRQ should occur later when $2000=$10" + test $10, 1, scanline_1_10 + 341 - 1 + cmp #$22 + jne test_failed + + set_test 11,"Scanline 1 IRQ should occur sooner when $2000=$10" + test $10, 1, scanline_1_10 + 341 + cmp #$21 + jne test_failed + + set_test 12,"Scanline 239 IRQ should occur later when $2000=$10" + test $10, 239, scanline_1_10 + 239*341 - 1 + cmp #$22 + jne test_failed + + set_test 13,"Scanline 239 IRQ should occur sooner when $2000=$10" + test $10, 239, scanline_1_10 + 239*341 + cmp #$21 + jne test_failed + + jmp tests_passed diff --git a/mmc3_test/source/5-MMC3.s b/mmc3_test/source/5-MMC3.s new file mode 100644 index 0000000..4b4eb5b --- /dev/null +++ b/mmc3_test/source/5-MMC3.s @@ -0,0 +1,31 @@ +; Tests MMC3-specifics + +.include "test_mmc3.inc" + +main: + jsr begin_mmc3_tests + + set_test 2,"Should reload and set IRQ every clock when reload is 0" + ldx #0 + jsr begin_counter_test + jsr clock_counter ; 0 + jsr should_be_set + jsr clock_counter ; 0 + jsr should_be_set + jsr clock_counter ; 0 + jsr should_be_set + + set_test 3,"IRQ should be set when counter is 0 after reloading" + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clear_irq + lda #0 + jsr set_reload + jsr clock_counter ; 0 + jsr should_be_set + + + jmp tests_passed + diff --git a/mmc3_test/source/6-MMC6.s b/mmc3_test/source/6-MMC6.s new file mode 100644 index 0000000..7c53dd1 --- /dev/null +++ b/mmc3_test/source/6-MMC6.s @@ -0,0 +1,31 @@ +; Tests MMC6-specific behavior. Some MMC3 chips also have this behavior, +; though their markings appear identical to those that have normal +; MMC3 behavior. My copy of Crystalis in particular behaves this way, +; but not my copy of Super Mario Bros. 3, even though both have a chip +; marked MMC3B. + +.include "test_mmc3.inc" + +main: + jsr begin_mmc3_tests + + set_test 2,"IRQ should be set when reloading to 0 after clear" + ldx #0 + jsr begin_counter_test + jsr clock_counter ; 0 + jsr should_be_set + + set_test 3,"IRQ shouldn't occur when reloading after counter normally reaches 0" + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + lda #0 + jsr set_reload + jsr clock_counter ; 0 + jsr clear_irq + jsr clock_counter ; 0 + jsr should_be_clear + ldx #255 + + jmp tests_passed + diff --git a/mmc3_test/source/common/ascii.chr b/mmc3_test/source/common/ascii.chr new file mode 100644 index 0000000..2c5b26b Binary files /dev/null and b/mmc3_test/source/common/ascii.chr differ diff --git a/mmc3_test/source/common/build_rom.s b/mmc3_test/source/common/build_rom.s new file mode 100644 index 0000000..7c6d139 --- /dev/null +++ b/mmc3_test/source/common/build_rom.s @@ -0,0 +1,93 @@ +; Builds program as iNES ROM + +; Default is 16K PRG and 8K CHR ROM, NROM (0) + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM=1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM=1 ; Use mapper that supports 8K WRAM in cart +CUSTOM_MAPPER=n ; Specify mapper number +.endif + +.ifndef CUSTOM_MAPPER + .ifdef CART_WRAM + CUSTOM_MAPPER = 2 ; UNROM + .else + CUSTOM_MAPPER = 0 ; NROM + .endif +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte $4E,$45,$53,26 ; "NES" EOF + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte CUSTOM_MAPPER*$10+$01 ; vertical mirroring +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word -1,-1,-1, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .define CHARS "CHARS_PRG" + .segment CHARS + ascii_chr: + + .segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: +.else + .define CHARS "CHARS" + .segment "CHARS_ASCII" + .align $200 + .incbin "ascii.chr" + .res $1800 +.endif + +.segment CHARS + .res $10,0 + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +; Move code to $C000 +.segment "DMC" + .res $4000 + +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + load_ascii_chr + .endif + rts + +post_exit: + jsr set_final_result + jsr play_hex + jmp forever + +; This helps devcart recover after running test. +; It is never executed by test ROM. +.segment "LOADER" + .incbin "devcart.bin" + +.code +.align 256 diff --git a/mmc3_test/source/common/console.s b/mmc3_test/source/common/console.s new file mode 100644 index 0000000..036553c --- /dev/null +++ b/mmc3_test/source/common/console.s @@ -0,0 +1,331 @@ +; Scrolling text console with line wrapping, 30x29 characters. +; Buffers lines for speed. Will work even if PPU doesn't +; support scrolling (until text reaches bottom). Keeps border +; along bottom in case TV cuts it off. +; +; Defers most initialization until first newline, at which +; point it clears nametable and makes palette non-black. +; +; ** ASCII font must already be in CHR, and mirroring +; must be vertical or single-screen. + +.ifndef CONSOLE_COLOR + CONSOLE_COLOR = $30 ; white +.endif + +console_screen_width = 32 ; if lower than 32, left-justifies + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs. OK if either/both are 0. +console_left_margin = 1 +console_right_margin = 1 + +console_width = console_screen_width - console_left_margin - console_right_margin + +zp_byte console_pos ; 0 to console_width +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_width + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + + setb console_pos,0 + rts + + +; Hides console by disabling PPU rendering and blacking out +; first four entries of palette. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +; Shows console display +; Preserved: X, Y +console_show: + pha + lda #CONSOLE_COLOR + jsr console_show_custom_color_ + pla + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + sty console_temp + + ldy console_pos + cpy #console_width + beq console_full_ + sta console_buf,y + iny + sty console_pos + + ldy console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_scroll_up_ + setb console_pos,0 + pla + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_apply_scroll_ + pla + rts + + +;**** Internal routines **** + +console_full_: + ldy console_temp + + ; Line is full + + ; If space, treat as newline + cmp #' ' + beq console_newline + + ; Wrap current line at appropriate point + pha + tya + pha + jsr console_wrap_ + pla + tay + pla + + jmp console_print + + +; Inserts newline into buffer at appropriate position, leaving +; next line ready in buffer +; Preserved: X, console_temp +console_wrap_: + ; Find beginning of last word + ldy #console_width + lda #' ' +: dey + bmi console_newline + cmp console_buf,y + bne :- + + ; y = 0 to console_width-1 + + ; Flush through current word and put remaining + ; in buffer for next line + jsr console_wait_vbl_ + + ; Time to last PPU write: 207 + 32*(26 + 10) + + lda console_scroll + jsr console_set_ppuaddr_ + + stx console_pos ; save X + + ldx #0 + + ; Print everything before last word +: lda console_buf,x + sta PPUDATA + inx + dey + bpl :- + + ; x = 1 to console_width + + ; Move last word to beginning of buffer, and + ; print spaces for rest of line + ldy #0 + beq :++ +: lda #' ' + sta PPUDATA + lda console_buf,x + inx + sta console_buf,y + iny +: cpx #console_width + bne :-- + + ldx console_pos ; restore X + + ; Append new text after that + sty console_pos + + ; FALL THROUGH + + +; Scrolls up 8 pixels and clears one line BELOW new line +; Preserved: X, console_temp +console_scroll_up_: + ; Scroll up 8 pixels + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + + ; Clear line AFTER that on screen + jsr console_add_8_to_scroll_ + jsr console_set_ppuaddr_ + + ldy #console_width + lda #' ' +: sta PPUDATA + dey + bne :- + ; FALL THROUGH + + +; Applies current scrolling position to PPU +; Preserved: X, Y, console_temp +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + rts + + +; Sets PPU address for row +; In: A = scroll position +; Preserved: X, Y +console_set_ppuaddr_: + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + ora #console_left_margin + sta PPUADDR + rts + + +; A = (A + 8) % 240 +; Preserved: X, Y +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_show_custom_color_: + pha + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + pla + jsr console_load_palette_ + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + rts + + +; Initializes PPU if necessary, then waits for VBL +; Preserved: A, X, Y, console_temp +console_wait_vbl_: + lda console_scroll + cmp #-1 + bne @already_initialized + + ; Deferred initialization of PPU until first use of console + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + tya + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldy #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dey + bne :- + + ; Clear attributes + lda #0 + ldy #$40 +: sta PPUDATA + dey + bne :- + + pla + tay + + jsr console_show +@already_initialized: + jmp wait_vbl_optional + + +; Flushes current line +; Preserved: X, Y +console_flush_: + lda console_scroll + jsr console_set_ppuaddr_ + + sty console_temp + + ; Copy line + ldy #0 + beq :++ +: lda console_buf,y + sta PPUDATA + iny +: cpy console_pos + bne :-- + + ldy console_temp + rts diff --git a/mmc3_test/source/common/crc.s b/mmc3_test/source/common/crc.s new file mode 100644 index 0000000..de96c2a --- /dev/null +++ b/mmc3_test/source/common/crc.s @@ -0,0 +1,118 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on + + +; EQ if checksum matches CRC +; Out: A=0 and EQ if match, A>0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/mmc3_test/source/common/delay.s b/mmc3_test/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/mmc3_test/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/mmc3_test/source/common/devcart.bin b/mmc3_test/source/common/devcart.bin new file mode 100644 index 0000000..6c515be Binary files /dev/null and b/mmc3_test/source/common/devcart.bin differ diff --git a/mmc3_test/source/common/macros.inc b/mmc3_test/source/common/macros.inc new file mode 100644 index 0000000..d4cb72e --- /dev/null +++ b/mmc3_test/source/common/macros.inc @@ -0,0 +1,169 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro inxy + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro diff --git a/mmc3_test/source/common/neshw.inc b/mmc3_test/source/common/neshw.inc new file mode 100644 index 0000000..814d772 --- /dev/null +++ b/mmc3_test/source/common/neshw.inc @@ -0,0 +1,43 @@ +; NES I/O locations and masks + +; Clocks per second +.ifndef CLOCK_RATE + CLOCK_RATE = 1789773 ; NTSC +; CLOCK_RATE = 1662607 ; PAL +.endif + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 + +.if CLOCK_RATE = 1789773 + PPU_FRAMELEN = 29781 +.elseif CLOCK_RATE = 1662607 + PPU_FRAMELEN = 33248 +.endif diff --git a/mmc3_test/source/common/ppu.s b/mmc3_test/source/common/ppu.s new file mode 100644 index 0000000..21cad12 --- /dev/null +++ b/mmc3_test/source/common/ppu.s @@ -0,0 +1,142 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts diff --git a/mmc3_test/source/common/print.s b/mmc3_test/source/common/print.s new file mode 100644 index 0000000..29fbc23 --- /dev/null +++ b/mmc3_test/source/common/print.s @@ -0,0 +1,235 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr @nibble + pla + + pha + and #$0F + jsr @nibble + pla + rts + +@nibble: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: A, X, Y +print_dec: + pha + sta print_temp_ + txa + pha + lda print_temp_ + + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jsr print_char + pla + tax + pla + rts + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/mmc3_test/source/common/shell.inc b/mmc3_test/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/mmc3_test/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/mmc3_test/source/common/shell.s b/mmc3_test/source/common/shell.s new file mode 100644 index 0000000..3747b46 --- /dev/null +++ b/mmc3_test/source/common/shell.s @@ -0,0 +1,388 @@ +; Common routines and runtime + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +;**** Special globals **** + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +.segment "NVRAM" + ; Beginning of variables not cleared at startup + nvram_begin: + +;**** Code segment setup **** + +.segment "RODATA" + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +; Move code to $E200 ($200 bytes for text output) +.segment "DMC" + .res $2200 + +; Devcart corrupts byte at $E000 when powering off +.segment "CODE" + nop + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts + +.endif + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sei + cld + ldx #$FF + txs + pha + + setb SNDCHN,0 + .ifndef BUILD_NSF + setb PPUCTRL,0 + .endif + + pla + pha + jsr report_result + ;jsr clear_nvram ; TODO: was this needed for anything? + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +; Clears $0-($100+S) and nv_ram_end-$7FF +clear_ram: + lda #0 + + ; Main pages + tax +: sta 0,x + sta $300,x + sta $400,x + sta $500,x + sta $600,x + sta $700,x + inx + bne :- + + ; Stack except that above stack pointer + tsx + inx +: dex + sta $100,x + bne :- + + ; BSS except nvram + ldx #<__NVRAM_SIZE__ +: sta __NVRAM_LOAD__,x + inx + bne :- + + rts + + +; Clears nvram +clear_nvram: + ldx #<__NVRAM_SIZE__ + beq @empty + lda #0 +: dex + sta __NVRAM_LOAD__,x + bne :- +@empty: + rts + + +; Prints filename and newline, if available, otherwise nothing. +; Preserved: A, X, Y +print_filename: + .ifdef FILENAME_KNOWN + pha + jsr print_newline + setw addr,filename + jsr print_str_addr + jsr print_newline + pla + .endif + rts + +.pushseg +.segment "RODATA" + ; Filename terminated with zero byte. + filename: + .ifdef FILENAME_KNOWN + .incbin "ram:nes_temp" + .endif + .byte 0 +.popseg + + +;**** ROM-specific **** +.ifndef BUILD_NSF + +.include "ppu.s" + +avoid_silent_nsf: +play_byte: + rts + +; Loads ASCII font into CHR RAM +.macro load_ascii_chr + bit PPUSTATUS + setb PPUADDR,$00 + setb PPUADDR,$00 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page +.endmacro + +; Disables interrupts and loops forever +.ifndef CUSTOM_FOREVER +forever: + sei + lda #0 + sta PPUCTRL +: beq :- + .res $10,$EA ; room for code to run loader +.endif + + +; Default NMI +.ifndef CUSTOM_NMI + zp_byte nmi_count + + nmi: + inc nmi_count + rti + + ; Waits for NMI. Must be using NMI handler that increments + ; nmi_count, with NMI enabled. + ; Preserved: X, Y + wait_nmi: + lda nmi_count + : cmp nmi_count + beq :- + rts +.endif + + +; Default IRQ +.ifndef CUSTOM_IRQ + irq: + bit SNDCHN ; clear APU IRQ flag + rti +.endif + +.endif + + +; Reports A in binary as high and low tones, with +; leading low tone for reference. Omits leading +; zeroes. +; Preserved: A, X, Y +play_hex: + pha + + ; Make low reference beep + clc + jsr @beep + + ; Remove high zero bits + sec +: rol a + bcc :- + + ; Play remaining bits + beq @zero +: jsr @beep + asl a + bne :- +@zero: + + delay_msec 300 + pla + rts + +; Plays low/high beep based on carry +; Preserved: A, X, Y +@beep: + pha + + ; Set up square + lda #1 + sta SNDCHN + sta $4001 + sta $4003 + adc #$FE ; period=$100 if carry, $1FF if none + sta $4002 + + ; Fade volume + lda #$0F +: ora #$30 + sta $4000 + delay_msec 8 + sec + sbc #$31 + bpl :- + + ; Silence + sta SNDCHN + delay_msec 160 + + pla + rts diff --git a/mmc3_test/source/common/sync_vbl.s b/mmc3_test/source/common/sync_vbl.s new file mode 100644 index 0000000..298904d --- /dev/null +++ b/mmc3_test/source/common/sync_vbl.s @@ -0,0 +1,163 @@ +; Synchronizes EXACTLY to VBL, to accuracy of 1/3 CPU clock +; (1/2 CPU clock if PPU is enabled). Reading PPUSTATUS +; 29768 clocks or later after return will have bit 7 set. +; Reading PPUSTATUS immediately will have bit 7 clear. +; Preserved: A, X, Y +; Time: 120-330 msec +.align 128 +sync_vbl: + pha + + ; Disable interrupts + sei + lda #0 + sta PPUCTRL + + ; Coarse synchronize + bit PPUSTATUS +: bit PPUSTATUS + bpl :- + + ; Delay so that VBL is sure to occur slightly after + ; critical window in loop below. + delay 29760 ; +1 works, +2 fails + jmp @first + + ; VBL occurs every 29780.67 (rendering disabled) + ; or 29780.5 (rendering enabled) CPU clocks. Loop + ; takes 29781 CPU clocks. Thus, time of VBL will + ; be effectively 1/3 or 1/2 CPU clock earlier in + ; loop each time. At some point, it will fall just + ; before second PPUSTATUS read below. +: delay 29781-4-4-3 +@first: bit PPUSTATUS ; clear flag (not really necessary) + bit PPUSTATUS ; see if just set + bpl :- + + pla + rts + + +; Same as sync_vbl, but additionally ensures that next frame +; will skip PPU clock at end of VBL if rendering is enabled. +; Preserved: A, X, Y +sync_vbl_odd: + pha + + ; Rendering must be disabled + jsr wait_vbl + lda #0 + sta PPUMASK + + jsr sync_vbl + jsr @render_frame + + ; See whether frame was short + ; If not, frames totaled 59561+1/3 CPU clocks + delay 29781-17-1 + lda PPUSTATUS + bmi :+ + jmp @end +: + ; If frame was short, first frame was + ; one clock shorter. Wait another frame to + ; toggle even/odd flag. Rendering enabled + ; for frame so total of all three frames + ; is 89341+1/3 CPU clocks, which has the + ; same fraction as the other case, thus + ; ensuring the same CPU-PPU synchronization. + jsr @render_frame + +@end: ; Establish same timing as sync_vbl + delay 29781-7 + + ; Be sure VBL flag is clear for this frame, as the + ; other sync routines do. + bit PPUSTATUS + + pla + rts + +@render_frame: + lda #PPUMASK_BG0 + sta PPUMASK + delay 29781-6-6-6-6+1 + lda #0 + sta PPUMASK + rts + + +; Same as sync_vbl_odd, but next frame will NOT skip PPU clock +; Preserved: A, X, Y +sync_vbl_even: + jsr sync_vbl_odd + delay 341*262*3 / 3 - 10; get to even frame without affecting sync + + ; Be sure VBL flag is clear for this frame, as the + ; other sync routines do. + bit PPUSTATUS + rts + + +; Same as sync_vbl_even, but also writes A to SPRDMA without +; affecting timing (in particular, SPRDMA's optional extra clock +; is dealt with). +; Preserved: A, X, Y +sync_vbl_even_dma: + jsr sync_vbl_odd + delay 341*262 - 534 + sta SPRDMA + bit PPUSTATUS + + ; Delay extra clock if ncessary. Unaffected by code + ; alignment since it branches to next instr. + bpl :+ +: + ; Be sure VBL flag is clear for this frame, as the + ; other sync routines do. + bit PPUSTATUS + rts + + +; Same as sync_vbl, but then delays A additional PPU clocks. +; Preserved: X, Y +.align 32 +sync_vbl_delay: + jsr sync_vbl + + ; VBL occurs every 29780.67 clocks, therefore + ; each iteration of the loop is like delaying + ; 1/3 CPU clock (1 PPU clock). +: delay 29781-7 + clc + adc #-1 + bcs :- + + delay 29781*2-10 + + ; Be sure VBL flag is clear for this frame, as the + ; other sync routines do. + bit PPUSTATUS + rts + + +; Effectively delays n PPU clocks, while maintaing +; even/odd frame (i.e. never delays an odd number of +; frames). PPU rendering must be off. +; Preserved: A, X, Y +.macro delay_ppu_even n + .if (n) < 8 + .error "time out of range" + .endif + .if (n) .MOD 3 = 1 + .if (n) > 4 + delay (n)/3-1 + .endif + delay 29781*4 + .elseif (n) .MOD 3 = 2 + delay (n)/3 + delay 29781*2 + .else + delay (n)/3 + .endif +.endmacro diff --git a/mmc3_test/source/common/test_mmc3.inc b/mmc3_test/source/common/test_mmc3.inc new file mode 100644 index 0000000..cacb8b6 --- /dev/null +++ b/mmc3_test/source/common/test_mmc3.inc @@ -0,0 +1,125 @@ +CUSTOM_IRQ=1 +CUSTOM_MAPPER=4 ; MMC3 +.include "shell.inc" + +r_set_reload = $C000 +r_clear_counter = $C001 +r_disable_irq = $E000 +r_enable_irq = $E001 + +begin_mmc3_tests: + delay_msec 200 + setb SNDMODE,$C0 ; disable frame irq + setb PPUMASK,0 ; disable PPU + setb PPUCTRL,0 + set_ppuaddr 0 + + lda #1 + jsr set_reload + jsr clear_counter + ldx #0 + jsr clock_counter_x + jsr clear_counter + ldx #0 + jsr clock_counter_x + jsr clear_irq + rts + +set_reload: + sta r_set_reload + rts + +clear_counter: + setb r_clear_counter,123 + rts + +disable_irq: + setb r_disable_irq,123 + rts + +; Disable then re-enable IRQ +clear_irq: + setb r_disable_irq,123 +enable_irq: + setb r_enable_irq,123 + rts + +; Clock counter X times +; Preserved: A, Y +clock_counter_x: +: jsr clock_counter + dex + bne :- + rts + +; Clock counter once +; Preserved: A, X, Y +clock_counter: + pha + setb PPUADDR,0 + sta PPUADDR + setb PPUADDR,$10 + sta PPUADDR + setb PPUADDR,0 + sta PPUADDR + pla + rts + +; Return X=1 if IRQ is pending, otherwise X=0 +; Preserved: Y +get_pending: + setb irq_flag,$01 + cli + nop + sei ; IRQ causes return from this routine + nop + ldx irq_flag + dex + rts + +; Decrement counter until IRQ occurs and return +; number of decrements required in A. +get_countx: + ldx #0 + cli + nop + nop +: setb PPUADDR,0 + sta PPUADDR + lda #$10 + inx + sta PPUADDR + sta PPUADDR + bne :- + sei + ldx #$FF + rts + +zp_res irq_flag + +irq: asl irq_flag + sta r_disable_irq + rti + +begin_counter_test: + jsr clock_counter ; avoid pathological reload behavior + jsr clock_counter + txa + jsr set_reload + jsr clear_counter + jsr clear_irq + rts + +should_be_clear: + jsr get_pending + cpx #0 + jne test_failed + jsr clear_irq + rts + +should_be_set: + jsr get_pending + cpx #1 + jne test_failed + jsr clear_irq + rts diff --git a/mmc3_test/source/common/testing.s b/mmc3_test/source/common/testing.s new file mode 100644 index 0000000..ba41f03 --- /dev/null +++ b/mmc3_test/source/common/testing.s @@ -0,0 +1,106 @@ +; Utilities for writing test ROMs + +; In NVRAM so these can be used before initializing runtime, +; then runtime initialized without clearing them +nv_res test_code ; code of current test +nv_res test_name,2 ; address of name of current test, or 0 of none + + +; Sets current test code and optional name. Also resets +; checksum. +; Preserved: A, X, Y +.macro set_test code,name + pha + lda #code + jsr set_test_ + .ifblank name + setb test_name+1,0 + .else + .local Addr + setw test_name,Addr + seg_data "RODATA",{Addr: .byte name,0} + .endif + pla +.endmacro + +set_test_: + sta test_code + jmp reset_crc + + +; Initializes testing module +init_testing: + jmp init_crc + + +; Reports that all tests passed +tests_passed: + jsr print_filename + print_str newline,"Passed" + lda #0 + jmp exit + + +; Reports "Done" if set_test has never been used, +; "Passed" if set_test 0 was last used, or +; failure if set_test n was last used. +tests_done: + ldx test_code + jeq tests_passed + inx + bne test_failed + jsr print_filename + print_str newline,"Done" + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + ldx test_code + + ; Treat $FF as 1, in case it wasn't ever set + inx + bne :+ + inx + stx test_code +: + ; If code >= 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/mmc3_test/source/common/text_out.s b/mmc3_test/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/mmc3_test/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +The first time a line of text is completed, the console initializes the +PPU. Most print routines update a running CRC-32 checksum which can be +checked with check_crc. This allows ALL the output to be checked very +easily. If the checksum doesn't match, it is printed, so during +development the code can be run on a NES to get the correct checksum, +which is then typed into the test code. The checksum is also useful when +comparing different outputs; rather than having to examine all the +output, one need only compare checksums. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported by the included code, due to +their complexity. + + +Macros +------ +Some macros are used to make common operations more convenient, defined +in common/macros.inc. The left is equivalent to the right: + + Macro Equivalent + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + setb addr,byte lda #byte + sta addr + + setw addr,word lda #word + sta addr+1 + + ldxy #value ldx #>value + ldy # diff --git a/mmc3_test_2/readme.txt b/mmc3_test_2/readme.txt new file mode 100644 index 0000000..6132318 --- /dev/null +++ b/mmc3_test_2/readme.txt @@ -0,0 +1,121 @@ +NES MMC3 Tests +-------------- +These tests verify a small part of MMC3 (and some MMC6) behavior, mostly +related to the scanline counter and IRQ. They should be run in order. + +The ROMs mainly test behavior by manually clocking the MMC3's IRQ +counter by writing to $2006 to change the current VRAM address. The last +two ROMs test behavior that differs among MMC3 chips. + + +MMC3 Operation +-------------- +I have fairly thoroughly tested MMC3 IRQ counter operation and found the +following behaviors that differ as described in kevtris's (draft?) MMC3 +documentation: + +- The counter can be clocked manually via bit 12 of the VRAM address +even when $2000 = $00 (bg and sprites both use tiles from $0xxx). + +- The IRQ flag is not set when the counter is cleared by writing to +$C001. + +- I uncovered some pathological behavior that isn't covered by the test +ROMs. If $C001 is written, the counter clocked, then $C001 written +again, on the next counter clock the counter will be ORed with $80 +(revision B)/frozen (revision A) and neither decremented nor reloaded. +If $C001 is written again at this point, on the next counter clock it +will be reloaded normally. I put a check in my emulator and none of the +several games I tested ever caused this situation to occur, so it's +probably not a good idea to implement this. + +The MMC3 in Crystalis (referred to here as revision A) worked as +described in kevtris's document, with the above changes. The MMC3 in +Super Mario Bros. 3 and Mega Man 3 (I think revision B, but I don't have +the special screw driver) further differed when $C000 was written with +0: + +- Writing 0 to $C000 works no differently than any other value written; +it will cause the counter to be reloaded every time it is clocked (once +it reaches zero). + +- When the counter is clocked, if it's not zero, it is decremented, +otherwise it is reloaded with the last value written to $C000. *After* +decrementing/reloading, if the counter is zero and IRQ is enabled via +$E001, the IRQ flag is set. + + +Multi-tests +----------- +The NES/NSF builds in the main directory consist of multiple sub-tests. +When run, they list the subtests as they are run. The final result code +refers to the first sub-test that failed. For more information about any +failed subtests, run them individually from rom_singles/ and +nsf_singles/. + + +Flashes, clicks, other glitches +------------------------------- +If a test prints "passed", it passed, even if there were some flashes or +odd sounds. Only a test which prints "done" at the end requires that you +watch/listen while it runs in order to determine whether it passed. Such +tests involve things which the CPU cannot directly test. + + +Alternate output +---------------- +Tests generally print information on screen, but also report the final +result audibly, and output text to memory, in case the PPU doesn't work +or there isn't one, as in an NSF or a NES emulator early in development. + +After the tests are done, the final result is reported as a series of +beeps (see below). For NSF builds, any important diagnostic bytes are +also reported as beeps, before the final result. + + +Output at $6000 +--------------- +All text output is written starting at $6004, with a zero-byte +terminator at the end. As more text is written, the terminator is moved +forward, so an emulator can print the current text at any time. + +The test status is written to $6000. $80 means the test is running, $81 +means the test needs the reset button pressed, but delayed by at least +100 msec from now. $00-$7F means the test has completed and given that +result code. + +To allow an emulator to know when one of these tests is running and the +data at $6000+ is valid, as opposed to some other NES program, $DE $B0 +$G1 is written to $6001-$6003. + + +Audible output +-------------- +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason. See the source +code of the test for more information about the meaning of a test code. +They are found after the set_test macro. For example, the cause of test +code 3 would be found in a line containing set_test 3. Examples: + + Tones Binary Decimal Meaning + - - - - - - - - - - - - - - - - - - - - + low 0 0 passed + low high 01 1 failed + low high low 010 2 error 2 + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to prevent the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +-- +Shay Green diff --git a/mmc3_test_2/rom_singles/1-clocking.nes b/mmc3_test_2/rom_singles/1-clocking.nes new file mode 100644 index 0000000..4458d56 Binary files /dev/null and b/mmc3_test_2/rom_singles/1-clocking.nes differ diff --git a/mmc3_test_2/rom_singles/2-details.nes b/mmc3_test_2/rom_singles/2-details.nes new file mode 100644 index 0000000..080cf47 Binary files /dev/null and b/mmc3_test_2/rom_singles/2-details.nes differ diff --git a/mmc3_test_2/rom_singles/3-A12_clocking.nes b/mmc3_test_2/rom_singles/3-A12_clocking.nes new file mode 100644 index 0000000..319b75e Binary files /dev/null and b/mmc3_test_2/rom_singles/3-A12_clocking.nes differ diff --git a/mmc3_test_2/rom_singles/4-scanline_timing.nes b/mmc3_test_2/rom_singles/4-scanline_timing.nes new file mode 100644 index 0000000..ebc0584 Binary files /dev/null and b/mmc3_test_2/rom_singles/4-scanline_timing.nes differ diff --git a/mmc3_test_2/rom_singles/5-MMC3.nes b/mmc3_test_2/rom_singles/5-MMC3.nes new file mode 100644 index 0000000..8c227b0 Binary files /dev/null and b/mmc3_test_2/rom_singles/5-MMC3.nes differ diff --git a/mmc3_test_2/rom_singles/6-MMC3_alt.nes b/mmc3_test_2/rom_singles/6-MMC3_alt.nes new file mode 100644 index 0000000..f9c1c2a Binary files /dev/null and b/mmc3_test_2/rom_singles/6-MMC3_alt.nes differ diff --git a/mmc3_test_2/source/1-clocking.s b/mmc3_test_2/source/1-clocking.s new file mode 100644 index 0000000..ca15681 --- /dev/null +++ b/mmc3_test_2/source/1-clocking.s @@ -0,0 +1,88 @@ +.include "test_mmc3.inc" + +main: + jsr begin_mmc3_tests + + set_test 2,"Counter/IRQ/A12 clocking isn't working at all" + ldx #10 + jsr begin_counter_test + jsr clock_counter ; counter = 10 + jsr clock_counter + jsr should_be_clear ; counter shouldn't be zero yet + + set_test 3,"Should decrement when A12 is toggled via PPUADDR" + ldx #2 + jsr begin_counter_test + ldx #9 ; counter = 2 + jsr clock_counter_x ; clock 9 times + jsr should_be_set ; should have hit zero at least once by now + + set_test 4,"Writing to $C000 shouldn't cause reload" + ldx #2 + jsr begin_counter_test + jsr clock_counter ; counter = 2 + lda #100 + jsr set_reload + ldx #8 + jsr clock_counter_x ; should reach 0 before reloading with 100 + jsr should_be_set ; and thus IRQ flag should be set by now + + set_test 5,"Writing to $C001 shouldn't cause immediate reload" + ldx #1 + jsr begin_counter_test + lda #1 + jsr set_reload ; shouldn't affect counter + jsr clear_counter ; request reload on next clock, not immediately + lda #4 + jsr set_reload ; reload = 4, which will get used next + jsr clock_counter ; counter = 4 + jsr clock_counter ; 3 + jsr should_be_clear + + set_test 6,"Should reload (no decrement) on first clock after clear" + ldx #2 + jsr begin_counter_test + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr should_be_clear + + set_test 7,"Clear should clear counter immediately" + ldx #2 + jsr begin_counter_test + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clear_counter ; clear counter + jsr clock_counter ; counter isn't 1 anymore, so IRQ shouldn't be set here + jsr should_be_clear + + set_test 8,"IRQ should be set when counter is decremented to 0" + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr should_be_set + + set_test 9,"IRQ should never be set when disabled" + ldx #1 + jsr begin_counter_test + jsr disable_irq + ldx #10 + jsr clock_counter_x + jsr should_be_clear + + set_test 10,"Should reload when clocked when counter is 0" + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + lda #10 + jsr set_reload + jsr clock_counter ; 0 + lda #2 + jsr set_reload + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clear_irq + jsr clock_counter ; 0 + jsr should_be_set + + jmp tests_passed diff --git a/mmc3_test_2/source/2-details.s b/mmc3_test_2/source/2-details.s new file mode 100644 index 0000000..cccdfd5 --- /dev/null +++ b/mmc3_test_2/source/2-details.s @@ -0,0 +1,88 @@ +; Tests MMC3 IRQ counter details + +.include "test_mmc3.inc" + +main: + jsr begin_mmc3_tests + + set_test 2,"Counter isn't working when reloaded with 255" + ldx #255 + jsr begin_counter_test + ldx #255 + jsr clock_counter_x ; first clock loads with 255 + jsr should_be_clear + jsr clock_counter + jsr should_be_set + + set_test 3,"Counter should run even when IRQ is disabled" + ldx #2 + jsr begin_counter_test + jsr disable_irq + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr enable_irq + jsr should_be_clear + jsr clock_counter ; 0 + jsr should_be_set + + set_test 4,"Counter should run even after IRQ flag has been set" + ldx #2 + jsr begin_counter_test + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clock_counter ; 2 + jsr clear_irq + jsr clock_counter ; 1 + jsr should_be_clear + jsr clock_counter ; 0 + jsr should_be_set + + set_test 5,"IRQ should not be set when counter reloads with non-zero" + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clear_irq + jsr clock_counter ; 1 + jsr should_be_clear + + set_test 6,"IRQ should not be set when counter is cleared via $C001" + ldx #2 + jsr begin_counter_test + jsr clock_counter ; 2 + jsr clock_counter ; 1 + jsr clear_counter + jsr should_be_clear + + set_test 7,"IRQ should be set when non-zero and reloading to 0 after clear" + ldx #3 + jsr begin_counter_test + jsr clock_counter ; 3 + jsr clock_counter ; 2 + lda #0 + jsr set_reload + jsr clear_counter + jsr clock_counter ; 0 + jsr should_be_set + + jsr clear_oam + set_test 8,"Counter should be clocked 241 times in PPU frame" + ldx #241 + jsr begin_counter_test + jsr wait_vbl + setb PPUSCROLL,0 + sta PPUSCROLL + setb PPUCTRL,$08 ; sprites use tiles at $1xxx + setb PPUMASK,$18 ; enable bg and sprites + delay 29800 + setb PPUMASK,$00 ; disable rendering + jsr should_be_clear + jsr clock_counter + jsr should_be_set + + jmp tests_passed + diff --git a/mmc3_test_2/source/3-A12_clocking.s b/mmc3_test_2/source/3-A12_clocking.s new file mode 100644 index 0000000..0d37c6f --- /dev/null +++ b/mmc3_test_2/source/3-A12_clocking.s @@ -0,0 +1,77 @@ +; Tests MMC3 IRQ clocking via bit 12 of VRAM address + +.include "test_mmc3.inc" + +main: + jsr begin_mmc3_tests + + setb PPUCTRL,0 ; disable PPU, sprites and bg use $0xxx patterns + sta PPUMASK + + set_test 2,"Shouldn't be clocked when A12 doesn't change" + ldx #1 + jsr begin_counter_test + lda #$00 ; transition everything but A12 + ldx #$ef + ldy #$ff + sta PPUADDR + sta PPUADDR + stx PPUADDR + sty PPUADDR + sta PPUADDR + sta PPUADDR + stx PPUADDR + sty PPUADDR + sta PPUADDR + sta PPUADDR + jsr should_be_clear + + set_test 3,"Shouldn't be clocked when A12 changes to 0" + ldx #1 + jsr begin_counter_test + jsr clock_counter ; avoid pathological behavior + setb PPUADDR,$10 + sta PPUADDR + jsr clear_counter + jsr clear_irq + ldx #$00 + ldy #$10 + stx PPUADDR + stx PPUADDR + sty PPUADDR ; counter = 1 + stx PPUADDR + stx PPUADDR ; second 1 to 0 transition + stx PPUADDR + jsr should_be_clear + + set_test 4,"Should be clocked when A12 changes to 1 via PPUADDR write" + ldx #1 + jsr begin_counter_test + jsr clock_counter + setb PPUADDR,$00 ; transition A12 from 0 to 1 + sta PPUADDR + setb PPUADDR,$10 + sta PPUADDR + jsr should_be_set + + set_test 5,"Should be clocked when A12 changes to 1 via PPUDATA read" + ldx #1 + jsr begin_counter_test + jsr clock_counter + setb PPUADDR,$0f ; vaddr = $0fff + setb PPUADDR,$ff + jsr should_be_clear + bit PPUDATA + jsr should_be_set + + set_test 6,"Should be clocked when A12 changes to 1 via PPUDATA write" + ldx #1 + jsr begin_counter_test + jsr clock_counter + setb PPUADDR,$0f ; vaddr = $0fff + setb PPUADDR,$ff + jsr should_be_clear + sta PPUDATA + jsr should_be_set + + jmp tests_passed diff --git a/mmc3_test_2/source/4-scanline_timing.s b/mmc3_test_2/source/4-scanline_timing.s new file mode 100644 index 0000000..fb36737 --- /dev/null +++ b/mmc3_test_2/source/4-scanline_timing.s @@ -0,0 +1,123 @@ +; Tests MMC3 IRQ timing to PPU clock accuracy. Tests both modes, +; $2000=$08 and $2000=$10. +; +; Timing tested is between $2002 reads of VBL flag first set, +; and IRQ occurring. + +.include "test_mmc3.inc" +.include "sync_vbl.s" + +.macro test mode, count, n + .local n_ + n_ = (n) + 1 + setb PPUMASK,0 + jsr sync_vbl_even + delay_ppu_even (n_ .MOD 3) + 9 + setb PPUCTRL,mode + lda #count + jsr begin_ + delay n_/3 - 3 - 43 + jsr end_ +.endmacro + +begin_: + pha + setb PPUMASK,$18 + pla + sta r_set_reload + sta r_clear_counter + sta r_disable_irq + sta r_enable_irq + rts + +end_: + setb irq_flag,$10 + cli + nop + nop + inc irq_flag + delay 1000 + sei + nop + lda irq_flag + cmp #$11 + beq @no_irq + rts + +@no_irq: + set_test 14,"IRQ never occurred" + jmp test_failed + +main: + jsr begin_mmc3_tests + + jsr clear_oam + + scanline_0_08 = 6976 + scanline_1_08 = scanline_0_08 + + set_test 2,"Scanline 0 IRQ should occur later when $2000=$08" + test $08, 0, scanline_0_08 - 1 + cmp #$22 + jne test_failed + + set_test 3,"Scanline 0 IRQ should occur sooner when $2000=$08" + test $08, 0, scanline_0_08 + cmp #$21 + jne test_failed + + set_test 4,"Scanline 1 IRQ should occur later when $2000=$08" + test $08, 1, scanline_1_08 + 341 - 1 + cmp #$22 + jne test_failed + + set_test 5,"Scanline 1 IRQ should occur sooner when $2000=$08" + test $08, 1, scanline_1_08 + 341 + cmp #$21 + jne test_failed + + set_test 6,"Scanline 239 IRQ should occur later when $2000=$08" + test $08, 239, scanline_1_08 + 239*341 - 1 + cmp #$22 + jne test_failed + + set_test 7,"Scanline 239 IRQ should occur sooner when $2000=$08" + test $08, 239, scanline_1_08 + 239*341 + cmp #$21 + jne test_failed + + + scanline_0_10 = 6976 - 256 + scanline_1_10 = scanline_0_10 - 21 + + set_test 8,"Scanline 0 IRQ should occur later when $2000=$10" + test $10, 0, scanline_0_10 - 1 + cmp #$22 + jne test_failed + + set_test 9,"Scanline 0 IRQ should occur sooner when $2000=$10" + test $10, 0, scanline_0_10 + cmp #$21 + jne test_failed + + set_test 10,"Scanline 1 IRQ should occur later when $2000=$10" + test $10, 1, scanline_1_10 + 341 - 1 + cmp #$22 + jne test_failed + + set_test 11,"Scanline 1 IRQ should occur sooner when $2000=$10" + test $10, 1, scanline_1_10 + 341 + cmp #$21 + jne test_failed + + set_test 12,"Scanline 239 IRQ should occur later when $2000=$10" + test $10, 239, scanline_1_10 + 239*341 - 1 + cmp #$22 + jne test_failed + + set_test 13,"Scanline 239 IRQ should occur sooner when $2000=$10" + test $10, 239, scanline_1_10 + 239*341 + cmp #$21 + jne test_failed + + jmp tests_passed diff --git a/mmc3_test_2/source/5-MMC3.s b/mmc3_test_2/source/5-MMC3.s new file mode 100644 index 0000000..1c7a9b2 --- /dev/null +++ b/mmc3_test_2/source/5-MMC3.s @@ -0,0 +1,31 @@ +; Tests MMC3-specifics + +.include "test_mmc3.inc" + +main: + jsr begin_mmc3_tests + + set_test 2,"Should reload and set IRQ every clock when reload is 0" + ldx #0 + jsr begin_counter_test + jsr clock_counter ; 0 + jsr clock_counter ; 0 + jsr should_be_set + jsr clock_counter ; 0 + jsr should_be_set + jsr clock_counter ; 0 + jsr should_be_set + + set_test 3,"IRQ should be set when counter is 0 after reloading, even when counter was 0 before reloading" + ldx #1 + jsr begin_counter_test + jsr clock_counter ; 1 + jsr clock_counter ; 0 + jsr clear_irq + lda #0 + jsr set_reload + jsr clock_counter ; 0 + jsr should_be_set + + jmp tests_passed + diff --git a/mmc3_test_2/source/6-MMC3_alt.s b/mmc3_test_2/source/6-MMC3_alt.s new file mode 100644 index 0000000..e0f3ac4 --- /dev/null +++ b/mmc3_test_2/source/6-MMC3_alt.s @@ -0,0 +1,52 @@ +; Tests alternate MMC3 behavior. Some MMC3 chips also have this behavior, +; though their markings appear identical to those that have normal +; MMC3 behavior. My copy of Crystalis in particular behaves this way, +; but not my copy of Super Mario Bros. 3, even though both have a chip +; marked MMC3B. + +.include "test_mmc3.inc" + +main: + jsr begin_mmc3_tests + + set_test 2,"IRQ shouldn't be set when reloading to 0 due to counter naturally reaching 0 previously" + ldx #2 + jsr begin_counter_test + jsr clock_counter ; reload with 2 + jsr clock_counter ; decrement to 1 + jsr clock_counter ; decrement to 0 + jsr should_be_set + lda #0 + jsr set_reload + jsr clock_counter ; reload with 0 + jsr should_be_clear + jsr clock_counter ; reload with 0 + jsr should_be_clear + jsr clock_counter ; reload with 0 + jsr should_be_clear + jsr clock_counter ; reload with 0 + jsr should_be_clear + + set_test 3,"IRQ should be set when reloading due to clear, even if counter was already 0" + ldx #2 + jsr begin_counter_test + jsr clock_counter ; reload with 2 + jsr clock_counter ; decrement to 1 + jsr clock_counter ; decrement to 0 + jsr should_be_set + lda #0 + jsr set_reload + jsr clock_counter ; reload with 0 + jsr should_be_clear + jsr clock_counter ; reload with 0 + jsr should_be_clear + lda #2 + jsr set_reload + jsr clear_counter ; this sets internal flag that is examined on next clock + lda #0 + jsr set_reload + jsr clock_counter ; reload with 0, AND set IRQ flag, unlike before + jsr should_be_set + + jmp tests_passed + diff --git a/mmc3_test_2/source/common/ascii.chr b/mmc3_test_2/source/common/ascii.chr new file mode 100644 index 0000000..2c5b26b Binary files /dev/null and b/mmc3_test_2/source/common/ascii.chr differ diff --git a/mmc3_test_2/source/common/build_rom.s b/mmc3_test_2/source/common/build_rom.s new file mode 100644 index 0000000..38eab29 --- /dev/null +++ b/mmc3_test_2/source/common/build_rom.s @@ -0,0 +1,92 @@ +; Builds program as iNES ROM + +; Default is 32K PRG and 8K CHR ROM, NROM (0) + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM=1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM=1 ; Use mapper that supports 8K WRAM in cart +CUSTOM_MAPPER=n ; Specify mapper number +.endif + +.ifndef CUSTOM_MAPPER + .ifdef CART_WRAM + CUSTOM_MAPPER = 2 ; UNROM + .else + CUSTOM_MAPPER = 0 ; NROM + .endif +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte $4E,$45,$53,26 ; "NES" EOF + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte CUSTOM_MAPPER*$10+$01 ; vertical mirroring +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word -1,-1,-1, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .define CHARS "CHARS_PRG" + .segment CHARS + ascii_chr: + + .segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: +.else + .define CHARS "CHARS" + .segment "CHARS_ASCII" + .align $200 + .incbin "ascii.chr" + .res $1800 +.endif + +.segment CHARS + .res $10,0 + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +.segment "CODE" + .res $4000 + +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + load_chr_ram + .endif + rts + +post_exit: + jsr set_final_result + jsr play_hex + jmp forever + +; This helps devcart recover after running test. +; It is never executed by test ROM. +.segment "LOADER" + .incbin "devcart.bin" + +.code +.align 256 diff --git a/mmc3_test_2/source/common/console.s b/mmc3_test_2/source/common/console.s new file mode 100644 index 0000000..508898b --- /dev/null +++ b/mmc3_test_2/source/common/console.s @@ -0,0 +1,328 @@ +; Scrolling text console with word wrapping, 30x29 characters. +; +; * Defers PPU initialization until first flush/ newline. +; * Works even if PPU doesn't support scrolling. +; * Keeps border around edge of screen for TV overscan. +; * Requires vertical or single-screen mirroring. +; * Requires ASCII font in CHR. + +.ifndef CONSOLE_COLOR + CONSOLE_COLOR = $30 ; white +.endif + +console_screen_width = 32 ; if lower than 32, left-justifies + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs. OK if either/both are 0. +console_left_margin = 1 +console_right_margin = 1 + +console_width = console_screen_width - console_left_margin - console_right_margin + +zp_byte console_pos ; 0 to console_width +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_width + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + + setb console_pos,0 + rts + + +; Hides console by disabling PPU rendering and blacking out +; first four entries of palette. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +; Shows console display +; Preserved: A, X, Y +console_show: + pha + lda #CONSOLE_COLOR + jsr console_show_custom_color_ + pla + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + sty console_temp + + ldy console_pos + cpy #console_width + beq console_full_ + sta console_buf,y + iny + sty console_pos + + ldy console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_scroll_up_ + setb console_pos,0 + pla + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_apply_scroll_ + pla + rts + + +;**** Internal routines **** + +console_full_: + ldy console_temp + + ; Line is full + + ; If space, treat as newline + cmp #' ' + beq console_newline + + ; Wrap current line at appropriate point + pha + tya + pha + jsr console_wrap_ + pla + tay + pla + + jmp console_print + + +; Inserts newline into buffer at appropriate position, leaving +; next line ready in buffer +; Preserved: X, console_temp +console_wrap_: + ; Find beginning of last word + ldy #console_width + lda #' ' +: dey + bmi console_newline + cmp console_buf,y + bne :- + + ; y = 0 to console_width-1 + + ; Flush through current word and put remaining + ; in buffer for next line + jsr console_wait_vbl_ + + ; Time to last PPU write: 207 + 32*(26 + 10) + + lda console_scroll + jsr console_set_ppuaddr_ + + stx console_pos ; save X + + ldx #0 + + ; Print everything before last word +: lda console_buf,x + sta PPUDATA + inx + dey + bpl :- + + ; x = 1 to console_width + + ; Move last word to beginning of buffer, and + ; print spaces for rest of line + ldy #0 + beq :++ +: lda #' ' + sta PPUDATA + lda console_buf,x + inx + sta console_buf,y + iny +: cpx #console_width + bne :-- + + ldx console_pos ; restore X + + ; Append new text after that + sty console_pos + + ; FALL THROUGH + + +; Scrolls up 8 pixels and clears one line BELOW new line +; Preserved: X, console_temp +console_scroll_up_: + ; Scroll up 8 pixels + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + + ; Clear line AFTER that on screen + jsr console_add_8_to_scroll_ + jsr console_set_ppuaddr_ + + ldy #console_width + lda #' ' +: sta PPUDATA + dey + bne :- + ; FALL THROUGH + + +; Applies current scrolling position to PPU +; Preserved: X, Y, console_temp +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + rts + + +; Sets PPU address for row +; In: A = scroll position +; Preserved: X, Y +console_set_ppuaddr_: + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + ora #console_left_margin + sta PPUADDR + rts + + +; A = (A + 8) % 240 +; Preserved: X, Y +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_show_custom_color_: + pha + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + pla + jsr console_load_palette_ + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + rts + + +; Initializes PPU if necessary, then waits for VBL +; Preserved: A, X, Y, console_temp +console_wait_vbl_: + lda console_scroll + cmp #-1 + bne @already_initialized + + ; Deferred initialization of PPU until first use of console + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + tya + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldy #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dey + bne :- + + ; Clear attributes + lda #0 + ldy #$40 +: sta PPUDATA + dey + bne :- + + pla + tay + + jsr console_show +@already_initialized: + jmp wait_vbl_optional + + +; Flushes current line +; Preserved: X, Y +console_flush_: + lda console_scroll + jsr console_set_ppuaddr_ + + sty console_temp + + ; Copy line + ldy #0 + beq :++ +: lda console_buf,y + sta PPUDATA + iny +: cpy console_pos + bne :-- + + ldy console_temp + rts diff --git a/mmc3_test_2/source/common/crc.s b/mmc3_test_2/source/common/crc.s new file mode 100644 index 0000000..de96c2a --- /dev/null +++ b/mmc3_test_2/source/common/crc.s @@ -0,0 +1,118 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on + + +; EQ if checksum matches CRC +; Out: A=0 and EQ if match, A>0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/mmc3_test_2/source/common/delay.s b/mmc3_test_2/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/mmc3_test_2/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/mmc3_test_2/source/common/devcart.bin b/mmc3_test_2/source/common/devcart.bin new file mode 100644 index 0000000..684fbb9 Binary files /dev/null and b/mmc3_test_2/source/common/devcart.bin differ diff --git a/mmc3_test_2/source/common/macros.inc b/mmc3_test_2/source/common/macros.inc new file mode 100644 index 0000000..4307c66 --- /dev/null +++ b/mmc3_test_2/source/common/macros.inc @@ -0,0 +1,188 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro inxy + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro + +; Negates A and adds it to operand +.macro subaf Operand + eor #$FF + sec + adc Operand +.endmacro + +; Initializes CPU registers to reasonable values +.macro init_cpu_regs + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + .ifndef BUILD_NSF + inx + stx PPUCTRL + .endif +.endmacro diff --git a/mmc3_test_2/source/common/neshw.inc b/mmc3_test_2/source/common/neshw.inc new file mode 100644 index 0000000..d636a97 --- /dev/null +++ b/mmc3_test_2/source/common/neshw.inc @@ -0,0 +1,56 @@ +; NES I/O locations and masks + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 + +.ifndef REGION_FREE + .ifndef PAL_ONLY + .ifndef NTSC_ONLY + NTSC_ONLY = 1 + .endif + .endif +.else + .ifdef NTSC_ONLY + .error "NTSC_ONLY and REGION_FREE defined" + .endif + .ifdef PAL_ONLY + .error "PAL_ONLY and REGION_FREE defined" + .endif +.endif + +.ifdef NTSC_ONLY + CLOCK_RATE = 1789773 + PPU_FRAMELEN = 29781 +.endif + +.ifdef PAL_ONLY + CLOCK_RATE = 1662607 + PPU_FRAMELEN = 33248 +.endif diff --git a/mmc3_test_2/source/common/ppu.s b/mmc3_test_2/source/common/ppu.s new file mode 100644 index 0000000..4827b3e --- /dev/null +++ b/mmc3_test_2/source/common/ppu.s @@ -0,0 +1,203 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts + + +.macro check_ppu_region_ Len + ; Delays since VBL began + jsr wait_vbl_optional ; 10 average + delay Len - 18 - 200 + lda PPUSTATUS ; 4 + bmi @ok ; 2 + delay 200 + ; Next VBL should roughly begin here if it's the + ; one we are detecting + delay 200 + lda PPUSTATUS ; 2 + bpl @ok +.endmacro + +check_ppu_region: + +.ifndef REGION_FREE +.ifdef PAL_ONLY + check_ppu_region_ 29781 + print_str {newline,"Note: This test is meant for PAL NES only.",newline,newline} +.endif + +.ifdef NTSC_ONLY + check_ppu_region_ 33248 + print_str {newline,"Note: This test is meant for NTSC NES only.",newline,newline} +.endif +.endif +@ok: rts + + +; Loads ASCII font into CHR RAM and fills rest with $FF +.macro load_chr_ram + bit PPUSTATUS + setb PPUADDR,0 + setb PPUADDR,0 + + ; Copy ascii_chr to 0 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page + + ; Fill rest + lda #$FF +: sta PPUDATA + iny + bne :- + inx + cpx #$20 + bne :- +.endmacro diff --git a/mmc3_test_2/source/common/print.s b/mmc3_test_2/source/common/print.s new file mode 100644 index 0000000..ab8a308 --- /dev/null +++ b/mmc3_test_2/source/common/print.s @@ -0,0 +1,247 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr print_nibble_ + pla + + pha + and #$0F + jsr print_nibble_ + pla + rts + +print_nibble_: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints low 4 bits of A as single hex character +; Preserved: A, X, Y +print_nibble: + pha + and #$0F + jsr update_crc + jsr print_nibble_ + pla + rts + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: A, X, Y +print_dec: + pha + sta print_temp_ + jsr update_crc + txa + pha + lda print_temp_ + + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jsr print_char + pla + tax + pla + rts + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/mmc3_test_2/source/common/shell.inc b/mmc3_test_2/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/mmc3_test_2/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/mmc3_test_2/source/common/shell.s b/mmc3_test_2/source/common/shell.s new file mode 100644 index 0000000..d458c9b --- /dev/null +++ b/mmc3_test_2/source/common/shell.s @@ -0,0 +1,180 @@ +; Shell that sets up testing framework and calls main + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +; Move code from $C000 to $E200, to accommodate my devcarts +.segment "CODE" + .res $2200 + +; Put shell code after user code, so user code is in more +; consistent environment +.segment "CODE2" + + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + init_cpu_regs + + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell without affecting current set_test values +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + ; Clear APU registers + lda #0 + sta $4015 + ldx #$13 +: sta $4000,x + dex + bpl :- + + ; CPU registers + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sta temp + init_cpu_regs + setb SNDCHN,0 + lda temp + + jsr report_result + pha + jsr check_ppu_region + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +.include "shell_misc.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts +.endif diff --git a/mmc3_test_2/source/common/shell_misc.s b/mmc3_test_2/source/common/shell_misc.s new file mode 100644 index 0000000..7e7d4e9 --- /dev/null +++ b/mmc3_test_2/source/common/shell_misc.s @@ -0,0 +1,211 @@ +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +.macro fill_ram_ Begin, End + ; Simpler to count from negative size up to 0, + ; and adjust address downward to compensate + ; for initial low byte in Y index + .local Neg_size + Neg_size = (Begin) - (End) + ldxy #(Begin) - 4 + delay (n)/3-1 + .endif + delay 29781*4 + .elseif (n) .MOD 3 = 2 + delay (n)/3 + delay 29781*2 + .else + delay (n)/3 + .endif +.endmacro diff --git a/mmc3_test_2/source/common/test_mmc3.inc b/mmc3_test_2/source/common/test_mmc3.inc new file mode 100644 index 0000000..f7aeb7f --- /dev/null +++ b/mmc3_test_2/source/common/test_mmc3.inc @@ -0,0 +1,128 @@ +CUSTOM_IRQ = 1 +CUSTOM_MAPPER = 4 ; MMC3 +.include "shell.inc" + +r_set_reload = $C000 +r_clear_counter = $C001 +r_disable_irq = $E000 +r_enable_irq = $E001 + + +; Sets things for MMC3 tests +begin_mmc3_tests: + delay_msec 200 + setb SNDMODE,$C0 ; disable frame irq + setb PPUMASK,0 ; disable PPU + setb PPUCTRL,0 + set_ppuaddr 0 + + lda #1 + jsr set_reload + jsr clear_counter + ldx #0 + jsr clock_counter_x + jsr clear_counter + ldx #0 + jsr clock_counter_x + jsr clear_irq + rts + + +; Begins a test by loading X into scanline counter, first +; ensuring that pathological behavior won't occur +begin_counter_test: + jsr clock_counter ; avoid pathological reload behavior + jsr clock_counter + txa + jsr set_reload + jsr clear_counter + jsr clear_irq + rts + + +; Fails if MMC3 IRQ is set +should_be_clear: + jsr get_pending + jne test_failed + jsr clear_irq + rts + + +; Fails if MMC3 IRQ is clear, then clears it +should_be_set: + jsr get_pending + jeq test_failed + jsr clear_irq + rts + + +set_reload: + sta r_set_reload + rts + + +clear_counter: + setb r_clear_counter,123 + rts + + +disable_irq: + setb r_disable_irq,123 + rts + + +; Disables then re-enables IRQ +clear_irq: + setb r_disable_irq,123 +enable_irq: + setb r_enable_irq,123 + rts + + +; Clock counter X times +; Preserved: A, Y +clock_counter_x: +: jsr clock_counter + dex + bne :- + rts + + +; Clocks counter once +; Preserved: A, X, Y +clock_counter: + pha + lda #0 + sta PPUADDR + sta PPUADDR + lda #$10 + sta PPUADDR + sta PPUADDR + lda #0 + sta PPUADDR + sta PPUADDR + pla + rts + + +; Determines state of MMC3 IRQ flag, and acknowledges it +; Out: A = 0 if clear, 1 if set +; flags based on value of A +; Preserved: X, Y +get_pending: + setb irq_flag,1 + cli + nop + ; If IRQ was pending, it will occur here + ; and shift irq_flag left + sei + nop + lda irq_flag + lsr a + rts + +zp_byte irq_flag + +irq: asl irq_flag + sta r_disable_irq + rti diff --git a/mmc3_test_2/source/common/testing.s b/mmc3_test_2/source/common/testing.s new file mode 100644 index 0000000..6bb852a --- /dev/null +++ b/mmc3_test_2/source/common/testing.s @@ -0,0 +1,105 @@ +; Utilities for writing test ROMs + +; In NVRAM so these can be used before initializing runtime, +; then runtime initialized without clearing them +nv_res test_code ; code of current test +nv_res test_name,2 ; address of name of current test, or 0 of none + + +; Sets current test code and optional name. Also resets +; checksum. +; Preserved: A, X, Y +.macro set_test code,name + pha + lda #code + jsr set_test_ + .ifblank name + setb test_name+1,0 + .else + .local Addr + setw test_name,Addr + seg_data "RODATA",{Addr: .byte name,0} + .endif + pla +.endmacro + +set_test_: + sta test_code + jmp reset_crc + + +; Initializes testing module +init_testing = init_crc + + +; Reports that all tests passed +tests_passed: + jsr print_filename + print_str newline,"Passed" + lda #0 + jmp exit + + +; Reports "Done" if set_test has never been used, +; "Passed" if set_test 0 was last used, or +; failure if set_test n was last used. +tests_done: + ldx test_code + jeq tests_passed + inx + bne test_failed + jsr print_filename + print_str newline,"Done" + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + ldx test_code + + ; Treat $FF as 1, in case it wasn't ever set + inx + bne :+ + inx + stx test_code +: + ; If code >= 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/mmc3_test_2/source/common/text_out.s b/mmc3_test_2/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/mmc3_test_2/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +The first time a line of text is completed, the console initializes the +PPU. Most print routines update a running CRC-32 checksum which can be +checked with check_crc. This allows ALL the output to be checked very +easily. If the checksum doesn't match, it is printed, so during +development the code can be run on a NES to get the correct checksum, +which is then typed into the test code. The checksum is also useful when +comparing different outputs; rather than having to examine all the +output, one need only compare checksums. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported by the included code, due to +their complexity. + + +Macros +------ +Some macros are used to make common operations more convenient, defined +in common/macros.inc. The left is equivalent to the right: + + Macro Equivalent + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + setb addr,byte lda #byte + sta addr + + setw addr,word lda #word + sta addr+1 + + ldxy #value ldx #>value + ldy # diff --git a/nes15-1.0.0/LICENSE b/nes15-1.0.0/LICENSE new file mode 100644 index 0000000..1d2942c --- /dev/null +++ b/nes15-1.0.0/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2011 Mathew Brenaman. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + diff --git a/nes15-1.0.0/Makefile b/nes15-1.0.0/Makefile new file mode 100644 index 0000000..727d680 --- /dev/null +++ b/nes15-1.0.0/Makefile @@ -0,0 +1,114 @@ +# +# 'nes15' GNU Makefile +# + +INCS = \ + fifteen/fifteen.inc \ + snd/snd.inc \ + src/game.inc \ + src/gfx.inc \ + src/nmi.inc \ + src/play.inc \ + src/title.inc + +OBJS = \ + fifteen/fifteen.o \ + snd/mus0.o \ + snd/mus1.o \ + snd/sfx0.o \ + snd/sfx1.o \ + snd/snd.o \ + src/game.o \ + src/gfx.o \ + src/header.o \ + src/nmi.o \ + src/play.o \ + src/title.o + +LIBINCS = \ + nes-lib/apu.inc \ + nes-lib/bcd.inc \ + nes-lib/joy.inc \ + nes-lib/lfsr32.inc \ + nes-lib/muse.inc \ + nes-lib/oam.inc \ + nes-lib/pkb.inc \ + nes-lib/ppu.inc \ + nes-lib/vrub.inc + +LIBOBJS = \ + nes-lib/bcd16_from_bin.o \ + nes-lib/bcd16.o \ + nes-lib/bcd16_to_vrub.o \ + nes-lib/joy_read1.o \ + nes-lib/joy_repeat1.o \ + nes-lib/joy_update1.o \ + nes-lib/lfsr32.o \ + nes-lib/muse.o \ + nes-lib/oam_clear.o \ + nes-lib/pkb.o \ + nes-lib/pkb_to_vram.o \ + nes-lib/vrub_from_mem.o \ + nes-lib/vrub.o + +CHRS = \ + gfx/cursor.chr \ + gfx/edges.chr \ + gfx/font.chr \ + gfx/tile.chr + +VCHRS = \ + gfx/nums.chr + +PKBS = \ + gfx/play.pkb \ + gfx/title.pkb + +PALS = \ + gfx/bgd.pal \ + gfx/spr.pal + +INES = nes15.nes +AS65 = ca65 +LD65 = ld65 +AFLAGS65 = -l -I src -I fifteen -I gfx -I snd -I nes-lib + +ifdef PAL + AFLAGS65 += -DPAL=1 +endif + +LCONFIG = game.cfg +MAPFILE = map.txt +PCX2CHR = pcx2chr +BIN2PKB = bin2pkb + +# +# Targets +# + +$(INES): header.bin game.bin chr1.chr chr2.chr + cat header.bin game.bin chr1.chr chr2.chr > $(INES) + +header.bin game.bin chr1.chr chr2.chr: $(LCONFIG) $(OBJS) $(LIBOBJS) + $(LD65) -C $(LCONFIG) $(LIBOBJS) $(OBJS) -m $(MAPFILE) -o $@ + +$(OBJS): $(INCS) $(LIBINCS) $(CHRS) $(VCHRS) $(PKBS) $(PALS) +$(LIBOBJS): $(LIBINCS) + +$(CHRS): $(CHRS:.chr=.pcx) + $(PCX2CHR) $(@:.chr=.pcx) $@ + +$(VCHRS): $(VCHRS:.chr=.pcx) + $(PCX2CHR) $(@:.chr=.pcx) $@ -V + +%.pkb: %.nam + $(BIN2PKB) $< $@ + +%.o: %.s + $(AS65) $(AFLAGS65) $< -o $@ + +.PHONY: clean +clean: + rm $(INES) *.bin *.chr $(OBJS) $(OBJS:.o=.lst) $(LIBOBJS) \ + $(LIBOBJS:.o=.lst) $(MAPFILE) $(CHRS) $(VCHRS) $(PKBS) + diff --git a/nes15-1.0.0/README b/nes15-1.0.0/README new file mode 100644 index 0000000..408c825 --- /dev/null +++ b/nes15-1.0.0/README @@ -0,0 +1,118 @@ +nes15 - by Mathew Brenaman + + nes15 is an implementation of the classic Fifteen Puzzle for the + Nintendo Entertainment System. This version is an NROM-128 (16kb + PRG, 8kb CHR) and was tested on the real system with a PowerPak. It + should also work with most emulators such as Nestopia and FCE Ultra. + + The fifteen puzzle consists of a frame of numbered square tiles in + random order with one tile missing. The object of the puzzle is to + place the tiles in order by sliding them into the empty position: + + 1 2 3 4 + + 5 6 7 8 + + 9 10 11 12 + + 13 14 15 -- + + If you become frustrated trying solve the puzzle, I've implemented + an auto-solver that allows you to watch it be solved. While the + auto-solver does not always make the least amount of moves needed + to solve the puzzle, it should always at least solve it. + + This program is free software. It is distributed under the terms + of a simple BSD-style license (OSI-approved). See the 'LICENSE' + file for details. + + This product is not sponsored or endorsed by Nintendo. + + + +How to play +----------- + + At the title screen, pressing any button will began play. + + Game controls: + + Directional buttons: Move the cursor + A: Slide the tile under cursor into the gap + Start: Pause the game + Select: Toggle the auto-solver + + After the puzzle has been completed by either you or the auto-solver, + press either A or B and new puzzle will be generated. + + + +Compiling +--------- + + You will need ca65, a macro assembler included with cc65, which + can be found at: + http://www.cc65.org/ + + You will also need to compile and locally install the tools provided. + + If you are using Linux or something similar, simply running 'make' + should produce the NTSC ROM image. If you wish to build the PAL + version, run 'make PAL=1'. + + For other OS's, you will need to do a bit of fiddling with the + makefile and other witchery which I will not cover here. + + + +Contacting +---------- + + If you have any comments, questions, or bug reports, send them to: + mbrenaman@gmail.com + + + +Acknowledgements +---------------- + + Sources of NES programming information: + + http://wiki.nesdev.com/w/index.php/Nesdev_Wiki + http://nesdev.parodius.com/bbs/index.php + http://nintendoage.com/forum/messageview.cfm?catid=22&threadid=22487 + + + Some aspects of the game's implementation where inspired by: + + Posts found at: + http://nesdev.parodius.com/bbs/index.php + + SMBDis - A comprehensive disassembly of Super Mario Bros: + http://www.romhacking.net/docs/344/ + + Concentration Room: + http://www.pineight.com/croom/ + + LJ65: + http://www.pineight.com/nes/#t + + + The Font used for the digits on puzzle tiles is from what I believe + is an old DOS font, which can be found at: + http://orangetide.com/nesdev/gamefonts/ + + And possibly also at: + http://chris.pirillo.com/dos-fonts/ + + + The font used, titled "Ghost", I found here: + http://kofler.dot.at/c64/ + + It's seems to actually be the font used in the Commodore 64 version + of "Ghosts and Goblins". + + + The music is from "Eight Short Preludes and Fugues" by J.S. Bach + (possibly by J.T. Krebs), BWV 558. + diff --git a/nes15-1.0.0/bin2pkb/Makefile b/nes15-1.0.0/bin2pkb/Makefile new file mode 100644 index 0000000..5bd5d35 --- /dev/null +++ b/nes15-1.0.0/bin2pkb/Makefile @@ -0,0 +1,36 @@ +# +# 'bin2pkb' GNU Makefile +# + +OBJS = \ + ../clib/binio.o \ + ../clib/packbits.o \ + bin2pkb.o + +PROG = bin2pkb + +CC = gcc +LFLAGS = -o $(PROG) +CFLAGS = -I ../clib -Wall -Wextra -pedantic -ansi + +# +# Targets +# + +$(PROG): $(OBJS) + $(CC) $(OBJS) $(LFLAGS) + +-include $(OBJS:.o=.d) + +%.o: %.c + $(CC) $(CFLAGS) -c $*.c -o $*.o + $(CC) $(CFLAGS) -MM $*.c > $*.d + +.PHONY: debug +debug: CFLAGS += -g +debug: $(PROG) + +.PHONY: clean +clean: + rm $(OBJS) $(OBJS:.o=.d) $(PROG) + diff --git a/nes15-1.0.0/bin2pkb/bin2pkb.c b/nes15-1.0.0/bin2pkb/bin2pkb.c new file mode 100644 index 0000000..3165be0 --- /dev/null +++ b/nes15-1.0.0/bin2pkb/bin2pkb.c @@ -0,0 +1,85 @@ +/* + * File: bin2pkb.c + * Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) + * + * Binary file to Packbits encoded file converter + */ + +#include +#include +#include +#include "binio.h" +#include "packbits.h" + + + +/* + * Reads the binary data stored in 'src_file' and writes it out in Packbits + * encoding to 'dst_file'. + * + * Returns: If the conversion was successfully done, then a non-zero value is + * returned. Otherwise, zero is returned and an error message is printed to + * 'stderr'. + * + * Asserts: + * src_file != NULL + * dst_file != NULL + */ +static int bin2pkb(void); + + + +static const char *src_file = NULL; +static const char *dst_file = NULL; + + + +int main(int argc, char *argv[]) +{ + if (argc == 1) { + printf("Usage: bin2pkb [INFILE] [OUTFILE]\n"); + return EXIT_SUCCESS; + } + + if (argc == 2) { + fprintf(stderr, "Output file required\n"); + return EXIT_FAILURE; + } + + src_file = argv[1]; + dst_file = argv[2]; + + if (!bin2pkb()) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + + + +static int bin2pkb(void) +{ + unsigned char *data; + size_t size = 0; + + assert(src_file != NULL); + assert(dst_file != NULL); + + if ((data = binio_read_file(src_file, &size)) == NULL) { + return 0; + } + + printf("Compressing \"%s\" (%lu bytes)\n", src_file, + (unsigned long)size); + + if ((size = packbits_write_file(dst_file, data, size)) > 0) { + printf("Output \"%s\" (%lu bytes)\n", dst_file, + (unsigned long)size); + } + + free(data); + + return size > 0; +} + diff --git a/nes15-1.0.0/clib/binio.c b/nes15-1.0.0/clib/binio.c new file mode 100644 index 0000000..2247045 --- /dev/null +++ b/nes15-1.0.0/clib/binio.c @@ -0,0 +1,429 @@ +/* + * File: binio.c + * Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) + * + * Binary file I/O library implementation + */ + +#include +#include +#include +#include +#include +#include + +#include "binio.h" + + + +#if UCHAR_MAX > 0xFFU +# error 'binio' requires 8 bit chars +#endif + +typedef unsigned char uint8; +typedef signed char int8; + +#if USHRT_MAX > 0xFFFFU +# error 'binio' requires 16 bit shorts +#endif + +typedef unsigned short uint16; +typedef short int16; + +#if ULONG_MAX == 0xFFFFFFFFUL + +typedef unsigned long uint32; +typedef long int32; + +#elif UINT_MAX == 0xFFFFFFFFUL + +typedef unsigned int uint32; +typedef int int32; + +#else +# error 'binio' requires either 32 bit longs or 32 bit ints +#endif + + + +struct binio_file *binio_from_file(const char *file, const char *mode) +{ + struct binio_file *bf; + + assert(file != NULL); + assert(mode != NULL); + + if ((bf = malloc(sizeof *bf)) == NULL) { + fprintf(stderr, "Unable to allocate new file handle\n"); + return NULL; + } + + if ((bf->fp = fopen(file, mode)) == NULL) { + fprintf(stderr, "Error opening \"%s\", %s\n", file, + strerror(errno)); + return NULL; + } + + bf->name = file; + bf->eof = bf->error = 0; + + assert(bf->fp != NULL); + assert(bf->name == file); + assert(bf->eof == 0); + assert(bf->error == 0); + + return bf; +} + + + +void binio_close(struct binio_file *bf) +{ + assert(bf != NULL); + + if (fclose(bf->fp) == EOF) { + fprintf(stderr, "Error closing \"%s\", %s\n", bf->name, + strerror(errno)); + } + + free(bf); +} + + + +unsigned char *binio_read_file(const char *file, size_t *size) +{ + struct binio_file *bf; + unsigned char *data; + int success; + + assert(file != NULL); + assert(size != NULL); + + if ((bf = binio_from_file(file, "rb")) == NULL) { + return NULL; + } + + if ((*size = binio_get_size(bf)) == 0) { + binio_close(bf); + return NULL; + } + + if ((data = malloc(sizeof *data * *size)) == NULL) { + fprintf(stderr, "Unable to allocate new binary buffer\n"); + binio_close(bf); + return NULL; + } + + binio_read(bf, data, *size); + success = !(bf->error || bf->eof); + + if (bf->eof) { + fprintf(stderr, "Error reading \"%s\", Unexpected EOF\n", + bf->name); + } + + binio_close(bf); + + if (!success) { + free(data); + return NULL; + } + + return data; +} + + + +void binio_read(struct binio_file *bf, unsigned char *data, size_t size) +{ + size_t i; + + assert(bf != NULL); + assert(data != NULL); + assert(size > 0); + + for (i = 0; i < size; i++) { + data[i] = binio_read_u8(bf); + } +} + + + +unsigned char binio_read_u8(struct binio_file *bf) +{ + uint8 u8; + + assert(bf != NULL); + + if (bf->eof || bf->error) { + return 0; + } + + if (fread(&u8, 1, 1, bf->fp) == 1) { + return u8; + } + + if (feof(bf->fp)) { + bf->eof = 1; + } else if (ferror(bf->fp)) { + bf->error = 1; + fprintf(stderr, "Error reading \"%s\", %s\n", bf->name, + strerror(errno)); + } + + return 0; +} + + + +signed char binio_read_s8(struct binio_file *bf) +{ + return (int8)binio_read_u8(bf); +} + + + +unsigned short binio_read_u16le(struct binio_file *bf) +{ + uint16 u16; + + u16 = binio_read_u8(bf); + u16 |= (uint16)binio_read_u8(bf) << 8; + + return u16; +} + + + +short binio_read_s16le(struct binio_file *bf) +{ + return (int16)binio_read_u16le(bf); +} + + + +unsigned short binio_read_u16be(struct binio_file *bf) +{ + uint16 u16; + + u16 = (uint16)binio_read_u8(bf) << 8; + u16 |= binio_read_u8(bf); + + return u16; +} + + + +short binio_read_s16be(struct binio_file *bf) +{ + return (int16)binio_read_u16be(bf); +} + + + +unsigned long binio_read_u32le(struct binio_file *bf) +{ + uint32 u32; + + u32 = binio_read_u8(bf); + u32 |= (uint32)binio_read_u8(bf) << 8; + u32 |= (uint32)binio_read_u8(bf) << 16; + u32 |= (uint32)binio_read_u8(bf) << 24; + + return u32; +} + + + +long binio_read_s32le(struct binio_file *bf) +{ + return (int32)binio_read_u32le(bf); +} + + + +unsigned long binio_read_u32be(struct binio_file *bf) +{ + uint32 u32; + + u32 = (uint32)binio_read_u8(bf) << 24; + u32 |= (uint32)binio_read_u8(bf) << 16; + u32 |= (uint32)binio_read_u8(bf) << 8; + u32 |= binio_read_u8(bf); + + return u32; +} + + + +long binio_read_s32be(struct binio_file *bf) +{ + return (int32)binio_read_u32be(bf); +} + + + +int binio_write_file(const char *file, const unsigned char *data, size_t size) +{ + struct binio_file *bf; + int success; + + if ((bf = binio_from_file(file, "wb")) == NULL) { + return 0; + } + + binio_write(bf, data, size); + success = !bf->error; + binio_close(bf); + + return success; +} + + + +void binio_write(struct binio_file *bf, const unsigned char *data, size_t size) +{ + size_t i; + + assert(bf != NULL); + assert(data != NULL); + assert(size > 0); + + for (i = 0; i < size; i++) { + binio_write_u8(bf, data[i]); + } +} + + + +void binio_write_u8(struct binio_file *bf, unsigned char u8) +{ + assert(bf != NULL); + + if (bf->error) { + return; + } + + if (fwrite(&u8, 1, 1, bf->fp) == 1) { + return; + } + + bf->error = 1; + + if (ferror(bf->fp)) { + fprintf(stderr, "Error writing \"%s\", %s\n", bf->name, + strerror(errno)); + } +} + + + +void binio_write_s8(struct binio_file *bf, signed char s8) +{ + binio_write_u8(bf, (uint8)s8); +} + + + +void binio_write_u16le(struct binio_file *bf, unsigned short u16) +{ + binio_write_u8(bf, (uint16)(u16 & 0xFF)); + binio_write_u8(bf, (uint16)(u16 >> 8 & 0xFF)); +} + + + +void binio_write_s16le(struct binio_file *bf, short s16) +{ + binio_write_u16le(bf, (uint16)s16); +} + + + +void binio_write_u16be(struct binio_file *bf, unsigned short u16) +{ + binio_write_u8(bf, (uint16)(u16 >> 8 & 0xFF)); + binio_write_u8(bf, (uint16)(u16 & 0xFF)); +} + + + +void binio_write_s16be(struct binio_file *bf, short s16) +{ + binio_write_u16be(bf, (uint16)s16); +} + + + +void binio_write_u32le(struct binio_file *bf, unsigned long u32) +{ + binio_write_u8(bf, (uint32)(u32 & 0xFF)); + binio_write_u8(bf, (uint32)(u32 >> 8 & 0xFF)); + binio_write_u8(bf, (uint32)(u32 >> 16 & 0xFF)); + binio_write_u8(bf, (uint32)(u32 >> 24 & 0xFF)); +} + + + +void binio_write_s32le(struct binio_file *bf, long s32) +{ + binio_write_u32le(bf, (uint32)s32); +} + + + +void binio_write_u32be(struct binio_file *bf, unsigned long u32) +{ + binio_write_u8(bf, (uint32)(u32 >> 24 & 0xFF)); + binio_write_u8(bf, (uint32)(u32 >> 16 & 0xFF)); + binio_write_u8(bf, (uint32)(u32 >> 8 & 0xFF)); + binio_write_u8(bf, (uint32)(u32 & 0xFF)); +} + + + +void binio_write_s32be(struct binio_file *bf, long s32) +{ + binio_write_u32be(bf, (uint32)s32); +} + + + +void binio_rewind(struct binio_file *bf) +{ + assert(bf != NULL); + + if (fseek(bf->fp, 0L, SEEK_SET) == -1) { + bf->error = 1; + fprintf(stderr, "Error rewinding \"%s\", %s\n", bf->name, + strerror(errno)); + } else { + bf->eof = 0; + } +} + + + +size_t binio_get_size(struct binio_file *bf) +{ + size_t size = 0; + + assert(bf != NULL); + + binio_read_u8(bf); + + while (!bf->eof) { + binio_read_u8(bf); + size++; + } + + binio_rewind(bf); + + if (bf->error) { + return 0; + } + + return size; +} + diff --git a/nes15-1.0.0/clib/binio.h b/nes15-1.0.0/clib/binio.h new file mode 100644 index 0000000..c259976 --- /dev/null +++ b/nes15-1.0.0/clib/binio.h @@ -0,0 +1,185 @@ +/* + * File: binio.h + * Namespace: binio_ / BINIO_ + * Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) + * + * Binary file I/O library + * + * Note that 'binio' assumes that the user will never write or expect to read a + * value outside of minimums guaranteed by the C Standard. For example, nothing + * greater than 255 for unsigned chars, or nothing less than -2147483647L for + * longs. + */ + +#ifndef BINIO_H +#define BINIO_H + +#include + + + +/* + * Binary file handle returned by 'binio_from*' and freed by 'binio_close'. + */ +struct binio_file { + FILE *fp; /* Private */ + const char *name; /* Read-only */ + int eof; /* Read-only */ + int error; /* Read-only */ +}; + + + +/* + * Opens the file 'file' for use. The string 'mode' is passed to 'fopen' and + * should always include the 'b' option. + * + * Returns: If the file was opened successfully, a new file handle is returned. + * Otherwise, NULL returned and an error message is printed to 'stderr'. + * + * Asserts: + * file != NULL + * mode != NULL + * Returned 'binio_file' is valid + */ +struct binio_file *binio_from_file(const char *file, const char *mode); + +/* + * Closes the binary file 'bf'. Note that the file handle is freed by this + * function. If an error occurred while closing the file, an error message is + * printed to 'stderr'. + * + * Asserts: + * bf != NULL + */ +void binio_close(struct binio_file *bf); + +/* + * Reads the entire file 'file' into a newly allocated buffer. + * + * Returns: If the buffer was successfully created and read into, it is + * returned and 'size' is set to the size of the buffer. Otherwise, NULL is + * returned and an error message is printed to 'stderr'. + * + * Asserts: + * file != NULL + * size != NULL + */ +unsigned char *binio_read_file(const char *file, size_t *size); + +/* + * Reads 'size' bytes from the binary file 'bf' from its current position + * forward into the buffer 'data'. If the end of the file was reached before + * reading is complete, the file handle's 'eof' member will be set to a + * non-zero value. If an error occurred while reading, the file handle's + * 'error' member will be set to a non-zero value and an error message will be + * printed to 'stderr'. + * + * Asserts: + * bf != NULL + * data != NULL + * size > 0 + */ +void binio_read(struct binio_file *bf, unsigned char *data, size_t size); + +/* + * The following input functions read binary values from the binary file 'bf'. + * Functions with names that end with 'le' read data in little-endian format. + * Functions with names that end with 'be' read data in big-endian format. If + * the end of the input file is reached, the file handle's 'eof' member will be + * set to a non-zero value. If an error occurs while reading, the file handle's + * 'error' member will be set to a non-zero value and an error message will be + * printed to 'stderr'. + * + * Returns: If successful, the value read from file is returned, otherwise + * zero is returned. + * + * Asserts: + * bf != NULL + */ +unsigned char binio_read_u8(struct binio_file *bf); +signed char binio_read_s8(struct binio_file *bf); +unsigned short binio_read_u16le(struct binio_file *bf); +short binio_read_s16le(struct binio_file *bf); +unsigned short binio_read_u16be(struct binio_file *bf); +short binio_read_s16be(struct binio_file *bf); +unsigned long binio_read_u32le(struct binio_file *bf); +long binio_read_s32le(struct binio_file *bf); +unsigned long binio_read_u32be(struct binio_file *bf); +long binio_read_s32be(struct binio_file *bf); + +/* + * Writes the binary data in 'data' with the size 'size' to the file 'file'. + * + * Returns: If the binary data was successfully written, zero is returned. + * Otherwise, a non-zero value is returned and an error message is printed to + * 'stderr'. + * + * Asserts: + * file != NULL + * data != NULL + * size > 0 + */ +int binio_write_file(const char *file, const unsigned char *data, size_t size); + +/* + * Writes 'size' bytes to the binary file 'bf' from its current position + * forward from the buffer 'data'. If an error occurs while writing, the file + * handle's 'error' member will be set to a non-zero value and an error message + * will be printed to 'stderr'. + * + * Asserts: + * bf != NULL + * data != NULL + * size > 0 + */ +void binio_write(struct binio_file *bf, const unsigned char *data, + size_t size); + +/* + * The following output functions write binary values to the binary file 'bf'. + * Functions with names that end with 'le' write data in little-endian format. + * Functions with names that end with 'be' write data in big-endian format. If + * an error occurs while writing, the file handle's 'error' member will be set + * to a non-zero value and an error message will be printed to 'stderr'. + * + * Asserts: + * bf != NULL + */ +void binio_write_u8(struct binio_file *bf, unsigned char u8); +void binio_write_s8(struct binio_file *bf, signed char s8); +void binio_write_u16le(struct binio_file *bf, unsigned short u16); +void binio_write_s16le(struct binio_file *bf, short s16); +void binio_write_u16be(struct binio_file *bf, unsigned short u16); +void binio_write_s16be(struct binio_file *bf, short s16); +void binio_write_u32le(struct binio_file *bf, unsigned long u32); +void binio_write_s32le(struct binio_file *bf, long s32); +void binio_write_u32be(struct binio_file *bf, unsigned long u32); +void binio_write_s32be(struct binio_file *bf, long s32); + +/* + * Resets the current position of the binary file 'bf' to its beginning. If an + * error occurs while rewinding, the file handle's 'error' member will be set + * to a non-zero value and an error message will be printed to 'stderr'. + * + * Asserts: + * bf != NULL + */ +void binio_rewind(struct binio_file *bf); + +/* + * Gets the number of bytes left to be read in the binary file 'bf', then + * resets the binary file's position back to the beginning of the file. + * + * Returns: If successful, the number of bytes left in the file is returned. + * Otherwise, if an error occurs or there where no bytes left to read, zero + * is returned. If an error does occur, the file handle's 'error' member will + * be set to a non-zero value and an error message will be printed to 'stderr'. + * + * Asserts: + * bf != NULL + */ +size_t binio_get_size(struct binio_file *bf); + +#endif /* BINIO_H */ + diff --git a/nes15-1.0.0/clib/img8.c b/nes15-1.0.0/clib/img8.c new file mode 100644 index 0000000..b46ed28 --- /dev/null +++ b/nes15-1.0.0/clib/img8.c @@ -0,0 +1,53 @@ +/* + * File: img8.c + * Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) + * + * 8bpp image library implementation + */ + +#include +#include +#include + +#include "img8.h" + + + +struct img8_image *img8_new(int w, int h) +{ + struct img8_image *img; + + assert(w > 0 && h > 0); + + if ((img = malloc(sizeof *img)) == NULL) { + fprintf(stderr, "Unable to allocate new image\n"); + return NULL; + } + + if ((img->data = malloc(sizeof *img->data * w * h)) == NULL) { + fprintf(stderr, "Unable to allocate new image data\n"); + free(img); + return NULL; + } + + img->w = w; + img->h = h; + + assert(img->w == w); + assert(img->h == h); + assert(img->data != NULL); + + return img; +} + + + +void img8_free(struct img8_image *img) +{ + assert(img->data != NULL); + assert(img != NULL); + + free(img->data); + free(img); +} + diff --git a/nes15-1.0.0/clib/img8.h b/nes15-1.0.0/clib/img8.h new file mode 100644 index 0000000..02e8c8f --- /dev/null +++ b/nes15-1.0.0/clib/img8.h @@ -0,0 +1,87 @@ +/* + * File: img8.h + * Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) + * + * 8bpp image library + */ + +#ifndef IMG8_H +#define IMG8_H + +#define IMG8_PAL_SIZE 256 + + + +struct binio_file; + +/* + * 8bpp image created and freed by the library. Its members are considered + * read-only. + */ +struct img8_image { + int w, h; + unsigned char *data; +}; + +/* + * Used to represent palette entries for 8bpp images. + */ +struct img8_color { + unsigned char r, g, b; +}; + + + +/* + * Creates a new image with the width 'w' and the height 'h'. + * + * Returns: If the image was created successfully, it is returned. Otherwise, + * NULL is returned and an error message is printed to 'stderr'. + * + * Asserts: + * w > 0 && h > 0 + * Returned 'img8_image' is valid + */ +struct img8_image *img8_new(int w, int h); + +/* + * Frees the image 'img'. + * + * Asserts: + * img != NULL + * img->data != NULL + */ +void img8_free(struct img8_image *img); + +/* + * Reads a new image from the 256 color PCX file 'bf' and stores the palette + * read in pal. If 'pal' is NULL, then the palette will not be read. If the end + * of the file was reached before reading is complete, the file handle's 'eof' + * member will be set to a non-zero value. If an error occurred while reading, + * the file handle's 'error' member will be set to a non-zero value and an + * error message will be printed to 'stderr'. + * + * Returns: If the image was successfully created, it is returned. Otherwise, + * NULL is returned and an error message is printed to 'stderr'. + * + * Asserts: + * file != NULL + */ +struct img8_image *img8_read_pcx_file(const char *file, + struct img8_color *pal); + +/* + * Reads a new image from the 256 color PCX binary file 'bf' and stores the + * palette read in 'pal'. If 'pal' is NULL, then the palette will not be read. + * + * Returns: If the image was successfully created and read, it is returned. + * Otherwise, NULL is returned and an error message is printed to 'stderr'. + * + * Asserts: + * bf != NULL + */ +struct img8_image *img8_read_pcx(struct binio_file *bf, + struct img8_color *pal); + +#endif /* IMG8_H */ + diff --git a/nes15-1.0.0/clib/img8_pcx.c b/nes15-1.0.0/clib/img8_pcx.c new file mode 100644 index 0000000..39dc9ba --- /dev/null +++ b/nes15-1.0.0/clib/img8_pcx.c @@ -0,0 +1,197 @@ +/* + * File: img8_pcx.c + * Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) + * + * PCX image loading and saving functions for 'img8' + */ + +#include +#include +#include +#include "binio.h" + +#include "img8.h" + + + +#define COLOR_MAP_SIZE 48 +#define FILLER_SIZE 54 + + + +struct pcx_header { + unsigned char man; + unsigned char ver; + unsigned char enc; + unsigned char bpp; + unsigned short x_min, y_min, x_max, y_max; + unsigned short h_dpi, v_dpi; + unsigned char color_map[COLOR_MAP_SIZE]; + unsigned char res; + unsigned char num_planes; + unsigned short bpl; + unsigned short pal_info; + unsigned short h_scr_size; + unsigned short v_scr_size; + unsigned char filler[FILLER_SIZE]; +}; + + + +/* + * Reads the PCX header from the binary file 'bf' and stores it in 'h'. + * + * Returns: If the PCX header was read successfully and is valid, then a + * non-zero is returned. Otherwise, zero is returned and an error message is + * printed to 'stderr'. + * + * Asserts: + * bf != NULL + * h != NULL + */ +static int read_pcx_header(struct binio_file *bf, struct pcx_header *h); + + + +struct img8_image *img8_read_pcx_file(const char *file, struct img8_color *pal) +{ + struct binio_file *bf; + struct img8_image *img; + int success; + + assert(file != NULL); + + if ((bf = binio_from_file(file, "rb")) == NULL) { + return NULL; + } + + img = img8_read_pcx(bf, pal); + binio_close(bf); + + if (img == NULL) { + return NULL; + } + + success = !(bf->error || bf->eof); + + if (bf->eof) { + fprintf(stderr, "Error reading \"%s\", File truncated\n", + bf->name); + } + + if (!success) { + img8_free(img); + return NULL; + } + + return img; +} + + + +struct img8_image *img8_read_pcx(struct binio_file *bf, struct img8_color *pal) +{ + struct pcx_header header; + struct img8_image *img; + unsigned char *dst; + unsigned char u8; + int h; + int i; + int c = 0; + + assert(bf != NULL); + + if (!read_pcx_header(bf, &header)) { + return NULL; + } + + if ((img = img8_new(header.x_max - header.x_min + 1, + header.y_max - header.y_min + 1)) == NULL) { + return NULL; + } + + dst = img->data; + + for (h = 0; h < img->h; h++) { + i = 0; + + while (i < header.bpl) { + u8 = binio_read_u8(bf); + + if ((u8 & 0xC0) == 0xC0) { + c = u8 & 0x3F; + u8 = binio_read_u8(bf); + } else { + c = 1; + } + + while (c-- > 0) { + dst[i++] = u8; + } + } + + dst += img->w; + } + + binio_read_u8(bf); + + if (pal != NULL) { + for (i = 0; i < IMG8_PAL_SIZE; i++) { + pal[i].r = binio_read_u8(bf); + pal[i].g = binio_read_u8(bf); + pal[i].b = binio_read_u8(bf); + } + } + + return img; +} + + + +static int read_pcx_header(struct binio_file *bf, struct pcx_header *h) +{ + assert(bf != NULL); + assert(h != NULL); + + h->man = binio_read_u8(bf); + h->ver = binio_read_u8(bf); + h->enc = binio_read_u8(bf); + h->bpp = binio_read_u8(bf); + h->x_min = binio_read_u16le(bf); + h->y_min = binio_read_u16le(bf); + h->x_max = binio_read_u16le(bf); + h->y_max = binio_read_u16le(bf); + h->h_dpi = binio_read_u16le(bf); + h->v_dpi = binio_read_u16le(bf); + + binio_read(bf, h->color_map, COLOR_MAP_SIZE); + + h->res = binio_read_u8(bf); + h->num_planes = binio_read_u8(bf); + h->bpl = binio_read_u16le(bf); + h->pal_info = binio_read_u16le(bf); + h->h_scr_size = binio_read_u16le(bf); + h->v_scr_size = binio_read_u16le(bf); + + binio_read(bf, h->filler, FILLER_SIZE); + + if (bf->error) { + return 0; + } + + if (bf->eof) { + fprintf(stderr, "Error reading \"%s\", Header truncated\n", + bf->name); + return 0; + } + + if (h->man != 10 || h->ver != 5 || h->enc != 1 || h->bpp != 8 + || h->num_planes != 1) { + fprintf(stderr, "Error reading \"%s\", Not a valid 256 " + "color PCX file\n", bf->name); + return 0; + } + + return 1; +} + diff --git a/nes15-1.0.0/clib/neschr.c b/nes15-1.0.0/clib/neschr.c new file mode 100644 index 0000000..3c4bcd6 --- /dev/null +++ b/nes15-1.0.0/clib/neschr.c @@ -0,0 +1,221 @@ +/* + * File: neschr.c + * Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) + * + * NES CHR image library implementation + */ + +#include +#include +#include +#include +#include "binio.h" +#include "img8.h" + +#include "neschr.h" + + + +/* + * Reads a CHR tile into the CHR image 'chr' at 'dst' from the 8bpp image 'img' + * starting from the top-left corner 'sx' and 'sy'. + * + * Asserts: + * chr != NULL + * img != NULL + * dst < chr->size + * sx >= 0 && sx < img->w && sy >= 0 && sy < img->h + */ +static void tile_from_img8(struct neschr_chr *chr, + const struct img8_image *img, size_t dst, int sx, int sy); + + + +struct neschr_chr *neschr_new(size_t size) +{ + struct neschr_chr *chr; + + assert(size > 0); + + if ((chr = malloc(sizeof *chr)) == NULL) { + fprintf(stderr, "Unable to allocate new NES CHR\n"); + return NULL; + } + + if ((chr->data = malloc(sizeof *chr->data * size)) == NULL) { + fprintf(stderr, "Unable to allocate new NES CHR data\n"); + free(chr); + return NULL; + } + + chr->size = size; + + assert(chr->size == size); + assert(chr->data != NULL); + + return chr; +} + + + +void neschr_free(struct neschr_chr *chr) +{ + assert(chr != NULL); + assert(chr->data != NULL); + + free(chr->data); + free(chr); +} + + + +struct neschr_chr *neschr_read_file(const char *file) +{ + struct neschr_chr *chr; + unsigned char *data; + size_t size; + + assert(file != NULL); + + if ((chr = malloc(sizeof *chr)) == NULL) { + fprintf(stderr, "Unable to allocate new NES CHR\n"); + return NULL; + } + + if ((data = binio_read_file(file, &size)) == NULL) { + free(chr); + return NULL; + } + + chr->size = size; + chr->data = data; + + assert(chr->size == size); + assert(chr->data != NULL); + + return chr; +} + + + +struct neschr_chr *neschr_read(struct binio_file *bf, size_t size) +{ + struct neschr_chr *chr; + + assert(bf != NULL); + assert(size > 0); + + if ((chr = neschr_new(size)) == NULL) { + return NULL; + } + + binio_read(bf, chr->data, size); + + assert(chr->size == size); + assert(chr->data != NULL); + + return chr; +} + + + +int neschr_write_file(const char *file, const struct neschr_chr *chr) +{ + assert(file != NULL); + assert(chr != NULL); + + return binio_write_file(file, chr->data, chr->size); +} + + + +void neschr_write(struct binio_file *bf, const struct neschr_chr *chr) +{ + assert(bf != NULL); + assert(chr != NULL); + + binio_write(bf, chr->data, chr->size); +} + + + +struct neschr_chr *neschr_from_img8(const struct img8_image *img, int ver) +{ + struct neschr_chr *chr; + int x, y; + int w = (img->w + 7) / 8 * 8; + int h = (img->h + 7) / 8 * 8; + size_t i = 0; + + assert(img != NULL); + + if ((chr = neschr_new(img->w * img->h / (NESCHR_TILE_SIZE + * NESCHR_TILE_SIZE) * NESCHR_TILE_BYTES)) == NULL) { + return NULL; + } + + if (!ver) { + for (y = 0; y < h; y += NESCHR_TILE_SIZE) { + for (x = 0; x < w; x += NESCHR_TILE_SIZE) { + tile_from_img8(chr, img, i, x, y); + i += NESCHR_TILE_BYTES; + } + } + } else { + for (x = 0; x < w; x += NESCHR_TILE_SIZE) { + for (y = 0; y < h; y += NESCHR_TILE_SIZE) { + tile_from_img8(chr, img, i, x, y); + i += NESCHR_TILE_BYTES; + } + } + } + + assert(chr->size == i); + assert(chr->data != NULL); + + return chr; +} + + + +int neschr_is_equal(const struct neschr_chr *chr1, + const struct neschr_chr *chr2) +{ + assert(chr1 != NULL); + assert(chr2 != NULL); + + if (chr1->size != chr2->size) { + return 0; + } + + return !memcmp(chr1->data, chr2->data, chr1->size); +} + + + +static void tile_from_img8(struct neschr_chr *chr, + const struct img8_image *img, size_t dst, int sx, int sy) +{ + int p; + int x, y; + unsigned char u8; + + assert(chr != NULL); + assert(img != NULL); + assert(dst < chr->size); + assert(sx >= 0 && sx < img->w && sy >= 0 && sy < img->h); + + for (p = 0; p < NESCHR_NUM_PLANES; p++) { + for (y = sy; y < sy + NESCHR_TILE_SIZE; y++) { + u8 = 0; + + for (x = sx; x < sx + NESCHR_TILE_SIZE; x++) { + u8 <<= 1; + u8 |= (img->data[x + y * img->w] >> p) & 1; + } + + chr->data[dst++] = u8; + } + } +} + diff --git a/nes15-1.0.0/clib/neschr.h b/nes15-1.0.0/clib/neschr.h new file mode 100644 index 0000000..618a479 --- /dev/null +++ b/nes15-1.0.0/clib/neschr.h @@ -0,0 +1,140 @@ +/* + * File: neschr.h + * Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) + * + * NES CHR image library + */ + +#ifndef NESCHR_H +#define NESCHR_H + +#include + + + +#define NESCHR_BPP 2 +#define NESCHR_TILE_SIZE 8 +#define NESCHR_NUM_PLANES 2 +#define NESCHR_TILE_BYTES (NESCHR_TILE_SIZE * NESCHR_NUM_PLANES) + + + +struct binio_file; +struct img8_image; + +/* + * CHR image created and freed by the library. Its members are considered + * read-only. + */ +struct neschr_chr { + size_t size; + unsigned char *data; +}; + + + +/* + * Creates a new CHR image with the size 'size'. + * + * Returns: If the CHR image was created successfully, it is returned. + * Otherwise, NULL is returned and an error message is printed to 'stderr'. + * + * Asserts: + * size > 0 + * Returned 'neschr_chr' is valid + */ +struct neschr_chr *neschr_new(size_t size); + +/* + * Frees the CHR image 'chr'. + * + * Asserts: + * chr != NULL + * chr->data != NULL + */ +void neschr_free(struct neschr_chr *chr); + +/* + * Reads a new CHR image from the file 'file'. + * + * Returns: If the CHR image was successfully created and read, it is + * returned. Otherwise, NULL is returned and an error message is printed to + * 'stderr'. + * + * Asserts: + * file != NULL + * Returned 'neschr_chr' is valid + */ +struct neschr_chr *neschr_read_file(const char *file); + +/* + * Reads a new CHR image from the file 'bf' with the size 'size'. If the end of + * the file was reached before reading is complete, the file handle's 'eof' + * member will be set to a non-zero value. If an error occurred while reading, + * the file handle's 'error' member will be set to a non-zero value and an + * error message will be printed to 'stderr'. + * + * Returns: If the CHR image was successfully created, it is returned. + * Otherwise, NULL is returned and an error message is printed to 'stderr'. + * + * Asserts: + * bf != NULL + * size > 0 + * Returned 'neschr_chr' is valid + */ +struct neschr_chr *neschr_read(struct binio_file *bf, size_t size); + +/* + * Writes the CHR image 'chr' to the file 'file'. + * + * Returns: If the CHR image was successfully written, a non-zero value is + * returned. Otherwise, zero is returned and an error message is printed to + * 'stderr'. + * + * Asserts: + * file != NULL + * chr != NULL + */ +int neschr_write_file(const char *file, const struct neschr_chr *chr); + +/* + * Writes the CHR image 'chr' to the binary file 'bf'. If an error occurs while + * writing, the file handle's 'error' member will be set to a non-zero value + * and an error message will be printed to 'stderr'. + * + * Asserts: + * bf != NULL + * chr != NULL + */ +void neschr_write(struct binio_file *bf, const struct neschr_chr *chr); + +/* + * Creates a new CHR image from the 8bpp image 'img'. If 'ver' is set to a + * non-zero value, the 8bpp image is read from top to bottom as opposed to left + * to right. + * + * Returns: If the CHR image was created successfully, it is returned. + * Otherwise, NULL is returned and an error message is printed to 'stderr'. + * + * Asserts: + * img != NULL + * Returned 'neschr_chr' is valid + */ +struct neschr_chr *neschr_from_img8(const struct img8_image *img, int ver); + +/* + * Tests if the CHR image 'chr1' contains the same data as the CHR image + * 'chr2'. + * + * Returns: If both images contain the same data, then a non-zero value is + * returned. Otherwise, zero is returned. + * + * Asserts: + * chr1 != NULL + * chr2 != NULL + */ +int neschr_is_equal(const struct neschr_chr *chr1, + const struct neschr_chr *chr2); + +#endif /* NESCHR_H */ + diff --git a/nes15-1.0.0/clib/packbits.c b/nes15-1.0.0/clib/packbits.c new file mode 100644 index 0000000..7f8bdb4 --- /dev/null +++ b/nes15-1.0.0/clib/packbits.c @@ -0,0 +1,208 @@ +/* + * File: packbits.c + * Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) + * + * Packbits encoding and decoding library implementation + */ + +#include +#include +#include +#include "binio.h" + +#include "packbits.h" + + + +unsigned char *packbits_read_file(const char *file, size_t *size) +{ + struct binio_file *bf; + unsigned char *data; + int success; + + assert(file != NULL); + assert(size != NULL); + + if ((bf = binio_from_file(file, "rb")) == NULL) { + return NULL; + } + + if ((*size = packbits_get_size(bf)) == 0) { + binio_close(bf); + return NULL; + } + + if ((data = malloc(sizeof *data * *size)) == NULL) { + fprintf(stderr, "Unable to allocate decoded PackBits data\n"); + binio_close(bf); + return NULL; + } + + packbits_read(bf, data, *size); + success = !(bf->error || bf->eof); + + if (bf->eof) { + fprintf(stderr, "Error reading \"%s\", File truncated\n", + bf->name); + } + + binio_close(bf); + + if (!success) { + free(data); + return NULL; + } + + return data; +} + + + +void packbits_read(struct binio_file *bf, unsigned char *data, size_t size) +{ + signed char header; + unsigned char *data_end = data + size; + unsigned char u8; + int i; + + assert(bf != NULL); + assert(data != NULL); + assert(size > 0); + + while ((header = binio_read_s8(bf)) != -128 && data != data_end) { + if (header >= 0) { + for (i = header + 1; i > 0; i--) { + *data++ = binio_read_u8(bf); + } + } else { + u8 = binio_read_u8(bf); + + for (i = 1 - header; i > 0; i--) { + *data++ = u8; + } + } + } +} + + + +size_t packbits_write_file(const char *file, const unsigned char *data, + size_t size) +{ + struct binio_file *bf; + size_t out_size; + + assert(file != NULL); + assert(data != NULL); + assert(size > 0); + + if ((bf = binio_from_file(file, "wb")) == NULL) { + return 0; + } + + out_size = packbits_write(bf, data, size); + binio_close(bf); + + return out_size; +} + + + +size_t packbits_write(struct binio_file *bf, const unsigned char *data, + size_t size) +{ + const unsigned char *data_end = data + size; + const unsigned char *curr; + size_t dst_size = 0; + + assert(bf != NULL); + assert(data != NULL); + assert(size > 0); + + while (data < data_end) { + if (data < data_end - 1 && *data == *(data + 1)) { + curr = data + 1; + + while (curr != data_end) { + if (*curr != *(curr - 1)) { + break; + } + + if (curr - data >= 128) { + break; + } + + curr++; + } + + binio_write_s8(bf, 1 - (curr - data)); + binio_write_u8(bf, *data); + data = curr; + dst_size += 2; + } else { + curr = data + 1; + + while (curr != data_end) { + if (*curr == *(curr + 1)) { + break; + } + + if (curr - data >= 128) { + break; + } + + curr++; + } + + binio_write_s8(bf, curr - data - 1); + dst_size++; + + while (data < curr) { + binio_write_u8(bf, *data++); + dst_size++; + } + } + } + + binio_write_s8(bf, -128); + dst_size++; + + if (bf->error) { + return 0; + } + + return dst_size; +} + + + +size_t packbits_get_size(struct binio_file *bf) +{ + size_t size = 0; + signed char header; + int i; + + assert(bf != NULL); + + while ((header = binio_read_s8(bf)) != -128 && !bf->eof) { + if (header >= 0) { + for (i = header + 1; i > 0; i--) { + binio_read_u8(bf); + size++; + } + } else { + binio_read_u8(bf); + size += 1 - header; + } + + } + + binio_rewind(bf); + + if (bf->error) { + return 0; + } + + return size; +} + diff --git a/nes15-1.0.0/clib/packbits.h b/nes15-1.0.0/clib/packbits.h new file mode 100644 index 0000000..dcf2d9e --- /dev/null +++ b/nes15-1.0.0/clib/packbits.h @@ -0,0 +1,112 @@ +/* + * File: packbits.h + * Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) + * + * Packbits encoding and decoding library + * + * A PackBits data stream consists of packets with a one-byte header followed + * by data. The header is a signed byte; the data is treated as an unsigned + * char. + * + * Header byte Data following the header byte + * + * 0 to 127 (1 + n) literal bytes of data + * -1 to -127 One byte of data, repeated (1 - n) times in the + * decompressed output + * -128 Marks the end of the encoded data + * + * Note that the PackBits encoding used here differs somewhat from the + * original. The header byte -128 is used to mark the end of the encoded data + * instead of indicating a no-op or a run. + */ + +#ifndef PACKBITS_H +#define PACKBITS_H + +#include + + + +struct binio_file; + + +/* + * Reads the entire Packbits encoded file 'file' into a newly allocated buffer. + * + * Returns: If the buffer was successfully created and read into, it is + * returned and 'size' is set to the size of the buffer. Otherwise, NULL is + * returned and an error message is printed to 'stderr'. + * + * Asserts: + * file != NULL + * size != NULL + */ +unsigned char *packbits_read_file(const char *file, size_t *size); + +/* + * Reads 'size' bytes of decoded data from the PackBits encoded binary file + * 'bf' from its current position forward into 'data'. If the end of the file + * was reached before reading is complete, the file handle's 'eof' member will + * be set to a non-zero value. If an error occurred while reading, the file + * handle's 'error' member will be set to a non-zero value and an error message + * will be printed to 'stderr'. + * + * Asserts: + * bf != NULL + * data != NULL + * size > 0 + */ +void packbits_read(struct binio_file *bf, unsigned char *data, size_t size); + + +/* + * Writes the binary data in 'data' with the size 'size' to the file 'file' in + * PackBits encoding. + * + * Returns: If the binary data was compressed and successfully written, the + * size of the encoded data is returned. Otherwise, zero is returned and an + * error message is printed to 'stderr'. + * + * Asserts: + * file != NULL + * data != NULL + * size > 0 + */ +size_t packbits_write_file(const char *file, const unsigned char *data, + size_t size); + +/* + * Writes 'size' bytes to the binary file 'bf' from its current position + * forward from the buffer 'data' in PackBits encoding. If an error occurs + * while writing, the file handle's 'error' member will be set to a non-zero + * value and an error message will be printed to 'stderr'. + * + * Returns: If the binary data was compressed and successfully written, the + * size of the encoded data is returned, otherwise zero is returned. + * + * Asserts: + * bf != NULL + * data != NULL + * size > 0 + */ +size_t packbits_write(struct binio_file *bf, const unsigned char *data, + size_t size); + +/* + * Gets the number of bytes (after decoding) left to be read in the PackBits + * encoded binary file 'bf', then resets the binary file's position back to the + * beginning of the file. + * + * Returns: If successful, the number of bytes left (after decoding) in the + * file is returned. Otherwise, if an error occurs or there where no bytes left + * to read, zero is returned. If an error does occur, the file handle's 'error' + * member will be set to a non-zero value and an error message will be printed + * to 'stderr'. + * + * Asserts: + * bf != NULL + */ +size_t packbits_get_size(struct binio_file *bf); + +#endif /* PACKBITS_H */ + diff --git a/nes15-1.0.0/fifteen/Makefile b/nes15-1.0.0/fifteen/Makefile new file mode 100644 index 0000000..14fd3cd --- /dev/null +++ b/nes15-1.0.0/fifteen/Makefile @@ -0,0 +1,35 @@ +# +# 'fifteen' test GNU Makefile +# + +INCS = \ + ../nes-lib/lfsr32.inc \ + fifteen.inc + +OBJS = \ + ../nes-lib/lfsr32.o \ + fifteen.o \ + test.o + +BIN = test.bin + +AS65 = ca65 +LD65 = ld65 +AFLAGS65 = -l -I ../nes-lib +LCONFIG = test.cfg +MAPFILE = map.txt + +# +# Targets +# + +$(BIN): $(OBJS) + $(LD65) -C $(LCONFIG) $(OBJS) -m $(MAPFILE) -o $(BIN) + +.PHONY: clean +clean: + rm $(BIN) $(OBJS) $(OBJS:.o=.lst) $(MAPFILE) + +%.o: %.s $(INCS) $(LCONFIG) + $(AS65) $(AFLAGS65) $< -o $@ + diff --git a/nes15-1.0.0/fifteen/fifteen.inc b/nes15-1.0.0/fifteen/fifteen.inc new file mode 100644 index 0000000..3bf7533 --- /dev/null +++ b/nes15-1.0.0/fifteen/fifteen.inc @@ -0,0 +1,104 @@ +; +; File: fifteen.inc +; Namespace: fifteen_ / FIFTEEN +; Code Segment: FIFTEENLIB +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Fifteen puzzle library +; + +.ifndef FIFTEEN_INC +FIFTEEN_INC = 1 + +; Dimensions of the puzzle board + +FIFTEEN_WIDTH = 4 +FIFTEEN_HEIGHT = 4 +FIFTEEN_SIZE = FIFTEEN_WIDTH * FIFTEEN_HEIGHT + + + +; Current state of the puzzle board. The gap is represented by zero and the +; tiles are represented by the values 1 to 15. + +.global fifteen_board + +; Horizontal position of the puzzle's gap + +.globalzp fifteen_gap_x + +; Vertical position of the puzzle's gap + +.globalzp fifteen_gap_y + + + +; +; Generates a new puzzle state. Note that the state will always be solvable. +; +; Destroyed: a, x, y +; +.global fifteen_gen + +; +; Tests if the puzzle is currently in a solvable state. Note that the puzzle is +; solvable if the parity of the permutation of the board is not equal to the +; parity of the gap's vertical position (perm_parity ^ (fifteen_gap_y & 1)). +; +; Out: +; C = Set if the puzzle is in an solvable state, else reset +; +; Destroyed: a, x, y +; +.global fifteen_solvable + +; +; Attempts to move a tile into the gap's position. +; +; In: +; a = The horizontal position of the tile to move +; y = The vertical position of the tile to move +; Out: +; Z = Reset if the move was invalid, else set if the move was performed +; +; Destroyed: a, x, y +; +.global fifteen_move + +; +; Tests if the puzzle has been solved. +; +; Out: +; Z = Reset if the puzzle is unsolved, else set +; +; Destroyed: a, y +; Preserved: x +; +.global fifteen_solved + +; +; Initializes the puzzle solver. This should be called once before using +; 'fifteen_solve' to obtain moves. If the puzzle state is altered by moves +; other than those provided by the solver, this should be called again. +; +; Destroyed: a, x, y +; +.global fifteen_init_solver + +; +; Returns the next move generated by the puzzle solver. If the puzzle is +; altered by moves other than those returned by this subroutine, then +; 'fifteen_init_solver' should be called again. Never call this subroutine when +; the puzzle is solved. +; +; Out: +; a = The horizontal position of the tile to move +; y = The vertical position of the tile to move +; +; Destroyed: x +; +.global fifteen_solve + +.endif + diff --git a/nes15-1.0.0/fifteen/fifteen.s b/nes15-1.0.0/fifteen/fifteen.s new file mode 100644 index 0000000..a35f08c --- /dev/null +++ b/nes15-1.0.0/fifteen/fifteen.s @@ -0,0 +1,1152 @@ +; +; File: fifteen.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Fifteen puzzle subroutines and data +; +; The solver used is based on documentation and source code by 'Karl Hornell' +; found at: +; http://www.javaonthebrain.com/java/puzz15/ +; + +.include "lfsr32.inc" + +.include "fifteen.inc" + + + +.segment "ZEROPAGE" + +fifteen_gap_x: .res 1 +fifteen_gap_y: .res 1 + +; Temporary storage used by the module + +temp: .res 6 + +; Current position of the gap in the solver's copy of the puzzle's state + +solver_gap: .res 1 + +; Current position of the next move in 'solver_moves' that will be returned +; when 'fifteen_solve' is called. This is also used by the move generator to +; keep track of the end of the moves list. + +solver_end: .res 1 + +; Current goal the solver is trying to accomplish + +solver_goal: .res 1 + + + +.segment "BSS" + +fifteen_board: .res FIFTEEN_SIZE + +; The solver's copy of the current state of the puzzle. Values greater than $7F +; are used to mark locked tiles that should not be moved by the solver. + +solver_board: .res FIFTEEN_SIZE + +; List of moves to be used by the solver to accomplish its current goal. The +; end of the list is marked with a value greater than $7F. Note the size is an +; arbitrary estimate. + +solver_moves: .res 64 + + + +.segment "FIFTEENLIB" + +.proc fifteen_gen + +divisor = temp + 1 +last_tiles = temp + 1 + + ; Initialize the board with the goal state randomly shuffled. + + lda fifteen_goal + sta fifteen_board + ldx #2 + +shuffle_loop: + ldy #4 + jsr lfsr32_next + lda lfsr32 + stx divisor + sec + +mod_loop: + sbc divisor + bcs mod_loop + adc divisor ; Carry is already cleared + tay + lda fifteen_board, y + sta fifteen_board - 1, x + lda fifteen_goal - 1, x + sta fifteen_board, y + inx + cpx #FIFTEEN_SIZE + 1 + bne shuffle_loop + + ; Find the location of the gap and save it. + + ldx #FIFTEEN_SIZE + +gap_loop: + dex + lda fifteen_board, x + bne gap_loop + txa + and #$03 + sta fifteen_gap_x + txa + lsr + lsr + sta fifteen_gap_y + + ; Find the last two tiles from the bottom-right corner backwards. + + ldx #FIFTEEN_SIZE + ldy #2 + +last_loop: + dex + lda fifteen_board, x + beq last_loop + stx last_tiles - 1, y + dey + bne last_loop + + ; If the puzzle is in an unsolvable state, reverse the last two tiles + ; on the board. This inverts the permutation's parity, thus leaving the + ; puzzle in a solvable state. + + jsr fifteen_solvable + bcs done + ldx last_tiles + lda fifteen_board, x + pha + ldy last_tiles + 1 + lda fifteen_board, y + sta fifteen_board, x + pla + sta fifteen_board, y + +done: + rts + +.endproc + + + +.proc fifteen_solvable + +perm_parity = temp + + ; Initialize the parity and check for inversions from the first tile + ; forward to the second to the last. + + ldx #0 + stx perm_parity + +outer: + + ; Count the inversions forward from the current tile. Note that we + ; don't count inversions from the puzzle's gap. + + lda fifteen_board, x + beq cont_outer + txa + tay + iny + +inner: + + ; If an inversion with a tile was found, invert the current parity. + + lda fifteen_board, y + beq cont_inner + cmp fifteen_board, x + bcs cont_inner + inc perm_parity + +cont_inner: + iny + cpy #FIFTEEN_SIZE + bne inner + +cont_outer: + inx + cpx #FIFTEEN_SIZE - 1 + bne outer + + ; Return solvability in the carry flag + ; (bit 0 of perm_parity ^ fifteen_gap_y). + + lda perm_parity + eor fifteen_gap_y + lsr + rts + +.endproc + + + +.proc fifteen_move + +tile_x = temp + + sta tile_x + + ; If the tile to move is lined up with the gap horizontally, then that + ; tile's vertical position must be directly adjacent to the gap. + + cmp fifteen_gap_x + bne test_ver + tya + sbc fifteen_gap_y ; Carry is already set + cmp #1 + beq valid + cmp #-1 + beq valid + rts + + ; Else, if the tile to move is lined up with the gap vertically, then + ; that tile's horizontal position must be directly adjacent to the gap. + +test_ver: + cpy fifteen_gap_y + bne done + sbc fifteen_gap_x ; Carry is already set + cmp #1 + beq valid + cmp #-1 + beq valid + rts + + ; If the move is valid, swap the tile with the gap and set the gap's + ; new position. + +valid: + tya + asl + asl + adc tile_x ; Carry is already cleared + tax + lda fifteen_gap_y + sty fifteen_gap_y + asl + asl + adc fifteen_gap_x ; Carry is already cleared + tay + lda tile_x + sta fifteen_gap_x + lda fifteen_board, x + sta fifteen_board, y + lda #0 ; Done last to ensure Z = 0 + sta fifteen_board, x + +done: + rts + +.endproc + + + +.proc fifteen_solved + + ldy #FIFTEEN_SIZE + +loop: + lda fifteen_board - 1, y + cmp fifteen_goal - 1, y + bne done + dey + bne loop + +done: + rts + +.endproc + + + +.proc fifteen_init_solver + +lock_pos = temp + + ; Copy the puzzle state to the solver's puzzle state copy. + + ldx #FIFTEEN_SIZE + +copy_loop: + lda fifteen_board - 1, x + sta solver_board - 1, x + dex + bne copy_loop + + ; Set the solver's current position of the gap. + + lda fifteen_gap_y + asl + asl + ora fifteen_gap_x + sta solver_gap + + ; Find the goal the solver should start from, keeping track of the last + ; position read from in 'goal_table'. + + ldx #0 + stx lock_pos + stx solver_goal + +goal_loop: + ldy goal_table, x + lda solver_board - 1, y + cmp goal_table, x + bne lock_tiles + inx + lda goal_table, x + bne goal_loop + inc solver_goal + stx lock_pos ; Save current position for lock marking + inx + bne goal_loop + + ; Mark any tiles that are in their destination positions as locked. + +lock_tiles: + ldx lock_pos + +lock_loop: + dex + bmi done + ldy goal_table, x + beq lock_loop + lda #$FF + sta solver_board - 1, y + bne lock_loop + + ; Finally, build the list of moves for the first goal that needs to be + ; accomplished and return. + +done: + jmp build_moves + +; List of all tiles needed by each of the solver's goals + +goal_table: + .byte 1, 0 + .byte 2, 0 + .byte 3, 4, 0 + .byte 5, 0 + .byte 6, 0 + .byte 7, 8, 0 + .byte 9, 13, 0 + .byte 10, 14, 0 + .byte 11, 12, 15, 0 + +.endproc + + + +.proc fifteen_solve + + ; If another move is waiting on the moves list, then return it. + + ldx solver_end + lda solver_moves, x + bmi next_goal + inc solver_end + tax + lsr + lsr + tay + txa + and #$03 + rts + + ; Else, reinitialize the solver for the next goal and try again. Note + ; that 'fifteen_init_solver' is used to skip over any new goals that + ; might already be accomplished. + +next_goal: + jsr fifteen_init_solver + jmp fifteen_solve + +.endproc + + + +; +; Fills 'solver_moves' with the moves needed to accomplish the solver's current +; goal. +; +; Out: +; solver_end = 0 +; +; Destroyed: a, x, y +; +.proc build_moves + + lda #0 + sta solver_end + jsr build + ldx solver_end + lda #0 + sta solver_end + lda #$FF + sta solver_moves, x + rts + +build: + lda solver_goal + asl + tax + lda build_table + 1, x + pha + lda build_table, x + pha + rts + +build_table: + .addr build_1 - 1 + .addr build_2 - 1 + .addr build_3_4 - 1 + .addr build_5 - 1 + .addr build_6 - 1 + .addr build_7_8 - 1 + .addr build_9_13 - 1 + .addr build_10_14 - 1 + .addr build_15 - 1 + +; +; Builds the moves needed to move a tile to a specified destination, avoiding +; any locked tiles. After the tile is placed at its destination it is locked +; into place. Note that this should not be called to move a locked tile. +; +; In: +; a = The tile to move +; y = The position to move the tile to +; Out: +; solver_end = The position ahead of the last move in the list +; +; Destroyed: a, x, y, temp.../+6 +; +.proc move_tile + +; Current position of the tile being moved + +tile_pos = temp + 1 + +; Position the tile should be moved to + +tile_goal = temp + 2 + +; Position the gap should be moved to + +gap_goal = temp + 3 + +; Counters used to hold the weights of the gap's path clockwise and counter- +; clockwise around the tile being moved. + +cw_weight = temp + 4 +ccw_weight = temp + 5 + + sty tile_goal + + ; Find the position of the tile to be moved. + + ldx #$FF + +find_loop: + inx + cmp solver_board, x + bne find_loop + stx tile_pos + lda #$FF ; Lock the tile to be moved + sta solver_board, x + +move_loop: + + ; If the current tile has reached its goal, then return. + + lda tile_pos + cmp tile_goal + bne test_goal + rts + + ; Determine the position the gap should move toward. + +test_goal: + lda tile_pos ; Reset the gap's goal + sta gap_goal + lda tile_goal ; Test if the gap's goal should be moved + and #$03 ; horizontally + sta temp + lda tile_pos + and #$03 + cmp temp + bcs test_goal_left ; If the tile is to the right, test left + inc gap_goal ; Else, move the goal right + bne move_gap_loop + +test_goal_left: + beq test_goal_y ; If lined up, test for vertical movement + dec gap_goal ; Else, move the goal left + bpl move_gap_loop + +test_goal_y: + lda tile_goal + lsr + lsr + sta temp + lda tile_pos + lsr + lsr + cmp temp + bcs move_goal_up ; If the tile is above, move the goal up + lda gap_goal ; Else, move the goal down + adc #4 ; Carry is already cleared + sta gap_goal + bne move_gap_loop + +move_goal_up: + lda gap_goal + sbc #4 ; Carry is already set + sta gap_goal + + ; Move the gap until it is orthogonally or diagonally adjacent to the + ; tile to be moved. + +move_gap_loop: + + ; If the gap is adjacent to the tile to be moved, then continue on to + ; test for rotational movement. + + lda tile_pos ; Find the displacement from the tile + sec + sbc solver_gap + tay + ldx #8 + +gap_adj_loop: + tya ; If the displacement is listed and is not out + cmp round_disp - 1, x ; of bounds, then the gap is adjacent + bne gap_adj_cont + lda solver_gap + and #$03 + clc + adc round_dx - 1, x + cmp #4 + bcc rotate_gap + +gap_adj_cont: + dex ; Else, continue backwards in the table + bne gap_adj_loop + + ; Else, move the gap closer to the current tile. + + lda gap_goal ; Test for horizontal movement + and #$03 + sta temp + lda solver_gap + and #$03 + cmp temp + bcc move_gap_right ; If the gap is to left, try right + beq test_gap_y ; Else, if lined up, test for vertical movement + ldx solver_gap ; Else, try to move the gap left + dex + lda solver_board, x + bpl move_gap ; If not blocked by a locked tile, move left + bmi test_gap_y ; Else, test for vertical movement + +move_gap_right: + ldx solver_gap + inx + lda solver_board, x + bpl move_gap ; If not blocked by a locked tile, move right + +test_gap_y: + lda solver_gap ; Test for vertical movement + lsr + lsr + sta temp + lda gap_goal + lsr + lsr + cmp temp + bcs move_gap_down ; If the gap is above, move down + lda solver_gap ; Else, try to move up + sbc #3 ; Carry is already cleared + tax + lda solver_board, x + bpl move_gap ; If not blocked by a locked tile, move up + +move_gap_down: + lda solver_gap ; Else, just move down + adc #3 ; Carry is already set + tax + + ; Exchange the gap with the tile at its goal position and continue. + +move_gap: + ldy solver_end + txa + sta solver_moves, y + inc solver_end + ldy solver_gap + lda solver_board, x + sta solver_board, y + lda #0 + sta solver_board, x + stx solver_gap + beq move_gap_loop + + ; Move the gap along the shortest path either clockwise or counter- + ; clockwise around the tile to be moved. + +rotate_gap: + lda solver_gap ; If the gap is already at its goal, skip past + cmp gap_goal ; rotational movement + bne init_weights + jmp cont_move + +init_weights: + lda #0 ; Reset the path weight counters + sta cw_weight + sta ccw_weight + + ; Find the gap's current displacement from the current tile starting in + ; the middle of the displacement table. + + ldx #8 + +find_disp_loop: + lda tile_pos + clc + adc round_disp, x + cmp solver_gap + beq test_cw + inx + bne find_disp_loop + + ; Test the clockwise path around the current tile. + +test_cw: + stx temp + +cw_loop: + lda tile_pos ; If the current position is over the goal, + clc ; then the path if fully tested + adc round_disp, x + cmp gap_goal + beq test_ccw + inx + cmp #FIFTEEN_SIZE ; If out of bounds, add highest value + bcs cw_blocked + tay + lda tile_pos + and #$03 + adc round_dx, x ; Carry is already clear + cmp #4 + bcs cw_blocked + lda solver_board, y ; If blocked by locked tile, add highest value + bmi cw_blocked + inc cw_weight ; Else, add the lowest value to the weight for + bne cw_loop ; this path, making it more desirable + +cw_blocked: + lda cw_weight + clc + adc #8 + sta cw_weight + bne cw_loop + + ; Test the counter-clockwise path around the current tile. + +test_ccw: + ldx temp + +ccw_loop: + lda tile_pos ; If the current position is over the goal, + clc ; then the path if fully tested + adc round_disp, x + cmp gap_goal + beq rotate_loop + dex + cmp #FIFTEEN_SIZE ; If out of bounds, add highest value + bcs ccw_blocked + tay + lda tile_pos + and #$03 + adc round_dx, x ; Carry is already clear + cmp #4 + bcs ccw_blocked + lda solver_board, y ; If blocked by locked tile, add highest value + bmi ccw_blocked + inc ccw_weight ; Else, add the lowest value to the weight for + bne ccw_loop ; this path, making it more desirable + +ccw_blocked: + lda ccw_weight + clc + adc #8 + sta ccw_weight + bne ccw_loop + + ; Move the gap along the shortest path found. + +rotate_loop: + lda cw_weight + cmp ccw_weight + bcc rotate_cw + dec temp + bpl cont_rotate + +rotate_cw: + inc temp + +cont_rotate: + ldx temp + lda tile_pos + clc + adc round_disp, x + ldy solver_end ; Save the move to the moves list + sta solver_moves, y + inc solver_end + tay + ldx solver_gap ; Exchange the gap with the tile to be moved + lda solver_board, y + sta solver_board, x + lda #0 + sta solver_board, y + sty solver_gap + cpy gap_goal ; Repeat until the gap has reached its goal + bne rotate_loop + + ; Add the final move to the list and continue moving. + +cont_move: + ldy solver_end + lda tile_pos + sta solver_moves, y + inc solver_end + tay + lda #0 + sta solver_board, y + lda #$FF + ldx gap_goal + sta solver_board, x + stx tile_pos + sty solver_gap + jmp move_loop + +; Rotational displacement lookup table stored from the top-left going forward +; clockwise + +round_disp: + .byte -4, -3, 1, 5, 4, 3, -1, -5 + .byte -4, -3, 1, 5, 4, 3, -1, -5 + .byte -4, -3, 1, 5, 4, 3, -1, -5 + .byte -4 + +; Horizontal change lookup table stored from the top-left going forward +; clockwise + +round_dx: + .byte 0, 1, 1, 1, 0, -1, -1, -1 + .byte 0, 1, 1, 1, 0, -1, -1, -1 + .byte 0, 1, 1, 1, 0, -1, -1, -1 + .byte 0 + +.endproc + +; +; Copies a predefined list of moves from memory into 'solver_moves', adjusting +; the solver's copy of the puzzle state as needed. The list provided should be +; followed by a value greater than $7F. +; +; In: +; a = The low byte of the address of the move list +; y = The high byte of the address of the move list +; +; Out: +; solver_end = The position ahead of the last move in the list +; +; Destroyed: a, x, y, temp/+1 +; +.proc read_moves + +addr = temp + + sta addr + sty addr + 1 + +loop: + ldy #0 ; Read moves until a terminating value is found + lda (addr), y + bmi done + tax ; Exchange the tile to move with the gap + ldy solver_gap + lda solver_board, x + sta solver_board, y + lda #0 + sta solver_board, x + stx solver_gap + ldy solver_end ; Save the move to the moves list + txa + sta solver_moves, y + inc solver_end + inc addr ; Advance to the next byte and continue + bne loop + inc addr + 1 + bne loop + +done: + rts + +.endproc + + + +; +; Move building subroutines for each solver goal. +; +.proc build_1 + + lda #1 + ldy #0 + jmp move_tile + +.endproc + +.proc build_2 + + lda #2 + ldy #1 + jmp move_tile + +.endproc + +.proc build_3_4 + + lda #3 + ldy #3 + jsr move_tile + + ; Corner case #1: + ; x x 4 3 + ; x x x x + ; x x x x + ; x x x x + + lda solver_board + 2 + beq test_case2 + cmp #4 + bne move4 + ldy #6 + jsr move_tile + jmp case2 + + ; Corner case #2: + ; x x 0 3 + ; x x 4 x + ; x x x x + ; x x x x + +test_case2: + lda solver_board + 6 + cmp #4 + bne move4 + +case2: + lda #moves0 + jsr read_moves + jmp finish + +move4: + lda #4 + ldy #7 + jsr move_tile + +finish: + lda #$FF ; Lock tile 4 + sta solver_board + 7 + lda #3 ; Unlock and move tile 3 + sta solver_board + 3 + ldy #2 + jsr move_tile + lda #4 ; Unlock and move tile 4 + sta solver_board + 7 + ldy #3 + jmp move_tile + +moves0: + .byte 3, 7, 11, 10, 6, 7, 11, 10, 6, 7, 3, 2, 6, 7, 11, $FF + +.endproc + +.proc build_5 + + lda #5 + ldy #4 + jmp move_tile + +.endproc + +.proc build_6 + + lda #6 + ldy #5 + jmp move_tile + +.endproc + +.proc build_7_8 + + lda #7 + ldy #7 + jsr move_tile + + ; Corner case #1: + ; x x x x + ; x x 8 7 + ; x x x x + ; x x x x + + lda solver_board + 6 + beq test_case2 + cmp #8 + bne move8 + ldy #10 + jsr move_tile + jmp case2 + + ; Corner case #2: + ; x x x x + ; x x 0 7 + ; x x 8 x + ; x x x x + +test_case2: + lda solver_board + 10 + cmp #8 + bne move8 + +case2: + lda #moves0 + jsr read_moves + jmp finish + +move8: + lda #8 + ldy #11 + jsr move_tile + +finish: + lda #$FF ; Lock tile 8 + sta solver_board + 11 + lda #7 ; Unlock and move tile 7 + sta solver_board + 7 + ldy #6 + jsr move_tile + lda #8 ; Unlock and move tile 8 + sta solver_board + 11 + ldy #7 + jmp move_tile + +moves0: + .byte 7, 11, 15, 14, 10, 11, 15, 14, 10, 11, 7, 6, 10, 11, 15, $FF + +.endproc + +.proc build_9_13 + + lda #13 + ldy #8 + jsr move_tile + + ; Corner case #1: + ; x x x x + ; x x x x + ; 13 x x x + ; 9 x x x + + lda solver_board + 12 + beq test_case2 + cmp #9 + bne move9 + ldy #13 + jsr move_tile + jmp case2 + + ; Corner case #2: + ; x x x x + ; x x x x + ; 13 x x x + ; 0 9 x x + +test_case2: + lda solver_board + 13 + cmp #9 + bne move9 + +case2: + lda #moves0 + jsr read_moves + jmp finish + +move9: + lda #9 + ldy #9 + jsr move_tile + +finish: + lda #$FF ; Lock tile 9 + sta solver_board + 9 + lda #13 ; Unlock and move tile 13 + sta solver_board + 8 + ldy #12 + jsr move_tile + lda #9 ; Unlock and move tile 9 + sta solver_board + 9 + ldy #8 + jmp move_tile + +moves0: + .byte 8, 9, 10, 14, 13, 9, 10, 14, 13, 9, 8, 12, 13, 9, 10, $FF + +.endproc + +.proc build_10_14 + + lda #14 + ldy #9 + jsr move_tile + + ; Corner case #1: + ; x x x x + ; x x x x + ; x 14 x x + ; x 10 x x + + lda solver_board + 13 + beq test_case2 + cmp #10 + bne move10 + ldy #14 + jsr move_tile + jmp case2 + + ; Corner case #2: + ; x x x x + ; x x x x + ; x 14 x x + ; x 0 10 x + +test_case2: + lda solver_board + 14 + cmp #10 + bne move10 + +case2: + lda #moves0 + jsr read_moves + jmp finish + +move10: + lda #10 + tay + jsr move_tile + +finish: + lda #$FF ; Lock tile 10 + sta solver_board + 10 + lda #14 ; Unlock and move tile 14 + sta solver_board + 9 + ldy #13 + jsr move_tile + lda #10 ; Unlock and move tile 10 + sta solver_board + 10 + ldy #9 + jmp move_tile + +moves0: + .byte 9, 10, 11, 15, 14, 10, 11, 15, 14, 10, 9, 13, 14, 10, 11, $FF + +.endproc + +.proc build_15 + + ; Rotate the last tiles counter-clockwise until the puzzle is solved. + +loop: + lda solver_board + 15 + bne test11 + lda solver_board + 14 + cmp #15 + beq done + lda #moves0 + jsr read_moves + jmp loop + +test11: + lda solver_board + 11 + bne test10 + lda #moves1 + jsr read_moves + jmp loop + +test10: + lda solver_board + 10 + bne move15 + lda #moves2 + jsr read_moves + jmp loop + +move15: + lda #moves3 + jsr read_moves + jmp loop + +done: + rts + +moves0: + .byte 11 + +moves1: + .byte 10 + +moves2: + .byte 14 + +moves3: + .byte 15, $FF + +.endproc + +.endproc + + + +; Goal state of the board + +fifteen_goal: + .byte 1, 2, 3, 4 + .byte 5, 6, 7, 8 + .byte 9, 10, 11, 12 + .byte 13, 14, 15, 0 + diff --git a/nes15-1.0.0/fifteen/test.cfg b/nes15-1.0.0/fifteen/test.cfg new file mode 100644 index 0000000..9159e65 --- /dev/null +++ b/nes15-1.0.0/fifteen/test.cfg @@ -0,0 +1,19 @@ +# +# 'fifteen' test linker configuration +# + +MEMORY { + ZP: start = $00, size = $100, type = rw; + RAM: start = $200, size = $600, type = rw; + ROM: start = $8000, size = $8000, type = ro, file %O, fill = yes; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + BSS: load = RAM, type = bss; + CODE: load = ROM, type = ro; + LFSRLIB: load = ROM, type = ro; + FIFTEENLIB: load = ROM, type = ro; + VECTORS: load = ROM, start = $FFFA, type = ro; +} + diff --git a/nes15-1.0.0/fifteen/test.s b/nes15-1.0.0/fifteen/test.s new file mode 100644 index 0000000..1f45f85 --- /dev/null +++ b/nes15-1.0.0/fifteen/test.s @@ -0,0 +1,98 @@ +; +; File: test.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Test program for the fifteen puzzle library +; + +.include "lfsr32.inc" +.include "fifteen.inc" + + + +NUM_TESTS = 10000 + + + +.segment "ZEROPAGE" + +failed: .res 1 +count: .res 2 + + + +.segment "CODE" + +; +; Reset routine used to test the fifteen puzzle library. If all tests were +; successful, then 'failed' will be set to zero, else 'failed' will be set to +; one. +; +.proc reset + + cld + ldx #$FF + txs + lda #1 + sta failed + lda #<($10000 - NUM_TESTS) + sta count + lda #>($10000 - NUM_TESTS) + sta count + 1 + + lda #53 + sta lfsr32 + sta lfsr32 + 1 + sta lfsr32 + 2 + sta lfsr32 + 3 + +loop: + ; Ensure the puzzle generator is providing solvable puzzles. + + jsr fifteen_gen + jsr fifteen_solvable + bcc done + + ; Test the puzzle solver. + + jsr fifteen_init_solver + +solve_loop: + jsr fifteen_solved + beq next + jsr fifteen_solve + jsr fifteen_move + jmp solve_loop + +next: + inc count + bne loop + inc count + 1 + bne loop + dec failed + +done: + jmp done + +.endproc + + + +; +; Dummy NMI and IRQ routines +; +; Preserved: a, x, y +; +nmi: +irq: + rti + + + +.segment "VECTORS" + + .addr nmi + .addr reset + .addr irq + diff --git a/nes15-1.0.0/game.cfg b/nes15-1.0.0/game.cfg new file mode 100644 index 0000000..abff2a7 --- /dev/null +++ b/nes15-1.0.0/game.cfg @@ -0,0 +1,35 @@ +# +# 'nes15' linker configuration +# + +MEMORY { + HEADER: start = $7FF0, size = $0010, type = ro, file = "header.bin", + fill = yes; + ZP: start = $00, size = $100, type = rw; + RAM: start = $300, size = $500, type = rw; + PGRROM: start = $C000, size = $4000, type = ro, file = "game.bin", + fill = yes; + CHRROM1: start = $10000, size = $1000, type = ro, file = "chr1.chr", + fill = yes; + CHRROM2: start = $11000, size = $2000, type = ro, file = "chr2.chr", + fill = yes; +} + +SEGMENTS { + INESHDR: load = HEADER, type = ro; + ZEROPAGE: load = ZP, type = zp; + BSS: load = RAM, type = bss; + CODE: load = PGRROM, type = ro; + BCDLIB: load = PGRROM, type = ro; + FIFTEENLIB: load = PGRROM, type = ro; + JOYLIB: load = PGRROM, type = ro; + LFSRLIB: load = PGRROM, type = ro; + MUSELIB: load = PGRROM, type = ro; + OAMLIB: load = PGRROM, type = ro; + PKBLIB: load = PGRROM, type = ro; + VRUBLIB: load = PGRROM, type = ro; + VECTORS: load = PGRROM, start = $FFFA, type = ro; + CHR1: load = CHRROM1, type = ro, optional = yes; + CHR2: load = CHRROM2, type = ro, optional = yes; +} + diff --git a/nes15-1.0.0/gfx/bgd.pal b/nes15-1.0.0/gfx/bgd.pal new file mode 100644 index 0000000..5de6df4 --- /dev/null +++ b/nes15-1.0.0/gfx/bgd.pal @@ -0,0 +1 @@ +(8 \ No newline at end of file diff --git a/nes15-1.0.0/gfx/cursor.pcx b/nes15-1.0.0/gfx/cursor.pcx new file mode 100644 index 0000000..fc0601b Binary files /dev/null and b/nes15-1.0.0/gfx/cursor.pcx differ diff --git a/nes15-1.0.0/gfx/edges.pcx b/nes15-1.0.0/gfx/edges.pcx new file mode 100644 index 0000000..638304f Binary files /dev/null and b/nes15-1.0.0/gfx/edges.pcx differ diff --git a/nes15-1.0.0/gfx/font.pcx b/nes15-1.0.0/gfx/font.pcx new file mode 100644 index 0000000..390424a Binary files /dev/null and b/nes15-1.0.0/gfx/font.pcx differ diff --git a/nes15-1.0.0/gfx/nums.pcx b/nes15-1.0.0/gfx/nums.pcx new file mode 100644 index 0000000..f0e43b1 Binary files /dev/null and b/nes15-1.0.0/gfx/nums.pcx differ diff --git a/nes15-1.0.0/gfx/play.nam b/nes15-1.0.0/gfx/play.nam new file mode 100644 index 0000000..4c16eaf Binary files /dev/null and b/nes15-1.0.0/gfx/play.nam differ diff --git a/nes15-1.0.0/gfx/spr.pal b/nes15-1.0.0/gfx/spr.pal new file mode 100644 index 0000000..1196f29 Binary files /dev/null and b/nes15-1.0.0/gfx/spr.pal differ diff --git a/nes15-1.0.0/gfx/tile.pcx b/nes15-1.0.0/gfx/tile.pcx new file mode 100644 index 0000000..f75706c Binary files /dev/null and b/nes15-1.0.0/gfx/tile.pcx differ diff --git a/nes15-1.0.0/gfx/title.nam b/nes15-1.0.0/gfx/title.nam new file mode 100644 index 0000000..a68fe9c Binary files /dev/null and b/nes15-1.0.0/gfx/title.nam differ diff --git a/nes15-1.0.0/nes-lib/apu.inc b/nes15-1.0.0/nes-lib/apu.inc new file mode 100644 index 0000000..3408e73 --- /dev/null +++ b/nes15-1.0.0/nes-lib/apu.inc @@ -0,0 +1,37 @@ +; +; File: apu.inc +; Namespace: apu_ / APU_ +; Code Segment: APULIB +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; General purpose APU library +; + +.ifndef APU_INC +APU_INC = 1 + +; APU I/O locations + +SQ1_VOL = $4000 +SQ1_SWEEP = $4001 +SQ1_LO = $4002 +SQ1_HI = $4003 +SQ2_VOL = $4004 +SQ2_SWEEP = $4005 +SQ2_LO = $4006 +SQ2_HI = $4007 +TRI_LINEAR = $4008 +TRI_LO = $400A +TRI_HI = $400B +NOISE_VOL = $400C +NOISE_LO = $400E +NOISE_HI = $400F +DMC_FREQ = $4010 +DMC_RAW = $4011 +DMC_START = $4012 +DMC_LEN = $4013 +SND_CHN = $4015 + +.endif + diff --git a/nes15-1.0.0/nes-lib/bcd.inc b/nes15-1.0.0/nes-lib/bcd.inc new file mode 100644 index 0000000..85df8d9 --- /dev/null +++ b/nes15-1.0.0/nes-lib/bcd.inc @@ -0,0 +1,109 @@ +; +; File: bcd.inc +; Namespace: bcd_ / BCD_ +; Code Segment: BCDLIB +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Binary-coded decimal library +; + +.ifndef BCD_INC +BCD_INC = 1 + +; Output binary-coded decimal number and temporary storage + +.global bcd_num + +; Output binary value and temporary storage + +.global bcd_bin + + + +; +; Converts an 8-bit binary value to BCD format. +; +; In: +; a = The 8-bit binary value to convert +; Out: +; a = The 8-bit BCD result +; +; Preserved: x +; Destroyed: a, y, bcd_num +; +.global bcd8_from_bin + +; +; Converts an 8-bit BCD number to binary format. +; +; In: +; a = The 8-bit BCD number to convert +; Out: +; a = The 8-bit binary result +; +; Preserved: x +; Destroyed: a, y, bcd_bin +; +.global bcd8_to_bin + +; +; Sets up a VRUB update to draw an 8-bit BCD number to the screen. Note that +; ASCII digit values are used and leading zeros are replaced with whitespace. +; +; In: +; a = The low byte of the address to draw to +; y = The high byte of the address to draw to +; bcd_num = The 8-bit BCD number to draw +; +; Preserved: y, bcd_num +; Destroyed: a, x +; +.global bcd8_to_vrub + +; +; Converts a 16-bit binary value to BCD format. +; +; In: +; a = The low byte of the 16-bit binary value to convert +; y = The high byte of the 16-bit binary value to convert +; Out: +; a, bcd_num = The low byte of the 16-bit BCD result +; y, bcd_num + 1 = The high byte of the 16-bit BCD result +; +; Preserved: x +; Destroyed: a, y, bcd_bin/+1 +; +.global bcd16_from_bin + +; +; Converts a 16-bit BCD number to binary format. +; +; In: +; a = The low byte of the 16-bit BCD number to convert +; y = The high byte of the 16-bit BCD number to convert +; Out: +; a, bcd_bin = The low byte of the 16-bit binary result +; y, bcd_bin + 1 = The high byte of the 16-bit binary result +; +; Preserved: x +; Destroyed: a, y, bcd_num/+1 +; +.global bcd16_to_bin + +; +; Sets up a VRUB update to draw a 16-bit BCD number to the screen. Note that +; ASCII digit values are used and leading zeros are replaced with whitespace. +; +; In: +; a = The low byte of the address to draw to +; y = The high byte of the address to draw to +; bcd_num/+1 = The 16-bit BCD number to draw +; +; Preserved: bcd_num/+1 +; Destroyed: a, x, y +; +.global bcd16_to_vrub + +.endif + diff --git a/nes15-1.0.0/nes-lib/bcd16.s b/nes15-1.0.0/nes-lib/bcd16.s new file mode 100644 index 0000000..5832a79 --- /dev/null +++ b/nes15-1.0.0/nes-lib/bcd16.s @@ -0,0 +1,35 @@ +; +; File: bcd16.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Common 16-bit binary-coded decimal subroutines and data +; + +.include "bcd.inc" + + + +.segment "BSS" + +bcd_num: .res 2 +bcd_bin: .res 2 + + + +.segment "BCDLIB" + +.export bcd_table_lo +bcd_table_lo: + .byte <1, <2, <4, <8 + .byte <10, <20, <40, <80 + .byte <100, <200, <400, <800 + .byte <1000, <2000, <4000, <8000 + +.export bcd_table_hi +bcd_table_hi: + .byte >1, >2, >4, >8 + .byte >10, >20, >40, >80 + .byte >100, >200, >400, >800 + .byte >1000, >2000, >4000, >8000 + diff --git a/nes15-1.0.0/nes-lib/bcd16_from_bin.s b/nes15-1.0.0/nes-lib/bcd16_from_bin.s new file mode 100644 index 0000000..333fbfe --- /dev/null +++ b/nes15-1.0.0/nes-lib/bcd16_from_bin.s @@ -0,0 +1,57 @@ +; +; File: bcd16_from_bin.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; 'bcd16_from_bin' subroutine +; +; The technique used was adapted from source code by 'Damian Yerrick' found at: +; http://wiki.nesdev.com/w/index.php/16-bit_BCD +; + +.include "bcd.inc" + + + +.import bcd_table_lo +.import bcd_table_hi + + + +.segment "ZEROPAGE" + +temp: .res 1 + + + +.segment "BCDLIB" + +.proc bcd16_from_bin + + sta bcd_bin + sty bcd_bin + 1 + ldy #16 + +loop: + lda bcd_bin + sec + sbc bcd_table_lo - 1, y + sta temp + lda bcd_bin + 1 + sbc bcd_table_hi - 1, y + bcc lower + sta bcd_bin + 1 + lda temp + sta bcd_bin + +lower: + rol bcd_num + rol bcd_num + 1 + dey + bne loop + lda bcd_num + ldy bcd_num + 1 + rts + +.endproc + diff --git a/nes15-1.0.0/nes-lib/bcd16_to_vrub.s b/nes15-1.0.0/nes-lib/bcd16_to_vrub.s new file mode 100644 index 0000000..204976e --- /dev/null +++ b/nes15-1.0.0/nes-lib/bcd16_to_vrub.s @@ -0,0 +1,80 @@ +; +; File: bcd16_to_vrub.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; 'bcd16_to_vrub' subroutine +; + +.include "bcd.inc" + +.include "vrub.inc" + + + +.segment "BCDLIB" + +.proc bcd16_to_vrub + + ; Setup the VRUB update to draw number. + + ldx vrub_end + sta vrub_buff + VRUB_DSTLO, x + tya + sta vrub_buff + VRUB_DSTHI, x + lda #$80 + sta vrub_buff + VRUB_CTRL, x + lda #4 + sta vrub_buff + VRUB_LEN, x + inx + inx + inx + inx + + ; Write the digits to be drawn. + + ldy #1 + +digit_loop: + lda bcd_num, y + and #$F0 + lsr + lsr + lsr + lsr + ora #'0' + sta vrub_buff, x + inx + lda bcd_num, y + and #$0F + ora #'0' + sta vrub_buff, x + inx + dey + bpl digit_loop + + ; Finish the VRUB update. + + lda #$00 + sta vrub_buff, x + stx vrub_end + + ; Remove any leading zeros from the number to be drawn. + + ldy #3 + +zero_loop: + lda vrub_buff - 4, x + cmp #'0' + bne done + lda #' ' + sta vrub_buff - 4, x + inx + dey + bne zero_loop + +done: + rts + +.endproc + diff --git a/nes15-1.0.0/nes-lib/joy.inc b/nes15-1.0.0/nes-lib/joy.inc new file mode 100644 index 0000000..6f74855 --- /dev/null +++ b/nes15-1.0.0/nes-lib/joy.inc @@ -0,0 +1,116 @@ +; +; File: joy.inc +; Namespace: joy_ / JOY_ +; Code Segment: JOYLIB +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Joypad reading and handling library +; + +.ifndef JOY_INC +JOY_INC = 1 + +; Joypad I/O locations + +JOY1 = $4016 +JOY2 = $4017 + +; Number of frames before the joypad button repeat starts + +.globalzp JOY_REP_DELAY + +; Interval of frames between joypad button repeats + +.globalzp JOY_REP_SPEED + + + +; State of the last joypad read + +.global joy_state + +; State of which buttons are being held down + +.global joy_held + +; State of which buttons where just pressed + +.global joy_pressed + +; State of which buttons where just released + +.global joy_released + +; State fed back into 'joy_pressed' on button repeats + +.global joy_repeat + +; Timer used for joypad button repeat + +.global joy_timer + + + +; +; Resets the held, pressed, and released states for JOY1. +; +; Out: +; a = 0 +; +; Preserved: x, y +; Destroyed: a +; +.macro joy_init1 + + lda #$00 + sta joy_held + sta joy_pressed + sta joy_released + +.endmacro + +; +; Strobes and reads the state of JOY1. Note that the method used is unreliable +; if the DMC is playing. +; +; Out: +; a, joy_state = The 8-bit state of the joypad read. The button state +; from bit 7 to bit 0 is: +; +; Right, Left, Down, Up, Start, Select, B, A +; +; Preserved: x, y +; +.global joy_read1 + +; +; Reads the state of JOY1 using 'joy_read1' and updates the states of the +; buttons held, pressed, and released. +; +; Out: +; joy_held = The buttons currently being held down +; joy_pressed = The buttons just pressed +; joy_release = The buttons just released +; +; The state of each button is stored in the same order as 'joy_state'. +; +; Preserved: x +; Destroyed: a, y +; +.global joy_update1 + +; +; Updates the button repeat timer and the state of the buttons just pressed for +; JOY1. This should be called once per frame after calling 'joy_update1'. +; +; Out: +; joy_pressed = The buttons just pressed and repeated +; +; Preserved: x, y +; Destroyed: a +; +.global joy_repeat1 + +.endif + diff --git a/nes15-1.0.0/nes-lib/joy_read1.s b/nes15-1.0.0/nes-lib/joy_read1.s new file mode 100644 index 0000000..24b16c6 --- /dev/null +++ b/nes15-1.0.0/nes-lib/joy_read1.s @@ -0,0 +1,40 @@ +; +; File: joy_read1.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; 'joy_read1' subroutine +; + +.include "joy.inc" + + + +.segment "BSS" + +joy_state: .res 1 + + + +.segment "JOYLIB" + +.proc joy_read1 + + lda #$01 + sta JOY1 + lsr + sta JOY1 + ror + sta joy_state + +loop: + lda JOY1 + and #$03 + cmp #$01 + ror joy_state + bcc loop + lda joy_state + rts + +.endproc + diff --git a/nes15-1.0.0/nes-lib/joy_repeat1.s b/nes15-1.0.0/nes-lib/joy_repeat1.s new file mode 100644 index 0000000..e796240 --- /dev/null +++ b/nes15-1.0.0/nes-lib/joy_repeat1.s @@ -0,0 +1,51 @@ +; +; File: joy_update1.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; 'joy_repeat1' subroutine +; +; Adapted from the source code to 'Concentration Room' by 'Damian Yerrick' +; found at: +; http://www.pineight.com/croom/ +; + +.include "joy.inc" + + + +.segment "BSS" + +joy_repeat: .res 1 +joy_timer: .res 1 + + + +.segment "JOYLIB" + +.proc joy_repeat1 + + lda joy_held + beq done + lda joy_pressed + beq cont + sta joy_repeat + lda #JOY_REP_DELAY + sta joy_timer + bne done + +cont: + dec joy_timer + bne done + lda #JOY_REP_SPEED + sta joy_timer + lda joy_repeat + and joy_held + ora joy_pressed + sta joy_pressed + +done: + rts + +.endproc + diff --git a/nes15-1.0.0/nes-lib/joy_update1.s b/nes15-1.0.0/nes-lib/joy_update1.s new file mode 100644 index 0000000..b9b1996 --- /dev/null +++ b/nes15-1.0.0/nes-lib/joy_update1.s @@ -0,0 +1,42 @@ +; +; File: joy_update1.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; 'joy_update1' subroutine +; +; Adapted from the source code by 'blargg' posted in the thread 'Button +; Handling Headaches' at: +; http://nesdev.parodius.com/bbs/ +; + +.include "joy.inc" + + + +.segment "BSS" + +joy_held: .res 1 +joy_pressed: .res 1 +joy_released: .res 1 + + + +.segment "JOYLIB" + +.proc joy_update1 + + jsr joy_read1 + tay + ora joy_held + eor joy_held + sta joy_pressed + tya + eor joy_held + and joy_held + sta joy_released + sty joy_held + rts + +.endproc + diff --git a/nes15-1.0.0/nes-lib/lfsr32.inc b/nes15-1.0.0/nes-lib/lfsr32.inc new file mode 100644 index 0000000..2e84925 --- /dev/null +++ b/nes15-1.0.0/nes-lib/lfsr32.inc @@ -0,0 +1,33 @@ +; +; File: lfsr32.inc +; Namespace: lfsr_ / LFSR_ +; Code Segment: LFSRLIB +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; 32-bit Galois Linear Feedback Shift Register +; + +.ifndef LFSR32_INC +LFSR32_INC = 1 + +; Used to hold the current state of the LFSR. It is the user's responsibility +; to set the initial state. + +.globalzp lfsr32 + + + +; +; 32-bit Galois LFSR with taps at 32, 31, 29, and 1. +; +; In: +; y = The number of states to shift through +; +; Preserved: x +; Destroyed: a, y +; +.global lfsr32_next + +.endif + diff --git a/nes15-1.0.0/nes-lib/lfsr32.s b/nes15-1.0.0/nes-lib/lfsr32.s new file mode 100644 index 0000000..3110626 --- /dev/null +++ b/nes15-1.0.0/nes-lib/lfsr32.s @@ -0,0 +1,41 @@ +; +; File: lfsr32.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; 'lfsr32_next' subroutine +; + +.include "lfsr32.inc" + + + +.segment "ZEROPAGE" + +lfsr32: .res 4 + + + +.segment "LFSRLIB" + +.proc lfsr32_next + + lsr lfsr32 + 3 + ror lfsr32 + 2 + ror lfsr32 + 1 + ror lfsr32 + bcc zero + lda lfsr32 + 3 ; Taps 32, 31, and 29 + eor #$D0 + sta lfsr32 + 3 + lda lfsr32 + eor #$01 ; Tap 1 + sta lfsr32 + +zero: + dey + bne lfsr32_next + rts + +.endproc + diff --git a/nes15-1.0.0/nes-lib/muse.inc b/nes15-1.0.0/nes-lib/muse.inc new file mode 100644 index 0000000..e70ce27 --- /dev/null +++ b/nes15-1.0.0/nes-lib/muse.inc @@ -0,0 +1,470 @@ +; +; File: muse.inc +; Namespace: muse_ / MUSE_ +; Code Segment: MUSELIB +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; MUSE - MUsic and Sound effects Engine +; +; Sound data format - +; +; Each sound is composed of one or more streams. Streams are sequences of data +; consisting of notes, rests, key-offs, ties, and opcodes. There are two sets +; of four streams available to the user, each corresponding to one of the four +; hardware sound channels (squares, triangle, and noise). The first set of +; streams is dedicated to sound effects and the second is dedicated to music. +; +; Headers - +; +; Sound data begins with the main list of sound headers. This list must be +; defined by the user at 'muse_sounds_lo/hi' and is expected to contain the +; starting addresses of each header. Each header must use the following format: +; +; 1 - Type of sound +; +; Zero indicates music and values greater than zero indicate a sound +; effect and its priority. If a sound effect is already playing and its +; priority is greater than the priority of the next sound, then the new +; sound will not be played. +; +; Then for each stream to used by the sound - +; +; 1 - Sound stream to be used (be sure to use the correct ones) +; 2 - Address of the stream's data +; +; Finally, the header should be terminated with a value greater than $7F. +; +; Stream data - +; +; A stream's data is divided into two categories, notes/rests/key-offs/ties +; commands and opcodes. Bytes with a value less than MUSE_OPCODE are commands +; and should be provided in the following format: +; +; nnnnnddd +; +; nnnnn = Type +; +; Values less than MUSE_REST are notes. This value is added to +; the current transposition base which can be adjusted with the +; provided opcodes. +; +; For the square and triangle channels, the value of the note +; plus the transposition base is used as an index into a period +; lookup table. This table begins at A0 and ends at E7 where +; middle C (261.625565 Hz) is C3 for the square channels and C4 +; for the triangle channel. For the noise channel, the value of +; the note plus the transposition base is used as the period and +; the tone. The lower four bits are written as the period and the +; fifth bit written as the tone. +; +; MUSE_REST silences the stream until the next note. +; +; MUSE_KEY_OFF disables sustaining and looping in the stream's +; current register envelope until the next note. +; +; MUSE_TIE continues the last note/rest/key-off command. +; +; Note that a stream's first command should not be a tie or a +; key-off. +; +; ddd = Duration +; +; The duration value is used as an index into a table of lengths +; where: +; +; 0 = 1 tick (1/32) +; 1 = 2 ticks (1/16) +; 2 = 3 ticks (3/32) +; 3 = 4 ticks (1/8) +; 4 = 6 ticks (3/16) +; 5 = 8 ticks (1/4) +; 6 = 12 ticks (3/8) +; 7 = 16 ticks (1/2) +; +; To create notes with a length not provided, ties must be used. +; +; Bytes with values greater than or equal to MUSE_OPCODE are opcodes. These +; are processed one after the other until the stream reader reaches either a +; new command or the end of the stream's data. The following opcodes are +; available to the user: +; +; MUSE_SET_TRANS - Set the transposition base +; +; 1 - New transposition base +; +; MUSE_MOVE_TRANS - Move the transposition base +; +; 1 - Value to add to the transposition base +; +; MUSE_LOOP_TRANS - Move the transposition base with a value from a +; lookup table indexed by the current loop count. +; +; 2 - Address of the offset lookup table +; +; MUSE_SET_TEMPO - Set the music's tempo +; +; 1 - New tempo value. +; +; The value needed for the tempo for NTSC systems can be found +; with: +; +; tempo = 128 * BPM / 225 - 1 +; +; And for PAL systems: +; +; tempo = 256 * BPM / 375 - 1 +; +; Where BPM is number of quarter beats per minute. +; Note that the default tempo is $FF. +; +; MUSE_SET_ENV - Set the current register envelope +; +; 1 - New register envelope index +; +; MUSE_SET_SWEEP - Set the current hardware sweep value +; +; 1 - New hardware sweep value +; +; By default the value of the hardware sweeps for the streams +; mapped to the square channels is $08 (disabled). User defined +; values will be used by the streams until they are changed again +; by the user or the stream reaches the MUSE_END opcode. Note +; that when disabling the sweep registers, you should use the +; value $08. Also note that this opcode should never be used with +; the streams mapped to the triangle and noise channels. +; +; MUSE_LOOP - Loop back to stream position +; +; 1 - Number of repetitions +; +; 2 - The address to loop back to +; +; Each stream has an internal loop counter that is initially +; zero. When this opcode is encountered: +; +; If the number of repetitions is zero, loop back +; unconditionally. +; Else, if the loop counter is less than the number of +; repetitions, increment the loop counter and loop back to the +; provided address. +; Else, the opcode is ignored. +; +; Note that nested loops will not work as one might expect since +; there is only one loop counter per stream. +; +; MUSE_END - End of stream data +; +; Register envelopes - +; +; User-defined register envelopes are strings of bytes where all but bits 4 +; and 5 are written directly to the sound channel's first register (SQ1_VOL, +; SQ2_VOL, TRI_LINEAR, NOISE_VOL). The remaining bits indicate what action the +; envelope reader should take after writing the register bits: +; +; 00 = Continue to next byte +; 01 = Save current position as the loop point and continue +; 10 = Loop back to the position saved with '01' or the beginning +; until key-off +; 11 = Sustain current position until key-off +; +; The address of the list of register envelopes must be defined by the user at +; 'muse_envs_lo/hi'. This list must contain the starting addresses of each +; envelope. All register envelopes should be terminated with zero. Note that +; the triangle channel should be turned on with the value $C0 and muted with +; the value $80. +; +; Regional support - +; +; By default MUSE supports NTSC systems. For PAL support, simply compile with +; the PAL symbol defined. 'muse_create_tempo' will then compensate for the +; change in frame rate and the proper period table values will be used. +; + +.ifndef MUSE_INC +MUSE_INC = 1 + +; Possible sound engine states stored in 'muse_state' + +.enum + ; Initial state after calling 'muse_off' + + MUSE_OFF + + ; Initial state after calling 'muse_on' + + MUSE_ON + + ; State set by the user to pause music playback. Note that this should + ; only be used when the state is set to 'MUSE_ON'. + + MUSE_PAUSE + + ; Set by the user to restore music playback. Note that this should + ; only be used when the state is set to 'MUSE_PAUSED'. + + MUSE_RESTORE + +.endenum + +; Channels used by the sound engine's streams + +.enum + + MUSE_SQ1 + MUSE_SQ2 + MUSE_TRI + MUSE_NOISE + +.endenum + +; Available sound engine streams. Streams 'MUS0' to 'MUS3' are mapped to the +; hardware channels 'SQ1' to 'NOISE' and are be to used to play music. Streams +; 'SFX0' to 'SFX3' are mapped to the hardware channels 'SQ1' to 'NOISE' and are +; to be used to play sound effects. + +.enum + + MUSE_MUS0 + MUSE_MUS1 + MUSE_MUS2 + MUSE_MUS3 + MUSE_SFX0 + MUSE_SFX1 + MUSE_SFX2 + MUSE_SFX3 + MUSE_NUM_STREAMS + +.endenum + +; Sound engine commands and opcodes + +.enum + + MUSE_C = $00 + MUSE_CS = $08 + MUSE_DB = MUSE_CS + MUSE_D = $10 + MUSE_DS = $18 + MUSE_EB = MUSE_DS + MUSE_E = $20 + MUSE_F = $28 + MUSE_FS = $30 + MUSE_GB = MUSE_FS + MUSE_G = $38 + MUSE_GS = $40 + MUSE_AB = MUSE_GS + MUSE_A = $48 + MUSE_AS = $50 + MUSE_BB = MUSE_AS + MUSE_B = $58 + MUSE_CH = $60 + MUSE_CSH = $68 + MUSE_DBH = MUSE_CSH + MUSE_DH = $70 + MUSE_DSH = $78 + MUSE_EBH = MUSE_DSH + MUSE_EH = $80 + MUSE_FH = $88 + MUSE_FSH = $90 + MUSE_GBH = MUSE_FSH + MUSE_GH = $98 + MUSE_GSH = $A0 + MUSE_ABH = MUSE_GSH + MUSE_AH = $A8 + MUSE_ASH = $B0 + MUSE_BBH = MUSE_ASH + MUSE_BH = $B8 + MUSE_REST = $C0 + MUSE_KEY_OFF = $C8 + MUSE_TIE = $D0 + MUSE_OPCODE = $D8 + MUSE_SET_TRANS = MUSE_OPCODE + MUSE_MOVE_TRANS + MUSE_LOOP_TRANS + MUSE_SET_TEMPO + MUSE_SET_ENV + MUSE_SET_SWEEP + MUSE_LOOP + MUSE_END + +.endenum + +; Sound engine duration indexes + +.enum + + MUSE_32 + MUSE_16 + MUSE_D16 + MUSE_8 + MUSE_D8 + MUSE_4 + MUSE_D4 + MUSE_2 + +.endenum + + + +; List of the addresses of the user-defined sounds + +.global muse_sounds_lo +.global muse_sounds_hi + +; List of the addresses of the user-defined register envelopes + +.global muse_envs_lo +.global muse_envs_hi + +; Current state of the sound engine + +.global muse_state + +; Bitfield indicating which streams are active. Streams starting from +; 'MUSE_MUS0' up are mapped to bits zero and up. + +.global muse_active + + + +; +; Stops any sound effects currently playing. +; +; Preserved: x, y +; Destroyed: a +; + +.macro muse_stop_sfx + + lda muse_active + and #$0F + sta muse_active + +.endmacro + +; +; Tests if a sound effect is currently being played. +; +; Out: +; Z = Reset if a sound effect is playing, else set +; +; Preserved: x, y +; Destroyed: a +; +.macro muse_sfx_playing + + lda muse_active + and #$F0 + +.endmacro + +; +; Stops any music currently playing. +; +; Preserved: x, y +; Destroyed: a +; + +.macro muse_stop_music + + lda muse_active + and #$F0 + sta muse_active + +.endmacro + +; +; Tests if music is currently being played. +; +; Out: +; Z = Reset if music is playing, else set +; +; Preserved: x, y +; Destroyed: a +; +.macro muse_music_playing + + lda muse_active + and #$0F + +.endmacro + +; +; Toggles between the sound engines music pausing states. Note that this +; should only be used when the sound engine is active. +; +; Preserved: x, y +; Destroyed: a +; +.macro muse_toggle_music + + lda #MUSE_PAUSE + cmp muse_state + bne done + + lda #MUSE_RESTORE + +.local done +done: + sta muse_state + +.endmacro + +; +; Creates a tempo constant for use with sound engine. +; +; In: +; tempo = The name of the symbol to created +; bmp = The number of quarter beats (MUSE_4) per minute +; +.macro muse_create_tempo tempo, bpm + +.ifndef PAL + tempo = 128 * bpm / 225 - 1 +.else + tempo = 256 * bpm / 375 - 1 +.endif + +.endmacro + +; +; Initializes the sound engine for use. Either this or 'muse_off' must be +; called at the start of the program. After calling 'muse_off', this subroutine +; should be called again if sound output is needed. +; +; Preserved: y +; Destroyed: a, x +; +.global muse_on + +; +; Kills all sound engine activity and mutes the sound channels used by the +; sound engine. Either this or 'muse_on' must be called at the start of the +; program. +; +; Preserved: x, y +; Destroyed: a +; +.global muse_off + +; +; Plays the specified sound effect or music. +; +; In: +; a = The index of the sound play +; +; Destroyed: a, x, y +; +.global muse_play + +; +; Updates the sound engine for one frame. Preferably this should be called +; during each NMI, however that is not a requirement. +; +; Destroyed: a, x, y +; +.global muse_update + +.endif + diff --git a/nes15-1.0.0/nes-lib/muse.s b/nes15-1.0.0/nes-lib/muse.s new file mode 100644 index 0000000..cb3f133 --- /dev/null +++ b/nes15-1.0.0/nes-lib/muse.s @@ -0,0 +1,932 @@ +; +; File: muse.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; MUSE sound engine subroutines and data +; + +.include "apu.inc" + +.include "muse.inc" + + + +.segment "ZEROPAGE" + +; Temporary storage used by 'muse_play' + +temp: .res 3 + +; Temporary storage used by 'muse_update'. Note that separate temporary +; storage is used since the user is allowed to call 'muse_update' during the +; NMI. + +update_temp: .res 4 + +; Current channel being updated by 'muse_update' multiplied by four + +curr_chn: .res 1 + + + +.segment "BSS" + +muse_state: .res 1 +muse_active = stream_sweep + 2 + +; Current sound effect's priority rating + +sfx_priority = stream_sweep + 3 + +; Current global music tempo + +curr_tempo = stream_sweep + 6 + +; The current tempo plus one is added to this and stored back in each frame. +; When a carry occurs, each active music stream's timer is updated. + +tempo_sum = stream_sweep + 7 + +; Address of the next byte in the stream's data + +stream_lo: .res MUSE_NUM_STREAMS +stream_hi: .res MUSE_NUM_STREAMS + +; Stream's command timer + +stream_timer: .res MUSE_NUM_STREAMS + +; Stream's loop counter + +stream_loop: .res MUSE_NUM_STREAMS + +; Note and note state: rk-nnnnn +; +; r = Rest +; 0: Note is playing +; 1: Rest command active +; +; k = Key-off +; 0: Note is in key-on state +; 1: Note is in key-off state +; +; nnnnn = Value of the last note read + +stream_note: .res MUSE_NUM_STREAMS + +; Stream's current transposition base + +stream_trans: .res MUSE_NUM_STREAMS + +; Stream's current hardware sweep value. Note that this is only used by the +; streams mapped to the square channels. + +stream_sweep: .res MUSE_NUM_STREAMS + +; Index of the stream's current register envelope + +stream_env: .res MUSE_NUM_STREAMS + +; Stream's current position in its register envelope + +stream_env_pos: .res MUSE_NUM_STREAMS + +; Stream's current loop back point in its register envelope + +stream_env_loop: .res MUSE_NUM_STREAMS + + + +.segment "MUSELIB" + +.proc muse_on + + ldx #MUSE_ON ; Set the sound engine state to active + stx muse_state + dex + stx muse_active ; Reset all active stream flags + lda #$0F ; Enable sound for the four channels used + sta SND_CHN + lda #$30 ; Mute all sound channels used + sta SQ1_VOL + sta SQ2_VOL + sta NOISE_VOL + lda #$80 + sta TRI_LINEAR + rts + +.endproc + + + +.proc muse_off + + lda #MUSE_OFF + sta muse_state ; Set the sound engine state to inactive + sta muse_active ; Set all streams to inactive + sta SND_CHN ; Silence all sound channels + rts + +.endproc + + + +.proc muse_play + +addr = temp +active = temp + 2 + + ; Load the first header byte for the sound to be played. + + tay + lda muse_sounds_lo, y + sta addr + lda muse_sounds_hi, y + sta addr + 1 + ldy #0 + lda (addr), y + beq play_music + + ; If the sound is a sound effect, play if either no other sound effect + ; is playing or the sound effect playing has a priority less than or + ; equal to the priority of the new sound. + + tax + muse_sfx_playing + beq play_sfx + cpx sfx_priority + bcc done + +play_sfx: + stx sfx_priority ; Save the sound effect's priority + lda muse_active ; Disable all SFX streams to prevent updates + and #$0F ; while setting up new SFX + bpl start + +play_music: + lda #$FF ; Reset the global music tempo to its default + sta curr_tempo + sty tempo_sum + lda #MUSE_ON ; Set the sound engine state to active in case + sta muse_state ; music was paused + lda muse_active ; Disable all music channels to prevent + and #$F0 ; updates while setting up the new music + +start: + sta muse_active ; Save the updated stream states + sta active + +loop: + iny + + ; Read the stream's index. If a terminating value was read, then + ; reading is done. + + lda (addr), y + bmi update_active + tax + iny + + ; Read the address of the stream's data. + + lda (addr), y + sta stream_lo, x + iny + lda (addr), y + sta stream_hi, x + lda #0 ; Reset the stream's loop counter + sta stream_loop, x + lda #1 ; Set the stream's timer to one so it is + sta stream_timer, x ; updated on the next 'muse_update' call + lda stream_masks, x ; Set the stream's active flag in temporary + ora active ; memory + sta active + + ; If the stream being initialized is mapped to one of the square + ; channels, then reset its hardware sweep value. + + txa + and #$03 + cmp #MUSE_TRI + bcs loop + lda #$08 + sta stream_sweep, x + bne loop + + ; Copy over the new set of stream active flags from temporary memory + ; since we are ready for 'muse_update' to begin using them. + +update_active: + lda active + sta muse_active + +done: + rts + +.endproc + + + +.proc muse_update + + ; If the sound engine is inactive, then return. + + lda muse_state + beq done + + ; Update 'tempo_sum' with the current tempo plus one. Whenever a carry + ; occurs, music streams will be updated. + + lda tempo_sum + sec + adc curr_tempo + sta tempo_sum + + ; Update each stream starting from sound effects down to music. + + ldx #MUSE_NUM_STREAMS - 1 + +loop: + txa ; Save the channel the stream is mapped to for + and #$03 ; later subroutines + asl + asl + sta curr_chn + + ; Update the stream if it is active. + + lda stream_masks, x + and muse_active + beq volume + jsr update_stream + + ; Write the stream's volume if necessary. + +volume: + jsr write_vol + + ; Update the stream's register envelope if it is still active. + + lda stream_masks, x + and muse_active + beq cont + jsr update_env + +cont: + dex + bpl loop + + ; If music was just restored, reset the sound engine's state to + ; 'MUSE_ON'. + + lda #MUSE_RESTORE + cmp muse_state + bne done + lda #MUSE_ON + sta muse_state + +done: + rts + +.endproc + + + +; +; Reads a stream's data and performs any updates. This should only be called +; for active streams. +; +; In: +; x = Index of stream to update +; curr_chn = Index of the stream's channel multiplied by four +; +; Preserved: x +; Destroyed: a, y, update_temp/+1/+2/+3 +; +.proc update_stream + +addr = update_temp +addr2 = update_temp + 2 + + cpx #MUSE_SFX0 ; If updating a sound effect stream, skip to + bcs update_timer ; the timer update + lda muse_state ; Else, if music is paused, then return + cmp #MUSE_PAUSE + beq done + cmp #MUSE_RESTORE ; Else, if restoring music, write the current + bne test_tempo ; period of the stream and continue + jsr restore_period + +test_tempo: + lda curr_tempo ; If 'tempo_sum' did not carry this frame, then + cmp tempo_sum ; return + bcc done + +update_timer: + dec stream_timer, x ; If the timer is not finished counting down, + bne done ; then return + + lda stream_lo, x ; Set the address to read the stream's data + sta addr ; from + lda stream_hi, x + sta addr + 1 + + ; Load the next byte from the stream's data and advance forward. + +loop: + ldy #0 + lda (addr), y + inc addr + bne test_byte + inc addr + 1 + +test_byte: + cmp #MUSE_OPCODE + bcc test_cmd + + ; If the byte read is an opcode, perform the opcode. + + sbc #MUSE_OPCODE + jsr perform_opcode + lda stream_masks, x ; Ensure the stream is still active, if not + and muse_active ; then return + beq done + + ; Advance the current position in the stream's data past the opcode's + ; data and continue to process the stream's data. + + tya + clc + adc addr + sta addr + bcc loop + inc addr + 1 + bne loop + + ; Else, the byte read is a command. Regardless of the command read, + ; set the stream's timer to the length provided by the command. + +test_cmd: + pha + and #$07 + tay + lda length_table, y + sta stream_timer, x + + ; Save the current stream position back into the stream. + + lda addr + sta stream_lo, x + lda addr + 1 + sta stream_hi, x + pla + cmp #MUSE_TIE ; If a tie command was read, then return + bcs done + cmp #MUSE_KEY_OFF ; If a key-off command was read, then continue + bcs key_off_cmd + cmp #MUSE_REST ; If a rest command was read, then continue + bcs rest_cmd + + ; Else, this is note command and we must save the note read, initialize + ; the stream's register envelope, and write the new period to the APU. + + lsr + lsr + lsr + sta stream_note, x + lda #0 + sta stream_env_pos, x + sta stream_env_loop, x + jmp write_period + +key_off_cmd: + lda #$40 ; On key-offs, set the stream's key-off flag + bne finish_rest + +done: + rts + +rest_cmd: + lda #$80 ; On rests, set the stream's rest flag + +finish_rest: + ora stream_note, x + sta stream_note, x + rts + +; +; Performs an opcode read from the specified stream's data. +; +; In: +; a = Value of the opcode to perform +; x = Index of the stream being updated +; curr_chn = Index of the stream's channel multiplied by four +; addr = Address of the current position in the stream's data +; Out: +; y = Number of bytes used as the opcode's parameters +; +; Preserved: x +; Destroyed: a, update_temp+2/+3 +; +.proc perform_opcode + + asl + tay + lda opcode_table + 1, y + pha + lda opcode_table, y + pha + ldy #0 + rts + +opcode_table: + .addr opcode_set_trans - 1 + .addr opcode_move_trans - 1 + .addr opcode_loop_trans - 1 + .addr opcode_set_tempo - 1 + .addr opcode_set_env - 1 + .addr opcode_set_sweep - 1 + .addr opcode_loop - 1 + .addr opcode_end - 1 + +.endproc + +; +; Opcode subroutines called by 'perform_opcode'. Note that y is initially set +; to zero when these are called and it is their responsibility to set y to the +; number of bytes to advance in the current stream's data. +; +.proc opcode_set_trans + + ; Save the new transposition base and return. + + lda (addr), y + sta stream_trans, x + iny + rts + +.endproc + +.proc opcode_move_trans + + ; Add the next value read to the transposition base and return. + + lda (addr), y + clc + adc stream_trans, x + sta stream_trans, x + iny + rts + +.endproc + +.proc opcode_loop_trans + + ; Load the transposition table's address and read the byte offset by + ; the stream's current loop count. + + lda (addr), y + sta addr2 + iny + lda (addr), y + sta addr2 + 1 + ldy stream_loop, x + + ; Add the value read to the stream's current transposition base. + + lda (addr2), y + clc + adc stream_trans, x + sta stream_trans, x + ldy #2 + rts + +.endproc + +.proc opcode_set_tempo + + ; Save the new tempo and reset 'tempo_sum'. + + lda (addr), y + sta curr_tempo + sty tempo_sum + iny + rts + +.endproc + +.proc opcode_set_env + + ; Save the index of the register envelope to be used and reinitialize + ; the stream. + + lda (addr), y + sta stream_env, x + tya + sta stream_env_pos, x + sta stream_env_loop, x + iny + rts + +.endproc + +.proc opcode_set_sweep + + ; Save the new value of the hardware sweep register to be used and + ; return. + + lda (addr), y + sta stream_sweep, x + iny + rts + +.endproc + +.proc opcode_loop + + lda (addr), y ; Read the number of repetitions + beq save_addr ; If zero, then loop back indefinitely + cmp stream_loop, x ; Else, if the stream's loop counter is equal + beq loop_done ; to the number repetitions, the loop is done + inc stream_loop, x ; Else, increment the stream's loop counter + +save_addr: + iny ; Save the address read as the new address to + lda (addr), y ; read from and return + pha + iny + lda (addr), y + sta addr + 1 + pla + sta addr + ldy #0 + rts + +loop_done: + tya ; Else, reset the stream's loop counter and + sta stream_loop, x ; return + ldy #3 + rts + +.endproc + +.proc opcode_end + + lda muse_active ; Reset the stream's active flag + eor stream_masks, x + sta muse_active + + ; If ending a sound effect stream while it is overridding an active + ; music stream, then restore the music stream's period. + + cpx #MUSE_SFX0 + bcc done + lda stream_masks - 4, x + and muse_active + beq done + txa + and #$03 + tax + jsr restore_period + txa + ora #$04 + tax + +done: + rts + +.endproc + +.endproc + + + +; +; Updates a stream's position in its current register envelope. This should +; only be called for active streams. +; +; In: +; x = Index of stream to update +; curr_chn = Index of the stream's channel multiplied by four +; +; Preserved: x +; Destroyed: a, y, update_temp/+1 +; +.proc update_env + +addr = update_temp + + ; Skip updating the register envelopes of either stream's in the middle + ; of rest command or paused music streams. + + lda stream_note, x + bmi done + cpx #MUSE_SFX0 + bcs update + lda muse_state + cmp #MUSE_PAUSE + beq done + +update: + + ; Load the next byte to be read from the register envelope data. + + ldy stream_env, x + lda muse_envs_lo, y + sta addr + lda muse_envs_hi, y + sta addr + 1 + ldy stream_env_pos, x + + lda (addr), y ; If the next byte is zero, then the envelope + beq done ; is done + and #$30 ; Else, handle the control bits + beq env_cont + cmp #$10 + beq env_loop_point + cmp #$20 + beq env_loop + + ; Sustain current position until key-off command. + + lda stream_note, x + asl + bmi env_cont + rts + + ; Loop back to saved loop point until key-off. + +env_loop: + lda stream_note, x + asl + bmi env_cont + lda stream_env_loop, x + sta stream_env_pos, x + rts + + ; Save current position as loop point and continue. + +env_loop_point: + tya + sta stream_env_loop, x + +env_cont: + inc stream_env_pos, x + +done: + rts + +.endproc + + + +; +; Makes the necessary APU period and sweep writes for the specified stream. +; +; In: +; x = Stream to write from +; curr_chn = Index of the stream's channel multiplied by four +; +; Preserved: x +; Destroyed: a, y, update_temp+2/+3 +; +.proc write_period + +period = update_temp + 2 + + ; Skip writing to the APU for a music stream that is currently being + ; overridden by a sound effect stream. + + cpx #MUSE_SFX0 + bcs cont + lda stream_masks + 4, x + and muse_active + bne done + +cont: + + ; Add the current note to the transposition base to determine the + ; actual note to be played. + + lda stream_note, x + and #$1F + clc + adc stream_trans, x + tay + lda curr_chn ; If writing to the noise channel, continue + cmp #MUSE_NOISE * 4 + beq write_noise + + ; Lookup the period to be written from the period lookup table. + + lda period_table_lo, y + sta period + lda period_table_hi, y + sta period + 1 + ldy curr_chn ; If writing to the triangle channel, continue + cpy #MUSE_TRI * 4 + beq write_tri + lda stream_sweep, x ; Else, write the sweep and period for the + sta SQ1_SWEEP, y ; square channel and return + lda period + sta SQ1_LO, y + lda period + 1 + sta SQ1_HI, y + rts + +write_tri: + lda period ; Else, set the triangle channel's period and + sta TRI_LO ; return + lda period + 1 + sta TRI_HI + rts + + ; Else, set the noise channel's period and tone and then return. + +write_noise: + tya + and #$1F + cmp #$10 + bcc write_noise_lo + and #$0F + ora #$80 + +write_noise_lo: + sta NOISE_LO + lda #$00 + sta NOISE_HI + +done: + rts + +.endproc + + + +; +; Restores the period of the specified interrupted stream. +; +; In: +; x = Stream to restore +; curr_chn = Index of the stream's channel multiplied by four +; +; Preserved: x +; Destroyed: a, y, update_temp+2/+3 +; +.proc restore_period + + ; Mute any interrupted square channel streams using the hardware + ; sweep. Silence is better than hearing an incorrect period. + + ldy curr_chn + cpy #MUSE_TRI * 4 + bcs write + lda stream_sweep, x + bpl write + lda stream_note, x + ora #$80 + sta stream_note, x + rts + +write: + jmp write_period ; Else, rewrite the stream's current period + +.endproc + + + +; +; Makes the necessary APU volume/duty writes for the specified stream. +; +; In: +; x = Stream to write from +; curr_chn = Index of the stream's channel multiplied by four +; +; Preserved: x +; Destroyed: a, y, update_temp/+1 +; +.proc write_vol + +addr = update_temp + + lda stream_masks, x ; If the stream is active, continue + and muse_active + bne active + cpx #MUSE_SFX0 ; Else, do not make any writes for inactive + bcs done ; sound effect streams + lda stream_masks + 4, x ; Else, silence the channel if writing for an + and muse_active ; inactive non-overridden music stream + bne done + + ; Silence the stream and return. + +silence: + ldy curr_chn + cpy #MUSE_TRI * 4 + beq silence_tri + lda #$30 + bne update_apu + +silence_tri: + lda #$80 + bne update_apu + +active: + cpx #MUSE_SFX0 ; If active sound effect, continue to write + bcs write + lda stream_masks + 4, x ; Else, skip writing if overridden music + and muse_active + bne done + lda muse_state ; Else, silence if music is paused + cmp #MUSE_PAUSE + beq silence + + ; If the stream is note in the middle of a rest command, then write the + ; stream's current duty/volume from its register envelope. + +write: + lda stream_note, x + bmi silence + ldy stream_env, x + lda muse_envs_lo, y + sta addr + lda muse_envs_hi, y + sta addr + 1 + ldy stream_env_pos, x + lda (addr), y ; If the stream is at the end of its register + beq silence ; envelope, then silence and return. + and #$CF + ldy curr_chn + cpy #MUSE_TRI * 4 + beq update_apu + ora #$30 + +update_apu: + sta SQ1_VOL, y + +done: + rts + +.endproc + + +; Period tables generated with the Python program 'mktables.py' found at: +; http://wiki.nesdev.com/w/index.php/APU_period_table + +.ifndef PAL + +; NTSC period lookup table + +period_table_lo: + .byte $f1, $7f, $13, $ad, $4d, $f3, $9d, $4c, $00, $b8, $74, $34 + .byte $f8, $bf, $89, $56, $26, $f9, $ce, $a6, $80, $5c, $3a, $1a + .byte $fb, $df, $c4, $ab, $93, $7c, $67, $52, $3f, $2d, $1c, $0c + .byte $fd, $ef, $e1, $d5, $c9, $bd, $b3, $a9, $9f, $96, $8e, $86 + .byte $7e, $77, $70, $6a, $64, $5e, $59, $54, $4f, $4b, $46, $42 + .byte $3f, $3b, $38, $34, $31, $2f, $2c, $29, $27, $25, $23, $21 + .byte $1f, $1d, $1b, $1a, $18, $17, $15, $14 + +period_table_hi: + .byte $07, $07, $07, $06, $06, $05, $05, $05, $05, $04, $04, $04 + .byte $03, $03, $03, $03, $03, $02, $02, $02, $02, $02, $02, $02 + .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01 + .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + .byte $00, $00, $00, $00, $00, $00, $00, $00 + +.else + +; PAL period lookup table + +period_table_lo: + .byte $60, $f6, $92, $34, $db, $86, $37, $ec, $a5, $62, $23, $e8 + .byte $b0, $7b, $49, $19, $ed, $c3, $9b, $75, $52, $31, $11, $f3 + .byte $d7, $bd, $a4, $8c, $76, $61, $4d, $3a, $29, $18, $08, $f9 + .byte $eb, $de, $d1, $c6, $ba, $b0, $a6, $9d, $94, $8b, $84, $7c + .byte $75, $6e, $68, $62, $5d, $57, $52, $4e, $49, $45, $41, $3e + .byte $3a, $37, $34, $31, $2e, $2b, $29, $26, $24, $22, $20, $1e + .byte $1d, $1b, $19, $18, $16, $15, $14, $13 + +period_table_hi: + .byte $07, $06, $06, $06, $05, $05, $05, $04, $04, $04, $04, $03 + .byte $03, $03, $03, $03, $02, $02, $02, $02, $02, $02, $02, $01 + .byte $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $01, $00 + .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + .byte $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + .byte $00, $00, $00, $00, $00, $00, $00, $00 + +.endif + +; Command length lookup table + +length_table: + .byte 1, 2, 3, 4, 6, 8, 12, 16 + +; Stream bitmask lookup table + +stream_masks: + .byte $01, $02, $04, $08, $10, $20, $40, $80 + diff --git a/nes15-1.0.0/nes-lib/oam.inc b/nes15-1.0.0/nes-lib/oam.inc new file mode 100644 index 0000000..2e70edd --- /dev/null +++ b/nes15-1.0.0/nes-lib/oam.inc @@ -0,0 +1,66 @@ +; +; File: oam.inc +; Namespace: oam_ / OAM_ +; Code Segment: OAMLIB +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; General purpose OAM library +; + +.ifndef OAM_INC +OAM_INC = 1 + +; OAM I/O location + +OAM_DMA = $4014 + +; OAM data offsets + +.enum + + OAM_Y + OAM_TILE + OAM_ATT + OAM_X + OAM_SIZE + +.endenum + + + +; RAM location of the OAM buffer +; +; Bytes: 256 + +.global oam_buff + + + +; +; Copies the contents of the OAM buffer to OAM using OAM_DMA. +; +; Preserved: x, y +; Destroyed: a +; +.macro oam_copy + + lda #0 + sta OAMADDR + lda #>oam_buff + sta OAM_DMA + +.endmacro + + + +; +; Sets all sprites in the OAM buffer to a non-visible state. +; +; Preserved: y +; Destroyed: a, x +; +.global oam_clear + +.endif + diff --git a/nes15-1.0.0/nes-lib/oam_clear.s b/nes15-1.0.0/nes-lib/oam_clear.s new file mode 100644 index 0000000..073afeb --- /dev/null +++ b/nes15-1.0.0/nes-lib/oam_clear.s @@ -0,0 +1,30 @@ +; +; File: oam_clear.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; 'oam_clear' subroutine +; + +.include "oam.inc" + + + +.segment "OAMLIB" + +.proc oam_clear + + ldx #0 + lda #248 + +loop: + sta oam_buff + OAM_Y, x + inx + inx + inx + inx + bne loop + rts + +.endproc + diff --git a/nes15-1.0.0/nes-lib/pkb.inc b/nes15-1.0.0/nes-lib/pkb.inc new file mode 100644 index 0000000..2761e04 --- /dev/null +++ b/nes15-1.0.0/nes-lib/pkb.inc @@ -0,0 +1,46 @@ +; +; File: pkb.inc +; Namespace: pkb_ / PKB_ +; Code Segment: PKBLIB +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; PackBits decoding library +; +; A PackBits data stream consists of packets with a one-byte header followed by +; data. The header is a signed byte; the data is treated as an unsigned char. +; +; Header byte Data following the header byte +; +; 0 to 127 (1 + n) literal bytes of data +; -1 to -127 One byte of data, repeated (1 - n) times in the +; decompressed output +; -128 Marks the end of the encoded data +; +; Note that the PackBits encoding used here differs somewhat from the original. +; The header byte -128 is used to mark the end of the encoded data instead of +; indicating a no-op or a run. +; + +.ifndef PKB_INC +PKB_INC = 1 + +; Address of the PackBits encoded data to be decoded + +.globalzp pkb_src + + + +; +; Decodes and writes PackBits encoded data to PPUDATA. Note that the user is +; responsible for setting PPUADDR. +; +; In: +; pkb_src/+1 = Address of the PackBits data to decode +; +; Destroyed: a, x, y, pkb_src/+1 +; +.global pkb_to_vram + +.endif + diff --git a/nes15-1.0.0/nes-lib/pkb.s b/nes15-1.0.0/nes-lib/pkb.s new file mode 100644 index 0000000..d3109f4 --- /dev/null +++ b/nes15-1.0.0/nes-lib/pkb.s @@ -0,0 +1,16 @@ +; +; File: pkb.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Common Packbits decoding subroutines and data +; + +.include "pkb.inc" + + + +.segment "ZEROPAGE" + +pkb_src: .res 2 + diff --git a/nes15-1.0.0/nes-lib/pkb_to_vram.s b/nes15-1.0.0/nes-lib/pkb_to_vram.s new file mode 100644 index 0000000..e4c18ff --- /dev/null +++ b/nes15-1.0.0/nes-lib/pkb_to_vram.s @@ -0,0 +1,72 @@ +; +; File: pkb_to_vram.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; 'pkb_to_vram' subroutine +; + +.include "ppu.inc" + +.include "pkb.inc" + + + +.segment "PKBLIB" + +.proc pkb_to_vram + + ldy #0 + lda (pkb_src), y + bpl literal + cmp #-128 + beq done + + ; Write out repeated data + + eor #$FF + clc + adc #2 + tax + iny + lda (pkb_src), y + iny + +run_loop: + sta PPUDATA + dex + bne run_loop + beq cont + + ; Write out literal data + +literal: + clc + adc #1 + tax + iny + +literal_loop: + lda (pkb_src), y + sta PPUDATA + iny + dex + bne literal_loop + + ; Advance the source address to the next header byte + +cont: + tya + clc + adc pkb_src + sta pkb_src + lda #0 + adc pkb_src + 1 + sta pkb_src + 1 + bne pkb_to_vram + +done: + rts + +.endproc + diff --git a/nes15-1.0.0/nes-lib/ppu.inc b/nes15-1.0.0/nes-lib/ppu.inc new file mode 100644 index 0000000..d5eec11 --- /dev/null +++ b/nes15-1.0.0/nes-lib/ppu.inc @@ -0,0 +1,66 @@ +; +; File: ppu.inc +; Namespace: ppu_ / PPU_ +; Code Segment: PPULIB +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; General purpose PPU library +; + +.ifndef PPU_INC +PPU_INC = 1 + +; PPU I/O locations + +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +OAMADDR = $2003 +OAMDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 + + + +; +; Polls PPUSTATUS until the veritical blanking flag is set. +; +; Preserved: a, x, y +; +.macro ppu_poll_vblank + +.local loop +loop: + bit PPUSTATUS + bpl loop + +.endmacro + +.endif + +; +; Fills all name and attribute tables with zero. +; +; Destroyed: a, x, y +; +.macro ppu_clear_nts + + ldx #$10 + lda #$20 + sta PPUADDR + lda #0 + tay + sta PPUADDR + +.local loop +loop: + sta PPUDATA + iny + bne loop + dex + bne loop + +.endmacro + diff --git a/nes15-1.0.0/nes-lib/vrub.inc b/nes15-1.0.0/nes-lib/vrub.inc new file mode 100644 index 0000000..2084c01 --- /dev/null +++ b/nes15-1.0.0/nes-lib/vrub.inc @@ -0,0 +1,152 @@ +; +; File: vrub.inc +; Namespace: vrub_ / VRUB_ +; Code Segment: VRUBLIB +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; The VRAM update buffer +; + +.ifndef VRUB_INC +VRUB_INC = 1 + +.enum + + ; Control byte: t---i--r + ; + ; t = Terminate + ; 0: No transfers left + ; 1: Continue transferring + ; + ; i = VRAM increment + ; 0: Increment by 1 + ; 1: Increment by 32 + ; + ; r = Repeat byte + ; 0: No repeating + ; 1: Repeat first byte read from source + + VRUB_CTRL + + ; VRAM destination address + + VRUB_DSTHI + VRUB_DSTLO + + ; Length of the transfer + + VRUB_LEN + + ; Begining of the data to transfer + + VRUB_DATA + +.endenum + + + +; ROM/RAM to VRAM updates storage. Note that the first byte must be set to zero +; before NMIs are enabled. +; +; Bytes: May be as little as 'VRUB_DATA + 1' to a full page if needed + +.global vrub_buff + +; Current position of the next entry to write to. If set to zero, 'vrub_update' +; does nothing. This should initially be set to zero before NMIs are enabled. +; After an update is added to the 'vrub_buff', 'vrub_end' should be set to the +; first byte of the next update and a null terminator (zero) should written. + +.globalzp vrub_end + + + +; +; VRUB NMI macro. Note that afterwards PPUCTRL and PPUSCROLL should be updated +; to reset the PPU's internal address. +; +; In: +; ppuctrl = The bits to write to PPUCTRL when setting the VRAM increment +; +; Preserved: vrub_buff +; Destroyed: a, x, y, vrub_end +; +.macro vrub_update ppuctrl + + ldy #0 + sty vrub_end + +.local buff_loop +buff_loop: + lda vrub_buff + VRUB_CTRL, y + bpl done + lsr + ora ppuctrl + sta PPUCTRL + lda vrub_buff + VRUB_DSTHI, y + sta PPUADDR + lda vrub_buff + VRUB_DSTLO, y + sta PPUADDR + ldx vrub_buff + VRUB_LEN, y + iny + iny + iny + iny + bcs repeat + +.local literal_loop +literal_loop: + lda vrub_buff, y + sta PPUDATA + iny + dex + bne literal_loop + beq buff_loop + +.local repeat +repeat: + lda vrub_buff, y + iny + +.local repeat_loop +repeat_loop: + sta PPUDATA + dex + bne repeat_loop + beq buff_loop + +.local done +done: + +.endmacro + +; +; Creates a VRUB update to draw a string to VRAM. +; +; In: +; addr = The address to draw the string to +; string = The string to be drawn +; +.macro vrub_str addr, string + + .byte $80, >addr, $*.d + +.PHONY: debug +debug: CFLAGS += -g +debug: $(PROG) + +.PHONY: clean +clean: + rm $(OBJS) $(OBJS:.o=.d) $(PROG) + diff --git a/nes15-1.0.0/pcx2chr/pcx2chr.c b/nes15-1.0.0/pcx2chr/pcx2chr.c new file mode 100644 index 0000000..9dc3a97 --- /dev/null +++ b/nes15-1.0.0/pcx2chr/pcx2chr.c @@ -0,0 +1,171 @@ +/* + * File: pcx2chr.c + * Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) + * + * PCX file to NES CHR file converter + */ + +#include +#include +#include +#include +#include "img8.h" +#include "neschr.h" + + + +/* + * Processes the argument 'arg' passed to the program, setting options based on + * what was read. + * + * Returns: If a valid argument was passed and processed, then zero is + * returned. Otherwise, a non-zero value is returned and an error message is + * printed to 'stderr'. + * + * Asserts: + * arg != NULL + */ +static int process_arg(const char *arg); + +/* + * Converts the PCX file stored in 'src_file' to the NES CHR file stored in + * 'dst_file'. If 'vertical' is set to a non-zero value, then the PCX file + * will be read from top to bottom. + * + * Returns: If the conversion was successfully done, then a non-zero value is + * returned. Otherwise, zero is returned and an error message is printed to + * 'stderr'. + * + * Asserts: + * src_file != NULL + * dst_file != NULL + */ +static int pcx2chr(void); + + + +static const char help_str[] = + "Usage: pcx2chr [OPTION]... [PCXFILE] [CHRFILE]\n" + "\nOptions:\n" + "\t-V, --vertical\tread from PCX file vertically\n" + "\t-h, -?, --help\tdisplay this help and exit\n"; + +static const char *src_file = NULL; +static const char *dst_file = NULL; +static int vertical = 0; +static int print_help = 0; + + + +int main(int argc, char *argv[]) +{ + int i; + + for (i = 1; i < argc; i++) { + if (!process_arg(argv[i])) { + return EXIT_FAILURE; + } + + if (print_help) { + printf(help_str); + return EXIT_SUCCESS; + } + } + + if (src_file == NULL) { + printf(help_str); + return EXIT_SUCCESS; + } + + if (dst_file == NULL) { + fprintf(stderr, "Output file required\n"); + return EXIT_FAILURE; + } + + if (!pcx2chr()) { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + + + +static int process_arg(const char *arg) +{ + const char *p; + + assert(arg != NULL); + + if (arg[0] != '-' || arg[1] == '\0') { + if (src_file == NULL) { + src_file = arg; + } else if (dst_file == NULL) { + dst_file = arg; + } + + return 1; + } + + p = arg; + + while (*++p != '\0') { + switch (*p) { + case 'V': + vertical = 1; + break; + case 'h': + case '?': + print_help = 1; + break; + case '-': + if (!strcmp(arg, "--help")) { + print_help = 1; + } else if (!strcmp(arg, "--vertical")) { + vertical = 1; + } else { + fprintf(stderr, "Unrecognized " + "option '%s'\n", arg); + + return 0; + } + + return 1; + default: + fprintf(stderr, "Unrecognized option '%c'\n", + *p); + return 0; + } + } + + return 1; +} + + + +static int pcx2chr(void) +{ + struct img8_image *img; + struct neschr_chr *chr; + int success; + + assert(src_file != NULL); + assert(dst_file != NULL); + + if ((img = img8_read_pcx_file(src_file, NULL)) == NULL) { + return 0; + } + + chr = neschr_from_img8(img, vertical); + img8_free(img); + + if (chr == NULL) { + return 0; + } + + success = neschr_write_file(dst_file, chr); + neschr_free(chr); + + return success; +} + diff --git a/nes15-1.0.0/snd/mus0.s b/nes15-1.0.0/snd/mus0.s new file mode 100644 index 0000000..e42d7f2 --- /dev/null +++ b/nes15-1.0.0/snd/mus0.s @@ -0,0 +1,600 @@ +; +; File: mus0.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Prelude from BWV 558 by J.S. Bach (possibly by J. T. Krebs), preceded by the +; new puzzle SFX +; + +.include "muse.inc" + +.include "snd.inc" + + + +muse_create_tempo TEMPO, 95 + + + +.segment "CODE" + +.export snd_mus0 +.proc snd_mus0 + + .byte 0 + .byte MUSE_MUS0 + .addr stream0 + .byte MUSE_MUS1 + .addr stream1 + .byte MUSE_MUS2 + .addr stream2 + .byte MUSE_MUS3 + .addr stream3 + .byte $80 + + + +stream0: + .byte MUSE_SET_ENV, SND0_ENV0 + .byte MUSE_REST | MUSE_D4 + .byte MUSE_TIE | MUSE_D4 + .byte MUSE_SET_TEMPO, TEMPO + +stream0_loop0: + .byte MUSE_SET_TRANS, 12 * 2 + 3 + .byte MUSE_REST | MUSE_8 + .byte MUSE_G | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_DH | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_G | MUSE_8 + .byte MUSE_EBH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_KEY_OFF | MUSE_8 + .byte MUSE_F | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_CH | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_F | MUSE_8 + .byte MUSE_DH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_KEY_OFF | MUSE_8 + .byte MUSE_DH | MUSE_8 + .byte MUSE_CH | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_A | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_G | MUSE_8 + .byte MUSE_FS | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_G | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_CH | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_FS | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_KEY_OFF | MUSE_16 + +stream0_loop1: + .byte MUSE_GH | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_GH | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_LOOP, 1 + .addr stream0_loop1 + .byte MUSE_GH | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_GH | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_GH | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_GH | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_G | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_CH | MUSE_16 + + .byte MUSE_A | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_BB | MUSE_16 + + .byte MUSE_G | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_G | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_F | MUSE_16 + + .byte MUSE_BB | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_G | MUSE_8 + .byte MUSE_EBH | MUSE_8 + +stream0_loop2: + .byte MUSE_CH | MUSE_32 + .byte MUSE_DH | MUSE_32 + .byte MUSE_LOOP, 5 + .addr stream0_loop2 + .byte MUSE_BB | MUSE_8 + + .byte MUSE_BB | MUSE_D4 + .byte MUSE_CH | MUSE_8 + .byte MUSE_DH | MUSE_8 + .byte MUSE_EH | MUSE_8 + .byte MUSE_FH | MUSE_2 + .byte MUSE_TIE | MUSE_D4 + + .byte MUSE_FH | MUSE_8 + .byte MUSE_EH | MUSE_8 + .byte MUSE_DH | MUSE_8 + .byte MUSE_CSH | MUSE_8 + .byte MUSE_B | MUSE_8 + +stream0_loop3: + .byte MUSE_CSH | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_EH | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_AH | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_LOOP, 1 + .addr stream0_loop3 + +stream0_loop4: + .byte MUSE_DH | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_AH | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_LOOP, 1 + .addr stream0_loop4 + + .byte MUSE_GH | MUSE_16 + .byte MUSE_AH | MUSE_16 + .byte MUSE_FH | MUSE_8 + +stream0_loop5: + .byte MUSE_EH | MUSE_32 + .byte MUSE_FH | MUSE_32 + .byte MUSE_LOOP, 5 + .addr stream0_loop5 + .byte MUSE_DH | MUSE_8 + + .byte MUSE_DH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_8 + .byte MUSE_G | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_DH | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_G | MUSE_8 + + .byte MUSE_EBH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_8 + .byte MUSE_F | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_CH | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_F | MUSE_8 + + .byte MUSE_DH | MUSE_2 + .byte MUSE_TIE | MUSE_D4 + .byte MUSE_DH | MUSE_8 + .byte MUSE_CH | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_A | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_G | MUSE_8 + +stream0_loop6: + .byte MUSE_FS | MUSE_16 + .byte MUSE_D | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_D | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_D | MUSE_16 + .byte MUSE_FS | MUSE_16 + .byte MUSE_D | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_D | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_D | MUSE_16 + + .byte MUSE_G | MUSE_16 + .byte MUSE_D | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_D | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_D | MUSE_16 + .byte MUSE_G | MUSE_16 + .byte MUSE_D | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_D | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_D | MUSE_16 + .byte MUSE_LOOP, 1 + .addr stream0_loop6 + + .byte MUSE_CH | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_BB | MUSE_8 + +stream0_loop7: + .byte MUSE_A | MUSE_32 + .byte MUSE_BB | MUSE_32 + .byte MUSE_LOOP, 5 + .addr stream0_loop7 + .byte MUSE_G | MUSE_8 + .byte MUSE_G | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_KEY_OFF | MUSE_4 + .byte MUSE_G | MUSE_2 + .byte MUSE_TIE | MUSE_8 + .byte MUSE_A | MUSE_8 + +stream0_loop8: + .byte MUSE_FS | MUSE_32 + .byte MUSE_G | MUSE_32 + .byte MUSE_LOOP, 5 + .addr stream0_loop8 + .byte MUSE_G | MUSE_8 + + .byte MUSE_G | MUSE_4 + .byte MUSE_TIE | MUSE_2 + .byte MUSE_FS | MUSE_4 + .byte MUSE_TIE | MUSE_2 + .byte MUSE_LOOP, 0 + .addr stream0_loop0 + + + +stream1: + .byte MUSE_SET_ENV, SND0_ENV1 + .byte MUSE_REST | MUSE_D4 + .byte MUSE_TIE | MUSE_D4 + +stream1_loop0: + .byte MUSE_SET_TRANS, 12 * 0 + 3 + .byte MUSE_GH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_8 + .byte MUSE_CH | MUSE_8 + .byte MUSE_EBH | MUSE_8 + .byte MUSE_GH | MUSE_8 + .byte MUSE_MOVE_TRANS, 12 + .byte MUSE_CH | MUSE_8 + .byte MUSE_MOVE_TRANS, -12 + .byte MUSE_CH | MUSE_8 + + .byte MUSE_FH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_DH | MUSE_8 + .byte MUSE_FH | MUSE_8 + .byte MUSE_BBH | MUSE_8 + .byte MUSE_BB | MUSE_8 + + .byte MUSE_GH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_MOVE_TRANS, 12 + .byte MUSE_CH | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_DH | MUSE_8 + .byte MUSE_CH | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_A | MUSE_8 + + .byte MUSE_BB | MUSE_8 + .byte MUSE_CH | MUSE_8 + .byte MUSE_DH | MUSE_8 + .byte MUSE_CH | MUSE_8 + .byte MUSE_EBH | MUSE_8 + .byte MUSE_CH | MUSE_8 + +stream1_loop1: + .byte MUSE_A | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_LOOP, 2 + .addr stream1_loop1 + + .byte MUSE_DH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_CH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_A | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_BB | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_G | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_CH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_BB | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_2 + .byte MUSE_BB | MUSE_2 + .byte MUSE_A | MUSE_4 + + .byte MUSE_KEY_OFF | MUSE_16 + .byte MUSE_GH | MUSE_16 + .byte MUSE_FH | MUSE_16 + .byte MUSE_EBH | MUSE_16 + .byte MUSE_DH | MUSE_8 + .byte MUSE_EBH | MUSE_8 + .byte MUSE_FH | MUSE_8 + .byte MUSE_GH | MUSE_8 + .byte MUSE_KEY_OFF | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_A | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_F | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_CH | MUSE_16 + + .byte MUSE_DH | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_2 + .byte MUSE_EH | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_2 + + .byte MUSE_DH | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_2 + .byte MUSE_DH | MUSE_4 + .byte MUSE_A | MUSE_4 + .byte MUSE_CSH | MUSE_4 + +stream1_loop2: + .byte MUSE_KEY_OFF | MUSE_8 + .byte MUSE_D | MUSE_8 + .byte MUSE_F | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_F | MUSE_8 + .byte MUSE_D | MUSE_8 + .byte MUSE_G | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_MOVE_TRANS, -2 + .byte MUSE_LOOP, 1 + .addr stream1_loop2 + .byte MUSE_SET_TRANS, 12 * 0 + 3 + + .byte MUSE_KEY_OFF | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_DH | MUSE_8 + .byte MUSE_FH | MUSE_8 + .byte MUSE_BBH | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_GH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_AH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_BBH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_AH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_GH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_GH | MUSE_2 + .byte MUSE_FSH | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_16 + .byte MUSE_GH | MUSE_16 + .byte MUSE_FSH | MUSE_16 + .byte MUSE_GH | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_GH | MUSE_16 + .byte MUSE_FSH | MUSE_16 + .byte MUSE_GH | MUSE_16 + .byte MUSE_BB | MUSE_16 + .byte MUSE_DH | MUSE_16 + .byte MUSE_CH | MUSE_16 + .byte MUSE_DH | MUSE_16 + + .byte MUSE_KEY_OFF | MUSE_4 + .byte MUSE_GH | MUSE_2 + .byte MUSE_MOVE_TRANS, 12 + .byte MUSE_DH | MUSE_4 + .byte MUSE_A | MUSE_4 + .byte MUSE_CH | MUSE_4 + + .byte MUSE_DH | MUSE_D4 + .byte MUSE_TIE | MUSE_2 + .byte MUSE_DH | MUSE_8 + .byte MUSE_CH | MUSE_8 + .byte MUSE_BB | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_FS | MUSE_8 + .byte MUSE_LOOP, 0 + .addr stream1_loop0 + + + +stream2: + .byte MUSE_SET_ENV, SND0_ENV2 + .byte MUSE_REST | MUSE_D4 + .byte MUSE_TIE | MUSE_D4 + +stream2_loop0: + .byte MUSE_SET_TRANS, 12 * 1 + 3 + .byte MUSE_G | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_CH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_F | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_BB | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_EBH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_DH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_G | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_DH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_B | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_CH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_FH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_BB | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_EBH | MUSE_2 + .byte MUSE_TIE | MUSE_2 + .byte MUSE_KEY_OFF | MUSE_2 + + .byte MUSE_DH | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_2 + .byte MUSE_EBH | MUSE_4 + .byte MUSE_FH | MUSE_4 + .byte MUSE_F | MUSE_4 + + .byte MUSE_BB | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_A | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_G | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_2 + .byte MUSE_G | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_2 + + .byte MUSE_F | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_2 + .byte MUSE_G | MUSE_4 + .byte MUSE_A | MUSE_2 + + .byte MUSE_DH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_BB | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_CH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_F | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_BB | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_EBH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_DH | MUSE_2 + .byte MUSE_TIE | MUSE_2 + .byte MUSE_TIE | MUSE_2 + + .byte MUSE_TIE | MUSE_2 + .byte MUSE_TIE | MUSE_4 + .byte MUSE_BB | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_CH | MUSE_4 + .byte MUSE_DH | MUSE_4 + .byte MUSE_D | MUSE_4 + .byte MUSE_G | MUSE_2 + .byte MUSE_TIE | MUSE_2 + + .byte MUSE_EH | MUSE_2 + .byte MUSE_DH | MUSE_2 + .byte MUSE_TIE | MUSE_4 + + .byte MUSE_G | MUSE_4 + .byte MUSE_TIE | MUSE_2 + .byte MUSE_DH | MUSE_4 + .byte MUSE_TIE | MUSE_2 + .byte MUSE_LOOP, 0 + .addr stream2_loop0 + + + +stream3: + .byte MUSE_SET_ENV, SND1_ENV0 + .byte MUSE_SET_TRANS, 0 + + .byte MUSE_DS | MUSE_D16 + .byte MUSE_REST | MUSE_D16 + .byte MUSE_DS | MUSE_D8 + .byte MUSE_END + +.endproc + diff --git a/nes15-1.0.0/snd/mus1.s b/nes15-1.0.0/snd/mus1.s new file mode 100644 index 0000000..f072dd1 --- /dev/null +++ b/nes15-1.0.0/snd/mus1.s @@ -0,0 +1,75 @@ +; +; File: mus1.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Puzzle solved music +; + +.include "muse.inc" + +.include "snd.inc" + + + +muse_create_tempo TEMPO, 95 + + + +.segment "CODE" + +.export snd_mus1 +.proc snd_mus1 + + .byte 0 + .byte MUSE_MUS0 + .addr stream0 + .byte MUSE_MUS1 + .addr stream1 + .byte MUSE_MUS2 + .addr stream2 + .byte $80 + + + +stream0: + .byte MUSE_SET_ENV, SND0_ENV0 + .byte MUSE_SET_TEMPO, TEMPO + .byte MUSE_SET_TRANS, 12 * 2 + 3 + .byte MUSE_REST | MUSE_8 + .byte MUSE_F | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_CH | MUSE_8 + .byte MUSE_A | MUSE_8 + .byte MUSE_F | MUSE_8 + + .byte MUSE_BB | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_2 + .byte MUSE_END + + + +stream1: + .byte MUSE_SET_ENV, SND0_ENV1 + .byte MUSE_SET_TRANS, 12 * 1 + 3 + + .byte MUSE_CH | MUSE_4 + .byte MUSE_TIE | MUSE_2 + .byte MUSE_BB | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_2 + .byte MUSE_END + + + +stream2: + .byte MUSE_SET_ENV, SND0_ENV2 + .byte MUSE_SET_TRANS, 12 * 1 + 3 + + .byte MUSE_F | MUSE_4 + .byte MUSE_TIE | MUSE_2 + .byte MUSE_BB | MUSE_4 + .byte MUSE_KEY_OFF | MUSE_2 + .byte MUSE_END + +.endproc + diff --git a/nes15-1.0.0/snd/sfx0.s b/nes15-1.0.0/snd/sfx0.s new file mode 100644 index 0000000..055410d --- /dev/null +++ b/nes15-1.0.0/snd/sfx0.s @@ -0,0 +1,35 @@ +; +; File: sfx0.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Tile movement SFX +; + +.include "muse.inc" + +.include "snd.inc" + + + +.segment "CODE" + +.export snd_sfx0 +.proc snd_sfx0 + + .byte 1 + .byte MUSE_SFX3 + .addr stream0 + .byte $80 + + + +stream0: + .byte MUSE_SET_ENV, SND1_ENV0 + .byte MUSE_SET_TRANS, 0 + + .byte MUSE_DS | MUSE_D8 + .byte MUSE_END + +.endproc + diff --git a/nes15-1.0.0/snd/sfx1.s b/nes15-1.0.0/snd/sfx1.s new file mode 100644 index 0000000..264a0be --- /dev/null +++ b/nes15-1.0.0/snd/sfx1.s @@ -0,0 +1,38 @@ +; +; File: sfx1.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Play paused SFX +; + +.include "muse.inc" + +.include "snd.inc" + + + +.segment "CODE" + +.export snd_sfx1 +.proc snd_sfx1 + + .byte 1 + .byte MUSE_SFX1 + .addr stream0 + .byte $80 + + + +stream0: + .byte MUSE_SET_ENV, SND3_ENV0 + .byte MUSE_SET_TRANS, 12 * 2 + 3 + + .byte MUSE_BB | MUSE_8 + .byte MUSE_FH | MUSE_8 + .byte MUSE_DH | MUSE_8 + .byte MUSE_BBH | MUSE_2 + .byte MUSE_END + +.endproc + diff --git a/nes15-1.0.0/snd/snd.inc b/nes15-1.0.0/snd/snd.inc new file mode 100644 index 0000000..e0d3cf9 --- /dev/null +++ b/nes15-1.0.0/snd/snd.inc @@ -0,0 +1,39 @@ +; +; File: snd.inc +; Namespace: snd_ / SND_ +; Code Segment: CODE +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Music and sound effects data module +; + +.ifndef SND_INC +SND_INC = 1 + +; Available music and sound effects + +.enum + + SND_MUS0 + SND_MUS1 + SND_NUM_MUS + SND_SFX0 = SND_NUM_MUS + SND_SFX1 + +.endenum + +; Available register envelopes + +.enum + + SND0_ENV0 + SND0_ENV1 + SND0_ENV2 + SND1_ENV0 + SND3_ENV0 + +.endenum + +.endif + diff --git a/nes15-1.0.0/snd/snd.s b/nes15-1.0.0/snd/snd.s new file mode 100644 index 0000000..ea250b1 --- /dev/null +++ b/nes15-1.0.0/snd/snd.s @@ -0,0 +1,71 @@ +; +; File: snd.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Music and sound effects data +; + +.include "muse.inc" + + + +.segment "CODE" + +; Register envelopes + +muse_envs_lo: + .byte env0 + .byte >env1 + .byte >env2 + .byte >env3 + .byte >env4 + + + +env0: + .byte $42, $48, $46, $76, $45, $45, $44, $44, $44, $43, $43, $43, $43 + .byte $42, $42, $42, $42, $42, $41, $41, $41, $41, $41, $41, $00 + +env1: + .byte $81, $86, $84, $B4, $83, $83, $82, $82, $82, $81, $81, $81, $81 + .byte $00 + +env2: + .byte $F0, $00 + +env3: + .byte $01, $02, $06, $0C, $0F, $00 + +env4: + .byte $8F, $8C, $8A, $89, $88, $87, $86, $85, $85, $84, $84, $83, $83 + .byte $82, $82, $82, $81, $81, $81, $00 + + + +; Music and sound effects + +.import snd_mus0 +.import snd_mus1 +.import snd_sfx0 +.import snd_sfx1 + +muse_sounds_lo: + .byte snd_mus0 + .byte >snd_mus1 + .byte >snd_sfx0 + .byte >snd_sfx1 + diff --git a/nes15-1.0.0/src/game.inc b/nes15-1.0.0/src/game.inc new file mode 100644 index 0000000..8e61901 --- /dev/null +++ b/nes15-1.0.0/src/game.inc @@ -0,0 +1,59 @@ +; +; File: game.inc +; Namespace: game / GAME_ +; Code Segment: CODE +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Main game module +; + +.ifndef GAME_INC +GAME_INC = 1 + +; Main game states + +.enum + + ; Title screen state + ; + ; Preconditions: + ; + ; Rendering must be turned off via 'nmi_ppumask'. + ; The OAM buffer must be cleared. + + GAME_TITLE + + ; Playing game state + ; + ; Preconditions: + ; + ; Rendering must be turned off via 'nmi_ppumask'. + ; The base name table in 'nmi_ppuctrl' must be $2000. + ; The OAM buffer must be cleared. + ; The LFSR must be seeded. + + GAME_PLAY + +.endenum + + + +; Temporary storage used by the game modules + +.globalzp game_temp + + + +; +; Changes the current game state and calls its initialization function. +; +; In: +; a = The game state to change to +; +; Destroyed: Assume everything +; +.global game_change_state + +.endif + diff --git a/nes15-1.0.0/src/game.s b/nes15-1.0.0/src/game.s new file mode 100644 index 0000000..a013073 --- /dev/null +++ b/nes15-1.0.0/src/game.s @@ -0,0 +1,150 @@ +; +; File: game.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Main game subroutines and data +; + +.include "apu.inc" +.include "joy.inc" +.include "muse.inc" +.include "oam.inc" +.include "ppu.inc" +.include "vrub.inc" + +.include "fifteen.inc" +.include "gfx.inc" +.include "nmi.inc" +.include "play.inc" +.include "title.inc" + +.include "game.inc" + + + +JOY_REP_DELAY = 30 +JOY_REP_SPEED = 3 + + + +vrub_buff = $100 +oam_buff = $200 + +.segment "ZEROPAGE" + +game_temp: .res 4 + +; The current game state + +game_state: .res 1 + + + +.segment "CODE" + +; +; Reset routine +; +.proc reset + + sei + cld + ldx #$FF + txs + inx + stx PPUCTRL ; Disable NMI + stx PPUMASK ; Disable rendering + stx DMC_FREQ ; Disable DMC IRQs + + bit PPUSTATUS ; Clear the vblank flag + ppu_poll_vblank ; First of two waits for vblanks to ensure that + ; the PPU has stabilized + + stx nmi_ppumask ; Reset PPUMASK mirror + stx nmi_draw ; Reset NMI draw flag + stx vrub_buff ; Reset VRUB buffer and end position + stx vrub_end + stx joy_held ; Initialize read joypad states + stx joy_pressed + stx joy_released + joy_init1 ; Initialize the joypad state + jsr oam_clear ; Initialize the OAM buffer + jsr muse_on ; Initialize the sound engine + + ppu_poll_vblank ; Second vblank wait + + lda #$80 ; Enable NMIs, set pattern and name tables + sta nmi_ppuctrl + sta PPUCTRL + + jsr gfx_write_pals ; Write the sprite and background palettes + dec nmi_draw ; Enable NMI drawing so OAM is cleared and the + ; palettes are written + + lda #GAME_TITLE ; Set game state to the title screen + jsr game_change_state + + ; Main loop + +loop: + jsr update + jmp loop + +.endproc + + + +.proc game_change_state + + sta game_state + asl + tax + lda init_table + 1, x + pha + lda init_table, x + pha + rts + +init_table: + .addr title_init - 1 + .addr play_init - 1 + +.endproc + + + +; +; Calls the current state's update subroutine. +; +; Destroyed: Assume everything +; +.proc update + + lda game_state + asl + tax + lda update_table + 1, x + pha + lda update_table, x + pha + rts + +update_table: + .addr title_update - 1 + .addr play_update - 1 + +.endproc + + + +.byte $48, $41, $50, $50, $59, $20, $45, $41, $53, $54, $45, $52, $21 + + + +.segment "VECTORS" + + .addr nmi + .addr reset + .addr nmi_done + diff --git a/nes15-1.0.0/src/gfx.inc b/nes15-1.0.0/src/gfx.inc new file mode 100644 index 0000000..fa02f28 --- /dev/null +++ b/nes15-1.0.0/src/gfx.inc @@ -0,0 +1,191 @@ +; +; File: gfx.inc +; Namespace: gfx_ / GFX_ +; Code Segment: CODE +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Graphics module +; + +.ifndef GFX_INC +GFX_INC = 1 + +; Game display message indexes + +.enum + + GFX_MSG_CLEAR + GFX_MSG_SOLVED + GFX_MSG_SOLVING + +.endenum + + + +; High byte of the name table to draw to + +.globalzp gfx_nt + +; Tile sprite's horizontal position on the puzzle board + +.globalzp gfx_tile_x + +; Cursor's horizontal position on the puzzle board + +.globalzp gfx_cursor_x + +; Tile sprite's vertical position on the puzzle board + +.globalzp gfx_tile_y + +; Cursor's vertical position on the puzzle board + +.globalzp gfx_cursor_y + +; Tile sprite's on-screen horizontal position + +.globalzp gfx_tile_sx + +; Cursor's on-screen horizontal position + +.globalzp gfx_cursor_sx + +; Tile sprite's on-screen vertical position + +.globalzp gfx_tile_sy + +; Cursor's on-screen vertical position + +.globalzp gfx_cursor_sy + +.endif + + + +; +; Sets up the VRUB update to write the background and sprite palettes used by +; the game. +; +; Destroyed: a, x, y +; +.global gfx_write_pals + +; +; Sets up the VRUB updates to draw a puzzle tile to the background. Note that +; this should not be used to draw the puzzle's gap. +; +; In: +; a = The horizontal position of the tile to draw +; y = The vertical position of the tile to draw +; +; Destroyed: a, x, y, game_temp/+1/+2 +; +.global gfx_draw_tile_bgd + +; +; Sets up the VRUB updates to erase a puzzle tile from the background. +; +; In: +; a = The horizontal position of the tile to erase +; y = The vertical position of the tile to erase +; +; Destroyed: a, x, y, game_temp/+1 +; +.global gfx_erase_tile_bgd + +; +; Initializes the tile sprite and draws it to the OAM buffer. Note that this +; should not be used to draw the puzzle's gap. +; +; In: +; gfx_tile_x = The tile sprite's starting horizontal position +; gfx_tile_y = The tile sprite's starting vertical position +; gfx_tile_sx = The horizontal position of the tile sprite's destination +; on the puzzle board +; gfx_tile_sy = The vertical position of the tile sprite's destination on +; the puzzle board +; +; Destroyed: a, x, y +; +.global gfx_init_tile_spr + +; +; Positions the OAM sprites used by the tile sprite. +; +; Destroyed: a, x, y +; +.global gfx_pos_tile_spr + +; +; Moves the tile sprite's on-screen position to match its position on the +; puzzle board. +; +; Out: +; Z = Reset if the tile sprite's on-screen position matches its puzzle +; board position, else set +; +; Destroyed: a, x, y +; +.global gfx_update_tile_spr + +; +; Sets the OAM sprites used by the tile sprite to a non-visible state. +; +; Destroyed: a, x, y +; +.global gfx_erase_tile_spr + +; +; Initializes the cursor and draws it to the OAM buffer. +; +; In: +; gfx_cursor_x = The cursor's horizontal position on the puzzle board +; gfx_cursor_y = The cursor's vertical position on the puzzle board +; +; Preserved: x, y +; Destroyed: a +; +.global gfx_init_cursor + +; +; Positions the OAM sprites used by the cursor. +; +; Preserved: x, y +; Destroyed: a +; +.global gfx_pos_cursor + +; +; Moves the cursor's on-screen position to match its position on the puzzle +; board. +; +; Out: +; Z = Reset if the cursor's on-screen position matches its puzzle board +; position, else set +; +; Destroyed: a, x, y +; +.global gfx_update_cursor + +; +; Draws the number of moves used by the player to the screen. +; +; In: +; a = The low byte of the number of moves to draw +; y = The high byte of the number of moves to draw +; +; Destroyed: a, x, y +; +.global gfx_draw_moves + +; +; Draws a message to the message display area of the game screen. +; +; In: +; a = The index of the message to draw +; +; Destroyed: a, x, y +; +.global gfx_draw_msg + diff --git a/nes15-1.0.0/src/gfx.s b/nes15-1.0.0/src/gfx.s new file mode 100644 index 0000000..f520bee --- /dev/null +++ b/nes15-1.0.0/src/gfx.s @@ -0,0 +1,666 @@ +; +; File: gfx.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Graphics subroutines and data +; + +.include "bcd.inc" +.include "oam.inc" +.include "ppu.inc" +.include "vrub.inc" + +.include "fifteen.inc" +.include "game.inc" + +.include "gfx.inc" + + + +; Puzzle tile's dimensions in pattern table tiles + +TILE_WIDTH = 4 +TILE_HEIGHT = 4 +TILE_SIZE = TILE_WIDTH * TILE_HEIGHT + +; Pattern table indexes used to draw the puzzle's tiles + +TILE_TLCORNER = $00 +TILE_TRCORNER = $01 +TILE_BLCORNER = $02 +TILE_BRCORNER = $03 +TILE_TEDGE = $04 +TILE_BEDGE = $05 +TILE_LEDGE = $06 +TILE_REDGE = $07 +TILE_NUM_BASE = $60 + +; Name table position of the top left corner of the tile area + +TILE_NT_X = 8 +TILE_NT_Y = 6 + +; Name table address of the top left corner of the tile area + +TILE_NT_ADDR = TILE_NT_X + TILE_NT_Y * 32 + +; First and last OAM sprites used by the tile sprite + +TILE_OAM_FIRST = CURSOR_OAM_LAST + OAM_SIZE +TILE_OAM_LAST = TILE_OAM_FIRST + (TILE_SIZE - 1) * OAM_SIZE + +; Pattern table indexes used to draw the cursor + +CURSOR_TLCORNER = $08 +CURSOR_TRCORNER = $09 +CURSOR_BLCORNER = $0A +CURSOR_BRCORNER = $0B + +; First and last OAM sprites used by the cursor + +CURSOR_OAM_FIRST = 0 +CURSOR_OAM_LAST = 3 * OAM_SIZE + +; Name table address of the moves display area + +MOVES_NT_ADDR = 22 + 27 * 32 + +; Name table address of the message display area + +MSG_NT_ADDR = 6 + 26 * 32 + + + +.segment "ZEROPAGE" + +gfx_nt: .res 1 + +gfx_tile_x: .res 1 +gfx_cursor_x: .res 1 + +gfx_tile_y: .res 1 +gfx_cursor_y: .res 1 + +gfx_tile_sx: .res 1 +gfx_cursor_sx: .res 1 + +gfx_tile_sy: .res 1 +gfx_cursor_sy: .res 1 + + + +.segment "CODE" + +.proc gfx_write_pals + + lda #pal_update + jmp vrub_from_mem + +pal_update: + .byte $80, $3F, $00, 32 + .incbin "bgd.pal" + .incbin "spr.pal" + +.endproc + + + +.proc gfx_draw_tile_bgd + +nt_addr = game_temp +tile_offset = game_temp + 2 + + ; Store the offset to add to the tile's center indexes. + + sta nt_addr + tya + asl + asl + adc nt_addr ; Carry is already cleared + tax + lda fifteen_board, x + asl + asl + sta tile_offset + + ; Store the name table address to write to. + + jsr calc_nt_addr + + ; Setup the VRUB updates for each column of the tile. + + ldx vrub_end + ldy #TILE_WIDTH - 1 + +loop: + + ; Store the pattern table indexes for this column. + + lda pt_indexes, y + sta vrub_buff + VRUB_DATA, x + lda pt_indexes + TILE_WIDTH, y + sta vrub_buff + VRUB_DATA + 1, x + lda pt_indexes + TILE_WIDTH * 2, y + sta vrub_buff + VRUB_DATA + 2, x + lda pt_indexes + TILE_WIDTH * 3, y + sta vrub_buff + VRUB_DATA + 3, x + + ; If drawing the inner columns, add the offset already stored. + + tya + beq cont + cpy #TILE_WIDTH - 1 + bcs cont + lda vrub_buff + VRUB_DATA + 1, x + adc tile_offset + sta vrub_buff + VRUB_DATA + 1, x + lda vrub_buff + VRUB_DATA + 2, x + adc tile_offset + sta vrub_buff + VRUB_DATA + 2, x + +cont: + ; Set the control byte for the update. + + lda #$88 + sta vrub_buff + VRUB_CTRL, x + + ; Finish writing the update and move to the next column. + + jsr finish_update + txa + adc #VRUB_DATA + TILE_HEIGHT + tax + dey + bpl loop + lda #$00 + sta vrub_buff + VRUB_CTRL, x + stx vrub_end + rts + +; +; Calculates the name table address for the top left-hand corner of a puzzle +; tile (nt_addr * 4 + y * 128 + TILE_NT_ADDR). Note that this is reused by +; 'gfx_erase_tile_bgd'. +; +; In: +; nt_addr = The horizontal position of the tile to draw +; y = The vertical position of the tile to draw +; +; Out: +; nt_addr/+1 = The name table address for the puzzle tile +; +; Preserved: x +; Destroyed: a, y +; +.proc calc_nt_addr + + tya + ldy nt_addr + lsr + sta nt_addr + 1 + ror + sta nt_addr + tya + asl + asl + adc nt_addr ; Carry is already cleared + adc #TILE_NT_ADDR + ora gfx_nt + sta nt_addr + 1 + rts + +.endproc + +; +; Sets the destination and length bytes for current column's VRUB update. Note +; that this is reused by 'gfx_erase_tile_bgd'. +; +; In: +; x = The index in 'vrub_buff' to write to +; y = The column of the tile being written +; +; Preserved: y +; Destroyed: a +; +.proc finish_update + + tya + clc + adc nt_addr + sta vrub_buff + VRUB_DSTLO, X + lda #0 + adc nt_addr + 1 + sta vrub_buff + VRUB_DSTHI, X + lda #TILE_HEIGHT + sta vrub_buff + VRUB_LEN, x + rts + +.endproc + +pt_indexes: + .byte TILE_TLCORNER, TILE_TEDGE, TILE_TEDGE, TILE_TRCORNER + .byte TILE_LEDGE, TILE_NUM_BASE - 4, TILE_NUM_BASE - 2, TILE_REDGE + .byte TILE_LEDGE, TILE_NUM_BASE - 3, TILE_NUM_BASE - 1, TILE_REDGE + .byte TILE_BLCORNER, TILE_BEDGE, TILE_BEDGE, TILE_BRCORNER + +.endproc + + + +.proc gfx_erase_tile_bgd + +nt_addr = game_temp + + ; Store the name table address to write to. + + sta nt_addr + jsr gfx_draw_tile_bgd::calc_nt_addr + + ; Setup the VRUB updates for each column of the tile to erase. + + ldx vrub_end + ldy #TILE_WIDTH - 1 + +loop: + + ; Set the control byte for the update. + + lda #$89 + sta vrub_buff + VRUB_CTRL, x + + ; Store the blank pattern table index, which will be repeated for the + ; entire column. + + lda #' ' + sta vrub_buff + VRUB_DATA, x + + ; Finish writing the update and move to the next column. + + jsr gfx_draw_tile_bgd::finish_update + txa + adc #VRUB_DATA + 1 + tax + dey + bpl loop + lda #$00 + sta vrub_buff + VRUB_CTRL, x + stx vrub_end + rts + +.endproc + + + +.proc gfx_init_tile_spr + +; Center OAM sprites that contain the tile's number + +oam0 = oam_buff + TILE_OAM_FIRST + 5 * OAM_SIZE +oam1 = oam_buff + TILE_OAM_FIRST + 6 * OAM_SIZE +oam2 = oam_buff + TILE_OAM_FIRST + 9 * OAM_SIZE +oam3 = oam_buff + TILE_OAM_FIRST + 10 * OAM_SIZE + + ; Set the sprite's pattern table indexes and color. + + ldx #TILE_OAM_LAST + ldy #TILE_SIZE - 1 + +loop: + lda gfx_draw_tile_bgd::pt_indexes, y + sta oam_buff + OAM_TILE, x + lda #1 + sta oam_buff + OAM_ATT, x + dex + dex + dex + dex + dey + bpl loop + + lda gfx_tile_y + asl + asl + adc gfx_tile_x ; Carry is already cleared + tay + lda fifteen_board, y + asl + asl + adc #TILE_NUM_BASE - 4 ; Carry is already cleared + tay + sty oam0 + OAM_TILE + iny + sty oam2 + OAM_TILE + iny + sty oam1 + OAM_TILE + iny + sty oam3 + OAM_TILE + + ; Initialize the tile sprite's on-screen position from the puzzle board + ; position already stored in 'gfx_tile_sx' and 'gfx_tile_sy'. + + lda gfx_tile_sx + lsr + ror + ror + ror + adc #TILE_NT_X * 8 ; Carry is already cleared + sta gfx_tile_sx + lda gfx_tile_sy + lsr + ror + ror + ror + adc #TILE_NT_Y * 8 ; Carry is already cleared + sta gfx_tile_sy + rts + +.endproc + + + +.proc gfx_pos_tile_spr + + ldx #TILE_OAM_LAST + ldy #TILE_SIZE - 1 + +loop: + tya + and #$0C + asl + adc gfx_tile_sy ; Carry is already cleared + adc #$FF + sta oam_buff + OAM_Y, x + tya + and #$03 + asl + asl + asl + adc gfx_tile_sx ; Carrys is already cleared + sta oam_buff + OAM_X, x + dex + dex + dex + dex + dey + bpl loop + rts + +.endproc + + + +.proc gfx_update_tile_spr + + ldx #0 + +; +; Update either the tile sprite or the cursor. Note that this is reused by +; 'gfx_update_cursor'. +; +; In: +; x = Which sprite to update (0 = tile, 1 = cursor) +; +; Out: +; Z = Reset if the sprite's on-screen position matches its puzzle board +; position, else set +; +; Preserved: x +; Destroyed: a, y +; +.proc update + + ; Keep count of the number of coordinates updated. + + ldy #0 + + ; Update the sprite's horizontal position. + + lda gfx_tile_x, x + lsr + ror + ror + ror + adc #TILE_NT_X * 8 ; Carry is already cleared + sec + sbc gfx_tile_sx, x + beq move_ver + bcs move_right + + lsr + lsr + ora #$C0 + bne hor_done + +move_right: + adc #2 ; Carry is already set + lsr + lsr + +hor_done: + clc + adc gfx_tile_sx, x + sta gfx_tile_sx, x + iny + + ; Update the sprite's vertical position. + +move_ver: + lda gfx_tile_y, x + lsr + ror + ror + ror + adc #TILE_NT_Y * 8 ; Carry is already cleared + sec + sbc gfx_tile_sy, x + beq done + bcs move_down + + lsr + lsr + ora #$C0 + bne ver_done + +move_down: + adc #2 ; Carry is already set + lsr + lsr + +ver_done: + clc + adc gfx_tile_sy, x + sta gfx_tile_sy, x + iny + +done: + tya ; Set Z based on the number coordinates updated + rts + +.endproc + +.endproc + + + +.proc gfx_erase_tile_spr + + ldx #TILE_OAM_LAST + ldy #TILE_SIZE - 1 + lda #248 + +loop: + sta oam_buff + OAM_Y, x + dex + dex + dex + dex + dey + bpl loop + rts + +.endproc + + + +.proc gfx_init_cursor + +oam0 = oam_buff + CURSOR_OAM_FIRST +oam1 = oam_buff + CURSOR_OAM_FIRST + OAM_SIZE +oam2 = oam_buff + CURSOR_OAM_FIRST + OAM_SIZE * 2 +oam3 = oam_buff + CURSOR_OAM_FIRST + OAM_SIZE * 3 + + ; Set the sprite's pattern table indexes and color. + + lda #CURSOR_TLCORNER + sta oam0 + OAM_TILE + lda #CURSOR_BLCORNER + sta oam1 + OAM_TILE + lda #CURSOR_TRCORNER + sta oam2 + OAM_TILE + lda #CURSOR_BRCORNER + sta oam3 + OAM_TILE + + lda #0 + sta oam0 + OAM_ATT + sta oam1 + OAM_ATT + sta oam2 + OAM_ATT + sta oam3 + OAM_ATT + + ; Initialize the cursor's on-screen position. + + lda gfx_cursor_x + lsr + ror + ror + ror + adc #TILE_NT_X * 8 ; Carry is already cleared + sta gfx_cursor_sx + lda gfx_cursor_y + lsr + ror + ror + ror + adc #TILE_NT_Y * 8 ; Carry is already cleared + sta gfx_cursor_sy + rts + +.endproc + + + +.proc gfx_pos_cursor + +oam0 = oam_buff + CURSOR_OAM_FIRST +oam1 = oam_buff + CURSOR_OAM_FIRST + OAM_SIZE +oam2 = oam_buff + CURSOR_OAM_FIRST + OAM_SIZE * 2 +oam3 = oam_buff + CURSOR_OAM_FIRST + OAM_SIZE * 3 + + lda gfx_cursor_sx + sta oam0 + OAM_X + sta oam1 + OAM_X + clc + adc #(TILE_WIDTH - 1) * 8 + sta oam2 + OAM_X + sta oam3 + OAM_X + + lda gfx_cursor_sy + adc #$FF ; Carry is already cleared + sta oam0 + OAM_Y + sta oam2 + OAM_Y + clc + adc #(TILE_HEIGHT - 1) * 8 + sta oam1 + OAM_Y + sta oam3 + OAM_Y + rts + +.endproc + + + +.proc gfx_update_cursor + + ldx #1 + jmp gfx_update_tile_spr::update + +.endproc + + + +.proc gfx_draw_moves + + ; Convert the number of moves to BCD format. + + jsr bcd16_from_bin + + ; Setup the VRUB update to draw the number of moves. + + lda #>MOVES_NT_ADDR + ora gfx_nt + tay + lda #msg0, >msg1, >msg2 + +; Game screen messages + +msg0: + vrub_str MSG_NT_ADDR, " " + .byte $00 + +msg1: + vrub_str MSG_NT_ADDR, "SOLVED! " + .byte $00 + +msg2: + vrub_str MSG_NT_ADDR, "SOLVING..." + .byte $00 + +.endproc + + + +.segment "CHR1" + + .incbin "edges.chr" ; $00 + .incbin "cursor.chr" ; $08 + .incbin "tile.chr" ; $0C + .res $130 + .incbin "font.chr" ; $20 + .incbin "nums.chr" ; $60 + diff --git a/nes15-1.0.0/src/header.s b/nes15-1.0.0/src/header.s new file mode 100644 index 0000000..bce6d38 --- /dev/null +++ b/nes15-1.0.0/src/header.s @@ -0,0 +1,16 @@ +; +; File: header.inc +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; NROM-128 iNES header +; + +.segment "INESHDR" + + .byte "NES", $1A + .byte $01 ; 1 16k bank of PRGROM + .byte $01 ; 1 8k back of CHRROM + .byte $01, $00 ; Mapper 0 with vertical mirroring + .byte $00 + diff --git a/nes15-1.0.0/src/nmi.inc b/nes15-1.0.0/src/nmi.inc new file mode 100644 index 0000000..7173000 --- /dev/null +++ b/nes15-1.0.0/src/nmi.inc @@ -0,0 +1,69 @@ +; +; File: nmi.inc +; Namespace: nmi_ / NMI_ +; Code Segment: CODE +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; NMI module +; + +.ifndef NMI_INC +NMI_INC = 1 + +; Counter incremented every NMI. This is used primarily by 'nmi_wait' to wait +; for the end of the next NMI. + +.globalzp nmi_count + +; Copy of PPUCTRL written during every NMI + +.globalzp nmi_ppuctrl + +; Copy of PPUMASK written during every NMI. Note that if you intend to turn +; on or off rendering, you should write to this, then use 'nmi_wait' to wait +; for the mirror to be copied to PPUMASK. + +.globalzp nmi_ppumask + +; Flag that tells the NMI that the current frame is ready to be drawn, causing +; it to draw the OAM buffer and perform VRUB updates. + +.globalzp nmi_draw + + + +; +; Waits for the end of the next NMI. +; +; Preserved: x, y +; Destroyed: a +; +.macro nmi_wait + + lda nmi_count + +.local loop +loop: + cmp nmi_count + beq loop + +.endmacro + +; +; NMI routine +; +; Preserved: a, x, y +; +.global nmi + +; +; Simply a lable set to the 'rti' instruction at the end of NMI routine. This +; is provided so it can be used as the IRQ routine. +; +; Preserved: a, x, y +; +.global nmi_done + +.endif + diff --git a/nes15-1.0.0/src/nmi.s b/nes15-1.0.0/src/nmi.s new file mode 100644 index 0000000..4f966ff --- /dev/null +++ b/nes15-1.0.0/src/nmi.s @@ -0,0 +1,66 @@ +; +; File: nmi.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; NMI subroutines and data +; + +.include "muse.inc" +.include "oam.inc" +.include "ppu.inc" +.include "vrub.inc" + +.include "nmi.inc" + + + +.segment "ZEROPAGE" + +nmi_count: .res 1 +nmi_ppuctrl: .res 1 +nmi_ppumask: .res 1 +nmi_draw: .res 1 + + + +.segment "CODE" + +.proc nmi + + pha + txa + pha + tya + pha + + bit nmi_draw ; Test if the frame is ready to be displayed + bpl no_draw + oam_copy ; Update OAM + vrub_update nmi_ppuctrl ; Perform VRUB updates + inc nmi_draw ; Reset the frame ready flag + +no_draw: + lda nmi_ppuctrl ; Update PPUCTRL + sta PPUCTRL + lda #0 + sta PPUSCROLL ; Update PPUSCROLL + sta PPUSCROLL + lda nmi_ppumask ; Update PPUMASK + sta PPUMASK + inc nmi_count ; Update the NMI counter used for waiting + jsr muse_update ; Update the sound engine + + pla + tay + pla + tax + pla + +done: + rti + +.endproc + +nmi_done = nmi::done + diff --git a/nes15-1.0.0/src/play.inc b/nes15-1.0.0/src/play.inc new file mode 100644 index 0000000..021514f --- /dev/null +++ b/nes15-1.0.0/src/play.inc @@ -0,0 +1,29 @@ +; +; File: play.inc +; Namespace: play_ / PLAY_ +; Code Segment: CODE +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Playing game state module +; + +.ifndef PLAY_INC +PLAY_INC = 1 + +; +; Initializes the playing game state. +; +; Destroyed: Assume everything +; +.global play_init + +; +; Updates the playing game state. +; +; Destroyed: Assume everything +; +.global play_update + +.endif + diff --git a/nes15-1.0.0/src/play.s b/nes15-1.0.0/src/play.s new file mode 100644 index 0000000..1dcbf7f --- /dev/null +++ b/nes15-1.0.0/src/play.s @@ -0,0 +1,571 @@ +; +; File: play.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Playing game state subroutines and data +; + +.include "joy.inc" +.include "muse.inc" +.include "pkb.inc" +.include "ppu.inc" + +.include "fifteen.inc" +.include "game.inc" +.include "gfx.inc" +.include "nmi.inc" +.include "snd.inc" + +.include "play.inc" + + + +; Playing game states + +.enum + + ; Generates and draws a new puzzle and prepares the game for play. + ; + ; Preconditions: + ; + ; 'gfx_nt' must be initialized. + ; The background must be drawn to both name tables. + + PLAY_NEW_PUZZLE + + ; Waits for player input and performs any valid actions. + ; + ; Preconditions: + ; + ; The puzzle board must be drawn. + ; The message area must be cleared. + + PLAY_WAIT_ACTION + + ; Displays tile movement. + ; + ; Preconditions: + ; + ; A valid move must have occurred. + ; 'tile_x' and 'tile_x' must be set to the tile's new position. + ; The cursor's position must be set to the tile's old position. + + PLAY_MOVE_TILE + + ; Displays the solved message and waits for input. + ; + ; Preconditions: + ; + ; The puzzle must be solved. + + PLAY_PUZZLE_SOLVED + +.endenum + +; Maximum number of that can be counted + +MAX_MOVES = 9999 + + + +.segment "ZEROPAGE" + +; Current playing game state + +play_state: .res 1 + +; Play state flags: ps------ +; +; p = Paused +; 0: Play is active +; 1: Play is paused +; +; s = Auto-solving +; 0: Auto-solver is inactive +; 1: Auto-solver is active + +play_flags: .res 1 + +; Position of the current tile being processed + +tile_x: .res 1 +tile_y: .res 1 + +; Number of moves used by the player + +moves: .res 2 + + + +.segment "CODE" + +; +; 'PLAY_NEW_PUZZLE' state +; +.proc new_puzzle + +.proc init + + jsr fifteen_gen ; Generate a new puzzle state + lda gfx_nt ; Prepare to draw to the off-screen name table + eor #$04 + sta gfx_nt + lda #0 ; Initialize the current position to draw + sta tile_x + sta tile_y + tay ; Reset and the number of moves used + sta moves + sty moves + 1 + jsr gfx_draw_moves ; Draw the number of moves to the screen + lda #GFX_MSG_CLEAR ; Clear the message display area + jsr gfx_draw_msg + lda #SND_MUS0 ; Start the background music + jmp muse_play + +.endproc + +.proc update + + ; If the current position is the gap, erase any tile that might be + ; there, else draw the tile at the current position. + + lda tile_x + ldy tile_y + cmp fifteen_gap_x + bne draw_tile + cpy fifteen_gap_y + bne draw_tile + jsr gfx_erase_tile_bgd + jmp next + +draw_tile: + jsr gfx_draw_tile_bgd + +next: + + ; Advance to the next tile. + + inc tile_x + lda tile_x + sec + sbc #FIFTEEN_WIDTH + bne update + sta tile_x + inc tile_y + lda tile_y + cmp #FIFTEEN_HEIGHT + bne done + + ; Begin play if the puzzle board is fully draw. + + lda nmi_ppuctrl ; Switch the visible name table to one drawn to + eor #$01 + sta nmi_ppuctrl + lda #$1E ; Enable rendering if not already enabled + sta nmi_ppumask + lda #PLAY_WAIT_ACTION ; Change the state to 'wait_action' + jmp change_state + +done: + rts + +.endproc + +.endproc + + + +; +; 'PLAY_WAIT_ACTION' state +; +.proc wait_action + +.proc init + + rts + +.endproc + +.proc update + + ; If auto-solving is enabled, perform the next move provided by the + ; auto-solver. + + bit play_flags + bvc test_move + lda fifteen_gap_x + sta tile_x + lda fifteen_gap_y + sta tile_y + jsr fifteen_solve + jsr fifteen_move + lda fifteen_gap_x + sta gfx_cursor_x + lda fifteen_gap_y + sta gfx_cursor_y + lda #PLAY_MOVE_TILE + jmp change_state + + ; Else, attempt to move the tile under the cursor if 'A' is pressed. + +test_move: + lda joy_pressed + lsr + bcc done + lda fifteen_gap_x + sta tile_x + lda fifteen_gap_y + sta tile_y + lda gfx_cursor_x + ldy gfx_cursor_y + jsr fifteen_move + bne done + lda #PLAY_MOVE_TILE + jmp change_state + +done: + rts + +.endproc + +.endproc + + + +; +; 'PLAY_MOVE_TILE' state +; +.proc move_tile + +.proc init + + ; Play the tile movement SFX. + + lda #SND_SFX0 + jsr muse_play + + ; Initialize the tile sprite. Note that the tile sprite starts under + ; the cursor and must move to the new position of the tile that was + ; moved. + + lda gfx_cursor_x + sta gfx_tile_sx + lda gfx_cursor_y + sta gfx_tile_sy + lda tile_x + sta gfx_tile_x + lda tile_y + sta gfx_tile_y + jsr gfx_init_tile_spr + jsr gfx_pos_tile_spr + + ; Erase the moved tile from the background. + + lda gfx_cursor_x + ldy gfx_cursor_y + jsr gfx_erase_tile_bgd + + ; Update and draw the number of moves used by the player. + + lda moves + cmp #MAX_MOVES + beq done + +update_moves: + inc moves + bne draw_moves + inc moves + 1 + +draw_moves: + lda moves + ldy moves + 1 + jmp gfx_draw_moves + +done: + rts + +.endproc + +.proc update + + ; Update the tile sprite. + + jsr gfx_update_tile_spr + beq done + jmp gfx_pos_tile_spr + + ; Continue play if the tile sprite has reached its destination. + +done: + lda gfx_tile_x + ldy gfx_tile_y + jsr gfx_draw_tile_bgd ; Draw the moved tile to the background + jsr gfx_erase_tile_spr ; Erase the tile sprite + + ; If the puzzle is solved, switch to 'solved' state. + + jsr fifteen_solved + bne unsolved + lda #PLAY_PUZZLE_SOLVED + jmp change_state + + ; Else, go back to 'wait_action' state. + +unsolved: + lda #PLAY_WAIT_ACTION + jmp change_state + +.endproc + +.endproc + + + +; +; 'PLAY_PUZZLE_SOLVED' state +; +.proc puzzle_solved + +.proc init + + lda #0 ; Disable the auto-solver and pausing + sta play_flags + lda #SND_MUS1 ; Play the puzzle solved music + jsr muse_play + lda #GFX_MSG_SOLVED ; Draw the solved message to the screen + jmp gfx_draw_msg + +.endproc + +.proc update + + ; If the solved music is still playing, then continue waiting. + + muse_music_playing + bne done + + ; Else, continue waiting until the player presses either 'A' or 'B'. + + lda joy_pressed + and #$03 + beq done + + ; Else, generate a new puzzle and begin playing. + + lda #PLAY_NEW_PUZZLE + jmp change_state + +done: + rts + +.endproc + +.endproc + + + +.proc play_init + + ; Draw the playing state's PackBits encoded name table to both name + ; tables. + + lda #$20 + sta PPUADDR + sta gfx_nt + lda #$00 + sta PPUADDR + sta play_flags ; Initialize the play state flags + sta gfx_cursor_x ; Initialize the cursor to top left-hand corner + sta gfx_cursor_y ; of the puzzle board + lda #play_pkb + sta pkb_src + 1 + jsr pkb_to_vram ; Write background to the first name table + lda #$24 + sta PPUADDR + lda #$00 + sta PPUADDR + lda #play_pkb + sta pkb_src + 1 + jsr pkb_to_vram ; Write background to the second name table + jsr gfx_init_cursor ; Initialize the cursor sprite + lda #PLAY_NEW_PUZZLE ; Change the play state to 'new_puzzle' + jmp change_state + +play_pkb: + .incbin "play.pkb" + +.endproc + + + +.proc play_update + + nmi_wait ; Wait for the end of the next NMI + jsr joy_update1 ; Read the current joypad state + + ; If play is is not paused, continue to test input. + + lda joy_pressed + bit play_flags + bpl test_pause + + ; Else, if the player has pressed 'Start' then unpause the game and + ; the background music. + + and #$08 + bne unpause + jmp update_done + +unpause: + muse_toggle_music + lda play_flags + and #$7F + sta play_flags + bpl test_solver + + ; Test if the player has pressed 'Start' and pause play if so. + +test_pause: + and #$08 + beq test_solver + muse_toggle_music + lda #SND_SFX1 + jsr muse_play + lda play_flags + ora #$80 + sta play_flags + bmi update_done + + ; If the auto-solver is not enabled, continue to test input. + +test_solver: + lda joy_pressed + bit play_flags + bvc test_solver_on + + ; Else, if the player has pressed 'Select' then disable the auto- + ; solver. + + and #$04 + beq test_done + lda play_flags ; Disable the auto-solver + and #$BF + sta play_flags + lda #GFX_MSG_CLEAR ; Clear the message display area + jsr gfx_draw_msg + jmp test_done + + ; Test if the player has pressed 'Select' and enable the auto-solver + ; if so. + +test_solver_on: + and #$04 + beq test_dir + lda play_state ; Skip if the puzzle is already solved + cmp #PLAY_PUZZLE_SOLVED + beq test_dir + jsr fifteen_init_solver ; Else, initialize the auto-solver + lda #GFX_MSG_SOLVING ; Draw the solving message to the screen + jsr gfx_draw_msg + lda play_flags ; Set the auto-solver flag + ora #$40 + sta play_flags + bne test_done + + ; Test the directional buttons and move the cursor if so. + +test_dir: + jsr joy_repeat1 ; Only apply repeat for directional buttons + lda joy_pressed ; Test for move cursor right + bpl test_left + ldy gfx_cursor_x + cpy #FIFTEEN_WIDTH - 1 + beq test_left + inc gfx_cursor_x + +test_left: + asl ; Test for move cursor left + bpl test_down + ldy gfx_cursor_x + beq test_down + dec gfx_cursor_x + +test_down: + asl ; Test for move cursor down + bpl test_up + ldy gfx_cursor_y + cpy #FIFTEEN_HEIGHT - 1 + beq test_up + inc gfx_cursor_y + +test_up: + asl ; Test for move cursor up + bpl test_done + ldy gfx_cursor_y + beq test_done + dec gfx_cursor_y + +test_done: + jsr gfx_update_cursor ; Update and position the cursor + jsr gfx_pos_cursor + jsr update ; Call the current state's update subroutine + +update_done: + + dec nmi_draw ; Inform the NMI that its time for a redraw + rts + +update: + lda play_state + asl + tax + lda update_table + 1, x + pha + lda update_table, x + pha + rts + +update_table: + .addr new_puzzle::update - 1 + .addr wait_action::update - 1 + .addr move_tile::update - 1 + .addr puzzle_solved::update - 1 + +.endproc + + +; +; Changes the current game state and calls its initialization function. +; +; In: +; a = The playing state to change to +; +; Destroyed: Assume everything +; +.proc change_state + + sta play_state + asl + tax + lda init_table + 1, x + pha + lda init_table, x + pha + rts + +init_table: + .addr new_puzzle::init - 1 + .addr wait_action::init - 1 + .addr move_tile::init - 1 + .addr puzzle_solved::init - 1 + +.endproc + diff --git a/nes15-1.0.0/src/title.inc b/nes15-1.0.0/src/title.inc new file mode 100644 index 0000000..9473bfe --- /dev/null +++ b/nes15-1.0.0/src/title.inc @@ -0,0 +1,29 @@ +; +; File: title.inc +; Namespace: title_ / TITLE_ +; Code Segment: CODE +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Title screen state module +; + +.ifndef TITLE_INC +TITLE_INC = 1 + +; +; Initializes the title screen state. +; +; Destroyed: Assume everything +; +.global title_init + +; +; Updates the title screen state. +; +; Destroyed: Assume everything +; +.global title_update + +.endif + diff --git a/nes15-1.0.0/src/title.s b/nes15-1.0.0/src/title.s new file mode 100644 index 0000000..6947d28 --- /dev/null +++ b/nes15-1.0.0/src/title.s @@ -0,0 +1,70 @@ +; +; File: title.s +; Copyright (c) 2011 Mathew Brenaman (see 'LICENSE' for details) +; Assembled with ca65 +; +; Title screen state subroutines and data +; + +.include "joy.inc" +.include "lfsr32.inc" +.include "pkb.inc" +.include "ppu.inc" + +.include "game.inc" +.include "nmi.inc" + +.include "title.inc" + + + +.segment "CODE" + +.proc title_init + + lda #$20 ; Draw the title screen's Packbit encoded name + sta PPUADDR ; table data + lda #$00 + sta PPUADDR + lda #title_pkb + sta pkb_src + 1 + jsr pkb_to_vram + jsr joy_update1 ; Initial read to clear out pressed buttons + lda #$1E ; Enable sprite and background rendering + sta nmi_ppumask + nmi_wait + rts + +title_pkb: + .incbin "title.pkb" + + +.endproc + + + +.proc title_update + + jsr joy_update1 ; Continue waiting until the player presses any + lda joy_pressed ; button + beq cont + lda #$00 ; Else, disable rendering + sta nmi_ppumask + nmi_wait + lda #82 ; Seed LFSR with the only available entropy + eor nmi_count + sta lfsr32 + sta lfsr32 + 1 + eor nmi_count + sta lfsr32 + 2 + sta lfsr32 + 3 + lda #GAME_PLAY ; Change the state to 'play' + jmp game_change_state + +cont: + rts + +.endproc + diff --git a/nes_instr_test/nsf_singles/01-implied.nsf b/nes_instr_test/nsf_singles/01-implied.nsf new file mode 100644 index 0000000..42fc4dd Binary files /dev/null and b/nes_instr_test/nsf_singles/01-implied.nsf differ diff --git a/nes_instr_test/nsf_singles/02-immediate.nsf b/nes_instr_test/nsf_singles/02-immediate.nsf new file mode 100644 index 0000000..2ca7cd4 Binary files /dev/null and b/nes_instr_test/nsf_singles/02-immediate.nsf differ diff --git a/nes_instr_test/nsf_singles/03-zero_page.nsf b/nes_instr_test/nsf_singles/03-zero_page.nsf new file mode 100644 index 0000000..28a5f9e Binary files /dev/null and b/nes_instr_test/nsf_singles/03-zero_page.nsf differ diff --git a/nes_instr_test/nsf_singles/04-zp_xy.nsf b/nes_instr_test/nsf_singles/04-zp_xy.nsf new file mode 100644 index 0000000..739acb0 Binary files /dev/null and b/nes_instr_test/nsf_singles/04-zp_xy.nsf differ diff --git a/nes_instr_test/nsf_singles/05-absolute.nsf b/nes_instr_test/nsf_singles/05-absolute.nsf new file mode 100644 index 0000000..f1c654c Binary files /dev/null and b/nes_instr_test/nsf_singles/05-absolute.nsf differ diff --git a/nes_instr_test/nsf_singles/06-abs_xy.nsf b/nes_instr_test/nsf_singles/06-abs_xy.nsf new file mode 100644 index 0000000..3414986 Binary files /dev/null and b/nes_instr_test/nsf_singles/06-abs_xy.nsf differ diff --git a/nes_instr_test/nsf_singles/07-ind_x.nsf b/nes_instr_test/nsf_singles/07-ind_x.nsf new file mode 100644 index 0000000..568707e Binary files /dev/null and b/nes_instr_test/nsf_singles/07-ind_x.nsf differ diff --git a/nes_instr_test/nsf_singles/08-ind_y.nsf b/nes_instr_test/nsf_singles/08-ind_y.nsf new file mode 100644 index 0000000..76e5ecb Binary files /dev/null and b/nes_instr_test/nsf_singles/08-ind_y.nsf differ diff --git a/nes_instr_test/nsf_singles/09-branches.nsf b/nes_instr_test/nsf_singles/09-branches.nsf new file mode 100644 index 0000000..327a387 Binary files /dev/null and b/nes_instr_test/nsf_singles/09-branches.nsf differ diff --git a/nes_instr_test/nsf_singles/10-stack.nsf b/nes_instr_test/nsf_singles/10-stack.nsf new file mode 100644 index 0000000..fb8bc7e Binary files /dev/null and b/nes_instr_test/nsf_singles/10-stack.nsf differ diff --git a/nes_instr_test/nsf_singles/11-special.nsf b/nes_instr_test/nsf_singles/11-special.nsf new file mode 100644 index 0000000..5a11570 Binary files /dev/null and b/nes_instr_test/nsf_singles/11-special.nsf differ diff --git a/nes_instr_test/readme.txt b/nes_instr_test/readme.txt new file mode 100644 index 0000000..cb78006 --- /dev/null +++ b/nes_instr_test/readme.txt @@ -0,0 +1,364 @@ +NES CPU Instruction Behavior Tests +---------------------------------- +These tests verify most instruction behavior fairly thoroughly, +including unofficial instructions. Failing instructions are listed by +their opcode and name. Serious errors in behavior of basic opcodes might +cause many false errors. + +The *_singles/ test all instructions, but test the official ones first, +so you can tell whether you pass those even if your emulator hangs on +the unofficial ones. + +The nsf_singles builds audibly report the opcodes of any failed +instructions before the final result. + + +Internal operation +------------------ +Most instructions are tested by setting many combinations of input +values for registers, flags, and memory, running the instruction under +test, then updating a running checksum with the resulting values. After +trying all interesting input combinations, the checksum is compared with +the correct one (what a NES gives) to find whether the instruction +passed. This approach makes it very easy to write the tests, since the +instructions don't have to be each coded for separately; instead, only +the different addressing modes need separate tests. + + +Instructions +------------ +U = Unofficial +X = Freezes CPU, so not tested +? = Inconsistent/unknown behavior, so not tested + +00 BRK #n +01 ORA (z,X) +02 X KIL +03 U SLO (z,X) +04 U DOP z +05 ORA z +06 ASL z +07 U SLO z +08 PHP +09 ORA #n +0A ASL A +0B U AAC #n +0C U TOP abs +0D ORA a +0E ASL a +0F U SLO abs +10 BPL r +11 ORA (z),Y +12 X KIL +13 U SLO (z),Y +14 U DOP z,X +15 ORA z,X +16 ASL z,X +17 U SLO z,X +18 CLC +19 ORA a,Y +1A U NOP +1B U SLO abs,Y +1C U TOP abs,X +1D ORA a,X +1E ASL a,X +1F U SLO abs,X +20 JSR a +21 AND (z,X) +22 X KIL +23 U RLA (z,X) +24 BIT z +25 AND z +26 ROL z +27 U RLA z +28 PLP +29 AND #n +2A ROL A +2B U AAC #n +2C BIT a +2D AND a +2E ROL a +2F U RLA abs +30 BMI r +31 AND (z),Y +32 X KIL +33 U RLA (z),Y +34 U DOP z,X +35 AND z,X +36 ROL z,X +37 U RLA z,X +38 SEC +39 AND a,Y +3A U NOP +3B U RLA abs,Y +3C U TOP abs,X +3D AND a,X +3E ROL a,X +3F U RLA abs,X +40 RTI +41 EOR (z,X) +42 X KIL +43 U SRE (z,X) +44 U DOP z +45 EOR z +46 LSR z +47 U SRE z +48 PHA +49 EOR #n +4A LSR A +4B U ASR #n +4C JMP a +4D EOR a +4E LSR a +4F U SRE abs +50 BVC r +51 EOR (z),Y +52 X KIL +53 U SRE (z),Y +54 U DOP z,X +55 EOR z,X +56 LSR z,X +57 U SRE z,X +58 CLI +59 EOR a,Y +5A U NOP +5B U SRE abs,Y +5C U TOP abs,X +5D EOR a,X +5E LSR a,X +5F U SRE abs,X +60 RTS +61 ADC (z,X) +62 X KIL +63 U RRA (z,X) +64 U DOP z +65 ADC z +66 ROR z +67 U RRA z +68 PLA +69 ADC #n +6A ROR A +6B U ARR #n +6C JMP (a) +6D ADC a +6E ROR a +6F U RRA abs +70 BVS r +71 ADC (z),Y +72 X KIL +73 U RRA (z),Y +74 U DOP z,X +75 ADC z,X +76 ROR z,X +77 U RRA z,X +78 SEI +79 ADC a,Y +7A U NOP +7B U RRA abs,Y +7C U TOP abs,X +7D ADC a,X +7E ROR a,X +7F U RRA abs,X +80 U DOP #n +81 STA (z,X) +82 U DOP #n +83 U AAX (z,X) +84 STY z +85 STA z +86 STX z +87 U AAX z +88 DEY +89 U DOP #n +8A TXA +8B ? XAA #n +8C STY a +8D STA a +8E STX a +8F U AAX abs +90 BCC r +91 STA (z),Y +92 X KIL +93 ? AXA (z),Y +94 STY z,X +95 STA z,X +96 STX z,Y +97 U AAX z,Y +98 TYA +99 STA a,Y +9A TXS +9B ? XAS abs,Y +9C U SYA abs,X +9D STA a,X +9E U SXA abs,Y +9F ? AXA abs,Y +A0 LDY #n +A1 LDA (z,X) +A2 LDX #n +A3 U LAX (z,X) +A4 LDY z +A5 LDA z +A6 LDX z +A7 U LAX z +A8 TAY +A9 LDA #n +AA TAX +AB U ATX #n +AC LDY a +AD LDA a +AE LDX a +AF U LAX abs +B0 BCS r +B1 LDA (z),Y +B2 X KIL +B3 U LAX (z),Y +B4 LDY z,X +B5 LDA z,X +B6 LDX z,Y +B7 U LAX z,Y +B8 CLV +B9 LDA a,Y +BA TSX +BB ? LAR abs,Y +BC LDY a,X +BD LDA a,X +BE LDX a,Y +BF U LAX abs,Y +C0 CPY #n +C1 CMP (z,X) +C2 U DOP #n +C3 U DCP (z,X) +C4 CPY z +C5 CMP z +C6 DEC z +C7 U DCP z +C8 INY +C9 CMP #n +CA DEX +CB U AXS #n +CC CPY a +CD CMP a +CE DEC a +CF U DCP abs +D0 BNE r +D1 CMP (z),Y +D2 X KIL +D3 U DCP (z),Y +D4 U DOP z,X +D5 CMP z,X +D6 DEC z,X +D7 U DCP z,X +D8 CLD +D9 CMP a,Y +DA U NOP +DB U DCP abs,Y +DC U TOP abs,X +DD CMP a,X +DE DEC a,X +DF U DCP abs,X +E0 CPX #n +E1 SBC (z,X) +E2 U DOP #n +E3 U ISC (z,X) +E4 CPX z +E5 SBC z +E6 INC z +E7 U ISC z +E8 INX +E9 SBC #n +EA NOP +EB U SBC #n +EC CPX a +ED SBC a +EE INC a +EF U ISC abs +F0 BEQ r +F1 SBC (z),Y +F2 X KIL +F3 U ISC (z),Y +F4 U DOP z,X +F5 SBC z,X +F6 INC z,X +F7 U ISC z,X +F8 SED +F9 SBC a,Y +FA U NOP +FB U ISC abs,Y +FC U TOP abs,X +FD SBC a,X +FE INC a,X +FF U ISC abs,X + + +Multi-tests +----------- +The NES/NSF builds in the main directory consist of multiple sub-tests. +When run, they list the subtests as they are run. The final result code +refers to the first sub-test that failed. For more information about any +failed subtests, run them individually from nes_singles/ and +nsf_singles/. + + +Multi-tests +----------- +The NES/NSF builds in the main directory consist of multiple sub-tests. +When run, they list the subtests as they are run. The final result code +refers to the first sub-test that failed. For more information about any +failed subtests, run them individually from nes_singles/ and +nsf_singles/. + + +Flashes, clicks, other glitches +------------------------------- +Some tests might need to turn the screen off and on, or cause slight +audio clicks. This does not indicate failure, and should be ignored. +Only the test result reported at the end is important, unless stated +otherwise. + + +Text output +----------- +Tests generally print information on screen. They also output the same +text as a zero-terminted string beginning at $6004, allowing examination +of output in an NSF player, or a NES emulator without a working PPU. The +tests also work properly if the PPU doesn't set the VBL flag properly or +doesn't implement it at all. + +The final result is displayed and also written to $6000. Before the test +starts, $80 is written there so you can tell when it's done. If a test +needs the NES to be reset, it writes $81 there. In addition, $DE $B0 $G1 +is written to $6001-$6003 to allow an emulator to detect when a test is +being run, as opposed to some other NES program. In NSF builds, the +final result is also reported via a series of beeps (see below). + +See the source code for more information about a particular test and why +it might be failing. Each test has comments and correct output at the +top. + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to avoid the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +In addition to the other text output methods described above, NSF builds +report essential information bytes audibly, including the final result. +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason as listed in the +source code by the corresponding set_code line. Examples: + +Tones Binary Decimal Meaning +- - - - - - - - - - - - - - - - - - - - +low 0 0 passed +low high 01 1 failed +low high low 010 2 error 2 + +-- +Shay Green diff --git a/nes_instr_test/rom_singles/01-implied.nes b/nes_instr_test/rom_singles/01-implied.nes new file mode 100644 index 0000000..38211b4 Binary files /dev/null and b/nes_instr_test/rom_singles/01-implied.nes differ diff --git a/nes_instr_test/rom_singles/02-immediate.nes b/nes_instr_test/rom_singles/02-immediate.nes new file mode 100644 index 0000000..5009154 Binary files /dev/null and b/nes_instr_test/rom_singles/02-immediate.nes differ diff --git a/nes_instr_test/rom_singles/03-zero_page.nes b/nes_instr_test/rom_singles/03-zero_page.nes new file mode 100644 index 0000000..c6c66d0 Binary files /dev/null and b/nes_instr_test/rom_singles/03-zero_page.nes differ diff --git a/nes_instr_test/rom_singles/04-zp_xy.nes b/nes_instr_test/rom_singles/04-zp_xy.nes new file mode 100644 index 0000000..c875f36 Binary files /dev/null and b/nes_instr_test/rom_singles/04-zp_xy.nes differ diff --git a/nes_instr_test/rom_singles/05-absolute.nes b/nes_instr_test/rom_singles/05-absolute.nes new file mode 100644 index 0000000..ecac8f1 Binary files /dev/null and b/nes_instr_test/rom_singles/05-absolute.nes differ diff --git a/nes_instr_test/rom_singles/06-abs_xy.nes b/nes_instr_test/rom_singles/06-abs_xy.nes new file mode 100644 index 0000000..3438592 Binary files /dev/null and b/nes_instr_test/rom_singles/06-abs_xy.nes differ diff --git a/nes_instr_test/rom_singles/07-ind_x.nes b/nes_instr_test/rom_singles/07-ind_x.nes new file mode 100644 index 0000000..6ef806e Binary files /dev/null and b/nes_instr_test/rom_singles/07-ind_x.nes differ diff --git a/nes_instr_test/rom_singles/08-ind_y.nes b/nes_instr_test/rom_singles/08-ind_y.nes new file mode 100644 index 0000000..ca3bccb Binary files /dev/null and b/nes_instr_test/rom_singles/08-ind_y.nes differ diff --git a/nes_instr_test/rom_singles/09-branches.nes b/nes_instr_test/rom_singles/09-branches.nes new file mode 100644 index 0000000..142bed9 Binary files /dev/null and b/nes_instr_test/rom_singles/09-branches.nes differ diff --git a/nes_instr_test/rom_singles/10-stack.nes b/nes_instr_test/rom_singles/10-stack.nes new file mode 100644 index 0000000..76b91cd Binary files /dev/null and b/nes_instr_test/rom_singles/10-stack.nes differ diff --git a/nes_instr_test/rom_singles/11-special.nes b/nes_instr_test/rom_singles/11-special.nes new file mode 100644 index 0000000..dc71716 Binary files /dev/null and b/nes_instr_test/rom_singles/11-special.nes differ diff --git a/nes_instr_test/source/01-implied.s b/nes_instr_test/source/01-implied.s new file mode 100644 index 0000000..0e9254f --- /dev/null +++ b/nes_instr_test/source/01-implied.s @@ -0,0 +1,81 @@ +;CALIBRATE=1 +.include "instr_test.inc" +instrs: + entry $2A,"ROL A" ; A = op A + entry $0A,"ASL A" + entry $6A,"ROR A" + entry $4A,"LSR A" + + entry $8A,"TXA" ; AXY = AXY + entry $98,"TYA" + entry $AA,"TAX" + entry $A8,"TAY" + + entry $E8,"INX" ; XY = op XY + entry $C8,"INY" + entry $CA,"DEX" + entry $88,"DEY" + + entry $38,"SEC" ; flags = op flags + entry $18,"CLC" + entry $F8,"SED" + entry $D8,"CLD" + entry $78,"SEI" + entry $58,"CLI" + entry $B8,"CLV" + + entry $EA,"NOP" + +.ifndef OFFICIAL_ONLY + entry $1A,"NOP" + entry $3A,"NOP" + entry $5A,"NOP" + entry $7A,"NOP" + entry $DA,"NOP" + entry $FA,"NOP" +.endif +instrs_size = * - instrs + +instr_template: + nop + jmp instr_done +instr_template_size = * - instr_template + +operand = in_a + +.define set_in set_paxyso +.define check_out check_paxyso + +.include "instr_test_end.s" + +test_values: + test_normal + rts + +correct_checksums: +.dword $013A2933 +.dword $A38733B0 +.dword $6EC2BCA6 +.dword $763FEBC5 +.dword $0FF1C1E6 +.dword $5B2EB5B7 +.dword $1D8ACEF5 +.dword $83DC03F9 +.dword $8EBDF63B +.dword $F34CAA18 +.dword $9123FF08 +.dword $48897445 +.dword $4BE14840 +.dword $E7C7ECC0 +.dword $408EF097 +.dword $A6AEF749 +.dword $8F06AD7B +.dword $FC96AE14 +.dword $28F10ADA +.dword $CA7E6620 +.dword $CA7E6620 +.dword $CA7E6620 +.dword $CA7E6620 +.dword $CA7E6620 +.dword $CA7E6620 +.dword $CA7E6620 diff --git a/nes_instr_test/source/02-immediate.s b/nes_instr_test/source/02-immediate.s new file mode 100644 index 0000000..eabe937 --- /dev/null +++ b/nes_instr_test/source/02-immediate.s @@ -0,0 +1,75 @@ +;CALIBRATE=1 +.include "instr_test.inc" + +instrs: + entry $A9,"LDA #n" ; AXY = #n + entry $A2,"LDX #n" + entry $A0,"LDY #n" + + entry $69,"ADC #n" ; A = A op #n + entry $E9,"SBC #n" + entry $09,"ORA #n" + entry $29,"AND #n" + entry $49,"EOR #n" + + entry $C9,"CMP #n" ; AXY op #n + entry $E0,"CPX #n" + entry $C0,"CPY #n" +.ifndef OFFICIAL_ONLY + entry $EB,"SBC #n" + + entry $80,"DOP #n" + entry $82,"DOP #n" + entry $89,"DOP #n" + entry $C2,"DOP #n" + entry $E2,"DOP #n" + + entry $0B,"AAC #n" + entry $2B,"AAC #n" + entry $4B,"ASR #n" + entry $6B,"ARR #n" + entry $AB,"ATX #n" + entry $CB,"AXS #n" +.endif +instrs_size = * - instrs + +operand = instr+1 + +instr_template: + lda #0 + jmp instr_done +instr_template_size = * - instr_template + +.define set_in set_paxyso +.define check_out check_paxyso + +.include "instr_test_end.s" + +test_values: + test_normal + rts + +correct_checksums: +.dword $AB3E4F82 +.dword $7B121231 +.dword $E544DF3D +.dword $86046BF5 +.dword $999E9E48 +.dword $DC562F7E +.dword $6BF08A00 +.dword $D2A32FD6 +.dword $7DF1D50B +.dword $751D8339 +.dword $A451BD7A +.dword $999E9E48 +.dword $ACE6BAE4 +.dword $ACE6BAE4 +.dword $ACE6BAE4 +.dword $ACE6BAE4 +.dword $ACE6BAE4 +.dword $DC37BE89 +.dword $DC37BE89 +.dword $C07C3593 +.dword $49618FA8 +.dword $1D8ACEF5 +.dword $1240499F diff --git a/nes_instr_test/source/03-zero_page.s b/nes_instr_test/source/03-zero_page.s new file mode 100644 index 0000000..2455c89 --- /dev/null +++ b/nes_instr_test/source/03-zero_page.s @@ -0,0 +1,94 @@ +;CALIBRATE=1 +.include "instr_test.inc" + +instrs: + entry $A5,"LDA z" ; AXY = z + entry $A6,"LDX z" + entry $A4,"LDY z" + + entry $85,"STA z" ; z = AXY + entry $86,"STX z" + entry $84,"STY z" + + entry $E6,"INC z" ; z = op z + entry $C6,"DEC z" + entry $06,"ASL z" + entry $46,"LSR z" + entry $26,"ROL z" + entry $66,"ROR z" + + entry $65,"ADC z" ; A = A op z + entry $E5,"SBC z" + entry $05,"ORA z" + entry $25,"AND z" + entry $45,"EOR z" + + entry $24,"BIT z" ; AXY op z + entry $C5,"CMP z" + entry $E4,"CPX z" + entry $C4,"CPY z" +.ifndef OFFICIAL_ONLY + entry $04,"DOP z" + entry $44,"DOP z" + entry $64,"DOP z" + + entry $07,"SLO z" + entry $27,"RLA z" + entry $47,"SRE z" + entry $67,"RRA z" + entry $87,"AAX z" + entry $A7,"LAX z" + entry $C7,"DCP z" + entry $E7,"ISC z" +.endif +instrs_size = * - instrs + +operand = <$FE + +instr_template: + lda operand + sta <(address+1) + + lda #<(operand+1) + sta <(address+2) + lda #>(operand+1) + sta <(address+3) + + lda #0 + jsr :+ + lda #2 +: sta in_x + test_normal + rts + +correct_checksums: +.dword $B9D16BC6 +.dword $DBC21F73 +.dword $84827E50 +.dword $FE9A8B04 +.dword $9EEFAAD8 +.dword $65F6C5BB +.dword $82C41B16 +.dword $DC68A9E8 +.dword $04A09668 +.dword $417BDD05 +.dword $9A40C4E4 +.dword $0CB8C16E +.dword $EC8492F8 +.dword $AFC77201 +.dword $5BFDAB74 +.dword $C62D3147 diff --git a/nes_instr_test/source/08-ind_y.s b/nes_instr_test/source/08-ind_y.s new file mode 100644 index 0000000..1b0edda --- /dev/null +++ b/nes_instr_test/source/08-ind_y.s @@ -0,0 +1,88 @@ +;CALIBRATE=1 +.include "instr_test.inc" + +instrs: + entry $B1,"LDA (z),Y" ; A = (z),Y + + entry $91,"STA (z),Y" ; (z),Y = A + + entry $D1,"CMP (z),Y" ; A op (z),Y + + entry $11,"ORA (z),Y" ; A = A op (z),Y + entry $F1,"SBC (z),Y" + entry $71,"ADC (z),Y" + entry $31,"AND (z),Y" + entry $51,"EOR (z),Y" +.ifndef OFFICIAL_ONLY + entry $13,"SLO (z),Y" + entry $33,"RLA (z),Y" + entry $53,"SRE (z),Y" + entry $73,"RRA (z),Y" + entry $B3,"LAX (z),Y" + entry $D3,"DCP (z),Y" + entry $F3,"ISC (z),Y" +.endif +instrs_size = * - instrs + +address = <$FF +operand = $2FF + +instr_template: + lda (address),y + jmp instr_done +instr_template_size = * - instr_template + +.macro set_in + lda values+1,y + sta operand+1 + + lda values+2,y + sta operand+2 + + set_paxyso +.endmacro + +.macro check_out + check_paxyso + + lda operand+1 + jsr update_crc_fast + + lda operand+2 + jsr update_crc_fast + + lda address + jsr update_crc_fast +.endmacro + +.include "instr_test_end.s" + +test_values: + lda #operand + sta <(address+1) + + lda #0 + jsr :+ + lda #1 +: sta in_y + test_normal + rts + +correct_checksums: +.dword $A70CC617 +.dword $C51FB2A2 +.dword $9A5FD381 +.dword $7B2B686A +.dword $80320709 +.dword $E04726D5 +.dword $9C19B6C7 +.dword $C2B50439 +.dword $1A7D3BB9 +.dword $5FA670D4 +.dword $849D6935 +.dword $12656CBF +.dword $964F3A4A +.dword $452006A5 +.dword $D8F09C96 diff --git a/nes_instr_test/source/09-branches.s b/nes_instr_test/source/09-branches.s new file mode 100644 index 0000000..2fcd4f1 --- /dev/null +++ b/nes_instr_test/source/09-branches.s @@ -0,0 +1,48 @@ +;CALIBRATE=1 +.include "instr_test.inc" + +instrs: + entry $90,"BCC r" ; PC = PC op flags + entry $50,"BVC r" + entry $D0,"BNE r" + entry $30,"BMI r" + entry $10,"BPL r" + entry $F0,"BEQ r" + entry $B0,"BCS r" + entry $70,"BVS r" +instrs_size = * - instrs + +zp_byte operand + +instr_template: + bne :+ + sta operand +: jmp instr_done +instr_template_size = * - instr_template + +values2: + .byte 0,$FF,$01,$02,$04,$08,$10,$20,$40,$80 +values2_size = * - values2 + +.macro set_in + sta in_p + set_paxyso +.endmacro + +.define check_out check_paxyso + +.include "instr_test_end.s" + +test_values: + test_normal + rts + +correct_checksums: +.dword $D5B22EE0 +.dword $9CEB0A7E +.dword $DE8DEFE5 +.dword $D704F89C +.dword $AD3F7EB0 +.dword $A4B669C9 +.dword $AF89A8CC +.dword $E6D08C52 diff --git a/nes_instr_test/source/10-stack.s b/nes_instr_test/source/10-stack.s new file mode 100644 index 0000000..219f1ae --- /dev/null +++ b/nes_instr_test/source/10-stack.s @@ -0,0 +1,118 @@ +;CALIBRATE=1 +.include "instr_test.inc" + +instrs: + entry $48,"PHA" + entry $08,"PHP" + + entry $68,"PLA" + entry $28,"PLP" + + entry $9A,"TXS" + entry $BA,"TSX" +instrs_size = * - instrs + +instr_template: + pha + jmp instr_done +instr_template_size = * - instr_template + +values2: + .byte 0,$FF,$01,$02,$04,$08,$10,$20,$40,$80 +values2_size = * - values2 + +zp_byte operand + +.macro set_in + sta in_p + set_paxyso + + ; Clear bytes on stack + stx $17F + sty $180 + stx $181 + + sty $1FE + stx $1FF + sty $100 + stx $101 + sty $102 +.endmacro + +zp_byte save +zp_byte save2 +zp_byte save3 +zp_byte save4 +zp_byte save5 + +.macro check_out + php + sta save ; A + pla + sta save2 ; P + pla + sta save3 ; PLA + stx save4 ; X + tsx + stx save5 ; S + + ldx saved_s + txs + + ; Output + tya + jsr update_crc_fast + + lda save + jsr update_crc_fast + + lda save2 + jsr update_crc_fast + + lda save3 + jsr update_crc_fast + + lda save4 + jsr update_crc_fast + + lda save5 + jsr update_crc_fast + + ldx in_s + dex + lda $100,x + jsr update_crc_fast + + inx + lda $100,x + jsr update_crc_fast + + inx + lda $100,x + jsr update_crc_fast +.endmacro + +.include "instr_test_end.s" + +test_values: + ; Values for SP + lda #$80 + jsr :+ + lda #$00 + jsr :+ + lda #$01 + jsr :+ + lda #$FF + jsr :+ + lda #$FE +: sta in_s + test_normal + rts + +correct_checksums: +.dword $AA53E72F +.dword $F46D6C3F +.dword $4B0D5E27 +.dword $A1AB7B53 +.dword $8A5B86A7 +.dword $6157E3AF diff --git a/nes_instr_test/source/11-special.s b/nes_instr_test/source/11-special.s new file mode 100644 index 0000000..dc3534e --- /dev/null +++ b/nes_instr_test/source/11-special.s @@ -0,0 +1,90 @@ +CUSTOM_IRQ=1 +.include "shell.inc" + +irq: pla + pha + rti + +jmp_6ff: + .byte $6C ; JMP ($6FF) (to avoid warning) + .word $6FF + +main: + setb SNDMODE,$40 ; disable frame IRQ + + set_test 3,"JMP ($6FF) should get high byte from $600" + setb $6FF,$F0 + setb $600,$07 + setb $700,$06 + setb $7F0,$E8 ; INX + setb $7F1,$60 ; RTS + setb $6F0,$60 ; RTS + ldx #0 + jsr jmp_6ff + cpx #1 + jne test_failed + + set_test 4,"RTS should return to addr+1" + lda #>:+ + pha + lda #<:+ + pha + ldx #0 + rts + inx +: inx + inx + cpx #1 + jne test_failed + + set_test 5,"RTI should return to addr" + lda #>:+ + pha + lda #<:+ + pha + ldx #0 + php + rti + inx +: inx + inx + cpx #2 + jne test_failed + + set_test 6,"JSR should push addr of next instr - 1" + setb $6FE,$20 ; JSR + setb $6FF,<:+ + setb $700,>:+ + jmp $6FE +: pla + cmp #$00 + jne test_failed + pla + cmp #$07 + jne test_failed + + set_test 7,"BRK should push status with bits 4 and 5 set" + lda #$00 + pha + plp + brk + nop + cmp #$30 + jne test_failed + lda #$FF + pha + plp + brk + nop + cmp #$FF + jne test_failed + + set_test 8,"BRK should push address BRK + 2" + ldx #1 + brk + inx + inx + cpx #2 + jne test_failed + + jmp tests_passed diff --git a/nes_instr_test/source/common/ascii.chr b/nes_instr_test/source/common/ascii.chr new file mode 100644 index 0000000..a3514f4 Binary files /dev/null and b/nes_instr_test/source/common/ascii.chr differ diff --git a/nes_instr_test/source/common/build_nsf.s b/nes_instr_test/source/common/build_nsf.s new file mode 100644 index 0000000..9bea408 --- /dev/null +++ b/nes_instr_test/source/common/build_nsf.s @@ -0,0 +1,135 @@ +; Builds program as NSF music file + +.ifndef NSF_SONG_COUNT + NSF_SONG_COUNT = 1 +.endif + +.ifndef CUSTOM_NSF_HEADER + .segment "HEADER" + .byte "NESM",26,1 + .byte NSF_SONG_COUNT + .byte 1 ; start with first song + .word load_addr,reset,nsf_play +.endif + +.segment "DMC" + load_addr: + +.ifndef CUSTOM_NSF_VECTORS + .segment "VECTORS" + .word 0,0,0,nmi,internal_error,irq +.endif + +.include "shell.s" + +nv_res nsf_track,1 +nv_res nsf_running,1 + +.ifdef EMPTY_NSF_PLAY + CUSTOM_NSF_PLAY = 1 + nsf_play: + rts +.endif + +std_reset: + sta nsf_track + + ; In case NSF player interrupts init with play, + ; wait a while in init. If play interrupts, then + ; we just use that play call to run program, and + ; this is never resumed. + setb nsf_running,1 + delay_msec 500 + +.ifndef CUSTOM_NSF_PLAY + nsf_play: +.endif +std_nsf_play: + php + bit nsf_running + bpl :+ + plp + rts + +: lda nsf_running + beq :+ + + ; First call + setb nsf_running,0 + delay_msec 20 + jmp run_shell + + ; Player let init run too long before interrupting + ; with play, or play call interrupted itself. +: setb nsf_running,$80 + jsr clear_ram + jsr init_shell + print_str "NSF player called play recursively" + jmp internal_error + + +init_runtime: + rts + +post_exit: +forever: + jmp forever + +; Reports A in binary as high and low tones, with +; leading low tone for reference. Omits leading +; zeroes. +; Preserved: A, X, Y +play_byte: + pha + + ; Make low reference beep + clc + jsr @beep + + ; Remove high zero bits + sec +: rol a + bcc :- + + ; Play remaining bits + beq @zero +: jsr @beep + asl a + bne :- +@zero: + + delay_msec 300 + pla + rts + +; Plays low/high beep based on carry +; Preserved: A, X, Y +@beep: + pha + + ; Set up square + lda #1 + sta SNDCHN + sta $4001 + sta $4003 + adc #$FE ; period=$100 if carry, $1FF if none + sta $4002 + + ; Fade volume + lda #$0F +: ora #$30 + sta $4000 + delay_msec 8 + sec + sbc #$31 + bpl :- + + ; Silence + sta SNDCHN + delay_msec 160 + + pla + rts + +; User code goes in main code segment +.code diff --git a/nes_instr_test/source/common/build_rom.s b/nes_instr_test/source/common/build_rom.s new file mode 100644 index 0000000..c0236c4 --- /dev/null +++ b/nes_instr_test/source/common/build_rom.s @@ -0,0 +1,87 @@ +; Builds program as iNES ROM + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM=1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM=1 ; Use mapper that supports 8K WRAM in cart +CUSTOM_MAPPER=n ; Specify mapper number +.endif + +.ifndef CUSTOM_MAPPER + .ifdef CART_WRAM + CUSTOM_MAPPER = 2 ; UNROM + .else + CUSTOM_MAPPER = 0 ; NROM + .endif +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte "NES",26 + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte CUSTOM_MAPPER*$10+$01 ; vertical mirroring +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word 0,0,0, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .rodata + ascii_chr: + .incbin "ascii.chr" + ascii_chr_end: +.else + .segment "CHARS" + .incbin "ascii.chr" + .align $2000 +.endif + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + ; Load ASCII font into CHR RAM + ldy #0 + sty PPUADDR + sty PPUADDR + ldx #ascii_chr + @page: + stx addr+1 + : lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page + .endif + rts + +post_exit: + jsr clear_nvram + jmp forever + +; User code goes in main code segment +.code diff --git a/nes_instr_test/source/common/console.s b/nes_instr_test/source/common/console.s new file mode 100644 index 0000000..65f3b11 --- /dev/null +++ b/nes_instr_test/source/common/console.s @@ -0,0 +1,222 @@ +; Scrolling text console with line wrapping, 30x29 characters. +; Buffers lines for speed. Will work even if PPU doesn't +; support scrolling (until text reaches bottom). Keeps border +; along bottom in case TV cuts it off. +; +; Defers most initialization until first newline, at which +; point it clears nametable and makes palette non-black. +; +; ** ASCII font must already be in CHR, and mirroring +; must be vertical or single-screen. + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs +console_margin = 1 + +console_buf_size = 32 +console_width = console_buf_size - (console_margin*2) + +zp_byte console_pos +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_buf_size + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + jmp console_clear_line_ + + +; Hides console by blacking palette and disabling PPU. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +console_wait_vbl_: + lda console_scroll + cmp #-1 + jne wait_vbl_optional + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + txa + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldx #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + + ; Clear attributes + lda #0 + ldx #$40 +: sta PPUDATA + dex + bne :- + + pla + tax + jmp console_show + + +; Shows console display +; Preserved: X, Y +console_show: + pha + + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + + lda #$30 ; white + jsr console_load_palette_ + + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + stx console_temp + + ; Newline if buf full and next char isn't space + ldx console_pos + bpl :+ + cmp #' ' + beq @ignore_space + ldx console_temp + jsr console_newline + stx console_temp + ldx console_pos +: + ; Write to buffer + sta console_buf+console_margin,x + dex + stx console_pos + +@ignore_space: + ldx console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_clear_line_ + + ; Scroll up 8 pixels and clear one line AHEAD + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + jsr console_add_8_to_scroll_ + jsr console_flush_a + jmp console_apply_scroll_ + + +; A = (A + 8) % 240 +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_clear_line_: + stx console_temp + + ; Start new clear line + lda #' ' + ldx #console_buf_size-1 +: sta console_buf,x + dex + bpl :- + ldx #console_width-1 + stx console_pos + + ldx console_temp + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + + pla + rts + +console_flush_: + lda console_scroll +console_flush_a: + ; Address line in nametable + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + sta PPUADDR + + ; Copy line + stx console_temp + ldx #console_buf_size-1 +: lda console_buf,x + sta PPUDATA + dex + bpl :- + ldx console_temp + + rts + diff --git a/nes_instr_test/source/common/crc.s b/nes_instr_test/source/common/crc.s new file mode 100644 index 0000000..797204c --- /dev/null +++ b/nes_instr_test/source/common/crc.s @@ -0,0 +1,87 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on diff --git a/nes_instr_test/source/common/crc_fast.s b/nes_instr_test/source/common/crc_fast.s new file mode 100644 index 0000000..5e1ffb4 --- /dev/null +++ b/nes_instr_test/source/common/crc_fast.s @@ -0,0 +1,65 @@ +; Fast table-based CRC-32 + +; Uses 1K of RAM +checksum_t0 = $400 +checksum_t1 = $500 +checksum_t2 = $600 +checksum_t3 = $700 + +; Initializes fast CRC tables and resets checksum. +; Preserved: Y +; Time: ~60 msec +init_crc_fast: + ldx #0 +@byte: ; Calculate CRC for this byte + lda #0 + sta checksum+3 + sta checksum+2 + sta checksum+1 + stx checksum + jsr update_crc_ + + ; Write in table + lda checksum + sta checksum_t0,x + lda checksum+1 + sta checksum_t1,x + lda checksum+2 + sta checksum_t2,x + lda checksum+3 + sta checksum_t3,x + + inx + bne @byte + + jmp reset_crc + + +; Updates checksum with byte from A +; Preserved: X, Y +; Time: 54 clocks +update_crc_fast: + stx checksum_temp + +; Updates checksum with byte from A +; Preserved: Y +; Time: 42 clocks +.macro update_crc_fast + eor checksum + tax + lda checksum+1 + eor checksum_t0,x + sta checksum + lda checksum+2 + eor checksum_t1,x + sta checksum+1 + lda checksum+3 + eor checksum_t2,x + sta checksum+2 + lda checksum_t3,x + sta checksum+3 +.endmacro + + update_crc_fast + ldx checksum_temp + rts diff --git a/nes_instr_test/source/common/delay.s b/nes_instr_test/source/common/delay.s new file mode 100644 index 0000000..7e0acc0 --- /dev/null +++ b/nes_instr_test/source/common/delay.s @@ -0,0 +1,191 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100 usec. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/nes_instr_test/source/common/instr_test.inc b/nes_instr_test/source/common/instr_test.inc new file mode 100644 index 0000000..5082938 --- /dev/null +++ b/nes_instr_test/source/common/instr_test.inc @@ -0,0 +1,51 @@ +.include "shell.inc" +.include "crc_fast.s" + +; Instructions to test +.macro entry op,name + .byte op,0 + .local Addr + .word Addr + seg_data "STRINGS",{Addr: .byte name,0} +.endmacro + +zp_byte in_p +zp_byte in_a +zp_byte in_x +zp_byte in_y +zp_byte in_s + +values: + .byte 0,1,2,$40,$7F,$80,$81,$FF +values_size = * - values + .byte 0,1,2,$40,$7F,$80,$81,$FF + +.macro set_paxyso + ldx in_s + txs + lda values,y + sta operand + lda in_p + pha + lda in_a + ldx in_x + ldy in_y + plp +.endmacro + +.macro check_paxyso + php + cld ; limits effect of buggy emulator + jsr update_crc_fast + pla + jsr update_crc_fast + txa + jsr update_crc_fast + tya + jsr update_crc_fast + tsx + txa + jsr update_crc_fast + lda operand + jsr update_crc_fast +.endmacro diff --git a/nes_instr_test/source/common/instr_test_end.s b/nes_instr_test/source/common/instr_test_end.s new file mode 100644 index 0000000..4ad5371 --- /dev/null +++ b/nes_instr_test/source/common/instr_test_end.s @@ -0,0 +1,180 @@ +zp_byte instrs_idx +zp_byte failed_count + +main: ldx #$A2 + txs + + jsr init_crc_fast + + ; Test each instruction + lda #0 +@loop: sta instrs_idx + tay + + jsr reset_crc + lda instrs,y + jsr test_instr + jsr check_result + + lda instrs_idx + clc + adc #4 + cmp #instrs_size + bne @loop + + lda failed_count + jne test_failed + jmp tests_passed + +; Check result of test +check_result: +.ifdef CALIBRATE + ; Print correct CRC + jsr crc_off + print_str ".dword $" + ldx #0 +: lda checksum,x + jsr print_hex + inx + cpx #4 + bne :- + jsr print_newline + jsr crc_on +.else + ; Verify CRC + ldx #3 + ldy instrs_idx +: lda checksum,x + cmp correct_checksums,y + bne @wrong + iny + dex + bpl :- +.endif + rts + +; Print failed opcode and name +@wrong: + ldy instrs_idx + lda instrs,y + jsr print_a + jsr play_byte + lda instrs+2,y + sta addr + lda instrs+3,y + sta addr+1 + jsr print_str_addr + jsr print_newline + inc failed_count + rts + +bss_res instr,instr_template_size + +; Tests instr A +test_instr: + sta instr + jsr avoid_silent_nsf + + ; Copy rest of template + ldx #instr_template_size - 1 +: lda instr_template,x + sta instr,x + dex + bne :- + + ; Disable and clear frame IRQ + lda #$40 + sta SNDMODE + + ; Default stack + lda #$90 + sta in_s + + ; Test with different flags + lda #$00 + jsr test_flags + lda #$FF + jsr test_flags + + rts + +zp_byte operand_idx + +test_flags: + sta in_p + + ldy #values_size-1 +: sty operand_idx + + lda values,y + sta in_a + + lda values+1,y + sta in_x + + lda values+2,y + sta in_y + + jsr test_values + + ldy operand_idx + dey + bpl :- + + rts + +.ifndef values2 + values2 = values + values2_size = values_size +.endif + +.macro test_normal +zp_byte a_idx +zp_byte saved_s + + tsx + stx saved_s + + ldy #values2_size-1 +inner: sty a_idx + + lda values2,y + sta operand + + set_in + +.if 0 + ; P A X Y S O (z,x) (z),y + jsr print_p + jsr print_a + jsr print_x + jsr print_y + jsr print_s + lda operand + jsr print_a +.ifdef address + lda (address,x) + jsr print_a + lda (address),y + jsr print_a +.else + lda operand,x + jsr print_a + lda operand,y + jsr print_a +.endif + jsr print_newline +.endif + + jmp instr +instr_done: + + check_out + + ldy a_idx + dey + bpl inner + + ldx saved_s + txs +.endmacro diff --git a/nes_instr_test/source/common/macros.inc b/nes_instr_test/source/common/macros.inc new file mode 100644 index 0000000..bd74554 --- /dev/null +++ b/nes_instr_test/source/common/macros.inc @@ -0,0 +1,135 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "STRINGS",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro diff --git a/nes_instr_test/source/common/neshw.inc b/nes_instr_test/source/common/neshw.inc new file mode 100644 index 0000000..20bf574 --- /dev/null +++ b/nes_instr_test/source/common/neshw.inc @@ -0,0 +1,37 @@ +; NES I/O locations and masks + +; Clocks per second +.ifndef CLOCK_RATE + CLOCK_RATE = 1789773 ; NTSC +; CLOCK_RATE = 1662607 ; PAL +.endif + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 diff --git a/nes_instr_test/source/common/ppu.s b/nes_instr_test/source/common/ppu.s new file mode 100644 index 0000000..28d616d --- /dev/null +++ b/nes_instr_test/source/common/ppu.s @@ -0,0 +1,141 @@ +; PPU utilities + +; Special tile images +tile_blank = 0 +tile_color1 = 1 +tile_color2 = 2 +tile_solid = 3 +tile_upper_left = 4 +tile_upper_right = 5 +tile_lower_left = 6 +tile_lower_right = 7 +tile_upper_left_lower_right = 8 + + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + lda #0 + jsr fill_screen + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts diff --git a/nes_instr_test/source/common/print.s b/nes_instr_test/source/common/print.s new file mode 100644 index 0000000..ff28609 --- /dev/null +++ b/nes_instr_test/source/common/print.s @@ -0,0 +1,207 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr @nibble + pla + + pha + and #$0F + jsr @nibble + pla + rts + +@nibble: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + .ifnblank str2 + jsr_with_addr print_str_addr,{.byte str,str2,0} + .else + jsr_with_addr print_str_addr,{.byte str,0} + .endif +.endmacro + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: Y +print_dec: + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jmp print_char + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/nes_instr_test/source/common/shell.inc b/nes_instr_test/source/common/shell.inc new file mode 100644 index 0000000..ce65f2d --- /dev/null +++ b/nes_instr_test/source/common/shell.inc @@ -0,0 +1,23 @@ +; Included at beginning of program + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/nes_instr_test/source/common/shell.s b/nes_instr_test/source/common/shell.s new file mode 100644 index 0000000..21954a2 --- /dev/null +++ b/nes_instr_test/source/common/shell.s @@ -0,0 +1,352 @@ +; Common routines and runtime + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +; Number of clocks delay before reset is jumped to +.ifndef DEVCART_DELAY + DEVCART_DELAY = 0 +.endif + +;**** Special globals **** + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E + +.segment "NVRAM" + ; Beginning of variables not cleared at startup + nvram_begin: + +;**** Code segment setup **** + +.ifndef NOT_FOR_DEVCART + ; Move code to $E000 + .segment "DMC" + .res $6000 +.endif + +; Devcart corrupts byte at $E000 when powering off +.code + nop + +; Goes after all other read-only data, to avoid trailing zeroes +; from getting stripped off by devcart loader. +.segment "ROEND" + .byte $FF + +; Cause support code to go AFTER user code, so that +; changes to support code don't move user code around. +.segment "CODE2" + + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "print.s" +.include "delay.s" +.include "crc.s" +.include "testing.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print +.endif + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_oam +.endif + + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sei + cld + ldx #$FF + txs + + .ifndef BUILD_NSF + ldx #0 + stx PPUCTRL + .endif + + jsr report_result + jmp post_exit + + +; Reports final result code in A +; Preserved: A +report_result: + pha + jsr :+ + jsr play_byte + pla + jmp set_final_result + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +; Clears $0-($100+S) and nv_ram_end-$7FF +clear_ram: + lda #0 + + ; Main pages + tax +: sta 0,x + sta $300,x + sta $400,x + sta $500,x + sta $600,x + sta $700,x + inx + bne :- + + ; Stack except that above stack pointer + tsx + inx +: dex + sta $100,x + bne :- + + ; BSS except nvram + ldx #<__NVRAM_SIZE__ +: sta __NVRAM_LOAD__,x + inx + bne :- + + rts + + +; Clears nvram +clear_nvram: + ldx #<__NVRAM_SIZE__ + beq @empty + lda #0 +: dex + sta __NVRAM_LOAD__,x + bne :- +@empty: + rts + + +; Prints filename and newline, if available, otherwise nothing. +; Preserved: A, X, Y +print_filename: + .ifdef FILENAME_KNOWN + pha + jsr print_newline + setw addr,filename + jsr print_str_addr + jsr print_newline + pla + .endif + rts + +.pushseg +.segment "STRINGS" + ; Filename terminated with zero byte. + filename: + .ifdef FILENAME_KNOWN + .incbin "ram:rom.nes" + .endif + .byte 0 +.popseg + + +;**** NSF-specific **** + +.ifdef BUILD_NSF +bss_res nsf_activity,1 + +; Call periodically where there would otherwise be silence, +; so that NSF build won't be cut short in NSF player that +; detects silence. +; Preserved: A, X, Y +avoid_silent_nsf: + pha + lda nsf_activity + clc + adc #4 + sta nsf_activity + sta $4011 + pla + rts + + +init_wait_vbl: +wait_vbl_optional: +;wait_vbl: ; undefined so assembler error results if called + rts + + +.ifndef CUSTOM_IRQ + irq: +.endif +nmi: + jmp internal_error + +;**** ROM-specific **** +.else ; .ifndef BUILD_NSF + +.include "ppu.s" + + +avoid_silent_nsf: +play_byte: + rts + + +; Disables interrupts and loops forever +.ifndef CUSTOM_FOREVER +forever: + sei + lda #0 + sta PPUCTRL +: beq :- +.endif + +; Default NMI +.ifndef CUSTOM_NMI + zp_byte nmi_count + + nmi: + inc nmi_count + rti + + ; Waits for NMI. Must be using NMI handler that increments + ; nmi_count, with NMI enabled. + ; Preserved: X, Y + wait_nmi: + lda nmi_count + : cmp nmi_count + beq :- + rts +.endif + + +; Default IRQ +.ifndef CUSTOM_IRQ + irq: + bit SNDCHN ; clear APU IRQ flag + rti +.endif + +.endif diff --git a/nes_instr_test/source/common/testing.s b/nes_instr_test/source/common/testing.s new file mode 100644 index 0000000..e654e09 --- /dev/null +++ b/nes_instr_test/source/common/testing.s @@ -0,0 +1,124 @@ +; Utilities for writing test ROMs + +; In NVRAM so these can be used before initializing runtime, +; then runtime initialized without clearing them +nv_res test_code ; code of current test +nv_res test_name,2 ; address of name of current test, or 0 of none + + +; Sets current test code and optional name. Also resets +; checksum. +; Preserved: A, X, Y +.macro set_test code,name + pha + lda #code + jsr set_test_ + .ifblank name + setb test_name+1,0 + .else + .local Addr + setw test_name,Addr + seg_data "STRINGS",{Addr: .byte name,0} + .endif + pla +.endmacro + +set_test_: + sta test_code + jmp reset_crc + + +; Initializes testing module +init_testing: + jmp init_crc + + +; Reports that all tests passed +tests_passed: +.ifndef BUILD_MULTI + jsr print_filename + print_str newline,"Passed" +.endif + lda #0 + jmp exit + + +; Reports "Done" if set_test has never been used, +; "Passed" if set_test 0 was last used, or +; failure if set_test n was last used. +tests_done: + ldx test_code + jeq tests_passed + inx + bne test_failed +.ifndef BUILD_MULTI + jsr print_filename + print_str newline,"Done" +.endif + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + ldx test_code + + ; Treat $FF as 1, in case it wasn't ever set + inx + bne :+ + inx + stx test_code +: + ; If code >= 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: +.ifndef BUILD_MULTI + jsr print_filename +.endif + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (addr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + + jsr reset_crc + pla + tay + pla + rts + +@wrong: jsr print_newline + jsr print_crc + jsr print_newline + jmp test_failed diff --git a/nes_instr_test/source/common/text_out.s b/nes_instr_test/source/common/text_out.s new file mode 100644 index 0000000..46ab8f1 --- /dev/null +++ b/nes_instr_test/source/common/text_out.s @@ -0,0 +1,82 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Executing from $6000 already + lda #>init_text_out + bpl :+ + + ; Disable if there's no RAM there + lda $6000 + eor #$FF + sta $6000 + cmp $6000 + bne :+ + + ; Disable if changes to $6000 affect $E000, + ; as on my devcart + ldy $E000 + eor #$FF + sta $6000 ; restore original data in RAM + cpy $E000 ; see if $E000 changed as well + bne :+ + + ; Terminate output string + setb text_out_base,0 + + setb final_result,$80 + + ; Signature that tells emulator data is valid + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base +: stx text_out_addr+1 + setb text_out_addr, Character to write +; Preserved: A, X, Y +write_text_out: + sty text_out_temp + + ldy text_out_addr+1 + beq @off + + ; Write new terminator FIRST, then new char before it, + ; in case emulator looks at string in middle of this routine. + ldy #1 + pha + lda #0 + sta (text_out_addr),y + dey + pla + sta (text_out_addr),y + + inc text_out_addr + bne :+ + inc text_out_addr+1 +: +@off: + ldy text_out_temp + rts diff --git a/nes_instr_test/source/nes.cfg b/nes_instr_test/source/nes.cfg new file mode 100644 index 0000000..035d32d --- /dev/null +++ b/nes_instr_test/source/nes.cfg @@ -0,0 +1,51 @@ +# ca65 linker configuration for iNES ROM with code at $E000 + +# fill=yes forces area to be padded to specified size in output +MEMORY +{ + # My devcart only has memory from $E000-$FFFF + HEADER: start = 0, size = $10, type = ro, fill=yes; + BANKS: start = $8000, size =$20000, type = ro; + ROM: start = $8000, size = $7FF4, type = ro, fill=yes; + + # Extra 6 bytes in vectors because built-in NES configuration + # does the same. Stupid, but better to keep compatible with it + # so small examples can use the built-in configuration. + VECTORS:start = $FFF4, size = $C, type = ro, fill=yes; + + CHARS: start = 0, size = $2000, type = ro; + + ZP: start = $10, size = $F0, type = rw; + SRAM: start = $0200, size = $0200, type = rw; +} + +# align=$100 allows use of .align directive with a value up to $100 +# optional=yes avoids warning if segment is never used +# define=yes defines __NAME_LOAD__ and __NAME_SIZE__ +SEGMENTS +{ + HEADER: load = HEADER, type = ro; + DMC: load = ROM, type = ro, optional=yes; + CODE: load = ROM, type = ro, align=$100; + + # Library code goes into this segment, keeping user code same + # length regardless of runtime: devcart, ROM, NSF, etc. + CODE2: load = ROM, type = ro, align=$100, optional=yes; + RODATA: load = ROM, type = ro; + + # Separate segment for strings so RODATA can have pointers to + # strings + STRINGS: load = ROM, type = ro, optional=yes; + + # So trailing zeroes won't get stripped off when sending to devcart + ROEND: load = ROM, type = ro, optional=yes; + + VECTORS: load = VECTORS,type = ro; + BANKS: load = BANKS, type = ro, align=$2000, optional=yes; + + CHARS: load = CHARS, type = ro, align=$2000, optional=yes; + + ZEROPAGE: load = ZP, type = zp; + NVRAM: load = SRAM, type = bss, define=yes, optional=yes; + BSS: load = SRAM, type = bss; +} diff --git a/nes_instr_test/source/nsf.cfg b/nes_instr_test/source/nsf.cfg new file mode 100644 index 0000000..f7481c7 --- /dev/null +++ b/nes_instr_test/source/nsf.cfg @@ -0,0 +1,31 @@ +# ca65 linker configuration for 32K NSF music file + +# fill=yes forces area to be padded to specified size in output +MEMORY { + HEADER: start = 0, size = $80, type = ro, fill = yes; + + ROM: start = $8000, size = $7FF4, type = ro, fill = yes; + VECTORS:start = $FFF4, size = $C, type = ro, fill = yes; + + ZP: start = $10, size = $F0, type = rw; + SRAM: start = $0200, size = $0600, type = rw; +} + +# align=$100 allows use of .align directive with a value up to $100 +# define=yes defines __NAME_LOAD__ and __NAME_SIZE__ +SEGMENTS { + HEADER: load = HEADER, type = ro; + DMC: load = ROM, type = ro, optional=yes; + CODE: load = ROM, type = ro, align = $100; + + CODE2: load = ROM, type = ro, align = $100; + RODATA: load = ROM, type = ro; + STRINGS: load = ROM, type = ro; + ROEND: load = ROM, type = ro, optional=yes; + + VECTORS: load = VECTORS,type = ro; + + ZEROPAGE: load = ZP, type = zp; + NVRAM: load = SRAM, type = bss, define=yes, optional=yes; + BSS: load = SRAM, type = bss; +} diff --git a/nes_instr_test/source/readme.txt b/nes_instr_test/source/readme.txt new file mode 100644 index 0000000..57da11e --- /dev/null +++ b/nes_instr_test/source/readme.txt @@ -0,0 +1,88 @@ +NES Tests Source Code +--------------------- + +Building with ca65 +------------------ +To assemble a test ROM with ca65, use the following commands: + + ca65 -I common -o test.o source_filename_here.s + ld65 -C nes.cfg test.o -o test.nes + +To assemble as an NSF music file: + + ca65 -I common -o test.o source_filename_here.s -D BUILD_NSF + ld65 -C nsf.cfg test.o -o test.nsf + +Note that some tests might only work when built as a ROM or NSF file, +but not both. + +Some tests might include a ROM/NSF that has all the tests combined. +Building such a multi-test is complex and the necessary files aren't +included. Also, tests you build won't print their name if they fail, +since that requires special arrangements. + + +Framework +--------- +Each test is in a single source file, and makes use of several library +source files from common/. This framework provides common services and +reduces code to only that which performs the actual test. Virtually all +tests include "shell.inc" at the beginning, which sets things up and +includes all the appropriate library files. + +The reset handler does minimal NES hardware initialization, clears RAM, +sets up the text console, then runs main. Main can exit by returning or +jumping to "exit" with an error code in A. Exit reports the code then +goes into an infinite loop. If the code is 0, it doesn't do anything, +otherwise it reports the code. Code 1 is reported as "Failed", and the +rest as "Error ". + +Several routines are available to print values and text to the console. +Most update a running CRC-32 checksum which can be checked with +check_crc, allowing ALL the output to be checked very easily. If the +checksum doesn't match, it is printed, so you can run the code on a NES +and paste the correct checksum into your code. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported due to their complexity. I +load the code at $E000 since my devcart requires it, and I don't want +the normal ROM to differ in any way from what I've tested. This also +allows easy self-modifying code. + +Some macros are used to make common operations more convenient. The left +is equivalent to the right: + + Macro Equivalent + ------------------------------------- + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + set addr,byte lda #byte + sta addr + + zp_byte name .zeropage + name: .res 1 + .code + + zp_res name,n .zeropage + name: .res n + .code + + bss_res name,n .bss + name: .res n + .code + + for_loop routine,begin,end,step + calls routine with A set to successive values + + loop_n_times routine,count + calls routine with A from 0 to count-1 + +-- +Shay Green diff --git a/nmi_sync/demo_ntsc.nes b/nmi_sync/demo_ntsc.nes new file mode 100644 index 0000000..cb1c217 Binary files /dev/null and b/nmi_sync/demo_ntsc.nes differ diff --git a/nmi_sync/demo_ntsc.s b/nmi_sync/demo_ntsc.s new file mode 100644 index 0000000..d11e132 --- /dev/null +++ b/nmi_sync/demo_ntsc.s @@ -0,0 +1,153 @@ +; Uses nmi_sync to manually display line on screen +; using timed write. See readme.txt. +; +; NTSC NES only. Tested on hardware. +; +; ca65 -o rom.o demo_ntsc.s +; ld65 -C unrom.cfg rom.o -o demo_ntsc.nes +; +; Shay Green + +.include "nmi_sync.s" + +reset: + ; Initialize PPU and palette + jsr init_graphics + + ; Synchronize to PPU and enable NMI + jsr init_nmi_sync + + ; Loop endlessly +loop: jsr wait_nmi + + ; You could run normal code between NMIs here, + ; as long as it completes BEFORE NMI. If it + ; takes too long, synchronization may be off + ; by a few cycles for that frame. + ; ... + + jmp loop + + +.align 256 ; branches must not cross page +nmi: + pha + + ; Do this sometime before you DMA sprites + jsr begin_nmi_sync + + ; DMA then enable sprites. Instructions before + ; STA $4014 (excluding begin_nmi_sync) must take + ; an even number of cycles. The only required + ; instruction here is STA $4014. + bit <0 ; to make cycle count even + lda #0 + sta $2003 + lda #>sprites + sta $4014 + lda #$10 + sta $2001 + + ; Our instructions up to this point MUST total + ; 1715 cycles, so we'll burn the rest in a loop. + + ; delay 1715 - 30 + lda #29 + sec +nmi_1: pha + lda #9 +nmi_2: sbc #1 + bne nmi_2 + pla + sbc #1 + bne nmi_1 + + jsr end_nmi_sync + + ; We're now synchronized exactly to 2286 cycles + ; after beginning of frame. + + ; delay 16168 - 2286 - 5 + nop + lda #24 + sec +nmi_3: pha + lda #113 +nmi_4: sbc #1 + bne nmi_4 + pla + sbc #1 + bne nmi_3 + + ; Draw short line using monochrome mode bit + lda #$11 + sta $2001 ; writes 16168 cycles into frame + lda #$10 + sta $2001 + + pla + rti + + +.align 256 +sprites: + ; Reference sprites around manually-drawn line + .byte 118, 0, 0, 80 + .byte 118, 0, 0, 80+8 + .byte 118, 1, 0, 80+16 + + .byte 122, 0, 0, 80 + .byte 122, 0, 0, 80+8 + + .res 256 - 5*4, $FF + + +init_graphics: + sei + + ; Init PPU + bit $2002 +init_graphics_1: + bit $2002 + bpl init_graphics_1 +init_graphics_2: + bit $2002 + bpl init_graphics_2 + lda #0 + sta $2000 + sta $2001 + + ; Load alternating black and white palette + lda #$3F + sta $2006 + ldy #$E0 + sty $2006 +init_graphics_3: + sta $2007 + eor #$0F + iny + bne init_graphics_3 + + rts + +; Freeze program if this somehow gets triggered, rather +; than silently messing up timing +irq: jmp irq + + +.segment "HEADER" + .byte "NES",26, 2,1, 0,0 ; 32K PRG, 8K CHR, UNROM + .byte 0,0,0,0,0,0,0,0 + +.segment "VECTORS" + .word 0,0,0, nmi, reset, irq + +.segment "CHARS" + ; Characters for sprites + .byte $FF,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0 + + .byte $FF,$FF,$FF,$FF,$FF,0,0,0 + .byte 0,0,0,0,0,0,0,0 + + .res $2000 - $20 diff --git a/nmi_sync/demo_pal.nes b/nmi_sync/demo_pal.nes new file mode 100644 index 0000000..033d9e6 Binary files /dev/null and b/nmi_sync/demo_pal.nes differ diff --git a/nmi_sync/demo_pal.s b/nmi_sync/demo_pal.s new file mode 100644 index 0000000..e91514a --- /dev/null +++ b/nmi_sync/demo_pal.s @@ -0,0 +1,153 @@ +; Uses nmi_sync to manually display line on screen +; using timed write. See readme.txt. +; +; PAL NES only. Tested on hardware. +; +; ca65 -o rom.o demo_pal.s +; ld65 -C unrom.cfg rom.o -o demo_pal.nes +; +; Shay Green + +.include "nmi_sync.s" + +reset: + ; Initialize PPU and palette + jsr init_graphics + + ; Synchronize with PPU and enable NMI + jsr init_nmi_sync_pal + + ; Loop endlessly +loop: jsr wait_nmi + + ; You could run normal code between NMIs here, + ; as long as it completes BEFORE NMI. If it + ; takes too long, synchronization may be off + ; by a few cycles for that frame. + ; ... + + jmp loop + + +.align 256 ; branches must not cross page +nmi: + pha + + ; Do this sometime before you DMA sprites + jsr begin_nmi_sync + + ; DMA then enable sprites. Instructions before + ; STA $4014 (excluding begin_nmi_sync) must take + ; an even number of cycles. The only required + ; instruction here is STA $4014. + bit <0 ; to make cycle count even + lda #0 + sta $2003 + lda #>sprites + sta $4014 + lda #$10 + sta $2001 + + ; Our instructions up to this point MUST total + ; 6900 cycles, so we'll burn the rest in a loop. + + ; delay 6900 - 30 + lda #9 + sec +nmi_1: pha + lda #150 +nmi_2: sbc #1 + bne nmi_2 + pla + sbc #1 + bne nmi_1 + + jsr end_nmi_sync + + ; We're now synchronized exactly to 7471 cycles + ; after beginning of frame. + + ; delay 20486 - 7471 - 5 + nop + lda #85 + sec +nmi_3: pha + lda #28 +nmi_4: sbc #1 + bne nmi_4 + pla + sbc #1 + bne nmi_3 + + ; Draw short line using monochrome mode bit + lda #$11 + sta $2001 ; writes 20486 cycles into frame + lda #$10 + sta $2001 + + pla + rti + + +.align 256 +sprites: + ; Reference sprites around manually-drawn line + .byte 118, 0, 0, 82 + .byte 118, 0, 0, 82+8 + .byte 118, 1, 0, 82+16 + + .byte 122, 0, 0, 84 + .byte 122, 0, 0, 84+8 + + .res 256 - 5*4, $FF + + +init_graphics: + sei + + ; Init PPU + bit $2002 +init_graphics_1: + bit $2002 + bpl init_graphics_1 +init_graphics_2: + bit $2002 + bpl init_graphics_2 + lda #0 + sta $2000 + sta $2001 + + ; Load alternating black and white palette + lda #$3F + sta $2006 + ldy #$E0 + sty $2006 +init_graphics_3: + sta $2007 + eor #$0F + iny + bne init_graphics_3 + + rts + +; Freeze program if this somehow gets triggered, rather +; than silently messing up timing +irq: jmp irq + + +.segment "HEADER" + .byte "NES",26, 2,1, 0,0 ; 32K PRG, 8K CHR, UNROM + .byte 0,0,0,0,0,0,0,0 + +.segment "VECTORS" + .word 0,0,0, nmi, reset, irq + +.segment "CHARS" + ; Characters for sprites + .byte $FF,0,0,0,0,0,0,0 + .byte 0,0,0,0,0,0,0,0 + + .byte $FF,$FF,$FF,$FF,$FF,0,0,0 + .byte 0,0,0,0,0,0,0,0 + + .res $2000 - $20 diff --git a/nmi_sync/nmi_sync.s b/nmi_sync/nmi_sync.s new file mode 100644 index 0000000..b0557f1 --- /dev/null +++ b/nmi_sync/nmi_sync.s @@ -0,0 +1,185 @@ +; Allows precise PPU synchronization in NMI handler, without +; having to cycle-time code outside NMI handler. + +.zeropage +nmi_sync_count: .res 1 + +.code +.align 256 ; branches must not cross page + +; Initializes synchronization and enables NMI +; Preserved: X, Y +; Time: 15 frames average, 28 frames max +init_nmi_sync: + ; Disable interrupts and rendering + sei + lda #0 + sta $2000 + sta $2001 + + ; Coarse synchronize + bit $2002 +init_nmi_sync_1: + bit $2002 + bpl init_nmi_sync_1 + + ; Synchronize to odd CPU cycle + sta $4014 + + ; Fine synchronize + lda #3 +init_nmi_sync_2: + sta nmi_sync_count + bit $2002 + bit $2002 + php + eor #$02 + nop + nop + plp + bpl init_nmi_sync_2 + + ; Delay one frame +init_nmi_sync_3: + bit $2002 + bpl init_nmi_sync_3 + + ; Enable rendering long enough for frame to + ; be shortened if it's a short one, but not long + ; enough that background will get displayed. + lda #$08 + sta $2001 + + ; Can reduce delay by up to 5 and this still works, + ; so there's a good margin. + ; delay 2377 + lda #216 +init_nmi_sync_4: + nop + nop + sec + sbc #1 + bne init_nmi_sync_4 + + sta $2001 + + lda nmi_sync_count + + ; Wait for this and next frame to finish. + ; If this frame was short, loop ends. If it was + ; long, loop runs for a third frame. +init_nmi_sync_5: + bit $2002 + bit $2002 + php + eor #$02 + sta nmi_sync_count + nop + nop + plp + bpl init_nmi_sync_5 + + ; Enable NMI + lda #$80 + sta $2000 + + rts + + +; Initializes synchronization and enables NMI on PAL NES +; Preserved: X, Y +; Time: about 20 frames +init_nmi_sync_pal: + ; NMI will first occur within frame 2 after + ; synchronization + lda #2 + sta nmi_sync_count + + ; Disable interrupts and rendering + sei + lda #0 + sta $2000 + sta $2001 + + ; Coarse synchronize + bit $2002 +init_nmi_sync_pal_1: + bit $2002 + bpl init_nmi_sync_pal_1 + + ; Synchronize to odd CPU cycle + sta $4014 + bit <0 + + ; Fine synchronize +init_nmi_sync_pal_2: + bit <0 + nop + bit $2002 + bit $2002 + bpl init_nmi_sync_pal_2 + + ; Enable NMI + lda #$80 + sta $2000 + + rts + + +; Waits until NMI occurs. +; Preserved: A, X, Y +wait_nmi: + pha + + ; Reset high/low flag so NMI can depend on it + bit $2002 + + ; NMI must not occur during taken branch, so we + ; only use branch to get out of loop. + lda nmi_sync_count +wait_nmi_1: + cmp nmi_sync_count + bne wait_nmi_2 + jmp wait_nmi_1 +wait_nmi_2: + pla + rts + + +; Must be called in NMI handler, before sprite DMA. +; Preserved: X, Y +begin_nmi_sync: + lda nmi_sync_count + and #$02 + beq begin_nmi_sync_1 +begin_nmi_sync_1: + rts + + +; Must be called after sprite DMA. Instructions before this +; must total 1715 (NTSC)/6900 (PAL) cycles, treating +; JSR begin_nmi_sync and STA $4014 as taking 10 cycles total) +; Next instruction will begin 2286 (NTSC)/7471 (PAL) cycles +; after the cycle that the frame began in. +; Preserved: X, Y +end_nmi_sync: + lda nmi_sync_count + inc nmi_sync_count + and #$02 + bne end_nmi_sync_1 +end_nmi_sync_1: + lda $2002 + bmi end_nmi_sync_2 +end_nmi_sync_2: + bmi end_nmi_sync_3 +end_nmi_sync_3: + rts + + +; Keeps track of synchronization on frames where no +; synchronization is needed (where begin_nmi_sync/end_nmi_sync +; aren't called). +; Preserved: A, X, Y +track_nmi_sync: + inc nmi_sync_count + rts diff --git a/nmi_sync/readme.txt b/nmi_sync/readme.txt new file mode 100644 index 0000000..274b9cd --- /dev/null +++ b/nmi_sync/readme.txt @@ -0,0 +1,268 @@ +NES Precise NMI Synchronization +------------------------------- +This library allows synchronizing exactly to the PPU from within a +normal NMI handler. It allows PPU writes from within an NMI handler of +the same precision that is otherwise only possible using completely +cycle-timed code. It supports NTSC and PAL. + +The code is written for the ca65 assembler. Other assemblers will +require minor changes. + +For more about how the technique works, see +http://wiki.nesdev.com/w/index.php/Consistent_frame_synchronization . + + +Demos +----- +NTSC and PAL demos are included. These show minimal use of this library +to manually draw a line using timed writes. They manually draw a line by +setting bit 0 of $2001 to enable monochrome mode. The time of the write +determines the position on screen, so any synchronization problems will +cause the line's left side to move. Reference lines are shown above and +below the manually-drawn one, showing the correct left edge position. + +On the NTSC version, the left pixel of the middle line will be darker, +since it's flashing: + + ******************** + *** + -******************* + *** + ******************** + +On the PAL version, the left pixel or two will be darker, since it's +flashing. The left edge's general position will change randomly each +time you press reset. The upper line shows the farthest left it can ever +be after reset, and the lower line shows the farthest right it can be. +It may appear as one of the following: + + ******************** + *** + -******************* + *** + ****************** + + + ******************** + *** + -****************** + *** + ****************** + + + ******************** + *** + -***************** + *** + ****************** + + +Usage +----- +To use this library: + +* Include "nmi_sync.s". + +* Call init_nmi_sync[_pal] before synchronization is needed, then wait +in a loop that calls wait_nmi, does anything that is necessary between +NMIs, then loops back. + +* Inside NMI, call begin_nmi_sync, do sprite DMA, delay appropriately, +then call end_nmi_sync. If running on NTSC NES, sprite and/or background +rendering MUST be enabled before calling end_nmi_sync. It can be +disabled again after it returns. + +* On frames where synchronization isn't needed, but will be needed a few +frames later, call track_nmi_sync. If it won't be needed for a long +time, nothing needs to be done, and init_nmi_sync can be called again +later to re-synchronize and start over. + +After end_nmi_sync returns, the next instruction will be synchronized to +2286 (NTSC)/7471 (PAL) cycles after the frame began. + +If the NMI handler's timing is off by even one cycle, synchronization +will fail sometimes. To verify timing, write an odd value to $2001 after +synchronization. The point where monochrome mode begins on the scanline +should be very stable. If it ever jiggles, then something is wrong in +your code. + +The following demonstrates: + + .include "nmi_sync.s" + + reset: + ... + jsr init_nmi_sync/init_nmi_sync_pal + + loop: + jsr wait_nmi + ...anything done outside of NMI... + jmp loop + + nmi: + ...save registers... + jsr begin_nmi_sync ; count as 6 cycles + ... + + ; Instructions between nmi: and STA $4014 must take an even + ; number of cycles. STA $4014 must be done as a part of + ; synchronization. + sta $4014 ; count as 4 cycles + ... + + ; Instructions between nmi: and here must take + ; 1715 (NTSC)/6900 (PAL) cycles. + + delay 1715 - ... ; NTSC + delay 6900 - ... ; PAL + + ; On NTSC, sprite and/or background rendering MUST be + ; enabled at this point, or else synchronization will + ; be lost. + + jsr end_nmi_sync + + ; Sprite and background rendering can be disabled again + ; at this point, if it's not needed. + + ; Next instruction is now synchronized to exactly + ; 2286 (NTSC)/7471 (PAL) cycles after cycle that + ; frame began in. + ... + + ...restore registers... + rti + + +NTSC Timing +----------- +Given the following NMI handler + + nmi: + ... + jsr end_nmi_sync + delay N cycles + lda #$01 + sta $2001 ; writes 2286+N+5 cycles into frame + +The $2001 write will be 2286+N+5 cycles after frame began. To have the +$2001 write at a particular pixel, calculate N with + + pixel = y * 341 + x + N = (pixel + 290) / 3 + +For example, to write at y=121 x=80, N should be 13877. + +The pixel position can be calculated from N with + + pixel = N * 3 - 290 + y = pixel / 341 + x = pixel - (y * 341) + +where y=0 x=0 is the top-left pixel. For example, if the delay is 13877, +then the $2001 write will occur at y=121 x=80. + +After init_nmi_sync is called, the first, third, fifth, etc. frames have +the above timing. On the second, fourth, sixth, etc. frames, the write +is one pixel LATER (x=81 in the example). This one-pixel jitter is an +unavoidable hardware limitation. + +The above applies to enabling monochrome mode by setting bit 0 of $2001; +other registers take effect at slightly different times. For some +registers, the pixel written can vary slightly after pressing reset. +It's best to use the above as a guide, reduce delay until glitches occur +due to it occurring too early, increase delay until glitches occur as +well, then choose a delay in the middle of those two extremes. + + +PAL Timing +---------- +Given the following NMI handler + + nmi: + ... + jsr end_nmi_sync + delay N cycles + lda #$01 + sta $2001 ; writes 7471+N+5 cycles into frame + +The $2001 write will be 7471+N+5 cycles after frame began. To have the +$2001 write at a particular pixel, calculate N with + + pixel = y * 341 + x + N = (pixel * 5 + 1444) / 16 + +For example, to write at y=121 x=82, N should be 13009. + +The pixel position can be calculated from N with + + pixel = (N * 16 - 1444 + extra) / 5 + y = pixel / 341 + x = pixel - (y * 341) + +where y=0 x=0 is the top-left pixel, and extra is an additional delay +that depends on whether it's an even or odd frame, and also a random +offset selected when reset is pressed. For example, if the delay is +13009, then the $2001 write will occur no earlier than y=121 x=81. + +After init_nmi_sync is called, the first, third, fifth, etc. frames have +the above timing. On the second, fourth, sixth, etc. frames, extra is 8 +greater, causing the write to be one or two pixels LATER (x=83 or 84 in +the example). This jitter is an unavoidable hardware limitation. + +After pressing reset, extra is set to a random value from 0 to 7, +causing writes to be one or two pixels later. This doesn't change until +reset is pressed. This is also unavoidable. + +The above applies to enabling monochrome mode by setting bit 0 of $2001; +other registers take effect at slightly different times. For some +registers, the pixel written can vary slightly after pressing reset. +It's best to use the above as a guide, reduce delay until glitches occur +due to it occurring too early, increase delay until glitches occur as +well, then choose a delay in the middle of those two extremes. + + +Limitations +----------- +* DMC samples can't be played, since they introduce too much timing +variation. A normal NMI performs just as well/poorly in this case. + +* If NMI occurs while executing an instruction that takes more than +three cycles, synchronization will be upset for that frame. Note that a +taken branch counts as more than three cycles, due to an obscure detail. +To avoid this, call wait_nmi each frame, or sit in a loop of +instructions two/three-cycle instructions. I have found some workarounds +that allow NMI to occur during almost any instruction, but they require +some extra helper sprites and use of the sprite overflow flag; contact +me for details. + +* Every frame from that point, NMI must either call begin/end_nmi_sync, +or track_nmi_sync, or else synchronization will be lost and +init_nmi_sync will need to be called again. + +* The NMI handler must not read $2002 until after end_nmi_sync has been +called. + +* After synchronizing, NMI and rendering must be enabled by the next +frame, and left enabled (rendering can be disabled on PAL since it +doesn't affect PPU timing). If rendering isn't desired, it can be +enabled just before calling end_nmi_sync, then disabled afterwards. + +* Sprite DMA must be done on frames needing synchronization, even if no +sprites are being used. + +* Even when perfectly synchronized, frames don't always begin exactly on +a cycle. On NTSC, a given cycle will toggle between two adjacent pixels. +On PAL, it will toggle between the calculated pixel and one or two +pixels after. These effects are hardware limitations; this library +synchronizes as precisely as is possible in software. + + +Thanks +------ +* Bregalad for his initial questions that inspired the idea, and for +trying an early version. + + +-- +Shay Green diff --git a/nmi_sync/unrom.cfg b/nmi_sync/unrom.cfg new file mode 100644 index 0000000..93fc936 --- /dev/null +++ b/nmi_sync/unrom.cfg @@ -0,0 +1,27 @@ +# 32K iNES ROM with optional 8K CHR + +MEMORY +{ + ZP: start = $10, size = $E0; + RAM: start = $200, size = $500; + + HEADER: start = 0, size = $10, fill=yes; + + ROM: start = $8000, size = $7FF4, fill=yes, fillval=$FF; + VECTORS:start = $FFF4, size = $C, fill=yes; + + CHARS: start = 0, size = $2000, fillval=$FF; +} + +SEGMENTS +{ + ZEROPAGE: load = ZP, type = zp; + BSS: load = RAM, type = bss,align=$100; + + HEADER: load = HEADER, type = ro; + CODE: load = ROM, type = ro, align=$100; + RODATA: load = ROM, type = ro, align=$100; + VECTORS: load = VECTORS, type = ro; + + CHARS: load = CHARS, type = ro, align=$2000, optional=yes; +} diff --git a/ny2011/ny2011.nes b/ny2011/ny2011.nes new file mode 100644 index 0000000..ce76082 Binary files /dev/null and b/ny2011/ny2011.nes differ diff --git a/oam_read/oam_read.nes b/oam_read/oam_read.nes new file mode 100644 index 0000000..31d8f75 Binary files /dev/null and b/oam_read/oam_read.nes differ diff --git a/oam_read/readme.txt b/oam_read/readme.txt new file mode 100644 index 0000000..c429480 --- /dev/null +++ b/oam_read/readme.txt @@ -0,0 +1,161 @@ +NES OAM Read Test +----------------- +Tests OAM reading ($2004), being sure it reads the byte from OAM at the +current address in $2003. It scans OAM from 0 to $FF, testing each byte +in sequence. It prints a '-' where it reads back from the current +address, and '*' where it doesn't. Each row represents 16 bytes of OAM, +16 rows total. + + +Results +------- +On my NTSC front-loader NES, I get the following four general patterns +at random after power/reset: + +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- + +oam_read + +Passed + + +---------------- +---------------- +--------*------* +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- + +694ADBE0 +oam_read + +Failed + + +---------------- +---------------- +********-------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- +---------------- + +E9E8E60F +oam_read + +Failed + + +**************** +*********------- +--------*-*-*-*- +*-*-*-*-*-*-*-*- +*-*-*-*-*-*-*-*- +*-*-*-*-*-*-*-*- +***-*-*-*-*-*-*- +*-*-*-*-*-*-*-*- +*-*-*-*-*-*-*-*- +*-*-*-*-*-*-*-*- +*-*-*-*-*-*-*-*- +*-*-*-*-*-*-*-*- +*-*-*-*-*-*-*-*- +*-*-*-*-*-*-*-*- +***-*-*-*-*-*-*- +*-*-*-*-*-*-*-*- + +44551956 +oam_read + +Failed + + +Flashes, clicks, other glitches +------------------------------- +Some tests might need to turn the screen off and on, or cause slight +audio clicks. This does not indicate failure, and should be ignored. +Only the test result reported at the end is important, unless stated +otherwise. + + +Text output +----------- +Tests generally print information on screen. They also output the same +text as a zero-terminted string beginning at $6004, allowing examination +of output in an NSF player, or a NES emulator without a working PPU. The +tests also work properly if the PPU doesn't set the VBL flag properly or +doesn't implement it at all. + +The final result is displayed and also written to $6000. Before the test +starts, $80 is written there so you can tell when it's done. If a test +needs the NES to be reset, it writes $81 there (emulator should wait a +couple of frames after seeing $81). In addition, $DE $B0 $G1 is written +to $6001-$6003 to allow an emulator to detect when a test is being run, +as opposed to some other NES program. In NSF builds, the final result is +also reported via a series of beeps (see below). + +See the source code for more information about a particular test and why +it might be failing. Each test has comments and correct output at the +top. + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to avoid the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +In addition to the other text output methods described above, NSF builds +report essential information bytes audibly, including the final result. +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason as listed in the +source code by the corresponding set_code line. Examples: + +Tones Binary Decimal Meaning +- - - - - - - - - - - - - - - - - - - - +low 0 0 passed +low high 01 1 failed +low high low 010 2 error 2 + +-- +Shay Green diff --git a/oam_read/source/common/ascii.chr b/oam_read/source/common/ascii.chr new file mode 100644 index 0000000..2c5b26b Binary files /dev/null and b/oam_read/source/common/ascii.chr differ diff --git a/oam_read/source/common/build_rom.s b/oam_read/source/common/build_rom.s new file mode 100644 index 0000000..7636916 --- /dev/null +++ b/oam_read/source/common/build_rom.s @@ -0,0 +1,91 @@ +; Builds program as iNES ROM + +; Default is 16K PRG and 8K CHR ROM, NROM (0) + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM=1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM=1 ; Use mapper that supports 8K WRAM in cart +CUSTOM_MAPPER=n ; Specify mapper number +.endif + +.ifndef CUSTOM_MAPPER + .ifdef CART_WRAM + CUSTOM_MAPPER = 2 ; UNROM + .else + CUSTOM_MAPPER = 0 ; NROM + .endif +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte $4E,$45,$53,26 ; "NES" EOF + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte CUSTOM_MAPPER*$10+$01 ; vertical mirroring +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word -1,-1,-1, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .define CHARS "CHARS_PRG" + .segment CHARS + ascii_chr: + + .segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: +.else + .define CHARS "CHARS" + .segment "CHARS_ASCII" + .align $200 + .incbin "ascii.chr" + .res $1800 +.endif + +.segment CHARS + .res $10,0 + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +; Move code to $C000 +.segment "DMC" + .res $4000 + +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + load_ascii_chr + .endif + rts + +post_exit: + jsr set_final_result + jmp forever + +; This helps devcart recover after running test. +; It is never executed by test ROM. +.segment "LOADER" + .incbin "devcart.bin" + +.code diff --git a/oam_read/source/common/console.s b/oam_read/source/common/console.s new file mode 100644 index 0000000..f2d6c19 --- /dev/null +++ b/oam_read/source/common/console.s @@ -0,0 +1,224 @@ +; Scrolling text console with line wrapping, 30x29 characters. +; Buffers lines for speed. Will work even if PPU doesn't +; support scrolling (until text reaches bottom). Keeps border +; along bottom in case TV cuts it off. +; +; Defers most initialization until first newline, at which +; point it clears nametable and makes palette non-black. +; +; ** ASCII font must already be in CHR, and mirroring +; must be vertical or single-screen. + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs +console_margin = 1 + +console_buf_size = 32 +console_width = console_buf_size - (console_margin*2) + +zp_byte console_pos +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_buf_size + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + jmp console_clear_line_ + + +; Hides console by blacking palette and disabling PPU. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +console_wait_vbl_: + lda console_scroll + cmp #-1 + jne wait_vbl_optional + + ; Deferred initialization of PPU until first use of console + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + txa + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldx #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + + ; Clear attributes + lda #0 + ldx #$40 +: sta PPUDATA + dex + bne :- + + pla + tax + jmp console_show + + +; Shows console display +; Preserved: X, Y +console_show: + pha + + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + + lda #$30 ; white + jsr console_load_palette_ + + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + stx console_temp + + ; Newline if buf full and next char isn't space + ldx console_pos + bpl :+ + cmp #' ' + beq @ignore_space + ldx console_temp + jsr console_newline + stx console_temp + ldx console_pos +: + ; Write to buffer + sta console_buf+console_margin,x + dex + stx console_pos + +@ignore_space: + ldx console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_clear_line_ + + ; Scroll up 8 pixels and clear one line AHEAD + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + jsr console_add_8_to_scroll_ + jsr console_flush_a + jmp console_apply_scroll_ + + +; A = (A + 8) % 240 +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_clear_line_: + stx console_temp + + ; Start new clear line + lda #' ' + ldx #console_buf_size-1 +: sta console_buf,x + dex + bpl :- + ldx #console_width-1 + stx console_pos + + ldx console_temp + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + + pla + rts + +console_flush_: + lda console_scroll +console_flush_a: + ; Address line in nametable + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + sta PPUADDR + + ; Copy line + stx console_temp + ldx #console_buf_size-1 +: lda console_buf,x + sta PPUDATA + dex + bpl :- + ldx console_temp + + rts + diff --git a/oam_read/source/common/crc.s b/oam_read/source/common/crc.s new file mode 100644 index 0000000..de96c2a --- /dev/null +++ b/oam_read/source/common/crc.s @@ -0,0 +1,118 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on + + +; EQ if checksum matches CRC +; Out: A=0 and EQ if match, A>0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/oam_read/source/common/delay.s b/oam_read/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/oam_read/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/oam_read/source/common/devcart.bin b/oam_read/source/common/devcart.bin new file mode 100644 index 0000000..e8958c6 Binary files /dev/null and b/oam_read/source/common/devcart.bin differ diff --git a/oam_read/source/common/macros.inc b/oam_read/source/common/macros.inc new file mode 100644 index 0000000..52982a6 --- /dev/null +++ b/oam_read/source/common/macros.inc @@ -0,0 +1,169 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro incxy7 + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro diff --git a/oam_read/source/common/neshw.inc b/oam_read/source/common/neshw.inc new file mode 100644 index 0000000..bdd4136 --- /dev/null +++ b/oam_read/source/common/neshw.inc @@ -0,0 +1,37 @@ +; NES I/O locations and masks + +; Clocks per second +.ifndef CLOCK_RATE + CLOCK_RATE = 1789773 ; NTSC +; CLOCK_RATE = 1662607 ; PAL +.endif + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 diff --git a/oam_read/source/common/ppu.s b/oam_read/source/common/ppu.s new file mode 100644 index 0000000..155a9ed --- /dev/null +++ b/oam_read/source/common/ppu.s @@ -0,0 +1,129 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + lda #0 + jsr fill_screen + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts diff --git a/oam_read/source/common/print.s b/oam_read/source/common/print.s new file mode 100644 index 0000000..d2d7b81 --- /dev/null +++ b/oam_read/source/common/print.s @@ -0,0 +1,225 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr @nibble + pla + + pha + and #$0F + jsr @nibble + pla + rts + +@nibble: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: Y +print_dec: + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jmp print_char + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/oam_read/source/common/shell.inc b/oam_read/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/oam_read/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/oam_read/source/common/shell.s b/oam_read/source/common/shell.s new file mode 100644 index 0000000..635ce9f --- /dev/null +++ b/oam_read/source/common/shell.s @@ -0,0 +1,330 @@ +; Common routines and runtime + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +;**** Special globals **** + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +.segment "NVRAM" + ; Beginning of variables not cleared at startup + nvram_begin: + +;**** Code segment setup **** + +.segment "RODATA" + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +; Move code to $E200 ($200 bytes for text output) +.segment "DMC" + .res $2200 + +; Devcart corrupts byte at $E000 when powering off +.segment "CODE" + nop + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "print.s" +.include "delay.s" +.include "crc.s" +.include "testing.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts + +.endif + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_oam +.endif + + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sei + cld + ldx #$FF + txs + pha + + setb SNDCHN,0 + .ifndef BUILD_NSF + setb PPUCTRL,0 + .endif + + pla + pha + jsr report_result + ;jsr clear_nvram ; TODO: was this needed for anything? + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +; Clears $0-($100+S) and nv_ram_end-$7FF +clear_ram: + lda #0 + + ; Main pages + tax +: sta 0,x + sta $300,x + sta $400,x + sta $500,x + sta $600,x + sta $700,x + inx + bne :- + + ; Stack except that above stack pointer + tsx + inx +: dex + sta $100,x + bne :- + + ; BSS except nvram + ldx #<__NVRAM_SIZE__ +: sta __NVRAM_LOAD__,x + inx + bne :- + + rts + + +; Clears nvram +clear_nvram: + ldx #<__NVRAM_SIZE__ + beq @empty + lda #0 +: dex + sta __NVRAM_LOAD__,x + bne :- +@empty: + rts + + +; Prints filename and newline, if available, otherwise nothing. +; Preserved: A, X, Y +print_filename: + .ifdef FILENAME_KNOWN + pha + jsr print_newline + setw addr,filename + jsr print_str_addr + jsr print_newline + pla + .endif + rts + +.pushseg +.segment "RODATA" + ; Filename terminated with zero byte. + filename: + .ifdef FILENAME_KNOWN + .incbin "ram:nes_temp" + .endif + .byte 0 +.popseg + + +;**** ROM-specific **** +.ifndef BUILD_NSF + +.include "ppu.s" + +avoid_silent_nsf: +play_byte: + rts + +; Loads ASCII font into CHR RAM +.macro load_ascii_chr + bit PPUSTATUS + setb PPUADDR,$00 + setb PPUADDR,$00 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page +.endmacro + +; Disables interrupts and loops forever +.ifndef CUSTOM_FOREVER +forever: + sei + lda #0 + sta PPUCTRL +: beq :- + .res $10,$EA ; room for code to run loader +.endif + + +; Default NMI +.ifndef CUSTOM_NMI + zp_byte nmi_count + + nmi: + inc nmi_count + rti + + ; Waits for NMI. Must be using NMI handler that increments + ; nmi_count, with NMI enabled. + ; Preserved: X, Y + wait_nmi: + lda nmi_count + : cmp nmi_count + beq :- + rts +.endif + + +; Default IRQ +.ifndef CUSTOM_IRQ + irq: + bit SNDCHN ; clear APU IRQ flag + rti +.endif + +.endif diff --git a/oam_read/source/common/testing.s b/oam_read/source/common/testing.s new file mode 100644 index 0000000..ba41f03 --- /dev/null +++ b/oam_read/source/common/testing.s @@ -0,0 +1,106 @@ +; Utilities for writing test ROMs + +; In NVRAM so these can be used before initializing runtime, +; then runtime initialized without clearing them +nv_res test_code ; code of current test +nv_res test_name,2 ; address of name of current test, or 0 of none + + +; Sets current test code and optional name. Also resets +; checksum. +; Preserved: A, X, Y +.macro set_test code,name + pha + lda #code + jsr set_test_ + .ifblank name + setb test_name+1,0 + .else + .local Addr + setw test_name,Addr + seg_data "RODATA",{Addr: .byte name,0} + .endif + pla +.endmacro + +set_test_: + sta test_code + jmp reset_crc + + +; Initializes testing module +init_testing: + jmp init_crc + + +; Reports that all tests passed +tests_passed: + jsr print_filename + print_str newline,"Passed" + lda #0 + jmp exit + + +; Reports "Done" if set_test has never been used, +; "Passed" if set_test 0 was last used, or +; failure if set_test n was last used. +tests_done: + ldx test_code + jeq tests_passed + inx + bne test_failed + jsr print_filename + print_str newline,"Done" + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + ldx test_code + + ; Treat $FF as 1, in case it wasn't ever set + inx + bne :+ + inx + stx test_code +: + ; If code >= 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/oam_read/source/common/text_out.s b/oam_read/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/oam_read/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +Most update a running CRC-32 checksum which can be checked with +check_crc, allowing ALL the output to be checked very easily. If the +checksum doesn't match, it is printed, so you can run the code on a NES +and paste the correct checksum into your code. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported due to their complexity. I +load the code at $E000 since my devcart requires it, and I don't want +the normal ROM to differ in any way from what I've tested. This also +allows easy self-modifying code. + +Some macros are used to make common operations more convenient. The left +is equivalent to the right: + + Macro Equivalent + ------------------------------------- + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + set addr,byte lda #byte + sta addr + + zp_byte name .zeropage + name: .res 1 + .code + + zp_res name,n .zeropage + name: .res n + .code + + bss_res name,n .bss + name: .res n + .code + + for_loop routine,begin,end,step + calls routine with A set to successive values + + loop_n_times routine,count + calls routine with A from 0 to count-1 + +-- +Shay Green diff --git a/oam_stress/oam_stress.nes b/oam_stress/oam_stress.nes new file mode 100644 index 0000000..a6168a8 Binary files /dev/null and b/oam_stress/oam_stress.nes differ diff --git a/oam_stress/readme.txt b/oam_stress/readme.txt new file mode 100644 index 0000000..1834e64 --- /dev/null +++ b/oam_stress/readme.txt @@ -0,0 +1,81 @@ +NES OAM Stress Test +------------------- +Thoroughly tests OAM address ($2003) and read/write ($2004). On an NTSC +NES, this passes only for one of the four random PPU-CPU +synchronizations at power/reset. Test takes about 30 seconds, unless it +fails. + +This test randomly sets the address, then randomly either writes a +random number of random bytes, or reads from the current address a +random number of times and verifies that it matches what's expected. It +does this for tens of seconds (refreshing OAM periodically so it doesn't +fade). Once done, it verifies that all bytes in OAM match what's +expected. + +Expected behavior: + +$2003 write sets OAM address. + +$2004 write sets byte at current OAM address to byte written, then +increments OAM address. + +$2004 read gives byte at current OAM address, without modifying OAM +address. + + +Flashes, clicks, other glitches +------------------------------- +Some tests might need to turn the screen off and on, or cause slight +audio clicks. This does not indicate failure, and should be ignored. +Only the test result reported at the end is important, unless stated +otherwise. + + +Text output +----------- +Tests generally print information on screen. They also output the same +text as a zero-terminted string beginning at $6004, allowing examination +of output in an NSF player, or a NES emulator without a working PPU. The +tests also work properly if the PPU doesn't set the VBL flag properly or +doesn't implement it at all. + +The final result is displayed and also written to $6000. Before the test +starts, $80 is written there so you can tell when it's done. If a test +needs the NES to be reset, it writes $81 there (emulator should wait a +couple of frames after seeing $81). In addition, $DE $B0 $G1 is written +to $6001-$6003 to allow an emulator to detect when a test is being run, +as opposed to some other NES program. In NSF builds, the final result is +also reported via a series of beeps (see below). + +See the source code for more information about a particular test and why +it might be failing. Each test has comments and correct output at the +top. + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to avoid the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +In addition to the other text output methods described above, NSF builds +report essential information bytes audibly, including the final result. +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason as listed in the +source code by the corresponding set_code line. Examples: + +Tones Binary Decimal Meaning +- - - - - - - - - - - - - - - - - - - - +low 0 0 passed +low high 01 1 failed +low high low 010 2 error 2 + +-- +Shay Green diff --git a/oam_stress/source/common/ascii.chr b/oam_stress/source/common/ascii.chr new file mode 100644 index 0000000..2c5b26b Binary files /dev/null and b/oam_stress/source/common/ascii.chr differ diff --git a/oam_stress/source/common/build_multi.s b/oam_stress/source/common/build_multi.s new file mode 100644 index 0000000..e96b5a4 --- /dev/null +++ b/oam_stress/source/common/build_multi.s @@ -0,0 +1,116 @@ +; Builds program as bank for multi-test MMC1 ROM + +; Define these so that error will occur if already defined +; by program +CUSTOM_MAPPER = 1 ; MMC1 +CHR_RAM = 1 + +.ifndef BANK_COUNT + BANK_COUNT = 16 +.endif + +.segment "VECTORS" + .word -1, -1 + .word reset + .word nmi, multi_reset, irq + +;;;; CHR-RAM +.define CHARS "CHARS_PRG" +.segment CHARS + ascii_chr: + +.segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: + +;;;; Shell +NEED_CONSOLE = 1 +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + load_ascii_chr + rts + +nv_res cur_bank + +.macro write_mmc1 Addr +: sta Addr + lsr a + cmp #$40 >> 5 + bne :- +.endmacro + +; Copies and executes following code (256 bytes max) +; from RAM at $700 +.macro exec_in_ram + ldx #0 +: lda @source,x + sta @dest,x + inx + bne :- + jmp @dest +@source = * +.org $700 +@dest: +.endmacro + +multi_reset: + exec_in_ram + + ; 16K PRG, upper bank switchable, and reset shift reg first + lda #$4A << 1 + write_mmc1 $8000 + + ; Select first bank and execute + lda #$40 + write_mmc1 $E000 + setb cur_bank,0 + jmp ($FFF8) +.reloc + +post_exit: + cmp #0 + beq @passed + + ; Print which test number failed + print_str newline,"While running test " + ldx cur_bank + inx + txa + jsr print_dec + print_str {" of ",.sprintf("%d", BANK_COUNT),newline,newline,newline} + + setb final_result,1 +: jmp :- + +@passed: + exec_in_ram + + ; 16K PRG, upper bank switchable, and reset shift reg first + lda #$4A << 1 + write_mmc1 $8000 + + ; Next bank + inc cur_bank + lda cur_bank + cmp #BANK_COUNT + bne @run_bank + jsr console_init ; clear screen + print_str {"All ",.sprintf("%d",BANK_COUNT)," tests passed",newline,newline,newline} + + setb final_result,0 +: jmp :- + +@run_bank: + ora #$40 + write_mmc1 $E000 + jmp ($FFF8) + +.reloc diff --git a/oam_stress/source/common/build_nsf.s b/oam_stress/source/common/build_nsf.s new file mode 100644 index 0000000..731c271 --- /dev/null +++ b/oam_stress/source/common/build_nsf.s @@ -0,0 +1,162 @@ +; Builds program as NSF music file + +.ifndef NSF_SONG_COUNT + NSF_SONG_COUNT = 1 +.endif + +.ifndef CUSTOM_NSF_HEADER + .segment "HEADER" + .byte "NESM",26,1 + .byte NSF_SONG_COUNT + .byte 1 ; start with first song + .word load_addr,reset,nsf_play +.endif + +.segment "CODE" + load_addr: + +.ifndef CUSTOM_NSF_VECTORS + .segment "VECTORS" + .word 0,0,0,nmi,internal_error,irq +.endif + +.include "shell.s" + +nv_res nsf_track,1 +nv_res nsf_running,1 + +.ifdef EMPTY_NSF_PLAY + CUSTOM_NSF_PLAY = 1 + nsf_play: + rts +.endif + +std_reset: + sta nsf_track + + ; In case NSF player interrupts init with play, + ; wait a while in init. If play interrupts, then + ; we just use that play call to run program, and + ; this is never resumed. + setb nsf_running,1 + delay_msec 500 + +.ifndef CUSTOM_NSF_PLAY + nsf_play: +.endif +std_nsf_play: + php + bit nsf_running + bpl :+ + plp + rts + +: lda nsf_running + beq :+ + + ; First call + setb nsf_running,0 + delay_msec 20 + jmp run_shell + + ; Player let init run too long before interrupting + ; with play, or play call interrupted itself. +: setb nsf_running,$80 + jsr clear_ram + jsr init_shell + print_str "NSF player called play recursively" + jmp internal_error + + +init_runtime: + rts + +post_exit: + jsr set_final_result +forever: + jmp forever + +; Reports A in binary as high and low tones, with +; leading low tone for reference. Omits leading +; zeroes. +; Preserved: A, X, Y +play_byte: + pha + + ; Make low reference beep + clc + jsr @beep + + ; Remove high zero bits + sec +: rol a + bcc :- + + ; Play remaining bits + beq @zero +: jsr @beep + asl a + bne :- +@zero: + + delay_msec 300 + pla + rts + +; Plays low/high beep based on carry +; Preserved: A, X, Y +@beep: + pha + + ; Set up square + lda #1 + sta SNDCHN + sta $4001 + sta $4003 + adc #$FE ; period=$100 if carry, $1FF if none + sta $4002 + + ; Fade volume + lda #$0F +: ora #$30 + sta $4000 + delay_msec 8 + sec + sbc #$31 + bpl :- + + ; Silence + sta SNDCHN + delay_msec 160 + + pla + rts + +bss_res nsf_activity,1 + +; Call periodically where there would otherwise be silence, +; so that NSF build won't be cut short in NSF player that +; detects silence. +; Preserved: A, X, Y +avoid_silent_nsf: + pha + lda nsf_activity + clc + adc #4 + sta nsf_activity + sta $4011 + pla + rts + + +init_wait_vbl: +wait_vbl_optional: +;wait_vbl: ; undefined so assembler error results if called + rts + + +.ifndef CUSTOM_IRQ + irq: +.endif +nmi: + jmp internal_error diff --git a/oam_stress/source/common/build_rom.s b/oam_stress/source/common/build_rom.s new file mode 100644 index 0000000..7636916 --- /dev/null +++ b/oam_stress/source/common/build_rom.s @@ -0,0 +1,91 @@ +; Builds program as iNES ROM + +; Default is 16K PRG and 8K CHR ROM, NROM (0) + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM=1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM=1 ; Use mapper that supports 8K WRAM in cart +CUSTOM_MAPPER=n ; Specify mapper number +.endif + +.ifndef CUSTOM_MAPPER + .ifdef CART_WRAM + CUSTOM_MAPPER = 2 ; UNROM + .else + CUSTOM_MAPPER = 0 ; NROM + .endif +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte $4E,$45,$53,26 ; "NES" EOF + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte CUSTOM_MAPPER*$10+$01 ; vertical mirroring +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word -1,-1,-1, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .define CHARS "CHARS_PRG" + .segment CHARS + ascii_chr: + + .segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: +.else + .define CHARS "CHARS" + .segment "CHARS_ASCII" + .align $200 + .incbin "ascii.chr" + .res $1800 +.endif + +.segment CHARS + .res $10,0 + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +; Move code to $C000 +.segment "DMC" + .res $4000 + +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + load_ascii_chr + .endif + rts + +post_exit: + jsr set_final_result + jmp forever + +; This helps devcart recover after running test. +; It is never executed by test ROM. +.segment "LOADER" + .incbin "devcart.bin" + +.code diff --git a/oam_stress/source/common/console.s b/oam_stress/source/common/console.s new file mode 100644 index 0000000..f2d6c19 --- /dev/null +++ b/oam_stress/source/common/console.s @@ -0,0 +1,224 @@ +; Scrolling text console with line wrapping, 30x29 characters. +; Buffers lines for speed. Will work even if PPU doesn't +; support scrolling (until text reaches bottom). Keeps border +; along bottom in case TV cuts it off. +; +; Defers most initialization until first newline, at which +; point it clears nametable and makes palette non-black. +; +; ** ASCII font must already be in CHR, and mirroring +; must be vertical or single-screen. + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs +console_margin = 1 + +console_buf_size = 32 +console_width = console_buf_size - (console_margin*2) + +zp_byte console_pos +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_buf_size + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + jmp console_clear_line_ + + +; Hides console by blacking palette and disabling PPU. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +console_wait_vbl_: + lda console_scroll + cmp #-1 + jne wait_vbl_optional + + ; Deferred initialization of PPU until first use of console + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + txa + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldx #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + + ; Clear attributes + lda #0 + ldx #$40 +: sta PPUDATA + dex + bne :- + + pla + tax + jmp console_show + + +; Shows console display +; Preserved: X, Y +console_show: + pha + + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + + lda #$30 ; white + jsr console_load_palette_ + + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + stx console_temp + + ; Newline if buf full and next char isn't space + ldx console_pos + bpl :+ + cmp #' ' + beq @ignore_space + ldx console_temp + jsr console_newline + stx console_temp + ldx console_pos +: + ; Write to buffer + sta console_buf+console_margin,x + dex + stx console_pos + +@ignore_space: + ldx console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_clear_line_ + + ; Scroll up 8 pixels and clear one line AHEAD + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + jsr console_add_8_to_scroll_ + jsr console_flush_a + jmp console_apply_scroll_ + + +; A = (A + 8) % 240 +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_clear_line_: + stx console_temp + + ; Start new clear line + lda #' ' + ldx #console_buf_size-1 +: sta console_buf,x + dex + bpl :- + ldx #console_width-1 + stx console_pos + + ldx console_temp + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + + pla + rts + +console_flush_: + lda console_scroll +console_flush_a: + ; Address line in nametable + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + sta PPUADDR + + ; Copy line + stx console_temp + ldx #console_buf_size-1 +: lda console_buf,x + sta PPUDATA + dex + bpl :- + ldx console_temp + + rts + diff --git a/oam_stress/source/common/crc.s b/oam_stress/source/common/crc.s new file mode 100644 index 0000000..de96c2a --- /dev/null +++ b/oam_stress/source/common/crc.s @@ -0,0 +1,118 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on + + +; EQ if checksum matches CRC +; Out: A=0 and EQ if match, A>0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/oam_stress/source/common/crc_fast.s b/oam_stress/source/common/crc_fast.s new file mode 100644 index 0000000..5e1ffb4 --- /dev/null +++ b/oam_stress/source/common/crc_fast.s @@ -0,0 +1,65 @@ +; Fast table-based CRC-32 + +; Uses 1K of RAM +checksum_t0 = $400 +checksum_t1 = $500 +checksum_t2 = $600 +checksum_t3 = $700 + +; Initializes fast CRC tables and resets checksum. +; Preserved: Y +; Time: ~60 msec +init_crc_fast: + ldx #0 +@byte: ; Calculate CRC for this byte + lda #0 + sta checksum+3 + sta checksum+2 + sta checksum+1 + stx checksum + jsr update_crc_ + + ; Write in table + lda checksum + sta checksum_t0,x + lda checksum+1 + sta checksum_t1,x + lda checksum+2 + sta checksum_t2,x + lda checksum+3 + sta checksum_t3,x + + inx + bne @byte + + jmp reset_crc + + +; Updates checksum with byte from A +; Preserved: X, Y +; Time: 54 clocks +update_crc_fast: + stx checksum_temp + +; Updates checksum with byte from A +; Preserved: Y +; Time: 42 clocks +.macro update_crc_fast + eor checksum + tax + lda checksum+1 + eor checksum_t0,x + sta checksum + lda checksum+2 + eor checksum_t1,x + sta checksum+1 + lda checksum+3 + eor checksum_t2,x + sta checksum+2 + lda checksum_t3,x + sta checksum+3 +.endmacro + + update_crc_fast + ldx checksum_temp + rts diff --git a/oam_stress/source/common/delay.s b/oam_stress/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/oam_stress/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/oam_stress/source/common/devcart.bin b/oam_stress/source/common/devcart.bin new file mode 100644 index 0000000..e8958c6 Binary files /dev/null and b/oam_stress/source/common/devcart.bin differ diff --git a/oam_stress/source/common/macros.inc b/oam_stress/source/common/macros.inc new file mode 100644 index 0000000..52982a6 --- /dev/null +++ b/oam_stress/source/common/macros.inc @@ -0,0 +1,169 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro incxy7 + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro diff --git a/oam_stress/source/common/neshw.inc b/oam_stress/source/common/neshw.inc new file mode 100644 index 0000000..bdd4136 --- /dev/null +++ b/oam_stress/source/common/neshw.inc @@ -0,0 +1,37 @@ +; NES I/O locations and masks + +; Clocks per second +.ifndef CLOCK_RATE + CLOCK_RATE = 1789773 ; NTSC +; CLOCK_RATE = 1662607 ; PAL +.endif + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 diff --git a/oam_stress/source/common/ppu.s b/oam_stress/source/common/ppu.s new file mode 100644 index 0000000..155a9ed --- /dev/null +++ b/oam_stress/source/common/ppu.s @@ -0,0 +1,129 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + lda #0 + jsr fill_screen + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts diff --git a/oam_stress/source/common/print.s b/oam_stress/source/common/print.s new file mode 100644 index 0000000..d2d7b81 --- /dev/null +++ b/oam_stress/source/common/print.s @@ -0,0 +1,225 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr @nibble + pla + + pha + and #$0F + jsr @nibble + pla + rts + +@nibble: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: Y +print_dec: + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jmp print_char + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/oam_stress/source/common/shell.inc b/oam_stress/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/oam_stress/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/oam_stress/source/common/shell.s b/oam_stress/source/common/shell.s new file mode 100644 index 0000000..635ce9f --- /dev/null +++ b/oam_stress/source/common/shell.s @@ -0,0 +1,330 @@ +; Common routines and runtime + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +;**** Special globals **** + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +.segment "NVRAM" + ; Beginning of variables not cleared at startup + nvram_begin: + +;**** Code segment setup **** + +.segment "RODATA" + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +; Move code to $E200 ($200 bytes for text output) +.segment "DMC" + .res $2200 + +; Devcart corrupts byte at $E000 when powering off +.segment "CODE" + nop + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "print.s" +.include "delay.s" +.include "crc.s" +.include "testing.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts + +.endif + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_oam +.endif + + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sei + cld + ldx #$FF + txs + pha + + setb SNDCHN,0 + .ifndef BUILD_NSF + setb PPUCTRL,0 + .endif + + pla + pha + jsr report_result + ;jsr clear_nvram ; TODO: was this needed for anything? + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +; Clears $0-($100+S) and nv_ram_end-$7FF +clear_ram: + lda #0 + + ; Main pages + tax +: sta 0,x + sta $300,x + sta $400,x + sta $500,x + sta $600,x + sta $700,x + inx + bne :- + + ; Stack except that above stack pointer + tsx + inx +: dex + sta $100,x + bne :- + + ; BSS except nvram + ldx #<__NVRAM_SIZE__ +: sta __NVRAM_LOAD__,x + inx + bne :- + + rts + + +; Clears nvram +clear_nvram: + ldx #<__NVRAM_SIZE__ + beq @empty + lda #0 +: dex + sta __NVRAM_LOAD__,x + bne :- +@empty: + rts + + +; Prints filename and newline, if available, otherwise nothing. +; Preserved: A, X, Y +print_filename: + .ifdef FILENAME_KNOWN + pha + jsr print_newline + setw addr,filename + jsr print_str_addr + jsr print_newline + pla + .endif + rts + +.pushseg +.segment "RODATA" + ; Filename terminated with zero byte. + filename: + .ifdef FILENAME_KNOWN + .incbin "ram:nes_temp" + .endif + .byte 0 +.popseg + + +;**** ROM-specific **** +.ifndef BUILD_NSF + +.include "ppu.s" + +avoid_silent_nsf: +play_byte: + rts + +; Loads ASCII font into CHR RAM +.macro load_ascii_chr + bit PPUSTATUS + setb PPUADDR,$00 + setb PPUADDR,$00 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page +.endmacro + +; Disables interrupts and loops forever +.ifndef CUSTOM_FOREVER +forever: + sei + lda #0 + sta PPUCTRL +: beq :- + .res $10,$EA ; room for code to run loader +.endif + + +; Default NMI +.ifndef CUSTOM_NMI + zp_byte nmi_count + + nmi: + inc nmi_count + rti + + ; Waits for NMI. Must be using NMI handler that increments + ; nmi_count, with NMI enabled. + ; Preserved: X, Y + wait_nmi: + lda nmi_count + : cmp nmi_count + beq :- + rts +.endif + + +; Default IRQ +.ifndef CUSTOM_IRQ + irq: + bit SNDCHN ; clear APU IRQ flag + rti +.endif + +.endif diff --git a/oam_stress/source/common/testing.s b/oam_stress/source/common/testing.s new file mode 100644 index 0000000..ba41f03 --- /dev/null +++ b/oam_stress/source/common/testing.s @@ -0,0 +1,106 @@ +; Utilities for writing test ROMs + +; In NVRAM so these can be used before initializing runtime, +; then runtime initialized without clearing them +nv_res test_code ; code of current test +nv_res test_name,2 ; address of name of current test, or 0 of none + + +; Sets current test code and optional name. Also resets +; checksum. +; Preserved: A, X, Y +.macro set_test code,name + pha + lda #code + jsr set_test_ + .ifblank name + setb test_name+1,0 + .else + .local Addr + setw test_name,Addr + seg_data "RODATA",{Addr: .byte name,0} + .endif + pla +.endmacro + +set_test_: + sta test_code + jmp reset_crc + + +; Initializes testing module +init_testing: + jmp init_crc + + +; Reports that all tests passed +tests_passed: + jsr print_filename + print_str newline,"Passed" + lda #0 + jmp exit + + +; Reports "Done" if set_test has never been used, +; "Passed" if set_test 0 was last used, or +; failure if set_test n was last used. +tests_done: + ldx test_code + jeq tests_passed + inx + bne test_failed + jsr print_filename + print_str newline,"Done" + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + ldx test_code + + ; Treat $FF as 1, in case it wasn't ever set + inx + bne :+ + inx + stx test_code +: + ; If code >= 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/oam_stress/source/common/text_out.s b/oam_stress/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/oam_stress/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +Most update a running CRC-32 checksum which can be checked with +check_crc, allowing ALL the output to be checked very easily. If the +checksum doesn't match, it is printed, so you can run the code on a NES +and paste the correct checksum into your code. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported due to their complexity. I +load the code at $E000 since my devcart requires it, and I don't want +the normal ROM to differ in any way from what I've tested. This also +allows easy self-modifying code. + +Some macros are used to make common operations more convenient. The left +is equivalent to the right: + + Macro Equivalent + ------------------------------------- + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + set addr,byte lda #byte + sta addr + + zp_byte name .zeropage + name: .res 1 + .code + + zp_res name,n .zeropage + name: .res n + .code + + bss_res name,n .bss + name: .res n + .code + + for_loop routine,begin,end,step + calls routine with A set to successive values + + loop_n_times routine,count + calls routine with A from 0 to count-1 + +-- +Shay Green diff --git a/other/2003-test.nes b/other/2003-test.nes new file mode 100644 index 0000000..fc69530 Binary files /dev/null and b/other/2003-test.nes differ diff --git a/other/8bitpeoples_-_deadline_console_invitro.nes b/other/8bitpeoples_-_deadline_console_invitro.nes new file mode 100644 index 0000000..1ea66da Binary files /dev/null and b/other/8bitpeoples_-_deadline_console_invitro.nes differ diff --git a/other/BLOCKS.NES b/other/BLOCKS.NES new file mode 100644 index 0000000..de7372c Binary files /dev/null and b/other/BLOCKS.NES differ diff --git a/other/BladeBuster.nes b/other/BladeBuster.nes new file mode 100644 index 0000000..8876d57 Binary files /dev/null and b/other/BladeBuster.nes differ diff --git a/other/CMC80s.NES b/other/CMC80s.NES new file mode 100644 index 0000000..8e3a362 Binary files /dev/null and b/other/CMC80s.NES differ diff --git a/other/DROPOFF7.NES b/other/DROPOFF7.NES new file mode 100644 index 0000000..cedb2e4 Binary files /dev/null and b/other/DROPOFF7.NES differ diff --git "a/other/Duelito - L\351eme.txt" "b/other/Duelito - L\351eme.txt" new file mode 100644 index 0000000..c8f9603 --- /dev/null +++ "b/other/Duelito - L\351eme.txt" @@ -0,0 +1,36 @@ +Duelito +©1999 Hassán Hernández Benítez (alias Bokudono o Bokuten). + +Jugar es simple. Muévete a izquierda o derecha y salta con A. +Presiona Start para volver a empezar. + + +Nota: El suelo aparece desplazado en NESticle. Como éste es el +emulador más popular, el juego está adaptado para funcionar bien, +pero en otros emuladores o versiones anteriores de NESticle +probablemente se vea mal. + + +Quiero agradecer a: + +YOSHi por su documento (obvio) + +Tony Young Gracias a su página y demo por fin empecé a entender el + documento de YOSHi + (http://members.aol.com/TYoung79/nesprog.html) + +Thomas N. Anderson por TASM + +Jonathan Bowen por su resumen del juego de instrucciones del 6502 + +Chris Covell por RGB + (http://mypage.direct.ca/c/ccovell/) + +Snow Bro por Tile Layer + SnowBro Software (http://home.sol.no/~kenhanse/nes/) + + +Dudas y sugerencias: hebhassan@hotmail.com + Waseiyakusha(http://members.xoom.com/has_san/index.html) + + diff --git a/other/Duelito.nes b/other/Duelito.nes new file mode 100644 index 0000000..c3517d6 Binary files /dev/null and b/other/Duelito.nes differ diff --git a/other/FLAME.NES b/other/FLAME.NES new file mode 100644 index 0000000..54d9776 Binary files /dev/null and b/other/FLAME.NES differ diff --git a/other/GENIE.NES b/other/GENIE.NES new file mode 100644 index 0000000..c2fdb65 Binary files /dev/null and b/other/GENIE.NES differ diff --git a/other/GREYS.NES b/other/GREYS.NES new file mode 100644 index 0000000..12f4282 Binary files /dev/null and b/other/GREYS.NES differ diff --git a/other/HelloWorldDemo.nes b/other/HelloWorldDemo.nes new file mode 100644 index 0000000..fe7bb74 Binary files /dev/null and b/other/HelloWorldDemo.nes differ diff --git a/other/LINUS.ASM b/other/LINUS.ASM new file mode 100644 index 0000000..3fbd0cd --- /dev/null +++ b/other/LINUS.ASM @@ -0,0 +1,124 @@ + + +;variables + + .org 00100h + +int_en: .block 1 +sng_ctr: .block 1 +pv_btn: .block 1 + +;code + + .org 0c000h + +start: sei + cld + ldx #0ffh + txs + +w_vbi: lda 2002h + bpl w_vbi + + lda #0 + tax + +ci_lp: sta 0000h,x + sta 0100h,x + sta 0200h,x + sta 0300h,x + sta 0400h,x + sta 0500h,x + sta 0600h,x + sta 0700h,x + inx + bne ci_lp ;clear RAM + + lda #080h + sta 2000h + lda #081h + sta 2001h ;set up PPU for interrupts, disable screen + + lda #00c + sta sng_ctr + jsr 9a3ch ;init tune + lda #1 + sta int_en + +k_loop: jsr r_btn + and #010h + beq k_loop + + inc sng_ctr + lda #02bh + cmp sng_ctr + bne no_scr + lda #0 + sta sng_ctr + +no_scr: lda #0 + sta int_en + lda sng_ctr + jsr 9a3ch + lda #1 + sta int_en + jmp k_loop ;check button, if pressed inc song # and re-init + + +interrupt: pha + txa + pha + tya + pha + lda int_en + beq no_ints + jsr 9a7bh ;play tune + +no_ints: pla + tay + pla + tax + pla + rti + +r_btn: ldy #8 ;read keypad + ldx #1 + stx 4016h + dex + stx 4016h + +r_bit: lda 4016h + ror a + txa + rol a + tax + dey + bne r_bit + + cmp pv_btn + beq no_chg + sta pv_btn + rts + +no_chg: lda #0 + rts + +;info line + + .db "Linus Spacehead music... " + .db "ripped and player by k.horton " + .db "khorton@iquest.net " + .db "press START to change song" + +;fill empty space + + .fill 0fffah-*,0eah + + +;vectors + + .dw interrupt + .dw start + .dw interrupt + + .end diff --git a/other/LINUS.NSF b/other/LINUS.NSF new file mode 100644 index 0000000..c02887a Binary files /dev/null and b/other/LINUS.NSF differ diff --git a/other/LINUSMUS.NES b/other/LINUSMUS.NES new file mode 100644 index 0000000..a146d2f Binary files /dev/null and b/other/LINUSMUS.NES differ diff --git a/other/MOTION.NES b/other/MOTION.NES new file mode 100644 index 0000000..f9bcd0c Binary files /dev/null and b/other/MOTION.NES differ diff --git a/other/NESMPROP.TXT b/other/NESMPROP.TXT new file mode 100644 index 0000000..a5e211c --- /dev/null +++ b/other/NESMPROP.TXT @@ -0,0 +1,108 @@ + Proposed NES music format + ------------------------- + + +This file encompasses a way to transfer NES music data in a small, easy to +use format. + +The basic idea is one rips the music/sound code from an NES game and prepends +a small header to the data. + +A program of some form (6502/sound emulator) then takes the data and loads +it into the proper place into the 6502's address space, then inits and plays +the tune. + +Here's an overview of the proposed header: + +offset # of bytes Function +---------------------------- + +0000 5 STRING "NESM",01Ah ; denotes an NES sound format file +0005 1 BYTE version number +0006 1 BYTE total songs (1=1 song, 2=2 songs, etc) +0007 1 BYTE starting song (1= 1st song, 2=2nd song, etc) +0008 2 WORD (lo/hi) load address of data (0000-FFFF) +000a 2 WORD (lo/hi) init address of data (0000-FFFF) +000c 2 WORD (lo/hi) play address of data (0000-FFFF) +000e 32 STRING The name of the song, null terminated +002e 32 STRING The artist, if known, null terminated +004e 32 STRING The Copyright holder, null terminated +006e 2 WORD (lo/hi) speed, in 1/1000000th sec ticks (see note) +0070 16 ---- 16 extra bytes for expansion (bankswitch info, if needed) +0080 nnn ---- the music program/data follows + +This may look somewhat familiar; if so that's because this is somewhat +sorta of based on the PSID file format for C64 music/sound. + +I have included a working sample file of this format, "linus.nsf". The +".nsf" extention is short for "NES Sound Format" and should be used for all +such files. + +I will attempt to describe how to load and play the above sample file, and +I have included an .NES file for use on the emulators that plays the sample +file. To change song, hit start. This will increment the song and re-run +the init code. See included ASM file for info + +The header for the file appears like so: + + +offset: bytes + +0000: "NESM",01ah ;this denotes an NESM file +0005: 001h ;version 1 of the format +0006: 02bh ;there are 43 songs +0007: 00dh ;start on 12th song +0008: 08000h ;load that data in starting at 8000h +000a: 09a3ch ;init code located at 9A3Ch +000c: 09a8bh ;play code located at 9a8bh +000e: "Linus Spacehead",0,0,0,0,0,0,0,0... ;name of song +002e: "",0,0,0,0... ;who dunnit +004e: "1992 Codemasters",0,0,0,0... ;copyright date/owner +006e: 0411ah ;speed of tune (see note) +0070: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ;unused bytes, set to 00h +0080: data of song follows + +The current version of this format is 1, denoted by "01h" located in offset +0005h. + + +The way it works is pretty simple. + +1) Set up RAM at 0000-07ffh, and from 5000-FFFFh. Now, load the data from + the NESM file starting at the denoted start address (offsets 0008h and + 0009h in file). Continue loading data in until EOF. + +2) Set up some form of song selector for the user. Get the number of songs + from the file, and the start song and set up your UI accordingly. + +3) Set the accumulator to the selected song minus 1. (i.e. if you wish to + play the 1st song, set the accumulator to 0.) + +4) Call the init address denoted at offsets 000ah and 000bh in the file. + +5) Now, repeatedly call the play address, denoted at offsets 000ch and 000dh + in the file. + +6) If another song is to be played, set the accumulator and re-run the init + routine. + +Note about song playback speed: + +offsets 006eh and 006fh in the file denote the speed of playback in +1/1000000ths of a second. For the "usual" 60Hz playback rate, set this to +411ah. + +To generate a differing playback rate, use this formula: + + + speed +PBRATE= --------- + 1000000 + +Where PBRATE is the value you stick into 006e/006fh in the file, and +speed is the desired speed in hertz. + + +That's it! + + diff --git a/other/PCM.demo.wgraphics.nes b/other/PCM.demo.wgraphics.nes new file mode 100644 index 0000000..549dacc Binary files /dev/null and b/other/PCM.demo.wgraphics.nes differ diff --git a/other/RasterChromaLuma.NES b/other/RasterChromaLuma.NES new file mode 100644 index 0000000..d6721e4 Binary files /dev/null and b/other/RasterChromaLuma.NES differ diff --git a/other/RasterDemo.NES b/other/RasterDemo.NES new file mode 100644 index 0000000..b515aa0 Binary files /dev/null and b/other/RasterDemo.NES differ diff --git a/other/RasterTest1.NES b/other/RasterTest1.NES new file mode 100644 index 0000000..12ecc6c Binary files /dev/null and b/other/RasterTest1.NES differ diff --git a/other/RasterTest2.NES b/other/RasterTest2.NES new file mode 100644 index 0000000..abc86d0 Binary files /dev/null and b/other/RasterTest2.NES differ diff --git a/other/RasterTest3.NES b/other/RasterTest3.NES new file mode 100644 index 0000000..bfcf45d Binary files /dev/null and b/other/RasterTest3.NES differ diff --git a/other/RasterTest3a.NES b/other/RasterTest3a.NES new file mode 100644 index 0000000..7809084 Binary files /dev/null and b/other/RasterTest3a.NES differ diff --git a/other/RasterTest3b.NES b/other/RasterTest3b.NES new file mode 100644 index 0000000..37740c2 Binary files /dev/null and b/other/RasterTest3b.NES differ diff --git a/other/RasterTest3c.NES b/other/RasterTest3c.NES new file mode 100644 index 0000000..af30985 Binary files /dev/null and b/other/RasterTest3c.NES differ diff --git a/other/RasterTest3d.NES b/other/RasterTest3d.NES new file mode 100644 index 0000000..24d242e Binary files /dev/null and b/other/RasterTest3d.NES differ diff --git a/other/RasterTest3e.NES b/other/RasterTest3e.NES new file mode 100644 index 0000000..a98d7ce Binary files /dev/null and b/other/RasterTest3e.NES differ diff --git a/other/ReadmeCMC80s.TXT b/other/ReadmeCMC80s.TXT new file mode 100644 index 0000000..55397fc --- /dev/null +++ b/other/ReadmeCMC80s.TXT @@ -0,0 +1,31 @@ +This is an NES demo that I made to show off some of the NES' features. +It doesn't do too much that resembles an actual game, but it should give +you an idea of what the NES can do technically. Besides, I'm proud of +what I did, since I'm an amateur at NES (and 6502) programming. + +This demo is called "CMC'80s", and it pays tribute to the NES as well +as the decade that gave birth to Nintendo's great little system. I +tried to emulate the visual style of demos from the C-64 era. + +Technically, this demo _really_ gives the NES a workout. Therefore, only +a real NES runs this demo completely correctly. Some emulators that come +close, but not perfect, are LoopyNES and BioNES on the PC, and DarkNESs on +the Amiga. Mac emulation has not been tested, but should be comparable. + +If you are an emulator author, and want to emulate this demo correctly, you +should note that this demo utilizes of the NES: + + · "Colour Emphasis" bits. + · Mid-HBlank PPU writing. + · Sprite 0 hit detection. + · Sprite / Background priority. + · Split-screen scrolling. + · Cycle-accurate scanline timing. + +Please E-Mail me if you like this demo. Enjoy! + +Chris Covell +ccovell@direct.ca +http://mypage.direct.ca/c/ccovell/ +http://www.zyx.com/ccovell +Powered by Amiga! diff --git a/other/ReadmeSayoonara!.txt b/other/ReadmeSayoonara!.txt new file mode 100644 index 0000000..81031a5 --- /dev/null +++ b/other/ReadmeSayoonara!.txt @@ -0,0 +1,41 @@ +SAYOONARA! By Chris C (CMC). +---------- + +The demo. +--------- + +This is a small (but lengthy) demo which I programmed for the NES +in October of 2001. I wrote it because I may be heading off to +work in Japan for a year, and this may be my last opportunity to +release a "major" demo for a while. So, basically, this demo says +"goodbye" and "thanks" to all those who have made my NES-development +life so fun and rewarding. + +There are some scrolltexts inside the demo which explain more. I suggest +that you read them fully to catch some important tidbits about the demo. + +You never know what's in store!! + + +Compatibility +------------- + +Because "Sayoonara!" does a few tricks with the NES' PPU, not all emulators +can run it correctly. Forget about NESticle. Get something better if you +have a PC, such as LoopyNES or FCE Ultra. Even emulators such as BioNES and +FWNES have some problems displaying the demo correctly. As for other computer +systems, I can't recommend anything at the moment. + + +Contact +------- + +I'm the author of the demo. My name is Chris Covell. +If you like the demo, feel free to e-mail me at ccovell@direct.ca +Or, look around for my webpage. It is currently at: + [ http://mypage.direct.ca/c/ccovell ] + +The music in the demo was ripped from "Ferrari Grand Prix" on the NES. +Sorry, but I can't compose music. I'm no good at it. :-( + +So long for now! diff --git a/other/Retrocoders - Years behind.NES b/other/Retrocoders - Years behind.NES new file mode 100644 index 0000000..fdeaa24 Binary files /dev/null and b/other/Retrocoders - Years behind.NES differ diff --git a/other/S0.NES b/other/S0.NES new file mode 100644 index 0000000..e6b2335 Binary files /dev/null and b/other/S0.NES differ diff --git a/other/SPRITE.NES b/other/SPRITE.NES new file mode 100644 index 0000000..ef5fcf2 Binary files /dev/null and b/other/SPRITE.NES differ diff --git a/other/Sayoonara!.NES b/other/Sayoonara!.NES new file mode 100644 index 0000000..2046cf8 Binary files /dev/null and b/other/Sayoonara!.NES differ diff --git a/other/SimpleParallaxDemo.nes b/other/SimpleParallaxDemo.nes new file mode 100644 index 0000000..f4efe1d Binary files /dev/null and b/other/SimpleParallaxDemo.nes differ diff --git a/other/TANESPOT.NES b/other/TANESPOT.NES new file mode 100644 index 0000000..9d8e0b4 Binary files /dev/null and b/other/TANESPOT.NES differ diff --git a/other/TEST.NES b/other/TEST.NES new file mode 100644 index 0000000..ab9c268 Binary files /dev/null and b/other/TEST.NES differ diff --git a/other/The Duel - Readme.txt b/other/The Duel - Readme.txt new file mode 100644 index 0000000..181f856 --- /dev/null +++ b/other/The Duel - Readme.txt @@ -0,0 +1,34 @@ +Duelito (The Duel) +©1999 Hassán Hernández Benítez (alias Bokudono or Bokuten). + +Playing is easy. Just move to left and right, and press A for jump. + + +Note: NESticle makes the floor to appear lower. As it is the most +popular emulator, the game is adapted to it and is supposed to +look correctly, but in other emulators or different versions of +NESticle it may look bad. + + +I would like to thank: + +YOSHi for his document (obvious) + +Tony Young With his page and demo I began to understand YOSHi's document + (http://members.aol.com/TYoung79/nesprog.html) + +Thomas N. Anderson for TASM + +Jonathan Bowen for his summary of the 6502 instruction set + +Chris Covell for RGB + (http://mypage.direct.ca/c/ccovell/) + +Snow Bro for Tile Layer + SnowBro Software (http://home.sol.no/~kenhanse/nes/) + + +Questions/suggestions: hebhassan@hotmail.com + Waseiyakusha(http://members.xoom.com/has_san/index.html) + + diff --git a/other/apocalypse.nes b/other/apocalypse.nes new file mode 100644 index 0000000..1ece5aa Binary files /dev/null and b/other/apocalypse.nes differ diff --git a/other/apocalypse.src b/other/apocalypse.src new file mode 100644 index 0000000..a9607f4 --- /dev/null +++ b/other/apocalypse.src @@ -0,0 +1,811 @@ + processor 6502 + + SEG.U vars + ORG $0 + +MAXTILES = 128 +TILESPERFRAME = 47 + +xposPtr ds 2 +yposPtr ds 2 +currdirPtr ds 2 +tempVar1 ds 1 +tempVar2 ds 1 +rnd ds 1 +frameCount ds 1 + + ORG $200 +XPOS ds MAXTILES +YPOS ds MAXTILES + ORG $300 +CURRDIR ds MAXTILES + ORG $400 +LASTTIH ds TILESPERFRAME +LASTTIL ds TILESPERFRAME +LIFETIH ds TILESPERFRAME +LIFETIL ds TILESPERFRAME +CURRVAL ds TILESPERFRAME + + SEG GAME + + ; iNES header + ORG $BFF0 + dc.b "NES",$1a + dc.b $01 ; 1 16K PRG ROM + dc.b $01 ; 1 8K CHR ROM + dc.b $00 ; Mapper 0 NROM + + ; Binary Start + org $c000, 0 + +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 + +vblankwait1: ; First wait for vblank to make sure PPU is ready + BIT $2002 + BPL vblankwait1 + +vblankwait2: ; Second wait for vblank, PPU is ready after this + BIT $2002 + BPL vblankwait2 + + +LoadPalettes: + LDA $2002 ; read PPU status to reset the high/low latch + LDA #$3F + STA $2006 ; write the high byte of $3F00 address + LDA #$00 + STA $2006 ; write the low byte of $3F00 address + LDX #$00 ; start out at 0 +LoadPalettesLoop: + LDA palette,x ; load data from address (palette + the value in x) + STA $2007 ; write to PPU + INX ; X = X + 1 + CPX #$20 ; Compare X to hex $10, decimal 16 - copying 16 bytes = 4 sprites + BNE LoadPalettesLoop ; Branch to LoadPalettesLoop if compare was Not Equal to zero + ; if compare was equal to 32, keep going down + +LoadSprites: + LDX #$00 ; start at 0 +LoadSpritesLoop: + LDA sprites,x ; load data from address (sprites + x) + STA $0200,x ; store into RAM address ($0200 + x) + INX ; X = X + 1 + CPX #$10 ; Compare X to hex $10, decimal 16 + BNE LoadSpritesLoop ; Branch to LoadSpritesLoop if compare was Not Equal to zero + ; if compare was equal to 16, keep going down + + LDA $2002 ; read PPU status to reset the high/low latch + LDA #$20 + STA $2006 ; write the high byte of $2000 address + LDA #$00 + STA $2006 ; write the low byte of $2000 address + + LDY #03 + LDX #$00 + LDA #$FF +WriteTile + STA $2007 ; write to PPU + INX + BNE WriteTile + DEY + BPL WriteTile + +; Randomly fill attributes + LDA $2002 ; read PPU status to reset the high/low latch + LDA #$23 + STA $2006 ; write the high byte of $3F00 address + LDA #$C0 + STA $2006 ; write the low byte of $3F00 address + + LDX #$3F ; start out at 0 +FillAttributes + JSR Random + STA $2007 ; write to PPU + DEX + BNE FillAttributes + + LDX #MAXTILES-1 +InitDots + JSR Random + LSR + ADC #64 + STA XPOS,X + JSR Random + STA CURRDIR,X + JSR Random + LSR + ADC #64 + STA YPOS,X + DEX + BPL InitDots + + LDA #XPOS + STA xposPtr+1 + + LDA #YPOS + STA yposPtr+1 + + LDA #CURRDIR + STA currdirPtr+1 + + LDA #0 + STA frameCount + + + LDA #%10001000 ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1 + STA $2000 + + LDA #%00011110 ; enable sprites, enable background, no clipping on left side + STA $2001 + +Forever: + JMP Forever ;jump back to Forever, infinite loop + + + +NMI: +; LDA #$00 +; STA $2003 ; set the low byte (00) of the RAM address +; LDA #$02 +; STA $4014 ; set the high byte (02) of the RAM address, start the transfer +; +; +;LatchController: +; LDA #$01 +; STA $4016 +; LDA #$00 +; STA $4016 ; tell both the controllers to latch buttons +; +; +;ReadA: +; LDA $4016 ; player 1 - A +; AND #%00000001 ; only look at bit 0 +; BEQ ReadADone ; branch to ReadADone if button is NOT pressed (0) +; ; add instructions here to do something when button IS pressed (1) +; LDA $0203 ; load sprite X position +; CLC ; make sure the carry flag is clear +; ADC #$01 ; A = A + 1 +; STA $0203 ; save sprite X position +;ReadADone: ; handling this button is done +; +; +;Re.byte: +; LDA $4016 ; player 1 - B +; AND #%00000001 ; only look at bit 0 +; BEQ Re.byteDone ; branch to Re.byteDone if button is NOT pressed (0) +; ; add instructions here to do something when button IS pressed (1) +; LDA $0203 ; load sprite X position +; SEC ; make sure carry flag is set +; SBC #$01 ; A = A - 1 +; STA $0203 ; save sprite X position +;Re.byteDone: ; handling this button is done + +; Clear old and draw new tiles during VBLANK + LDA $2002 + LDY #$FF + LDX #TILESPERFRAME-1 +DrawTile + + LDA LASTTIH,X + STA $2006 + LDA LASTTIL,X + STA $2006 + STY $2007 ; Clear old tile + + LDA LIFETIH,X + STA $2006 + LDA LIFETIL,X + STA $2006 + LDA CURRVAL,X + STA $2007 ; Draw new tile + + DEX + BPL DrawTile + +;;This is the PPU clean up section, so rendering the next frame starts properly. + LDA #%10001000 ; enable NMI, sprites from Pattern Table 0, background from Pattern Table 1 + STA $2000 + LDA #%00011110 ; enable sprites, enable background, no clipping on left side + STA $2001 + LDA #$00 ;;tell the ppu there is no background scrolling + STA $2005 + STA $2005 + +; Advance to next frame + INC frameCount + LDA frameCount + AND #$01 + TAX + + LDA TILEINDEXOFFSETLO,X + STA xposPtr + STA currdirPtr + LDA TILEINDEXOFFSETHI,X + STA yposPtr + + LDY #TILESPERFRAME-1 +NextPixel + + TYA + AND #1 + LSR + ROR + LSR + STA tempVar2 + +; Move all dots + LDA (currdirPtr),Y + LSR + LSR + TAX + BCS MoveUp + + LDA (yposPtr),Y + CMP #231 + BNE NoRev1 + SEC + SBC #1 + STA (yposPtr),Y + LDA (currdirPtr),Y + EOR #2 + STA (currdirPtr),Y + JMP Sideways + +NoRev1 + ADC #1 + STA (yposPtr),Y + JMP Sideways + +MoveUp + LDA (yposPtr),Y + CMP #8 + BNE NoRev2 + CLC + ADC #1 + STA (yposPtr),Y + LDA (currdirPtr),Y + EOR #2 + STA (currdirPtr),Y + JMP Sideways + +NoRev2 + SBC #1 + STA (yposPtr),Y + +Sideways + TXA + LSR + BCS MoveLeft + + LDA (xposPtr),Y + CMP #255 + BNE NoRev3 + SEC + SBC #1 + STA (xposPtr),Y + LDA (currdirPtr),Y + EOR #4 + STA (currdirPtr),Y + JMP MovementDone + +NoRev3 + ADC #1 + STA (xposPtr),Y + JMP MovementDone + +MoveLeft + LDA (xposPtr),Y + CMP #0 + BNE NoRev4 + CLC + ADC #1 + STA (xposPtr),Y + LDA (currdirPtr),Y + EOR #4 + STA (currdirPtr),Y + JMP MovementDone + +NoRev4 + SBC #1 + STA (xposPtr),Y +MovementDone + +; Calc all dot and tile updates + + LDA LIFETIH,Y + STA LASTTIH,Y + LDA LIFETIL,Y + STA LASTTIL,Y + +; Tile Low Byte 1 + LDA (yposPtr),Y + TAX + ASL + ASL + AND #%11100000 + STA tempVar1 + +; Tile High Byte + TXA + ROL + ROL + ROL + AND #3 + CLC + ADC #$20 + STA LIFETIH,Y + +; Tile Low Byte 2 + LDA (xposPtr),Y + LSR + LSR + LSR + ORA tempVar1 + STA LIFETIL,Y + +; Tile # + TXA + AND #%00000111 + ASL + ASL + ASL + STA tempVar1 + LDA (xposPtr),Y + AND #%00000111 + ORA tempVar1 + ORA tempVar2 + STA CURRVAL,Y + + DEY + BPL ContinueLoop + RTI ; return from interrupt +ContinueLoop + JMP NextPixel + +;;;;;;;;;;;;;; + + +Random + LDA rnd + BNE .skipInit + LDA #$FF +.skipInit: + ASL + ASL + ASL + EOR rnd + ASL + ROL rnd + RTS + + + .org $E000 +palette: + .byte $3F,$21,$22,$23, $3F,$25,$36,$3B, $3F,$28,$29,$2A, $3F,$2C,$3D,$10 ;;background palette + .byte $3F,$00,$00,$00, $3F,$00,$00,$00, $3F,$00,$00,$00, $3F,$00,$00,$00 ;;sprite palette + +sprites: + ;vert tile attr horiz + .byte $80, $32, $00, $80 ;sprite 0 + .byte $80, $33, $00, $88 ;sprite 1 + .byte $88, $34, $00, $80 ;sprite 2 + .byte $88, $35, $00, $88 ;sprite 3 + + .org $F000 +TILEINDEXOFFSETLO + .byte 0*TILESPERFRAME, 1*TILESPERFRAME +TILEINDEXOFFSETHI + .byte 2*TILESPERFRAME, 3*TILESPERFRAME + + org $FFFA ;first of the three vectors starts here + dc.w NMI ;when an NMI happens (once per frame if enabled) the + ;processor will jump to the label NMI: + dc.w RESET ;when the processor first turns on or is reset, it will jump + ;to the label RESET: + dc.w 0 ;external interrupt IRQ is not used in this tutorial + + SEG CHR ROM + ORG $10000 + + .byte %10000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %01000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00100000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00010000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00001000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000100, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000010, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000001, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %10000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %01000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00100000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00010000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00001000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000100, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000010, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000001, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %10000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %01000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00100000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00010000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00001000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000100, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000010, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000001, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %10000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %01000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00100000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00010000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00001000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000100, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000010, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000001, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %10000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %01000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00100000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00010000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000100, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000010, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000001, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %10000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %01000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00100000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00010000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000100, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000010, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000001, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %10000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %01000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00100000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00010000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00001000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000100, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000010, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000001, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %10000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %01000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00100000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00010000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00001000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000100 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000010 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000001 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %10000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %10000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %01000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %01000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00100000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00100000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00010000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00010000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00001000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00001000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000100, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000100, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000010, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000010, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000001, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000001, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %10000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %10000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %01000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %01000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00100000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00100000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00010000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00010000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00001000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00001000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000100, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000100, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000010, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000010, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000001, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000001, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %10000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %10000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %01000000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %01000000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00100000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00100000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00010000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00010000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00001000, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00001000, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000100, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000100, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000010, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000010, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000001, %00000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000001, %00000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %10000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %10000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %01000000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %01000000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00100000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00100000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00010000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00010000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00001000, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00001000, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000100, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000100, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000010, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000010, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000001, %00000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000001, %00000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %10000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %10000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %01000000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %01000000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00100000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00100000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00010000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00010000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000100, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000100, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000010, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000010, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000001, %00000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000001, %00000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %10000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %10000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %01000000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %01000000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00100000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00100000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00010000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00010000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00001000, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000100, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000100, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000010, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000010, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000001, %00000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000001, %00000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %10000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %10000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %01000000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %01000000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00100000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00100000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00010000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00010000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00001000, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00001000, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000100, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000100, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000010, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000010, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000001, %00000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000001, %00000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %10000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %10000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %01000000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %01000000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00100000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00100000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00010000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00010000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00001000 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00001000 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000100 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000100 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000010 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000010 + + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000001 + .byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000001 + + ORG $12000-1,0 + .byte %00000000 \ No newline at end of file diff --git a/other/blargg_litewall-2.nes b/other/blargg_litewall-2.nes new file mode 100644 index 0000000..9d7c9fd Binary files /dev/null and b/other/blargg_litewall-2.nes differ diff --git a/other/blargg_litewall-9.nes b/other/blargg_litewall-9.nes new file mode 100644 index 0000000..2ab82f3 Binary files /dev/null and b/other/blargg_litewall-9.nes differ diff --git a/other/demo jitter.nes b/other/demo jitter.nes new file mode 100644 index 0000000..a109172 Binary files /dev/null and b/other/demo jitter.nes differ diff --git a/other/demo.nes b/other/demo.nes new file mode 100644 index 0000000..9b44a78 Binary files /dev/null and b/other/demo.nes differ diff --git a/other/dnsf2_enginetest3.nsf b/other/dnsf2_enginetest3.nsf new file mode 100644 index 0000000..fd9649a Binary files /dev/null and b/other/dnsf2_enginetest3.nsf differ diff --git a/other/fceuxd.nes b/other/fceuxd.nes new file mode 100644 index 0000000..e80b0ac Binary files /dev/null and b/other/fceuxd.nes differ diff --git a/other/firefly.asm b/other/firefly.asm new file mode 100644 index 0000000..e2f863e --- /dev/null +++ b/other/firefly.asm @@ -0,0 +1,379 @@ + enum 0 + ystart1 db 0 + ystart2 db 0 + ystart3 db 0 + yline1 db 0 + yline2 db 0 + yline3 db 0 + IRQline db 0 + fly_x db 0 + fly_y db 0 + fly_frame db 0 + light dw 0 + timingtoggle db 0 + ende + +do_mmc5test + lda #0 + sta fly_frame + sta timingtoggle + + lda #120 + sta fly_y + sta fly_x + + lda #0 + sta $5101 ;CHR mode: 8k switchable + lda #0 + sta $5127 ;BG bank 0 + lda #0 + sta $512b + + lda #%00000000 + sta $5105 ;mirror + + setVADDR $2000 + ldx #0 +- lda brick,x + sta $2007 + inx + bne - +- lda brick+$100,x + sta $2007 + inx + bne - +- lda brick+$200,x + sta $2007 + inx + bne - +- lda brick+$300,x + sta $2007 + inx + bne - + + jsr clearsprites + jsr setpal + + lda #$00 + sta $202 + sta $206 + lda #$01 + sta $20a + sta $20e + + setNMI mmc5nmi + + lda #$80 + bit $2002 + sta $2000 ;NMI on + +- jmp - + +;------------------ +mmc5nmi: + jsr move_fly + lda #2 + sta $4014 ;spriteDMA + + setVADDR $3f13 + ldx #$39 + lda fly_frame + beq + + ldx #$19 ++ stx $2007 + + lda #0 + sta $2005 + sta $2005 ;scroll reset + + jsr readjoypad + and #joySTART + beq + + lda timingtoggle + eor #1 + sta timingtoggle ++ + lda #%10100000 + sta $2000 ;8x16 obj + lda #%11111110 ;2 + sta $2001 ;screen on + + setIRQ mmc5irq1 + + lda #$80 ;IRQen + sta $5204 + lda fly_y ;IRQline + sta $5203 + sta IRQline + + plp ;"cli" + pla + pla + jmp IRQwait + +move_fly + lda fly_frame + eor #2 + sta fly_frame + tax + lda anim,x + sta $201 + lda anim+1,x + sta $205 + lda anim+4,x + sta $209 + lda anim+5,x + sta $20d + lda lighttbl,x + sta light + lda lighttbl+1,x + sta light+1 + inc fly_x + lda fly_x + sta $203 + clc + adc #4 + sta $20b + clc + adc #4 + sta $207 + clc + adc #4 + sta $20f + ldy fly_x + lda div3,y + ldx timingtoggle + clc + bne + + adc #7 ++ sta timingdelay + lda tbl_sin2,y + lsr + adc #60 + sta $200 + sta $204 + clc + adc #4 + sta $208 + sta $20c + sec + ldx fly_frame + sbc light_y,x + sta fly_y + rts +light_y dw 31,27 +lighttbl + dw light1,light2 +anim db 1,3,5,7,9,11,13,15 + i = 255 +div3 rept 256 + db 26+i/3 + i=i-1 + endr +;--------------- +mmc5irq1 ;12 + lda $5204 ;4 + plp ;4 + pla ;4 + pla ;4 =28 + inc IRQline ;5 + lda IRQline ;3 + sta $5203 ;4 =40 + eatcycles(113-40) + ;--- + sei ;2 + lda $5204 ;4 + cli ;2 + + + + lda timingdelay + jsr timingloop + jmp (light) +macro flame1(start,end,third) + lda #%01111110 ;2 + ldx #%11111110 ;2 + eatcycles(start+4) + sta $2001 ;4 + eatcycles(end-start-4) + stx $2001 ;4 + eatcycles(114-end-12-third) +endm +macro flame2(start1,start2,end2,end1,third) + lda #%01111110 ;2 + ldx #%00011110 ;2 + ldy #%11111110 ;2 + eatcycles(start1+2) + sta $2001 ;4 + eatcycles(start2-start1-4) + stx $2001 ;4 + eatcycles(end2-start2-4) + sta $2001 ;4 + eatcycles(end1-end2-4) + sty $2001 ;4 + eatcycles(114-end1-12-third) +endm + +light1 + flame1(17,24,0) + flame1(15,26,0) + flame1(13,28,1) + flame1(12,29,0) + flame1(11,30,0) + flame1(10,31,1) + flame1(9,32,0) + flame1(8,33,0) + flame1(7,34,1) + flame1(7,34,0) + flame1(6,35,0) + flame1(6,35,1) + flame1(5,36,0) + flame1(5,36,0) + flame1(4,37,1) + flame1(4,37,0) + flame1(3,38,0) + flame2(3,18,24,38,1) + flame2(3,16,25,38,0) + flame2(2,15,26,39,0) + flame2(2,14,27,39,1) + flame2(2,13,28,39,0) + flame2(2,13,28,39,0) + flame2(1,12,29,40,1) + flame2(1,11,30,40,0) + flame2(1,11,30,40,0) + flame2(1,11,30,40,1) + flame2(1,10,31,40,0) + flame2(1,10,31,40,0) + flame2(0,10,31,41,1) + flame2(0,10,31,41,0) + flame2(0,9,32,41,0) + flame2(0,9,32,41,1) + flame2(0,9,32,41,0) + flame2(0,9,32,41,0) + flame2(0,9,32,41,1) + flame2(0,9,32,41,0) + flame2(0,9,32,41,0) + flame2(0,9,32,41,1) + flame2(0,9,32,41,0) + flame2(0,9,32,41,0) + flame2(0,9,32,41,1) + flame2(0,10,31,41,0) + flame2(0,10,31,41,0) + flame2(1,10,31,40,1) + flame2(1,10,31,40,0) + flame2(1,11,30,40,0) + flame2(1,11,30,40,1) + flame2(1,11,30,40,0) + flame2(1,12,29,40,0) + flame2(2,13,28,39,1) + flame2(2,13,28,39,0) + flame2(2,14,27,39,0) + flame2(2,15,26,39,1) + flame2(3,16,25,38,0) + flame2(3,18,24,38,0) + flame1(3,38,1) + flame1(4,37,0) + flame1(4,37,0) + flame1(5,36,1) + flame1(5,36,0) + flame1(6,35,0) + flame1(6,35,1) + flame1(7,34,0) + flame1(7,34,0) + flame1(8,33,1) + flame1(9,32,0) + flame1(10,31,0) + flame1(11,30,1) + flame1(12,29,0) + flame1(13,28,0) + flame1(15,26,1) + flame1(17,24,0) +- jmp - +light2 + flame1(17,24,0) + flame1(15,26,0) + flame1(13,28,1) + flame1(12,29,0) + flame1(11,30,0) + flame1(10,31,1) + flame1(9,32,0) + flame1(8,33,0) + flame1(7,34,1) + flame1(7,34,0) + flame1(6,35,0) + flame1(6,35,1) + flame1(5,36,0) + flame1(5,36,0) + flame1(4,37,1) + flame2(4,18,24,37,0) + flame2(4,16,25,37,0) + flame2(3,15,26,38,1) + flame2(3,14,27,38,0) + flame2(3,14,27,38,0) + flame2(2,13,28,39,1) + flame2(2,12,29,39,0) + flame2(2,12,29,39,0) + flame2(2,12,29,39,1) + flame2(2,11,30,39,0) + flame2(1,11,30,40,0) + flame2(1,11,30,40,1) + flame2(1,10,31,40,0) + flame2(1,10,31,40,0) + flame2(1,10,31,40,1) + flame2(1,10,31,40,0) + flame2(1,10,31,40,0) + flame2(1,10,31,40,1) + flame2(1,10,31,40,0) + flame2(1,10,31,40,0) + flame2(1,10,31,40,1) + flame2(1,10,31,40,0) + flame2(1,10,31,40,0) + flame2(1,11,30,40,1) + flame2(1,11,30,40,0) + flame2(2,11,30,39,0) + flame2(2,12,29,39,1) + flame2(2,12,29,39,0) + flame2(2,12,29,39,0) + flame2(2,13,28,39,1) + flame2(3,14,27,38,0) + flame2(3,14,27,38,0) + flame2(3,15,26,38,1) + flame2(4,16,25,37,0) + flame2(4,18,24,37,0) + flame1(4,37,1) + flame1(5,36,0) + flame1(5,36,0) + flame1(6,35,1) + flame1(6,35,0) + flame1(7,34,0) + flame1(7,34,1) + flame1(8,33,0) + flame1(9,32,0) + flame1(10,31,1) + flame1(11,30,0) + flame1(12,29,0) + flame1(13,28,1) + flame1(15,26,0) + flame1(17,24,0) + +- jmp - +;--- +setpal + lda #$3f ;set palette + sta $2006 + lda #$00 + sta $2006 + ldx #0 +- lda pal,x + sta $2007 + inx + cpx #32 + bne - + rts + +pal dc.b $0d,$1d,$05,$07, $0d,$0d,$11,$31, $0d,$0d,$13,$33, $0d,$0d,$19,$39 + dc.b $0d,$10,$18,$39, $0d,$0d,$0d,$0d, $0d,$0d,$0d,$0d, $0d,$0d,$0d,$0d + + align $100 +brick incbin mmc5test\brick.nam + incbin mmc5test\brick.atr \ No newline at end of file diff --git a/other/firefly.nes b/other/firefly.nes new file mode 100644 index 0000000..3d2f031 Binary files /dev/null and b/other/firefly.nes differ diff --git a/other/fun-n-games.nes b/other/fun-n-games.nes new file mode 100644 index 0000000..c1fb87a Binary files /dev/null and b/other/fun-n-games.nes differ diff --git a/other/litewall2.nes b/other/litewall2.nes new file mode 100644 index 0000000..ebd10a4 Binary files /dev/null and b/other/litewall2.nes differ diff --git a/other/litewall3.nes b/other/litewall3.nes new file mode 100644 index 0000000..a72ca11 Binary files /dev/null and b/other/litewall3.nes differ diff --git a/other/litewall5.nes b/other/litewall5.nes new file mode 100644 index 0000000..feb3347 Binary files /dev/null and b/other/litewall5.nes differ diff --git a/other/logo (E).nes b/other/logo (E).nes new file mode 100644 index 0000000..b413db1 Binary files /dev/null and b/other/logo (E).nes differ diff --git a/other/manhole.nes b/other/manhole.nes new file mode 100644 index 0000000..3902ca9 Binary files /dev/null and b/other/manhole.nes differ diff --git a/other/max-300.nes b/other/max-300.nes new file mode 100644 index 0000000..0f1597f Binary files /dev/null and b/other/max-300.nes differ diff --git a/other/midscanline.nes b/other/midscanline.nes new file mode 100644 index 0000000..2997857 Binary files /dev/null and b/other/midscanline.nes differ diff --git a/other/minipack.nes b/other/minipack.nes new file mode 100644 index 0000000..4f786f6 Binary files /dev/null and b/other/minipack.nes differ diff --git a/other/minipack.txt b/other/minipack.txt new file mode 100644 index 0000000..b7d058b --- /dev/null +++ b/other/minipack.txt @@ -0,0 +1,376 @@ +------------------------------------------------------------- + +2003 MiniGame Compo - http://www.ffd2.com/minigame/ + +NES Compilation v1.01 + + + +Credits: + + +BoXBoY by Neil Tew +Escape from Pong by Halley's Comet Software +Munchie Attack by Joe Parsell +Bomber 4K by RoboNES +Galaxy Patrol by Michael Martin + + +Packed by Memblers +FilePack compression by Mickael Pointier +Original tune by Mike Alsop +Music Engine by Bananmos + + +History: v1 - released + v1.01 - fixed bug with Escape from Pong + + +Docs attached. + + + + +------------------------------------------------------------- + + + + +############ +# BoXBoY # +############ + +A 4kb NES game for the 2003 MiniGames Compo (www.ffd2.com/minigame/) +Created by Neal Tew (loopy at mm dot st) + + +How to play: +----------- + +Arrange the colored boxes in the correct order! +Complete ten color sets to advance to the next level! +Finish all 6 levels to win the game! + +Controls: +-------- + +D-pad: Move up, down, left, right (duh) +B button: Drop box to lower platform +A button: Toss box to higher platform +Start: Pause the game +Select: Faster! + +Emulators: +--------- + +This should run in most "Good" emulators (i.e. NOT Nesticle). +It's been tested on: + +- Nintendulator +- FCE Ultra +- Nesten +- Nestopia +- Nester +- Jnes +- LoopyNES + + + + +------------------------------------------------------------- + + + + +Escape From Pong rev 2 + +A 1K NES entry into the MiniGame 2003 Compo (http://www.ffd2.com/minigame/) +by Halley's Comet Software (http://here.is/halleyscomet) + +Assembled with DASM by Matthew Dillon. +Written in edit.com + +1,021 bytes of code and data. + +Object: +Your goal, as a Ping Pong ball, is to avoid the paddles and other obstacles +to escape from the screen in 12 variously difficult levels. + +Gameplay: +Levels consist of one paddle which always tries to stay in front of the ball. +There are also white walls which the ball will bounce off of and red walls +which will restart the level. If you get in the way of a paddle the level +will also restart, this is to prevent you from having the paddle push you +through a wall. In order to advance to the next level you must get the ball +to leave the screen. +The early levels do not have gravity, but later levels do. +The difficulty level generally gets higher as the game progesses. +When you have completed all 12 levels the game will restart from level 1, +but the paddles will move faster. There is no way to actually "win" the game, +but then again there is no way to "lose". +I have personally played every level the first time through, but I don't +know if it is even physically possible to beat it again. + +Controls: +The directional pad controls thrust. Remember that the ball will accelerate +in the opposite direction of the button you press. +Your thrust is slightly more powerful than gravity (when gravity is active). + +Recommended Emulators: +loopyNES (runs well on older computers, the best alternative to Nesticle), +FCEU, nnnesterj + +NonRecommended Emulators: +Nesticle (scroll issues), Nester (colors are wrong) + +Bugs: +Due to an optimization of the controller code the start, select, a, and b +buttons also activate the thrust. This actually makes control quite a bit +easier, but it is an unintended feature. +B = Up +A = Down +Select = Left +Start = Right + +I have not tested the program on an actual NES, so it very well may not +work with the real hardware, but it works on every decent emulator I've +tried it with. + +History: +first release: er, it was the... first release? +revision 1: Nintendulator helped me find a bug, I had not set the + interrupt flag. Only unusually accurate emulators (and the + NES itself, I suppose) would have a problem with it. +revision 2: Fixed a glitch of the screen when loading a level by not + enabling the screen in the middle of a frame, only required + a slight reordering of commands. + +Based on concepts from: +* Pong +* Lunar Lander +* Some QBasic game (I remember finding it on AOL, it involved a bouncing ball) +* Several other games I've written (Rise of the Triangle, Cheese Reactor, + Mushrong) + +Things I had fully coded but took out due to space restraints: +* Little flames showing the ball's thrust +* Multiple paddles +* More levels +* Breakout style walls that disappear when you hit them + +Things I wanted to do but didn't (due to space restraints): +* Imperfectly elastic walls, which slow the ball down when hit +* Music +* Pause +* Multiplayer (actually would be easy to implement) + +Why the file is so big: +The 1,021 bytes of the game are distributed as follows: 1,017 bytes at the +beginning, which make up the program and header, and 4 bytes at the end (the +reset and NMI vectors). The last two bytes, for the BRK/IRQ vector, are not +used. No iNES format ROM can be less than 16,400 bytes in size, all of the +other bytes are filler. + +Level data: +The levels are read from scripts as specified by comments in the source. +Level data begins at the label "level". Levels of up to 256 bytes and games of +unlimited size (within the restraints of 16KB of PRGROM) can be created and +should be fully supported by the physics engine. + +Copyrights: +The source code and binary are (c) 2003 Halley's Comet Software. +They may be distributed or resued in any way without express permission so +long as credit is given to the author. + + + + +------------------------------------------------------------- + + + + +Munchie Attack + +A 4KB NES game for the 2003 MiniGame Compo + + +The story so far: + +Ninja have taken leaders of first-world nations hostage, +and are threatening to increase subsidies for domestic +food producers to even more insane levels than usual! + +As the hungriest person in the world, it is your job to +eat everything in sight to put an end to their nefarious +plans. + +Only you can prevent world-wide poverty and hunger! +You must act now! + + +Directions: + + +Once the intro has completed, press any key to begin the game. + +Use the directional pad to move your Munching Mouth (TM) +around the screen. Score points for eating food, but you +must beware of the knives and shuriken the ninja are throwing +at you. They will do anything they can to stop you! + +You start the game with 4 lives, and are awarded an extra life +for every 100,000 points accumulated. + + + +Point Values: + +McFries - 100 points +Cow Flesh Sandwich - 200 points +Fishy - 400 points +Apples & Oranges - 600 points +Mystery Food - 1,000 points +Squeedo del Tepton (TM) - ???? points + + + +Notes: + +_________________________________________ + +_________________________________________ + +_________________________________________ + +_________________________________________ + + + +FCC Regulations: +(omitted) + + +This game is for use only on the planet Gaia orbiting +the star Sol, unless written or telepathic permission +is obtained from the author. (Except on Fridays.) + + + + + + +------------------------------------------------------------- + + + + +Bomber 4K + + +objective: +perilessly fly over 3 cities and destroy each one to win + +keys: +A button - drop bomb +Start button - start game +select button - return to startup page from game over/win + +Emu's tested with: +slow emu / high accuracy: +fce ultra - yes +nestopia - yes +nintendulator - yes +fast / inaccurate +nestcl95 - yes +vortendo - sprites flicker + +rom statistics: +bomber.pal 28 +finishtext.dat 9 +lev_attribs.dat 64 +title_attribs.dat 64 +metatiledata.dat 176 +title.dat 256 +lev1.dat 256 +lev2.dat 256 +lev3.dat 256 +gameover.dat 256 +bomber.chr 1168 +ines header 16 +non-code total = 2805 +code 1283 +interrupt vectors 6 +code total = 1288 + +total rom size without 0 filling = 4094 bytes + + + + + +------------------------------------------------------------- + + + + + +GALAXY PATROL + +(c) 2003 Michael Martin +Platform: NES +Size: 3,691 bytes (16 byte iNES header, 2,103 bytes code, 1,572 bytes + data) +Tested on emulators: FCE Ultra, Nintendulator, NESten. + +Verification note +----------------- + +Note to the people verifying this entry: The iNES format operates in +16kB chunks and requires data at the beginning and at the end. I have +provided two versions of Galaxy Patrol, depending on how you elect to +handle this. If you want the bulk of the program up front, then +4k-galaxy-start.nes has the code organized in this way. It ends at file +location 0x0e70, and then is all zeroes through the final six bytes +which store the start locations, etc. for the machine. Likewise, +4k-galaxy-end.nes has the 16-byte header, then 12kB+16 bytes of zeroes, +and then the uninterrupted program code. The output of hexdump -C on +both of these .nes files has also been included for your convenience. + +Game Concept +------------ + +Welcome to Galaxy Patrol! You must fly through the starfield, avoiding +stars and collecting fuel so as to continue your journey. Your score is +based on the amount of fuel you have spent. + +Game Controls +------------- + +The Select and Start keys will let you navigate the main menu. Once in +game, the directional pad will control your direction of motion. You +will *always* be drifting to the left or right, and will "bounce" off +the sides of the screen if you collide with them. Colliding with a star +or running out of fuel will end the game. Colliding with a fuel dump +will give you 25 units of fuel (max 99). + +Special Thanks +-------------- + +Galaxy Patrol was inspired by Tim Hartnell's 1983 game of the same name +for the Timex Sinclair 1000. The game mechanics have been modified a +bit (you can't lurk on the edges anymore) and the fact that your ship +moves with a pixel-level of granularity instead of a character-level one +changes the tactics considerably. + +Special thanks also go out to Quietust for playtesting the earlier +versions of this game and helping to nail some of the graphical glitches +therein. + + + + +------------------------------------------------------------- + + +_EOF_ diff --git a/other/nes1.wav b/other/nes1.wav new file mode 100644 index 0000000..a746e51 Binary files /dev/null and b/other/nes1.wav differ diff --git a/other/nescafe.nes b/other/nescafe.nes new file mode 100644 index 0000000..c9b4389 Binary files /dev/null and b/other/nescafe.nes differ diff --git a/other/nestest.log b/other/nestest.log new file mode 100644 index 0000000..92aaa89 --- /dev/null +++ b/other/nestest.log @@ -0,0 +1,8991 @@ +C000 4C F5 C5 JMP $C5F5 A:00 X:00 Y:00 P:24 SP:FD CYC: 0 SL:241 +C5F5 A2 00 LDX #$00 A:00 X:00 Y:00 P:24 SP:FD CYC: 9 SL:241 +C5F7 86 00 STX $00 = 00 A:00 X:00 Y:00 P:26 SP:FD CYC: 15 SL:241 +C5F9 86 10 STX $10 = 00 A:00 X:00 Y:00 P:26 SP:FD CYC: 24 SL:241 +C5FB 86 11 STX $11 = 00 A:00 X:00 Y:00 P:26 SP:FD CYC: 33 SL:241 +C5FD 20 2D C7 JSR $C72D A:00 X:00 Y:00 P:26 SP:FD CYC: 42 SL:241 +C72D EA NOP A:00 X:00 Y:00 P:26 SP:FB CYC: 60 SL:241 +C72E 38 SEC A:00 X:00 Y:00 P:26 SP:FB CYC: 66 SL:241 +C72F B0 04 BCS $C735 A:00 X:00 Y:00 P:27 SP:FB CYC: 72 SL:241 +C735 EA NOP A:00 X:00 Y:00 P:27 SP:FB CYC: 81 SL:241 +C736 18 CLC A:00 X:00 Y:00 P:27 SP:FB CYC: 87 SL:241 +C737 B0 03 BCS $C73C A:00 X:00 Y:00 P:26 SP:FB CYC: 93 SL:241 +C739 4C 40 C7 JMP $C740 A:00 X:00 Y:00 P:26 SP:FB CYC: 99 SL:241 +C740 EA NOP A:00 X:00 Y:00 P:26 SP:FB CYC:108 SL:241 +C741 38 SEC A:00 X:00 Y:00 P:26 SP:FB CYC:114 SL:241 +C742 90 03 BCC $C747 A:00 X:00 Y:00 P:27 SP:FB CYC:120 SL:241 +C744 4C 4B C7 JMP $C74B A:00 X:00 Y:00 P:27 SP:FB CYC:126 SL:241 +C74B EA NOP A:00 X:00 Y:00 P:27 SP:FB CYC:135 SL:241 +C74C 18 CLC A:00 X:00 Y:00 P:27 SP:FB CYC:141 SL:241 +C74D 90 04 BCC $C753 A:00 X:00 Y:00 P:26 SP:FB CYC:147 SL:241 +C753 EA NOP A:00 X:00 Y:00 P:26 SP:FB CYC:156 SL:241 +C754 A9 00 LDA #$00 A:00 X:00 Y:00 P:26 SP:FB CYC:162 SL:241 +C756 F0 04 BEQ $C75C A:00 X:00 Y:00 P:26 SP:FB CYC:168 SL:241 +C75C EA NOP A:00 X:00 Y:00 P:26 SP:FB CYC:177 SL:241 +C75D A9 40 LDA #$40 A:00 X:00 Y:00 P:26 SP:FB CYC:183 SL:241 +C75F F0 03 BEQ $C764 A:40 X:00 Y:00 P:24 SP:FB CYC:189 SL:241 +C761 4C 68 C7 JMP $C768 A:40 X:00 Y:00 P:24 SP:FB CYC:195 SL:241 +C768 EA NOP A:40 X:00 Y:00 P:24 SP:FB CYC:204 SL:241 +C769 A9 40 LDA #$40 A:40 X:00 Y:00 P:24 SP:FB CYC:210 SL:241 +C76B D0 04 BNE $C771 A:40 X:00 Y:00 P:24 SP:FB CYC:216 SL:241 +C771 EA NOP A:40 X:00 Y:00 P:24 SP:FB CYC:225 SL:241 +C772 A9 00 LDA #$00 A:40 X:00 Y:00 P:24 SP:FB CYC:231 SL:241 +C774 D0 03 BNE $C779 A:00 X:00 Y:00 P:26 SP:FB CYC:237 SL:241 +C776 4C 7D C7 JMP $C77D A:00 X:00 Y:00 P:26 SP:FB CYC:243 SL:241 +C77D EA NOP A:00 X:00 Y:00 P:26 SP:FB CYC:252 SL:241 +C77E A9 FF LDA #$FF A:00 X:00 Y:00 P:26 SP:FB CYC:258 SL:241 +C780 85 01 STA $01 = 00 A:FF X:00 Y:00 P:A4 SP:FB CYC:264 SL:241 +C782 24 01 BIT $01 = FF A:FF X:00 Y:00 P:A4 SP:FB CYC:273 SL:241 +C784 70 04 BVS $C78A A:FF X:00 Y:00 P:E4 SP:FB CYC:282 SL:241 +C78A EA NOP A:FF X:00 Y:00 P:E4 SP:FB CYC:291 SL:241 +C78B 24 01 BIT $01 = FF A:FF X:00 Y:00 P:E4 SP:FB CYC:297 SL:241 +C78D 50 03 BVC $C792 A:FF X:00 Y:00 P:E4 SP:FB CYC:306 SL:241 +C78F 4C 96 C7 JMP $C796 A:FF X:00 Y:00 P:E4 SP:FB CYC:312 SL:241 +C796 EA NOP A:FF X:00 Y:00 P:E4 SP:FB CYC:321 SL:241 +C797 A9 00 LDA #$00 A:FF X:00 Y:00 P:E4 SP:FB CYC:327 SL:241 +C799 85 01 STA $01 = FF A:00 X:00 Y:00 P:66 SP:FB CYC:333 SL:241 +C79B 24 01 BIT $01 = 00 A:00 X:00 Y:00 P:66 SP:FB CYC: 1 SL:242 +C79D 50 04 BVC $C7A3 A:00 X:00 Y:00 P:26 SP:FB CYC: 10 SL:242 +C7A3 EA NOP A:00 X:00 Y:00 P:26 SP:FB CYC: 19 SL:242 +C7A4 24 01 BIT $01 = 00 A:00 X:00 Y:00 P:26 SP:FB CYC: 25 SL:242 +C7A6 70 03 BVS $C7AB A:00 X:00 Y:00 P:26 SP:FB CYC: 34 SL:242 +C7A8 4C AF C7 JMP $C7AF A:00 X:00 Y:00 P:26 SP:FB CYC: 40 SL:242 +C7AF EA NOP A:00 X:00 Y:00 P:26 SP:FB CYC: 49 SL:242 +C7B0 A9 00 LDA #$00 A:00 X:00 Y:00 P:26 SP:FB CYC: 55 SL:242 +C7B2 10 04 BPL $C7B8 A:00 X:00 Y:00 P:26 SP:FB CYC: 61 SL:242 +C7B8 EA NOP A:00 X:00 Y:00 P:26 SP:FB CYC: 70 SL:242 +C7B9 A9 80 LDA #$80 A:00 X:00 Y:00 P:26 SP:FB CYC: 76 SL:242 +C7BB 10 03 BPL $C7C0 A:80 X:00 Y:00 P:A4 SP:FB CYC: 82 SL:242 +C7BD 4C D9 C7 JMP $C7D9 A:80 X:00 Y:00 P:A4 SP:FB CYC: 88 SL:242 +C7D9 EA NOP A:80 X:00 Y:00 P:A4 SP:FB CYC: 97 SL:242 +C7DA 60 RTS A:80 X:00 Y:00 P:A4 SP:FB CYC:103 SL:242 +C600 20 DB C7 JSR $C7DB A:80 X:00 Y:00 P:A4 SP:FD CYC:121 SL:242 +C7DB EA NOP A:80 X:00 Y:00 P:A4 SP:FB CYC:139 SL:242 +C7DC A9 FF LDA #$FF A:80 X:00 Y:00 P:A4 SP:FB CYC:145 SL:242 +C7DE 85 01 STA $01 = 00 A:FF X:00 Y:00 P:A4 SP:FB CYC:151 SL:242 +C7E0 24 01 BIT $01 = FF A:FF X:00 Y:00 P:A4 SP:FB CYC:160 SL:242 +C7E2 A9 00 LDA #$00 A:FF X:00 Y:00 P:E4 SP:FB CYC:169 SL:242 +C7E4 38 SEC A:00 X:00 Y:00 P:66 SP:FB CYC:175 SL:242 +C7E5 78 SEI A:00 X:00 Y:00 P:67 SP:FB CYC:181 SL:242 +C7E6 F8 SED A:00 X:00 Y:00 P:67 SP:FB CYC:187 SL:242 +C7E7 08 PHP A:00 X:00 Y:00 P:6F SP:FB CYC:193 SL:242 +C7E8 68 PLA A:00 X:00 Y:00 P:6F SP:FA CYC:202 SL:242 +C7E9 29 EF AND #$EF A:7F X:00 Y:00 P:6D SP:FB CYC:214 SL:242 +C7EB C9 6F CMP #$6F A:6F X:00 Y:00 P:6D SP:FB CYC:220 SL:242 +C7ED F0 04 BEQ $C7F3 A:6F X:00 Y:00 P:6F SP:FB CYC:226 SL:242 +C7F3 EA NOP A:6F X:00 Y:00 P:6F SP:FB CYC:235 SL:242 +C7F4 A9 40 LDA #$40 A:6F X:00 Y:00 P:6F SP:FB CYC:241 SL:242 +C7F6 85 01 STA $01 = FF A:40 X:00 Y:00 P:6D SP:FB CYC:247 SL:242 +C7F8 24 01 BIT $01 = 40 A:40 X:00 Y:00 P:6D SP:FB CYC:256 SL:242 +C7FA D8 CLD A:40 X:00 Y:00 P:6D SP:FB CYC:265 SL:242 +C7FB A9 10 LDA #$10 A:40 X:00 Y:00 P:65 SP:FB CYC:271 SL:242 +C7FD 18 CLC A:10 X:00 Y:00 P:65 SP:FB CYC:277 SL:242 +C7FE 08 PHP A:10 X:00 Y:00 P:64 SP:FB CYC:283 SL:242 +C7FF 68 PLA A:10 X:00 Y:00 P:64 SP:FA CYC:292 SL:242 +C800 29 EF AND #$EF A:74 X:00 Y:00 P:64 SP:FB CYC:304 SL:242 +C802 C9 64 CMP #$64 A:64 X:00 Y:00 P:64 SP:FB CYC:310 SL:242 +C804 F0 04 BEQ $C80A A:64 X:00 Y:00 P:67 SP:FB CYC:316 SL:242 +C80A EA NOP A:64 X:00 Y:00 P:67 SP:FB CYC:325 SL:242 +C80B A9 80 LDA #$80 A:64 X:00 Y:00 P:67 SP:FB CYC:331 SL:242 +C80D 85 01 STA $01 = 40 A:80 X:00 Y:00 P:E5 SP:FB CYC:337 SL:242 +C80F 24 01 BIT $01 = 80 A:80 X:00 Y:00 P:E5 SP:FB CYC: 5 SL:243 +C811 F8 SED A:80 X:00 Y:00 P:A5 SP:FB CYC: 14 SL:243 +C812 A9 00 LDA #$00 A:80 X:00 Y:00 P:AD SP:FB CYC: 20 SL:243 +C814 38 SEC A:00 X:00 Y:00 P:2F SP:FB CYC: 26 SL:243 +C815 08 PHP A:00 X:00 Y:00 P:2F SP:FB CYC: 32 SL:243 +C816 68 PLA A:00 X:00 Y:00 P:2F SP:FA CYC: 41 SL:243 +C817 29 EF AND #$EF A:3F X:00 Y:00 P:2D SP:FB CYC: 53 SL:243 +C819 C9 2F CMP #$2F A:2F X:00 Y:00 P:2D SP:FB CYC: 59 SL:243 +C81B F0 04 BEQ $C821 A:2F X:00 Y:00 P:2F SP:FB CYC: 65 SL:243 +C821 EA NOP A:2F X:00 Y:00 P:2F SP:FB CYC: 74 SL:243 +C822 A9 FF LDA #$FF A:2F X:00 Y:00 P:2F SP:FB CYC: 80 SL:243 +C824 48 PHA A:FF X:00 Y:00 P:AD SP:FB CYC: 86 SL:243 +C825 28 PLP A:FF X:00 Y:00 P:AD SP:FA CYC: 95 SL:243 +C826 D0 09 BNE $C831 A:FF X:00 Y:00 P:EF SP:FB CYC:107 SL:243 +C828 10 07 BPL $C831 A:FF X:00 Y:00 P:EF SP:FB CYC:113 SL:243 +C82A 50 05 BVC $C831 A:FF X:00 Y:00 P:EF SP:FB CYC:119 SL:243 +C82C 90 03 BCC $C831 A:FF X:00 Y:00 P:EF SP:FB CYC:125 SL:243 +C82E 4C 35 C8 JMP $C835 A:FF X:00 Y:00 P:EF SP:FB CYC:131 SL:243 +C835 EA NOP A:FF X:00 Y:00 P:EF SP:FB CYC:140 SL:243 +C836 A9 04 LDA #$04 A:FF X:00 Y:00 P:EF SP:FB CYC:146 SL:243 +C838 48 PHA A:04 X:00 Y:00 P:6D SP:FB CYC:152 SL:243 +C839 28 PLP A:04 X:00 Y:00 P:6D SP:FA CYC:161 SL:243 +C83A F0 09 BEQ $C845 A:04 X:00 Y:00 P:24 SP:FB CYC:173 SL:243 +C83C 30 07 BMI $C845 A:04 X:00 Y:00 P:24 SP:FB CYC:179 SL:243 +C83E 70 05 BVS $C845 A:04 X:00 Y:00 P:24 SP:FB CYC:185 SL:243 +C840 B0 03 BCS $C845 A:04 X:00 Y:00 P:24 SP:FB CYC:191 SL:243 +C842 4C 49 C8 JMP $C849 A:04 X:00 Y:00 P:24 SP:FB CYC:197 SL:243 +C849 EA NOP A:04 X:00 Y:00 P:24 SP:FB CYC:206 SL:243 +C84A F8 SED A:04 X:00 Y:00 P:24 SP:FB CYC:212 SL:243 +C84B A9 FF LDA #$FF A:04 X:00 Y:00 P:2C SP:FB CYC:218 SL:243 +C84D 85 01 STA $01 = 80 A:FF X:00 Y:00 P:AC SP:FB CYC:224 SL:243 +C84F 24 01 BIT $01 = FF A:FF X:00 Y:00 P:AC SP:FB CYC:233 SL:243 +C851 18 CLC A:FF X:00 Y:00 P:EC SP:FB CYC:242 SL:243 +C852 A9 00 LDA #$00 A:FF X:00 Y:00 P:EC SP:FB CYC:248 SL:243 +C854 48 PHA A:00 X:00 Y:00 P:6E SP:FB CYC:254 SL:243 +C855 A9 FF LDA #$FF A:00 X:00 Y:00 P:6E SP:FA CYC:263 SL:243 +C857 68 PLA A:FF X:00 Y:00 P:EC SP:FA CYC:269 SL:243 +C858 D0 09 BNE $C863 A:00 X:00 Y:00 P:6E SP:FB CYC:281 SL:243 +C85A 30 07 BMI $C863 A:00 X:00 Y:00 P:6E SP:FB CYC:287 SL:243 +C85C 50 05 BVC $C863 A:00 X:00 Y:00 P:6E SP:FB CYC:293 SL:243 +C85E B0 03 BCS $C863 A:00 X:00 Y:00 P:6E SP:FB CYC:299 SL:243 +C860 4C 67 C8 JMP $C867 A:00 X:00 Y:00 P:6E SP:FB CYC:305 SL:243 +C867 EA NOP A:00 X:00 Y:00 P:6E SP:FB CYC:314 SL:243 +C868 A9 00 LDA #$00 A:00 X:00 Y:00 P:6E SP:FB CYC:320 SL:243 +C86A 85 01 STA $01 = FF A:00 X:00 Y:00 P:6E SP:FB CYC:326 SL:243 +C86C 24 01 BIT $01 = 00 A:00 X:00 Y:00 P:6E SP:FB CYC:335 SL:243 +C86E 38 SEC A:00 X:00 Y:00 P:2E SP:FB CYC: 3 SL:244 +C86F A9 FF LDA #$FF A:00 X:00 Y:00 P:2F SP:FB CYC: 9 SL:244 +C871 48 PHA A:FF X:00 Y:00 P:AD SP:FB CYC: 15 SL:244 +C872 A9 00 LDA #$00 A:FF X:00 Y:00 P:AD SP:FA CYC: 24 SL:244 +C874 68 PLA A:00 X:00 Y:00 P:2F SP:FA CYC: 30 SL:244 +C875 F0 09 BEQ $C880 A:FF X:00 Y:00 P:AD SP:FB CYC: 42 SL:244 +C877 10 07 BPL $C880 A:FF X:00 Y:00 P:AD SP:FB CYC: 48 SL:244 +C879 70 05 BVS $C880 A:FF X:00 Y:00 P:AD SP:FB CYC: 54 SL:244 +C87B 90 03 BCC $C880 A:FF X:00 Y:00 P:AD SP:FB CYC: 60 SL:244 +C87D 4C 84 C8 JMP $C884 A:FF X:00 Y:00 P:AD SP:FB CYC: 66 SL:244 +C884 60 RTS A:FF X:00 Y:00 P:AD SP:FB CYC: 75 SL:244 +C603 20 85 C8 JSR $C885 A:FF X:00 Y:00 P:AD SP:FD CYC: 93 SL:244 +C885 EA NOP A:FF X:00 Y:00 P:AD SP:FB CYC:111 SL:244 +C886 18 CLC A:FF X:00 Y:00 P:AD SP:FB CYC:117 SL:244 +C887 A9 FF LDA #$FF A:FF X:00 Y:00 P:AC SP:FB CYC:123 SL:244 +C889 85 01 STA $01 = 00 A:FF X:00 Y:00 P:AC SP:FB CYC:129 SL:244 +C88B 24 01 BIT $01 = FF A:FF X:00 Y:00 P:AC SP:FB CYC:138 SL:244 +C88D A9 55 LDA #$55 A:FF X:00 Y:00 P:EC SP:FB CYC:147 SL:244 +C88F 09 AA ORA #$AA A:55 X:00 Y:00 P:6C SP:FB CYC:153 SL:244 +C891 B0 0B BCS $C89E A:FF X:00 Y:00 P:EC SP:FB CYC:159 SL:244 +C893 10 09 BPL $C89E A:FF X:00 Y:00 P:EC SP:FB CYC:165 SL:244 +C895 C9 FF CMP #$FF A:FF X:00 Y:00 P:EC SP:FB CYC:171 SL:244 +C897 D0 05 BNE $C89E A:FF X:00 Y:00 P:6F SP:FB CYC:177 SL:244 +C899 50 03 BVC $C89E A:FF X:00 Y:00 P:6F SP:FB CYC:183 SL:244 +C89B 4C A2 C8 JMP $C8A2 A:FF X:00 Y:00 P:6F SP:FB CYC:189 SL:244 +C8A2 EA NOP A:FF X:00 Y:00 P:6F SP:FB CYC:198 SL:244 +C8A3 38 SEC A:FF X:00 Y:00 P:6F SP:FB CYC:204 SL:244 +C8A4 B8 CLV A:FF X:00 Y:00 P:6F SP:FB CYC:210 SL:244 +C8A5 A9 00 LDA #$00 A:FF X:00 Y:00 P:2F SP:FB CYC:216 SL:244 +C8A7 09 00 ORA #$00 A:00 X:00 Y:00 P:2F SP:FB CYC:222 SL:244 +C8A9 D0 09 BNE $C8B4 A:00 X:00 Y:00 P:2F SP:FB CYC:228 SL:244 +C8AB 70 07 BVS $C8B4 A:00 X:00 Y:00 P:2F SP:FB CYC:234 SL:244 +C8AD 90 05 BCC $C8B4 A:00 X:00 Y:00 P:2F SP:FB CYC:240 SL:244 +C8AF 30 03 BMI $C8B4 A:00 X:00 Y:00 P:2F SP:FB CYC:246 SL:244 +C8B1 4C B8 C8 JMP $C8B8 A:00 X:00 Y:00 P:2F SP:FB CYC:252 SL:244 +C8B8 EA NOP A:00 X:00 Y:00 P:2F SP:FB CYC:261 SL:244 +C8B9 18 CLC A:00 X:00 Y:00 P:2F SP:FB CYC:267 SL:244 +C8BA 24 01 BIT $01 = FF A:00 X:00 Y:00 P:2E SP:FB CYC:273 SL:244 +C8BC A9 55 LDA #$55 A:00 X:00 Y:00 P:EE SP:FB CYC:282 SL:244 +C8BE 29 AA AND #$AA A:55 X:00 Y:00 P:6C SP:FB CYC:288 SL:244 +C8C0 D0 09 BNE $C8CB A:00 X:00 Y:00 P:6E SP:FB CYC:294 SL:244 +C8C2 50 07 BVC $C8CB A:00 X:00 Y:00 P:6E SP:FB CYC:300 SL:244 +C8C4 B0 05 BCS $C8CB A:00 X:00 Y:00 P:6E SP:FB CYC:306 SL:244 +C8C6 30 03 BMI $C8CB A:00 X:00 Y:00 P:6E SP:FB CYC:312 SL:244 +C8C8 4C CF C8 JMP $C8CF A:00 X:00 Y:00 P:6E SP:FB CYC:318 SL:244 +C8CF EA NOP A:00 X:00 Y:00 P:6E SP:FB CYC:327 SL:244 +C8D0 38 SEC A:00 X:00 Y:00 P:6E SP:FB CYC:333 SL:244 +C8D1 B8 CLV A:00 X:00 Y:00 P:6F SP:FB CYC:339 SL:244 +C8D2 A9 F8 LDA #$F8 A:00 X:00 Y:00 P:2F SP:FB CYC: 4 SL:245 +C8D4 29 EF AND #$EF A:F8 X:00 Y:00 P:AD SP:FB CYC: 10 SL:245 +C8D6 90 0B BCC $C8E3 A:E8 X:00 Y:00 P:AD SP:FB CYC: 16 SL:245 +C8D8 10 09 BPL $C8E3 A:E8 X:00 Y:00 P:AD SP:FB CYC: 22 SL:245 +C8DA C9 E8 CMP #$E8 A:E8 X:00 Y:00 P:AD SP:FB CYC: 28 SL:245 +C8DC D0 05 BNE $C8E3 A:E8 X:00 Y:00 P:2F SP:FB CYC: 34 SL:245 +C8DE 70 03 BVS $C8E3 A:E8 X:00 Y:00 P:2F SP:FB CYC: 40 SL:245 +C8E0 4C E7 C8 JMP $C8E7 A:E8 X:00 Y:00 P:2F SP:FB CYC: 46 SL:245 +C8E7 EA NOP A:E8 X:00 Y:00 P:2F SP:FB CYC: 55 SL:245 +C8E8 18 CLC A:E8 X:00 Y:00 P:2F SP:FB CYC: 61 SL:245 +C8E9 24 01 BIT $01 = FF A:E8 X:00 Y:00 P:2E SP:FB CYC: 67 SL:245 +C8EB A9 5F LDA #$5F A:E8 X:00 Y:00 P:EC SP:FB CYC: 76 SL:245 +C8ED 49 AA EOR #$AA A:5F X:00 Y:00 P:6C SP:FB CYC: 82 SL:245 +C8EF B0 0B BCS $C8FC A:F5 X:00 Y:00 P:EC SP:FB CYC: 88 SL:245 +C8F1 10 09 BPL $C8FC A:F5 X:00 Y:00 P:EC SP:FB CYC: 94 SL:245 +C8F3 C9 F5 CMP #$F5 A:F5 X:00 Y:00 P:EC SP:FB CYC:100 SL:245 +C8F5 D0 05 BNE $C8FC A:F5 X:00 Y:00 P:6F SP:FB CYC:106 SL:245 +C8F7 50 03 BVC $C8FC A:F5 X:00 Y:00 P:6F SP:FB CYC:112 SL:245 +C8F9 4C 00 C9 JMP $C900 A:F5 X:00 Y:00 P:6F SP:FB CYC:118 SL:245 +C900 EA NOP A:F5 X:00 Y:00 P:6F SP:FB CYC:127 SL:245 +C901 38 SEC A:F5 X:00 Y:00 P:6F SP:FB CYC:133 SL:245 +C902 B8 CLV A:F5 X:00 Y:00 P:6F SP:FB CYC:139 SL:245 +C903 A9 70 LDA #$70 A:F5 X:00 Y:00 P:2F SP:FB CYC:145 SL:245 +C905 49 70 EOR #$70 A:70 X:00 Y:00 P:2D SP:FB CYC:151 SL:245 +C907 D0 09 BNE $C912 A:00 X:00 Y:00 P:2F SP:FB CYC:157 SL:245 +C909 70 07 BVS $C912 A:00 X:00 Y:00 P:2F SP:FB CYC:163 SL:245 +C90B 90 05 BCC $C912 A:00 X:00 Y:00 P:2F SP:FB CYC:169 SL:245 +C90D 30 03 BMI $C912 A:00 X:00 Y:00 P:2F SP:FB CYC:175 SL:245 +C90F 4C 16 C9 JMP $C916 A:00 X:00 Y:00 P:2F SP:FB CYC:181 SL:245 +C916 EA NOP A:00 X:00 Y:00 P:2F SP:FB CYC:190 SL:245 +C917 18 CLC A:00 X:00 Y:00 P:2F SP:FB CYC:196 SL:245 +C918 24 01 BIT $01 = FF A:00 X:00 Y:00 P:2E SP:FB CYC:202 SL:245 +C91A A9 00 LDA #$00 A:00 X:00 Y:00 P:EE SP:FB CYC:211 SL:245 +C91C 69 69 ADC #$69 A:00 X:00 Y:00 P:6E SP:FB CYC:217 SL:245 +C91E 30 0B BMI $C92B A:69 X:00 Y:00 P:2C SP:FB CYC:223 SL:245 +C920 B0 09 BCS $C92B A:69 X:00 Y:00 P:2C SP:FB CYC:229 SL:245 +C922 C9 69 CMP #$69 A:69 X:00 Y:00 P:2C SP:FB CYC:235 SL:245 +C924 D0 05 BNE $C92B A:69 X:00 Y:00 P:2F SP:FB CYC:241 SL:245 +C926 70 03 BVS $C92B A:69 X:00 Y:00 P:2F SP:FB CYC:247 SL:245 +C928 4C 2F C9 JMP $C92F A:69 X:00 Y:00 P:2F SP:FB CYC:253 SL:245 +C92F EA NOP A:69 X:00 Y:00 P:2F SP:FB CYC:262 SL:245 +C930 38 SEC A:69 X:00 Y:00 P:2F SP:FB CYC:268 SL:245 +C931 F8 SED A:69 X:00 Y:00 P:2F SP:FB CYC:274 SL:245 +C932 24 01 BIT $01 = FF A:69 X:00 Y:00 P:2F SP:FB CYC:280 SL:245 +C934 A9 01 LDA #$01 A:69 X:00 Y:00 P:ED SP:FB CYC:289 SL:245 +C936 69 69 ADC #$69 A:01 X:00 Y:00 P:6D SP:FB CYC:295 SL:245 +C938 30 0B BMI $C945 A:6B X:00 Y:00 P:2C SP:FB CYC:301 SL:245 +C93A B0 09 BCS $C945 A:6B X:00 Y:00 P:2C SP:FB CYC:307 SL:245 +C93C C9 6B CMP #$6B A:6B X:00 Y:00 P:2C SP:FB CYC:313 SL:245 +C93E D0 05 BNE $C945 A:6B X:00 Y:00 P:2F SP:FB CYC:319 SL:245 +C940 70 03 BVS $C945 A:6B X:00 Y:00 P:2F SP:FB CYC:325 SL:245 +C942 4C 49 C9 JMP $C949 A:6B X:00 Y:00 P:2F SP:FB CYC:331 SL:245 +C949 EA NOP A:6B X:00 Y:00 P:2F SP:FB CYC:340 SL:245 +C94A D8 CLD A:6B X:00 Y:00 P:2F SP:FB CYC: 5 SL:246 +C94B 38 SEC A:6B X:00 Y:00 P:27 SP:FB CYC: 11 SL:246 +C94C B8 CLV A:6B X:00 Y:00 P:27 SP:FB CYC: 17 SL:246 +C94D A9 7F LDA #$7F A:6B X:00 Y:00 P:27 SP:FB CYC: 23 SL:246 +C94F 69 7F ADC #$7F A:7F X:00 Y:00 P:25 SP:FB CYC: 29 SL:246 +C951 10 0B BPL $C95E A:FF X:00 Y:00 P:E4 SP:FB CYC: 35 SL:246 +C953 B0 09 BCS $C95E A:FF X:00 Y:00 P:E4 SP:FB CYC: 41 SL:246 +C955 C9 FF CMP #$FF A:FF X:00 Y:00 P:E4 SP:FB CYC: 47 SL:246 +C957 D0 05 BNE $C95E A:FF X:00 Y:00 P:67 SP:FB CYC: 53 SL:246 +C959 50 03 BVC $C95E A:FF X:00 Y:00 P:67 SP:FB CYC: 59 SL:246 +C95B 4C 62 C9 JMP $C962 A:FF X:00 Y:00 P:67 SP:FB CYC: 65 SL:246 +C962 EA NOP A:FF X:00 Y:00 P:67 SP:FB CYC: 74 SL:246 +C963 18 CLC A:FF X:00 Y:00 P:67 SP:FB CYC: 80 SL:246 +C964 24 01 BIT $01 = FF A:FF X:00 Y:00 P:66 SP:FB CYC: 86 SL:246 +C966 A9 7F LDA #$7F A:FF X:00 Y:00 P:E4 SP:FB CYC: 95 SL:246 +C968 69 80 ADC #$80 A:7F X:00 Y:00 P:64 SP:FB CYC:101 SL:246 +C96A 10 0B BPL $C977 A:FF X:00 Y:00 P:A4 SP:FB CYC:107 SL:246 +C96C B0 09 BCS $C977 A:FF X:00 Y:00 P:A4 SP:FB CYC:113 SL:246 +C96E C9 FF CMP #$FF A:FF X:00 Y:00 P:A4 SP:FB CYC:119 SL:246 +C970 D0 05 BNE $C977 A:FF X:00 Y:00 P:27 SP:FB CYC:125 SL:246 +C972 70 03 BVS $C977 A:FF X:00 Y:00 P:27 SP:FB CYC:131 SL:246 +C974 4C 7B C9 JMP $C97B A:FF X:00 Y:00 P:27 SP:FB CYC:137 SL:246 +C97B EA NOP A:FF X:00 Y:00 P:27 SP:FB CYC:146 SL:246 +C97C 38 SEC A:FF X:00 Y:00 P:27 SP:FB CYC:152 SL:246 +C97D B8 CLV A:FF X:00 Y:00 P:27 SP:FB CYC:158 SL:246 +C97E A9 7F LDA #$7F A:FF X:00 Y:00 P:27 SP:FB CYC:164 SL:246 +C980 69 80 ADC #$80 A:7F X:00 Y:00 P:25 SP:FB CYC:170 SL:246 +C982 D0 09 BNE $C98D A:00 X:00 Y:00 P:27 SP:FB CYC:176 SL:246 +C984 30 07 BMI $C98D A:00 X:00 Y:00 P:27 SP:FB CYC:182 SL:246 +C986 70 05 BVS $C98D A:00 X:00 Y:00 P:27 SP:FB CYC:188 SL:246 +C988 90 03 BCC $C98D A:00 X:00 Y:00 P:27 SP:FB CYC:194 SL:246 +C98A 4C 91 C9 JMP $C991 A:00 X:00 Y:00 P:27 SP:FB CYC:200 SL:246 +C991 EA NOP A:00 X:00 Y:00 P:27 SP:FB CYC:209 SL:246 +C992 38 SEC A:00 X:00 Y:00 P:27 SP:FB CYC:215 SL:246 +C993 B8 CLV A:00 X:00 Y:00 P:27 SP:FB CYC:221 SL:246 +C994 A9 9F LDA #$9F A:00 X:00 Y:00 P:27 SP:FB CYC:227 SL:246 +C996 F0 09 BEQ $C9A1 A:9F X:00 Y:00 P:A5 SP:FB CYC:233 SL:246 +C998 10 07 BPL $C9A1 A:9F X:00 Y:00 P:A5 SP:FB CYC:239 SL:246 +C99A 70 05 BVS $C9A1 A:9F X:00 Y:00 P:A5 SP:FB CYC:245 SL:246 +C99C 90 03 BCC $C9A1 A:9F X:00 Y:00 P:A5 SP:FB CYC:251 SL:246 +C99E 4C A5 C9 JMP $C9A5 A:9F X:00 Y:00 P:A5 SP:FB CYC:257 SL:246 +C9A5 EA NOP A:9F X:00 Y:00 P:A5 SP:FB CYC:266 SL:246 +C9A6 18 CLC A:9F X:00 Y:00 P:A5 SP:FB CYC:272 SL:246 +C9A7 24 01 BIT $01 = FF A:9F X:00 Y:00 P:A4 SP:FB CYC:278 SL:246 +C9A9 A9 00 LDA #$00 A:9F X:00 Y:00 P:E4 SP:FB CYC:287 SL:246 +C9AB D0 09 BNE $C9B6 A:00 X:00 Y:00 P:66 SP:FB CYC:293 SL:246 +C9AD 30 07 BMI $C9B6 A:00 X:00 Y:00 P:66 SP:FB CYC:299 SL:246 +C9AF 50 05 BVC $C9B6 A:00 X:00 Y:00 P:66 SP:FB CYC:305 SL:246 +C9B1 B0 03 BCS $C9B6 A:00 X:00 Y:00 P:66 SP:FB CYC:311 SL:246 +C9B3 4C BA C9 JMP $C9BA A:00 X:00 Y:00 P:66 SP:FB CYC:317 SL:246 +C9BA EA NOP A:00 X:00 Y:00 P:66 SP:FB CYC:326 SL:246 +C9BB 24 01 BIT $01 = FF A:00 X:00 Y:00 P:66 SP:FB CYC:332 SL:246 +C9BD A9 40 LDA #$40 A:00 X:00 Y:00 P:E6 SP:FB CYC: 0 SL:247 +C9BF C9 40 CMP #$40 A:40 X:00 Y:00 P:64 SP:FB CYC: 6 SL:247 +C9C1 30 09 BMI $C9CC A:40 X:00 Y:00 P:67 SP:FB CYC: 12 SL:247 +C9C3 90 07 BCC $C9CC A:40 X:00 Y:00 P:67 SP:FB CYC: 18 SL:247 +C9C5 D0 05 BNE $C9CC A:40 X:00 Y:00 P:67 SP:FB CYC: 24 SL:247 +C9C7 50 03 BVC $C9CC A:40 X:00 Y:00 P:67 SP:FB CYC: 30 SL:247 +C9C9 4C D0 C9 JMP $C9D0 A:40 X:00 Y:00 P:67 SP:FB CYC: 36 SL:247 +C9D0 EA NOP A:40 X:00 Y:00 P:67 SP:FB CYC: 45 SL:247 +C9D1 B8 CLV A:40 X:00 Y:00 P:67 SP:FB CYC: 51 SL:247 +C9D2 C9 3F CMP #$3F A:40 X:00 Y:00 P:27 SP:FB CYC: 57 SL:247 +C9D4 F0 09 BEQ $C9DF A:40 X:00 Y:00 P:25 SP:FB CYC: 63 SL:247 +C9D6 30 07 BMI $C9DF A:40 X:00 Y:00 P:25 SP:FB CYC: 69 SL:247 +C9D8 90 05 BCC $C9DF A:40 X:00 Y:00 P:25 SP:FB CYC: 75 SL:247 +C9DA 70 03 BVS $C9DF A:40 X:00 Y:00 P:25 SP:FB CYC: 81 SL:247 +C9DC 4C E3 C9 JMP $C9E3 A:40 X:00 Y:00 P:25 SP:FB CYC: 87 SL:247 +C9E3 EA NOP A:40 X:00 Y:00 P:25 SP:FB CYC: 96 SL:247 +C9E4 C9 41 CMP #$41 A:40 X:00 Y:00 P:25 SP:FB CYC:102 SL:247 +C9E6 F0 07 BEQ $C9EF A:40 X:00 Y:00 P:A4 SP:FB CYC:108 SL:247 +C9E8 10 05 BPL $C9EF A:40 X:00 Y:00 P:A4 SP:FB CYC:114 SL:247 +C9EA 10 03 BPL $C9EF A:40 X:00 Y:00 P:A4 SP:FB CYC:120 SL:247 +C9EC 4C F3 C9 JMP $C9F3 A:40 X:00 Y:00 P:A4 SP:FB CYC:126 SL:247 +C9F3 EA NOP A:40 X:00 Y:00 P:A4 SP:FB CYC:135 SL:247 +C9F4 A9 80 LDA #$80 A:40 X:00 Y:00 P:A4 SP:FB CYC:141 SL:247 +C9F6 C9 00 CMP #$00 A:80 X:00 Y:00 P:A4 SP:FB CYC:147 SL:247 +C9F8 F0 07 BEQ $CA01 A:80 X:00 Y:00 P:A5 SP:FB CYC:153 SL:247 +C9FA 10 05 BPL $CA01 A:80 X:00 Y:00 P:A5 SP:FB CYC:159 SL:247 +C9FC 90 03 BCC $CA01 A:80 X:00 Y:00 P:A5 SP:FB CYC:165 SL:247 +C9FE 4C 05 CA JMP $CA05 A:80 X:00 Y:00 P:A5 SP:FB CYC:171 SL:247 +CA05 EA NOP A:80 X:00 Y:00 P:A5 SP:FB CYC:180 SL:247 +CA06 C9 80 CMP #$80 A:80 X:00 Y:00 P:A5 SP:FB CYC:186 SL:247 +CA08 D0 07 BNE $CA11 A:80 X:00 Y:00 P:27 SP:FB CYC:192 SL:247 +CA0A 30 05 BMI $CA11 A:80 X:00 Y:00 P:27 SP:FB CYC:198 SL:247 +CA0C 90 03 BCC $CA11 A:80 X:00 Y:00 P:27 SP:FB CYC:204 SL:247 +CA0E 4C 15 CA JMP $CA15 A:80 X:00 Y:00 P:27 SP:FB CYC:210 SL:247 +CA15 EA NOP A:80 X:00 Y:00 P:27 SP:FB CYC:219 SL:247 +CA16 C9 81 CMP #$81 A:80 X:00 Y:00 P:27 SP:FB CYC:225 SL:247 +CA18 B0 07 BCS $CA21 A:80 X:00 Y:00 P:A4 SP:FB CYC:231 SL:247 +CA1A F0 05 BEQ $CA21 A:80 X:00 Y:00 P:A4 SP:FB CYC:237 SL:247 +CA1C 10 03 BPL $CA21 A:80 X:00 Y:00 P:A4 SP:FB CYC:243 SL:247 +CA1E 4C 25 CA JMP $CA25 A:80 X:00 Y:00 P:A4 SP:FB CYC:249 SL:247 +CA25 EA NOP A:80 X:00 Y:00 P:A4 SP:FB CYC:258 SL:247 +CA26 C9 7F CMP #$7F A:80 X:00 Y:00 P:A4 SP:FB CYC:264 SL:247 +CA28 90 07 BCC $CA31 A:80 X:00 Y:00 P:25 SP:FB CYC:270 SL:247 +CA2A F0 05 BEQ $CA31 A:80 X:00 Y:00 P:25 SP:FB CYC:276 SL:247 +CA2C 30 03 BMI $CA31 A:80 X:00 Y:00 P:25 SP:FB CYC:282 SL:247 +CA2E 4C 35 CA JMP $CA35 A:80 X:00 Y:00 P:25 SP:FB CYC:288 SL:247 +CA35 EA NOP A:80 X:00 Y:00 P:25 SP:FB CYC:297 SL:247 +CA36 24 01 BIT $01 = FF A:80 X:00 Y:00 P:25 SP:FB CYC:303 SL:247 +CA38 A0 40 LDY #$40 A:80 X:00 Y:00 P:E5 SP:FB CYC:312 SL:247 +CA3A C0 40 CPY #$40 A:80 X:00 Y:40 P:65 SP:FB CYC:318 SL:247 +CA3C D0 09 BNE $CA47 A:80 X:00 Y:40 P:67 SP:FB CYC:324 SL:247 +CA3E 30 07 BMI $CA47 A:80 X:00 Y:40 P:67 SP:FB CYC:330 SL:247 +CA40 90 05 BCC $CA47 A:80 X:00 Y:40 P:67 SP:FB CYC:336 SL:247 +CA42 50 03 BVC $CA47 A:80 X:00 Y:40 P:67 SP:FB CYC: 1 SL:248 +CA44 4C 4B CA JMP $CA4B A:80 X:00 Y:40 P:67 SP:FB CYC: 7 SL:248 +CA4B EA NOP A:80 X:00 Y:40 P:67 SP:FB CYC: 16 SL:248 +CA4C B8 CLV A:80 X:00 Y:40 P:67 SP:FB CYC: 22 SL:248 +CA4D C0 3F CPY #$3F A:80 X:00 Y:40 P:27 SP:FB CYC: 28 SL:248 +CA4F F0 09 BEQ $CA5A A:80 X:00 Y:40 P:25 SP:FB CYC: 34 SL:248 +CA51 30 07 BMI $CA5A A:80 X:00 Y:40 P:25 SP:FB CYC: 40 SL:248 +CA53 90 05 BCC $CA5A A:80 X:00 Y:40 P:25 SP:FB CYC: 46 SL:248 +CA55 70 03 BVS $CA5A A:80 X:00 Y:40 P:25 SP:FB CYC: 52 SL:248 +CA57 4C 5E CA JMP $CA5E A:80 X:00 Y:40 P:25 SP:FB CYC: 58 SL:248 +CA5E EA NOP A:80 X:00 Y:40 P:25 SP:FB CYC: 67 SL:248 +CA5F C0 41 CPY #$41 A:80 X:00 Y:40 P:25 SP:FB CYC: 73 SL:248 +CA61 F0 07 BEQ $CA6A A:80 X:00 Y:40 P:A4 SP:FB CYC: 79 SL:248 +CA63 10 05 BPL $CA6A A:80 X:00 Y:40 P:A4 SP:FB CYC: 85 SL:248 +CA65 10 03 BPL $CA6A A:80 X:00 Y:40 P:A4 SP:FB CYC: 91 SL:248 +CA67 4C 6E CA JMP $CA6E A:80 X:00 Y:40 P:A4 SP:FB CYC: 97 SL:248 +CA6E EA NOP A:80 X:00 Y:40 P:A4 SP:FB CYC:106 SL:248 +CA6F A0 80 LDY #$80 A:80 X:00 Y:40 P:A4 SP:FB CYC:112 SL:248 +CA71 C0 00 CPY #$00 A:80 X:00 Y:80 P:A4 SP:FB CYC:118 SL:248 +CA73 F0 07 BEQ $CA7C A:80 X:00 Y:80 P:A5 SP:FB CYC:124 SL:248 +CA75 10 05 BPL $CA7C A:80 X:00 Y:80 P:A5 SP:FB CYC:130 SL:248 +CA77 90 03 BCC $CA7C A:80 X:00 Y:80 P:A5 SP:FB CYC:136 SL:248 +CA79 4C 80 CA JMP $CA80 A:80 X:00 Y:80 P:A5 SP:FB CYC:142 SL:248 +CA80 EA NOP A:80 X:00 Y:80 P:A5 SP:FB CYC:151 SL:248 +CA81 C0 80 CPY #$80 A:80 X:00 Y:80 P:A5 SP:FB CYC:157 SL:248 +CA83 D0 07 BNE $CA8C A:80 X:00 Y:80 P:27 SP:FB CYC:163 SL:248 +CA85 30 05 BMI $CA8C A:80 X:00 Y:80 P:27 SP:FB CYC:169 SL:248 +CA87 90 03 BCC $CA8C A:80 X:00 Y:80 P:27 SP:FB CYC:175 SL:248 +CA89 4C 90 CA JMP $CA90 A:80 X:00 Y:80 P:27 SP:FB CYC:181 SL:248 +CA90 EA NOP A:80 X:00 Y:80 P:27 SP:FB CYC:190 SL:248 +CA91 C0 81 CPY #$81 A:80 X:00 Y:80 P:27 SP:FB CYC:196 SL:248 +CA93 B0 07 BCS $CA9C A:80 X:00 Y:80 P:A4 SP:FB CYC:202 SL:248 +CA95 F0 05 BEQ $CA9C A:80 X:00 Y:80 P:A4 SP:FB CYC:208 SL:248 +CA97 10 03 BPL $CA9C A:80 X:00 Y:80 P:A4 SP:FB CYC:214 SL:248 +CA99 4C A0 CA JMP $CAA0 A:80 X:00 Y:80 P:A4 SP:FB CYC:220 SL:248 +CAA0 EA NOP A:80 X:00 Y:80 P:A4 SP:FB CYC:229 SL:248 +CAA1 C0 7F CPY #$7F A:80 X:00 Y:80 P:A4 SP:FB CYC:235 SL:248 +CAA3 90 07 BCC $CAAC A:80 X:00 Y:80 P:25 SP:FB CYC:241 SL:248 +CAA5 F0 05 BEQ $CAAC A:80 X:00 Y:80 P:25 SP:FB CYC:247 SL:248 +CAA7 30 03 BMI $CAAC A:80 X:00 Y:80 P:25 SP:FB CYC:253 SL:248 +CAA9 4C B0 CA JMP $CAB0 A:80 X:00 Y:80 P:25 SP:FB CYC:259 SL:248 +CAB0 EA NOP A:80 X:00 Y:80 P:25 SP:FB CYC:268 SL:248 +CAB1 24 01 BIT $01 = FF A:80 X:00 Y:80 P:25 SP:FB CYC:274 SL:248 +CAB3 A2 40 LDX #$40 A:80 X:00 Y:80 P:E5 SP:FB CYC:283 SL:248 +CAB5 E0 40 CPX #$40 A:80 X:40 Y:80 P:65 SP:FB CYC:289 SL:248 +CAB7 D0 09 BNE $CAC2 A:80 X:40 Y:80 P:67 SP:FB CYC:295 SL:248 +CAB9 30 07 BMI $CAC2 A:80 X:40 Y:80 P:67 SP:FB CYC:301 SL:248 +CABB 90 05 BCC $CAC2 A:80 X:40 Y:80 P:67 SP:FB CYC:307 SL:248 +CABD 50 03 BVC $CAC2 A:80 X:40 Y:80 P:67 SP:FB CYC:313 SL:248 +CABF 4C C6 CA JMP $CAC6 A:80 X:40 Y:80 P:67 SP:FB CYC:319 SL:248 +CAC6 EA NOP A:80 X:40 Y:80 P:67 SP:FB CYC:328 SL:248 +CAC7 B8 CLV A:80 X:40 Y:80 P:67 SP:FB CYC:334 SL:248 +CAC8 E0 3F CPX #$3F A:80 X:40 Y:80 P:27 SP:FB CYC:340 SL:248 +CACA F0 09 BEQ $CAD5 A:80 X:40 Y:80 P:25 SP:FB CYC: 5 SL:249 +CACC 30 07 BMI $CAD5 A:80 X:40 Y:80 P:25 SP:FB CYC: 11 SL:249 +CACE 90 05 BCC $CAD5 A:80 X:40 Y:80 P:25 SP:FB CYC: 17 SL:249 +CAD0 70 03 BVS $CAD5 A:80 X:40 Y:80 P:25 SP:FB CYC: 23 SL:249 +CAD2 4C D9 CA JMP $CAD9 A:80 X:40 Y:80 P:25 SP:FB CYC: 29 SL:249 +CAD9 EA NOP A:80 X:40 Y:80 P:25 SP:FB CYC: 38 SL:249 +CADA E0 41 CPX #$41 A:80 X:40 Y:80 P:25 SP:FB CYC: 44 SL:249 +CADC F0 07 BEQ $CAE5 A:80 X:40 Y:80 P:A4 SP:FB CYC: 50 SL:249 +CADE 10 05 BPL $CAE5 A:80 X:40 Y:80 P:A4 SP:FB CYC: 56 SL:249 +CAE0 10 03 BPL $CAE5 A:80 X:40 Y:80 P:A4 SP:FB CYC: 62 SL:249 +CAE2 4C E9 CA JMP $CAE9 A:80 X:40 Y:80 P:A4 SP:FB CYC: 68 SL:249 +CAE9 EA NOP A:80 X:40 Y:80 P:A4 SP:FB CYC: 77 SL:249 +CAEA A2 80 LDX #$80 A:80 X:40 Y:80 P:A4 SP:FB CYC: 83 SL:249 +CAEC E0 00 CPX #$00 A:80 X:80 Y:80 P:A4 SP:FB CYC: 89 SL:249 +CAEE F0 07 BEQ $CAF7 A:80 X:80 Y:80 P:A5 SP:FB CYC: 95 SL:249 +CAF0 10 05 BPL $CAF7 A:80 X:80 Y:80 P:A5 SP:FB CYC:101 SL:249 +CAF2 90 03 BCC $CAF7 A:80 X:80 Y:80 P:A5 SP:FB CYC:107 SL:249 +CAF4 4C FB CA JMP $CAFB A:80 X:80 Y:80 P:A5 SP:FB CYC:113 SL:249 +CAFB EA NOP A:80 X:80 Y:80 P:A5 SP:FB CYC:122 SL:249 +CAFC E0 80 CPX #$80 A:80 X:80 Y:80 P:A5 SP:FB CYC:128 SL:249 +CAFE D0 07 BNE $CB07 A:80 X:80 Y:80 P:27 SP:FB CYC:134 SL:249 +CB00 30 05 BMI $CB07 A:80 X:80 Y:80 P:27 SP:FB CYC:140 SL:249 +CB02 90 03 BCC $CB07 A:80 X:80 Y:80 P:27 SP:FB CYC:146 SL:249 +CB04 4C 0B CB JMP $CB0B A:80 X:80 Y:80 P:27 SP:FB CYC:152 SL:249 +CB0B EA NOP A:80 X:80 Y:80 P:27 SP:FB CYC:161 SL:249 +CB0C E0 81 CPX #$81 A:80 X:80 Y:80 P:27 SP:FB CYC:167 SL:249 +CB0E B0 07 BCS $CB17 A:80 X:80 Y:80 P:A4 SP:FB CYC:173 SL:249 +CB10 F0 05 BEQ $CB17 A:80 X:80 Y:80 P:A4 SP:FB CYC:179 SL:249 +CB12 10 03 BPL $CB17 A:80 X:80 Y:80 P:A4 SP:FB CYC:185 SL:249 +CB14 4C 1B CB JMP $CB1B A:80 X:80 Y:80 P:A4 SP:FB CYC:191 SL:249 +CB1B EA NOP A:80 X:80 Y:80 P:A4 SP:FB CYC:200 SL:249 +CB1C E0 7F CPX #$7F A:80 X:80 Y:80 P:A4 SP:FB CYC:206 SL:249 +CB1E 90 07 BCC $CB27 A:80 X:80 Y:80 P:25 SP:FB CYC:212 SL:249 +CB20 F0 05 BEQ $CB27 A:80 X:80 Y:80 P:25 SP:FB CYC:218 SL:249 +CB22 30 03 BMI $CB27 A:80 X:80 Y:80 P:25 SP:FB CYC:224 SL:249 +CB24 4C 2B CB JMP $CB2B A:80 X:80 Y:80 P:25 SP:FB CYC:230 SL:249 +CB2B EA NOP A:80 X:80 Y:80 P:25 SP:FB CYC:239 SL:249 +CB2C 38 SEC A:80 X:80 Y:80 P:25 SP:FB CYC:245 SL:249 +CB2D B8 CLV A:80 X:80 Y:80 P:25 SP:FB CYC:251 SL:249 +CB2E A2 9F LDX #$9F A:80 X:80 Y:80 P:25 SP:FB CYC:257 SL:249 +CB30 F0 09 BEQ $CB3B A:80 X:9F Y:80 P:A5 SP:FB CYC:263 SL:249 +CB32 10 07 BPL $CB3B A:80 X:9F Y:80 P:A5 SP:FB CYC:269 SL:249 +CB34 70 05 BVS $CB3B A:80 X:9F Y:80 P:A5 SP:FB CYC:275 SL:249 +CB36 90 03 BCC $CB3B A:80 X:9F Y:80 P:A5 SP:FB CYC:281 SL:249 +CB38 4C 3F CB JMP $CB3F A:80 X:9F Y:80 P:A5 SP:FB CYC:287 SL:249 +CB3F EA NOP A:80 X:9F Y:80 P:A5 SP:FB CYC:296 SL:249 +CB40 18 CLC A:80 X:9F Y:80 P:A5 SP:FB CYC:302 SL:249 +CB41 24 01 BIT $01 = FF A:80 X:9F Y:80 P:A4 SP:FB CYC:308 SL:249 +CB43 A2 00 LDX #$00 A:80 X:9F Y:80 P:E4 SP:FB CYC:317 SL:249 +CB45 D0 09 BNE $CB50 A:80 X:00 Y:80 P:66 SP:FB CYC:323 SL:249 +CB47 30 07 BMI $CB50 A:80 X:00 Y:80 P:66 SP:FB CYC:329 SL:249 +CB49 50 05 BVC $CB50 A:80 X:00 Y:80 P:66 SP:FB CYC:335 SL:249 +CB4B B0 03 BCS $CB50 A:80 X:00 Y:80 P:66 SP:FB CYC: 0 SL:250 +CB4D 4C 54 CB JMP $CB54 A:80 X:00 Y:80 P:66 SP:FB CYC: 6 SL:250 +CB54 EA NOP A:80 X:00 Y:80 P:66 SP:FB CYC: 15 SL:250 +CB55 38 SEC A:80 X:00 Y:80 P:66 SP:FB CYC: 21 SL:250 +CB56 B8 CLV A:80 X:00 Y:80 P:67 SP:FB CYC: 27 SL:250 +CB57 A0 9F LDY #$9F A:80 X:00 Y:80 P:27 SP:FB CYC: 33 SL:250 +CB59 F0 09 BEQ $CB64 A:80 X:00 Y:9F P:A5 SP:FB CYC: 39 SL:250 +CB5B 10 07 BPL $CB64 A:80 X:00 Y:9F P:A5 SP:FB CYC: 45 SL:250 +CB5D 70 05 BVS $CB64 A:80 X:00 Y:9F P:A5 SP:FB CYC: 51 SL:250 +CB5F 90 03 BCC $CB64 A:80 X:00 Y:9F P:A5 SP:FB CYC: 57 SL:250 +CB61 4C 68 CB JMP $CB68 A:80 X:00 Y:9F P:A5 SP:FB CYC: 63 SL:250 +CB68 EA NOP A:80 X:00 Y:9F P:A5 SP:FB CYC: 72 SL:250 +CB69 18 CLC A:80 X:00 Y:9F P:A5 SP:FB CYC: 78 SL:250 +CB6A 24 01 BIT $01 = FF A:80 X:00 Y:9F P:A4 SP:FB CYC: 84 SL:250 +CB6C A0 00 LDY #$00 A:80 X:00 Y:9F P:E4 SP:FB CYC: 93 SL:250 +CB6E D0 09 BNE $CB79 A:80 X:00 Y:00 P:66 SP:FB CYC: 99 SL:250 +CB70 30 07 BMI $CB79 A:80 X:00 Y:00 P:66 SP:FB CYC:105 SL:250 +CB72 50 05 BVC $CB79 A:80 X:00 Y:00 P:66 SP:FB CYC:111 SL:250 +CB74 B0 03 BCS $CB79 A:80 X:00 Y:00 P:66 SP:FB CYC:117 SL:250 +CB76 4C 7D CB JMP $CB7D A:80 X:00 Y:00 P:66 SP:FB CYC:123 SL:250 +CB7D EA NOP A:80 X:00 Y:00 P:66 SP:FB CYC:132 SL:250 +CB7E A9 55 LDA #$55 A:80 X:00 Y:00 P:66 SP:FB CYC:138 SL:250 +CB80 A2 AA LDX #$AA A:55 X:00 Y:00 P:64 SP:FB CYC:144 SL:250 +CB82 A0 33 LDY #$33 A:55 X:AA Y:00 P:E4 SP:FB CYC:150 SL:250 +CB84 C9 55 CMP #$55 A:55 X:AA Y:33 P:64 SP:FB CYC:156 SL:250 +CB86 D0 23 BNE $CBAB A:55 X:AA Y:33 P:67 SP:FB CYC:162 SL:250 +CB88 E0 AA CPX #$AA A:55 X:AA Y:33 P:67 SP:FB CYC:168 SL:250 +CB8A D0 1F BNE $CBAB A:55 X:AA Y:33 P:67 SP:FB CYC:174 SL:250 +CB8C C0 33 CPY #$33 A:55 X:AA Y:33 P:67 SP:FB CYC:180 SL:250 +CB8E D0 1B BNE $CBAB A:55 X:AA Y:33 P:67 SP:FB CYC:186 SL:250 +CB90 C9 55 CMP #$55 A:55 X:AA Y:33 P:67 SP:FB CYC:192 SL:250 +CB92 D0 17 BNE $CBAB A:55 X:AA Y:33 P:67 SP:FB CYC:198 SL:250 +CB94 E0 AA CPX #$AA A:55 X:AA Y:33 P:67 SP:FB CYC:204 SL:250 +CB96 D0 13 BNE $CBAB A:55 X:AA Y:33 P:67 SP:FB CYC:210 SL:250 +CB98 C0 33 CPY #$33 A:55 X:AA Y:33 P:67 SP:FB CYC:216 SL:250 +CB9A D0 0F BNE $CBAB A:55 X:AA Y:33 P:67 SP:FB CYC:222 SL:250 +CB9C C9 56 CMP #$56 A:55 X:AA Y:33 P:67 SP:FB CYC:228 SL:250 +CB9E F0 0B BEQ $CBAB A:55 X:AA Y:33 P:E4 SP:FB CYC:234 SL:250 +CBA0 E0 AB CPX #$AB A:55 X:AA Y:33 P:E4 SP:FB CYC:240 SL:250 +CBA2 F0 07 BEQ $CBAB A:55 X:AA Y:33 P:E4 SP:FB CYC:246 SL:250 +CBA4 C0 34 CPY #$34 A:55 X:AA Y:33 P:E4 SP:FB CYC:252 SL:250 +CBA6 F0 03 BEQ $CBAB A:55 X:AA Y:33 P:E4 SP:FB CYC:258 SL:250 +CBA8 4C AF CB JMP $CBAF A:55 X:AA Y:33 P:E4 SP:FB CYC:264 SL:250 +CBAF A0 71 LDY #$71 A:55 X:AA Y:33 P:E4 SP:FB CYC:273 SL:250 +CBB1 20 31 F9 JSR $F931 A:55 X:AA Y:71 P:64 SP:FB CYC:279 SL:250 +F931 24 01 BIT $01 = FF A:55 X:AA Y:71 P:64 SP:F9 CYC:297 SL:250 +F933 A9 40 LDA #$40 A:55 X:AA Y:71 P:E4 SP:F9 CYC:306 SL:250 +F935 38 SEC A:40 X:AA Y:71 P:64 SP:F9 CYC:312 SL:250 +F936 60 RTS A:40 X:AA Y:71 P:65 SP:F9 CYC:318 SL:250 +CBB4 E9 40 SBC #$40 A:40 X:AA Y:71 P:65 SP:FB CYC:336 SL:250 +CBB6 20 37 F9 JSR $F937 A:00 X:AA Y:71 P:27 SP:FB CYC: 1 SL:251 +F937 30 0B BMI $F944 A:00 X:AA Y:71 P:27 SP:F9 CYC: 19 SL:251 +F939 90 09 BCC $F944 A:00 X:AA Y:71 P:27 SP:F9 CYC: 25 SL:251 +F93B D0 07 BNE $F944 A:00 X:AA Y:71 P:27 SP:F9 CYC: 31 SL:251 +F93D 70 05 BVS $F944 A:00 X:AA Y:71 P:27 SP:F9 CYC: 37 SL:251 +F93F C9 00 CMP #$00 A:00 X:AA Y:71 P:27 SP:F9 CYC: 43 SL:251 +F941 D0 01 BNE $F944 A:00 X:AA Y:71 P:27 SP:F9 CYC: 49 SL:251 +F943 60 RTS A:00 X:AA Y:71 P:27 SP:F9 CYC: 55 SL:251 +CBB9 C8 INY A:00 X:AA Y:71 P:27 SP:FB CYC: 73 SL:251 +CBBA 20 47 F9 JSR $F947 A:00 X:AA Y:72 P:25 SP:FB CYC: 79 SL:251 +F947 B8 CLV A:00 X:AA Y:72 P:25 SP:F9 CYC: 97 SL:251 +F948 38 SEC A:00 X:AA Y:72 P:25 SP:F9 CYC:103 SL:251 +F949 A9 40 LDA #$40 A:00 X:AA Y:72 P:25 SP:F9 CYC:109 SL:251 +F94B 60 RTS A:40 X:AA Y:72 P:25 SP:F9 CYC:115 SL:251 +CBBD E9 3F SBC #$3F A:40 X:AA Y:72 P:25 SP:FB CYC:133 SL:251 +CBBF 20 4C F9 JSR $F94C A:01 X:AA Y:72 P:25 SP:FB CYC:139 SL:251 +F94C F0 0B BEQ $F959 A:01 X:AA Y:72 P:25 SP:F9 CYC:157 SL:251 +F94E 30 09 BMI $F959 A:01 X:AA Y:72 P:25 SP:F9 CYC:163 SL:251 +F950 90 07 BCC $F959 A:01 X:AA Y:72 P:25 SP:F9 CYC:169 SL:251 +F952 70 05 BVS $F959 A:01 X:AA Y:72 P:25 SP:F9 CYC:175 SL:251 +F954 C9 01 CMP #$01 A:01 X:AA Y:72 P:25 SP:F9 CYC:181 SL:251 +F956 D0 01 BNE $F959 A:01 X:AA Y:72 P:27 SP:F9 CYC:187 SL:251 +F958 60 RTS A:01 X:AA Y:72 P:27 SP:F9 CYC:193 SL:251 +CBC2 C8 INY A:01 X:AA Y:72 P:27 SP:FB CYC:211 SL:251 +CBC3 20 5C F9 JSR $F95C A:01 X:AA Y:73 P:25 SP:FB CYC:217 SL:251 +F95C A9 40 LDA #$40 A:01 X:AA Y:73 P:25 SP:F9 CYC:235 SL:251 +F95E 38 SEC A:40 X:AA Y:73 P:25 SP:F9 CYC:241 SL:251 +F95F 24 01 BIT $01 = FF A:40 X:AA Y:73 P:25 SP:F9 CYC:247 SL:251 +F961 60 RTS A:40 X:AA Y:73 P:E5 SP:F9 CYC:256 SL:251 +CBC6 E9 41 SBC #$41 A:40 X:AA Y:73 P:E5 SP:FB CYC:274 SL:251 +CBC8 20 62 F9 JSR $F962 A:FF X:AA Y:73 P:A4 SP:FB CYC:280 SL:251 +F962 B0 0B BCS $F96F A:FF X:AA Y:73 P:A4 SP:F9 CYC:298 SL:251 +F964 F0 09 BEQ $F96F A:FF X:AA Y:73 P:A4 SP:F9 CYC:304 SL:251 +F966 10 07 BPL $F96F A:FF X:AA Y:73 P:A4 SP:F9 CYC:310 SL:251 +F968 70 05 BVS $F96F A:FF X:AA Y:73 P:A4 SP:F9 CYC:316 SL:251 +F96A C9 FF CMP #$FF A:FF X:AA Y:73 P:A4 SP:F9 CYC:322 SL:251 +F96C D0 01 BNE $F96F A:FF X:AA Y:73 P:27 SP:F9 CYC:328 SL:251 +F96E 60 RTS A:FF X:AA Y:73 P:27 SP:F9 CYC:334 SL:251 +CBCB C8 INY A:FF X:AA Y:73 P:27 SP:FB CYC: 11 SL:252 +CBCC 20 72 F9 JSR $F972 A:FF X:AA Y:74 P:25 SP:FB CYC: 17 SL:252 +F972 18 CLC A:FF X:AA Y:74 P:25 SP:F9 CYC: 35 SL:252 +F973 A9 80 LDA #$80 A:FF X:AA Y:74 P:24 SP:F9 CYC: 41 SL:252 +F975 60 RTS A:80 X:AA Y:74 P:A4 SP:F9 CYC: 47 SL:252 +CBCF E9 00 SBC #$00 A:80 X:AA Y:74 P:A4 SP:FB CYC: 65 SL:252 +CBD1 20 76 F9 JSR $F976 A:7F X:AA Y:74 P:65 SP:FB CYC: 71 SL:252 +F976 90 05 BCC $F97D A:7F X:AA Y:74 P:65 SP:F9 CYC: 89 SL:252 +F978 C9 7F CMP #$7F A:7F X:AA Y:74 P:65 SP:F9 CYC: 95 SL:252 +F97A D0 01 BNE $F97D A:7F X:AA Y:74 P:67 SP:F9 CYC:101 SL:252 +F97C 60 RTS A:7F X:AA Y:74 P:67 SP:F9 CYC:107 SL:252 +CBD4 C8 INY A:7F X:AA Y:74 P:67 SP:FB CYC:125 SL:252 +CBD5 20 80 F9 JSR $F980 A:7F X:AA Y:75 P:65 SP:FB CYC:131 SL:252 +F980 38 SEC A:7F X:AA Y:75 P:65 SP:F9 CYC:149 SL:252 +F981 A9 81 LDA #$81 A:7F X:AA Y:75 P:65 SP:F9 CYC:155 SL:252 +F983 60 RTS A:81 X:AA Y:75 P:E5 SP:F9 CYC:161 SL:252 +CBD8 E9 7F SBC #$7F A:81 X:AA Y:75 P:E5 SP:FB CYC:179 SL:252 +CBDA 20 84 F9 JSR $F984 A:02 X:AA Y:75 P:65 SP:FB CYC:185 SL:252 +F984 50 07 BVC $F98D A:02 X:AA Y:75 P:65 SP:F9 CYC:203 SL:252 +F986 90 05 BCC $F98D A:02 X:AA Y:75 P:65 SP:F9 CYC:209 SL:252 +F988 C9 02 CMP #$02 A:02 X:AA Y:75 P:65 SP:F9 CYC:215 SL:252 +F98A D0 01 BNE $F98D A:02 X:AA Y:75 P:67 SP:F9 CYC:221 SL:252 +F98C 60 RTS A:02 X:AA Y:75 P:67 SP:F9 CYC:227 SL:252 +CBDD 60 RTS A:02 X:AA Y:75 P:67 SP:FB CYC:245 SL:252 +C606 20 DE CB JSR $CBDE A:02 X:AA Y:75 P:67 SP:FD CYC:263 SL:252 +CBDE EA NOP A:02 X:AA Y:75 P:67 SP:FB CYC:281 SL:252 +CBDF A9 FF LDA #$FF A:02 X:AA Y:75 P:67 SP:FB CYC:287 SL:252 +CBE1 85 01 STA $01 = FF A:FF X:AA Y:75 P:E5 SP:FB CYC:293 SL:252 +CBE3 A9 44 LDA #$44 A:FF X:AA Y:75 P:E5 SP:FB CYC:302 SL:252 +CBE5 A2 55 LDX #$55 A:44 X:AA Y:75 P:65 SP:FB CYC:308 SL:252 +CBE7 A0 66 LDY #$66 A:44 X:55 Y:75 P:65 SP:FB CYC:314 SL:252 +CBE9 E8 INX A:44 X:55 Y:66 P:65 SP:FB CYC:320 SL:252 +CBEA 88 DEY A:44 X:56 Y:66 P:65 SP:FB CYC:326 SL:252 +CBEB E0 56 CPX #$56 A:44 X:56 Y:65 P:65 SP:FB CYC:332 SL:252 +CBED D0 21 BNE $CC10 A:44 X:56 Y:65 P:67 SP:FB CYC:338 SL:252 +CBEF C0 65 CPY #$65 A:44 X:56 Y:65 P:67 SP:FB CYC: 3 SL:253 +CBF1 D0 1D BNE $CC10 A:44 X:56 Y:65 P:67 SP:FB CYC: 9 SL:253 +CBF3 E8 INX A:44 X:56 Y:65 P:67 SP:FB CYC: 15 SL:253 +CBF4 E8 INX A:44 X:57 Y:65 P:65 SP:FB CYC: 21 SL:253 +CBF5 88 DEY A:44 X:58 Y:65 P:65 SP:FB CYC: 27 SL:253 +CBF6 88 DEY A:44 X:58 Y:64 P:65 SP:FB CYC: 33 SL:253 +CBF7 E0 58 CPX #$58 A:44 X:58 Y:63 P:65 SP:FB CYC: 39 SL:253 +CBF9 D0 15 BNE $CC10 A:44 X:58 Y:63 P:67 SP:FB CYC: 45 SL:253 +CBFB C0 63 CPY #$63 A:44 X:58 Y:63 P:67 SP:FB CYC: 51 SL:253 +CBFD D0 11 BNE $CC10 A:44 X:58 Y:63 P:67 SP:FB CYC: 57 SL:253 +CBFF CA DEX A:44 X:58 Y:63 P:67 SP:FB CYC: 63 SL:253 +CC00 C8 INY A:44 X:57 Y:63 P:65 SP:FB CYC: 69 SL:253 +CC01 E0 57 CPX #$57 A:44 X:57 Y:64 P:65 SP:FB CYC: 75 SL:253 +CC03 D0 0B BNE $CC10 A:44 X:57 Y:64 P:67 SP:FB CYC: 81 SL:253 +CC05 C0 64 CPY #$64 A:44 X:57 Y:64 P:67 SP:FB CYC: 87 SL:253 +CC07 D0 07 BNE $CC10 A:44 X:57 Y:64 P:67 SP:FB CYC: 93 SL:253 +CC09 C9 44 CMP #$44 A:44 X:57 Y:64 P:67 SP:FB CYC: 99 SL:253 +CC0B D0 03 BNE $CC10 A:44 X:57 Y:64 P:67 SP:FB CYC:105 SL:253 +CC0D 4C 14 CC JMP $CC14 A:44 X:57 Y:64 P:67 SP:FB CYC:111 SL:253 +CC14 EA NOP A:44 X:57 Y:64 P:67 SP:FB CYC:120 SL:253 +CC15 38 SEC A:44 X:57 Y:64 P:67 SP:FB CYC:126 SL:253 +CC16 A2 69 LDX #$69 A:44 X:57 Y:64 P:67 SP:FB CYC:132 SL:253 +CC18 A9 96 LDA #$96 A:44 X:69 Y:64 P:65 SP:FB CYC:138 SL:253 +CC1A 24 01 BIT $01 = FF A:96 X:69 Y:64 P:E5 SP:FB CYC:144 SL:253 +CC1C A0 FF LDY #$FF A:96 X:69 Y:64 P:E5 SP:FB CYC:153 SL:253 +CC1E C8 INY A:96 X:69 Y:FF P:E5 SP:FB CYC:159 SL:253 +CC1F D0 3D BNE $CC5E A:96 X:69 Y:00 P:67 SP:FB CYC:165 SL:253 +CC21 30 3B BMI $CC5E A:96 X:69 Y:00 P:67 SP:FB CYC:171 SL:253 +CC23 90 39 BCC $CC5E A:96 X:69 Y:00 P:67 SP:FB CYC:177 SL:253 +CC25 50 37 BVC $CC5E A:96 X:69 Y:00 P:67 SP:FB CYC:183 SL:253 +CC27 C0 00 CPY #$00 A:96 X:69 Y:00 P:67 SP:FB CYC:189 SL:253 +CC29 D0 33 BNE $CC5E A:96 X:69 Y:00 P:67 SP:FB CYC:195 SL:253 +CC2B C8 INY A:96 X:69 Y:00 P:67 SP:FB CYC:201 SL:253 +CC2C F0 30 BEQ $CC5E A:96 X:69 Y:01 P:65 SP:FB CYC:207 SL:253 +CC2E 30 2E BMI $CC5E A:96 X:69 Y:01 P:65 SP:FB CYC:213 SL:253 +CC30 90 2C BCC $CC5E A:96 X:69 Y:01 P:65 SP:FB CYC:219 SL:253 +CC32 50 2A BVC $CC5E A:96 X:69 Y:01 P:65 SP:FB CYC:225 SL:253 +CC34 18 CLC A:96 X:69 Y:01 P:65 SP:FB CYC:231 SL:253 +CC35 B8 CLV A:96 X:69 Y:01 P:64 SP:FB CYC:237 SL:253 +CC36 A0 00 LDY #$00 A:96 X:69 Y:01 P:24 SP:FB CYC:243 SL:253 +CC38 88 DEY A:96 X:69 Y:00 P:26 SP:FB CYC:249 SL:253 +CC39 F0 23 BEQ $CC5E A:96 X:69 Y:FF P:A4 SP:FB CYC:255 SL:253 +CC3B 10 21 BPL $CC5E A:96 X:69 Y:FF P:A4 SP:FB CYC:261 SL:253 +CC3D B0 1F BCS $CC5E A:96 X:69 Y:FF P:A4 SP:FB CYC:267 SL:253 +CC3F 70 1D BVS $CC5E A:96 X:69 Y:FF P:A4 SP:FB CYC:273 SL:253 +CC41 C0 FF CPY #$FF A:96 X:69 Y:FF P:A4 SP:FB CYC:279 SL:253 +CC43 D0 19 BNE $CC5E A:96 X:69 Y:FF P:27 SP:FB CYC:285 SL:253 +CC45 18 CLC A:96 X:69 Y:FF P:27 SP:FB CYC:291 SL:253 +CC46 88 DEY A:96 X:69 Y:FF P:26 SP:FB CYC:297 SL:253 +CC47 F0 15 BEQ $CC5E A:96 X:69 Y:FE P:A4 SP:FB CYC:303 SL:253 +CC49 10 13 BPL $CC5E A:96 X:69 Y:FE P:A4 SP:FB CYC:309 SL:253 +CC4B B0 11 BCS $CC5E A:96 X:69 Y:FE P:A4 SP:FB CYC:315 SL:253 +CC4D 70 0F BVS $CC5E A:96 X:69 Y:FE P:A4 SP:FB CYC:321 SL:253 +CC4F C0 FE CPY #$FE A:96 X:69 Y:FE P:A4 SP:FB CYC:327 SL:253 +CC51 D0 0B BNE $CC5E A:96 X:69 Y:FE P:27 SP:FB CYC:333 SL:253 +CC53 C9 96 CMP #$96 A:96 X:69 Y:FE P:27 SP:FB CYC:339 SL:253 +CC55 D0 07 BNE $CC5E A:96 X:69 Y:FE P:27 SP:FB CYC: 4 SL:254 +CC57 E0 69 CPX #$69 A:96 X:69 Y:FE P:27 SP:FB CYC: 10 SL:254 +CC59 D0 03 BNE $CC5E A:96 X:69 Y:FE P:27 SP:FB CYC: 16 SL:254 +CC5B 4C 62 CC JMP $CC62 A:96 X:69 Y:FE P:27 SP:FB CYC: 22 SL:254 +CC62 EA NOP A:96 X:69 Y:FE P:27 SP:FB CYC: 31 SL:254 +CC63 38 SEC A:96 X:69 Y:FE P:27 SP:FB CYC: 37 SL:254 +CC64 A0 69 LDY #$69 A:96 X:69 Y:FE P:27 SP:FB CYC: 43 SL:254 +CC66 A9 96 LDA #$96 A:96 X:69 Y:69 P:25 SP:FB CYC: 49 SL:254 +CC68 24 01 BIT $01 = FF A:96 X:69 Y:69 P:A5 SP:FB CYC: 55 SL:254 +CC6A A2 FF LDX #$FF A:96 X:69 Y:69 P:E5 SP:FB CYC: 64 SL:254 +CC6C E8 INX A:96 X:FF Y:69 P:E5 SP:FB CYC: 70 SL:254 +CC6D D0 3D BNE $CCAC A:96 X:00 Y:69 P:67 SP:FB CYC: 76 SL:254 +CC6F 30 3B BMI $CCAC A:96 X:00 Y:69 P:67 SP:FB CYC: 82 SL:254 +CC71 90 39 BCC $CCAC A:96 X:00 Y:69 P:67 SP:FB CYC: 88 SL:254 +CC73 50 37 BVC $CCAC A:96 X:00 Y:69 P:67 SP:FB CYC: 94 SL:254 +CC75 E0 00 CPX #$00 A:96 X:00 Y:69 P:67 SP:FB CYC:100 SL:254 +CC77 D0 33 BNE $CCAC A:96 X:00 Y:69 P:67 SP:FB CYC:106 SL:254 +CC79 E8 INX A:96 X:00 Y:69 P:67 SP:FB CYC:112 SL:254 +CC7A F0 30 BEQ $CCAC A:96 X:01 Y:69 P:65 SP:FB CYC:118 SL:254 +CC7C 30 2E BMI $CCAC A:96 X:01 Y:69 P:65 SP:FB CYC:124 SL:254 +CC7E 90 2C BCC $CCAC A:96 X:01 Y:69 P:65 SP:FB CYC:130 SL:254 +CC80 50 2A BVC $CCAC A:96 X:01 Y:69 P:65 SP:FB CYC:136 SL:254 +CC82 18 CLC A:96 X:01 Y:69 P:65 SP:FB CYC:142 SL:254 +CC83 B8 CLV A:96 X:01 Y:69 P:64 SP:FB CYC:148 SL:254 +CC84 A2 00 LDX #$00 A:96 X:01 Y:69 P:24 SP:FB CYC:154 SL:254 +CC86 CA DEX A:96 X:00 Y:69 P:26 SP:FB CYC:160 SL:254 +CC87 F0 23 BEQ $CCAC A:96 X:FF Y:69 P:A4 SP:FB CYC:166 SL:254 +CC89 10 21 BPL $CCAC A:96 X:FF Y:69 P:A4 SP:FB CYC:172 SL:254 +CC8B B0 1F BCS $CCAC A:96 X:FF Y:69 P:A4 SP:FB CYC:178 SL:254 +CC8D 70 1D BVS $CCAC A:96 X:FF Y:69 P:A4 SP:FB CYC:184 SL:254 +CC8F E0 FF CPX #$FF A:96 X:FF Y:69 P:A4 SP:FB CYC:190 SL:254 +CC91 D0 19 BNE $CCAC A:96 X:FF Y:69 P:27 SP:FB CYC:196 SL:254 +CC93 18 CLC A:96 X:FF Y:69 P:27 SP:FB CYC:202 SL:254 +CC94 CA DEX A:96 X:FF Y:69 P:26 SP:FB CYC:208 SL:254 +CC95 F0 15 BEQ $CCAC A:96 X:FE Y:69 P:A4 SP:FB CYC:214 SL:254 +CC97 10 13 BPL $CCAC A:96 X:FE Y:69 P:A4 SP:FB CYC:220 SL:254 +CC99 B0 11 BCS $CCAC A:96 X:FE Y:69 P:A4 SP:FB CYC:226 SL:254 +CC9B 70 0F BVS $CCAC A:96 X:FE Y:69 P:A4 SP:FB CYC:232 SL:254 +CC9D E0 FE CPX #$FE A:96 X:FE Y:69 P:A4 SP:FB CYC:238 SL:254 +CC9F D0 0B BNE $CCAC A:96 X:FE Y:69 P:27 SP:FB CYC:244 SL:254 +CCA1 C9 96 CMP #$96 A:96 X:FE Y:69 P:27 SP:FB CYC:250 SL:254 +CCA3 D0 07 BNE $CCAC A:96 X:FE Y:69 P:27 SP:FB CYC:256 SL:254 +CCA5 C0 69 CPY #$69 A:96 X:FE Y:69 P:27 SP:FB CYC:262 SL:254 +CCA7 D0 03 BNE $CCAC A:96 X:FE Y:69 P:27 SP:FB CYC:268 SL:254 +CCA9 4C B0 CC JMP $CCB0 A:96 X:FE Y:69 P:27 SP:FB CYC:274 SL:254 +CCB0 EA NOP A:96 X:FE Y:69 P:27 SP:FB CYC:283 SL:254 +CCB1 A9 85 LDA #$85 A:96 X:FE Y:69 P:27 SP:FB CYC:289 SL:254 +CCB3 A2 34 LDX #$34 A:85 X:FE Y:69 P:A5 SP:FB CYC:295 SL:254 +CCB5 A0 99 LDY #$99 A:85 X:34 Y:69 P:25 SP:FB CYC:301 SL:254 +CCB7 18 CLC A:85 X:34 Y:99 P:A5 SP:FB CYC:307 SL:254 +CCB8 24 01 BIT $01 = FF A:85 X:34 Y:99 P:A4 SP:FB CYC:313 SL:254 +CCBA A8 TAY A:85 X:34 Y:99 P:E4 SP:FB CYC:322 SL:254 +CCBB F0 2E BEQ $CCEB A:85 X:34 Y:85 P:E4 SP:FB CYC:328 SL:254 +CCBD B0 2C BCS $CCEB A:85 X:34 Y:85 P:E4 SP:FB CYC:334 SL:254 +CCBF 50 2A BVC $CCEB A:85 X:34 Y:85 P:E4 SP:FB CYC:340 SL:254 +CCC1 10 28 BPL $CCEB A:85 X:34 Y:85 P:E4 SP:FB CYC: 5 SL:255 +CCC3 C9 85 CMP #$85 A:85 X:34 Y:85 P:E4 SP:FB CYC: 11 SL:255 +CCC5 D0 24 BNE $CCEB A:85 X:34 Y:85 P:67 SP:FB CYC: 17 SL:255 +CCC7 E0 34 CPX #$34 A:85 X:34 Y:85 P:67 SP:FB CYC: 23 SL:255 +CCC9 D0 20 BNE $CCEB A:85 X:34 Y:85 P:67 SP:FB CYC: 29 SL:255 +CCCB C0 85 CPY #$85 A:85 X:34 Y:85 P:67 SP:FB CYC: 35 SL:255 +CCCD D0 1C BNE $CCEB A:85 X:34 Y:85 P:67 SP:FB CYC: 41 SL:255 +CCCF A9 00 LDA #$00 A:85 X:34 Y:85 P:67 SP:FB CYC: 47 SL:255 +CCD1 38 SEC A:00 X:34 Y:85 P:67 SP:FB CYC: 53 SL:255 +CCD2 B8 CLV A:00 X:34 Y:85 P:67 SP:FB CYC: 59 SL:255 +CCD3 A8 TAY A:00 X:34 Y:85 P:27 SP:FB CYC: 65 SL:255 +CCD4 D0 15 BNE $CCEB A:00 X:34 Y:00 P:27 SP:FB CYC: 71 SL:255 +CCD6 90 13 BCC $CCEB A:00 X:34 Y:00 P:27 SP:FB CYC: 77 SL:255 +CCD8 70 11 BVS $CCEB A:00 X:34 Y:00 P:27 SP:FB CYC: 83 SL:255 +CCDA 30 0F BMI $CCEB A:00 X:34 Y:00 P:27 SP:FB CYC: 89 SL:255 +CCDC C9 00 CMP #$00 A:00 X:34 Y:00 P:27 SP:FB CYC: 95 SL:255 +CCDE D0 0B BNE $CCEB A:00 X:34 Y:00 P:27 SP:FB CYC:101 SL:255 +CCE0 E0 34 CPX #$34 A:00 X:34 Y:00 P:27 SP:FB CYC:107 SL:255 +CCE2 D0 07 BNE $CCEB A:00 X:34 Y:00 P:27 SP:FB CYC:113 SL:255 +CCE4 C0 00 CPY #$00 A:00 X:34 Y:00 P:27 SP:FB CYC:119 SL:255 +CCE6 D0 03 BNE $CCEB A:00 X:34 Y:00 P:27 SP:FB CYC:125 SL:255 +CCE8 4C EF CC JMP $CCEF A:00 X:34 Y:00 P:27 SP:FB CYC:131 SL:255 +CCEF EA NOP A:00 X:34 Y:00 P:27 SP:FB CYC:140 SL:255 +CCF0 A9 85 LDA #$85 A:00 X:34 Y:00 P:27 SP:FB CYC:146 SL:255 +CCF2 A2 34 LDX #$34 A:85 X:34 Y:00 P:A5 SP:FB CYC:152 SL:255 +CCF4 A0 99 LDY #$99 A:85 X:34 Y:00 P:25 SP:FB CYC:158 SL:255 +CCF6 18 CLC A:85 X:34 Y:99 P:A5 SP:FB CYC:164 SL:255 +CCF7 24 01 BIT $01 = FF A:85 X:34 Y:99 P:A4 SP:FB CYC:170 SL:255 +CCF9 AA TAX A:85 X:34 Y:99 P:E4 SP:FB CYC:179 SL:255 +CCFA F0 2E BEQ $CD2A A:85 X:85 Y:99 P:E4 SP:FB CYC:185 SL:255 +CCFC B0 2C BCS $CD2A A:85 X:85 Y:99 P:E4 SP:FB CYC:191 SL:255 +CCFE 50 2A BVC $CD2A A:85 X:85 Y:99 P:E4 SP:FB CYC:197 SL:255 +CD00 10 28 BPL $CD2A A:85 X:85 Y:99 P:E4 SP:FB CYC:203 SL:255 +CD02 C9 85 CMP #$85 A:85 X:85 Y:99 P:E4 SP:FB CYC:209 SL:255 +CD04 D0 24 BNE $CD2A A:85 X:85 Y:99 P:67 SP:FB CYC:215 SL:255 +CD06 E0 85 CPX #$85 A:85 X:85 Y:99 P:67 SP:FB CYC:221 SL:255 +CD08 D0 20 BNE $CD2A A:85 X:85 Y:99 P:67 SP:FB CYC:227 SL:255 +CD0A C0 99 CPY #$99 A:85 X:85 Y:99 P:67 SP:FB CYC:233 SL:255 +CD0C D0 1C BNE $CD2A A:85 X:85 Y:99 P:67 SP:FB CYC:239 SL:255 +CD0E A9 00 LDA #$00 A:85 X:85 Y:99 P:67 SP:FB CYC:245 SL:255 +CD10 38 SEC A:00 X:85 Y:99 P:67 SP:FB CYC:251 SL:255 +CD11 B8 CLV A:00 X:85 Y:99 P:67 SP:FB CYC:257 SL:255 +CD12 AA TAX A:00 X:85 Y:99 P:27 SP:FB CYC:263 SL:255 +CD13 D0 15 BNE $CD2A A:00 X:00 Y:99 P:27 SP:FB CYC:269 SL:255 +CD15 90 13 BCC $CD2A A:00 X:00 Y:99 P:27 SP:FB CYC:275 SL:255 +CD17 70 11 BVS $CD2A A:00 X:00 Y:99 P:27 SP:FB CYC:281 SL:255 +CD19 30 0F BMI $CD2A A:00 X:00 Y:99 P:27 SP:FB CYC:287 SL:255 +CD1B C9 00 CMP #$00 A:00 X:00 Y:99 P:27 SP:FB CYC:293 SL:255 +CD1D D0 0B BNE $CD2A A:00 X:00 Y:99 P:27 SP:FB CYC:299 SL:255 +CD1F E0 00 CPX #$00 A:00 X:00 Y:99 P:27 SP:FB CYC:305 SL:255 +CD21 D0 07 BNE $CD2A A:00 X:00 Y:99 P:27 SP:FB CYC:311 SL:255 +CD23 C0 99 CPY #$99 A:00 X:00 Y:99 P:27 SP:FB CYC:317 SL:255 +CD25 D0 03 BNE $CD2A A:00 X:00 Y:99 P:27 SP:FB CYC:323 SL:255 +CD27 4C 2E CD JMP $CD2E A:00 X:00 Y:99 P:27 SP:FB CYC:329 SL:255 +CD2E EA NOP A:00 X:00 Y:99 P:27 SP:FB CYC:338 SL:255 +CD2F A9 85 LDA #$85 A:00 X:00 Y:99 P:27 SP:FB CYC: 3 SL:256 +CD31 A2 34 LDX #$34 A:85 X:00 Y:99 P:A5 SP:FB CYC: 9 SL:256 +CD33 A0 99 LDY #$99 A:85 X:34 Y:99 P:25 SP:FB CYC: 15 SL:256 +CD35 18 CLC A:85 X:34 Y:99 P:A5 SP:FB CYC: 21 SL:256 +CD36 24 01 BIT $01 = FF A:85 X:34 Y:99 P:A4 SP:FB CYC: 27 SL:256 +CD38 98 TYA A:85 X:34 Y:99 P:E4 SP:FB CYC: 36 SL:256 +CD39 F0 2E BEQ $CD69 A:99 X:34 Y:99 P:E4 SP:FB CYC: 42 SL:256 +CD3B B0 2C BCS $CD69 A:99 X:34 Y:99 P:E4 SP:FB CYC: 48 SL:256 +CD3D 50 2A BVC $CD69 A:99 X:34 Y:99 P:E4 SP:FB CYC: 54 SL:256 +CD3F 10 28 BPL $CD69 A:99 X:34 Y:99 P:E4 SP:FB CYC: 60 SL:256 +CD41 C9 99 CMP #$99 A:99 X:34 Y:99 P:E4 SP:FB CYC: 66 SL:256 +CD43 D0 24 BNE $CD69 A:99 X:34 Y:99 P:67 SP:FB CYC: 72 SL:256 +CD45 E0 34 CPX #$34 A:99 X:34 Y:99 P:67 SP:FB CYC: 78 SL:256 +CD47 D0 20 BNE $CD69 A:99 X:34 Y:99 P:67 SP:FB CYC: 84 SL:256 +CD49 C0 99 CPY #$99 A:99 X:34 Y:99 P:67 SP:FB CYC: 90 SL:256 +CD4B D0 1C BNE $CD69 A:99 X:34 Y:99 P:67 SP:FB CYC: 96 SL:256 +CD4D A0 00 LDY #$00 A:99 X:34 Y:99 P:67 SP:FB CYC:102 SL:256 +CD4F 38 SEC A:99 X:34 Y:00 P:67 SP:FB CYC:108 SL:256 +CD50 B8 CLV A:99 X:34 Y:00 P:67 SP:FB CYC:114 SL:256 +CD51 98 TYA A:99 X:34 Y:00 P:27 SP:FB CYC:120 SL:256 +CD52 D0 15 BNE $CD69 A:00 X:34 Y:00 P:27 SP:FB CYC:126 SL:256 +CD54 90 13 BCC $CD69 A:00 X:34 Y:00 P:27 SP:FB CYC:132 SL:256 +CD56 70 11 BVS $CD69 A:00 X:34 Y:00 P:27 SP:FB CYC:138 SL:256 +CD58 30 0F BMI $CD69 A:00 X:34 Y:00 P:27 SP:FB CYC:144 SL:256 +CD5A C9 00 CMP #$00 A:00 X:34 Y:00 P:27 SP:FB CYC:150 SL:256 +CD5C D0 0B BNE $CD69 A:00 X:34 Y:00 P:27 SP:FB CYC:156 SL:256 +CD5E E0 34 CPX #$34 A:00 X:34 Y:00 P:27 SP:FB CYC:162 SL:256 +CD60 D0 07 BNE $CD69 A:00 X:34 Y:00 P:27 SP:FB CYC:168 SL:256 +CD62 C0 00 CPY #$00 A:00 X:34 Y:00 P:27 SP:FB CYC:174 SL:256 +CD64 D0 03 BNE $CD69 A:00 X:34 Y:00 P:27 SP:FB CYC:180 SL:256 +CD66 4C 6D CD JMP $CD6D A:00 X:34 Y:00 P:27 SP:FB CYC:186 SL:256 +CD6D EA NOP A:00 X:34 Y:00 P:27 SP:FB CYC:195 SL:256 +CD6E A9 85 LDA #$85 A:00 X:34 Y:00 P:27 SP:FB CYC:201 SL:256 +CD70 A2 34 LDX #$34 A:85 X:34 Y:00 P:A5 SP:FB CYC:207 SL:256 +CD72 A0 99 LDY #$99 A:85 X:34 Y:00 P:25 SP:FB CYC:213 SL:256 +CD74 18 CLC A:85 X:34 Y:99 P:A5 SP:FB CYC:219 SL:256 +CD75 24 01 BIT $01 = FF A:85 X:34 Y:99 P:A4 SP:FB CYC:225 SL:256 +CD77 8A TXA A:85 X:34 Y:99 P:E4 SP:FB CYC:234 SL:256 +CD78 F0 2E BEQ $CDA8 A:34 X:34 Y:99 P:64 SP:FB CYC:240 SL:256 +CD7A B0 2C BCS $CDA8 A:34 X:34 Y:99 P:64 SP:FB CYC:246 SL:256 +CD7C 50 2A BVC $CDA8 A:34 X:34 Y:99 P:64 SP:FB CYC:252 SL:256 +CD7E 30 28 BMI $CDA8 A:34 X:34 Y:99 P:64 SP:FB CYC:258 SL:256 +CD80 C9 34 CMP #$34 A:34 X:34 Y:99 P:64 SP:FB CYC:264 SL:256 +CD82 D0 24 BNE $CDA8 A:34 X:34 Y:99 P:67 SP:FB CYC:270 SL:256 +CD84 E0 34 CPX #$34 A:34 X:34 Y:99 P:67 SP:FB CYC:276 SL:256 +CD86 D0 20 BNE $CDA8 A:34 X:34 Y:99 P:67 SP:FB CYC:282 SL:256 +CD88 C0 99 CPY #$99 A:34 X:34 Y:99 P:67 SP:FB CYC:288 SL:256 +CD8A D0 1C BNE $CDA8 A:34 X:34 Y:99 P:67 SP:FB CYC:294 SL:256 +CD8C A2 00 LDX #$00 A:34 X:34 Y:99 P:67 SP:FB CYC:300 SL:256 +CD8E 38 SEC A:34 X:00 Y:99 P:67 SP:FB CYC:306 SL:256 +CD8F B8 CLV A:34 X:00 Y:99 P:67 SP:FB CYC:312 SL:256 +CD90 8A TXA A:34 X:00 Y:99 P:27 SP:FB CYC:318 SL:256 +CD91 D0 15 BNE $CDA8 A:00 X:00 Y:99 P:27 SP:FB CYC:324 SL:256 +CD93 90 13 BCC $CDA8 A:00 X:00 Y:99 P:27 SP:FB CYC:330 SL:256 +CD95 70 11 BVS $CDA8 A:00 X:00 Y:99 P:27 SP:FB CYC:336 SL:256 +CD97 30 0F BMI $CDA8 A:00 X:00 Y:99 P:27 SP:FB CYC: 1 SL:257 +CD99 C9 00 CMP #$00 A:00 X:00 Y:99 P:27 SP:FB CYC: 7 SL:257 +CD9B D0 0B BNE $CDA8 A:00 X:00 Y:99 P:27 SP:FB CYC: 13 SL:257 +CD9D E0 00 CPX #$00 A:00 X:00 Y:99 P:27 SP:FB CYC: 19 SL:257 +CD9F D0 07 BNE $CDA8 A:00 X:00 Y:99 P:27 SP:FB CYC: 25 SL:257 +CDA1 C0 99 CPY #$99 A:00 X:00 Y:99 P:27 SP:FB CYC: 31 SL:257 +CDA3 D0 03 BNE $CDA8 A:00 X:00 Y:99 P:27 SP:FB CYC: 37 SL:257 +CDA5 4C AC CD JMP $CDAC A:00 X:00 Y:99 P:27 SP:FB CYC: 43 SL:257 +CDAC EA NOP A:00 X:00 Y:99 P:27 SP:FB CYC: 52 SL:257 +CDAD BA TSX A:00 X:00 Y:99 P:27 SP:FB CYC: 58 SL:257 +CDAE 8E FF 07 STX $07FF = 00 A:00 X:FB Y:99 P:A5 SP:FB CYC: 64 SL:257 +CDB1 A0 33 LDY #$33 A:00 X:FB Y:99 P:A5 SP:FB CYC: 76 SL:257 +CDB3 A2 69 LDX #$69 A:00 X:FB Y:33 P:25 SP:FB CYC: 82 SL:257 +CDB5 A9 84 LDA #$84 A:00 X:69 Y:33 P:25 SP:FB CYC: 88 SL:257 +CDB7 18 CLC A:84 X:69 Y:33 P:A5 SP:FB CYC: 94 SL:257 +CDB8 24 01 BIT $01 = FF A:84 X:69 Y:33 P:A4 SP:FB CYC:100 SL:257 +CDBA 9A TXS A:84 X:69 Y:33 P:E4 SP:FB CYC:109 SL:257 +CDBB F0 32 BEQ $CDEF A:84 X:69 Y:33 P:E4 SP:69 CYC:115 SL:257 +CDBD 10 30 BPL $CDEF A:84 X:69 Y:33 P:E4 SP:69 CYC:121 SL:257 +CDBF B0 2E BCS $CDEF A:84 X:69 Y:33 P:E4 SP:69 CYC:127 SL:257 +CDC1 50 2C BVC $CDEF A:84 X:69 Y:33 P:E4 SP:69 CYC:133 SL:257 +CDC3 C9 84 CMP #$84 A:84 X:69 Y:33 P:E4 SP:69 CYC:139 SL:257 +CDC5 D0 28 BNE $CDEF A:84 X:69 Y:33 P:67 SP:69 CYC:145 SL:257 +CDC7 E0 69 CPX #$69 A:84 X:69 Y:33 P:67 SP:69 CYC:151 SL:257 +CDC9 D0 24 BNE $CDEF A:84 X:69 Y:33 P:67 SP:69 CYC:157 SL:257 +CDCB C0 33 CPY #$33 A:84 X:69 Y:33 P:67 SP:69 CYC:163 SL:257 +CDCD D0 20 BNE $CDEF A:84 X:69 Y:33 P:67 SP:69 CYC:169 SL:257 +CDCF A0 01 LDY #$01 A:84 X:69 Y:33 P:67 SP:69 CYC:175 SL:257 +CDD1 A9 04 LDA #$04 A:84 X:69 Y:01 P:65 SP:69 CYC:181 SL:257 +CDD3 38 SEC A:04 X:69 Y:01 P:65 SP:69 CYC:187 SL:257 +CDD4 B8 CLV A:04 X:69 Y:01 P:65 SP:69 CYC:193 SL:257 +CDD5 A2 00 LDX #$00 A:04 X:69 Y:01 P:25 SP:69 CYC:199 SL:257 +CDD7 BA TSX A:04 X:00 Y:01 P:27 SP:69 CYC:205 SL:257 +CDD8 F0 15 BEQ $CDEF A:04 X:69 Y:01 P:25 SP:69 CYC:211 SL:257 +CDDA 30 13 BMI $CDEF A:04 X:69 Y:01 P:25 SP:69 CYC:217 SL:257 +CDDC 90 11 BCC $CDEF A:04 X:69 Y:01 P:25 SP:69 CYC:223 SL:257 +CDDE 70 0F BVS $CDEF A:04 X:69 Y:01 P:25 SP:69 CYC:229 SL:257 +CDE0 E0 69 CPX #$69 A:04 X:69 Y:01 P:25 SP:69 CYC:235 SL:257 +CDE2 D0 0B BNE $CDEF A:04 X:69 Y:01 P:27 SP:69 CYC:241 SL:257 +CDE4 C9 04 CMP #$04 A:04 X:69 Y:01 P:27 SP:69 CYC:247 SL:257 +CDE6 D0 07 BNE $CDEF A:04 X:69 Y:01 P:27 SP:69 CYC:253 SL:257 +CDE8 C0 01 CPY #$01 A:04 X:69 Y:01 P:27 SP:69 CYC:259 SL:257 +CDEA D0 03 BNE $CDEF A:04 X:69 Y:01 P:27 SP:69 CYC:265 SL:257 +CDEC 4C F3 CD JMP $CDF3 A:04 X:69 Y:01 P:27 SP:69 CYC:271 SL:257 +CDF3 AE FF 07 LDX $07FF = FB A:04 X:69 Y:01 P:27 SP:69 CYC:280 SL:257 +CDF6 9A TXS A:04 X:FB Y:01 P:A5 SP:69 CYC:292 SL:257 +CDF7 60 RTS A:04 X:FB Y:01 P:A5 SP:FB CYC:298 SL:257 +C609 20 F8 CD JSR $CDF8 A:04 X:FB Y:01 P:A5 SP:FD CYC:316 SL:257 +CDF8 A9 FF LDA #$FF A:04 X:FB Y:01 P:A5 SP:FB CYC:334 SL:257 +CDFA 85 01 STA $01 = FF A:FF X:FB Y:01 P:A5 SP:FB CYC:340 SL:257 +CDFC BA TSX A:FF X:FB Y:01 P:A5 SP:FB CYC: 8 SL:258 +CDFD 8E FF 07 STX $07FF = FB A:FF X:FB Y:01 P:A5 SP:FB CYC: 14 SL:258 +CE00 EA NOP A:FF X:FB Y:01 P:A5 SP:FB CYC: 26 SL:258 +CE01 A2 80 LDX #$80 A:FF X:FB Y:01 P:A5 SP:FB CYC: 32 SL:258 +CE03 9A TXS A:FF X:80 Y:01 P:A5 SP:FB CYC: 38 SL:258 +CE04 A9 33 LDA #$33 A:FF X:80 Y:01 P:A5 SP:80 CYC: 44 SL:258 +CE06 48 PHA A:33 X:80 Y:01 P:25 SP:80 CYC: 50 SL:258 +CE07 A9 69 LDA #$69 A:33 X:80 Y:01 P:25 SP:7F CYC: 59 SL:258 +CE09 48 PHA A:69 X:80 Y:01 P:25 SP:7F CYC: 65 SL:258 +CE0A BA TSX A:69 X:80 Y:01 P:25 SP:7E CYC: 74 SL:258 +CE0B E0 7E CPX #$7E A:69 X:7E Y:01 P:25 SP:7E CYC: 80 SL:258 +CE0D D0 20 BNE $CE2F A:69 X:7E Y:01 P:27 SP:7E CYC: 86 SL:258 +CE0F 68 PLA A:69 X:7E Y:01 P:27 SP:7E CYC: 92 SL:258 +CE10 C9 69 CMP #$69 A:69 X:7E Y:01 P:25 SP:7F CYC:104 SL:258 +CE12 D0 1B BNE $CE2F A:69 X:7E Y:01 P:27 SP:7F CYC:110 SL:258 +CE14 68 PLA A:69 X:7E Y:01 P:27 SP:7F CYC:116 SL:258 +CE15 C9 33 CMP #$33 A:33 X:7E Y:01 P:25 SP:80 CYC:128 SL:258 +CE17 D0 16 BNE $CE2F A:33 X:7E Y:01 P:27 SP:80 CYC:134 SL:258 +CE19 BA TSX A:33 X:7E Y:01 P:27 SP:80 CYC:140 SL:258 +CE1A E0 80 CPX #$80 A:33 X:80 Y:01 P:A5 SP:80 CYC:146 SL:258 +CE1C D0 11 BNE $CE2F A:33 X:80 Y:01 P:27 SP:80 CYC:152 SL:258 +CE1E AD 80 01 LDA $0180 = 33 A:33 X:80 Y:01 P:27 SP:80 CYC:158 SL:258 +CE21 C9 33 CMP #$33 A:33 X:80 Y:01 P:25 SP:80 CYC:170 SL:258 +CE23 D0 0A BNE $CE2F A:33 X:80 Y:01 P:27 SP:80 CYC:176 SL:258 +CE25 AD 7F 01 LDA $017F = 69 A:33 X:80 Y:01 P:27 SP:80 CYC:182 SL:258 +CE28 C9 69 CMP #$69 A:69 X:80 Y:01 P:25 SP:80 CYC:194 SL:258 +CE2A D0 03 BNE $CE2F A:69 X:80 Y:01 P:27 SP:80 CYC:200 SL:258 +CE2C 4C 33 CE JMP $CE33 A:69 X:80 Y:01 P:27 SP:80 CYC:206 SL:258 +CE33 EA NOP A:69 X:80 Y:01 P:27 SP:80 CYC:215 SL:258 +CE34 A2 80 LDX #$80 A:69 X:80 Y:01 P:27 SP:80 CYC:221 SL:258 +CE36 9A TXS A:69 X:80 Y:01 P:A5 SP:80 CYC:227 SL:258 +CE37 20 3D CE JSR $CE3D A:69 X:80 Y:01 P:A5 SP:80 CYC:233 SL:258 +CE3D BA TSX A:69 X:80 Y:01 P:A5 SP:7E CYC:251 SL:258 +CE3E E0 7E CPX #$7E A:69 X:7E Y:01 P:25 SP:7E CYC:257 SL:258 +CE40 D0 19 BNE $CE5B A:69 X:7E Y:01 P:27 SP:7E CYC:263 SL:258 +CE42 68 PLA A:69 X:7E Y:01 P:27 SP:7E CYC:269 SL:258 +CE43 68 PLA A:39 X:7E Y:01 P:25 SP:7F CYC:281 SL:258 +CE44 BA TSX A:CE X:7E Y:01 P:A5 SP:80 CYC:293 SL:258 +CE45 E0 80 CPX #$80 A:CE X:80 Y:01 P:A5 SP:80 CYC:299 SL:258 +CE47 D0 12 BNE $CE5B A:CE X:80 Y:01 P:27 SP:80 CYC:305 SL:258 +CE49 A9 00 LDA #$00 A:CE X:80 Y:01 P:27 SP:80 CYC:311 SL:258 +CE4B 20 4E CE JSR $CE4E A:00 X:80 Y:01 P:27 SP:80 CYC:317 SL:258 +CE4E 68 PLA A:00 X:80 Y:01 P:27 SP:7E CYC:335 SL:258 +CE4F C9 4D CMP #$4D A:4D X:80 Y:01 P:25 SP:7F CYC: 6 SL:259 +CE51 D0 08 BNE $CE5B A:4D X:80 Y:01 P:27 SP:7F CYC: 12 SL:259 +CE53 68 PLA A:4D X:80 Y:01 P:27 SP:7F CYC: 18 SL:259 +CE54 C9 CE CMP #$CE A:CE X:80 Y:01 P:A5 SP:80 CYC: 30 SL:259 +CE56 D0 03 BNE $CE5B A:CE X:80 Y:01 P:27 SP:80 CYC: 36 SL:259 +CE58 4C 5F CE JMP $CE5F A:CE X:80 Y:01 P:27 SP:80 CYC: 42 SL:259 +CE5F EA NOP A:CE X:80 Y:01 P:27 SP:80 CYC: 51 SL:259 +CE60 A9 CE LDA #$CE A:CE X:80 Y:01 P:27 SP:80 CYC: 57 SL:259 +CE62 48 PHA A:CE X:80 Y:01 P:A5 SP:80 CYC: 63 SL:259 +CE63 A9 66 LDA #$66 A:CE X:80 Y:01 P:A5 SP:7F CYC: 72 SL:259 +CE65 48 PHA A:66 X:80 Y:01 P:25 SP:7F CYC: 78 SL:259 +CE66 60 RTS A:66 X:80 Y:01 P:25 SP:7E CYC: 87 SL:259 +CE67 A2 77 LDX #$77 A:66 X:80 Y:01 P:25 SP:80 CYC:105 SL:259 +CE69 A0 69 LDY #$69 A:66 X:77 Y:01 P:25 SP:80 CYC:111 SL:259 +CE6B 18 CLC A:66 X:77 Y:69 P:25 SP:80 CYC:117 SL:259 +CE6C 24 01 BIT $01 = FF A:66 X:77 Y:69 P:24 SP:80 CYC:123 SL:259 +CE6E A9 83 LDA #$83 A:66 X:77 Y:69 P:E4 SP:80 CYC:132 SL:259 +CE70 20 66 CE JSR $CE66 A:83 X:77 Y:69 P:E4 SP:80 CYC:138 SL:259 +CE66 60 RTS A:83 X:77 Y:69 P:E4 SP:7E CYC:156 SL:259 +CE73 F0 24 BEQ $CE99 A:83 X:77 Y:69 P:E4 SP:80 CYC:174 SL:259 +CE75 10 22 BPL $CE99 A:83 X:77 Y:69 P:E4 SP:80 CYC:180 SL:259 +CE77 B0 20 BCS $CE99 A:83 X:77 Y:69 P:E4 SP:80 CYC:186 SL:259 +CE79 50 1E BVC $CE99 A:83 X:77 Y:69 P:E4 SP:80 CYC:192 SL:259 +CE7B C9 83 CMP #$83 A:83 X:77 Y:69 P:E4 SP:80 CYC:198 SL:259 +CE7D D0 1A BNE $CE99 A:83 X:77 Y:69 P:67 SP:80 CYC:204 SL:259 +CE7F C0 69 CPY #$69 A:83 X:77 Y:69 P:67 SP:80 CYC:210 SL:259 +CE81 D0 16 BNE $CE99 A:83 X:77 Y:69 P:67 SP:80 CYC:216 SL:259 +CE83 E0 77 CPX #$77 A:83 X:77 Y:69 P:67 SP:80 CYC:222 SL:259 +CE85 D0 12 BNE $CE99 A:83 X:77 Y:69 P:67 SP:80 CYC:228 SL:259 +CE87 38 SEC A:83 X:77 Y:69 P:67 SP:80 CYC:234 SL:259 +CE88 B8 CLV A:83 X:77 Y:69 P:67 SP:80 CYC:240 SL:259 +CE89 A9 00 LDA #$00 A:83 X:77 Y:69 P:27 SP:80 CYC:246 SL:259 +CE8B 20 66 CE JSR $CE66 A:00 X:77 Y:69 P:27 SP:80 CYC:252 SL:259 +CE66 60 RTS A:00 X:77 Y:69 P:27 SP:7E CYC:270 SL:259 +CE8E D0 09 BNE $CE99 A:00 X:77 Y:69 P:27 SP:80 CYC:288 SL:259 +CE90 30 07 BMI $CE99 A:00 X:77 Y:69 P:27 SP:80 CYC:294 SL:259 +CE92 90 05 BCC $CE99 A:00 X:77 Y:69 P:27 SP:80 CYC:300 SL:259 +CE94 70 03 BVS $CE99 A:00 X:77 Y:69 P:27 SP:80 CYC:306 SL:259 +CE96 4C 9D CE JMP $CE9D A:00 X:77 Y:69 P:27 SP:80 CYC:312 SL:259 +CE9D EA NOP A:00 X:77 Y:69 P:27 SP:80 CYC:321 SL:259 +CE9E A9 CE LDA #$CE A:00 X:77 Y:69 P:27 SP:80 CYC:327 SL:259 +CEA0 48 PHA A:CE X:77 Y:69 P:A5 SP:80 CYC:333 SL:259 +CEA1 A9 AE LDA #$AE A:CE X:77 Y:69 P:A5 SP:7F CYC: 1 SL:260 +CEA3 48 PHA A:AE X:77 Y:69 P:A5 SP:7F CYC: 7 SL:260 +CEA4 A9 65 LDA #$65 A:AE X:77 Y:69 P:A5 SP:7E CYC: 16 SL:260 +CEA6 48 PHA A:65 X:77 Y:69 P:25 SP:7E CYC: 22 SL:260 +CEA7 A9 55 LDA #$55 A:65 X:77 Y:69 P:25 SP:7D CYC: 31 SL:260 +CEA9 A0 88 LDY #$88 A:55 X:77 Y:69 P:25 SP:7D CYC: 37 SL:260 +CEAB A2 99 LDX #$99 A:55 X:77 Y:88 P:A5 SP:7D CYC: 43 SL:260 +CEAD 40 RTI A:55 X:99 Y:88 P:A5 SP:7D CYC: 49 SL:260 +CEAE 30 35 BMI $CEE5 A:55 X:99 Y:88 P:65 SP:80 CYC: 67 SL:260 +CEB0 50 33 BVC $CEE5 A:55 X:99 Y:88 P:65 SP:80 CYC: 73 SL:260 +CEB2 F0 31 BEQ $CEE5 A:55 X:99 Y:88 P:65 SP:80 CYC: 79 SL:260 +CEB4 90 2F BCC $CEE5 A:55 X:99 Y:88 P:65 SP:80 CYC: 85 SL:260 +CEB6 C9 55 CMP #$55 A:55 X:99 Y:88 P:65 SP:80 CYC: 91 SL:260 +CEB8 D0 2B BNE $CEE5 A:55 X:99 Y:88 P:67 SP:80 CYC: 97 SL:260 +CEBA C0 88 CPY #$88 A:55 X:99 Y:88 P:67 SP:80 CYC:103 SL:260 +CEBC D0 27 BNE $CEE5 A:55 X:99 Y:88 P:67 SP:80 CYC:109 SL:260 +CEBE E0 99 CPX #$99 A:55 X:99 Y:88 P:67 SP:80 CYC:115 SL:260 +CEC0 D0 23 BNE $CEE5 A:55 X:99 Y:88 P:67 SP:80 CYC:121 SL:260 +CEC2 A9 CE LDA #$CE A:55 X:99 Y:88 P:67 SP:80 CYC:127 SL:260 +CEC4 48 PHA A:CE X:99 Y:88 P:E5 SP:80 CYC:133 SL:260 +CEC5 A9 CE LDA #$CE A:CE X:99 Y:88 P:E5 SP:7F CYC:142 SL:260 +CEC7 48 PHA A:CE X:99 Y:88 P:E5 SP:7F CYC:148 SL:260 +CEC8 A9 87 LDA #$87 A:CE X:99 Y:88 P:E5 SP:7E CYC:157 SL:260 +CECA 48 PHA A:87 X:99 Y:88 P:E5 SP:7E CYC:163 SL:260 +CECB A9 55 LDA #$55 A:87 X:99 Y:88 P:E5 SP:7D CYC:172 SL:260 +CECD 40 RTI A:55 X:99 Y:88 P:65 SP:7D CYC:178 SL:260 +CECE 10 15 BPL $CEE5 A:55 X:99 Y:88 P:A7 SP:80 CYC:196 SL:260 +CED0 70 13 BVS $CEE5 A:55 X:99 Y:88 P:A7 SP:80 CYC:202 SL:260 +CED2 D0 11 BNE $CEE5 A:55 X:99 Y:88 P:A7 SP:80 CYC:208 SL:260 +CED4 90 0F BCC $CEE5 A:55 X:99 Y:88 P:A7 SP:80 CYC:214 SL:260 +CED6 C9 55 CMP #$55 A:55 X:99 Y:88 P:A7 SP:80 CYC:220 SL:260 +CED8 D0 0B BNE $CEE5 A:55 X:99 Y:88 P:27 SP:80 CYC:226 SL:260 +CEDA C0 88 CPY #$88 A:55 X:99 Y:88 P:27 SP:80 CYC:232 SL:260 +CEDC D0 07 BNE $CEE5 A:55 X:99 Y:88 P:27 SP:80 CYC:238 SL:260 +CEDE E0 99 CPX #$99 A:55 X:99 Y:88 P:27 SP:80 CYC:244 SL:260 +CEE0 D0 03 BNE $CEE5 A:55 X:99 Y:88 P:27 SP:80 CYC:250 SL:260 +CEE2 4C E9 CE JMP $CEE9 A:55 X:99 Y:88 P:27 SP:80 CYC:256 SL:260 +CEE9 AE FF 07 LDX $07FF = FB A:55 X:99 Y:88 P:27 SP:80 CYC:265 SL:260 +CEEC 9A TXS A:55 X:FB Y:88 P:A5 SP:80 CYC:277 SL:260 +CEED 60 RTS A:55 X:FB Y:88 P:A5 SP:FB CYC:283 SL:260 +C60C 20 EE CE JSR $CEEE A:55 X:FB Y:88 P:A5 SP:FD CYC:301 SL:260 +CEEE A2 55 LDX #$55 A:55 X:FB Y:88 P:A5 SP:FB CYC:319 SL:260 +CEF0 A0 69 LDY #$69 A:55 X:55 Y:88 P:25 SP:FB CYC:325 SL:260 +CEF2 A9 FF LDA #$FF A:55 X:55 Y:69 P:25 SP:FB CYC:331 SL:260 +CEF4 85 01 STA $01 = FF A:FF X:55 Y:69 P:A5 SP:FB CYC:337 SL:260 +CEF6 EA NOP A:FF X:55 Y:69 P:A5 SP:FB CYC: 5 SL:-1 +CEF7 24 01 BIT $01 = FF A:FF X:55 Y:69 P:A5 SP:FB CYC: 11 SL:-1 +CEF9 38 SEC A:FF X:55 Y:69 P:E5 SP:FB CYC: 20 SL:-1 +CEFA A9 01 LDA #$01 A:FF X:55 Y:69 P:E5 SP:FB CYC: 26 SL:-1 +CEFC 4A LSR A A:01 X:55 Y:69 P:65 SP:FB CYC: 32 SL:-1 +CEFD 90 1D BCC $CF1C A:00 X:55 Y:69 P:67 SP:FB CYC: 38 SL:-1 +CEFF D0 1B BNE $CF1C A:00 X:55 Y:69 P:67 SP:FB CYC: 44 SL:-1 +CF01 30 19 BMI $CF1C A:00 X:55 Y:69 P:67 SP:FB CYC: 50 SL:-1 +CF03 50 17 BVC $CF1C A:00 X:55 Y:69 P:67 SP:FB CYC: 56 SL:-1 +CF05 C9 00 CMP #$00 A:00 X:55 Y:69 P:67 SP:FB CYC: 62 SL:-1 +CF07 D0 13 BNE $CF1C A:00 X:55 Y:69 P:67 SP:FB CYC: 68 SL:-1 +CF09 B8 CLV A:00 X:55 Y:69 P:67 SP:FB CYC: 74 SL:-1 +CF0A A9 AA LDA #$AA A:00 X:55 Y:69 P:27 SP:FB CYC: 80 SL:-1 +CF0C 4A LSR A A:AA X:55 Y:69 P:A5 SP:FB CYC: 86 SL:-1 +CF0D B0 0D BCS $CF1C A:55 X:55 Y:69 P:24 SP:FB CYC: 92 SL:-1 +CF0F F0 0B BEQ $CF1C A:55 X:55 Y:69 P:24 SP:FB CYC: 98 SL:-1 +CF11 30 09 BMI $CF1C A:55 X:55 Y:69 P:24 SP:FB CYC:104 SL:-1 +CF13 70 07 BVS $CF1C A:55 X:55 Y:69 P:24 SP:FB CYC:110 SL:-1 +CF15 C9 55 CMP #$55 A:55 X:55 Y:69 P:24 SP:FB CYC:116 SL:-1 +CF17 D0 03 BNE $CF1C A:55 X:55 Y:69 P:27 SP:FB CYC:122 SL:-1 +CF19 4C 20 CF JMP $CF20 A:55 X:55 Y:69 P:27 SP:FB CYC:128 SL:-1 +CF20 EA NOP A:55 X:55 Y:69 P:27 SP:FB CYC:137 SL:-1 +CF21 24 01 BIT $01 = FF A:55 X:55 Y:69 P:27 SP:FB CYC:143 SL:-1 +CF23 38 SEC A:55 X:55 Y:69 P:E5 SP:FB CYC:152 SL:-1 +CF24 A9 80 LDA #$80 A:55 X:55 Y:69 P:E5 SP:FB CYC:158 SL:-1 +CF26 0A ASL A A:80 X:55 Y:69 P:E5 SP:FB CYC:164 SL:-1 +CF27 90 1E BCC $CF47 A:00 X:55 Y:69 P:67 SP:FB CYC:170 SL:-1 +CF29 D0 1C BNE $CF47 A:00 X:55 Y:69 P:67 SP:FB CYC:176 SL:-1 +CF2B 30 1A BMI $CF47 A:00 X:55 Y:69 P:67 SP:FB CYC:182 SL:-1 +CF2D 50 18 BVC $CF47 A:00 X:55 Y:69 P:67 SP:FB CYC:188 SL:-1 +CF2F C9 00 CMP #$00 A:00 X:55 Y:69 P:67 SP:FB CYC:194 SL:-1 +CF31 D0 14 BNE $CF47 A:00 X:55 Y:69 P:67 SP:FB CYC:200 SL:-1 +CF33 B8 CLV A:00 X:55 Y:69 P:67 SP:FB CYC:206 SL:-1 +CF34 38 SEC A:00 X:55 Y:69 P:27 SP:FB CYC:212 SL:-1 +CF35 A9 55 LDA #$55 A:00 X:55 Y:69 P:27 SP:FB CYC:218 SL:-1 +CF37 0A ASL A A:55 X:55 Y:69 P:25 SP:FB CYC:224 SL:-1 +CF38 B0 0D BCS $CF47 A:AA X:55 Y:69 P:A4 SP:FB CYC:230 SL:-1 +CF3A F0 0B BEQ $CF47 A:AA X:55 Y:69 P:A4 SP:FB CYC:236 SL:-1 +CF3C 10 09 BPL $CF47 A:AA X:55 Y:69 P:A4 SP:FB CYC:242 SL:-1 +CF3E 70 07 BVS $CF47 A:AA X:55 Y:69 P:A4 SP:FB CYC:248 SL:-1 +CF40 C9 AA CMP #$AA A:AA X:55 Y:69 P:A4 SP:FB CYC:254 SL:-1 +CF42 D0 03 BNE $CF47 A:AA X:55 Y:69 P:27 SP:FB CYC:260 SL:-1 +CF44 4C 4B CF JMP $CF4B A:AA X:55 Y:69 P:27 SP:FB CYC:266 SL:-1 +CF4B EA NOP A:AA X:55 Y:69 P:27 SP:FB CYC:275 SL:-1 +CF4C 24 01 BIT $01 = FF A:AA X:55 Y:69 P:27 SP:FB CYC:281 SL:-1 +CF4E 38 SEC A:AA X:55 Y:69 P:E5 SP:FB CYC:290 SL:-1 +CF4F A9 01 LDA #$01 A:AA X:55 Y:69 P:E5 SP:FB CYC:296 SL:-1 +CF51 6A ROR A A:01 X:55 Y:69 P:65 SP:FB CYC:302 SL:-1 +CF52 90 1E BCC $CF72 A:80 X:55 Y:69 P:E5 SP:FB CYC:308 SL:-1 +CF54 F0 1C BEQ $CF72 A:80 X:55 Y:69 P:E5 SP:FB CYC:314 SL:-1 +CF56 10 1A BPL $CF72 A:80 X:55 Y:69 P:E5 SP:FB CYC:320 SL:-1 +CF58 50 18 BVC $CF72 A:80 X:55 Y:69 P:E5 SP:FB CYC:326 SL:-1 +CF5A C9 80 CMP #$80 A:80 X:55 Y:69 P:E5 SP:FB CYC:332 SL:-1 +CF5C D0 14 BNE $CF72 A:80 X:55 Y:69 P:67 SP:FB CYC:338 SL:-1 +CF5E B8 CLV A:80 X:55 Y:69 P:67 SP:FB CYC: 3 SL:0 +CF5F 18 CLC A:80 X:55 Y:69 P:27 SP:FB CYC: 9 SL:0 +CF60 A9 55 LDA #$55 A:80 X:55 Y:69 P:26 SP:FB CYC: 15 SL:0 +CF62 6A ROR A A:55 X:55 Y:69 P:24 SP:FB CYC: 21 SL:0 +CF63 90 0D BCC $CF72 A:2A X:55 Y:69 P:25 SP:FB CYC: 27 SL:0 +CF65 F0 0B BEQ $CF72 A:2A X:55 Y:69 P:25 SP:FB CYC: 33 SL:0 +CF67 30 09 BMI $CF72 A:2A X:55 Y:69 P:25 SP:FB CYC: 39 SL:0 +CF69 70 07 BVS $CF72 A:2A X:55 Y:69 P:25 SP:FB CYC: 45 SL:0 +CF6B C9 2A CMP #$2A A:2A X:55 Y:69 P:25 SP:FB CYC: 51 SL:0 +CF6D D0 03 BNE $CF72 A:2A X:55 Y:69 P:27 SP:FB CYC: 57 SL:0 +CF6F 4C 76 CF JMP $CF76 A:2A X:55 Y:69 P:27 SP:FB CYC: 63 SL:0 +CF76 EA NOP A:2A X:55 Y:69 P:27 SP:FB CYC: 72 SL:0 +CF77 24 01 BIT $01 = FF A:2A X:55 Y:69 P:27 SP:FB CYC: 78 SL:0 +CF79 38 SEC A:2A X:55 Y:69 P:E5 SP:FB CYC: 87 SL:0 +CF7A A9 80 LDA #$80 A:2A X:55 Y:69 P:E5 SP:FB CYC: 93 SL:0 +CF7C 2A ROL A A:80 X:55 Y:69 P:E5 SP:FB CYC: 99 SL:0 +CF7D 90 1E BCC $CF9D A:01 X:55 Y:69 P:65 SP:FB CYC:105 SL:0 +CF7F F0 1C BEQ $CF9D A:01 X:55 Y:69 P:65 SP:FB CYC:111 SL:0 +CF81 30 1A BMI $CF9D A:01 X:55 Y:69 P:65 SP:FB CYC:117 SL:0 +CF83 50 18 BVC $CF9D A:01 X:55 Y:69 P:65 SP:FB CYC:123 SL:0 +CF85 C9 01 CMP #$01 A:01 X:55 Y:69 P:65 SP:FB CYC:129 SL:0 +CF87 D0 14 BNE $CF9D A:01 X:55 Y:69 P:67 SP:FB CYC:135 SL:0 +CF89 B8 CLV A:01 X:55 Y:69 P:67 SP:FB CYC:141 SL:0 +CF8A 18 CLC A:01 X:55 Y:69 P:27 SP:FB CYC:147 SL:0 +CF8B A9 55 LDA #$55 A:01 X:55 Y:69 P:26 SP:FB CYC:153 SL:0 +CF8D 2A ROL A A:55 X:55 Y:69 P:24 SP:FB CYC:159 SL:0 +CF8E B0 0D BCS $CF9D A:AA X:55 Y:69 P:A4 SP:FB CYC:165 SL:0 +CF90 F0 0B BEQ $CF9D A:AA X:55 Y:69 P:A4 SP:FB CYC:171 SL:0 +CF92 10 09 BPL $CF9D A:AA X:55 Y:69 P:A4 SP:FB CYC:177 SL:0 +CF94 70 07 BVS $CF9D A:AA X:55 Y:69 P:A4 SP:FB CYC:183 SL:0 +CF96 C9 AA CMP #$AA A:AA X:55 Y:69 P:A4 SP:FB CYC:189 SL:0 +CF98 D0 03 BNE $CF9D A:AA X:55 Y:69 P:27 SP:FB CYC:195 SL:0 +CF9A 4C A1 CF JMP $CFA1 A:AA X:55 Y:69 P:27 SP:FB CYC:201 SL:0 +CFA1 60 RTS A:AA X:55 Y:69 P:27 SP:FB CYC:210 SL:0 +C60F 20 A2 CF JSR $CFA2 A:AA X:55 Y:69 P:27 SP:FD CYC:228 SL:0 +CFA2 A5 00 LDA $00 = 00 A:AA X:55 Y:69 P:27 SP:FB CYC:246 SL:0 +CFA4 8D FF 07 STA $07FF = FB A:00 X:55 Y:69 P:27 SP:FB CYC:255 SL:0 +CFA7 A9 00 LDA #$00 A:00 X:55 Y:69 P:27 SP:FB CYC:267 SL:0 +CFA9 85 80 STA $80 = 00 A:00 X:55 Y:69 P:27 SP:FB CYC:273 SL:0 +CFAB A9 02 LDA #$02 A:00 X:55 Y:69 P:27 SP:FB CYC:282 SL:0 +CFAD 85 81 STA $81 = 00 A:02 X:55 Y:69 P:25 SP:FB CYC:288 SL:0 +CFAF A9 FF LDA #$FF A:02 X:55 Y:69 P:25 SP:FB CYC:297 SL:0 +CFB1 85 01 STA $01 = FF A:FF X:55 Y:69 P:A5 SP:FB CYC:303 SL:0 +CFB3 A9 00 LDA #$00 A:FF X:55 Y:69 P:A5 SP:FB CYC:312 SL:0 +CFB5 85 82 STA $82 = 00 A:00 X:55 Y:69 P:27 SP:FB CYC:318 SL:0 +CFB7 A9 03 LDA #$03 A:00 X:55 Y:69 P:27 SP:FB CYC:327 SL:0 +CFB9 85 83 STA $83 = 00 A:03 X:55 Y:69 P:25 SP:FB CYC:333 SL:0 +CFBB 85 84 STA $84 = 00 A:03 X:55 Y:69 P:25 SP:FB CYC: 1 SL:1 +CFBD A9 00 LDA #$00 A:03 X:55 Y:69 P:25 SP:FB CYC: 10 SL:1 +CFBF 85 FF STA $FF = 00 A:00 X:55 Y:69 P:27 SP:FB CYC: 16 SL:1 +CFC1 A9 04 LDA #$04 A:00 X:55 Y:69 P:27 SP:FB CYC: 25 SL:1 +CFC3 85 00 STA $00 = 00 A:04 X:55 Y:69 P:25 SP:FB CYC: 31 SL:1 +CFC5 A9 5A LDA #$5A A:04 X:55 Y:69 P:25 SP:FB CYC: 40 SL:1 +CFC7 8D 00 02 STA $0200 = 00 A:5A X:55 Y:69 P:25 SP:FB CYC: 46 SL:1 +CFCA A9 5B LDA #$5B A:5A X:55 Y:69 P:25 SP:FB CYC: 58 SL:1 +CFCC 8D 00 03 STA $0300 = 00 A:5B X:55 Y:69 P:25 SP:FB CYC: 64 SL:1 +CFCF A9 5C LDA #$5C A:5B X:55 Y:69 P:25 SP:FB CYC: 76 SL:1 +CFD1 8D 03 03 STA $0303 = 00 A:5C X:55 Y:69 P:25 SP:FB CYC: 82 SL:1 +CFD4 A9 5D LDA #$5D A:5C X:55 Y:69 P:25 SP:FB CYC: 94 SL:1 +CFD6 8D 00 04 STA $0400 = 00 A:5D X:55 Y:69 P:25 SP:FB CYC:100 SL:1 +CFD9 A2 00 LDX #$00 A:5D X:55 Y:69 P:25 SP:FB CYC:112 SL:1 +CFDB A1 80 LDA ($80,X) @ 80 = 0200 = 5A A:5D X:00 Y:69 P:27 SP:FB CYC:118 SL:1 +CFDD C9 5A CMP #$5A A:5A X:00 Y:69 P:25 SP:FB CYC:136 SL:1 +CFDF D0 1F BNE $D000 A:5A X:00 Y:69 P:27 SP:FB CYC:142 SL:1 +CFE1 E8 INX A:5A X:00 Y:69 P:27 SP:FB CYC:148 SL:1 +CFE2 E8 INX A:5A X:01 Y:69 P:25 SP:FB CYC:154 SL:1 +CFE3 A1 80 LDA ($80,X) @ 82 = 0300 = 5B A:5A X:02 Y:69 P:25 SP:FB CYC:160 SL:1 +CFE5 C9 5B CMP #$5B A:5B X:02 Y:69 P:25 SP:FB CYC:178 SL:1 +CFE7 D0 17 BNE $D000 A:5B X:02 Y:69 P:27 SP:FB CYC:184 SL:1 +CFE9 E8 INX A:5B X:02 Y:69 P:27 SP:FB CYC:190 SL:1 +CFEA A1 80 LDA ($80,X) @ 83 = 0303 = 5C A:5B X:03 Y:69 P:25 SP:FB CYC:196 SL:1 +CFEC C9 5C CMP #$5C A:5C X:03 Y:69 P:25 SP:FB CYC:214 SL:1 +CFEE D0 10 BNE $D000 A:5C X:03 Y:69 P:27 SP:FB CYC:220 SL:1 +CFF0 A2 00 LDX #$00 A:5C X:03 Y:69 P:27 SP:FB CYC:226 SL:1 +CFF2 A1 FF LDA ($FF,X) @ FF = 0400 = 5D A:5C X:00 Y:69 P:27 SP:FB CYC:232 SL:1 +CFF4 C9 5D CMP #$5D A:5D X:00 Y:69 P:25 SP:FB CYC:250 SL:1 +CFF6 D0 08 BNE $D000 A:5D X:00 Y:69 P:27 SP:FB CYC:256 SL:1 +CFF8 A2 81 LDX #$81 A:5D X:00 Y:69 P:27 SP:FB CYC:262 SL:1 +CFFA A1 FF LDA ($FF,X) @ 80 = 0200 = 5A A:5D X:81 Y:69 P:A5 SP:FB CYC:268 SL:1 +CFFC C9 5A CMP #$5A A:5A X:81 Y:69 P:25 SP:FB CYC:286 SL:1 +CFFE F0 05 BEQ $D005 A:5A X:81 Y:69 P:27 SP:FB CYC:292 SL:1 +D005 A9 AA LDA #$AA A:5A X:81 Y:69 P:27 SP:FB CYC:301 SL:1 +D007 A2 00 LDX #$00 A:AA X:81 Y:69 P:A5 SP:FB CYC:307 SL:1 +D009 81 80 STA ($80,X) @ 80 = 0200 = 5A A:AA X:00 Y:69 P:27 SP:FB CYC:313 SL:1 +D00B E8 INX A:AA X:00 Y:69 P:27 SP:FB CYC:331 SL:1 +D00C E8 INX A:AA X:01 Y:69 P:25 SP:FB CYC:337 SL:1 +D00D A9 AB LDA #$AB A:AA X:02 Y:69 P:25 SP:FB CYC: 2 SL:2 +D00F 81 80 STA ($80,X) @ 82 = 0300 = 5B A:AB X:02 Y:69 P:A5 SP:FB CYC: 8 SL:2 +D011 E8 INX A:AB X:02 Y:69 P:A5 SP:FB CYC: 26 SL:2 +D012 A9 AC LDA #$AC A:AB X:03 Y:69 P:25 SP:FB CYC: 32 SL:2 +D014 81 80 STA ($80,X) @ 83 = 0303 = 5C A:AC X:03 Y:69 P:A5 SP:FB CYC: 38 SL:2 +D016 A2 00 LDX #$00 A:AC X:03 Y:69 P:A5 SP:FB CYC: 56 SL:2 +D018 A9 AD LDA #$AD A:AC X:00 Y:69 P:27 SP:FB CYC: 62 SL:2 +D01A 81 FF STA ($FF,X) @ FF = 0400 = 5D A:AD X:00 Y:69 P:A5 SP:FB CYC: 68 SL:2 +D01C AD 00 02 LDA $0200 = AA A:AD X:00 Y:69 P:A5 SP:FB CYC: 86 SL:2 +D01F C9 AA CMP #$AA A:AA X:00 Y:69 P:A5 SP:FB CYC: 98 SL:2 +D021 D0 15 BNE $D038 A:AA X:00 Y:69 P:27 SP:FB CYC:104 SL:2 +D023 AD 00 03 LDA $0300 = AB A:AA X:00 Y:69 P:27 SP:FB CYC:110 SL:2 +D026 C9 AB CMP #$AB A:AB X:00 Y:69 P:A5 SP:FB CYC:122 SL:2 +D028 D0 0E BNE $D038 A:AB X:00 Y:69 P:27 SP:FB CYC:128 SL:2 +D02A AD 03 03 LDA $0303 = AC A:AB X:00 Y:69 P:27 SP:FB CYC:134 SL:2 +D02D C9 AC CMP #$AC A:AC X:00 Y:69 P:A5 SP:FB CYC:146 SL:2 +D02F D0 07 BNE $D038 A:AC X:00 Y:69 P:27 SP:FB CYC:152 SL:2 +D031 AD 00 04 LDA $0400 = AD A:AC X:00 Y:69 P:27 SP:FB CYC:158 SL:2 +D034 C9 AD CMP #$AD A:AD X:00 Y:69 P:A5 SP:FB CYC:170 SL:2 +D036 F0 05 BEQ $D03D A:AD X:00 Y:69 P:27 SP:FB CYC:176 SL:2 +D03D AD FF 07 LDA $07FF = 00 A:AD X:00 Y:69 P:27 SP:FB CYC:185 SL:2 +D040 85 00 STA $00 = 04 A:00 X:00 Y:69 P:27 SP:FB CYC:197 SL:2 +D042 A9 00 LDA #$00 A:00 X:00 Y:69 P:27 SP:FB CYC:206 SL:2 +D044 8D 00 03 STA $0300 = AB A:00 X:00 Y:69 P:27 SP:FB CYC:212 SL:2 +D047 A9 AA LDA #$AA A:00 X:00 Y:69 P:27 SP:FB CYC:224 SL:2 +D049 8D 00 02 STA $0200 = AA A:AA X:00 Y:69 P:A5 SP:FB CYC:230 SL:2 +D04C A2 00 LDX #$00 A:AA X:00 Y:69 P:A5 SP:FB CYC:242 SL:2 +D04E A0 5A LDY #$5A A:AA X:00 Y:69 P:27 SP:FB CYC:248 SL:2 +D050 20 B6 F7 JSR $F7B6 A:AA X:00 Y:5A P:25 SP:FB CYC:254 SL:2 +F7B6 18 CLC A:AA X:00 Y:5A P:25 SP:F9 CYC:272 SL:2 +F7B7 A9 FF LDA #$FF A:AA X:00 Y:5A P:24 SP:F9 CYC:278 SL:2 +F7B9 85 01 STA $01 = FF A:FF X:00 Y:5A P:A4 SP:F9 CYC:284 SL:2 +F7BB 24 01 BIT $01 = FF A:FF X:00 Y:5A P:A4 SP:F9 CYC:293 SL:2 +F7BD A9 55 LDA #$55 A:FF X:00 Y:5A P:E4 SP:F9 CYC:302 SL:2 +F7BF 60 RTS A:55 X:00 Y:5A P:64 SP:F9 CYC:308 SL:2 +D053 01 80 ORA ($80,X) @ 80 = 0200 = AA A:55 X:00 Y:5A P:64 SP:FB CYC:326 SL:2 +D055 20 C0 F7 JSR $F7C0 A:FF X:00 Y:5A P:E4 SP:FB CYC: 3 SL:3 +F7C0 B0 09 BCS $F7CB A:FF X:00 Y:5A P:E4 SP:F9 CYC: 21 SL:3 +F7C2 10 07 BPL $F7CB A:FF X:00 Y:5A P:E4 SP:F9 CYC: 27 SL:3 +F7C4 C9 FF CMP #$FF A:FF X:00 Y:5A P:E4 SP:F9 CYC: 33 SL:3 +F7C6 D0 03 BNE $F7CB A:FF X:00 Y:5A P:67 SP:F9 CYC: 39 SL:3 +F7C8 50 01 BVC $F7CB A:FF X:00 Y:5A P:67 SP:F9 CYC: 45 SL:3 +F7CA 60 RTS A:FF X:00 Y:5A P:67 SP:F9 CYC: 51 SL:3 +D058 C8 INY A:FF X:00 Y:5A P:67 SP:FB CYC: 69 SL:3 +D059 20 CE F7 JSR $F7CE A:FF X:00 Y:5B P:65 SP:FB CYC: 75 SL:3 +F7CE 38 SEC A:FF X:00 Y:5B P:65 SP:F9 CYC: 93 SL:3 +F7CF B8 CLV A:FF X:00 Y:5B P:65 SP:F9 CYC: 99 SL:3 +F7D0 A9 00 LDA #$00 A:FF X:00 Y:5B P:25 SP:F9 CYC:105 SL:3 +F7D2 60 RTS A:00 X:00 Y:5B P:27 SP:F9 CYC:111 SL:3 +D05C 01 82 ORA ($82,X) @ 82 = 0300 = 00 A:00 X:00 Y:5B P:27 SP:FB CYC:129 SL:3 +D05E 20 D3 F7 JSR $F7D3 A:00 X:00 Y:5B P:27 SP:FB CYC:147 SL:3 +F7D3 D0 07 BNE $F7DC A:00 X:00 Y:5B P:27 SP:F9 CYC:165 SL:3 +F7D5 70 05 BVS $F7DC A:00 X:00 Y:5B P:27 SP:F9 CYC:171 SL:3 +F7D7 90 03 BCC $F7DC A:00 X:00 Y:5B P:27 SP:F9 CYC:177 SL:3 +F7D9 30 01 BMI $F7DC A:00 X:00 Y:5B P:27 SP:F9 CYC:183 SL:3 +F7DB 60 RTS A:00 X:00 Y:5B P:27 SP:F9 CYC:189 SL:3 +D061 C8 INY A:00 X:00 Y:5B P:27 SP:FB CYC:207 SL:3 +D062 20 DF F7 JSR $F7DF A:00 X:00 Y:5C P:25 SP:FB CYC:213 SL:3 +F7DF 18 CLC A:00 X:00 Y:5C P:25 SP:F9 CYC:231 SL:3 +F7E0 24 01 BIT $01 = FF A:00 X:00 Y:5C P:24 SP:F9 CYC:237 SL:3 +F7E2 A9 55 LDA #$55 A:00 X:00 Y:5C P:E6 SP:F9 CYC:246 SL:3 +F7E4 60 RTS A:55 X:00 Y:5C P:64 SP:F9 CYC:252 SL:3 +D065 21 80 AND ($80,X) @ 80 = 0200 = AA A:55 X:00 Y:5C P:64 SP:FB CYC:270 SL:3 +D067 20 E5 F7 JSR $F7E5 A:00 X:00 Y:5C P:66 SP:FB CYC:288 SL:3 +F7E5 D0 07 BNE $F7EE A:00 X:00 Y:5C P:66 SP:F9 CYC:306 SL:3 +F7E7 50 05 BVC $F7EE A:00 X:00 Y:5C P:66 SP:F9 CYC:312 SL:3 +F7E9 B0 03 BCS $F7EE A:00 X:00 Y:5C P:66 SP:F9 CYC:318 SL:3 +F7EB 30 01 BMI $F7EE A:00 X:00 Y:5C P:66 SP:F9 CYC:324 SL:3 +F7ED 60 RTS A:00 X:00 Y:5C P:66 SP:F9 CYC:330 SL:3 +D06A C8 INY A:00 X:00 Y:5C P:66 SP:FB CYC: 7 SL:4 +D06B A9 EF LDA #$EF A:00 X:00 Y:5D P:64 SP:FB CYC: 13 SL:4 +D06D 8D 00 03 STA $0300 = 00 A:EF X:00 Y:5D P:E4 SP:FB CYC: 19 SL:4 +D070 20 F1 F7 JSR $F7F1 A:EF X:00 Y:5D P:E4 SP:FB CYC: 31 SL:4 +F7F1 38 SEC A:EF X:00 Y:5D P:E4 SP:F9 CYC: 49 SL:4 +F7F2 B8 CLV A:EF X:00 Y:5D P:E5 SP:F9 CYC: 55 SL:4 +F7F3 A9 F8 LDA #$F8 A:EF X:00 Y:5D P:A5 SP:F9 CYC: 61 SL:4 +F7F5 60 RTS A:F8 X:00 Y:5D P:A5 SP:F9 CYC: 67 SL:4 +D073 21 82 AND ($82,X) @ 82 = 0300 = EF A:F8 X:00 Y:5D P:A5 SP:FB CYC: 85 SL:4 +D075 20 F6 F7 JSR $F7F6 A:E8 X:00 Y:5D P:A5 SP:FB CYC:103 SL:4 +F7F6 90 09 BCC $F801 A:E8 X:00 Y:5D P:A5 SP:F9 CYC:121 SL:4 +F7F8 10 07 BPL $F801 A:E8 X:00 Y:5D P:A5 SP:F9 CYC:127 SL:4 +F7FA C9 E8 CMP #$E8 A:E8 X:00 Y:5D P:A5 SP:F9 CYC:133 SL:4 +F7FC D0 03 BNE $F801 A:E8 X:00 Y:5D P:27 SP:F9 CYC:139 SL:4 +F7FE 70 01 BVS $F801 A:E8 X:00 Y:5D P:27 SP:F9 CYC:145 SL:4 +F800 60 RTS A:E8 X:00 Y:5D P:27 SP:F9 CYC:151 SL:4 +D078 C8 INY A:E8 X:00 Y:5D P:27 SP:FB CYC:169 SL:4 +D079 20 04 F8 JSR $F804 A:E8 X:00 Y:5E P:25 SP:FB CYC:175 SL:4 +F804 18 CLC A:E8 X:00 Y:5E P:25 SP:F9 CYC:193 SL:4 +F805 24 01 BIT $01 = FF A:E8 X:00 Y:5E P:24 SP:F9 CYC:199 SL:4 +F807 A9 5F LDA #$5F A:E8 X:00 Y:5E P:E4 SP:F9 CYC:208 SL:4 +F809 60 RTS A:5F X:00 Y:5E P:64 SP:F9 CYC:214 SL:4 +D07C 41 80 EOR ($80,X) @ 80 = 0200 = AA A:5F X:00 Y:5E P:64 SP:FB CYC:232 SL:4 +D07E 20 0A F8 JSR $F80A A:F5 X:00 Y:5E P:E4 SP:FB CYC:250 SL:4 +F80A B0 09 BCS $F815 A:F5 X:00 Y:5E P:E4 SP:F9 CYC:268 SL:4 +F80C 10 07 BPL $F815 A:F5 X:00 Y:5E P:E4 SP:F9 CYC:274 SL:4 +F80E C9 F5 CMP #$F5 A:F5 X:00 Y:5E P:E4 SP:F9 CYC:280 SL:4 +F810 D0 03 BNE $F815 A:F5 X:00 Y:5E P:67 SP:F9 CYC:286 SL:4 +F812 50 01 BVC $F815 A:F5 X:00 Y:5E P:67 SP:F9 CYC:292 SL:4 +F814 60 RTS A:F5 X:00 Y:5E P:67 SP:F9 CYC:298 SL:4 +D081 C8 INY A:F5 X:00 Y:5E P:67 SP:FB CYC:316 SL:4 +D082 A9 70 LDA #$70 A:F5 X:00 Y:5F P:65 SP:FB CYC:322 SL:4 +D084 8D 00 03 STA $0300 = EF A:70 X:00 Y:5F P:65 SP:FB CYC:328 SL:4 +D087 20 18 F8 JSR $F818 A:70 X:00 Y:5F P:65 SP:FB CYC:340 SL:4 +F818 38 SEC A:70 X:00 Y:5F P:65 SP:F9 CYC: 17 SL:5 +F819 B8 CLV A:70 X:00 Y:5F P:65 SP:F9 CYC: 23 SL:5 +F81A A9 70 LDA #$70 A:70 X:00 Y:5F P:25 SP:F9 CYC: 29 SL:5 +F81C 60 RTS A:70 X:00 Y:5F P:25 SP:F9 CYC: 35 SL:5 +D08A 41 82 EOR ($82,X) @ 82 = 0300 = 70 A:70 X:00 Y:5F P:25 SP:FB CYC: 53 SL:5 +D08C 20 1D F8 JSR $F81D A:00 X:00 Y:5F P:27 SP:FB CYC: 71 SL:5 +F81D D0 07 BNE $F826 A:00 X:00 Y:5F P:27 SP:F9 CYC: 89 SL:5 +F81F 70 05 BVS $F826 A:00 X:00 Y:5F P:27 SP:F9 CYC: 95 SL:5 +F821 90 03 BCC $F826 A:00 X:00 Y:5F P:27 SP:F9 CYC:101 SL:5 +F823 30 01 BMI $F826 A:00 X:00 Y:5F P:27 SP:F9 CYC:107 SL:5 +F825 60 RTS A:00 X:00 Y:5F P:27 SP:F9 CYC:113 SL:5 +D08F C8 INY A:00 X:00 Y:5F P:27 SP:FB CYC:131 SL:5 +D090 A9 69 LDA #$69 A:00 X:00 Y:60 P:25 SP:FB CYC:137 SL:5 +D092 8D 00 02 STA $0200 = AA A:69 X:00 Y:60 P:25 SP:FB CYC:143 SL:5 +D095 20 29 F8 JSR $F829 A:69 X:00 Y:60 P:25 SP:FB CYC:155 SL:5 +F829 18 CLC A:69 X:00 Y:60 P:25 SP:F9 CYC:173 SL:5 +F82A 24 01 BIT $01 = FF A:69 X:00 Y:60 P:24 SP:F9 CYC:179 SL:5 +F82C A9 00 LDA #$00 A:69 X:00 Y:60 P:E4 SP:F9 CYC:188 SL:5 +F82E 60 RTS A:00 X:00 Y:60 P:66 SP:F9 CYC:194 SL:5 +D098 61 80 ADC ($80,X) @ 80 = 0200 = 69 A:00 X:00 Y:60 P:66 SP:FB CYC:212 SL:5 +D09A 20 2F F8 JSR $F82F A:69 X:00 Y:60 P:24 SP:FB CYC:230 SL:5 +F82F 30 09 BMI $F83A A:69 X:00 Y:60 P:24 SP:F9 CYC:248 SL:5 +F831 B0 07 BCS $F83A A:69 X:00 Y:60 P:24 SP:F9 CYC:254 SL:5 +F833 C9 69 CMP #$69 A:69 X:00 Y:60 P:24 SP:F9 CYC:260 SL:5 +F835 D0 03 BNE $F83A A:69 X:00 Y:60 P:27 SP:F9 CYC:266 SL:5 +F837 70 01 BVS $F83A A:69 X:00 Y:60 P:27 SP:F9 CYC:272 SL:5 +F839 60 RTS A:69 X:00 Y:60 P:27 SP:F9 CYC:278 SL:5 +D09D C8 INY A:69 X:00 Y:60 P:27 SP:FB CYC:296 SL:5 +D09E 20 3D F8 JSR $F83D A:69 X:00 Y:61 P:25 SP:FB CYC:302 SL:5 +F83D 38 SEC A:69 X:00 Y:61 P:25 SP:F9 CYC:320 SL:5 +F83E 24 01 BIT $01 = FF A:69 X:00 Y:61 P:25 SP:F9 CYC:326 SL:5 +F840 A9 00 LDA #$00 A:69 X:00 Y:61 P:E5 SP:F9 CYC:335 SL:5 +F842 60 RTS A:00 X:00 Y:61 P:67 SP:F9 CYC: 0 SL:6 +D0A1 61 80 ADC ($80,X) @ 80 = 0200 = 69 A:00 X:00 Y:61 P:67 SP:FB CYC: 18 SL:6 +D0A3 20 43 F8 JSR $F843 A:6A X:00 Y:61 P:24 SP:FB CYC: 36 SL:6 +F843 30 09 BMI $F84E A:6A X:00 Y:61 P:24 SP:F9 CYC: 54 SL:6 +F845 B0 07 BCS $F84E A:6A X:00 Y:61 P:24 SP:F9 CYC: 60 SL:6 +F847 C9 6A CMP #$6A A:6A X:00 Y:61 P:24 SP:F9 CYC: 66 SL:6 +F849 D0 03 BNE $F84E A:6A X:00 Y:61 P:27 SP:F9 CYC: 72 SL:6 +F84B 70 01 BVS $F84E A:6A X:00 Y:61 P:27 SP:F9 CYC: 78 SL:6 +F84D 60 RTS A:6A X:00 Y:61 P:27 SP:F9 CYC: 84 SL:6 +D0A6 C8 INY A:6A X:00 Y:61 P:27 SP:FB CYC:102 SL:6 +D0A7 A9 7F LDA #$7F A:6A X:00 Y:62 P:25 SP:FB CYC:108 SL:6 +D0A9 8D 00 02 STA $0200 = 69 A:7F X:00 Y:62 P:25 SP:FB CYC:114 SL:6 +D0AC 20 51 F8 JSR $F851 A:7F X:00 Y:62 P:25 SP:FB CYC:126 SL:6 +F851 38 SEC A:7F X:00 Y:62 P:25 SP:F9 CYC:144 SL:6 +F852 B8 CLV A:7F X:00 Y:62 P:25 SP:F9 CYC:150 SL:6 +F853 A9 7F LDA #$7F A:7F X:00 Y:62 P:25 SP:F9 CYC:156 SL:6 +F855 60 RTS A:7F X:00 Y:62 P:25 SP:F9 CYC:162 SL:6 +D0AF 61 80 ADC ($80,X) @ 80 = 0200 = 7F A:7F X:00 Y:62 P:25 SP:FB CYC:180 SL:6 +D0B1 20 56 F8 JSR $F856 A:FF X:00 Y:62 P:E4 SP:FB CYC:198 SL:6 +F856 10 09 BPL $F861 A:FF X:00 Y:62 P:E4 SP:F9 CYC:216 SL:6 +F858 B0 07 BCS $F861 A:FF X:00 Y:62 P:E4 SP:F9 CYC:222 SL:6 +F85A C9 FF CMP #$FF A:FF X:00 Y:62 P:E4 SP:F9 CYC:228 SL:6 +F85C D0 03 BNE $F861 A:FF X:00 Y:62 P:67 SP:F9 CYC:234 SL:6 +F85E 50 01 BVC $F861 A:FF X:00 Y:62 P:67 SP:F9 CYC:240 SL:6 +F860 60 RTS A:FF X:00 Y:62 P:67 SP:F9 CYC:246 SL:6 +D0B4 C8 INY A:FF X:00 Y:62 P:67 SP:FB CYC:264 SL:6 +D0B5 A9 80 LDA #$80 A:FF X:00 Y:63 P:65 SP:FB CYC:270 SL:6 +D0B7 8D 00 02 STA $0200 = 7F A:80 X:00 Y:63 P:E5 SP:FB CYC:276 SL:6 +D0BA 20 64 F8 JSR $F864 A:80 X:00 Y:63 P:E5 SP:FB CYC:288 SL:6 +F864 18 CLC A:80 X:00 Y:63 P:E5 SP:F9 CYC:306 SL:6 +F865 24 01 BIT $01 = FF A:80 X:00 Y:63 P:E4 SP:F9 CYC:312 SL:6 +F867 A9 7F LDA #$7F A:80 X:00 Y:63 P:E4 SP:F9 CYC:321 SL:6 +F869 60 RTS A:7F X:00 Y:63 P:64 SP:F9 CYC:327 SL:6 +D0BD 61 80 ADC ($80,X) @ 80 = 0200 = 80 A:7F X:00 Y:63 P:64 SP:FB CYC: 4 SL:7 +D0BF 20 6A F8 JSR $F86A A:FF X:00 Y:63 P:A4 SP:FB CYC: 22 SL:7 +F86A 10 09 BPL $F875 A:FF X:00 Y:63 P:A4 SP:F9 CYC: 40 SL:7 +F86C B0 07 BCS $F875 A:FF X:00 Y:63 P:A4 SP:F9 CYC: 46 SL:7 +F86E C9 FF CMP #$FF A:FF X:00 Y:63 P:A4 SP:F9 CYC: 52 SL:7 +F870 D0 03 BNE $F875 A:FF X:00 Y:63 P:27 SP:F9 CYC: 58 SL:7 +F872 70 01 BVS $F875 A:FF X:00 Y:63 P:27 SP:F9 CYC: 64 SL:7 +F874 60 RTS A:FF X:00 Y:63 P:27 SP:F9 CYC: 70 SL:7 +D0C2 C8 INY A:FF X:00 Y:63 P:27 SP:FB CYC: 88 SL:7 +D0C3 20 78 F8 JSR $F878 A:FF X:00 Y:64 P:25 SP:FB CYC: 94 SL:7 +F878 38 SEC A:FF X:00 Y:64 P:25 SP:F9 CYC:112 SL:7 +F879 B8 CLV A:FF X:00 Y:64 P:25 SP:F9 CYC:118 SL:7 +F87A A9 7F LDA #$7F A:FF X:00 Y:64 P:25 SP:F9 CYC:124 SL:7 +F87C 60 RTS A:7F X:00 Y:64 P:25 SP:F9 CYC:130 SL:7 +D0C6 61 80 ADC ($80,X) @ 80 = 0200 = 80 A:7F X:00 Y:64 P:25 SP:FB CYC:148 SL:7 +D0C8 20 7D F8 JSR $F87D A:00 X:00 Y:64 P:27 SP:FB CYC:166 SL:7 +F87D D0 07 BNE $F886 A:00 X:00 Y:64 P:27 SP:F9 CYC:184 SL:7 +F87F 30 05 BMI $F886 A:00 X:00 Y:64 P:27 SP:F9 CYC:190 SL:7 +F881 70 03 BVS $F886 A:00 X:00 Y:64 P:27 SP:F9 CYC:196 SL:7 +F883 90 01 BCC $F886 A:00 X:00 Y:64 P:27 SP:F9 CYC:202 SL:7 +F885 60 RTS A:00 X:00 Y:64 P:27 SP:F9 CYC:208 SL:7 +D0CB C8 INY A:00 X:00 Y:64 P:27 SP:FB CYC:226 SL:7 +D0CC A9 40 LDA #$40 A:00 X:00 Y:65 P:25 SP:FB CYC:232 SL:7 +D0CE 8D 00 02 STA $0200 = 80 A:40 X:00 Y:65 P:25 SP:FB CYC:238 SL:7 +D0D1 20 89 F8 JSR $F889 A:40 X:00 Y:65 P:25 SP:FB CYC:250 SL:7 +F889 24 01 BIT $01 = FF A:40 X:00 Y:65 P:25 SP:F9 CYC:268 SL:7 +F88B A9 40 LDA #$40 A:40 X:00 Y:65 P:E5 SP:F9 CYC:277 SL:7 +F88D 60 RTS A:40 X:00 Y:65 P:65 SP:F9 CYC:283 SL:7 +D0D4 C1 80 CMP ($80,X) @ 80 = 0200 = 40 A:40 X:00 Y:65 P:65 SP:FB CYC:301 SL:7 +D0D6 20 8E F8 JSR $F88E A:40 X:00 Y:65 P:67 SP:FB CYC:319 SL:7 +F88E 30 07 BMI $F897 A:40 X:00 Y:65 P:67 SP:F9 CYC:337 SL:7 +F890 90 05 BCC $F897 A:40 X:00 Y:65 P:67 SP:F9 CYC: 2 SL:8 +F892 D0 03 BNE $F897 A:40 X:00 Y:65 P:67 SP:F9 CYC: 8 SL:8 +F894 50 01 BVC $F897 A:40 X:00 Y:65 P:67 SP:F9 CYC: 14 SL:8 +F896 60 RTS A:40 X:00 Y:65 P:67 SP:F9 CYC: 20 SL:8 +D0D9 C8 INY A:40 X:00 Y:65 P:67 SP:FB CYC: 38 SL:8 +D0DA 48 PHA A:40 X:00 Y:66 P:65 SP:FB CYC: 44 SL:8 +D0DB A9 3F LDA #$3F A:40 X:00 Y:66 P:65 SP:FA CYC: 53 SL:8 +D0DD 8D 00 02 STA $0200 = 40 A:3F X:00 Y:66 P:65 SP:FA CYC: 59 SL:8 +D0E0 68 PLA A:3F X:00 Y:66 P:65 SP:FA CYC: 71 SL:8 +D0E1 20 9A F8 JSR $F89A A:40 X:00 Y:66 P:65 SP:FB CYC: 83 SL:8 +F89A B8 CLV A:40 X:00 Y:66 P:65 SP:F9 CYC:101 SL:8 +F89B 60 RTS A:40 X:00 Y:66 P:25 SP:F9 CYC:107 SL:8 +D0E4 C1 80 CMP ($80,X) @ 80 = 0200 = 3F A:40 X:00 Y:66 P:25 SP:FB CYC:125 SL:8 +D0E6 20 9C F8 JSR $F89C A:40 X:00 Y:66 P:25 SP:FB CYC:143 SL:8 +F89C F0 07 BEQ $F8A5 A:40 X:00 Y:66 P:25 SP:F9 CYC:161 SL:8 +F89E 30 05 BMI $F8A5 A:40 X:00 Y:66 P:25 SP:F9 CYC:167 SL:8 +F8A0 90 03 BCC $F8A5 A:40 X:00 Y:66 P:25 SP:F9 CYC:173 SL:8 +F8A2 70 01 BVS $F8A5 A:40 X:00 Y:66 P:25 SP:F9 CYC:179 SL:8 +F8A4 60 RTS A:40 X:00 Y:66 P:25 SP:F9 CYC:185 SL:8 +D0E9 C8 INY A:40 X:00 Y:66 P:25 SP:FB CYC:203 SL:8 +D0EA 48 PHA A:40 X:00 Y:67 P:25 SP:FB CYC:209 SL:8 +D0EB A9 41 LDA #$41 A:40 X:00 Y:67 P:25 SP:FA CYC:218 SL:8 +D0ED 8D 00 02 STA $0200 = 3F A:41 X:00 Y:67 P:25 SP:FA CYC:224 SL:8 +D0F0 68 PLA A:41 X:00 Y:67 P:25 SP:FA CYC:236 SL:8 +D0F1 C1 80 CMP ($80,X) @ 80 = 0200 = 41 A:40 X:00 Y:67 P:25 SP:FB CYC:248 SL:8 +D0F3 20 A8 F8 JSR $F8A8 A:40 X:00 Y:67 P:A4 SP:FB CYC:266 SL:8 +F8A8 F0 05 BEQ $F8AF A:40 X:00 Y:67 P:A4 SP:F9 CYC:284 SL:8 +F8AA 10 03 BPL $F8AF A:40 X:00 Y:67 P:A4 SP:F9 CYC:290 SL:8 +F8AC 10 01 BPL $F8AF A:40 X:00 Y:67 P:A4 SP:F9 CYC:296 SL:8 +F8AE 60 RTS A:40 X:00 Y:67 P:A4 SP:F9 CYC:302 SL:8 +D0F6 C8 INY A:40 X:00 Y:67 P:A4 SP:FB CYC:320 SL:8 +D0F7 48 PHA A:40 X:00 Y:68 P:24 SP:FB CYC:326 SL:8 +D0F8 A9 00 LDA #$00 A:40 X:00 Y:68 P:24 SP:FA CYC:335 SL:8 +D0FA 8D 00 02 STA $0200 = 41 A:00 X:00 Y:68 P:26 SP:FA CYC: 0 SL:9 +D0FD 68 PLA A:00 X:00 Y:68 P:26 SP:FA CYC: 12 SL:9 +D0FE 20 B2 F8 JSR $F8B2 A:40 X:00 Y:68 P:24 SP:FB CYC: 24 SL:9 +F8B2 A9 80 LDA #$80 A:40 X:00 Y:68 P:24 SP:F9 CYC: 42 SL:9 +F8B4 60 RTS A:80 X:00 Y:68 P:A4 SP:F9 CYC: 48 SL:9 +D101 C1 80 CMP ($80,X) @ 80 = 0200 = 00 A:80 X:00 Y:68 P:A4 SP:FB CYC: 66 SL:9 +D103 20 B5 F8 JSR $F8B5 A:80 X:00 Y:68 P:A5 SP:FB CYC: 84 SL:9 +F8B5 F0 05 BEQ $F8BC A:80 X:00 Y:68 P:A5 SP:F9 CYC:102 SL:9 +F8B7 10 03 BPL $F8BC A:80 X:00 Y:68 P:A5 SP:F9 CYC:108 SL:9 +F8B9 90 01 BCC $F8BC A:80 X:00 Y:68 P:A5 SP:F9 CYC:114 SL:9 +F8BB 60 RTS A:80 X:00 Y:68 P:A5 SP:F9 CYC:120 SL:9 +D106 C8 INY A:80 X:00 Y:68 P:A5 SP:FB CYC:138 SL:9 +D107 48 PHA A:80 X:00 Y:69 P:25 SP:FB CYC:144 SL:9 +D108 A9 80 LDA #$80 A:80 X:00 Y:69 P:25 SP:FA CYC:153 SL:9 +D10A 8D 00 02 STA $0200 = 00 A:80 X:00 Y:69 P:A5 SP:FA CYC:159 SL:9 +D10D 68 PLA A:80 X:00 Y:69 P:A5 SP:FA CYC:171 SL:9 +D10E C1 80 CMP ($80,X) @ 80 = 0200 = 80 A:80 X:00 Y:69 P:A5 SP:FB CYC:183 SL:9 +D110 20 BF F8 JSR $F8BF A:80 X:00 Y:69 P:27 SP:FB CYC:201 SL:9 +F8BF D0 05 BNE $F8C6 A:80 X:00 Y:69 P:27 SP:F9 CYC:219 SL:9 +F8C1 30 03 BMI $F8C6 A:80 X:00 Y:69 P:27 SP:F9 CYC:225 SL:9 +F8C3 90 01 BCC $F8C6 A:80 X:00 Y:69 P:27 SP:F9 CYC:231 SL:9 +F8C5 60 RTS A:80 X:00 Y:69 P:27 SP:F9 CYC:237 SL:9 +D113 C8 INY A:80 X:00 Y:69 P:27 SP:FB CYC:255 SL:9 +D114 48 PHA A:80 X:00 Y:6A P:25 SP:FB CYC:261 SL:9 +D115 A9 81 LDA #$81 A:80 X:00 Y:6A P:25 SP:FA CYC:270 SL:9 +D117 8D 00 02 STA $0200 = 80 A:81 X:00 Y:6A P:A5 SP:FA CYC:276 SL:9 +D11A 68 PLA A:81 X:00 Y:6A P:A5 SP:FA CYC:288 SL:9 +D11B C1 80 CMP ($80,X) @ 80 = 0200 = 81 A:80 X:00 Y:6A P:A5 SP:FB CYC:300 SL:9 +D11D 20 C9 F8 JSR $F8C9 A:80 X:00 Y:6A P:A4 SP:FB CYC:318 SL:9 +F8C9 B0 05 BCS $F8D0 A:80 X:00 Y:6A P:A4 SP:F9 CYC:336 SL:9 +F8CB F0 03 BEQ $F8D0 A:80 X:00 Y:6A P:A4 SP:F9 CYC: 1 SL:10 +F8CD 10 01 BPL $F8D0 A:80 X:00 Y:6A P:A4 SP:F9 CYC: 7 SL:10 +F8CF 60 RTS A:80 X:00 Y:6A P:A4 SP:F9 CYC: 13 SL:10 +D120 C8 INY A:80 X:00 Y:6A P:A4 SP:FB CYC: 31 SL:10 +D121 48 PHA A:80 X:00 Y:6B P:24 SP:FB CYC: 37 SL:10 +D122 A9 7F LDA #$7F A:80 X:00 Y:6B P:24 SP:FA CYC: 46 SL:10 +D124 8D 00 02 STA $0200 = 81 A:7F X:00 Y:6B P:24 SP:FA CYC: 52 SL:10 +D127 68 PLA A:7F X:00 Y:6B P:24 SP:FA CYC: 64 SL:10 +D128 C1 80 CMP ($80,X) @ 80 = 0200 = 7F A:80 X:00 Y:6B P:A4 SP:FB CYC: 76 SL:10 +D12A 20 D3 F8 JSR $F8D3 A:80 X:00 Y:6B P:25 SP:FB CYC: 94 SL:10 +F8D3 90 05 BCC $F8DA A:80 X:00 Y:6B P:25 SP:F9 CYC:112 SL:10 +F8D5 F0 03 BEQ $F8DA A:80 X:00 Y:6B P:25 SP:F9 CYC:118 SL:10 +F8D7 30 01 BMI $F8DA A:80 X:00 Y:6B P:25 SP:F9 CYC:124 SL:10 +F8D9 60 RTS A:80 X:00 Y:6B P:25 SP:F9 CYC:130 SL:10 +D12D C8 INY A:80 X:00 Y:6B P:25 SP:FB CYC:148 SL:10 +D12E A9 40 LDA #$40 A:80 X:00 Y:6C P:25 SP:FB CYC:154 SL:10 +D130 8D 00 02 STA $0200 = 7F A:40 X:00 Y:6C P:25 SP:FB CYC:160 SL:10 +D133 20 31 F9 JSR $F931 A:40 X:00 Y:6C P:25 SP:FB CYC:172 SL:10 +F931 24 01 BIT $01 = FF A:40 X:00 Y:6C P:25 SP:F9 CYC:190 SL:10 +F933 A9 40 LDA #$40 A:40 X:00 Y:6C P:E5 SP:F9 CYC:199 SL:10 +F935 38 SEC A:40 X:00 Y:6C P:65 SP:F9 CYC:205 SL:10 +F936 60 RTS A:40 X:00 Y:6C P:65 SP:F9 CYC:211 SL:10 +D136 E1 80 SBC ($80,X) @ 80 = 0200 = 40 A:40 X:00 Y:6C P:65 SP:FB CYC:229 SL:10 +D138 20 37 F9 JSR $F937 A:00 X:00 Y:6C P:27 SP:FB CYC:247 SL:10 +F937 30 0B BMI $F944 A:00 X:00 Y:6C P:27 SP:F9 CYC:265 SL:10 +F939 90 09 BCC $F944 A:00 X:00 Y:6C P:27 SP:F9 CYC:271 SL:10 +F93B D0 07 BNE $F944 A:00 X:00 Y:6C P:27 SP:F9 CYC:277 SL:10 +F93D 70 05 BVS $F944 A:00 X:00 Y:6C P:27 SP:F9 CYC:283 SL:10 +F93F C9 00 CMP #$00 A:00 X:00 Y:6C P:27 SP:F9 CYC:289 SL:10 +F941 D0 01 BNE $F944 A:00 X:00 Y:6C P:27 SP:F9 CYC:295 SL:10 +F943 60 RTS A:00 X:00 Y:6C P:27 SP:F9 CYC:301 SL:10 +D13B C8 INY A:00 X:00 Y:6C P:27 SP:FB CYC:319 SL:10 +D13C A9 3F LDA #$3F A:00 X:00 Y:6D P:25 SP:FB CYC:325 SL:10 +D13E 8D 00 02 STA $0200 = 40 A:3F X:00 Y:6D P:25 SP:FB CYC:331 SL:10 +D141 20 47 F9 JSR $F947 A:3F X:00 Y:6D P:25 SP:FB CYC: 2 SL:11 +F947 B8 CLV A:3F X:00 Y:6D P:25 SP:F9 CYC: 20 SL:11 +F948 38 SEC A:3F X:00 Y:6D P:25 SP:F9 CYC: 26 SL:11 +F949 A9 40 LDA #$40 A:3F X:00 Y:6D P:25 SP:F9 CYC: 32 SL:11 +F94B 60 RTS A:40 X:00 Y:6D P:25 SP:F9 CYC: 38 SL:11 +D144 E1 80 SBC ($80,X) @ 80 = 0200 = 3F A:40 X:00 Y:6D P:25 SP:FB CYC: 56 SL:11 +D146 20 4C F9 JSR $F94C A:01 X:00 Y:6D P:25 SP:FB CYC: 74 SL:11 +F94C F0 0B BEQ $F959 A:01 X:00 Y:6D P:25 SP:F9 CYC: 92 SL:11 +F94E 30 09 BMI $F959 A:01 X:00 Y:6D P:25 SP:F9 CYC: 98 SL:11 +F950 90 07 BCC $F959 A:01 X:00 Y:6D P:25 SP:F9 CYC:104 SL:11 +F952 70 05 BVS $F959 A:01 X:00 Y:6D P:25 SP:F9 CYC:110 SL:11 +F954 C9 01 CMP #$01 A:01 X:00 Y:6D P:25 SP:F9 CYC:116 SL:11 +F956 D0 01 BNE $F959 A:01 X:00 Y:6D P:27 SP:F9 CYC:122 SL:11 +F958 60 RTS A:01 X:00 Y:6D P:27 SP:F9 CYC:128 SL:11 +D149 C8 INY A:01 X:00 Y:6D P:27 SP:FB CYC:146 SL:11 +D14A A9 41 LDA #$41 A:01 X:00 Y:6E P:25 SP:FB CYC:152 SL:11 +D14C 8D 00 02 STA $0200 = 3F A:41 X:00 Y:6E P:25 SP:FB CYC:158 SL:11 +D14F 20 5C F9 JSR $F95C A:41 X:00 Y:6E P:25 SP:FB CYC:170 SL:11 +F95C A9 40 LDA #$40 A:41 X:00 Y:6E P:25 SP:F9 CYC:188 SL:11 +F95E 38 SEC A:40 X:00 Y:6E P:25 SP:F9 CYC:194 SL:11 +F95F 24 01 BIT $01 = FF A:40 X:00 Y:6E P:25 SP:F9 CYC:200 SL:11 +F961 60 RTS A:40 X:00 Y:6E P:E5 SP:F9 CYC:209 SL:11 +D152 E1 80 SBC ($80,X) @ 80 = 0200 = 41 A:40 X:00 Y:6E P:E5 SP:FB CYC:227 SL:11 +D154 20 62 F9 JSR $F962 A:FF X:00 Y:6E P:A4 SP:FB CYC:245 SL:11 +F962 B0 0B BCS $F96F A:FF X:00 Y:6E P:A4 SP:F9 CYC:263 SL:11 +F964 F0 09 BEQ $F96F A:FF X:00 Y:6E P:A4 SP:F9 CYC:269 SL:11 +F966 10 07 BPL $F96F A:FF X:00 Y:6E P:A4 SP:F9 CYC:275 SL:11 +F968 70 05 BVS $F96F A:FF X:00 Y:6E P:A4 SP:F9 CYC:281 SL:11 +F96A C9 FF CMP #$FF A:FF X:00 Y:6E P:A4 SP:F9 CYC:287 SL:11 +F96C D0 01 BNE $F96F A:FF X:00 Y:6E P:27 SP:F9 CYC:293 SL:11 +F96E 60 RTS A:FF X:00 Y:6E P:27 SP:F9 CYC:299 SL:11 +D157 C8 INY A:FF X:00 Y:6E P:27 SP:FB CYC:317 SL:11 +D158 A9 00 LDA #$00 A:FF X:00 Y:6F P:25 SP:FB CYC:323 SL:11 +D15A 8D 00 02 STA $0200 = 41 A:00 X:00 Y:6F P:27 SP:FB CYC:329 SL:11 +D15D 20 72 F9 JSR $F972 A:00 X:00 Y:6F P:27 SP:FB CYC: 0 SL:12 +F972 18 CLC A:00 X:00 Y:6F P:27 SP:F9 CYC: 18 SL:12 +F973 A9 80 LDA #$80 A:00 X:00 Y:6F P:26 SP:F9 CYC: 24 SL:12 +F975 60 RTS A:80 X:00 Y:6F P:A4 SP:F9 CYC: 30 SL:12 +D160 E1 80 SBC ($80,X) @ 80 = 0200 = 00 A:80 X:00 Y:6F P:A4 SP:FB CYC: 48 SL:12 +D162 20 76 F9 JSR $F976 A:7F X:00 Y:6F P:65 SP:FB CYC: 66 SL:12 +F976 90 05 BCC $F97D A:7F X:00 Y:6F P:65 SP:F9 CYC: 84 SL:12 +F978 C9 7F CMP #$7F A:7F X:00 Y:6F P:65 SP:F9 CYC: 90 SL:12 +F97A D0 01 BNE $F97D A:7F X:00 Y:6F P:67 SP:F9 CYC: 96 SL:12 +F97C 60 RTS A:7F X:00 Y:6F P:67 SP:F9 CYC:102 SL:12 +D165 C8 INY A:7F X:00 Y:6F P:67 SP:FB CYC:120 SL:12 +D166 A9 7F LDA #$7F A:7F X:00 Y:70 P:65 SP:FB CYC:126 SL:12 +D168 8D 00 02 STA $0200 = 00 A:7F X:00 Y:70 P:65 SP:FB CYC:132 SL:12 +D16B 20 80 F9 JSR $F980 A:7F X:00 Y:70 P:65 SP:FB CYC:144 SL:12 +F980 38 SEC A:7F X:00 Y:70 P:65 SP:F9 CYC:162 SL:12 +F981 A9 81 LDA #$81 A:7F X:00 Y:70 P:65 SP:F9 CYC:168 SL:12 +F983 60 RTS A:81 X:00 Y:70 P:E5 SP:F9 CYC:174 SL:12 +D16E E1 80 SBC ($80,X) @ 80 = 0200 = 7F A:81 X:00 Y:70 P:E5 SP:FB CYC:192 SL:12 +D170 20 84 F9 JSR $F984 A:02 X:00 Y:70 P:65 SP:FB CYC:210 SL:12 +F984 50 07 BVC $F98D A:02 X:00 Y:70 P:65 SP:F9 CYC:228 SL:12 +F986 90 05 BCC $F98D A:02 X:00 Y:70 P:65 SP:F9 CYC:234 SL:12 +F988 C9 02 CMP #$02 A:02 X:00 Y:70 P:65 SP:F9 CYC:240 SL:12 +F98A D0 01 BNE $F98D A:02 X:00 Y:70 P:67 SP:F9 CYC:246 SL:12 +F98C 60 RTS A:02 X:00 Y:70 P:67 SP:F9 CYC:252 SL:12 +D173 60 RTS A:02 X:00 Y:70 P:67 SP:FB CYC:270 SL:12 +C612 20 74 D1 JSR $D174 A:02 X:00 Y:70 P:67 SP:FD CYC:288 SL:12 +D174 A9 55 LDA #$55 A:02 X:00 Y:70 P:67 SP:FB CYC:306 SL:12 +D176 85 78 STA $78 = 00 A:55 X:00 Y:70 P:65 SP:FB CYC:312 SL:12 +D178 A9 FF LDA #$FF A:55 X:00 Y:70 P:65 SP:FB CYC:321 SL:12 +D17A 85 01 STA $01 = FF A:FF X:00 Y:70 P:E5 SP:FB CYC:327 SL:12 +D17C 24 01 BIT $01 = FF A:FF X:00 Y:70 P:E5 SP:FB CYC:336 SL:12 +D17E A0 11 LDY #$11 A:FF X:00 Y:70 P:E5 SP:FB CYC: 4 SL:13 +D180 A2 23 LDX #$23 A:FF X:00 Y:11 P:65 SP:FB CYC: 10 SL:13 +D182 A9 00 LDA #$00 A:FF X:23 Y:11 P:65 SP:FB CYC: 16 SL:13 +D184 A5 78 LDA $78 = 55 A:00 X:23 Y:11 P:67 SP:FB CYC: 22 SL:13 +D186 F0 10 BEQ $D198 A:55 X:23 Y:11 P:65 SP:FB CYC: 31 SL:13 +D188 30 0E BMI $D198 A:55 X:23 Y:11 P:65 SP:FB CYC: 37 SL:13 +D18A C9 55 CMP #$55 A:55 X:23 Y:11 P:65 SP:FB CYC: 43 SL:13 +D18C D0 0A BNE $D198 A:55 X:23 Y:11 P:67 SP:FB CYC: 49 SL:13 +D18E C0 11 CPY #$11 A:55 X:23 Y:11 P:67 SP:FB CYC: 55 SL:13 +D190 D0 06 BNE $D198 A:55 X:23 Y:11 P:67 SP:FB CYC: 61 SL:13 +D192 E0 23 CPX #$23 A:55 X:23 Y:11 P:67 SP:FB CYC: 67 SL:13 +D194 50 02 BVC $D198 A:55 X:23 Y:11 P:67 SP:FB CYC: 73 SL:13 +D196 F0 04 BEQ $D19C A:55 X:23 Y:11 P:67 SP:FB CYC: 79 SL:13 +D19C A9 46 LDA #$46 A:55 X:23 Y:11 P:67 SP:FB CYC: 88 SL:13 +D19E 24 01 BIT $01 = FF A:46 X:23 Y:11 P:65 SP:FB CYC: 94 SL:13 +D1A0 85 78 STA $78 = 55 A:46 X:23 Y:11 P:E5 SP:FB CYC:103 SL:13 +D1A2 F0 0A BEQ $D1AE A:46 X:23 Y:11 P:E5 SP:FB CYC:112 SL:13 +D1A4 10 08 BPL $D1AE A:46 X:23 Y:11 P:E5 SP:FB CYC:118 SL:13 +D1A6 50 06 BVC $D1AE A:46 X:23 Y:11 P:E5 SP:FB CYC:124 SL:13 +D1A8 A5 78 LDA $78 = 46 A:46 X:23 Y:11 P:E5 SP:FB CYC:130 SL:13 +D1AA C9 46 CMP #$46 A:46 X:23 Y:11 P:65 SP:FB CYC:139 SL:13 +D1AC F0 04 BEQ $D1B2 A:46 X:23 Y:11 P:67 SP:FB CYC:145 SL:13 +D1B2 A9 55 LDA #$55 A:46 X:23 Y:11 P:67 SP:FB CYC:154 SL:13 +D1B4 85 78 STA $78 = 46 A:55 X:23 Y:11 P:65 SP:FB CYC:160 SL:13 +D1B6 24 01 BIT $01 = FF A:55 X:23 Y:11 P:65 SP:FB CYC:169 SL:13 +D1B8 A9 11 LDA #$11 A:55 X:23 Y:11 P:E5 SP:FB CYC:178 SL:13 +D1BA A2 23 LDX #$23 A:11 X:23 Y:11 P:65 SP:FB CYC:184 SL:13 +D1BC A0 00 LDY #$00 A:11 X:23 Y:11 P:65 SP:FB CYC:190 SL:13 +D1BE A4 78 LDY $78 = 55 A:11 X:23 Y:00 P:67 SP:FB CYC:196 SL:13 +D1C0 F0 10 BEQ $D1D2 A:11 X:23 Y:55 P:65 SP:FB CYC:205 SL:13 +D1C2 30 0E BMI $D1D2 A:11 X:23 Y:55 P:65 SP:FB CYC:211 SL:13 +D1C4 C0 55 CPY #$55 A:11 X:23 Y:55 P:65 SP:FB CYC:217 SL:13 +D1C6 D0 0A BNE $D1D2 A:11 X:23 Y:55 P:67 SP:FB CYC:223 SL:13 +D1C8 C9 11 CMP #$11 A:11 X:23 Y:55 P:67 SP:FB CYC:229 SL:13 +D1CA D0 06 BNE $D1D2 A:11 X:23 Y:55 P:67 SP:FB CYC:235 SL:13 +D1CC E0 23 CPX #$23 A:11 X:23 Y:55 P:67 SP:FB CYC:241 SL:13 +D1CE 50 02 BVC $D1D2 A:11 X:23 Y:55 P:67 SP:FB CYC:247 SL:13 +D1D0 F0 04 BEQ $D1D6 A:11 X:23 Y:55 P:67 SP:FB CYC:253 SL:13 +D1D6 A0 46 LDY #$46 A:11 X:23 Y:55 P:67 SP:FB CYC:262 SL:13 +D1D8 24 01 BIT $01 = FF A:11 X:23 Y:46 P:65 SP:FB CYC:268 SL:13 +D1DA 84 78 STY $78 = 55 A:11 X:23 Y:46 P:E5 SP:FB CYC:277 SL:13 +D1DC F0 0A BEQ $D1E8 A:11 X:23 Y:46 P:E5 SP:FB CYC:286 SL:13 +D1DE 10 08 BPL $D1E8 A:11 X:23 Y:46 P:E5 SP:FB CYC:292 SL:13 +D1E0 50 06 BVC $D1E8 A:11 X:23 Y:46 P:E5 SP:FB CYC:298 SL:13 +D1E2 A4 78 LDY $78 = 46 A:11 X:23 Y:46 P:E5 SP:FB CYC:304 SL:13 +D1E4 C0 46 CPY #$46 A:11 X:23 Y:46 P:65 SP:FB CYC:313 SL:13 +D1E6 F0 04 BEQ $D1EC A:11 X:23 Y:46 P:67 SP:FB CYC:319 SL:13 +D1EC 24 01 BIT $01 = FF A:11 X:23 Y:46 P:67 SP:FB CYC:328 SL:13 +D1EE A9 55 LDA #$55 A:11 X:23 Y:46 P:E5 SP:FB CYC:337 SL:13 +D1F0 85 78 STA $78 = 46 A:55 X:23 Y:46 P:65 SP:FB CYC: 2 SL:14 +D1F2 A0 11 LDY #$11 A:55 X:23 Y:46 P:65 SP:FB CYC: 11 SL:14 +D1F4 A9 23 LDA #$23 A:55 X:23 Y:11 P:65 SP:FB CYC: 17 SL:14 +D1F6 A2 00 LDX #$00 A:23 X:23 Y:11 P:65 SP:FB CYC: 23 SL:14 +D1F8 A6 78 LDX $78 = 55 A:23 X:00 Y:11 P:67 SP:FB CYC: 29 SL:14 +D1FA F0 10 BEQ $D20C A:23 X:55 Y:11 P:65 SP:FB CYC: 38 SL:14 +D1FC 30 0E BMI $D20C A:23 X:55 Y:11 P:65 SP:FB CYC: 44 SL:14 +D1FE E0 55 CPX #$55 A:23 X:55 Y:11 P:65 SP:FB CYC: 50 SL:14 +D200 D0 0A BNE $D20C A:23 X:55 Y:11 P:67 SP:FB CYC: 56 SL:14 +D202 C0 11 CPY #$11 A:23 X:55 Y:11 P:67 SP:FB CYC: 62 SL:14 +D204 D0 06 BNE $D20C A:23 X:55 Y:11 P:67 SP:FB CYC: 68 SL:14 +D206 C9 23 CMP #$23 A:23 X:55 Y:11 P:67 SP:FB CYC: 74 SL:14 +D208 50 02 BVC $D20C A:23 X:55 Y:11 P:67 SP:FB CYC: 80 SL:14 +D20A F0 04 BEQ $D210 A:23 X:55 Y:11 P:67 SP:FB CYC: 86 SL:14 +D210 A2 46 LDX #$46 A:23 X:55 Y:11 P:67 SP:FB CYC: 95 SL:14 +D212 24 01 BIT $01 = FF A:23 X:46 Y:11 P:65 SP:FB CYC:101 SL:14 +D214 86 78 STX $78 = 55 A:23 X:46 Y:11 P:E5 SP:FB CYC:110 SL:14 +D216 F0 0A BEQ $D222 A:23 X:46 Y:11 P:E5 SP:FB CYC:119 SL:14 +D218 10 08 BPL $D222 A:23 X:46 Y:11 P:E5 SP:FB CYC:125 SL:14 +D21A 50 06 BVC $D222 A:23 X:46 Y:11 P:E5 SP:FB CYC:131 SL:14 +D21C A6 78 LDX $78 = 46 A:23 X:46 Y:11 P:E5 SP:FB CYC:137 SL:14 +D21E E0 46 CPX #$46 A:23 X:46 Y:11 P:65 SP:FB CYC:146 SL:14 +D220 F0 04 BEQ $D226 A:23 X:46 Y:11 P:67 SP:FB CYC:152 SL:14 +D226 A9 C0 LDA #$C0 A:23 X:46 Y:11 P:67 SP:FB CYC:161 SL:14 +D228 85 78 STA $78 = 46 A:C0 X:46 Y:11 P:E5 SP:FB CYC:167 SL:14 +D22A A2 33 LDX #$33 A:C0 X:46 Y:11 P:E5 SP:FB CYC:176 SL:14 +D22C A0 88 LDY #$88 A:C0 X:33 Y:11 P:65 SP:FB CYC:182 SL:14 +D22E A9 05 LDA #$05 A:C0 X:33 Y:88 P:E5 SP:FB CYC:188 SL:14 +D230 24 78 BIT $78 = C0 A:05 X:33 Y:88 P:65 SP:FB CYC:194 SL:14 +D232 10 10 BPL $D244 A:05 X:33 Y:88 P:E7 SP:FB CYC:203 SL:14 +D234 50 0E BVC $D244 A:05 X:33 Y:88 P:E7 SP:FB CYC:209 SL:14 +D236 D0 0C BNE $D244 A:05 X:33 Y:88 P:E7 SP:FB CYC:215 SL:14 +D238 C9 05 CMP #$05 A:05 X:33 Y:88 P:E7 SP:FB CYC:221 SL:14 +D23A D0 08 BNE $D244 A:05 X:33 Y:88 P:67 SP:FB CYC:227 SL:14 +D23C E0 33 CPX #$33 A:05 X:33 Y:88 P:67 SP:FB CYC:233 SL:14 +D23E D0 04 BNE $D244 A:05 X:33 Y:88 P:67 SP:FB CYC:239 SL:14 +D240 C0 88 CPY #$88 A:05 X:33 Y:88 P:67 SP:FB CYC:245 SL:14 +D242 F0 04 BEQ $D248 A:05 X:33 Y:88 P:67 SP:FB CYC:251 SL:14 +D248 A9 03 LDA #$03 A:05 X:33 Y:88 P:67 SP:FB CYC:260 SL:14 +D24A 85 78 STA $78 = C0 A:03 X:33 Y:88 P:65 SP:FB CYC:266 SL:14 +D24C A9 01 LDA #$01 A:03 X:33 Y:88 P:65 SP:FB CYC:275 SL:14 +D24E 24 78 BIT $78 = 03 A:01 X:33 Y:88 P:65 SP:FB CYC:281 SL:14 +D250 30 08 BMI $D25A A:01 X:33 Y:88 P:25 SP:FB CYC:290 SL:14 +D252 70 06 BVS $D25A A:01 X:33 Y:88 P:25 SP:FB CYC:296 SL:14 +D254 F0 04 BEQ $D25A A:01 X:33 Y:88 P:25 SP:FB CYC:302 SL:14 +D256 C9 01 CMP #$01 A:01 X:33 Y:88 P:25 SP:FB CYC:308 SL:14 +D258 F0 04 BEQ $D25E A:01 X:33 Y:88 P:27 SP:FB CYC:314 SL:14 +D25E A0 7E LDY #$7E A:01 X:33 Y:88 P:27 SP:FB CYC:323 SL:14 +D260 A9 AA LDA #$AA A:01 X:33 Y:7E P:25 SP:FB CYC:329 SL:14 +D262 85 78 STA $78 = 03 A:AA X:33 Y:7E P:A5 SP:FB CYC:335 SL:14 +D264 20 B6 F7 JSR $F7B6 A:AA X:33 Y:7E P:A5 SP:FB CYC: 3 SL:15 +F7B6 18 CLC A:AA X:33 Y:7E P:A5 SP:F9 CYC: 21 SL:15 +F7B7 A9 FF LDA #$FF A:AA X:33 Y:7E P:A4 SP:F9 CYC: 27 SL:15 +F7B9 85 01 STA $01 = FF A:FF X:33 Y:7E P:A4 SP:F9 CYC: 33 SL:15 +F7BB 24 01 BIT $01 = FF A:FF X:33 Y:7E P:A4 SP:F9 CYC: 42 SL:15 +F7BD A9 55 LDA #$55 A:FF X:33 Y:7E P:E4 SP:F9 CYC: 51 SL:15 +F7BF 60 RTS A:55 X:33 Y:7E P:64 SP:F9 CYC: 57 SL:15 +D267 05 78 ORA $78 = AA A:55 X:33 Y:7E P:64 SP:FB CYC: 75 SL:15 +D269 20 C0 F7 JSR $F7C0 A:FF X:33 Y:7E P:E4 SP:FB CYC: 84 SL:15 +F7C0 B0 09 BCS $F7CB A:FF X:33 Y:7E P:E4 SP:F9 CYC:102 SL:15 +F7C2 10 07 BPL $F7CB A:FF X:33 Y:7E P:E4 SP:F9 CYC:108 SL:15 +F7C4 C9 FF CMP #$FF A:FF X:33 Y:7E P:E4 SP:F9 CYC:114 SL:15 +F7C6 D0 03 BNE $F7CB A:FF X:33 Y:7E P:67 SP:F9 CYC:120 SL:15 +F7C8 50 01 BVC $F7CB A:FF X:33 Y:7E P:67 SP:F9 CYC:126 SL:15 +F7CA 60 RTS A:FF X:33 Y:7E P:67 SP:F9 CYC:132 SL:15 +D26C C8 INY A:FF X:33 Y:7E P:67 SP:FB CYC:150 SL:15 +D26D A9 00 LDA #$00 A:FF X:33 Y:7F P:65 SP:FB CYC:156 SL:15 +D26F 85 78 STA $78 = AA A:00 X:33 Y:7F P:67 SP:FB CYC:162 SL:15 +D271 20 CE F7 JSR $F7CE A:00 X:33 Y:7F P:67 SP:FB CYC:171 SL:15 +F7CE 38 SEC A:00 X:33 Y:7F P:67 SP:F9 CYC:189 SL:15 +F7CF B8 CLV A:00 X:33 Y:7F P:67 SP:F9 CYC:195 SL:15 +F7D0 A9 00 LDA #$00 A:00 X:33 Y:7F P:27 SP:F9 CYC:201 SL:15 +F7D2 60 RTS A:00 X:33 Y:7F P:27 SP:F9 CYC:207 SL:15 +D274 05 78 ORA $78 = 00 A:00 X:33 Y:7F P:27 SP:FB CYC:225 SL:15 +D276 20 D3 F7 JSR $F7D3 A:00 X:33 Y:7F P:27 SP:FB CYC:234 SL:15 +F7D3 D0 07 BNE $F7DC A:00 X:33 Y:7F P:27 SP:F9 CYC:252 SL:15 +F7D5 70 05 BVS $F7DC A:00 X:33 Y:7F P:27 SP:F9 CYC:258 SL:15 +F7D7 90 03 BCC $F7DC A:00 X:33 Y:7F P:27 SP:F9 CYC:264 SL:15 +F7D9 30 01 BMI $F7DC A:00 X:33 Y:7F P:27 SP:F9 CYC:270 SL:15 +F7DB 60 RTS A:00 X:33 Y:7F P:27 SP:F9 CYC:276 SL:15 +D279 C8 INY A:00 X:33 Y:7F P:27 SP:FB CYC:294 SL:15 +D27A A9 AA LDA #$AA A:00 X:33 Y:80 P:A5 SP:FB CYC:300 SL:15 +D27C 85 78 STA $78 = 00 A:AA X:33 Y:80 P:A5 SP:FB CYC:306 SL:15 +D27E 20 DF F7 JSR $F7DF A:AA X:33 Y:80 P:A5 SP:FB CYC:315 SL:15 +F7DF 18 CLC A:AA X:33 Y:80 P:A5 SP:F9 CYC:333 SL:15 +F7E0 24 01 BIT $01 = FF A:AA X:33 Y:80 P:A4 SP:F9 CYC:339 SL:15 +F7E2 A9 55 LDA #$55 A:AA X:33 Y:80 P:E4 SP:F9 CYC: 7 SL:16 +F7E4 60 RTS A:55 X:33 Y:80 P:64 SP:F9 CYC: 13 SL:16 +D281 25 78 AND $78 = AA A:55 X:33 Y:80 P:64 SP:FB CYC: 31 SL:16 +D283 20 E5 F7 JSR $F7E5 A:00 X:33 Y:80 P:66 SP:FB CYC: 40 SL:16 +F7E5 D0 07 BNE $F7EE A:00 X:33 Y:80 P:66 SP:F9 CYC: 58 SL:16 +F7E7 50 05 BVC $F7EE A:00 X:33 Y:80 P:66 SP:F9 CYC: 64 SL:16 +F7E9 B0 03 BCS $F7EE A:00 X:33 Y:80 P:66 SP:F9 CYC: 70 SL:16 +F7EB 30 01 BMI $F7EE A:00 X:33 Y:80 P:66 SP:F9 CYC: 76 SL:16 +F7ED 60 RTS A:00 X:33 Y:80 P:66 SP:F9 CYC: 82 SL:16 +D286 C8 INY A:00 X:33 Y:80 P:66 SP:FB CYC:100 SL:16 +D287 A9 EF LDA #$EF A:00 X:33 Y:81 P:E4 SP:FB CYC:106 SL:16 +D289 85 78 STA $78 = AA A:EF X:33 Y:81 P:E4 SP:FB CYC:112 SL:16 +D28B 20 F1 F7 JSR $F7F1 A:EF X:33 Y:81 P:E4 SP:FB CYC:121 SL:16 +F7F1 38 SEC A:EF X:33 Y:81 P:E4 SP:F9 CYC:139 SL:16 +F7F2 B8 CLV A:EF X:33 Y:81 P:E5 SP:F9 CYC:145 SL:16 +F7F3 A9 F8 LDA #$F8 A:EF X:33 Y:81 P:A5 SP:F9 CYC:151 SL:16 +F7F5 60 RTS A:F8 X:33 Y:81 P:A5 SP:F9 CYC:157 SL:16 +D28E 25 78 AND $78 = EF A:F8 X:33 Y:81 P:A5 SP:FB CYC:175 SL:16 +D290 20 F6 F7 JSR $F7F6 A:E8 X:33 Y:81 P:A5 SP:FB CYC:184 SL:16 +F7F6 90 09 BCC $F801 A:E8 X:33 Y:81 P:A5 SP:F9 CYC:202 SL:16 +F7F8 10 07 BPL $F801 A:E8 X:33 Y:81 P:A5 SP:F9 CYC:208 SL:16 +F7FA C9 E8 CMP #$E8 A:E8 X:33 Y:81 P:A5 SP:F9 CYC:214 SL:16 +F7FC D0 03 BNE $F801 A:E8 X:33 Y:81 P:27 SP:F9 CYC:220 SL:16 +F7FE 70 01 BVS $F801 A:E8 X:33 Y:81 P:27 SP:F9 CYC:226 SL:16 +F800 60 RTS A:E8 X:33 Y:81 P:27 SP:F9 CYC:232 SL:16 +D293 C8 INY A:E8 X:33 Y:81 P:27 SP:FB CYC:250 SL:16 +D294 A9 AA LDA #$AA A:E8 X:33 Y:82 P:A5 SP:FB CYC:256 SL:16 +D296 85 78 STA $78 = EF A:AA X:33 Y:82 P:A5 SP:FB CYC:262 SL:16 +D298 20 04 F8 JSR $F804 A:AA X:33 Y:82 P:A5 SP:FB CYC:271 SL:16 +F804 18 CLC A:AA X:33 Y:82 P:A5 SP:F9 CYC:289 SL:16 +F805 24 01 BIT $01 = FF A:AA X:33 Y:82 P:A4 SP:F9 CYC:295 SL:16 +F807 A9 5F LDA #$5F A:AA X:33 Y:82 P:E4 SP:F9 CYC:304 SL:16 +F809 60 RTS A:5F X:33 Y:82 P:64 SP:F9 CYC:310 SL:16 +D29B 45 78 EOR $78 = AA A:5F X:33 Y:82 P:64 SP:FB CYC:328 SL:16 +D29D 20 0A F8 JSR $F80A A:F5 X:33 Y:82 P:E4 SP:FB CYC:337 SL:16 +F80A B0 09 BCS $F815 A:F5 X:33 Y:82 P:E4 SP:F9 CYC: 14 SL:17 +F80C 10 07 BPL $F815 A:F5 X:33 Y:82 P:E4 SP:F9 CYC: 20 SL:17 +F80E C9 F5 CMP #$F5 A:F5 X:33 Y:82 P:E4 SP:F9 CYC: 26 SL:17 +F810 D0 03 BNE $F815 A:F5 X:33 Y:82 P:67 SP:F9 CYC: 32 SL:17 +F812 50 01 BVC $F815 A:F5 X:33 Y:82 P:67 SP:F9 CYC: 38 SL:17 +F814 60 RTS A:F5 X:33 Y:82 P:67 SP:F9 CYC: 44 SL:17 +D2A0 C8 INY A:F5 X:33 Y:82 P:67 SP:FB CYC: 62 SL:17 +D2A1 A9 70 LDA #$70 A:F5 X:33 Y:83 P:E5 SP:FB CYC: 68 SL:17 +D2A3 85 78 STA $78 = AA A:70 X:33 Y:83 P:65 SP:FB CYC: 74 SL:17 +D2A5 20 18 F8 JSR $F818 A:70 X:33 Y:83 P:65 SP:FB CYC: 83 SL:17 +F818 38 SEC A:70 X:33 Y:83 P:65 SP:F9 CYC:101 SL:17 +F819 B8 CLV A:70 X:33 Y:83 P:65 SP:F9 CYC:107 SL:17 +F81A A9 70 LDA #$70 A:70 X:33 Y:83 P:25 SP:F9 CYC:113 SL:17 +F81C 60 RTS A:70 X:33 Y:83 P:25 SP:F9 CYC:119 SL:17 +D2A8 45 78 EOR $78 = 70 A:70 X:33 Y:83 P:25 SP:FB CYC:137 SL:17 +D2AA 20 1D F8 JSR $F81D A:00 X:33 Y:83 P:27 SP:FB CYC:146 SL:17 +F81D D0 07 BNE $F826 A:00 X:33 Y:83 P:27 SP:F9 CYC:164 SL:17 +F81F 70 05 BVS $F826 A:00 X:33 Y:83 P:27 SP:F9 CYC:170 SL:17 +F821 90 03 BCC $F826 A:00 X:33 Y:83 P:27 SP:F9 CYC:176 SL:17 +F823 30 01 BMI $F826 A:00 X:33 Y:83 P:27 SP:F9 CYC:182 SL:17 +F825 60 RTS A:00 X:33 Y:83 P:27 SP:F9 CYC:188 SL:17 +D2AD C8 INY A:00 X:33 Y:83 P:27 SP:FB CYC:206 SL:17 +D2AE A9 69 LDA #$69 A:00 X:33 Y:84 P:A5 SP:FB CYC:212 SL:17 +D2B0 85 78 STA $78 = 70 A:69 X:33 Y:84 P:25 SP:FB CYC:218 SL:17 +D2B2 20 29 F8 JSR $F829 A:69 X:33 Y:84 P:25 SP:FB CYC:227 SL:17 +F829 18 CLC A:69 X:33 Y:84 P:25 SP:F9 CYC:245 SL:17 +F82A 24 01 BIT $01 = FF A:69 X:33 Y:84 P:24 SP:F9 CYC:251 SL:17 +F82C A9 00 LDA #$00 A:69 X:33 Y:84 P:E4 SP:F9 CYC:260 SL:17 +F82E 60 RTS A:00 X:33 Y:84 P:66 SP:F9 CYC:266 SL:17 +D2B5 65 78 ADC $78 = 69 A:00 X:33 Y:84 P:66 SP:FB CYC:284 SL:17 +D2B7 20 2F F8 JSR $F82F A:69 X:33 Y:84 P:24 SP:FB CYC:293 SL:17 +F82F 30 09 BMI $F83A A:69 X:33 Y:84 P:24 SP:F9 CYC:311 SL:17 +F831 B0 07 BCS $F83A A:69 X:33 Y:84 P:24 SP:F9 CYC:317 SL:17 +F833 C9 69 CMP #$69 A:69 X:33 Y:84 P:24 SP:F9 CYC:323 SL:17 +F835 D0 03 BNE $F83A A:69 X:33 Y:84 P:27 SP:F9 CYC:329 SL:17 +F837 70 01 BVS $F83A A:69 X:33 Y:84 P:27 SP:F9 CYC:335 SL:17 +F839 60 RTS A:69 X:33 Y:84 P:27 SP:F9 CYC: 0 SL:18 +D2BA C8 INY A:69 X:33 Y:84 P:27 SP:FB CYC: 18 SL:18 +D2BB 20 3D F8 JSR $F83D A:69 X:33 Y:85 P:A5 SP:FB CYC: 24 SL:18 +F83D 38 SEC A:69 X:33 Y:85 P:A5 SP:F9 CYC: 42 SL:18 +F83E 24 01 BIT $01 = FF A:69 X:33 Y:85 P:A5 SP:F9 CYC: 48 SL:18 +F840 A9 00 LDA #$00 A:69 X:33 Y:85 P:E5 SP:F9 CYC: 57 SL:18 +F842 60 RTS A:00 X:33 Y:85 P:67 SP:F9 CYC: 63 SL:18 +D2BE 65 78 ADC $78 = 69 A:00 X:33 Y:85 P:67 SP:FB CYC: 81 SL:18 +D2C0 20 43 F8 JSR $F843 A:6A X:33 Y:85 P:24 SP:FB CYC: 90 SL:18 +F843 30 09 BMI $F84E A:6A X:33 Y:85 P:24 SP:F9 CYC:108 SL:18 +F845 B0 07 BCS $F84E A:6A X:33 Y:85 P:24 SP:F9 CYC:114 SL:18 +F847 C9 6A CMP #$6A A:6A X:33 Y:85 P:24 SP:F9 CYC:120 SL:18 +F849 D0 03 BNE $F84E A:6A X:33 Y:85 P:27 SP:F9 CYC:126 SL:18 +F84B 70 01 BVS $F84E A:6A X:33 Y:85 P:27 SP:F9 CYC:132 SL:18 +F84D 60 RTS A:6A X:33 Y:85 P:27 SP:F9 CYC:138 SL:18 +D2C3 C8 INY A:6A X:33 Y:85 P:27 SP:FB CYC:156 SL:18 +D2C4 A9 7F LDA #$7F A:6A X:33 Y:86 P:A5 SP:FB CYC:162 SL:18 +D2C6 85 78 STA $78 = 69 A:7F X:33 Y:86 P:25 SP:FB CYC:168 SL:18 +D2C8 20 51 F8 JSR $F851 A:7F X:33 Y:86 P:25 SP:FB CYC:177 SL:18 +F851 38 SEC A:7F X:33 Y:86 P:25 SP:F9 CYC:195 SL:18 +F852 B8 CLV A:7F X:33 Y:86 P:25 SP:F9 CYC:201 SL:18 +F853 A9 7F LDA #$7F A:7F X:33 Y:86 P:25 SP:F9 CYC:207 SL:18 +F855 60 RTS A:7F X:33 Y:86 P:25 SP:F9 CYC:213 SL:18 +D2CB 65 78 ADC $78 = 7F A:7F X:33 Y:86 P:25 SP:FB CYC:231 SL:18 +D2CD 20 56 F8 JSR $F856 A:FF X:33 Y:86 P:E4 SP:FB CYC:240 SL:18 +F856 10 09 BPL $F861 A:FF X:33 Y:86 P:E4 SP:F9 CYC:258 SL:18 +F858 B0 07 BCS $F861 A:FF X:33 Y:86 P:E4 SP:F9 CYC:264 SL:18 +F85A C9 FF CMP #$FF A:FF X:33 Y:86 P:E4 SP:F9 CYC:270 SL:18 +F85C D0 03 BNE $F861 A:FF X:33 Y:86 P:67 SP:F9 CYC:276 SL:18 +F85E 50 01 BVC $F861 A:FF X:33 Y:86 P:67 SP:F9 CYC:282 SL:18 +F860 60 RTS A:FF X:33 Y:86 P:67 SP:F9 CYC:288 SL:18 +D2D0 C8 INY A:FF X:33 Y:86 P:67 SP:FB CYC:306 SL:18 +D2D1 A9 80 LDA #$80 A:FF X:33 Y:87 P:E5 SP:FB CYC:312 SL:18 +D2D3 85 78 STA $78 = 7F A:80 X:33 Y:87 P:E5 SP:FB CYC:318 SL:18 +D2D5 20 64 F8 JSR $F864 A:80 X:33 Y:87 P:E5 SP:FB CYC:327 SL:18 +F864 18 CLC A:80 X:33 Y:87 P:E5 SP:F9 CYC: 4 SL:19 +F865 24 01 BIT $01 = FF A:80 X:33 Y:87 P:E4 SP:F9 CYC: 10 SL:19 +F867 A9 7F LDA #$7F A:80 X:33 Y:87 P:E4 SP:F9 CYC: 19 SL:19 +F869 60 RTS A:7F X:33 Y:87 P:64 SP:F9 CYC: 25 SL:19 +D2D8 65 78 ADC $78 = 80 A:7F X:33 Y:87 P:64 SP:FB CYC: 43 SL:19 +D2DA 20 6A F8 JSR $F86A A:FF X:33 Y:87 P:A4 SP:FB CYC: 52 SL:19 +F86A 10 09 BPL $F875 A:FF X:33 Y:87 P:A4 SP:F9 CYC: 70 SL:19 +F86C B0 07 BCS $F875 A:FF X:33 Y:87 P:A4 SP:F9 CYC: 76 SL:19 +F86E C9 FF CMP #$FF A:FF X:33 Y:87 P:A4 SP:F9 CYC: 82 SL:19 +F870 D0 03 BNE $F875 A:FF X:33 Y:87 P:27 SP:F9 CYC: 88 SL:19 +F872 70 01 BVS $F875 A:FF X:33 Y:87 P:27 SP:F9 CYC: 94 SL:19 +F874 60 RTS A:FF X:33 Y:87 P:27 SP:F9 CYC:100 SL:19 +D2DD C8 INY A:FF X:33 Y:87 P:27 SP:FB CYC:118 SL:19 +D2DE 20 78 F8 JSR $F878 A:FF X:33 Y:88 P:A5 SP:FB CYC:124 SL:19 +F878 38 SEC A:FF X:33 Y:88 P:A5 SP:F9 CYC:142 SL:19 +F879 B8 CLV A:FF X:33 Y:88 P:A5 SP:F9 CYC:148 SL:19 +F87A A9 7F LDA #$7F A:FF X:33 Y:88 P:A5 SP:F9 CYC:154 SL:19 +F87C 60 RTS A:7F X:33 Y:88 P:25 SP:F9 CYC:160 SL:19 +D2E1 65 78 ADC $78 = 80 A:7F X:33 Y:88 P:25 SP:FB CYC:178 SL:19 +D2E3 20 7D F8 JSR $F87D A:00 X:33 Y:88 P:27 SP:FB CYC:187 SL:19 +F87D D0 07 BNE $F886 A:00 X:33 Y:88 P:27 SP:F9 CYC:205 SL:19 +F87F 30 05 BMI $F886 A:00 X:33 Y:88 P:27 SP:F9 CYC:211 SL:19 +F881 70 03 BVS $F886 A:00 X:33 Y:88 P:27 SP:F9 CYC:217 SL:19 +F883 90 01 BCC $F886 A:00 X:33 Y:88 P:27 SP:F9 CYC:223 SL:19 +F885 60 RTS A:00 X:33 Y:88 P:27 SP:F9 CYC:229 SL:19 +D2E6 C8 INY A:00 X:33 Y:88 P:27 SP:FB CYC:247 SL:19 +D2E7 A9 40 LDA #$40 A:00 X:33 Y:89 P:A5 SP:FB CYC:253 SL:19 +D2E9 85 78 STA $78 = 80 A:40 X:33 Y:89 P:25 SP:FB CYC:259 SL:19 +D2EB 20 89 F8 JSR $F889 A:40 X:33 Y:89 P:25 SP:FB CYC:268 SL:19 +F889 24 01 BIT $01 = FF A:40 X:33 Y:89 P:25 SP:F9 CYC:286 SL:19 +F88B A9 40 LDA #$40 A:40 X:33 Y:89 P:E5 SP:F9 CYC:295 SL:19 +F88D 60 RTS A:40 X:33 Y:89 P:65 SP:F9 CYC:301 SL:19 +D2EE C5 78 CMP $78 = 40 A:40 X:33 Y:89 P:65 SP:FB CYC:319 SL:19 +D2F0 20 8E F8 JSR $F88E A:40 X:33 Y:89 P:67 SP:FB CYC:328 SL:19 +F88E 30 07 BMI $F897 A:40 X:33 Y:89 P:67 SP:F9 CYC: 5 SL:20 +F890 90 05 BCC $F897 A:40 X:33 Y:89 P:67 SP:F9 CYC: 11 SL:20 +F892 D0 03 BNE $F897 A:40 X:33 Y:89 P:67 SP:F9 CYC: 17 SL:20 +F894 50 01 BVC $F897 A:40 X:33 Y:89 P:67 SP:F9 CYC: 23 SL:20 +F896 60 RTS A:40 X:33 Y:89 P:67 SP:F9 CYC: 29 SL:20 +D2F3 C8 INY A:40 X:33 Y:89 P:67 SP:FB CYC: 47 SL:20 +D2F4 48 PHA A:40 X:33 Y:8A P:E5 SP:FB CYC: 53 SL:20 +D2F5 A9 3F LDA #$3F A:40 X:33 Y:8A P:E5 SP:FA CYC: 62 SL:20 +D2F7 85 78 STA $78 = 40 A:3F X:33 Y:8A P:65 SP:FA CYC: 68 SL:20 +D2F9 68 PLA A:3F X:33 Y:8A P:65 SP:FA CYC: 77 SL:20 +D2FA 20 9A F8 JSR $F89A A:40 X:33 Y:8A P:65 SP:FB CYC: 89 SL:20 +F89A B8 CLV A:40 X:33 Y:8A P:65 SP:F9 CYC:107 SL:20 +F89B 60 RTS A:40 X:33 Y:8A P:25 SP:F9 CYC:113 SL:20 +D2FD C5 78 CMP $78 = 3F A:40 X:33 Y:8A P:25 SP:FB CYC:131 SL:20 +D2FF 20 9C F8 JSR $F89C A:40 X:33 Y:8A P:25 SP:FB CYC:140 SL:20 +F89C F0 07 BEQ $F8A5 A:40 X:33 Y:8A P:25 SP:F9 CYC:158 SL:20 +F89E 30 05 BMI $F8A5 A:40 X:33 Y:8A P:25 SP:F9 CYC:164 SL:20 +F8A0 90 03 BCC $F8A5 A:40 X:33 Y:8A P:25 SP:F9 CYC:170 SL:20 +F8A2 70 01 BVS $F8A5 A:40 X:33 Y:8A P:25 SP:F9 CYC:176 SL:20 +F8A4 60 RTS A:40 X:33 Y:8A P:25 SP:F9 CYC:182 SL:20 +D302 C8 INY A:40 X:33 Y:8A P:25 SP:FB CYC:200 SL:20 +D303 48 PHA A:40 X:33 Y:8B P:A5 SP:FB CYC:206 SL:20 +D304 A9 41 LDA #$41 A:40 X:33 Y:8B P:A5 SP:FA CYC:215 SL:20 +D306 85 78 STA $78 = 3F A:41 X:33 Y:8B P:25 SP:FA CYC:221 SL:20 +D308 68 PLA A:41 X:33 Y:8B P:25 SP:FA CYC:230 SL:20 +D309 C5 78 CMP $78 = 41 A:40 X:33 Y:8B P:25 SP:FB CYC:242 SL:20 +D30B 20 A8 F8 JSR $F8A8 A:40 X:33 Y:8B P:A4 SP:FB CYC:251 SL:20 +F8A8 F0 05 BEQ $F8AF A:40 X:33 Y:8B P:A4 SP:F9 CYC:269 SL:20 +F8AA 10 03 BPL $F8AF A:40 X:33 Y:8B P:A4 SP:F9 CYC:275 SL:20 +F8AC 10 01 BPL $F8AF A:40 X:33 Y:8B P:A4 SP:F9 CYC:281 SL:20 +F8AE 60 RTS A:40 X:33 Y:8B P:A4 SP:F9 CYC:287 SL:20 +D30E C8 INY A:40 X:33 Y:8B P:A4 SP:FB CYC:305 SL:20 +D30F 48 PHA A:40 X:33 Y:8C P:A4 SP:FB CYC:311 SL:20 +D310 A9 00 LDA #$00 A:40 X:33 Y:8C P:A4 SP:FA CYC:320 SL:20 +D312 85 78 STA $78 = 41 A:00 X:33 Y:8C P:26 SP:FA CYC:326 SL:20 +D314 68 PLA A:00 X:33 Y:8C P:26 SP:FA CYC:335 SL:20 +D315 20 B2 F8 JSR $F8B2 A:40 X:33 Y:8C P:24 SP:FB CYC: 6 SL:21 +F8B2 A9 80 LDA #$80 A:40 X:33 Y:8C P:24 SP:F9 CYC: 24 SL:21 +F8B4 60 RTS A:80 X:33 Y:8C P:A4 SP:F9 CYC: 30 SL:21 +D318 C5 78 CMP $78 = 00 A:80 X:33 Y:8C P:A4 SP:FB CYC: 48 SL:21 +D31A 20 B5 F8 JSR $F8B5 A:80 X:33 Y:8C P:A5 SP:FB CYC: 57 SL:21 +F8B5 F0 05 BEQ $F8BC A:80 X:33 Y:8C P:A5 SP:F9 CYC: 75 SL:21 +F8B7 10 03 BPL $F8BC A:80 X:33 Y:8C P:A5 SP:F9 CYC: 81 SL:21 +F8B9 90 01 BCC $F8BC A:80 X:33 Y:8C P:A5 SP:F9 CYC: 87 SL:21 +F8BB 60 RTS A:80 X:33 Y:8C P:A5 SP:F9 CYC: 93 SL:21 +D31D C8 INY A:80 X:33 Y:8C P:A5 SP:FB CYC:111 SL:21 +D31E 48 PHA A:80 X:33 Y:8D P:A5 SP:FB CYC:117 SL:21 +D31F A9 80 LDA #$80 A:80 X:33 Y:8D P:A5 SP:FA CYC:126 SL:21 +D321 85 78 STA $78 = 00 A:80 X:33 Y:8D P:A5 SP:FA CYC:132 SL:21 +D323 68 PLA A:80 X:33 Y:8D P:A5 SP:FA CYC:141 SL:21 +D324 C5 78 CMP $78 = 80 A:80 X:33 Y:8D P:A5 SP:FB CYC:153 SL:21 +D326 20 BF F8 JSR $F8BF A:80 X:33 Y:8D P:27 SP:FB CYC:162 SL:21 +F8BF D0 05 BNE $F8C6 A:80 X:33 Y:8D P:27 SP:F9 CYC:180 SL:21 +F8C1 30 03 BMI $F8C6 A:80 X:33 Y:8D P:27 SP:F9 CYC:186 SL:21 +F8C3 90 01 BCC $F8C6 A:80 X:33 Y:8D P:27 SP:F9 CYC:192 SL:21 +F8C5 60 RTS A:80 X:33 Y:8D P:27 SP:F9 CYC:198 SL:21 +D329 C8 INY A:80 X:33 Y:8D P:27 SP:FB CYC:216 SL:21 +D32A 48 PHA A:80 X:33 Y:8E P:A5 SP:FB CYC:222 SL:21 +D32B A9 81 LDA #$81 A:80 X:33 Y:8E P:A5 SP:FA CYC:231 SL:21 +D32D 85 78 STA $78 = 80 A:81 X:33 Y:8E P:A5 SP:FA CYC:237 SL:21 +D32F 68 PLA A:81 X:33 Y:8E P:A5 SP:FA CYC:246 SL:21 +D330 C5 78 CMP $78 = 81 A:80 X:33 Y:8E P:A5 SP:FB CYC:258 SL:21 +D332 20 C9 F8 JSR $F8C9 A:80 X:33 Y:8E P:A4 SP:FB CYC:267 SL:21 +F8C9 B0 05 BCS $F8D0 A:80 X:33 Y:8E P:A4 SP:F9 CYC:285 SL:21 +F8CB F0 03 BEQ $F8D0 A:80 X:33 Y:8E P:A4 SP:F9 CYC:291 SL:21 +F8CD 10 01 BPL $F8D0 A:80 X:33 Y:8E P:A4 SP:F9 CYC:297 SL:21 +F8CF 60 RTS A:80 X:33 Y:8E P:A4 SP:F9 CYC:303 SL:21 +D335 C8 INY A:80 X:33 Y:8E P:A4 SP:FB CYC:321 SL:21 +D336 48 PHA A:80 X:33 Y:8F P:A4 SP:FB CYC:327 SL:21 +D337 A9 7F LDA #$7F A:80 X:33 Y:8F P:A4 SP:FA CYC:336 SL:21 +D339 85 78 STA $78 = 81 A:7F X:33 Y:8F P:24 SP:FA CYC: 1 SL:22 +D33B 68 PLA A:7F X:33 Y:8F P:24 SP:FA CYC: 10 SL:22 +D33C C5 78 CMP $78 = 7F A:80 X:33 Y:8F P:A4 SP:FB CYC: 22 SL:22 +D33E 20 D3 F8 JSR $F8D3 A:80 X:33 Y:8F P:25 SP:FB CYC: 31 SL:22 +F8D3 90 05 BCC $F8DA A:80 X:33 Y:8F P:25 SP:F9 CYC: 49 SL:22 +F8D5 F0 03 BEQ $F8DA A:80 X:33 Y:8F P:25 SP:F9 CYC: 55 SL:22 +F8D7 30 01 BMI $F8DA A:80 X:33 Y:8F P:25 SP:F9 CYC: 61 SL:22 +F8D9 60 RTS A:80 X:33 Y:8F P:25 SP:F9 CYC: 67 SL:22 +D341 C8 INY A:80 X:33 Y:8F P:25 SP:FB CYC: 85 SL:22 +D342 A9 40 LDA #$40 A:80 X:33 Y:90 P:A5 SP:FB CYC: 91 SL:22 +D344 85 78 STA $78 = 7F A:40 X:33 Y:90 P:25 SP:FB CYC: 97 SL:22 +D346 20 31 F9 JSR $F931 A:40 X:33 Y:90 P:25 SP:FB CYC:106 SL:22 +F931 24 01 BIT $01 = FF A:40 X:33 Y:90 P:25 SP:F9 CYC:124 SL:22 +F933 A9 40 LDA #$40 A:40 X:33 Y:90 P:E5 SP:F9 CYC:133 SL:22 +F935 38 SEC A:40 X:33 Y:90 P:65 SP:F9 CYC:139 SL:22 +F936 60 RTS A:40 X:33 Y:90 P:65 SP:F9 CYC:145 SL:22 +D349 E5 78 SBC $78 = 40 A:40 X:33 Y:90 P:65 SP:FB CYC:163 SL:22 +D34B 20 37 F9 JSR $F937 A:00 X:33 Y:90 P:27 SP:FB CYC:172 SL:22 +F937 30 0B BMI $F944 A:00 X:33 Y:90 P:27 SP:F9 CYC:190 SL:22 +F939 90 09 BCC $F944 A:00 X:33 Y:90 P:27 SP:F9 CYC:196 SL:22 +F93B D0 07 BNE $F944 A:00 X:33 Y:90 P:27 SP:F9 CYC:202 SL:22 +F93D 70 05 BVS $F944 A:00 X:33 Y:90 P:27 SP:F9 CYC:208 SL:22 +F93F C9 00 CMP #$00 A:00 X:33 Y:90 P:27 SP:F9 CYC:214 SL:22 +F941 D0 01 BNE $F944 A:00 X:33 Y:90 P:27 SP:F9 CYC:220 SL:22 +F943 60 RTS A:00 X:33 Y:90 P:27 SP:F9 CYC:226 SL:22 +D34E C8 INY A:00 X:33 Y:90 P:27 SP:FB CYC:244 SL:22 +D34F A9 3F LDA #$3F A:00 X:33 Y:91 P:A5 SP:FB CYC:250 SL:22 +D351 85 78 STA $78 = 40 A:3F X:33 Y:91 P:25 SP:FB CYC:256 SL:22 +D353 20 47 F9 JSR $F947 A:3F X:33 Y:91 P:25 SP:FB CYC:265 SL:22 +F947 B8 CLV A:3F X:33 Y:91 P:25 SP:F9 CYC:283 SL:22 +F948 38 SEC A:3F X:33 Y:91 P:25 SP:F9 CYC:289 SL:22 +F949 A9 40 LDA #$40 A:3F X:33 Y:91 P:25 SP:F9 CYC:295 SL:22 +F94B 60 RTS A:40 X:33 Y:91 P:25 SP:F9 CYC:301 SL:22 +D356 E5 78 SBC $78 = 3F A:40 X:33 Y:91 P:25 SP:FB CYC:319 SL:22 +D358 20 4C F9 JSR $F94C A:01 X:33 Y:91 P:25 SP:FB CYC:328 SL:22 +F94C F0 0B BEQ $F959 A:01 X:33 Y:91 P:25 SP:F9 CYC: 5 SL:23 +F94E 30 09 BMI $F959 A:01 X:33 Y:91 P:25 SP:F9 CYC: 11 SL:23 +F950 90 07 BCC $F959 A:01 X:33 Y:91 P:25 SP:F9 CYC: 17 SL:23 +F952 70 05 BVS $F959 A:01 X:33 Y:91 P:25 SP:F9 CYC: 23 SL:23 +F954 C9 01 CMP #$01 A:01 X:33 Y:91 P:25 SP:F9 CYC: 29 SL:23 +F956 D0 01 BNE $F959 A:01 X:33 Y:91 P:27 SP:F9 CYC: 35 SL:23 +F958 60 RTS A:01 X:33 Y:91 P:27 SP:F9 CYC: 41 SL:23 +D35B C8 INY A:01 X:33 Y:91 P:27 SP:FB CYC: 59 SL:23 +D35C A9 41 LDA #$41 A:01 X:33 Y:92 P:A5 SP:FB CYC: 65 SL:23 +D35E 85 78 STA $78 = 3F A:41 X:33 Y:92 P:25 SP:FB CYC: 71 SL:23 +D360 20 5C F9 JSR $F95C A:41 X:33 Y:92 P:25 SP:FB CYC: 80 SL:23 +F95C A9 40 LDA #$40 A:41 X:33 Y:92 P:25 SP:F9 CYC: 98 SL:23 +F95E 38 SEC A:40 X:33 Y:92 P:25 SP:F9 CYC:104 SL:23 +F95F 24 01 BIT $01 = FF A:40 X:33 Y:92 P:25 SP:F9 CYC:110 SL:23 +F961 60 RTS A:40 X:33 Y:92 P:E5 SP:F9 CYC:119 SL:23 +D363 E5 78 SBC $78 = 41 A:40 X:33 Y:92 P:E5 SP:FB CYC:137 SL:23 +D365 20 62 F9 JSR $F962 A:FF X:33 Y:92 P:A4 SP:FB CYC:146 SL:23 +F962 B0 0B BCS $F96F A:FF X:33 Y:92 P:A4 SP:F9 CYC:164 SL:23 +F964 F0 09 BEQ $F96F A:FF X:33 Y:92 P:A4 SP:F9 CYC:170 SL:23 +F966 10 07 BPL $F96F A:FF X:33 Y:92 P:A4 SP:F9 CYC:176 SL:23 +F968 70 05 BVS $F96F A:FF X:33 Y:92 P:A4 SP:F9 CYC:182 SL:23 +F96A C9 FF CMP #$FF A:FF X:33 Y:92 P:A4 SP:F9 CYC:188 SL:23 +F96C D0 01 BNE $F96F A:FF X:33 Y:92 P:27 SP:F9 CYC:194 SL:23 +F96E 60 RTS A:FF X:33 Y:92 P:27 SP:F9 CYC:200 SL:23 +D368 C8 INY A:FF X:33 Y:92 P:27 SP:FB CYC:218 SL:23 +D369 A9 00 LDA #$00 A:FF X:33 Y:93 P:A5 SP:FB CYC:224 SL:23 +D36B 85 78 STA $78 = 41 A:00 X:33 Y:93 P:27 SP:FB CYC:230 SL:23 +D36D 20 72 F9 JSR $F972 A:00 X:33 Y:93 P:27 SP:FB CYC:239 SL:23 +F972 18 CLC A:00 X:33 Y:93 P:27 SP:F9 CYC:257 SL:23 +F973 A9 80 LDA #$80 A:00 X:33 Y:93 P:26 SP:F9 CYC:263 SL:23 +F975 60 RTS A:80 X:33 Y:93 P:A4 SP:F9 CYC:269 SL:23 +D370 E5 78 SBC $78 = 00 A:80 X:33 Y:93 P:A4 SP:FB CYC:287 SL:23 +D372 20 76 F9 JSR $F976 A:7F X:33 Y:93 P:65 SP:FB CYC:296 SL:23 +F976 90 05 BCC $F97D A:7F X:33 Y:93 P:65 SP:F9 CYC:314 SL:23 +F978 C9 7F CMP #$7F A:7F X:33 Y:93 P:65 SP:F9 CYC:320 SL:23 +F97A D0 01 BNE $F97D A:7F X:33 Y:93 P:67 SP:F9 CYC:326 SL:23 +F97C 60 RTS A:7F X:33 Y:93 P:67 SP:F9 CYC:332 SL:23 +D375 C8 INY A:7F X:33 Y:93 P:67 SP:FB CYC: 9 SL:24 +D376 A9 7F LDA #$7F A:7F X:33 Y:94 P:E5 SP:FB CYC: 15 SL:24 +D378 85 78 STA $78 = 00 A:7F X:33 Y:94 P:65 SP:FB CYC: 21 SL:24 +D37A 20 80 F9 JSR $F980 A:7F X:33 Y:94 P:65 SP:FB CYC: 30 SL:24 +F980 38 SEC A:7F X:33 Y:94 P:65 SP:F9 CYC: 48 SL:24 +F981 A9 81 LDA #$81 A:7F X:33 Y:94 P:65 SP:F9 CYC: 54 SL:24 +F983 60 RTS A:81 X:33 Y:94 P:E5 SP:F9 CYC: 60 SL:24 +D37D E5 78 SBC $78 = 7F A:81 X:33 Y:94 P:E5 SP:FB CYC: 78 SL:24 +D37F 20 84 F9 JSR $F984 A:02 X:33 Y:94 P:65 SP:FB CYC: 87 SL:24 +F984 50 07 BVC $F98D A:02 X:33 Y:94 P:65 SP:F9 CYC:105 SL:24 +F986 90 05 BCC $F98D A:02 X:33 Y:94 P:65 SP:F9 CYC:111 SL:24 +F988 C9 02 CMP #$02 A:02 X:33 Y:94 P:65 SP:F9 CYC:117 SL:24 +F98A D0 01 BNE $F98D A:02 X:33 Y:94 P:67 SP:F9 CYC:123 SL:24 +F98C 60 RTS A:02 X:33 Y:94 P:67 SP:F9 CYC:129 SL:24 +D382 C8 INY A:02 X:33 Y:94 P:67 SP:FB CYC:147 SL:24 +D383 A9 40 LDA #$40 A:02 X:33 Y:95 P:E5 SP:FB CYC:153 SL:24 +D385 85 78 STA $78 = 7F A:40 X:33 Y:95 P:65 SP:FB CYC:159 SL:24 +D387 20 89 F8 JSR $F889 A:40 X:33 Y:95 P:65 SP:FB CYC:168 SL:24 +F889 24 01 BIT $01 = FF A:40 X:33 Y:95 P:65 SP:F9 CYC:186 SL:24 +F88B A9 40 LDA #$40 A:40 X:33 Y:95 P:E5 SP:F9 CYC:195 SL:24 +F88D 60 RTS A:40 X:33 Y:95 P:65 SP:F9 CYC:201 SL:24 +D38A AA TAX A:40 X:33 Y:95 P:65 SP:FB CYC:219 SL:24 +D38B E4 78 CPX $78 = 40 A:40 X:40 Y:95 P:65 SP:FB CYC:225 SL:24 +D38D 20 8E F8 JSR $F88E A:40 X:40 Y:95 P:67 SP:FB CYC:234 SL:24 +F88E 30 07 BMI $F897 A:40 X:40 Y:95 P:67 SP:F9 CYC:252 SL:24 +F890 90 05 BCC $F897 A:40 X:40 Y:95 P:67 SP:F9 CYC:258 SL:24 +F892 D0 03 BNE $F897 A:40 X:40 Y:95 P:67 SP:F9 CYC:264 SL:24 +F894 50 01 BVC $F897 A:40 X:40 Y:95 P:67 SP:F9 CYC:270 SL:24 +F896 60 RTS A:40 X:40 Y:95 P:67 SP:F9 CYC:276 SL:24 +D390 C8 INY A:40 X:40 Y:95 P:67 SP:FB CYC:294 SL:24 +D391 A9 3F LDA #$3F A:40 X:40 Y:96 P:E5 SP:FB CYC:300 SL:24 +D393 85 78 STA $78 = 40 A:3F X:40 Y:96 P:65 SP:FB CYC:306 SL:24 +D395 20 9A F8 JSR $F89A A:3F X:40 Y:96 P:65 SP:FB CYC:315 SL:24 +F89A B8 CLV A:3F X:40 Y:96 P:65 SP:F9 CYC:333 SL:24 +F89B 60 RTS A:3F X:40 Y:96 P:25 SP:F9 CYC:339 SL:24 +D398 E4 78 CPX $78 = 3F A:3F X:40 Y:96 P:25 SP:FB CYC: 16 SL:25 +D39A 20 9C F8 JSR $F89C A:3F X:40 Y:96 P:25 SP:FB CYC: 25 SL:25 +F89C F0 07 BEQ $F8A5 A:3F X:40 Y:96 P:25 SP:F9 CYC: 43 SL:25 +F89E 30 05 BMI $F8A5 A:3F X:40 Y:96 P:25 SP:F9 CYC: 49 SL:25 +F8A0 90 03 BCC $F8A5 A:3F X:40 Y:96 P:25 SP:F9 CYC: 55 SL:25 +F8A2 70 01 BVS $F8A5 A:3F X:40 Y:96 P:25 SP:F9 CYC: 61 SL:25 +F8A4 60 RTS A:3F X:40 Y:96 P:25 SP:F9 CYC: 67 SL:25 +D39D C8 INY A:3F X:40 Y:96 P:25 SP:FB CYC: 85 SL:25 +D39E A9 41 LDA #$41 A:3F X:40 Y:97 P:A5 SP:FB CYC: 91 SL:25 +D3A0 85 78 STA $78 = 3F A:41 X:40 Y:97 P:25 SP:FB CYC: 97 SL:25 +D3A2 E4 78 CPX $78 = 41 A:41 X:40 Y:97 P:25 SP:FB CYC:106 SL:25 +D3A4 20 A8 F8 JSR $F8A8 A:41 X:40 Y:97 P:A4 SP:FB CYC:115 SL:25 +F8A8 F0 05 BEQ $F8AF A:41 X:40 Y:97 P:A4 SP:F9 CYC:133 SL:25 +F8AA 10 03 BPL $F8AF A:41 X:40 Y:97 P:A4 SP:F9 CYC:139 SL:25 +F8AC 10 01 BPL $F8AF A:41 X:40 Y:97 P:A4 SP:F9 CYC:145 SL:25 +F8AE 60 RTS A:41 X:40 Y:97 P:A4 SP:F9 CYC:151 SL:25 +D3A7 C8 INY A:41 X:40 Y:97 P:A4 SP:FB CYC:169 SL:25 +D3A8 A9 00 LDA #$00 A:41 X:40 Y:98 P:A4 SP:FB CYC:175 SL:25 +D3AA 85 78 STA $78 = 41 A:00 X:40 Y:98 P:26 SP:FB CYC:181 SL:25 +D3AC 20 B2 F8 JSR $F8B2 A:00 X:40 Y:98 P:26 SP:FB CYC:190 SL:25 +F8B2 A9 80 LDA #$80 A:00 X:40 Y:98 P:26 SP:F9 CYC:208 SL:25 +F8B4 60 RTS A:80 X:40 Y:98 P:A4 SP:F9 CYC:214 SL:25 +D3AF AA TAX A:80 X:40 Y:98 P:A4 SP:FB CYC:232 SL:25 +D3B0 E4 78 CPX $78 = 00 A:80 X:80 Y:98 P:A4 SP:FB CYC:238 SL:25 +D3B2 20 B5 F8 JSR $F8B5 A:80 X:80 Y:98 P:A5 SP:FB CYC:247 SL:25 +F8B5 F0 05 BEQ $F8BC A:80 X:80 Y:98 P:A5 SP:F9 CYC:265 SL:25 +F8B7 10 03 BPL $F8BC A:80 X:80 Y:98 P:A5 SP:F9 CYC:271 SL:25 +F8B9 90 01 BCC $F8BC A:80 X:80 Y:98 P:A5 SP:F9 CYC:277 SL:25 +F8BB 60 RTS A:80 X:80 Y:98 P:A5 SP:F9 CYC:283 SL:25 +D3B5 C8 INY A:80 X:80 Y:98 P:A5 SP:FB CYC:301 SL:25 +D3B6 A9 80 LDA #$80 A:80 X:80 Y:99 P:A5 SP:FB CYC:307 SL:25 +D3B8 85 78 STA $78 = 00 A:80 X:80 Y:99 P:A5 SP:FB CYC:313 SL:25 +D3BA E4 78 CPX $78 = 80 A:80 X:80 Y:99 P:A5 SP:FB CYC:322 SL:25 +D3BC 20 BF F8 JSR $F8BF A:80 X:80 Y:99 P:27 SP:FB CYC:331 SL:25 +F8BF D0 05 BNE $F8C6 A:80 X:80 Y:99 P:27 SP:F9 CYC: 8 SL:26 +F8C1 30 03 BMI $F8C6 A:80 X:80 Y:99 P:27 SP:F9 CYC: 14 SL:26 +F8C3 90 01 BCC $F8C6 A:80 X:80 Y:99 P:27 SP:F9 CYC: 20 SL:26 +F8C5 60 RTS A:80 X:80 Y:99 P:27 SP:F9 CYC: 26 SL:26 +D3BF C8 INY A:80 X:80 Y:99 P:27 SP:FB CYC: 44 SL:26 +D3C0 A9 81 LDA #$81 A:80 X:80 Y:9A P:A5 SP:FB CYC: 50 SL:26 +D3C2 85 78 STA $78 = 80 A:81 X:80 Y:9A P:A5 SP:FB CYC: 56 SL:26 +D3C4 E4 78 CPX $78 = 81 A:81 X:80 Y:9A P:A5 SP:FB CYC: 65 SL:26 +D3C6 20 C9 F8 JSR $F8C9 A:81 X:80 Y:9A P:A4 SP:FB CYC: 74 SL:26 +F8C9 B0 05 BCS $F8D0 A:81 X:80 Y:9A P:A4 SP:F9 CYC: 92 SL:26 +F8CB F0 03 BEQ $F8D0 A:81 X:80 Y:9A P:A4 SP:F9 CYC: 98 SL:26 +F8CD 10 01 BPL $F8D0 A:81 X:80 Y:9A P:A4 SP:F9 CYC:104 SL:26 +F8CF 60 RTS A:81 X:80 Y:9A P:A4 SP:F9 CYC:110 SL:26 +D3C9 C8 INY A:81 X:80 Y:9A P:A4 SP:FB CYC:128 SL:26 +D3CA A9 7F LDA #$7F A:81 X:80 Y:9B P:A4 SP:FB CYC:134 SL:26 +D3CC 85 78 STA $78 = 81 A:7F X:80 Y:9B P:24 SP:FB CYC:140 SL:26 +D3CE E4 78 CPX $78 = 7F A:7F X:80 Y:9B P:24 SP:FB CYC:149 SL:26 +D3D0 20 D3 F8 JSR $F8D3 A:7F X:80 Y:9B P:25 SP:FB CYC:158 SL:26 +F8D3 90 05 BCC $F8DA A:7F X:80 Y:9B P:25 SP:F9 CYC:176 SL:26 +F8D5 F0 03 BEQ $F8DA A:7F X:80 Y:9B P:25 SP:F9 CYC:182 SL:26 +F8D7 30 01 BMI $F8DA A:7F X:80 Y:9B P:25 SP:F9 CYC:188 SL:26 +F8D9 60 RTS A:7F X:80 Y:9B P:25 SP:F9 CYC:194 SL:26 +D3D3 C8 INY A:7F X:80 Y:9B P:25 SP:FB CYC:212 SL:26 +D3D4 98 TYA A:7F X:80 Y:9C P:A5 SP:FB CYC:218 SL:26 +D3D5 AA TAX A:9C X:80 Y:9C P:A5 SP:FB CYC:224 SL:26 +D3D6 A9 40 LDA #$40 A:9C X:9C Y:9C P:A5 SP:FB CYC:230 SL:26 +D3D8 85 78 STA $78 = 7F A:40 X:9C Y:9C P:25 SP:FB CYC:236 SL:26 +D3DA 20 DD F8 JSR $F8DD A:40 X:9C Y:9C P:25 SP:FB CYC:245 SL:26 +F8DD 24 01 BIT $01 = FF A:40 X:9C Y:9C P:25 SP:F9 CYC:263 SL:26 +F8DF A0 40 LDY #$40 A:40 X:9C Y:9C P:E5 SP:F9 CYC:272 SL:26 +F8E1 60 RTS A:40 X:9C Y:40 P:65 SP:F9 CYC:278 SL:26 +D3DD C4 78 CPY $78 = 40 A:40 X:9C Y:40 P:65 SP:FB CYC:296 SL:26 +D3DF 20 E2 F8 JSR $F8E2 A:40 X:9C Y:40 P:67 SP:FB CYC:305 SL:26 +F8E2 30 07 BMI $F8EB A:40 X:9C Y:40 P:67 SP:F9 CYC:323 SL:26 +F8E4 90 05 BCC $F8EB A:40 X:9C Y:40 P:67 SP:F9 CYC:329 SL:26 +F8E6 D0 03 BNE $F8EB A:40 X:9C Y:40 P:67 SP:F9 CYC:335 SL:26 +F8E8 50 01 BVC $F8EB A:40 X:9C Y:40 P:67 SP:F9 CYC: 0 SL:27 +F8EA 60 RTS A:40 X:9C Y:40 P:67 SP:F9 CYC: 6 SL:27 +D3E2 E8 INX A:40 X:9C Y:40 P:67 SP:FB CYC: 24 SL:27 +D3E3 A9 3F LDA #$3F A:40 X:9D Y:40 P:E5 SP:FB CYC: 30 SL:27 +D3E5 85 78 STA $78 = 40 A:3F X:9D Y:40 P:65 SP:FB CYC: 36 SL:27 +D3E7 20 EE F8 JSR $F8EE A:3F X:9D Y:40 P:65 SP:FB CYC: 45 SL:27 +F8EE B8 CLV A:3F X:9D Y:40 P:65 SP:F9 CYC: 63 SL:27 +F8EF 60 RTS A:3F X:9D Y:40 P:25 SP:F9 CYC: 69 SL:27 +D3EA C4 78 CPY $78 = 3F A:3F X:9D Y:40 P:25 SP:FB CYC: 87 SL:27 +D3EC 20 F0 F8 JSR $F8F0 A:3F X:9D Y:40 P:25 SP:FB CYC: 96 SL:27 +F8F0 F0 07 BEQ $F8F9 A:3F X:9D Y:40 P:25 SP:F9 CYC:114 SL:27 +F8F2 30 05 BMI $F8F9 A:3F X:9D Y:40 P:25 SP:F9 CYC:120 SL:27 +F8F4 90 03 BCC $F8F9 A:3F X:9D Y:40 P:25 SP:F9 CYC:126 SL:27 +F8F6 70 01 BVS $F8F9 A:3F X:9D Y:40 P:25 SP:F9 CYC:132 SL:27 +F8F8 60 RTS A:3F X:9D Y:40 P:25 SP:F9 CYC:138 SL:27 +D3EF E8 INX A:3F X:9D Y:40 P:25 SP:FB CYC:156 SL:27 +D3F0 A9 41 LDA #$41 A:3F X:9E Y:40 P:A5 SP:FB CYC:162 SL:27 +D3F2 85 78 STA $78 = 3F A:41 X:9E Y:40 P:25 SP:FB CYC:168 SL:27 +D3F4 C4 78 CPY $78 = 41 A:41 X:9E Y:40 P:25 SP:FB CYC:177 SL:27 +D3F6 20 FC F8 JSR $F8FC A:41 X:9E Y:40 P:A4 SP:FB CYC:186 SL:27 +F8FC F0 05 BEQ $F903 A:41 X:9E Y:40 P:A4 SP:F9 CYC:204 SL:27 +F8FE 10 03 BPL $F903 A:41 X:9E Y:40 P:A4 SP:F9 CYC:210 SL:27 +F900 10 01 BPL $F903 A:41 X:9E Y:40 P:A4 SP:F9 CYC:216 SL:27 +F902 60 RTS A:41 X:9E Y:40 P:A4 SP:F9 CYC:222 SL:27 +D3F9 E8 INX A:41 X:9E Y:40 P:A4 SP:FB CYC:240 SL:27 +D3FA A9 00 LDA #$00 A:41 X:9F Y:40 P:A4 SP:FB CYC:246 SL:27 +D3FC 85 78 STA $78 = 41 A:00 X:9F Y:40 P:26 SP:FB CYC:252 SL:27 +D3FE 20 06 F9 JSR $F906 A:00 X:9F Y:40 P:26 SP:FB CYC:261 SL:27 +F906 A0 80 LDY #$80 A:00 X:9F Y:40 P:26 SP:F9 CYC:279 SL:27 +F908 60 RTS A:00 X:9F Y:80 P:A4 SP:F9 CYC:285 SL:27 +D401 C4 78 CPY $78 = 00 A:00 X:9F Y:80 P:A4 SP:FB CYC:303 SL:27 +D403 20 09 F9 JSR $F909 A:00 X:9F Y:80 P:A5 SP:FB CYC:312 SL:27 +F909 F0 05 BEQ $F910 A:00 X:9F Y:80 P:A5 SP:F9 CYC:330 SL:27 +F90B 10 03 BPL $F910 A:00 X:9F Y:80 P:A5 SP:F9 CYC:336 SL:27 +F90D 90 01 BCC $F910 A:00 X:9F Y:80 P:A5 SP:F9 CYC: 1 SL:28 +F90F 60 RTS A:00 X:9F Y:80 P:A5 SP:F9 CYC: 7 SL:28 +D406 E8 INX A:00 X:9F Y:80 P:A5 SP:FB CYC: 25 SL:28 +D407 A9 80 LDA #$80 A:00 X:A0 Y:80 P:A5 SP:FB CYC: 31 SL:28 +D409 85 78 STA $78 = 00 A:80 X:A0 Y:80 P:A5 SP:FB CYC: 37 SL:28 +D40B C4 78 CPY $78 = 80 A:80 X:A0 Y:80 P:A5 SP:FB CYC: 46 SL:28 +D40D 20 13 F9 JSR $F913 A:80 X:A0 Y:80 P:27 SP:FB CYC: 55 SL:28 +F913 D0 05 BNE $F91A A:80 X:A0 Y:80 P:27 SP:F9 CYC: 73 SL:28 +F915 30 03 BMI $F91A A:80 X:A0 Y:80 P:27 SP:F9 CYC: 79 SL:28 +F917 90 01 BCC $F91A A:80 X:A0 Y:80 P:27 SP:F9 CYC: 85 SL:28 +F919 60 RTS A:80 X:A0 Y:80 P:27 SP:F9 CYC: 91 SL:28 +D410 E8 INX A:80 X:A0 Y:80 P:27 SP:FB CYC:109 SL:28 +D411 A9 81 LDA #$81 A:80 X:A1 Y:80 P:A5 SP:FB CYC:115 SL:28 +D413 85 78 STA $78 = 80 A:81 X:A1 Y:80 P:A5 SP:FB CYC:121 SL:28 +D415 C4 78 CPY $78 = 81 A:81 X:A1 Y:80 P:A5 SP:FB CYC:130 SL:28 +D417 20 1D F9 JSR $F91D A:81 X:A1 Y:80 P:A4 SP:FB CYC:139 SL:28 +F91D B0 05 BCS $F924 A:81 X:A1 Y:80 P:A4 SP:F9 CYC:157 SL:28 +F91F F0 03 BEQ $F924 A:81 X:A1 Y:80 P:A4 SP:F9 CYC:163 SL:28 +F921 10 01 BPL $F924 A:81 X:A1 Y:80 P:A4 SP:F9 CYC:169 SL:28 +F923 60 RTS A:81 X:A1 Y:80 P:A4 SP:F9 CYC:175 SL:28 +D41A E8 INX A:81 X:A1 Y:80 P:A4 SP:FB CYC:193 SL:28 +D41B A9 7F LDA #$7F A:81 X:A2 Y:80 P:A4 SP:FB CYC:199 SL:28 +D41D 85 78 STA $78 = 81 A:7F X:A2 Y:80 P:24 SP:FB CYC:205 SL:28 +D41F C4 78 CPY $78 = 7F A:7F X:A2 Y:80 P:24 SP:FB CYC:214 SL:28 +D421 20 27 F9 JSR $F927 A:7F X:A2 Y:80 P:25 SP:FB CYC:223 SL:28 +F927 90 05 BCC $F92E A:7F X:A2 Y:80 P:25 SP:F9 CYC:241 SL:28 +F929 F0 03 BEQ $F92E A:7F X:A2 Y:80 P:25 SP:F9 CYC:247 SL:28 +F92B 30 01 BMI $F92E A:7F X:A2 Y:80 P:25 SP:F9 CYC:253 SL:28 +F92D 60 RTS A:7F X:A2 Y:80 P:25 SP:F9 CYC:259 SL:28 +D424 E8 INX A:7F X:A2 Y:80 P:25 SP:FB CYC:277 SL:28 +D425 8A TXA A:7F X:A3 Y:80 P:A5 SP:FB CYC:283 SL:28 +D426 A8 TAY A:A3 X:A3 Y:80 P:A5 SP:FB CYC:289 SL:28 +D427 20 90 F9 JSR $F990 A:A3 X:A3 Y:A3 P:A5 SP:FB CYC:295 SL:28 +F990 A2 55 LDX #$55 A:A3 X:A3 Y:A3 P:A5 SP:F9 CYC:313 SL:28 +F992 A9 FF LDA #$FF A:A3 X:55 Y:A3 P:25 SP:F9 CYC:319 SL:28 +F994 85 01 STA $01 = FF A:FF X:55 Y:A3 P:A5 SP:F9 CYC:325 SL:28 +F996 EA NOP A:FF X:55 Y:A3 P:A5 SP:F9 CYC:334 SL:28 +F997 24 01 BIT $01 = FF A:FF X:55 Y:A3 P:A5 SP:F9 CYC:340 SL:28 +F999 38 SEC A:FF X:55 Y:A3 P:E5 SP:F9 CYC: 8 SL:29 +F99A A9 01 LDA #$01 A:FF X:55 Y:A3 P:E5 SP:F9 CYC: 14 SL:29 +F99C 60 RTS A:01 X:55 Y:A3 P:65 SP:F9 CYC: 20 SL:29 +D42A 85 78 STA $78 = 7F A:01 X:55 Y:A3 P:65 SP:FB CYC: 38 SL:29 +D42C 46 78 LSR $78 = 01 A:01 X:55 Y:A3 P:65 SP:FB CYC: 47 SL:29 +D42E A5 78 LDA $78 = 00 A:01 X:55 Y:A3 P:67 SP:FB CYC: 62 SL:29 +D430 20 9D F9 JSR $F99D A:00 X:55 Y:A3 P:67 SP:FB CYC: 71 SL:29 +F99D 90 1B BCC $F9BA A:00 X:55 Y:A3 P:67 SP:F9 CYC: 89 SL:29 +F99F D0 19 BNE $F9BA A:00 X:55 Y:A3 P:67 SP:F9 CYC: 95 SL:29 +F9A1 30 17 BMI $F9BA A:00 X:55 Y:A3 P:67 SP:F9 CYC:101 SL:29 +F9A3 50 15 BVC $F9BA A:00 X:55 Y:A3 P:67 SP:F9 CYC:107 SL:29 +F9A5 C9 00 CMP #$00 A:00 X:55 Y:A3 P:67 SP:F9 CYC:113 SL:29 +F9A7 D0 11 BNE $F9BA A:00 X:55 Y:A3 P:67 SP:F9 CYC:119 SL:29 +F9A9 B8 CLV A:00 X:55 Y:A3 P:67 SP:F9 CYC:125 SL:29 +F9AA A9 AA LDA #$AA A:00 X:55 Y:A3 P:27 SP:F9 CYC:131 SL:29 +F9AC 60 RTS A:AA X:55 Y:A3 P:A5 SP:F9 CYC:137 SL:29 +D433 C8 INY A:AA X:55 Y:A3 P:A5 SP:FB CYC:155 SL:29 +D434 85 78 STA $78 = 00 A:AA X:55 Y:A4 P:A5 SP:FB CYC:161 SL:29 +D436 46 78 LSR $78 = AA A:AA X:55 Y:A4 P:A5 SP:FB CYC:170 SL:29 +D438 A5 78 LDA $78 = 55 A:AA X:55 Y:A4 P:24 SP:FB CYC:185 SL:29 +D43A 20 AD F9 JSR $F9AD A:55 X:55 Y:A4 P:24 SP:FB CYC:194 SL:29 +F9AD B0 0B BCS $F9BA A:55 X:55 Y:A4 P:24 SP:F9 CYC:212 SL:29 +F9AF F0 09 BEQ $F9BA A:55 X:55 Y:A4 P:24 SP:F9 CYC:218 SL:29 +F9B1 30 07 BMI $F9BA A:55 X:55 Y:A4 P:24 SP:F9 CYC:224 SL:29 +F9B3 70 05 BVS $F9BA A:55 X:55 Y:A4 P:24 SP:F9 CYC:230 SL:29 +F9B5 C9 55 CMP #$55 A:55 X:55 Y:A4 P:24 SP:F9 CYC:236 SL:29 +F9B7 D0 01 BNE $F9BA A:55 X:55 Y:A4 P:27 SP:F9 CYC:242 SL:29 +F9B9 60 RTS A:55 X:55 Y:A4 P:27 SP:F9 CYC:248 SL:29 +D43D C8 INY A:55 X:55 Y:A4 P:27 SP:FB CYC:266 SL:29 +D43E 20 BD F9 JSR $F9BD A:55 X:55 Y:A5 P:A5 SP:FB CYC:272 SL:29 +F9BD 24 01 BIT $01 = FF A:55 X:55 Y:A5 P:A5 SP:F9 CYC:290 SL:29 +F9BF 38 SEC A:55 X:55 Y:A5 P:E5 SP:F9 CYC:299 SL:29 +F9C0 A9 80 LDA #$80 A:55 X:55 Y:A5 P:E5 SP:F9 CYC:305 SL:29 +F9C2 60 RTS A:80 X:55 Y:A5 P:E5 SP:F9 CYC:311 SL:29 +D441 85 78 STA $78 = 55 A:80 X:55 Y:A5 P:E5 SP:FB CYC:329 SL:29 +D443 06 78 ASL $78 = 80 A:80 X:55 Y:A5 P:E5 SP:FB CYC:338 SL:29 +D445 A5 78 LDA $78 = 00 A:80 X:55 Y:A5 P:67 SP:FB CYC: 12 SL:30 +D447 20 C3 F9 JSR $F9C3 A:00 X:55 Y:A5 P:67 SP:FB CYC: 21 SL:30 +F9C3 90 1C BCC $F9E1 A:00 X:55 Y:A5 P:67 SP:F9 CYC: 39 SL:30 +F9C5 D0 1A BNE $F9E1 A:00 X:55 Y:A5 P:67 SP:F9 CYC: 45 SL:30 +F9C7 30 18 BMI $F9E1 A:00 X:55 Y:A5 P:67 SP:F9 CYC: 51 SL:30 +F9C9 50 16 BVC $F9E1 A:00 X:55 Y:A5 P:67 SP:F9 CYC: 57 SL:30 +F9CB C9 00 CMP #$00 A:00 X:55 Y:A5 P:67 SP:F9 CYC: 63 SL:30 +F9CD D0 12 BNE $F9E1 A:00 X:55 Y:A5 P:67 SP:F9 CYC: 69 SL:30 +F9CF B8 CLV A:00 X:55 Y:A5 P:67 SP:F9 CYC: 75 SL:30 +F9D0 A9 55 LDA #$55 A:00 X:55 Y:A5 P:27 SP:F9 CYC: 81 SL:30 +F9D2 38 SEC A:55 X:55 Y:A5 P:25 SP:F9 CYC: 87 SL:30 +F9D3 60 RTS A:55 X:55 Y:A5 P:25 SP:F9 CYC: 93 SL:30 +D44A C8 INY A:55 X:55 Y:A5 P:25 SP:FB CYC:111 SL:30 +D44B 85 78 STA $78 = 00 A:55 X:55 Y:A6 P:A5 SP:FB CYC:117 SL:30 +D44D 06 78 ASL $78 = 55 A:55 X:55 Y:A6 P:A5 SP:FB CYC:126 SL:30 +D44F A5 78 LDA $78 = AA A:55 X:55 Y:A6 P:A4 SP:FB CYC:141 SL:30 +D451 20 D4 F9 JSR $F9D4 A:AA X:55 Y:A6 P:A4 SP:FB CYC:150 SL:30 +F9D4 B0 0B BCS $F9E1 A:AA X:55 Y:A6 P:A4 SP:F9 CYC:168 SL:30 +F9D6 F0 09 BEQ $F9E1 A:AA X:55 Y:A6 P:A4 SP:F9 CYC:174 SL:30 +F9D8 10 07 BPL $F9E1 A:AA X:55 Y:A6 P:A4 SP:F9 CYC:180 SL:30 +F9DA 70 05 BVS $F9E1 A:AA X:55 Y:A6 P:A4 SP:F9 CYC:186 SL:30 +F9DC C9 AA CMP #$AA A:AA X:55 Y:A6 P:A4 SP:F9 CYC:192 SL:30 +F9DE D0 01 BNE $F9E1 A:AA X:55 Y:A6 P:27 SP:F9 CYC:198 SL:30 +F9E0 60 RTS A:AA X:55 Y:A6 P:27 SP:F9 CYC:204 SL:30 +D454 C8 INY A:AA X:55 Y:A6 P:27 SP:FB CYC:222 SL:30 +D455 20 E4 F9 JSR $F9E4 A:AA X:55 Y:A7 P:A5 SP:FB CYC:228 SL:30 +F9E4 24 01 BIT $01 = FF A:AA X:55 Y:A7 P:A5 SP:F9 CYC:246 SL:30 +F9E6 38 SEC A:AA X:55 Y:A7 P:E5 SP:F9 CYC:255 SL:30 +F9E7 A9 01 LDA #$01 A:AA X:55 Y:A7 P:E5 SP:F9 CYC:261 SL:30 +F9E9 60 RTS A:01 X:55 Y:A7 P:65 SP:F9 CYC:267 SL:30 +D458 85 78 STA $78 = AA A:01 X:55 Y:A7 P:65 SP:FB CYC:285 SL:30 +D45A 66 78 ROR $78 = 01 A:01 X:55 Y:A7 P:65 SP:FB CYC:294 SL:30 +D45C A5 78 LDA $78 = 80 A:01 X:55 Y:A7 P:E5 SP:FB CYC:309 SL:30 +D45E 20 EA F9 JSR $F9EA A:80 X:55 Y:A7 P:E5 SP:FB CYC:318 SL:30 +F9EA 90 1C BCC $FA08 A:80 X:55 Y:A7 P:E5 SP:F9 CYC:336 SL:30 +F9EC F0 1A BEQ $FA08 A:80 X:55 Y:A7 P:E5 SP:F9 CYC: 1 SL:31 +F9EE 10 18 BPL $FA08 A:80 X:55 Y:A7 P:E5 SP:F9 CYC: 7 SL:31 +F9F0 50 16 BVC $FA08 A:80 X:55 Y:A7 P:E5 SP:F9 CYC: 13 SL:31 +F9F2 C9 80 CMP #$80 A:80 X:55 Y:A7 P:E5 SP:F9 CYC: 19 SL:31 +F9F4 D0 12 BNE $FA08 A:80 X:55 Y:A7 P:67 SP:F9 CYC: 25 SL:31 +F9F6 B8 CLV A:80 X:55 Y:A7 P:67 SP:F9 CYC: 31 SL:31 +F9F7 18 CLC A:80 X:55 Y:A7 P:27 SP:F9 CYC: 37 SL:31 +F9F8 A9 55 LDA #$55 A:80 X:55 Y:A7 P:26 SP:F9 CYC: 43 SL:31 +F9FA 60 RTS A:55 X:55 Y:A7 P:24 SP:F9 CYC: 49 SL:31 +D461 C8 INY A:55 X:55 Y:A7 P:24 SP:FB CYC: 67 SL:31 +D462 85 78 STA $78 = 80 A:55 X:55 Y:A8 P:A4 SP:FB CYC: 73 SL:31 +D464 66 78 ROR $78 = 55 A:55 X:55 Y:A8 P:A4 SP:FB CYC: 82 SL:31 +D466 A5 78 LDA $78 = 2A A:55 X:55 Y:A8 P:25 SP:FB CYC: 97 SL:31 +D468 20 FB F9 JSR $F9FB A:2A X:55 Y:A8 P:25 SP:FB CYC:106 SL:31 +F9FB 90 0B BCC $FA08 A:2A X:55 Y:A8 P:25 SP:F9 CYC:124 SL:31 +F9FD F0 09 BEQ $FA08 A:2A X:55 Y:A8 P:25 SP:F9 CYC:130 SL:31 +F9FF 30 07 BMI $FA08 A:2A X:55 Y:A8 P:25 SP:F9 CYC:136 SL:31 +FA01 70 05 BVS $FA08 A:2A X:55 Y:A8 P:25 SP:F9 CYC:142 SL:31 +FA03 C9 2A CMP #$2A A:2A X:55 Y:A8 P:25 SP:F9 CYC:148 SL:31 +FA05 D0 01 BNE $FA08 A:2A X:55 Y:A8 P:27 SP:F9 CYC:154 SL:31 +FA07 60 RTS A:2A X:55 Y:A8 P:27 SP:F9 CYC:160 SL:31 +D46B C8 INY A:2A X:55 Y:A8 P:27 SP:FB CYC:178 SL:31 +D46C 20 0A FA JSR $FA0A A:2A X:55 Y:A9 P:A5 SP:FB CYC:184 SL:31 +FA0A 24 01 BIT $01 = FF A:2A X:55 Y:A9 P:A5 SP:F9 CYC:202 SL:31 +FA0C 38 SEC A:2A X:55 Y:A9 P:E5 SP:F9 CYC:211 SL:31 +FA0D A9 80 LDA #$80 A:2A X:55 Y:A9 P:E5 SP:F9 CYC:217 SL:31 +FA0F 60 RTS A:80 X:55 Y:A9 P:E5 SP:F9 CYC:223 SL:31 +D46F 85 78 STA $78 = 2A A:80 X:55 Y:A9 P:E5 SP:FB CYC:241 SL:31 +D471 26 78 ROL $78 = 80 A:80 X:55 Y:A9 P:E5 SP:FB CYC:250 SL:31 +D473 A5 78 LDA $78 = 01 A:80 X:55 Y:A9 P:65 SP:FB CYC:265 SL:31 +D475 20 10 FA JSR $FA10 A:01 X:55 Y:A9 P:65 SP:FB CYC:274 SL:31 +FA10 90 1C BCC $FA2E A:01 X:55 Y:A9 P:65 SP:F9 CYC:292 SL:31 +FA12 F0 1A BEQ $FA2E A:01 X:55 Y:A9 P:65 SP:F9 CYC:298 SL:31 +FA14 30 18 BMI $FA2E A:01 X:55 Y:A9 P:65 SP:F9 CYC:304 SL:31 +FA16 50 16 BVC $FA2E A:01 X:55 Y:A9 P:65 SP:F9 CYC:310 SL:31 +FA18 C9 01 CMP #$01 A:01 X:55 Y:A9 P:65 SP:F9 CYC:316 SL:31 +FA1A D0 12 BNE $FA2E A:01 X:55 Y:A9 P:67 SP:F9 CYC:322 SL:31 +FA1C B8 CLV A:01 X:55 Y:A9 P:67 SP:F9 CYC:328 SL:31 +FA1D 18 CLC A:01 X:55 Y:A9 P:27 SP:F9 CYC:334 SL:31 +FA1E A9 55 LDA #$55 A:01 X:55 Y:A9 P:26 SP:F9 CYC:340 SL:31 +FA20 60 RTS A:55 X:55 Y:A9 P:24 SP:F9 CYC: 5 SL:32 +D478 C8 INY A:55 X:55 Y:A9 P:24 SP:FB CYC: 23 SL:32 +D479 85 78 STA $78 = 01 A:55 X:55 Y:AA P:A4 SP:FB CYC: 29 SL:32 +D47B 26 78 ROL $78 = 55 A:55 X:55 Y:AA P:A4 SP:FB CYC: 38 SL:32 +D47D A5 78 LDA $78 = AA A:55 X:55 Y:AA P:A4 SP:FB CYC: 53 SL:32 +D47F 20 21 FA JSR $FA21 A:AA X:55 Y:AA P:A4 SP:FB CYC: 62 SL:32 +FA21 B0 0B BCS $FA2E A:AA X:55 Y:AA P:A4 SP:F9 CYC: 80 SL:32 +FA23 F0 09 BEQ $FA2E A:AA X:55 Y:AA P:A4 SP:F9 CYC: 86 SL:32 +FA25 10 07 BPL $FA2E A:AA X:55 Y:AA P:A4 SP:F9 CYC: 92 SL:32 +FA27 70 05 BVS $FA2E A:AA X:55 Y:AA P:A4 SP:F9 CYC: 98 SL:32 +FA29 C9 AA CMP #$AA A:AA X:55 Y:AA P:A4 SP:F9 CYC:104 SL:32 +FA2B D0 01 BNE $FA2E A:AA X:55 Y:AA P:27 SP:F9 CYC:110 SL:32 +FA2D 60 RTS A:AA X:55 Y:AA P:27 SP:F9 CYC:116 SL:32 +D482 A9 FF LDA #$FF A:AA X:55 Y:AA P:27 SP:FB CYC:134 SL:32 +D484 85 78 STA $78 = AA A:FF X:55 Y:AA P:A5 SP:FB CYC:140 SL:32 +D486 85 01 STA $01 = FF A:FF X:55 Y:AA P:A5 SP:FB CYC:149 SL:32 +D488 24 01 BIT $01 = FF A:FF X:55 Y:AA P:A5 SP:FB CYC:158 SL:32 +D48A 38 SEC A:FF X:55 Y:AA P:E5 SP:FB CYC:167 SL:32 +D48B E6 78 INC $78 = FF A:FF X:55 Y:AA P:E5 SP:FB CYC:173 SL:32 +D48D D0 0C BNE $D49B A:FF X:55 Y:AA P:67 SP:FB CYC:188 SL:32 +D48F 30 0A BMI $D49B A:FF X:55 Y:AA P:67 SP:FB CYC:194 SL:32 +D491 50 08 BVC $D49B A:FF X:55 Y:AA P:67 SP:FB CYC:200 SL:32 +D493 90 06 BCC $D49B A:FF X:55 Y:AA P:67 SP:FB CYC:206 SL:32 +D495 A5 78 LDA $78 = 00 A:FF X:55 Y:AA P:67 SP:FB CYC:212 SL:32 +D497 C9 00 CMP #$00 A:00 X:55 Y:AA P:67 SP:FB CYC:221 SL:32 +D499 F0 04 BEQ $D49F A:00 X:55 Y:AA P:67 SP:FB CYC:227 SL:32 +D49F A9 7F LDA #$7F A:00 X:55 Y:AA P:67 SP:FB CYC:236 SL:32 +D4A1 85 78 STA $78 = 00 A:7F X:55 Y:AA P:65 SP:FB CYC:242 SL:32 +D4A3 B8 CLV A:7F X:55 Y:AA P:65 SP:FB CYC:251 SL:32 +D4A4 18 CLC A:7F X:55 Y:AA P:25 SP:FB CYC:257 SL:32 +D4A5 E6 78 INC $78 = 7F A:7F X:55 Y:AA P:24 SP:FB CYC:263 SL:32 +D4A7 F0 0C BEQ $D4B5 A:7F X:55 Y:AA P:A4 SP:FB CYC:278 SL:32 +D4A9 10 0A BPL $D4B5 A:7F X:55 Y:AA P:A4 SP:FB CYC:284 SL:32 +D4AB 70 08 BVS $D4B5 A:7F X:55 Y:AA P:A4 SP:FB CYC:290 SL:32 +D4AD B0 06 BCS $D4B5 A:7F X:55 Y:AA P:A4 SP:FB CYC:296 SL:32 +D4AF A5 78 LDA $78 = 80 A:7F X:55 Y:AA P:A4 SP:FB CYC:302 SL:32 +D4B1 C9 80 CMP #$80 A:80 X:55 Y:AA P:A4 SP:FB CYC:311 SL:32 +D4B3 F0 04 BEQ $D4B9 A:80 X:55 Y:AA P:27 SP:FB CYC:317 SL:32 +D4B9 A9 00 LDA #$00 A:80 X:55 Y:AA P:27 SP:FB CYC:326 SL:32 +D4BB 85 78 STA $78 = 80 A:00 X:55 Y:AA P:27 SP:FB CYC:332 SL:32 +D4BD 24 01 BIT $01 = FF A:00 X:55 Y:AA P:27 SP:FB CYC: 0 SL:33 +D4BF 38 SEC A:00 X:55 Y:AA P:E7 SP:FB CYC: 9 SL:33 +D4C0 C6 78 DEC $78 = 00 A:00 X:55 Y:AA P:E7 SP:FB CYC: 15 SL:33 +D4C2 F0 0C BEQ $D4D0 A:00 X:55 Y:AA P:E5 SP:FB CYC: 30 SL:33 +D4C4 10 0A BPL $D4D0 A:00 X:55 Y:AA P:E5 SP:FB CYC: 36 SL:33 +D4C6 50 08 BVC $D4D0 A:00 X:55 Y:AA P:E5 SP:FB CYC: 42 SL:33 +D4C8 90 06 BCC $D4D0 A:00 X:55 Y:AA P:E5 SP:FB CYC: 48 SL:33 +D4CA A5 78 LDA $78 = FF A:00 X:55 Y:AA P:E5 SP:FB CYC: 54 SL:33 +D4CC C9 FF CMP #$FF A:FF X:55 Y:AA P:E5 SP:FB CYC: 63 SL:33 +D4CE F0 04 BEQ $D4D4 A:FF X:55 Y:AA P:67 SP:FB CYC: 69 SL:33 +D4D4 A9 80 LDA #$80 A:FF X:55 Y:AA P:67 SP:FB CYC: 78 SL:33 +D4D6 85 78 STA $78 = FF A:80 X:55 Y:AA P:E5 SP:FB CYC: 84 SL:33 +D4D8 B8 CLV A:80 X:55 Y:AA P:E5 SP:FB CYC: 93 SL:33 +D4D9 18 CLC A:80 X:55 Y:AA P:A5 SP:FB CYC: 99 SL:33 +D4DA C6 78 DEC $78 = 80 A:80 X:55 Y:AA P:A4 SP:FB CYC:105 SL:33 +D4DC F0 0C BEQ $D4EA A:80 X:55 Y:AA P:24 SP:FB CYC:120 SL:33 +D4DE 30 0A BMI $D4EA A:80 X:55 Y:AA P:24 SP:FB CYC:126 SL:33 +D4E0 70 08 BVS $D4EA A:80 X:55 Y:AA P:24 SP:FB CYC:132 SL:33 +D4E2 B0 06 BCS $D4EA A:80 X:55 Y:AA P:24 SP:FB CYC:138 SL:33 +D4E4 A5 78 LDA $78 = 7F A:80 X:55 Y:AA P:24 SP:FB CYC:144 SL:33 +D4E6 C9 7F CMP #$7F A:7F X:55 Y:AA P:24 SP:FB CYC:153 SL:33 +D4E8 F0 04 BEQ $D4EE A:7F X:55 Y:AA P:27 SP:FB CYC:159 SL:33 +D4EE A9 01 LDA #$01 A:7F X:55 Y:AA P:27 SP:FB CYC:168 SL:33 +D4F0 85 78 STA $78 = 7F A:01 X:55 Y:AA P:25 SP:FB CYC:174 SL:33 +D4F2 C6 78 DEC $78 = 01 A:01 X:55 Y:AA P:25 SP:FB CYC:183 SL:33 +D4F4 F0 04 BEQ $D4FA A:01 X:55 Y:AA P:27 SP:FB CYC:198 SL:33 +D4FA 60 RTS A:01 X:55 Y:AA P:27 SP:FB CYC:207 SL:33 +C615 20 FB D4 JSR $D4FB A:01 X:55 Y:AA P:27 SP:FD CYC:225 SL:33 +D4FB A9 55 LDA #$55 A:01 X:55 Y:AA P:27 SP:FB CYC:243 SL:33 +D4FD 8D 78 06 STA $0678 = 00 A:55 X:55 Y:AA P:25 SP:FB CYC:249 SL:33 +D500 A9 FF LDA #$FF A:55 X:55 Y:AA P:25 SP:FB CYC:261 SL:33 +D502 85 01 STA $01 = FF A:FF X:55 Y:AA P:A5 SP:FB CYC:267 SL:33 +D504 24 01 BIT $01 = FF A:FF X:55 Y:AA P:A5 SP:FB CYC:276 SL:33 +D506 A0 11 LDY #$11 A:FF X:55 Y:AA P:E5 SP:FB CYC:285 SL:33 +D508 A2 23 LDX #$23 A:FF X:55 Y:11 P:65 SP:FB CYC:291 SL:33 +D50A A9 00 LDA #$00 A:FF X:23 Y:11 P:65 SP:FB CYC:297 SL:33 +D50C AD 78 06 LDA $0678 = 55 A:00 X:23 Y:11 P:67 SP:FB CYC:303 SL:33 +D50F F0 10 BEQ $D521 A:55 X:23 Y:11 P:65 SP:FB CYC:315 SL:33 +D511 30 0E BMI $D521 A:55 X:23 Y:11 P:65 SP:FB CYC:321 SL:33 +D513 C9 55 CMP #$55 A:55 X:23 Y:11 P:65 SP:FB CYC:327 SL:33 +D515 D0 0A BNE $D521 A:55 X:23 Y:11 P:67 SP:FB CYC:333 SL:33 +D517 C0 11 CPY #$11 A:55 X:23 Y:11 P:67 SP:FB CYC:339 SL:33 +D519 D0 06 BNE $D521 A:55 X:23 Y:11 P:67 SP:FB CYC: 4 SL:34 +D51B E0 23 CPX #$23 A:55 X:23 Y:11 P:67 SP:FB CYC: 10 SL:34 +D51D 50 02 BVC $D521 A:55 X:23 Y:11 P:67 SP:FB CYC: 16 SL:34 +D51F F0 04 BEQ $D525 A:55 X:23 Y:11 P:67 SP:FB CYC: 22 SL:34 +D525 A9 46 LDA #$46 A:55 X:23 Y:11 P:67 SP:FB CYC: 31 SL:34 +D527 24 01 BIT $01 = FF A:46 X:23 Y:11 P:65 SP:FB CYC: 37 SL:34 +D529 8D 78 06 STA $0678 = 55 A:46 X:23 Y:11 P:E5 SP:FB CYC: 46 SL:34 +D52C F0 0B BEQ $D539 A:46 X:23 Y:11 P:E5 SP:FB CYC: 58 SL:34 +D52E 10 09 BPL $D539 A:46 X:23 Y:11 P:E5 SP:FB CYC: 64 SL:34 +D530 50 07 BVC $D539 A:46 X:23 Y:11 P:E5 SP:FB CYC: 70 SL:34 +D532 AD 78 06 LDA $0678 = 46 A:46 X:23 Y:11 P:E5 SP:FB CYC: 76 SL:34 +D535 C9 46 CMP #$46 A:46 X:23 Y:11 P:65 SP:FB CYC: 88 SL:34 +D537 F0 04 BEQ $D53D A:46 X:23 Y:11 P:67 SP:FB CYC: 94 SL:34 +D53D A9 55 LDA #$55 A:46 X:23 Y:11 P:67 SP:FB CYC:103 SL:34 +D53F 8D 78 06 STA $0678 = 46 A:55 X:23 Y:11 P:65 SP:FB CYC:109 SL:34 +D542 24 01 BIT $01 = FF A:55 X:23 Y:11 P:65 SP:FB CYC:121 SL:34 +D544 A9 11 LDA #$11 A:55 X:23 Y:11 P:E5 SP:FB CYC:130 SL:34 +D546 A2 23 LDX #$23 A:11 X:23 Y:11 P:65 SP:FB CYC:136 SL:34 +D548 A0 00 LDY #$00 A:11 X:23 Y:11 P:65 SP:FB CYC:142 SL:34 +D54A AC 78 06 LDY $0678 = 55 A:11 X:23 Y:00 P:67 SP:FB CYC:148 SL:34 +D54D F0 10 BEQ $D55F A:11 X:23 Y:55 P:65 SP:FB CYC:160 SL:34 +D54F 30 0E BMI $D55F A:11 X:23 Y:55 P:65 SP:FB CYC:166 SL:34 +D551 C0 55 CPY #$55 A:11 X:23 Y:55 P:65 SP:FB CYC:172 SL:34 +D553 D0 0A BNE $D55F A:11 X:23 Y:55 P:67 SP:FB CYC:178 SL:34 +D555 C9 11 CMP #$11 A:11 X:23 Y:55 P:67 SP:FB CYC:184 SL:34 +D557 D0 06 BNE $D55F A:11 X:23 Y:55 P:67 SP:FB CYC:190 SL:34 +D559 E0 23 CPX #$23 A:11 X:23 Y:55 P:67 SP:FB CYC:196 SL:34 +D55B 50 02 BVC $D55F A:11 X:23 Y:55 P:67 SP:FB CYC:202 SL:34 +D55D F0 04 BEQ $D563 A:11 X:23 Y:55 P:67 SP:FB CYC:208 SL:34 +D563 A0 46 LDY #$46 A:11 X:23 Y:55 P:67 SP:FB CYC:217 SL:34 +D565 24 01 BIT $01 = FF A:11 X:23 Y:46 P:65 SP:FB CYC:223 SL:34 +D567 8C 78 06 STY $0678 = 55 A:11 X:23 Y:46 P:E5 SP:FB CYC:232 SL:34 +D56A F0 0B BEQ $D577 A:11 X:23 Y:46 P:E5 SP:FB CYC:244 SL:34 +D56C 10 09 BPL $D577 A:11 X:23 Y:46 P:E5 SP:FB CYC:250 SL:34 +D56E 50 07 BVC $D577 A:11 X:23 Y:46 P:E5 SP:FB CYC:256 SL:34 +D570 AC 78 06 LDY $0678 = 46 A:11 X:23 Y:46 P:E5 SP:FB CYC:262 SL:34 +D573 C0 46 CPY #$46 A:11 X:23 Y:46 P:65 SP:FB CYC:274 SL:34 +D575 F0 04 BEQ $D57B A:11 X:23 Y:46 P:67 SP:FB CYC:280 SL:34 +D57B 24 01 BIT $01 = FF A:11 X:23 Y:46 P:67 SP:FB CYC:289 SL:34 +D57D A9 55 LDA #$55 A:11 X:23 Y:46 P:E5 SP:FB CYC:298 SL:34 +D57F 8D 78 06 STA $0678 = 46 A:55 X:23 Y:46 P:65 SP:FB CYC:304 SL:34 +D582 A0 11 LDY #$11 A:55 X:23 Y:46 P:65 SP:FB CYC:316 SL:34 +D584 A9 23 LDA #$23 A:55 X:23 Y:11 P:65 SP:FB CYC:322 SL:34 +D586 A2 00 LDX #$00 A:23 X:23 Y:11 P:65 SP:FB CYC:328 SL:34 +D588 AE 78 06 LDX $0678 = 55 A:23 X:00 Y:11 P:67 SP:FB CYC:334 SL:34 +D58B F0 10 BEQ $D59D A:23 X:55 Y:11 P:65 SP:FB CYC: 5 SL:35 +D58D 30 0E BMI $D59D A:23 X:55 Y:11 P:65 SP:FB CYC: 11 SL:35 +D58F E0 55 CPX #$55 A:23 X:55 Y:11 P:65 SP:FB CYC: 17 SL:35 +D591 D0 0A BNE $D59D A:23 X:55 Y:11 P:67 SP:FB CYC: 23 SL:35 +D593 C0 11 CPY #$11 A:23 X:55 Y:11 P:67 SP:FB CYC: 29 SL:35 +D595 D0 06 BNE $D59D A:23 X:55 Y:11 P:67 SP:FB CYC: 35 SL:35 +D597 C9 23 CMP #$23 A:23 X:55 Y:11 P:67 SP:FB CYC: 41 SL:35 +D599 50 02 BVC $D59D A:23 X:55 Y:11 P:67 SP:FB CYC: 47 SL:35 +D59B F0 04 BEQ $D5A1 A:23 X:55 Y:11 P:67 SP:FB CYC: 53 SL:35 +D5A1 A2 46 LDX #$46 A:23 X:55 Y:11 P:67 SP:FB CYC: 62 SL:35 +D5A3 24 01 BIT $01 = FF A:23 X:46 Y:11 P:65 SP:FB CYC: 68 SL:35 +D5A5 8E 78 06 STX $0678 = 55 A:23 X:46 Y:11 P:E5 SP:FB CYC: 77 SL:35 +D5A8 F0 0B BEQ $D5B5 A:23 X:46 Y:11 P:E5 SP:FB CYC: 89 SL:35 +D5AA 10 09 BPL $D5B5 A:23 X:46 Y:11 P:E5 SP:FB CYC: 95 SL:35 +D5AC 50 07 BVC $D5B5 A:23 X:46 Y:11 P:E5 SP:FB CYC:101 SL:35 +D5AE AE 78 06 LDX $0678 = 46 A:23 X:46 Y:11 P:E5 SP:FB CYC:107 SL:35 +D5B1 E0 46 CPX #$46 A:23 X:46 Y:11 P:65 SP:FB CYC:119 SL:35 +D5B3 F0 04 BEQ $D5B9 A:23 X:46 Y:11 P:67 SP:FB CYC:125 SL:35 +D5B9 A9 C0 LDA #$C0 A:23 X:46 Y:11 P:67 SP:FB CYC:134 SL:35 +D5BB 8D 78 06 STA $0678 = 46 A:C0 X:46 Y:11 P:E5 SP:FB CYC:140 SL:35 +D5BE A2 33 LDX #$33 A:C0 X:46 Y:11 P:E5 SP:FB CYC:152 SL:35 +D5C0 A0 88 LDY #$88 A:C0 X:33 Y:11 P:65 SP:FB CYC:158 SL:35 +D5C2 A9 05 LDA #$05 A:C0 X:33 Y:88 P:E5 SP:FB CYC:164 SL:35 +D5C4 2C 78 06 BIT $0678 = C0 A:05 X:33 Y:88 P:65 SP:FB CYC:170 SL:35 +D5C7 10 10 BPL $D5D9 A:05 X:33 Y:88 P:E7 SP:FB CYC:182 SL:35 +D5C9 50 0E BVC $D5D9 A:05 X:33 Y:88 P:E7 SP:FB CYC:188 SL:35 +D5CB D0 0C BNE $D5D9 A:05 X:33 Y:88 P:E7 SP:FB CYC:194 SL:35 +D5CD C9 05 CMP #$05 A:05 X:33 Y:88 P:E7 SP:FB CYC:200 SL:35 +D5CF D0 08 BNE $D5D9 A:05 X:33 Y:88 P:67 SP:FB CYC:206 SL:35 +D5D1 E0 33 CPX #$33 A:05 X:33 Y:88 P:67 SP:FB CYC:212 SL:35 +D5D3 D0 04 BNE $D5D9 A:05 X:33 Y:88 P:67 SP:FB CYC:218 SL:35 +D5D5 C0 88 CPY #$88 A:05 X:33 Y:88 P:67 SP:FB CYC:224 SL:35 +D5D7 F0 04 BEQ $D5DD A:05 X:33 Y:88 P:67 SP:FB CYC:230 SL:35 +D5DD A9 03 LDA #$03 A:05 X:33 Y:88 P:67 SP:FB CYC:239 SL:35 +D5DF 8D 78 06 STA $0678 = C0 A:03 X:33 Y:88 P:65 SP:FB CYC:245 SL:35 +D5E2 A9 01 LDA #$01 A:03 X:33 Y:88 P:65 SP:FB CYC:257 SL:35 +D5E4 2C 78 06 BIT $0678 = 03 A:01 X:33 Y:88 P:65 SP:FB CYC:263 SL:35 +D5E7 30 08 BMI $D5F1 A:01 X:33 Y:88 P:25 SP:FB CYC:275 SL:35 +D5E9 70 06 BVS $D5F1 A:01 X:33 Y:88 P:25 SP:FB CYC:281 SL:35 +D5EB F0 04 BEQ $D5F1 A:01 X:33 Y:88 P:25 SP:FB CYC:287 SL:35 +D5ED C9 01 CMP #$01 A:01 X:33 Y:88 P:25 SP:FB CYC:293 SL:35 +D5EF F0 04 BEQ $D5F5 A:01 X:33 Y:88 P:27 SP:FB CYC:299 SL:35 +D5F5 A0 B8 LDY #$B8 A:01 X:33 Y:88 P:27 SP:FB CYC:308 SL:35 +D5F7 A9 AA LDA #$AA A:01 X:33 Y:B8 P:A5 SP:FB CYC:314 SL:35 +D5F9 8D 78 06 STA $0678 = 03 A:AA X:33 Y:B8 P:A5 SP:FB CYC:320 SL:35 +D5FC 20 B6 F7 JSR $F7B6 A:AA X:33 Y:B8 P:A5 SP:FB CYC:332 SL:35 +F7B6 18 CLC A:AA X:33 Y:B8 P:A5 SP:F9 CYC: 9 SL:36 +F7B7 A9 FF LDA #$FF A:AA X:33 Y:B8 P:A4 SP:F9 CYC: 15 SL:36 +F7B9 85 01 STA $01 = FF A:FF X:33 Y:B8 P:A4 SP:F9 CYC: 21 SL:36 +F7BB 24 01 BIT $01 = FF A:FF X:33 Y:B8 P:A4 SP:F9 CYC: 30 SL:36 +F7BD A9 55 LDA #$55 A:FF X:33 Y:B8 P:E4 SP:F9 CYC: 39 SL:36 +F7BF 60 RTS A:55 X:33 Y:B8 P:64 SP:F9 CYC: 45 SL:36 +D5FF 0D 78 06 ORA $0678 = AA A:55 X:33 Y:B8 P:64 SP:FB CYC: 63 SL:36 +D602 20 C0 F7 JSR $F7C0 A:FF X:33 Y:B8 P:E4 SP:FB CYC: 75 SL:36 +F7C0 B0 09 BCS $F7CB A:FF X:33 Y:B8 P:E4 SP:F9 CYC: 93 SL:36 +F7C2 10 07 BPL $F7CB A:FF X:33 Y:B8 P:E4 SP:F9 CYC: 99 SL:36 +F7C4 C9 FF CMP #$FF A:FF X:33 Y:B8 P:E4 SP:F9 CYC:105 SL:36 +F7C6 D0 03 BNE $F7CB A:FF X:33 Y:B8 P:67 SP:F9 CYC:111 SL:36 +F7C8 50 01 BVC $F7CB A:FF X:33 Y:B8 P:67 SP:F9 CYC:117 SL:36 +F7CA 60 RTS A:FF X:33 Y:B8 P:67 SP:F9 CYC:123 SL:36 +D605 C8 INY A:FF X:33 Y:B8 P:67 SP:FB CYC:141 SL:36 +D606 A9 00 LDA #$00 A:FF X:33 Y:B9 P:E5 SP:FB CYC:147 SL:36 +D608 8D 78 06 STA $0678 = AA A:00 X:33 Y:B9 P:67 SP:FB CYC:153 SL:36 +D60B 20 CE F7 JSR $F7CE A:00 X:33 Y:B9 P:67 SP:FB CYC:165 SL:36 +F7CE 38 SEC A:00 X:33 Y:B9 P:67 SP:F9 CYC:183 SL:36 +F7CF B8 CLV A:00 X:33 Y:B9 P:67 SP:F9 CYC:189 SL:36 +F7D0 A9 00 LDA #$00 A:00 X:33 Y:B9 P:27 SP:F9 CYC:195 SL:36 +F7D2 60 RTS A:00 X:33 Y:B9 P:27 SP:F9 CYC:201 SL:36 +D60E 0D 78 06 ORA $0678 = 00 A:00 X:33 Y:B9 P:27 SP:FB CYC:219 SL:36 +D611 20 D3 F7 JSR $F7D3 A:00 X:33 Y:B9 P:27 SP:FB CYC:231 SL:36 +F7D3 D0 07 BNE $F7DC A:00 X:33 Y:B9 P:27 SP:F9 CYC:249 SL:36 +F7D5 70 05 BVS $F7DC A:00 X:33 Y:B9 P:27 SP:F9 CYC:255 SL:36 +F7D7 90 03 BCC $F7DC A:00 X:33 Y:B9 P:27 SP:F9 CYC:261 SL:36 +F7D9 30 01 BMI $F7DC A:00 X:33 Y:B9 P:27 SP:F9 CYC:267 SL:36 +F7DB 60 RTS A:00 X:33 Y:B9 P:27 SP:F9 CYC:273 SL:36 +D614 C8 INY A:00 X:33 Y:B9 P:27 SP:FB CYC:291 SL:36 +D615 A9 AA LDA #$AA A:00 X:33 Y:BA P:A5 SP:FB CYC:297 SL:36 +D617 8D 78 06 STA $0678 = 00 A:AA X:33 Y:BA P:A5 SP:FB CYC:303 SL:36 +D61A 20 DF F7 JSR $F7DF A:AA X:33 Y:BA P:A5 SP:FB CYC:315 SL:36 +F7DF 18 CLC A:AA X:33 Y:BA P:A5 SP:F9 CYC:333 SL:36 +F7E0 24 01 BIT $01 = FF A:AA X:33 Y:BA P:A4 SP:F9 CYC:339 SL:36 +F7E2 A9 55 LDA #$55 A:AA X:33 Y:BA P:E4 SP:F9 CYC: 7 SL:37 +F7E4 60 RTS A:55 X:33 Y:BA P:64 SP:F9 CYC: 13 SL:37 +D61D 2D 78 06 AND $0678 = AA A:55 X:33 Y:BA P:64 SP:FB CYC: 31 SL:37 +D620 20 E5 F7 JSR $F7E5 A:00 X:33 Y:BA P:66 SP:FB CYC: 43 SL:37 +F7E5 D0 07 BNE $F7EE A:00 X:33 Y:BA P:66 SP:F9 CYC: 61 SL:37 +F7E7 50 05 BVC $F7EE A:00 X:33 Y:BA P:66 SP:F9 CYC: 67 SL:37 +F7E9 B0 03 BCS $F7EE A:00 X:33 Y:BA P:66 SP:F9 CYC: 73 SL:37 +F7EB 30 01 BMI $F7EE A:00 X:33 Y:BA P:66 SP:F9 CYC: 79 SL:37 +F7ED 60 RTS A:00 X:33 Y:BA P:66 SP:F9 CYC: 85 SL:37 +D623 C8 INY A:00 X:33 Y:BA P:66 SP:FB CYC:103 SL:37 +D624 A9 EF LDA #$EF A:00 X:33 Y:BB P:E4 SP:FB CYC:109 SL:37 +D626 8D 78 06 STA $0678 = AA A:EF X:33 Y:BB P:E4 SP:FB CYC:115 SL:37 +D629 20 F1 F7 JSR $F7F1 A:EF X:33 Y:BB P:E4 SP:FB CYC:127 SL:37 +F7F1 38 SEC A:EF X:33 Y:BB P:E4 SP:F9 CYC:145 SL:37 +F7F2 B8 CLV A:EF X:33 Y:BB P:E5 SP:F9 CYC:151 SL:37 +F7F3 A9 F8 LDA #$F8 A:EF X:33 Y:BB P:A5 SP:F9 CYC:157 SL:37 +F7F5 60 RTS A:F8 X:33 Y:BB P:A5 SP:F9 CYC:163 SL:37 +D62C 2D 78 06 AND $0678 = EF A:F8 X:33 Y:BB P:A5 SP:FB CYC:181 SL:37 +D62F 20 F6 F7 JSR $F7F6 A:E8 X:33 Y:BB P:A5 SP:FB CYC:193 SL:37 +F7F6 90 09 BCC $F801 A:E8 X:33 Y:BB P:A5 SP:F9 CYC:211 SL:37 +F7F8 10 07 BPL $F801 A:E8 X:33 Y:BB P:A5 SP:F9 CYC:217 SL:37 +F7FA C9 E8 CMP #$E8 A:E8 X:33 Y:BB P:A5 SP:F9 CYC:223 SL:37 +F7FC D0 03 BNE $F801 A:E8 X:33 Y:BB P:27 SP:F9 CYC:229 SL:37 +F7FE 70 01 BVS $F801 A:E8 X:33 Y:BB P:27 SP:F9 CYC:235 SL:37 +F800 60 RTS A:E8 X:33 Y:BB P:27 SP:F9 CYC:241 SL:37 +D632 C8 INY A:E8 X:33 Y:BB P:27 SP:FB CYC:259 SL:37 +D633 A9 AA LDA #$AA A:E8 X:33 Y:BC P:A5 SP:FB CYC:265 SL:37 +D635 8D 78 06 STA $0678 = EF A:AA X:33 Y:BC P:A5 SP:FB CYC:271 SL:37 +D638 20 04 F8 JSR $F804 A:AA X:33 Y:BC P:A5 SP:FB CYC:283 SL:37 +F804 18 CLC A:AA X:33 Y:BC P:A5 SP:F9 CYC:301 SL:37 +F805 24 01 BIT $01 = FF A:AA X:33 Y:BC P:A4 SP:F9 CYC:307 SL:37 +F807 A9 5F LDA #$5F A:AA X:33 Y:BC P:E4 SP:F9 CYC:316 SL:37 +F809 60 RTS A:5F X:33 Y:BC P:64 SP:F9 CYC:322 SL:37 +D63B 4D 78 06 EOR $0678 = AA A:5F X:33 Y:BC P:64 SP:FB CYC:340 SL:37 +D63E 20 0A F8 JSR $F80A A:F5 X:33 Y:BC P:E4 SP:FB CYC: 11 SL:38 +F80A B0 09 BCS $F815 A:F5 X:33 Y:BC P:E4 SP:F9 CYC: 29 SL:38 +F80C 10 07 BPL $F815 A:F5 X:33 Y:BC P:E4 SP:F9 CYC: 35 SL:38 +F80E C9 F5 CMP #$F5 A:F5 X:33 Y:BC P:E4 SP:F9 CYC: 41 SL:38 +F810 D0 03 BNE $F815 A:F5 X:33 Y:BC P:67 SP:F9 CYC: 47 SL:38 +F812 50 01 BVC $F815 A:F5 X:33 Y:BC P:67 SP:F9 CYC: 53 SL:38 +F814 60 RTS A:F5 X:33 Y:BC P:67 SP:F9 CYC: 59 SL:38 +D641 C8 INY A:F5 X:33 Y:BC P:67 SP:FB CYC: 77 SL:38 +D642 A9 70 LDA #$70 A:F5 X:33 Y:BD P:E5 SP:FB CYC: 83 SL:38 +D644 8D 78 06 STA $0678 = AA A:70 X:33 Y:BD P:65 SP:FB CYC: 89 SL:38 +D647 20 18 F8 JSR $F818 A:70 X:33 Y:BD P:65 SP:FB CYC:101 SL:38 +F818 38 SEC A:70 X:33 Y:BD P:65 SP:F9 CYC:119 SL:38 +F819 B8 CLV A:70 X:33 Y:BD P:65 SP:F9 CYC:125 SL:38 +F81A A9 70 LDA #$70 A:70 X:33 Y:BD P:25 SP:F9 CYC:131 SL:38 +F81C 60 RTS A:70 X:33 Y:BD P:25 SP:F9 CYC:137 SL:38 +D64A 4D 78 06 EOR $0678 = 70 A:70 X:33 Y:BD P:25 SP:FB CYC:155 SL:38 +D64D 20 1D F8 JSR $F81D A:00 X:33 Y:BD P:27 SP:FB CYC:167 SL:38 +F81D D0 07 BNE $F826 A:00 X:33 Y:BD P:27 SP:F9 CYC:185 SL:38 +F81F 70 05 BVS $F826 A:00 X:33 Y:BD P:27 SP:F9 CYC:191 SL:38 +F821 90 03 BCC $F826 A:00 X:33 Y:BD P:27 SP:F9 CYC:197 SL:38 +F823 30 01 BMI $F826 A:00 X:33 Y:BD P:27 SP:F9 CYC:203 SL:38 +F825 60 RTS A:00 X:33 Y:BD P:27 SP:F9 CYC:209 SL:38 +D650 C8 INY A:00 X:33 Y:BD P:27 SP:FB CYC:227 SL:38 +D651 A9 69 LDA #$69 A:00 X:33 Y:BE P:A5 SP:FB CYC:233 SL:38 +D653 8D 78 06 STA $0678 = 70 A:69 X:33 Y:BE P:25 SP:FB CYC:239 SL:38 +D656 20 29 F8 JSR $F829 A:69 X:33 Y:BE P:25 SP:FB CYC:251 SL:38 +F829 18 CLC A:69 X:33 Y:BE P:25 SP:F9 CYC:269 SL:38 +F82A 24 01 BIT $01 = FF A:69 X:33 Y:BE P:24 SP:F9 CYC:275 SL:38 +F82C A9 00 LDA #$00 A:69 X:33 Y:BE P:E4 SP:F9 CYC:284 SL:38 +F82E 60 RTS A:00 X:33 Y:BE P:66 SP:F9 CYC:290 SL:38 +D659 6D 78 06 ADC $0678 = 69 A:00 X:33 Y:BE P:66 SP:FB CYC:308 SL:38 +D65C 20 2F F8 JSR $F82F A:69 X:33 Y:BE P:24 SP:FB CYC:320 SL:38 +F82F 30 09 BMI $F83A A:69 X:33 Y:BE P:24 SP:F9 CYC:338 SL:38 +F831 B0 07 BCS $F83A A:69 X:33 Y:BE P:24 SP:F9 CYC: 3 SL:39 +F833 C9 69 CMP #$69 A:69 X:33 Y:BE P:24 SP:F9 CYC: 9 SL:39 +F835 D0 03 BNE $F83A A:69 X:33 Y:BE P:27 SP:F9 CYC: 15 SL:39 +F837 70 01 BVS $F83A A:69 X:33 Y:BE P:27 SP:F9 CYC: 21 SL:39 +F839 60 RTS A:69 X:33 Y:BE P:27 SP:F9 CYC: 27 SL:39 +D65F C8 INY A:69 X:33 Y:BE P:27 SP:FB CYC: 45 SL:39 +D660 20 3D F8 JSR $F83D A:69 X:33 Y:BF P:A5 SP:FB CYC: 51 SL:39 +F83D 38 SEC A:69 X:33 Y:BF P:A5 SP:F9 CYC: 69 SL:39 +F83E 24 01 BIT $01 = FF A:69 X:33 Y:BF P:A5 SP:F9 CYC: 75 SL:39 +F840 A9 00 LDA #$00 A:69 X:33 Y:BF P:E5 SP:F9 CYC: 84 SL:39 +F842 60 RTS A:00 X:33 Y:BF P:67 SP:F9 CYC: 90 SL:39 +D663 6D 78 06 ADC $0678 = 69 A:00 X:33 Y:BF P:67 SP:FB CYC:108 SL:39 +D666 20 43 F8 JSR $F843 A:6A X:33 Y:BF P:24 SP:FB CYC:120 SL:39 +F843 30 09 BMI $F84E A:6A X:33 Y:BF P:24 SP:F9 CYC:138 SL:39 +F845 B0 07 BCS $F84E A:6A X:33 Y:BF P:24 SP:F9 CYC:144 SL:39 +F847 C9 6A CMP #$6A A:6A X:33 Y:BF P:24 SP:F9 CYC:150 SL:39 +F849 D0 03 BNE $F84E A:6A X:33 Y:BF P:27 SP:F9 CYC:156 SL:39 +F84B 70 01 BVS $F84E A:6A X:33 Y:BF P:27 SP:F9 CYC:162 SL:39 +F84D 60 RTS A:6A X:33 Y:BF P:27 SP:F9 CYC:168 SL:39 +D669 C8 INY A:6A X:33 Y:BF P:27 SP:FB CYC:186 SL:39 +D66A A9 7F LDA #$7F A:6A X:33 Y:C0 P:A5 SP:FB CYC:192 SL:39 +D66C 8D 78 06 STA $0678 = 69 A:7F X:33 Y:C0 P:25 SP:FB CYC:198 SL:39 +D66F 20 51 F8 JSR $F851 A:7F X:33 Y:C0 P:25 SP:FB CYC:210 SL:39 +F851 38 SEC A:7F X:33 Y:C0 P:25 SP:F9 CYC:228 SL:39 +F852 B8 CLV A:7F X:33 Y:C0 P:25 SP:F9 CYC:234 SL:39 +F853 A9 7F LDA #$7F A:7F X:33 Y:C0 P:25 SP:F9 CYC:240 SL:39 +F855 60 RTS A:7F X:33 Y:C0 P:25 SP:F9 CYC:246 SL:39 +D672 6D 78 06 ADC $0678 = 7F A:7F X:33 Y:C0 P:25 SP:FB CYC:264 SL:39 +D675 20 56 F8 JSR $F856 A:FF X:33 Y:C0 P:E4 SP:FB CYC:276 SL:39 +F856 10 09 BPL $F861 A:FF X:33 Y:C0 P:E4 SP:F9 CYC:294 SL:39 +F858 B0 07 BCS $F861 A:FF X:33 Y:C0 P:E4 SP:F9 CYC:300 SL:39 +F85A C9 FF CMP #$FF A:FF X:33 Y:C0 P:E4 SP:F9 CYC:306 SL:39 +F85C D0 03 BNE $F861 A:FF X:33 Y:C0 P:67 SP:F9 CYC:312 SL:39 +F85E 50 01 BVC $F861 A:FF X:33 Y:C0 P:67 SP:F9 CYC:318 SL:39 +F860 60 RTS A:FF X:33 Y:C0 P:67 SP:F9 CYC:324 SL:39 +D678 C8 INY A:FF X:33 Y:C0 P:67 SP:FB CYC: 1 SL:40 +D679 A9 80 LDA #$80 A:FF X:33 Y:C1 P:E5 SP:FB CYC: 7 SL:40 +D67B 8D 78 06 STA $0678 = 7F A:80 X:33 Y:C1 P:E5 SP:FB CYC: 13 SL:40 +D67E 20 64 F8 JSR $F864 A:80 X:33 Y:C1 P:E5 SP:FB CYC: 25 SL:40 +F864 18 CLC A:80 X:33 Y:C1 P:E5 SP:F9 CYC: 43 SL:40 +F865 24 01 BIT $01 = FF A:80 X:33 Y:C1 P:E4 SP:F9 CYC: 49 SL:40 +F867 A9 7F LDA #$7F A:80 X:33 Y:C1 P:E4 SP:F9 CYC: 58 SL:40 +F869 60 RTS A:7F X:33 Y:C1 P:64 SP:F9 CYC: 64 SL:40 +D681 6D 78 06 ADC $0678 = 80 A:7F X:33 Y:C1 P:64 SP:FB CYC: 82 SL:40 +D684 20 6A F8 JSR $F86A A:FF X:33 Y:C1 P:A4 SP:FB CYC: 94 SL:40 +F86A 10 09 BPL $F875 A:FF X:33 Y:C1 P:A4 SP:F9 CYC:112 SL:40 +F86C B0 07 BCS $F875 A:FF X:33 Y:C1 P:A4 SP:F9 CYC:118 SL:40 +F86E C9 FF CMP #$FF A:FF X:33 Y:C1 P:A4 SP:F9 CYC:124 SL:40 +F870 D0 03 BNE $F875 A:FF X:33 Y:C1 P:27 SP:F9 CYC:130 SL:40 +F872 70 01 BVS $F875 A:FF X:33 Y:C1 P:27 SP:F9 CYC:136 SL:40 +F874 60 RTS A:FF X:33 Y:C1 P:27 SP:F9 CYC:142 SL:40 +D687 C8 INY A:FF X:33 Y:C1 P:27 SP:FB CYC:160 SL:40 +D688 20 78 F8 JSR $F878 A:FF X:33 Y:C2 P:A5 SP:FB CYC:166 SL:40 +F878 38 SEC A:FF X:33 Y:C2 P:A5 SP:F9 CYC:184 SL:40 +F879 B8 CLV A:FF X:33 Y:C2 P:A5 SP:F9 CYC:190 SL:40 +F87A A9 7F LDA #$7F A:FF X:33 Y:C2 P:A5 SP:F9 CYC:196 SL:40 +F87C 60 RTS A:7F X:33 Y:C2 P:25 SP:F9 CYC:202 SL:40 +D68B 6D 78 06 ADC $0678 = 80 A:7F X:33 Y:C2 P:25 SP:FB CYC:220 SL:40 +D68E 20 7D F8 JSR $F87D A:00 X:33 Y:C2 P:27 SP:FB CYC:232 SL:40 +F87D D0 07 BNE $F886 A:00 X:33 Y:C2 P:27 SP:F9 CYC:250 SL:40 +F87F 30 05 BMI $F886 A:00 X:33 Y:C2 P:27 SP:F9 CYC:256 SL:40 +F881 70 03 BVS $F886 A:00 X:33 Y:C2 P:27 SP:F9 CYC:262 SL:40 +F883 90 01 BCC $F886 A:00 X:33 Y:C2 P:27 SP:F9 CYC:268 SL:40 +F885 60 RTS A:00 X:33 Y:C2 P:27 SP:F9 CYC:274 SL:40 +D691 C8 INY A:00 X:33 Y:C2 P:27 SP:FB CYC:292 SL:40 +D692 A9 40 LDA #$40 A:00 X:33 Y:C3 P:A5 SP:FB CYC:298 SL:40 +D694 8D 78 06 STA $0678 = 80 A:40 X:33 Y:C3 P:25 SP:FB CYC:304 SL:40 +D697 20 89 F8 JSR $F889 A:40 X:33 Y:C3 P:25 SP:FB CYC:316 SL:40 +F889 24 01 BIT $01 = FF A:40 X:33 Y:C3 P:25 SP:F9 CYC:334 SL:40 +F88B A9 40 LDA #$40 A:40 X:33 Y:C3 P:E5 SP:F9 CYC: 2 SL:41 +F88D 60 RTS A:40 X:33 Y:C3 P:65 SP:F9 CYC: 8 SL:41 +D69A CD 78 06 CMP $0678 = 40 A:40 X:33 Y:C3 P:65 SP:FB CYC: 26 SL:41 +D69D 20 8E F8 JSR $F88E A:40 X:33 Y:C3 P:67 SP:FB CYC: 38 SL:41 +F88E 30 07 BMI $F897 A:40 X:33 Y:C3 P:67 SP:F9 CYC: 56 SL:41 +F890 90 05 BCC $F897 A:40 X:33 Y:C3 P:67 SP:F9 CYC: 62 SL:41 +F892 D0 03 BNE $F897 A:40 X:33 Y:C3 P:67 SP:F9 CYC: 68 SL:41 +F894 50 01 BVC $F897 A:40 X:33 Y:C3 P:67 SP:F9 CYC: 74 SL:41 +F896 60 RTS A:40 X:33 Y:C3 P:67 SP:F9 CYC: 80 SL:41 +D6A0 C8 INY A:40 X:33 Y:C3 P:67 SP:FB CYC: 98 SL:41 +D6A1 48 PHA A:40 X:33 Y:C4 P:E5 SP:FB CYC:104 SL:41 +D6A2 A9 3F LDA #$3F A:40 X:33 Y:C4 P:E5 SP:FA CYC:113 SL:41 +D6A4 8D 78 06 STA $0678 = 40 A:3F X:33 Y:C4 P:65 SP:FA CYC:119 SL:41 +D6A7 68 PLA A:3F X:33 Y:C4 P:65 SP:FA CYC:131 SL:41 +D6A8 20 9A F8 JSR $F89A A:40 X:33 Y:C4 P:65 SP:FB CYC:143 SL:41 +F89A B8 CLV A:40 X:33 Y:C4 P:65 SP:F9 CYC:161 SL:41 +F89B 60 RTS A:40 X:33 Y:C4 P:25 SP:F9 CYC:167 SL:41 +D6AB CD 78 06 CMP $0678 = 3F A:40 X:33 Y:C4 P:25 SP:FB CYC:185 SL:41 +D6AE 20 9C F8 JSR $F89C A:40 X:33 Y:C4 P:25 SP:FB CYC:197 SL:41 +F89C F0 07 BEQ $F8A5 A:40 X:33 Y:C4 P:25 SP:F9 CYC:215 SL:41 +F89E 30 05 BMI $F8A5 A:40 X:33 Y:C4 P:25 SP:F9 CYC:221 SL:41 +F8A0 90 03 BCC $F8A5 A:40 X:33 Y:C4 P:25 SP:F9 CYC:227 SL:41 +F8A2 70 01 BVS $F8A5 A:40 X:33 Y:C4 P:25 SP:F9 CYC:233 SL:41 +F8A4 60 RTS A:40 X:33 Y:C4 P:25 SP:F9 CYC:239 SL:41 +D6B1 C8 INY A:40 X:33 Y:C4 P:25 SP:FB CYC:257 SL:41 +D6B2 48 PHA A:40 X:33 Y:C5 P:A5 SP:FB CYC:263 SL:41 +D6B3 A9 41 LDA #$41 A:40 X:33 Y:C5 P:A5 SP:FA CYC:272 SL:41 +D6B5 8D 78 06 STA $0678 = 3F A:41 X:33 Y:C5 P:25 SP:FA CYC:278 SL:41 +D6B8 68 PLA A:41 X:33 Y:C5 P:25 SP:FA CYC:290 SL:41 +D6B9 CD 78 06 CMP $0678 = 41 A:40 X:33 Y:C5 P:25 SP:FB CYC:302 SL:41 +D6BC 20 A8 F8 JSR $F8A8 A:40 X:33 Y:C5 P:A4 SP:FB CYC:314 SL:41 +F8A8 F0 05 BEQ $F8AF A:40 X:33 Y:C5 P:A4 SP:F9 CYC:332 SL:41 +F8AA 10 03 BPL $F8AF A:40 X:33 Y:C5 P:A4 SP:F9 CYC:338 SL:41 +F8AC 10 01 BPL $F8AF A:40 X:33 Y:C5 P:A4 SP:F9 CYC: 3 SL:42 +F8AE 60 RTS A:40 X:33 Y:C5 P:A4 SP:F9 CYC: 9 SL:42 +D6BF C8 INY A:40 X:33 Y:C5 P:A4 SP:FB CYC: 27 SL:42 +D6C0 48 PHA A:40 X:33 Y:C6 P:A4 SP:FB CYC: 33 SL:42 +D6C1 A9 00 LDA #$00 A:40 X:33 Y:C6 P:A4 SP:FA CYC: 42 SL:42 +D6C3 8D 78 06 STA $0678 = 41 A:00 X:33 Y:C6 P:26 SP:FA CYC: 48 SL:42 +D6C6 68 PLA A:00 X:33 Y:C6 P:26 SP:FA CYC: 60 SL:42 +D6C7 20 B2 F8 JSR $F8B2 A:40 X:33 Y:C6 P:24 SP:FB CYC: 72 SL:42 +F8B2 A9 80 LDA #$80 A:40 X:33 Y:C6 P:24 SP:F9 CYC: 90 SL:42 +F8B4 60 RTS A:80 X:33 Y:C6 P:A4 SP:F9 CYC: 96 SL:42 +D6CA CD 78 06 CMP $0678 = 00 A:80 X:33 Y:C6 P:A4 SP:FB CYC:114 SL:42 +D6CD 20 B5 F8 JSR $F8B5 A:80 X:33 Y:C6 P:A5 SP:FB CYC:126 SL:42 +F8B5 F0 05 BEQ $F8BC A:80 X:33 Y:C6 P:A5 SP:F9 CYC:144 SL:42 +F8B7 10 03 BPL $F8BC A:80 X:33 Y:C6 P:A5 SP:F9 CYC:150 SL:42 +F8B9 90 01 BCC $F8BC A:80 X:33 Y:C6 P:A5 SP:F9 CYC:156 SL:42 +F8BB 60 RTS A:80 X:33 Y:C6 P:A5 SP:F9 CYC:162 SL:42 +D6D0 C8 INY A:80 X:33 Y:C6 P:A5 SP:FB CYC:180 SL:42 +D6D1 48 PHA A:80 X:33 Y:C7 P:A5 SP:FB CYC:186 SL:42 +D6D2 A9 80 LDA #$80 A:80 X:33 Y:C7 P:A5 SP:FA CYC:195 SL:42 +D6D4 8D 78 06 STA $0678 = 00 A:80 X:33 Y:C7 P:A5 SP:FA CYC:201 SL:42 +D6D7 68 PLA A:80 X:33 Y:C7 P:A5 SP:FA CYC:213 SL:42 +D6D8 CD 78 06 CMP $0678 = 80 A:80 X:33 Y:C7 P:A5 SP:FB CYC:225 SL:42 +D6DB 20 BF F8 JSR $F8BF A:80 X:33 Y:C7 P:27 SP:FB CYC:237 SL:42 +F8BF D0 05 BNE $F8C6 A:80 X:33 Y:C7 P:27 SP:F9 CYC:255 SL:42 +F8C1 30 03 BMI $F8C6 A:80 X:33 Y:C7 P:27 SP:F9 CYC:261 SL:42 +F8C3 90 01 BCC $F8C6 A:80 X:33 Y:C7 P:27 SP:F9 CYC:267 SL:42 +F8C5 60 RTS A:80 X:33 Y:C7 P:27 SP:F9 CYC:273 SL:42 +D6DE C8 INY A:80 X:33 Y:C7 P:27 SP:FB CYC:291 SL:42 +D6DF 48 PHA A:80 X:33 Y:C8 P:A5 SP:FB CYC:297 SL:42 +D6E0 A9 81 LDA #$81 A:80 X:33 Y:C8 P:A5 SP:FA CYC:306 SL:42 +D6E2 8D 78 06 STA $0678 = 80 A:81 X:33 Y:C8 P:A5 SP:FA CYC:312 SL:42 +D6E5 68 PLA A:81 X:33 Y:C8 P:A5 SP:FA CYC:324 SL:42 +D6E6 CD 78 06 CMP $0678 = 81 A:80 X:33 Y:C8 P:A5 SP:FB CYC:336 SL:42 +D6E9 20 C9 F8 JSR $F8C9 A:80 X:33 Y:C8 P:A4 SP:FB CYC: 7 SL:43 +F8C9 B0 05 BCS $F8D0 A:80 X:33 Y:C8 P:A4 SP:F9 CYC: 25 SL:43 +F8CB F0 03 BEQ $F8D0 A:80 X:33 Y:C8 P:A4 SP:F9 CYC: 31 SL:43 +F8CD 10 01 BPL $F8D0 A:80 X:33 Y:C8 P:A4 SP:F9 CYC: 37 SL:43 +F8CF 60 RTS A:80 X:33 Y:C8 P:A4 SP:F9 CYC: 43 SL:43 +D6EC C8 INY A:80 X:33 Y:C8 P:A4 SP:FB CYC: 61 SL:43 +D6ED 48 PHA A:80 X:33 Y:C9 P:A4 SP:FB CYC: 67 SL:43 +D6EE A9 7F LDA #$7F A:80 X:33 Y:C9 P:A4 SP:FA CYC: 76 SL:43 +D6F0 8D 78 06 STA $0678 = 81 A:7F X:33 Y:C9 P:24 SP:FA CYC: 82 SL:43 +D6F3 68 PLA A:7F X:33 Y:C9 P:24 SP:FA CYC: 94 SL:43 +D6F4 CD 78 06 CMP $0678 = 7F A:80 X:33 Y:C9 P:A4 SP:FB CYC:106 SL:43 +D6F7 20 D3 F8 JSR $F8D3 A:80 X:33 Y:C9 P:25 SP:FB CYC:118 SL:43 +F8D3 90 05 BCC $F8DA A:80 X:33 Y:C9 P:25 SP:F9 CYC:136 SL:43 +F8D5 F0 03 BEQ $F8DA A:80 X:33 Y:C9 P:25 SP:F9 CYC:142 SL:43 +F8D7 30 01 BMI $F8DA A:80 X:33 Y:C9 P:25 SP:F9 CYC:148 SL:43 +F8D9 60 RTS A:80 X:33 Y:C9 P:25 SP:F9 CYC:154 SL:43 +D6FA C8 INY A:80 X:33 Y:C9 P:25 SP:FB CYC:172 SL:43 +D6FB A9 40 LDA #$40 A:80 X:33 Y:CA P:A5 SP:FB CYC:178 SL:43 +D6FD 8D 78 06 STA $0678 = 7F A:40 X:33 Y:CA P:25 SP:FB CYC:184 SL:43 +D700 20 31 F9 JSR $F931 A:40 X:33 Y:CA P:25 SP:FB CYC:196 SL:43 +F931 24 01 BIT $01 = FF A:40 X:33 Y:CA P:25 SP:F9 CYC:214 SL:43 +F933 A9 40 LDA #$40 A:40 X:33 Y:CA P:E5 SP:F9 CYC:223 SL:43 +F935 38 SEC A:40 X:33 Y:CA P:65 SP:F9 CYC:229 SL:43 +F936 60 RTS A:40 X:33 Y:CA P:65 SP:F9 CYC:235 SL:43 +D703 ED 78 06 SBC $0678 = 40 A:40 X:33 Y:CA P:65 SP:FB CYC:253 SL:43 +D706 20 37 F9 JSR $F937 A:00 X:33 Y:CA P:27 SP:FB CYC:265 SL:43 +F937 30 0B BMI $F944 A:00 X:33 Y:CA P:27 SP:F9 CYC:283 SL:43 +F939 90 09 BCC $F944 A:00 X:33 Y:CA P:27 SP:F9 CYC:289 SL:43 +F93B D0 07 BNE $F944 A:00 X:33 Y:CA P:27 SP:F9 CYC:295 SL:43 +F93D 70 05 BVS $F944 A:00 X:33 Y:CA P:27 SP:F9 CYC:301 SL:43 +F93F C9 00 CMP #$00 A:00 X:33 Y:CA P:27 SP:F9 CYC:307 SL:43 +F941 D0 01 BNE $F944 A:00 X:33 Y:CA P:27 SP:F9 CYC:313 SL:43 +F943 60 RTS A:00 X:33 Y:CA P:27 SP:F9 CYC:319 SL:43 +D709 C8 INY A:00 X:33 Y:CA P:27 SP:FB CYC:337 SL:43 +D70A A9 3F LDA #$3F A:00 X:33 Y:CB P:A5 SP:FB CYC: 2 SL:44 +D70C 8D 78 06 STA $0678 = 40 A:3F X:33 Y:CB P:25 SP:FB CYC: 8 SL:44 +D70F 20 47 F9 JSR $F947 A:3F X:33 Y:CB P:25 SP:FB CYC: 20 SL:44 +F947 B8 CLV A:3F X:33 Y:CB P:25 SP:F9 CYC: 38 SL:44 +F948 38 SEC A:3F X:33 Y:CB P:25 SP:F9 CYC: 44 SL:44 +F949 A9 40 LDA #$40 A:3F X:33 Y:CB P:25 SP:F9 CYC: 50 SL:44 +F94B 60 RTS A:40 X:33 Y:CB P:25 SP:F9 CYC: 56 SL:44 +D712 ED 78 06 SBC $0678 = 3F A:40 X:33 Y:CB P:25 SP:FB CYC: 74 SL:44 +D715 20 4C F9 JSR $F94C A:01 X:33 Y:CB P:25 SP:FB CYC: 86 SL:44 +F94C F0 0B BEQ $F959 A:01 X:33 Y:CB P:25 SP:F9 CYC:104 SL:44 +F94E 30 09 BMI $F959 A:01 X:33 Y:CB P:25 SP:F9 CYC:110 SL:44 +F950 90 07 BCC $F959 A:01 X:33 Y:CB P:25 SP:F9 CYC:116 SL:44 +F952 70 05 BVS $F959 A:01 X:33 Y:CB P:25 SP:F9 CYC:122 SL:44 +F954 C9 01 CMP #$01 A:01 X:33 Y:CB P:25 SP:F9 CYC:128 SL:44 +F956 D0 01 BNE $F959 A:01 X:33 Y:CB P:27 SP:F9 CYC:134 SL:44 +F958 60 RTS A:01 X:33 Y:CB P:27 SP:F9 CYC:140 SL:44 +D718 C8 INY A:01 X:33 Y:CB P:27 SP:FB CYC:158 SL:44 +D719 A9 41 LDA #$41 A:01 X:33 Y:CC P:A5 SP:FB CYC:164 SL:44 +D71B 8D 78 06 STA $0678 = 3F A:41 X:33 Y:CC P:25 SP:FB CYC:170 SL:44 +D71E 20 5C F9 JSR $F95C A:41 X:33 Y:CC P:25 SP:FB CYC:182 SL:44 +F95C A9 40 LDA #$40 A:41 X:33 Y:CC P:25 SP:F9 CYC:200 SL:44 +F95E 38 SEC A:40 X:33 Y:CC P:25 SP:F9 CYC:206 SL:44 +F95F 24 01 BIT $01 = FF A:40 X:33 Y:CC P:25 SP:F9 CYC:212 SL:44 +F961 60 RTS A:40 X:33 Y:CC P:E5 SP:F9 CYC:221 SL:44 +D721 ED 78 06 SBC $0678 = 41 A:40 X:33 Y:CC P:E5 SP:FB CYC:239 SL:44 +D724 20 62 F9 JSR $F962 A:FF X:33 Y:CC P:A4 SP:FB CYC:251 SL:44 +F962 B0 0B BCS $F96F A:FF X:33 Y:CC P:A4 SP:F9 CYC:269 SL:44 +F964 F0 09 BEQ $F96F A:FF X:33 Y:CC P:A4 SP:F9 CYC:275 SL:44 +F966 10 07 BPL $F96F A:FF X:33 Y:CC P:A4 SP:F9 CYC:281 SL:44 +F968 70 05 BVS $F96F A:FF X:33 Y:CC P:A4 SP:F9 CYC:287 SL:44 +F96A C9 FF CMP #$FF A:FF X:33 Y:CC P:A4 SP:F9 CYC:293 SL:44 +F96C D0 01 BNE $F96F A:FF X:33 Y:CC P:27 SP:F9 CYC:299 SL:44 +F96E 60 RTS A:FF X:33 Y:CC P:27 SP:F9 CYC:305 SL:44 +D727 C8 INY A:FF X:33 Y:CC P:27 SP:FB CYC:323 SL:44 +D728 A9 00 LDA #$00 A:FF X:33 Y:CD P:A5 SP:FB CYC:329 SL:44 +D72A 8D 78 06 STA $0678 = 41 A:00 X:33 Y:CD P:27 SP:FB CYC:335 SL:44 +D72D 20 72 F9 JSR $F972 A:00 X:33 Y:CD P:27 SP:FB CYC: 6 SL:45 +F972 18 CLC A:00 X:33 Y:CD P:27 SP:F9 CYC: 24 SL:45 +F973 A9 80 LDA #$80 A:00 X:33 Y:CD P:26 SP:F9 CYC: 30 SL:45 +F975 60 RTS A:80 X:33 Y:CD P:A4 SP:F9 CYC: 36 SL:45 +D730 ED 78 06 SBC $0678 = 00 A:80 X:33 Y:CD P:A4 SP:FB CYC: 54 SL:45 +D733 20 76 F9 JSR $F976 A:7F X:33 Y:CD P:65 SP:FB CYC: 66 SL:45 +F976 90 05 BCC $F97D A:7F X:33 Y:CD P:65 SP:F9 CYC: 84 SL:45 +F978 C9 7F CMP #$7F A:7F X:33 Y:CD P:65 SP:F9 CYC: 90 SL:45 +F97A D0 01 BNE $F97D A:7F X:33 Y:CD P:67 SP:F9 CYC: 96 SL:45 +F97C 60 RTS A:7F X:33 Y:CD P:67 SP:F9 CYC:102 SL:45 +D736 C8 INY A:7F X:33 Y:CD P:67 SP:FB CYC:120 SL:45 +D737 A9 7F LDA #$7F A:7F X:33 Y:CE P:E5 SP:FB CYC:126 SL:45 +D739 8D 78 06 STA $0678 = 00 A:7F X:33 Y:CE P:65 SP:FB CYC:132 SL:45 +D73C 20 80 F9 JSR $F980 A:7F X:33 Y:CE P:65 SP:FB CYC:144 SL:45 +F980 38 SEC A:7F X:33 Y:CE P:65 SP:F9 CYC:162 SL:45 +F981 A9 81 LDA #$81 A:7F X:33 Y:CE P:65 SP:F9 CYC:168 SL:45 +F983 60 RTS A:81 X:33 Y:CE P:E5 SP:F9 CYC:174 SL:45 +D73F ED 78 06 SBC $0678 = 7F A:81 X:33 Y:CE P:E5 SP:FB CYC:192 SL:45 +D742 20 84 F9 JSR $F984 A:02 X:33 Y:CE P:65 SP:FB CYC:204 SL:45 +F984 50 07 BVC $F98D A:02 X:33 Y:CE P:65 SP:F9 CYC:222 SL:45 +F986 90 05 BCC $F98D A:02 X:33 Y:CE P:65 SP:F9 CYC:228 SL:45 +F988 C9 02 CMP #$02 A:02 X:33 Y:CE P:65 SP:F9 CYC:234 SL:45 +F98A D0 01 BNE $F98D A:02 X:33 Y:CE P:67 SP:F9 CYC:240 SL:45 +F98C 60 RTS A:02 X:33 Y:CE P:67 SP:F9 CYC:246 SL:45 +D745 C8 INY A:02 X:33 Y:CE P:67 SP:FB CYC:264 SL:45 +D746 A9 40 LDA #$40 A:02 X:33 Y:CF P:E5 SP:FB CYC:270 SL:45 +D748 8D 78 06 STA $0678 = 7F A:40 X:33 Y:CF P:65 SP:FB CYC:276 SL:45 +D74B 20 89 F8 JSR $F889 A:40 X:33 Y:CF P:65 SP:FB CYC:288 SL:45 +F889 24 01 BIT $01 = FF A:40 X:33 Y:CF P:65 SP:F9 CYC:306 SL:45 +F88B A9 40 LDA #$40 A:40 X:33 Y:CF P:E5 SP:F9 CYC:315 SL:45 +F88D 60 RTS A:40 X:33 Y:CF P:65 SP:F9 CYC:321 SL:45 +D74E AA TAX A:40 X:33 Y:CF P:65 SP:FB CYC:339 SL:45 +D74F EC 78 06 CPX $0678 = 40 A:40 X:40 Y:CF P:65 SP:FB CYC: 4 SL:46 +D752 20 8E F8 JSR $F88E A:40 X:40 Y:CF P:67 SP:FB CYC: 16 SL:46 +F88E 30 07 BMI $F897 A:40 X:40 Y:CF P:67 SP:F9 CYC: 34 SL:46 +F890 90 05 BCC $F897 A:40 X:40 Y:CF P:67 SP:F9 CYC: 40 SL:46 +F892 D0 03 BNE $F897 A:40 X:40 Y:CF P:67 SP:F9 CYC: 46 SL:46 +F894 50 01 BVC $F897 A:40 X:40 Y:CF P:67 SP:F9 CYC: 52 SL:46 +F896 60 RTS A:40 X:40 Y:CF P:67 SP:F9 CYC: 58 SL:46 +D755 C8 INY A:40 X:40 Y:CF P:67 SP:FB CYC: 76 SL:46 +D756 A9 3F LDA #$3F A:40 X:40 Y:D0 P:E5 SP:FB CYC: 82 SL:46 +D758 8D 78 06 STA $0678 = 40 A:3F X:40 Y:D0 P:65 SP:FB CYC: 88 SL:46 +D75B 20 9A F8 JSR $F89A A:3F X:40 Y:D0 P:65 SP:FB CYC:100 SL:46 +F89A B8 CLV A:3F X:40 Y:D0 P:65 SP:F9 CYC:118 SL:46 +F89B 60 RTS A:3F X:40 Y:D0 P:25 SP:F9 CYC:124 SL:46 +D75E EC 78 06 CPX $0678 = 3F A:3F X:40 Y:D0 P:25 SP:FB CYC:142 SL:46 +D761 20 9C F8 JSR $F89C A:3F X:40 Y:D0 P:25 SP:FB CYC:154 SL:46 +F89C F0 07 BEQ $F8A5 A:3F X:40 Y:D0 P:25 SP:F9 CYC:172 SL:46 +F89E 30 05 BMI $F8A5 A:3F X:40 Y:D0 P:25 SP:F9 CYC:178 SL:46 +F8A0 90 03 BCC $F8A5 A:3F X:40 Y:D0 P:25 SP:F9 CYC:184 SL:46 +F8A2 70 01 BVS $F8A5 A:3F X:40 Y:D0 P:25 SP:F9 CYC:190 SL:46 +F8A4 60 RTS A:3F X:40 Y:D0 P:25 SP:F9 CYC:196 SL:46 +D764 C8 INY A:3F X:40 Y:D0 P:25 SP:FB CYC:214 SL:46 +D765 A9 41 LDA #$41 A:3F X:40 Y:D1 P:A5 SP:FB CYC:220 SL:46 +D767 8D 78 06 STA $0678 = 3F A:41 X:40 Y:D1 P:25 SP:FB CYC:226 SL:46 +D76A EC 78 06 CPX $0678 = 41 A:41 X:40 Y:D1 P:25 SP:FB CYC:238 SL:46 +D76D 20 A8 F8 JSR $F8A8 A:41 X:40 Y:D1 P:A4 SP:FB CYC:250 SL:46 +F8A8 F0 05 BEQ $F8AF A:41 X:40 Y:D1 P:A4 SP:F9 CYC:268 SL:46 +F8AA 10 03 BPL $F8AF A:41 X:40 Y:D1 P:A4 SP:F9 CYC:274 SL:46 +F8AC 10 01 BPL $F8AF A:41 X:40 Y:D1 P:A4 SP:F9 CYC:280 SL:46 +F8AE 60 RTS A:41 X:40 Y:D1 P:A4 SP:F9 CYC:286 SL:46 +D770 C8 INY A:41 X:40 Y:D1 P:A4 SP:FB CYC:304 SL:46 +D771 A9 00 LDA #$00 A:41 X:40 Y:D2 P:A4 SP:FB CYC:310 SL:46 +D773 8D 78 06 STA $0678 = 41 A:00 X:40 Y:D2 P:26 SP:FB CYC:316 SL:46 +D776 20 B2 F8 JSR $F8B2 A:00 X:40 Y:D2 P:26 SP:FB CYC:328 SL:46 +F8B2 A9 80 LDA #$80 A:00 X:40 Y:D2 P:26 SP:F9 CYC: 5 SL:47 +F8B4 60 RTS A:80 X:40 Y:D2 P:A4 SP:F9 CYC: 11 SL:47 +D779 AA TAX A:80 X:40 Y:D2 P:A4 SP:FB CYC: 29 SL:47 +D77A EC 78 06 CPX $0678 = 00 A:80 X:80 Y:D2 P:A4 SP:FB CYC: 35 SL:47 +D77D 20 B5 F8 JSR $F8B5 A:80 X:80 Y:D2 P:A5 SP:FB CYC: 47 SL:47 +F8B5 F0 05 BEQ $F8BC A:80 X:80 Y:D2 P:A5 SP:F9 CYC: 65 SL:47 +F8B7 10 03 BPL $F8BC A:80 X:80 Y:D2 P:A5 SP:F9 CYC: 71 SL:47 +F8B9 90 01 BCC $F8BC A:80 X:80 Y:D2 P:A5 SP:F9 CYC: 77 SL:47 +F8BB 60 RTS A:80 X:80 Y:D2 P:A5 SP:F9 CYC: 83 SL:47 +D780 C8 INY A:80 X:80 Y:D2 P:A5 SP:FB CYC:101 SL:47 +D781 A9 80 LDA #$80 A:80 X:80 Y:D3 P:A5 SP:FB CYC:107 SL:47 +D783 8D 78 06 STA $0678 = 00 A:80 X:80 Y:D3 P:A5 SP:FB CYC:113 SL:47 +D786 EC 78 06 CPX $0678 = 80 A:80 X:80 Y:D3 P:A5 SP:FB CYC:125 SL:47 +D789 20 BF F8 JSR $F8BF A:80 X:80 Y:D3 P:27 SP:FB CYC:137 SL:47 +F8BF D0 05 BNE $F8C6 A:80 X:80 Y:D3 P:27 SP:F9 CYC:155 SL:47 +F8C1 30 03 BMI $F8C6 A:80 X:80 Y:D3 P:27 SP:F9 CYC:161 SL:47 +F8C3 90 01 BCC $F8C6 A:80 X:80 Y:D3 P:27 SP:F9 CYC:167 SL:47 +F8C5 60 RTS A:80 X:80 Y:D3 P:27 SP:F9 CYC:173 SL:47 +D78C C8 INY A:80 X:80 Y:D3 P:27 SP:FB CYC:191 SL:47 +D78D A9 81 LDA #$81 A:80 X:80 Y:D4 P:A5 SP:FB CYC:197 SL:47 +D78F 8D 78 06 STA $0678 = 80 A:81 X:80 Y:D4 P:A5 SP:FB CYC:203 SL:47 +D792 EC 78 06 CPX $0678 = 81 A:81 X:80 Y:D4 P:A5 SP:FB CYC:215 SL:47 +D795 20 C9 F8 JSR $F8C9 A:81 X:80 Y:D4 P:A4 SP:FB CYC:227 SL:47 +F8C9 B0 05 BCS $F8D0 A:81 X:80 Y:D4 P:A4 SP:F9 CYC:245 SL:47 +F8CB F0 03 BEQ $F8D0 A:81 X:80 Y:D4 P:A4 SP:F9 CYC:251 SL:47 +F8CD 10 01 BPL $F8D0 A:81 X:80 Y:D4 P:A4 SP:F9 CYC:257 SL:47 +F8CF 60 RTS A:81 X:80 Y:D4 P:A4 SP:F9 CYC:263 SL:47 +D798 C8 INY A:81 X:80 Y:D4 P:A4 SP:FB CYC:281 SL:47 +D799 A9 7F LDA #$7F A:81 X:80 Y:D5 P:A4 SP:FB CYC:287 SL:47 +D79B 8D 78 06 STA $0678 = 81 A:7F X:80 Y:D5 P:24 SP:FB CYC:293 SL:47 +D79E EC 78 06 CPX $0678 = 7F A:7F X:80 Y:D5 P:24 SP:FB CYC:305 SL:47 +D7A1 20 D3 F8 JSR $F8D3 A:7F X:80 Y:D5 P:25 SP:FB CYC:317 SL:47 +F8D3 90 05 BCC $F8DA A:7F X:80 Y:D5 P:25 SP:F9 CYC:335 SL:47 +F8D5 F0 03 BEQ $F8DA A:7F X:80 Y:D5 P:25 SP:F9 CYC: 0 SL:48 +F8D7 30 01 BMI $F8DA A:7F X:80 Y:D5 P:25 SP:F9 CYC: 6 SL:48 +F8D9 60 RTS A:7F X:80 Y:D5 P:25 SP:F9 CYC: 12 SL:48 +D7A4 C8 INY A:7F X:80 Y:D5 P:25 SP:FB CYC: 30 SL:48 +D7A5 98 TYA A:7F X:80 Y:D6 P:A5 SP:FB CYC: 36 SL:48 +D7A6 AA TAX A:D6 X:80 Y:D6 P:A5 SP:FB CYC: 42 SL:48 +D7A7 A9 40 LDA #$40 A:D6 X:D6 Y:D6 P:A5 SP:FB CYC: 48 SL:48 +D7A9 8D 78 06 STA $0678 = 7F A:40 X:D6 Y:D6 P:25 SP:FB CYC: 54 SL:48 +D7AC 20 DD F8 JSR $F8DD A:40 X:D6 Y:D6 P:25 SP:FB CYC: 66 SL:48 +F8DD 24 01 BIT $01 = FF A:40 X:D6 Y:D6 P:25 SP:F9 CYC: 84 SL:48 +F8DF A0 40 LDY #$40 A:40 X:D6 Y:D6 P:E5 SP:F9 CYC: 93 SL:48 +F8E1 60 RTS A:40 X:D6 Y:40 P:65 SP:F9 CYC: 99 SL:48 +D7AF CC 78 06 CPY $0678 = 40 A:40 X:D6 Y:40 P:65 SP:FB CYC:117 SL:48 +D7B2 20 E2 F8 JSR $F8E2 A:40 X:D6 Y:40 P:67 SP:FB CYC:129 SL:48 +F8E2 30 07 BMI $F8EB A:40 X:D6 Y:40 P:67 SP:F9 CYC:147 SL:48 +F8E4 90 05 BCC $F8EB A:40 X:D6 Y:40 P:67 SP:F9 CYC:153 SL:48 +F8E6 D0 03 BNE $F8EB A:40 X:D6 Y:40 P:67 SP:F9 CYC:159 SL:48 +F8E8 50 01 BVC $F8EB A:40 X:D6 Y:40 P:67 SP:F9 CYC:165 SL:48 +F8EA 60 RTS A:40 X:D6 Y:40 P:67 SP:F9 CYC:171 SL:48 +D7B5 E8 INX A:40 X:D6 Y:40 P:67 SP:FB CYC:189 SL:48 +D7B6 A9 3F LDA #$3F A:40 X:D7 Y:40 P:E5 SP:FB CYC:195 SL:48 +D7B8 8D 78 06 STA $0678 = 40 A:3F X:D7 Y:40 P:65 SP:FB CYC:201 SL:48 +D7BB 20 EE F8 JSR $F8EE A:3F X:D7 Y:40 P:65 SP:FB CYC:213 SL:48 +F8EE B8 CLV A:3F X:D7 Y:40 P:65 SP:F9 CYC:231 SL:48 +F8EF 60 RTS A:3F X:D7 Y:40 P:25 SP:F9 CYC:237 SL:48 +D7BE CC 78 06 CPY $0678 = 3F A:3F X:D7 Y:40 P:25 SP:FB CYC:255 SL:48 +D7C1 20 F0 F8 JSR $F8F0 A:3F X:D7 Y:40 P:25 SP:FB CYC:267 SL:48 +F8F0 F0 07 BEQ $F8F9 A:3F X:D7 Y:40 P:25 SP:F9 CYC:285 SL:48 +F8F2 30 05 BMI $F8F9 A:3F X:D7 Y:40 P:25 SP:F9 CYC:291 SL:48 +F8F4 90 03 BCC $F8F9 A:3F X:D7 Y:40 P:25 SP:F9 CYC:297 SL:48 +F8F6 70 01 BVS $F8F9 A:3F X:D7 Y:40 P:25 SP:F9 CYC:303 SL:48 +F8F8 60 RTS A:3F X:D7 Y:40 P:25 SP:F9 CYC:309 SL:48 +D7C4 E8 INX A:3F X:D7 Y:40 P:25 SP:FB CYC:327 SL:48 +D7C5 A9 41 LDA #$41 A:3F X:D8 Y:40 P:A5 SP:FB CYC:333 SL:48 +D7C7 8D 78 06 STA $0678 = 3F A:41 X:D8 Y:40 P:25 SP:FB CYC:339 SL:48 +D7CA CC 78 06 CPY $0678 = 41 A:41 X:D8 Y:40 P:25 SP:FB CYC: 10 SL:49 +D7CD 20 FC F8 JSR $F8FC A:41 X:D8 Y:40 P:A4 SP:FB CYC: 22 SL:49 +F8FC F0 05 BEQ $F903 A:41 X:D8 Y:40 P:A4 SP:F9 CYC: 40 SL:49 +F8FE 10 03 BPL $F903 A:41 X:D8 Y:40 P:A4 SP:F9 CYC: 46 SL:49 +F900 10 01 BPL $F903 A:41 X:D8 Y:40 P:A4 SP:F9 CYC: 52 SL:49 +F902 60 RTS A:41 X:D8 Y:40 P:A4 SP:F9 CYC: 58 SL:49 +D7D0 E8 INX A:41 X:D8 Y:40 P:A4 SP:FB CYC: 76 SL:49 +D7D1 A9 00 LDA #$00 A:41 X:D9 Y:40 P:A4 SP:FB CYC: 82 SL:49 +D7D3 8D 78 06 STA $0678 = 41 A:00 X:D9 Y:40 P:26 SP:FB CYC: 88 SL:49 +D7D6 20 06 F9 JSR $F906 A:00 X:D9 Y:40 P:26 SP:FB CYC:100 SL:49 +F906 A0 80 LDY #$80 A:00 X:D9 Y:40 P:26 SP:F9 CYC:118 SL:49 +F908 60 RTS A:00 X:D9 Y:80 P:A4 SP:F9 CYC:124 SL:49 +D7D9 CC 78 06 CPY $0678 = 00 A:00 X:D9 Y:80 P:A4 SP:FB CYC:142 SL:49 +D7DC 20 09 F9 JSR $F909 A:00 X:D9 Y:80 P:A5 SP:FB CYC:154 SL:49 +F909 F0 05 BEQ $F910 A:00 X:D9 Y:80 P:A5 SP:F9 CYC:172 SL:49 +F90B 10 03 BPL $F910 A:00 X:D9 Y:80 P:A5 SP:F9 CYC:178 SL:49 +F90D 90 01 BCC $F910 A:00 X:D9 Y:80 P:A5 SP:F9 CYC:184 SL:49 +F90F 60 RTS A:00 X:D9 Y:80 P:A5 SP:F9 CYC:190 SL:49 +D7DF E8 INX A:00 X:D9 Y:80 P:A5 SP:FB CYC:208 SL:49 +D7E0 A9 80 LDA #$80 A:00 X:DA Y:80 P:A5 SP:FB CYC:214 SL:49 +D7E2 8D 78 06 STA $0678 = 00 A:80 X:DA Y:80 P:A5 SP:FB CYC:220 SL:49 +D7E5 CC 78 06 CPY $0678 = 80 A:80 X:DA Y:80 P:A5 SP:FB CYC:232 SL:49 +D7E8 20 13 F9 JSR $F913 A:80 X:DA Y:80 P:27 SP:FB CYC:244 SL:49 +F913 D0 05 BNE $F91A A:80 X:DA Y:80 P:27 SP:F9 CYC:262 SL:49 +F915 30 03 BMI $F91A A:80 X:DA Y:80 P:27 SP:F9 CYC:268 SL:49 +F917 90 01 BCC $F91A A:80 X:DA Y:80 P:27 SP:F9 CYC:274 SL:49 +F919 60 RTS A:80 X:DA Y:80 P:27 SP:F9 CYC:280 SL:49 +D7EB E8 INX A:80 X:DA Y:80 P:27 SP:FB CYC:298 SL:49 +D7EC A9 81 LDA #$81 A:80 X:DB Y:80 P:A5 SP:FB CYC:304 SL:49 +D7EE 8D 78 06 STA $0678 = 80 A:81 X:DB Y:80 P:A5 SP:FB CYC:310 SL:49 +D7F1 CC 78 06 CPY $0678 = 81 A:81 X:DB Y:80 P:A5 SP:FB CYC:322 SL:49 +D7F4 20 1D F9 JSR $F91D A:81 X:DB Y:80 P:A4 SP:FB CYC:334 SL:49 +F91D B0 05 BCS $F924 A:81 X:DB Y:80 P:A4 SP:F9 CYC: 11 SL:50 +F91F F0 03 BEQ $F924 A:81 X:DB Y:80 P:A4 SP:F9 CYC: 17 SL:50 +F921 10 01 BPL $F924 A:81 X:DB Y:80 P:A4 SP:F9 CYC: 23 SL:50 +F923 60 RTS A:81 X:DB Y:80 P:A4 SP:F9 CYC: 29 SL:50 +D7F7 E8 INX A:81 X:DB Y:80 P:A4 SP:FB CYC: 47 SL:50 +D7F8 A9 7F LDA #$7F A:81 X:DC Y:80 P:A4 SP:FB CYC: 53 SL:50 +D7FA 8D 78 06 STA $0678 = 81 A:7F X:DC Y:80 P:24 SP:FB CYC: 59 SL:50 +D7FD CC 78 06 CPY $0678 = 7F A:7F X:DC Y:80 P:24 SP:FB CYC: 71 SL:50 +D800 20 27 F9 JSR $F927 A:7F X:DC Y:80 P:25 SP:FB CYC: 83 SL:50 +F927 90 05 BCC $F92E A:7F X:DC Y:80 P:25 SP:F9 CYC:101 SL:50 +F929 F0 03 BEQ $F92E A:7F X:DC Y:80 P:25 SP:F9 CYC:107 SL:50 +F92B 30 01 BMI $F92E A:7F X:DC Y:80 P:25 SP:F9 CYC:113 SL:50 +F92D 60 RTS A:7F X:DC Y:80 P:25 SP:F9 CYC:119 SL:50 +D803 E8 INX A:7F X:DC Y:80 P:25 SP:FB CYC:137 SL:50 +D804 8A TXA A:7F X:DD Y:80 P:A5 SP:FB CYC:143 SL:50 +D805 A8 TAY A:DD X:DD Y:80 P:A5 SP:FB CYC:149 SL:50 +D806 20 90 F9 JSR $F990 A:DD X:DD Y:DD P:A5 SP:FB CYC:155 SL:50 +F990 A2 55 LDX #$55 A:DD X:DD Y:DD P:A5 SP:F9 CYC:173 SL:50 +F992 A9 FF LDA #$FF A:DD X:55 Y:DD P:25 SP:F9 CYC:179 SL:50 +F994 85 01 STA $01 = FF A:FF X:55 Y:DD P:A5 SP:F9 CYC:185 SL:50 +F996 EA NOP A:FF X:55 Y:DD P:A5 SP:F9 CYC:194 SL:50 +F997 24 01 BIT $01 = FF A:FF X:55 Y:DD P:A5 SP:F9 CYC:200 SL:50 +F999 38 SEC A:FF X:55 Y:DD P:E5 SP:F9 CYC:209 SL:50 +F99A A9 01 LDA #$01 A:FF X:55 Y:DD P:E5 SP:F9 CYC:215 SL:50 +F99C 60 RTS A:01 X:55 Y:DD P:65 SP:F9 CYC:221 SL:50 +D809 8D 78 06 STA $0678 = 7F A:01 X:55 Y:DD P:65 SP:FB CYC:239 SL:50 +D80C 4E 78 06 LSR $0678 = 01 A:01 X:55 Y:DD P:65 SP:FB CYC:251 SL:50 +D80F AD 78 06 LDA $0678 = 00 A:01 X:55 Y:DD P:67 SP:FB CYC:269 SL:50 +D812 20 9D F9 JSR $F99D A:00 X:55 Y:DD P:67 SP:FB CYC:281 SL:50 +F99D 90 1B BCC $F9BA A:00 X:55 Y:DD P:67 SP:F9 CYC:299 SL:50 +F99F D0 19 BNE $F9BA A:00 X:55 Y:DD P:67 SP:F9 CYC:305 SL:50 +F9A1 30 17 BMI $F9BA A:00 X:55 Y:DD P:67 SP:F9 CYC:311 SL:50 +F9A3 50 15 BVC $F9BA A:00 X:55 Y:DD P:67 SP:F9 CYC:317 SL:50 +F9A5 C9 00 CMP #$00 A:00 X:55 Y:DD P:67 SP:F9 CYC:323 SL:50 +F9A7 D0 11 BNE $F9BA A:00 X:55 Y:DD P:67 SP:F9 CYC:329 SL:50 +F9A9 B8 CLV A:00 X:55 Y:DD P:67 SP:F9 CYC:335 SL:50 +F9AA A9 AA LDA #$AA A:00 X:55 Y:DD P:27 SP:F9 CYC: 0 SL:51 +F9AC 60 RTS A:AA X:55 Y:DD P:A5 SP:F9 CYC: 6 SL:51 +D815 C8 INY A:AA X:55 Y:DD P:A5 SP:FB CYC: 24 SL:51 +D816 8D 78 06 STA $0678 = 00 A:AA X:55 Y:DE P:A5 SP:FB CYC: 30 SL:51 +D819 4E 78 06 LSR $0678 = AA A:AA X:55 Y:DE P:A5 SP:FB CYC: 42 SL:51 +D81C AD 78 06 LDA $0678 = 55 A:AA X:55 Y:DE P:24 SP:FB CYC: 60 SL:51 +D81F 20 AD F9 JSR $F9AD A:55 X:55 Y:DE P:24 SP:FB CYC: 72 SL:51 +F9AD B0 0B BCS $F9BA A:55 X:55 Y:DE P:24 SP:F9 CYC: 90 SL:51 +F9AF F0 09 BEQ $F9BA A:55 X:55 Y:DE P:24 SP:F9 CYC: 96 SL:51 +F9B1 30 07 BMI $F9BA A:55 X:55 Y:DE P:24 SP:F9 CYC:102 SL:51 +F9B3 70 05 BVS $F9BA A:55 X:55 Y:DE P:24 SP:F9 CYC:108 SL:51 +F9B5 C9 55 CMP #$55 A:55 X:55 Y:DE P:24 SP:F9 CYC:114 SL:51 +F9B7 D0 01 BNE $F9BA A:55 X:55 Y:DE P:27 SP:F9 CYC:120 SL:51 +F9B9 60 RTS A:55 X:55 Y:DE P:27 SP:F9 CYC:126 SL:51 +D822 C8 INY A:55 X:55 Y:DE P:27 SP:FB CYC:144 SL:51 +D823 20 BD F9 JSR $F9BD A:55 X:55 Y:DF P:A5 SP:FB CYC:150 SL:51 +F9BD 24 01 BIT $01 = FF A:55 X:55 Y:DF P:A5 SP:F9 CYC:168 SL:51 +F9BF 38 SEC A:55 X:55 Y:DF P:E5 SP:F9 CYC:177 SL:51 +F9C0 A9 80 LDA #$80 A:55 X:55 Y:DF P:E5 SP:F9 CYC:183 SL:51 +F9C2 60 RTS A:80 X:55 Y:DF P:E5 SP:F9 CYC:189 SL:51 +D826 8D 78 06 STA $0678 = 55 A:80 X:55 Y:DF P:E5 SP:FB CYC:207 SL:51 +D829 0E 78 06 ASL $0678 = 80 A:80 X:55 Y:DF P:E5 SP:FB CYC:219 SL:51 +D82C AD 78 06 LDA $0678 = 00 A:80 X:55 Y:DF P:67 SP:FB CYC:237 SL:51 +D82F 20 C3 F9 JSR $F9C3 A:00 X:55 Y:DF P:67 SP:FB CYC:249 SL:51 +F9C3 90 1C BCC $F9E1 A:00 X:55 Y:DF P:67 SP:F9 CYC:267 SL:51 +F9C5 D0 1A BNE $F9E1 A:00 X:55 Y:DF P:67 SP:F9 CYC:273 SL:51 +F9C7 30 18 BMI $F9E1 A:00 X:55 Y:DF P:67 SP:F9 CYC:279 SL:51 +F9C9 50 16 BVC $F9E1 A:00 X:55 Y:DF P:67 SP:F9 CYC:285 SL:51 +F9CB C9 00 CMP #$00 A:00 X:55 Y:DF P:67 SP:F9 CYC:291 SL:51 +F9CD D0 12 BNE $F9E1 A:00 X:55 Y:DF P:67 SP:F9 CYC:297 SL:51 +F9CF B8 CLV A:00 X:55 Y:DF P:67 SP:F9 CYC:303 SL:51 +F9D0 A9 55 LDA #$55 A:00 X:55 Y:DF P:27 SP:F9 CYC:309 SL:51 +F9D2 38 SEC A:55 X:55 Y:DF P:25 SP:F9 CYC:315 SL:51 +F9D3 60 RTS A:55 X:55 Y:DF P:25 SP:F9 CYC:321 SL:51 +D832 C8 INY A:55 X:55 Y:DF P:25 SP:FB CYC:339 SL:51 +D833 8D 78 06 STA $0678 = 00 A:55 X:55 Y:E0 P:A5 SP:FB CYC: 4 SL:52 +D836 0E 78 06 ASL $0678 = 55 A:55 X:55 Y:E0 P:A5 SP:FB CYC: 16 SL:52 +D839 AD 78 06 LDA $0678 = AA A:55 X:55 Y:E0 P:A4 SP:FB CYC: 34 SL:52 +D83C 20 D4 F9 JSR $F9D4 A:AA X:55 Y:E0 P:A4 SP:FB CYC: 46 SL:52 +F9D4 B0 0B BCS $F9E1 A:AA X:55 Y:E0 P:A4 SP:F9 CYC: 64 SL:52 +F9D6 F0 09 BEQ $F9E1 A:AA X:55 Y:E0 P:A4 SP:F9 CYC: 70 SL:52 +F9D8 10 07 BPL $F9E1 A:AA X:55 Y:E0 P:A4 SP:F9 CYC: 76 SL:52 +F9DA 70 05 BVS $F9E1 A:AA X:55 Y:E0 P:A4 SP:F9 CYC: 82 SL:52 +F9DC C9 AA CMP #$AA A:AA X:55 Y:E0 P:A4 SP:F9 CYC: 88 SL:52 +F9DE D0 01 BNE $F9E1 A:AA X:55 Y:E0 P:27 SP:F9 CYC: 94 SL:52 +F9E0 60 RTS A:AA X:55 Y:E0 P:27 SP:F9 CYC:100 SL:52 +D83F C8 INY A:AA X:55 Y:E0 P:27 SP:FB CYC:118 SL:52 +D840 20 E4 F9 JSR $F9E4 A:AA X:55 Y:E1 P:A5 SP:FB CYC:124 SL:52 +F9E4 24 01 BIT $01 = FF A:AA X:55 Y:E1 P:A5 SP:F9 CYC:142 SL:52 +F9E6 38 SEC A:AA X:55 Y:E1 P:E5 SP:F9 CYC:151 SL:52 +F9E7 A9 01 LDA #$01 A:AA X:55 Y:E1 P:E5 SP:F9 CYC:157 SL:52 +F9E9 60 RTS A:01 X:55 Y:E1 P:65 SP:F9 CYC:163 SL:52 +D843 8D 78 06 STA $0678 = AA A:01 X:55 Y:E1 P:65 SP:FB CYC:181 SL:52 +D846 6E 78 06 ROR $0678 = 01 A:01 X:55 Y:E1 P:65 SP:FB CYC:193 SL:52 +D849 AD 78 06 LDA $0678 = 80 A:01 X:55 Y:E1 P:E5 SP:FB CYC:211 SL:52 +D84C 20 EA F9 JSR $F9EA A:80 X:55 Y:E1 P:E5 SP:FB CYC:223 SL:52 +F9EA 90 1C BCC $FA08 A:80 X:55 Y:E1 P:E5 SP:F9 CYC:241 SL:52 +F9EC F0 1A BEQ $FA08 A:80 X:55 Y:E1 P:E5 SP:F9 CYC:247 SL:52 +F9EE 10 18 BPL $FA08 A:80 X:55 Y:E1 P:E5 SP:F9 CYC:253 SL:52 +F9F0 50 16 BVC $FA08 A:80 X:55 Y:E1 P:E5 SP:F9 CYC:259 SL:52 +F9F2 C9 80 CMP #$80 A:80 X:55 Y:E1 P:E5 SP:F9 CYC:265 SL:52 +F9F4 D0 12 BNE $FA08 A:80 X:55 Y:E1 P:67 SP:F9 CYC:271 SL:52 +F9F6 B8 CLV A:80 X:55 Y:E1 P:67 SP:F9 CYC:277 SL:52 +F9F7 18 CLC A:80 X:55 Y:E1 P:27 SP:F9 CYC:283 SL:52 +F9F8 A9 55 LDA #$55 A:80 X:55 Y:E1 P:26 SP:F9 CYC:289 SL:52 +F9FA 60 RTS A:55 X:55 Y:E1 P:24 SP:F9 CYC:295 SL:52 +D84F C8 INY A:55 X:55 Y:E1 P:24 SP:FB CYC:313 SL:52 +D850 8D 78 06 STA $0678 = 80 A:55 X:55 Y:E2 P:A4 SP:FB CYC:319 SL:52 +D853 6E 78 06 ROR $0678 = 55 A:55 X:55 Y:E2 P:A4 SP:FB CYC:331 SL:52 +D856 AD 78 06 LDA $0678 = 2A A:55 X:55 Y:E2 P:25 SP:FB CYC: 8 SL:53 +D859 20 FB F9 JSR $F9FB A:2A X:55 Y:E2 P:25 SP:FB CYC: 20 SL:53 +F9FB 90 0B BCC $FA08 A:2A X:55 Y:E2 P:25 SP:F9 CYC: 38 SL:53 +F9FD F0 09 BEQ $FA08 A:2A X:55 Y:E2 P:25 SP:F9 CYC: 44 SL:53 +F9FF 30 07 BMI $FA08 A:2A X:55 Y:E2 P:25 SP:F9 CYC: 50 SL:53 +FA01 70 05 BVS $FA08 A:2A X:55 Y:E2 P:25 SP:F9 CYC: 56 SL:53 +FA03 C9 2A CMP #$2A A:2A X:55 Y:E2 P:25 SP:F9 CYC: 62 SL:53 +FA05 D0 01 BNE $FA08 A:2A X:55 Y:E2 P:27 SP:F9 CYC: 68 SL:53 +FA07 60 RTS A:2A X:55 Y:E2 P:27 SP:F9 CYC: 74 SL:53 +D85C C8 INY A:2A X:55 Y:E2 P:27 SP:FB CYC: 92 SL:53 +D85D 20 0A FA JSR $FA0A A:2A X:55 Y:E3 P:A5 SP:FB CYC: 98 SL:53 +FA0A 24 01 BIT $01 = FF A:2A X:55 Y:E3 P:A5 SP:F9 CYC:116 SL:53 +FA0C 38 SEC A:2A X:55 Y:E3 P:E5 SP:F9 CYC:125 SL:53 +FA0D A9 80 LDA #$80 A:2A X:55 Y:E3 P:E5 SP:F9 CYC:131 SL:53 +FA0F 60 RTS A:80 X:55 Y:E3 P:E5 SP:F9 CYC:137 SL:53 +D860 8D 78 06 STA $0678 = 2A A:80 X:55 Y:E3 P:E5 SP:FB CYC:155 SL:53 +D863 2E 78 06 ROL $0678 = 80 A:80 X:55 Y:E3 P:E5 SP:FB CYC:167 SL:53 +D866 AD 78 06 LDA $0678 = 01 A:80 X:55 Y:E3 P:65 SP:FB CYC:185 SL:53 +D869 20 10 FA JSR $FA10 A:01 X:55 Y:E3 P:65 SP:FB CYC:197 SL:53 +FA10 90 1C BCC $FA2E A:01 X:55 Y:E3 P:65 SP:F9 CYC:215 SL:53 +FA12 F0 1A BEQ $FA2E A:01 X:55 Y:E3 P:65 SP:F9 CYC:221 SL:53 +FA14 30 18 BMI $FA2E A:01 X:55 Y:E3 P:65 SP:F9 CYC:227 SL:53 +FA16 50 16 BVC $FA2E A:01 X:55 Y:E3 P:65 SP:F9 CYC:233 SL:53 +FA18 C9 01 CMP #$01 A:01 X:55 Y:E3 P:65 SP:F9 CYC:239 SL:53 +FA1A D0 12 BNE $FA2E A:01 X:55 Y:E3 P:67 SP:F9 CYC:245 SL:53 +FA1C B8 CLV A:01 X:55 Y:E3 P:67 SP:F9 CYC:251 SL:53 +FA1D 18 CLC A:01 X:55 Y:E3 P:27 SP:F9 CYC:257 SL:53 +FA1E A9 55 LDA #$55 A:01 X:55 Y:E3 P:26 SP:F9 CYC:263 SL:53 +FA20 60 RTS A:55 X:55 Y:E3 P:24 SP:F9 CYC:269 SL:53 +D86C C8 INY A:55 X:55 Y:E3 P:24 SP:FB CYC:287 SL:53 +D86D 8D 78 06 STA $0678 = 01 A:55 X:55 Y:E4 P:A4 SP:FB CYC:293 SL:53 +D870 2E 78 06 ROL $0678 = 55 A:55 X:55 Y:E4 P:A4 SP:FB CYC:305 SL:53 +D873 AD 78 06 LDA $0678 = AA A:55 X:55 Y:E4 P:A4 SP:FB CYC:323 SL:53 +D876 20 21 FA JSR $FA21 A:AA X:55 Y:E4 P:A4 SP:FB CYC:335 SL:53 +FA21 B0 0B BCS $FA2E A:AA X:55 Y:E4 P:A4 SP:F9 CYC: 12 SL:54 +FA23 F0 09 BEQ $FA2E A:AA X:55 Y:E4 P:A4 SP:F9 CYC: 18 SL:54 +FA25 10 07 BPL $FA2E A:AA X:55 Y:E4 P:A4 SP:F9 CYC: 24 SL:54 +FA27 70 05 BVS $FA2E A:AA X:55 Y:E4 P:A4 SP:F9 CYC: 30 SL:54 +FA29 C9 AA CMP #$AA A:AA X:55 Y:E4 P:A4 SP:F9 CYC: 36 SL:54 +FA2B D0 01 BNE $FA2E A:AA X:55 Y:E4 P:27 SP:F9 CYC: 42 SL:54 +FA2D 60 RTS A:AA X:55 Y:E4 P:27 SP:F9 CYC: 48 SL:54 +D879 A9 FF LDA #$FF A:AA X:55 Y:E4 P:27 SP:FB CYC: 66 SL:54 +D87B 8D 78 06 STA $0678 = AA A:FF X:55 Y:E4 P:A5 SP:FB CYC: 72 SL:54 +D87E 85 01 STA $01 = FF A:FF X:55 Y:E4 P:A5 SP:FB CYC: 84 SL:54 +D880 24 01 BIT $01 = FF A:FF X:55 Y:E4 P:A5 SP:FB CYC: 93 SL:54 +D882 38 SEC A:FF X:55 Y:E4 P:E5 SP:FB CYC:102 SL:54 +D883 EE 78 06 INC $0678 = FF A:FF X:55 Y:E4 P:E5 SP:FB CYC:108 SL:54 +D886 D0 0D BNE $D895 A:FF X:55 Y:E4 P:67 SP:FB CYC:126 SL:54 +D888 30 0B BMI $D895 A:FF X:55 Y:E4 P:67 SP:FB CYC:132 SL:54 +D88A 50 09 BVC $D895 A:FF X:55 Y:E4 P:67 SP:FB CYC:138 SL:54 +D88C 90 07 BCC $D895 A:FF X:55 Y:E4 P:67 SP:FB CYC:144 SL:54 +D88E AD 78 06 LDA $0678 = 00 A:FF X:55 Y:E4 P:67 SP:FB CYC:150 SL:54 +D891 C9 00 CMP #$00 A:00 X:55 Y:E4 P:67 SP:FB CYC:162 SL:54 +D893 F0 04 BEQ $D899 A:00 X:55 Y:E4 P:67 SP:FB CYC:168 SL:54 +D899 A9 7F LDA #$7F A:00 X:55 Y:E4 P:67 SP:FB CYC:177 SL:54 +D89B 8D 78 06 STA $0678 = 00 A:7F X:55 Y:E4 P:65 SP:FB CYC:183 SL:54 +D89E B8 CLV A:7F X:55 Y:E4 P:65 SP:FB CYC:195 SL:54 +D89F 18 CLC A:7F X:55 Y:E4 P:25 SP:FB CYC:201 SL:54 +D8A0 EE 78 06 INC $0678 = 7F A:7F X:55 Y:E4 P:24 SP:FB CYC:207 SL:54 +D8A3 F0 0D BEQ $D8B2 A:7F X:55 Y:E4 P:A4 SP:FB CYC:225 SL:54 +D8A5 10 0B BPL $D8B2 A:7F X:55 Y:E4 P:A4 SP:FB CYC:231 SL:54 +D8A7 70 09 BVS $D8B2 A:7F X:55 Y:E4 P:A4 SP:FB CYC:237 SL:54 +D8A9 B0 07 BCS $D8B2 A:7F X:55 Y:E4 P:A4 SP:FB CYC:243 SL:54 +D8AB AD 78 06 LDA $0678 = 80 A:7F X:55 Y:E4 P:A4 SP:FB CYC:249 SL:54 +D8AE C9 80 CMP #$80 A:80 X:55 Y:E4 P:A4 SP:FB CYC:261 SL:54 +D8B0 F0 04 BEQ $D8B6 A:80 X:55 Y:E4 P:27 SP:FB CYC:267 SL:54 +D8B6 A9 00 LDA #$00 A:80 X:55 Y:E4 P:27 SP:FB CYC:276 SL:54 +D8B8 8D 78 06 STA $0678 = 80 A:00 X:55 Y:E4 P:27 SP:FB CYC:282 SL:54 +D8BB 24 01 BIT $01 = FF A:00 X:55 Y:E4 P:27 SP:FB CYC:294 SL:54 +D8BD 38 SEC A:00 X:55 Y:E4 P:E7 SP:FB CYC:303 SL:54 +D8BE CE 78 06 DEC $0678 = 00 A:00 X:55 Y:E4 P:E7 SP:FB CYC:309 SL:54 +D8C1 F0 0D BEQ $D8D0 A:00 X:55 Y:E4 P:E5 SP:FB CYC:327 SL:54 +D8C3 10 0B BPL $D8D0 A:00 X:55 Y:E4 P:E5 SP:FB CYC:333 SL:54 +D8C5 50 09 BVC $D8D0 A:00 X:55 Y:E4 P:E5 SP:FB CYC:339 SL:54 +D8C7 90 07 BCC $D8D0 A:00 X:55 Y:E4 P:E5 SP:FB CYC: 4 SL:55 +D8C9 AD 78 06 LDA $0678 = FF A:00 X:55 Y:E4 P:E5 SP:FB CYC: 10 SL:55 +D8CC C9 FF CMP #$FF A:FF X:55 Y:E4 P:E5 SP:FB CYC: 22 SL:55 +D8CE F0 04 BEQ $D8D4 A:FF X:55 Y:E4 P:67 SP:FB CYC: 28 SL:55 +D8D4 A9 80 LDA #$80 A:FF X:55 Y:E4 P:67 SP:FB CYC: 37 SL:55 +D8D6 8D 78 06 STA $0678 = FF A:80 X:55 Y:E4 P:E5 SP:FB CYC: 43 SL:55 +D8D9 B8 CLV A:80 X:55 Y:E4 P:E5 SP:FB CYC: 55 SL:55 +D8DA 18 CLC A:80 X:55 Y:E4 P:A5 SP:FB CYC: 61 SL:55 +D8DB CE 78 06 DEC $0678 = 80 A:80 X:55 Y:E4 P:A4 SP:FB CYC: 67 SL:55 +D8DE F0 0D BEQ $D8ED A:80 X:55 Y:E4 P:24 SP:FB CYC: 85 SL:55 +D8E0 30 0B BMI $D8ED A:80 X:55 Y:E4 P:24 SP:FB CYC: 91 SL:55 +D8E2 70 09 BVS $D8ED A:80 X:55 Y:E4 P:24 SP:FB CYC: 97 SL:55 +D8E4 B0 07 BCS $D8ED A:80 X:55 Y:E4 P:24 SP:FB CYC:103 SL:55 +D8E6 AD 78 06 LDA $0678 = 7F A:80 X:55 Y:E4 P:24 SP:FB CYC:109 SL:55 +D8E9 C9 7F CMP #$7F A:7F X:55 Y:E4 P:24 SP:FB CYC:121 SL:55 +D8EB F0 04 BEQ $D8F1 A:7F X:55 Y:E4 P:27 SP:FB CYC:127 SL:55 +D8F1 A9 01 LDA #$01 A:7F X:55 Y:E4 P:27 SP:FB CYC:136 SL:55 +D8F3 8D 78 06 STA $0678 = 7F A:01 X:55 Y:E4 P:25 SP:FB CYC:142 SL:55 +D8F6 CE 78 06 DEC $0678 = 01 A:01 X:55 Y:E4 P:25 SP:FB CYC:154 SL:55 +D8F9 F0 04 BEQ $D8FF A:01 X:55 Y:E4 P:27 SP:FB CYC:172 SL:55 +D8FF 60 RTS A:01 X:55 Y:E4 P:27 SP:FB CYC:181 SL:55 +C618 20 00 D9 JSR $D900 A:01 X:55 Y:E4 P:27 SP:FD CYC:199 SL:55 +D900 A9 A3 LDA #$A3 A:01 X:55 Y:E4 P:27 SP:FB CYC:217 SL:55 +D902 85 33 STA $33 = 00 A:A3 X:55 Y:E4 P:A5 SP:FB CYC:223 SL:55 +D904 A9 89 LDA #$89 A:A3 X:55 Y:E4 P:A5 SP:FB CYC:232 SL:55 +D906 8D 00 03 STA $0300 = 70 A:89 X:55 Y:E4 P:A5 SP:FB CYC:238 SL:55 +D909 A9 12 LDA #$12 A:89 X:55 Y:E4 P:A5 SP:FB CYC:250 SL:55 +D90B 8D 45 02 STA $0245 = 00 A:12 X:55 Y:E4 P:25 SP:FB CYC:256 SL:55 +D90E A9 FF LDA #$FF A:12 X:55 Y:E4 P:25 SP:FB CYC:268 SL:55 +D910 85 01 STA $01 = FF A:FF X:55 Y:E4 P:A5 SP:FB CYC:274 SL:55 +D912 A2 65 LDX #$65 A:FF X:55 Y:E4 P:A5 SP:FB CYC:283 SL:55 +D914 A9 00 LDA #$00 A:FF X:65 Y:E4 P:25 SP:FB CYC:289 SL:55 +D916 85 89 STA $89 = 00 A:00 X:65 Y:E4 P:27 SP:FB CYC:295 SL:55 +D918 A9 03 LDA #$03 A:00 X:65 Y:E4 P:27 SP:FB CYC:304 SL:55 +D91A 85 8A STA $8A = 00 A:03 X:65 Y:E4 P:25 SP:FB CYC:310 SL:55 +D91C A0 00 LDY #$00 A:03 X:65 Y:E4 P:25 SP:FB CYC:319 SL:55 +D91E 38 SEC A:03 X:65 Y:00 P:27 SP:FB CYC:325 SL:55 +D91F A9 00 LDA #$00 A:03 X:65 Y:00 P:27 SP:FB CYC:331 SL:55 +D921 B8 CLV A:00 X:65 Y:00 P:27 SP:FB CYC:337 SL:55 +D922 B1 89 LDA ($89),Y = 0300 @ 0300 = 89 A:00 X:65 Y:00 P:27 SP:FB CYC: 2 SL:56 +D924 F0 0C BEQ $D932 A:89 X:65 Y:00 P:A5 SP:FB CYC: 17 SL:56 +D926 90 0A BCC $D932 A:89 X:65 Y:00 P:A5 SP:FB CYC: 23 SL:56 +D928 70 08 BVS $D932 A:89 X:65 Y:00 P:A5 SP:FB CYC: 29 SL:56 +D92A C9 89 CMP #$89 A:89 X:65 Y:00 P:A5 SP:FB CYC: 35 SL:56 +D92C D0 04 BNE $D932 A:89 X:65 Y:00 P:27 SP:FB CYC: 41 SL:56 +D92E E0 65 CPX #$65 A:89 X:65 Y:00 P:27 SP:FB CYC: 47 SL:56 +D930 F0 04 BEQ $D936 A:89 X:65 Y:00 P:27 SP:FB CYC: 53 SL:56 +D936 A9 FF LDA #$FF A:89 X:65 Y:00 P:27 SP:FB CYC: 62 SL:56 +D938 85 97 STA $97 = 00 A:FF X:65 Y:00 P:A5 SP:FB CYC: 68 SL:56 +D93A 85 98 STA $98 = 00 A:FF X:65 Y:00 P:A5 SP:FB CYC: 77 SL:56 +D93C 24 98 BIT $98 = FF A:FF X:65 Y:00 P:A5 SP:FB CYC: 86 SL:56 +D93E A0 34 LDY #$34 A:FF X:65 Y:00 P:E5 SP:FB CYC: 95 SL:56 +D940 B1 97 LDA ($97),Y = FFFF @ 0033 = A3 A:FF X:65 Y:34 P:65 SP:FB CYC:101 SL:56 +D942 C9 A3 CMP #$A3 A:A3 X:65 Y:34 P:E5 SP:FB CYC:119 SL:56 +D944 D0 02 BNE $D948 A:A3 X:65 Y:34 P:67 SP:FB CYC:125 SL:56 +D946 B0 04 BCS $D94C A:A3 X:65 Y:34 P:67 SP:FB CYC:131 SL:56 +D94C A5 00 LDA $00 = 00 A:A3 X:65 Y:34 P:67 SP:FB CYC:140 SL:56 +D94E 48 PHA A:00 X:65 Y:34 P:67 SP:FB CYC:149 SL:56 +D94F A9 46 LDA #$46 A:00 X:65 Y:34 P:67 SP:FA CYC:158 SL:56 +D951 85 FF STA $FF = 00 A:46 X:65 Y:34 P:65 SP:FA CYC:164 SL:56 +D953 A9 01 LDA #$01 A:46 X:65 Y:34 P:65 SP:FA CYC:173 SL:56 +D955 85 00 STA $00 = 00 A:01 X:65 Y:34 P:65 SP:FA CYC:179 SL:56 +D957 A0 FF LDY #$FF A:01 X:65 Y:34 P:65 SP:FA CYC:188 SL:56 +D959 B1 FF LDA ($FF),Y = 0146 @ 0245 = 12 A:01 X:65 Y:FF P:E5 SP:FA CYC:194 SL:56 +D95B C9 12 CMP #$12 A:12 X:65 Y:FF P:65 SP:FA CYC:212 SL:56 +D95D F0 04 BEQ $D963 A:12 X:65 Y:FF P:67 SP:FA CYC:218 SL:56 +D963 68 PLA A:12 X:65 Y:FF P:67 SP:FA CYC:227 SL:56 +D964 85 00 STA $00 = 01 A:00 X:65 Y:FF P:67 SP:FB CYC:239 SL:56 +D966 A2 ED LDX #$ED A:00 X:65 Y:FF P:67 SP:FB CYC:248 SL:56 +D968 A9 00 LDA #$00 A:00 X:ED Y:FF P:E5 SP:FB CYC:254 SL:56 +D96A 85 33 STA $33 = A3 A:00 X:ED Y:FF P:67 SP:FB CYC:260 SL:56 +D96C A9 04 LDA #$04 A:00 X:ED Y:FF P:67 SP:FB CYC:269 SL:56 +D96E 85 34 STA $34 = 00 A:04 X:ED Y:FF P:65 SP:FB CYC:275 SL:56 +D970 A0 00 LDY #$00 A:04 X:ED Y:FF P:65 SP:FB CYC:284 SL:56 +D972 18 CLC A:04 X:ED Y:00 P:67 SP:FB CYC:290 SL:56 +D973 A9 FF LDA #$FF A:04 X:ED Y:00 P:66 SP:FB CYC:296 SL:56 +D975 85 01 STA $01 = FF A:FF X:ED Y:00 P:E4 SP:FB CYC:302 SL:56 +D977 24 01 BIT $01 = FF A:FF X:ED Y:00 P:E4 SP:FB CYC:311 SL:56 +D979 A9 AA LDA #$AA A:FF X:ED Y:00 P:E4 SP:FB CYC:320 SL:56 +D97B 8D 00 04 STA $0400 = AD A:AA X:ED Y:00 P:E4 SP:FB CYC:326 SL:56 +D97E A9 55 LDA #$55 A:AA X:ED Y:00 P:E4 SP:FB CYC:338 SL:56 +D980 11 33 ORA ($33),Y = 0400 @ 0400 = AA A:55 X:ED Y:00 P:64 SP:FB CYC: 3 SL:57 +D982 B0 08 BCS $D98C A:FF X:ED Y:00 P:E4 SP:FB CYC: 18 SL:57 +D984 10 06 BPL $D98C A:FF X:ED Y:00 P:E4 SP:FB CYC: 24 SL:57 +D986 C9 FF CMP #$FF A:FF X:ED Y:00 P:E4 SP:FB CYC: 30 SL:57 +D988 D0 02 BNE $D98C A:FF X:ED Y:00 P:67 SP:FB CYC: 36 SL:57 +D98A 70 02 BVS $D98E A:FF X:ED Y:00 P:67 SP:FB CYC: 42 SL:57 +D98E E8 INX A:FF X:ED Y:00 P:67 SP:FB CYC: 51 SL:57 +D98F 38 SEC A:FF X:EE Y:00 P:E5 SP:FB CYC: 57 SL:57 +D990 B8 CLV A:FF X:EE Y:00 P:E5 SP:FB CYC: 63 SL:57 +D991 A9 00 LDA #$00 A:FF X:EE Y:00 P:A5 SP:FB CYC: 69 SL:57 +D993 11 33 ORA ($33),Y = 0400 @ 0400 = AA A:00 X:EE Y:00 P:27 SP:FB CYC: 75 SL:57 +D995 F0 06 BEQ $D99D A:AA X:EE Y:00 P:A5 SP:FB CYC: 90 SL:57 +D997 70 04 BVS $D99D A:AA X:EE Y:00 P:A5 SP:FB CYC: 96 SL:57 +D999 90 02 BCC $D99D A:AA X:EE Y:00 P:A5 SP:FB CYC:102 SL:57 +D99B 30 02 BMI $D99F A:AA X:EE Y:00 P:A5 SP:FB CYC:108 SL:57 +D99F E8 INX A:AA X:EE Y:00 P:A5 SP:FB CYC:117 SL:57 +D9A0 18 CLC A:AA X:EF Y:00 P:A5 SP:FB CYC:123 SL:57 +D9A1 24 01 BIT $01 = FF A:AA X:EF Y:00 P:A4 SP:FB CYC:129 SL:57 +D9A3 A9 55 LDA #$55 A:AA X:EF Y:00 P:E4 SP:FB CYC:138 SL:57 +D9A5 31 33 AND ($33),Y = 0400 @ 0400 = AA A:55 X:EF Y:00 P:64 SP:FB CYC:144 SL:57 +D9A7 D0 06 BNE $D9AF A:00 X:EF Y:00 P:66 SP:FB CYC:159 SL:57 +D9A9 50 04 BVC $D9AF A:00 X:EF Y:00 P:66 SP:FB CYC:165 SL:57 +D9AB B0 02 BCS $D9AF A:00 X:EF Y:00 P:66 SP:FB CYC:171 SL:57 +D9AD 10 02 BPL $D9B1 A:00 X:EF Y:00 P:66 SP:FB CYC:177 SL:57 +D9B1 E8 INX A:00 X:EF Y:00 P:66 SP:FB CYC:186 SL:57 +D9B2 38 SEC A:00 X:F0 Y:00 P:E4 SP:FB CYC:192 SL:57 +D9B3 B8 CLV A:00 X:F0 Y:00 P:E5 SP:FB CYC:198 SL:57 +D9B4 A9 EF LDA #$EF A:00 X:F0 Y:00 P:A5 SP:FB CYC:204 SL:57 +D9B6 8D 00 04 STA $0400 = AA A:EF X:F0 Y:00 P:A5 SP:FB CYC:210 SL:57 +D9B9 A9 F8 LDA #$F8 A:EF X:F0 Y:00 P:A5 SP:FB CYC:222 SL:57 +D9BB 31 33 AND ($33),Y = 0400 @ 0400 = EF A:F8 X:F0 Y:00 P:A5 SP:FB CYC:228 SL:57 +D9BD 90 08 BCC $D9C7 A:E8 X:F0 Y:00 P:A5 SP:FB CYC:243 SL:57 +D9BF 10 06 BPL $D9C7 A:E8 X:F0 Y:00 P:A5 SP:FB CYC:249 SL:57 +D9C1 C9 E8 CMP #$E8 A:E8 X:F0 Y:00 P:A5 SP:FB CYC:255 SL:57 +D9C3 D0 02 BNE $D9C7 A:E8 X:F0 Y:00 P:27 SP:FB CYC:261 SL:57 +D9C5 50 02 BVC $D9C9 A:E8 X:F0 Y:00 P:27 SP:FB CYC:267 SL:57 +D9C9 E8 INX A:E8 X:F0 Y:00 P:27 SP:FB CYC:276 SL:57 +D9CA 18 CLC A:E8 X:F1 Y:00 P:A5 SP:FB CYC:282 SL:57 +D9CB 24 01 BIT $01 = FF A:E8 X:F1 Y:00 P:A4 SP:FB CYC:288 SL:57 +D9CD A9 AA LDA #$AA A:E8 X:F1 Y:00 P:E4 SP:FB CYC:297 SL:57 +D9CF 8D 00 04 STA $0400 = EF A:AA X:F1 Y:00 P:E4 SP:FB CYC:303 SL:57 +D9D2 A9 5F LDA #$5F A:AA X:F1 Y:00 P:E4 SP:FB CYC:315 SL:57 +D9D4 51 33 EOR ($33),Y = 0400 @ 0400 = AA A:5F X:F1 Y:00 P:64 SP:FB CYC:321 SL:57 +D9D6 B0 08 BCS $D9E0 A:F5 X:F1 Y:00 P:E4 SP:FB CYC:336 SL:57 +D9D8 10 06 BPL $D9E0 A:F5 X:F1 Y:00 P:E4 SP:FB CYC: 1 SL:58 +D9DA C9 F5 CMP #$F5 A:F5 X:F1 Y:00 P:E4 SP:FB CYC: 7 SL:58 +D9DC D0 02 BNE $D9E0 A:F5 X:F1 Y:00 P:67 SP:FB CYC: 13 SL:58 +D9DE 70 02 BVS $D9E2 A:F5 X:F1 Y:00 P:67 SP:FB CYC: 19 SL:58 +D9E2 E8 INX A:F5 X:F1 Y:00 P:67 SP:FB CYC: 28 SL:58 +D9E3 38 SEC A:F5 X:F2 Y:00 P:E5 SP:FB CYC: 34 SL:58 +D9E4 B8 CLV A:F5 X:F2 Y:00 P:E5 SP:FB CYC: 40 SL:58 +D9E5 A9 70 LDA #$70 A:F5 X:F2 Y:00 P:A5 SP:FB CYC: 46 SL:58 +D9E7 8D 00 04 STA $0400 = AA A:70 X:F2 Y:00 P:25 SP:FB CYC: 52 SL:58 +D9EA 51 33 EOR ($33),Y = 0400 @ 0400 = 70 A:70 X:F2 Y:00 P:25 SP:FB CYC: 64 SL:58 +D9EC D0 06 BNE $D9F4 A:00 X:F2 Y:00 P:27 SP:FB CYC: 79 SL:58 +D9EE 70 04 BVS $D9F4 A:00 X:F2 Y:00 P:27 SP:FB CYC: 85 SL:58 +D9F0 90 02 BCC $D9F4 A:00 X:F2 Y:00 P:27 SP:FB CYC: 91 SL:58 +D9F2 10 02 BPL $D9F6 A:00 X:F2 Y:00 P:27 SP:FB CYC: 97 SL:58 +D9F6 E8 INX A:00 X:F2 Y:00 P:27 SP:FB CYC:106 SL:58 +D9F7 18 CLC A:00 X:F3 Y:00 P:A5 SP:FB CYC:112 SL:58 +D9F8 24 01 BIT $01 = FF A:00 X:F3 Y:00 P:A4 SP:FB CYC:118 SL:58 +D9FA A9 69 LDA #$69 A:00 X:F3 Y:00 P:E6 SP:FB CYC:127 SL:58 +D9FC 8D 00 04 STA $0400 = 70 A:69 X:F3 Y:00 P:64 SP:FB CYC:133 SL:58 +D9FF A9 00 LDA #$00 A:69 X:F3 Y:00 P:64 SP:FB CYC:145 SL:58 +DA01 71 33 ADC ($33),Y = 0400 @ 0400 = 69 A:00 X:F3 Y:00 P:66 SP:FB CYC:151 SL:58 +DA03 30 08 BMI $DA0D A:69 X:F3 Y:00 P:24 SP:FB CYC:166 SL:58 +DA05 B0 06 BCS $DA0D A:69 X:F3 Y:00 P:24 SP:FB CYC:172 SL:58 +DA07 C9 69 CMP #$69 A:69 X:F3 Y:00 P:24 SP:FB CYC:178 SL:58 +DA09 D0 02 BNE $DA0D A:69 X:F3 Y:00 P:27 SP:FB CYC:184 SL:58 +DA0B 50 02 BVC $DA0F A:69 X:F3 Y:00 P:27 SP:FB CYC:190 SL:58 +DA0F E8 INX A:69 X:F3 Y:00 P:27 SP:FB CYC:199 SL:58 +DA10 38 SEC A:69 X:F4 Y:00 P:A5 SP:FB CYC:205 SL:58 +DA11 24 01 BIT $01 = FF A:69 X:F4 Y:00 P:A5 SP:FB CYC:211 SL:58 +DA13 A9 00 LDA #$00 A:69 X:F4 Y:00 P:E5 SP:FB CYC:220 SL:58 +DA15 71 33 ADC ($33),Y = 0400 @ 0400 = 69 A:00 X:F4 Y:00 P:67 SP:FB CYC:226 SL:58 +DA17 30 08 BMI $DA21 A:6A X:F4 Y:00 P:24 SP:FB CYC:241 SL:58 +DA19 B0 06 BCS $DA21 A:6A X:F4 Y:00 P:24 SP:FB CYC:247 SL:58 +DA1B C9 6A CMP #$6A A:6A X:F4 Y:00 P:24 SP:FB CYC:253 SL:58 +DA1D D0 02 BNE $DA21 A:6A X:F4 Y:00 P:27 SP:FB CYC:259 SL:58 +DA1F 50 02 BVC $DA23 A:6A X:F4 Y:00 P:27 SP:FB CYC:265 SL:58 +DA23 E8 INX A:6A X:F4 Y:00 P:27 SP:FB CYC:274 SL:58 +DA24 38 SEC A:6A X:F5 Y:00 P:A5 SP:FB CYC:280 SL:58 +DA25 B8 CLV A:6A X:F5 Y:00 P:A5 SP:FB CYC:286 SL:58 +DA26 A9 7F LDA #$7F A:6A X:F5 Y:00 P:A5 SP:FB CYC:292 SL:58 +DA28 8D 00 04 STA $0400 = 69 A:7F X:F5 Y:00 P:25 SP:FB CYC:298 SL:58 +DA2B 71 33 ADC ($33),Y = 0400 @ 0400 = 7F A:7F X:F5 Y:00 P:25 SP:FB CYC:310 SL:58 +DA2D 10 08 BPL $DA37 A:FF X:F5 Y:00 P:E4 SP:FB CYC:325 SL:58 +DA2F B0 06 BCS $DA37 A:FF X:F5 Y:00 P:E4 SP:FB CYC:331 SL:58 +DA31 C9 FF CMP #$FF A:FF X:F5 Y:00 P:E4 SP:FB CYC:337 SL:58 +DA33 D0 02 BNE $DA37 A:FF X:F5 Y:00 P:67 SP:FB CYC: 2 SL:59 +DA35 70 02 BVS $DA39 A:FF X:F5 Y:00 P:67 SP:FB CYC: 8 SL:59 +DA39 E8 INX A:FF X:F5 Y:00 P:67 SP:FB CYC: 17 SL:59 +DA3A 18 CLC A:FF X:F6 Y:00 P:E5 SP:FB CYC: 23 SL:59 +DA3B 24 01 BIT $01 = FF A:FF X:F6 Y:00 P:E4 SP:FB CYC: 29 SL:59 +DA3D A9 80 LDA #$80 A:FF X:F6 Y:00 P:E4 SP:FB CYC: 38 SL:59 +DA3F 8D 00 04 STA $0400 = 7F A:80 X:F6 Y:00 P:E4 SP:FB CYC: 44 SL:59 +DA42 A9 7F LDA #$7F A:80 X:F6 Y:00 P:E4 SP:FB CYC: 56 SL:59 +DA44 71 33 ADC ($33),Y = 0400 @ 0400 = 80 A:7F X:F6 Y:00 P:64 SP:FB CYC: 62 SL:59 +DA46 10 08 BPL $DA50 A:FF X:F6 Y:00 P:A4 SP:FB CYC: 77 SL:59 +DA48 B0 06 BCS $DA50 A:FF X:F6 Y:00 P:A4 SP:FB CYC: 83 SL:59 +DA4A C9 FF CMP #$FF A:FF X:F6 Y:00 P:A4 SP:FB CYC: 89 SL:59 +DA4C D0 02 BNE $DA50 A:FF X:F6 Y:00 P:27 SP:FB CYC: 95 SL:59 +DA4E 50 02 BVC $DA52 A:FF X:F6 Y:00 P:27 SP:FB CYC:101 SL:59 +DA52 E8 INX A:FF X:F6 Y:00 P:27 SP:FB CYC:110 SL:59 +DA53 38 SEC A:FF X:F7 Y:00 P:A5 SP:FB CYC:116 SL:59 +DA54 B8 CLV A:FF X:F7 Y:00 P:A5 SP:FB CYC:122 SL:59 +DA55 A9 80 LDA #$80 A:FF X:F7 Y:00 P:A5 SP:FB CYC:128 SL:59 +DA57 8D 00 04 STA $0400 = 80 A:80 X:F7 Y:00 P:A5 SP:FB CYC:134 SL:59 +DA5A A9 7F LDA #$7F A:80 X:F7 Y:00 P:A5 SP:FB CYC:146 SL:59 +DA5C 71 33 ADC ($33),Y = 0400 @ 0400 = 80 A:7F X:F7 Y:00 P:25 SP:FB CYC:152 SL:59 +DA5E D0 06 BNE $DA66 A:00 X:F7 Y:00 P:27 SP:FB CYC:167 SL:59 +DA60 30 04 BMI $DA66 A:00 X:F7 Y:00 P:27 SP:FB CYC:173 SL:59 +DA62 70 02 BVS $DA66 A:00 X:F7 Y:00 P:27 SP:FB CYC:179 SL:59 +DA64 B0 02 BCS $DA68 A:00 X:F7 Y:00 P:27 SP:FB CYC:185 SL:59 +DA68 E8 INX A:00 X:F7 Y:00 P:27 SP:FB CYC:194 SL:59 +DA69 24 01 BIT $01 = FF A:00 X:F8 Y:00 P:A5 SP:FB CYC:200 SL:59 +DA6B A9 40 LDA #$40 A:00 X:F8 Y:00 P:E7 SP:FB CYC:209 SL:59 +DA6D 8D 00 04 STA $0400 = 80 A:40 X:F8 Y:00 P:65 SP:FB CYC:215 SL:59 +DA70 D1 33 CMP ($33),Y = 0400 @ 0400 = 40 A:40 X:F8 Y:00 P:65 SP:FB CYC:227 SL:59 +DA72 30 06 BMI $DA7A A:40 X:F8 Y:00 P:67 SP:FB CYC:242 SL:59 +DA74 90 04 BCC $DA7A A:40 X:F8 Y:00 P:67 SP:FB CYC:248 SL:59 +DA76 D0 02 BNE $DA7A A:40 X:F8 Y:00 P:67 SP:FB CYC:254 SL:59 +DA78 70 02 BVS $DA7C A:40 X:F8 Y:00 P:67 SP:FB CYC:260 SL:59 +DA7C E8 INX A:40 X:F8 Y:00 P:67 SP:FB CYC:269 SL:59 +DA7D B8 CLV A:40 X:F9 Y:00 P:E5 SP:FB CYC:275 SL:59 +DA7E CE 00 04 DEC $0400 = 40 A:40 X:F9 Y:00 P:A5 SP:FB CYC:281 SL:59 +DA81 D1 33 CMP ($33),Y = 0400 @ 0400 = 3F A:40 X:F9 Y:00 P:25 SP:FB CYC:299 SL:59 +DA83 F0 06 BEQ $DA8B A:40 X:F9 Y:00 P:25 SP:FB CYC:314 SL:59 +DA85 30 04 BMI $DA8B A:40 X:F9 Y:00 P:25 SP:FB CYC:320 SL:59 +DA87 90 02 BCC $DA8B A:40 X:F9 Y:00 P:25 SP:FB CYC:326 SL:59 +DA89 50 02 BVC $DA8D A:40 X:F9 Y:00 P:25 SP:FB CYC:332 SL:59 +DA8D E8 INX A:40 X:F9 Y:00 P:25 SP:FB CYC: 0 SL:60 +DA8E EE 00 04 INC $0400 = 3F A:40 X:FA Y:00 P:A5 SP:FB CYC: 6 SL:60 +DA91 EE 00 04 INC $0400 = 40 A:40 X:FA Y:00 P:25 SP:FB CYC: 24 SL:60 +DA94 D1 33 CMP ($33),Y = 0400 @ 0400 = 41 A:40 X:FA Y:00 P:25 SP:FB CYC: 42 SL:60 +DA96 F0 02 BEQ $DA9A A:40 X:FA Y:00 P:A4 SP:FB CYC: 57 SL:60 +DA98 30 02 BMI $DA9C A:40 X:FA Y:00 P:A4 SP:FB CYC: 63 SL:60 +DA9C E8 INX A:40 X:FA Y:00 P:A4 SP:FB CYC: 72 SL:60 +DA9D A9 00 LDA #$00 A:40 X:FB Y:00 P:A4 SP:FB CYC: 78 SL:60 +DA9F 8D 00 04 STA $0400 = 41 A:00 X:FB Y:00 P:26 SP:FB CYC: 84 SL:60 +DAA2 A9 80 LDA #$80 A:00 X:FB Y:00 P:26 SP:FB CYC: 96 SL:60 +DAA4 D1 33 CMP ($33),Y = 0400 @ 0400 = 00 A:80 X:FB Y:00 P:A4 SP:FB CYC:102 SL:60 +DAA6 F0 04 BEQ $DAAC A:80 X:FB Y:00 P:A5 SP:FB CYC:117 SL:60 +DAA8 10 02 BPL $DAAC A:80 X:FB Y:00 P:A5 SP:FB CYC:123 SL:60 +DAAA B0 02 BCS $DAAE A:80 X:FB Y:00 P:A5 SP:FB CYC:129 SL:60 +DAAE E8 INX A:80 X:FB Y:00 P:A5 SP:FB CYC:138 SL:60 +DAAF A0 80 LDY #$80 A:80 X:FC Y:00 P:A5 SP:FB CYC:144 SL:60 +DAB1 8C 00 04 STY $0400 = 00 A:80 X:FC Y:80 P:A5 SP:FB CYC:150 SL:60 +DAB4 A0 00 LDY #$00 A:80 X:FC Y:80 P:A5 SP:FB CYC:162 SL:60 +DAB6 D1 33 CMP ($33),Y = 0400 @ 0400 = 80 A:80 X:FC Y:00 P:27 SP:FB CYC:168 SL:60 +DAB8 D0 04 BNE $DABE A:80 X:FC Y:00 P:27 SP:FB CYC:183 SL:60 +DABA 30 02 BMI $DABE A:80 X:FC Y:00 P:27 SP:FB CYC:189 SL:60 +DABC B0 02 BCS $DAC0 A:80 X:FC Y:00 P:27 SP:FB CYC:195 SL:60 +DAC0 E8 INX A:80 X:FC Y:00 P:27 SP:FB CYC:204 SL:60 +DAC1 EE 00 04 INC $0400 = 80 A:80 X:FD Y:00 P:A5 SP:FB CYC:210 SL:60 +DAC4 D1 33 CMP ($33),Y = 0400 @ 0400 = 81 A:80 X:FD Y:00 P:A5 SP:FB CYC:228 SL:60 +DAC6 B0 04 BCS $DACC A:80 X:FD Y:00 P:A4 SP:FB CYC:243 SL:60 +DAC8 F0 02 BEQ $DACC A:80 X:FD Y:00 P:A4 SP:FB CYC:249 SL:60 +DACA 30 02 BMI $DACE A:80 X:FD Y:00 P:A4 SP:FB CYC:255 SL:60 +DACE E8 INX A:80 X:FD Y:00 P:A4 SP:FB CYC:264 SL:60 +DACF CE 00 04 DEC $0400 = 81 A:80 X:FE Y:00 P:A4 SP:FB CYC:270 SL:60 +DAD2 CE 00 04 DEC $0400 = 80 A:80 X:FE Y:00 P:A4 SP:FB CYC:288 SL:60 +DAD5 D1 33 CMP ($33),Y = 0400 @ 0400 = 7F A:80 X:FE Y:00 P:24 SP:FB CYC:306 SL:60 +DAD7 90 04 BCC $DADD A:80 X:FE Y:00 P:25 SP:FB CYC:321 SL:60 +DAD9 F0 02 BEQ $DADD A:80 X:FE Y:00 P:25 SP:FB CYC:327 SL:60 +DADB 10 02 BPL $DADF A:80 X:FE Y:00 P:25 SP:FB CYC:333 SL:60 +DADF 60 RTS A:80 X:FE Y:00 P:25 SP:FB CYC: 1 SL:61 +C61B A5 00 LDA $00 = 00 A:80 X:FE Y:00 P:25 SP:FD CYC: 19 SL:61 +C61D 85 10 STA $10 = 00 A:00 X:FE Y:00 P:27 SP:FD CYC: 28 SL:61 +C61F A9 00 LDA #$00 A:00 X:FE Y:00 P:27 SP:FD CYC: 37 SL:61 +C621 85 00 STA $00 = 00 A:00 X:FE Y:00 P:27 SP:FD CYC: 43 SL:61 +C623 20 E0 DA JSR $DAE0 A:00 X:FE Y:00 P:27 SP:FD CYC: 52 SL:61 +DAE0 A9 00 LDA #$00 A:00 X:FE Y:00 P:27 SP:FB CYC: 70 SL:61 +DAE2 85 33 STA $33 = 00 A:00 X:FE Y:00 P:27 SP:FB CYC: 76 SL:61 +DAE4 A9 04 LDA #$04 A:00 X:FE Y:00 P:27 SP:FB CYC: 85 SL:61 +DAE6 85 34 STA $34 = 04 A:04 X:FE Y:00 P:25 SP:FB CYC: 91 SL:61 +DAE8 A0 00 LDY #$00 A:04 X:FE Y:00 P:25 SP:FB CYC:100 SL:61 +DAEA A2 01 LDX #$01 A:04 X:FE Y:00 P:27 SP:FB CYC:106 SL:61 +DAEC 24 01 BIT $01 = FF A:04 X:01 Y:00 P:25 SP:FB CYC:112 SL:61 +DAEE A9 40 LDA #$40 A:04 X:01 Y:00 P:E5 SP:FB CYC:121 SL:61 +DAF0 8D 00 04 STA $0400 = 7F A:40 X:01 Y:00 P:65 SP:FB CYC:127 SL:61 +DAF3 38 SEC A:40 X:01 Y:00 P:65 SP:FB CYC:139 SL:61 +DAF4 F1 33 SBC ($33),Y = 0400 @ 0400 = 40 A:40 X:01 Y:00 P:65 SP:FB CYC:145 SL:61 +DAF6 30 0A BMI $DB02 A:00 X:01 Y:00 P:27 SP:FB CYC:160 SL:61 +DAF8 90 08 BCC $DB02 A:00 X:01 Y:00 P:27 SP:FB CYC:166 SL:61 +DAFA D0 06 BNE $DB02 A:00 X:01 Y:00 P:27 SP:FB CYC:172 SL:61 +DAFC 70 04 BVS $DB02 A:00 X:01 Y:00 P:27 SP:FB CYC:178 SL:61 +DAFE C9 00 CMP #$00 A:00 X:01 Y:00 P:27 SP:FB CYC:184 SL:61 +DB00 F0 02 BEQ $DB04 A:00 X:01 Y:00 P:27 SP:FB CYC:190 SL:61 +DB04 E8 INX A:00 X:01 Y:00 P:27 SP:FB CYC:199 SL:61 +DB05 B8 CLV A:00 X:02 Y:00 P:25 SP:FB CYC:205 SL:61 +DB06 38 SEC A:00 X:02 Y:00 P:25 SP:FB CYC:211 SL:61 +DB07 A9 40 LDA #$40 A:00 X:02 Y:00 P:25 SP:FB CYC:217 SL:61 +DB09 CE 00 04 DEC $0400 = 40 A:40 X:02 Y:00 P:25 SP:FB CYC:223 SL:61 +DB0C F1 33 SBC ($33),Y = 0400 @ 0400 = 3F A:40 X:02 Y:00 P:25 SP:FB CYC:241 SL:61 +DB0E F0 0A BEQ $DB1A A:01 X:02 Y:00 P:25 SP:FB CYC:256 SL:61 +DB10 30 08 BMI $DB1A A:01 X:02 Y:00 P:25 SP:FB CYC:262 SL:61 +DB12 90 06 BCC $DB1A A:01 X:02 Y:00 P:25 SP:FB CYC:268 SL:61 +DB14 70 04 BVS $DB1A A:01 X:02 Y:00 P:25 SP:FB CYC:274 SL:61 +DB16 C9 01 CMP #$01 A:01 X:02 Y:00 P:25 SP:FB CYC:280 SL:61 +DB18 F0 02 BEQ $DB1C A:01 X:02 Y:00 P:27 SP:FB CYC:286 SL:61 +DB1C E8 INX A:01 X:02 Y:00 P:27 SP:FB CYC:295 SL:61 +DB1D A9 40 LDA #$40 A:01 X:03 Y:00 P:25 SP:FB CYC:301 SL:61 +DB1F 38 SEC A:40 X:03 Y:00 P:25 SP:FB CYC:307 SL:61 +DB20 24 01 BIT $01 = FF A:40 X:03 Y:00 P:25 SP:FB CYC:313 SL:61 +DB22 EE 00 04 INC $0400 = 3F A:40 X:03 Y:00 P:E5 SP:FB CYC:322 SL:61 +DB25 EE 00 04 INC $0400 = 40 A:40 X:03 Y:00 P:65 SP:FB CYC:340 SL:61 +DB28 F1 33 SBC ($33),Y = 0400 @ 0400 = 41 A:40 X:03 Y:00 P:65 SP:FB CYC: 17 SL:62 +DB2A B0 0A BCS $DB36 A:FF X:03 Y:00 P:A4 SP:FB CYC: 32 SL:62 +DB2C F0 08 BEQ $DB36 A:FF X:03 Y:00 P:A4 SP:FB CYC: 38 SL:62 +DB2E 10 06 BPL $DB36 A:FF X:03 Y:00 P:A4 SP:FB CYC: 44 SL:62 +DB30 70 04 BVS $DB36 A:FF X:03 Y:00 P:A4 SP:FB CYC: 50 SL:62 +DB32 C9 FF CMP #$FF A:FF X:03 Y:00 P:A4 SP:FB CYC: 56 SL:62 +DB34 F0 02 BEQ $DB38 A:FF X:03 Y:00 P:27 SP:FB CYC: 62 SL:62 +DB38 E8 INX A:FF X:03 Y:00 P:27 SP:FB CYC: 71 SL:62 +DB39 18 CLC A:FF X:04 Y:00 P:25 SP:FB CYC: 77 SL:62 +DB3A A9 00 LDA #$00 A:FF X:04 Y:00 P:24 SP:FB CYC: 83 SL:62 +DB3C 8D 00 04 STA $0400 = 41 A:00 X:04 Y:00 P:26 SP:FB CYC: 89 SL:62 +DB3F A9 80 LDA #$80 A:00 X:04 Y:00 P:26 SP:FB CYC:101 SL:62 +DB41 F1 33 SBC ($33),Y = 0400 @ 0400 = 00 A:80 X:04 Y:00 P:A4 SP:FB CYC:107 SL:62 +DB43 90 04 BCC $DB49 A:7F X:04 Y:00 P:65 SP:FB CYC:122 SL:62 +DB45 C9 7F CMP #$7F A:7F X:04 Y:00 P:65 SP:FB CYC:128 SL:62 +DB47 F0 02 BEQ $DB4B A:7F X:04 Y:00 P:67 SP:FB CYC:134 SL:62 +DB4B E8 INX A:7F X:04 Y:00 P:67 SP:FB CYC:143 SL:62 +DB4C 38 SEC A:7F X:05 Y:00 P:65 SP:FB CYC:149 SL:62 +DB4D A9 7F LDA #$7F A:7F X:05 Y:00 P:65 SP:FB CYC:155 SL:62 +DB4F 8D 00 04 STA $0400 = 00 A:7F X:05 Y:00 P:65 SP:FB CYC:161 SL:62 +DB52 A9 81 LDA #$81 A:7F X:05 Y:00 P:65 SP:FB CYC:173 SL:62 +DB54 F1 33 SBC ($33),Y = 0400 @ 0400 = 7F A:81 X:05 Y:00 P:E5 SP:FB CYC:179 SL:62 +DB56 50 06 BVC $DB5E A:02 X:05 Y:00 P:65 SP:FB CYC:194 SL:62 +DB58 90 04 BCC $DB5E A:02 X:05 Y:00 P:65 SP:FB CYC:200 SL:62 +DB5A C9 02 CMP #$02 A:02 X:05 Y:00 P:65 SP:FB CYC:206 SL:62 +DB5C F0 02 BEQ $DB60 A:02 X:05 Y:00 P:67 SP:FB CYC:212 SL:62 +DB60 E8 INX A:02 X:05 Y:00 P:67 SP:FB CYC:221 SL:62 +DB61 A9 00 LDA #$00 A:02 X:06 Y:00 P:65 SP:FB CYC:227 SL:62 +DB63 A9 87 LDA #$87 A:00 X:06 Y:00 P:67 SP:FB CYC:233 SL:62 +DB65 91 33 STA ($33),Y = 0400 @ 0400 = 7F A:87 X:06 Y:00 P:E5 SP:FB CYC:239 SL:62 +DB67 AD 00 04 LDA $0400 = 87 A:87 X:06 Y:00 P:E5 SP:FB CYC:257 SL:62 +DB6A C9 87 CMP #$87 A:87 X:06 Y:00 P:E5 SP:FB CYC:269 SL:62 +DB6C F0 02 BEQ $DB70 A:87 X:06 Y:00 P:67 SP:FB CYC:275 SL:62 +DB70 E8 INX A:87 X:06 Y:00 P:67 SP:FB CYC:284 SL:62 +DB71 A9 7E LDA #$7E A:87 X:07 Y:00 P:65 SP:FB CYC:290 SL:62 +DB73 8D 00 02 STA $0200 = 7F A:7E X:07 Y:00 P:65 SP:FB CYC:296 SL:62 +DB76 A9 DB LDA #$DB A:7E X:07 Y:00 P:65 SP:FB CYC:308 SL:62 +DB78 8D 01 02 STA $0201 = 00 A:DB X:07 Y:00 P:E5 SP:FB CYC:314 SL:62 +DB7B 6C 00 02 JMP ($0200) = DB7E A:DB X:07 Y:00 P:E5 SP:FB CYC:326 SL:62 +DB7E A9 00 LDA #$00 A:DB X:07 Y:00 P:E5 SP:FB CYC: 0 SL:63 +DB80 8D FF 02 STA $02FF = 00 A:00 X:07 Y:00 P:67 SP:FB CYC: 6 SL:63 +DB83 A9 01 LDA #$01 A:00 X:07 Y:00 P:67 SP:FB CYC: 18 SL:63 +DB85 8D 00 03 STA $0300 = 89 A:01 X:07 Y:00 P:65 SP:FB CYC: 24 SL:63 +DB88 A9 03 LDA #$03 A:01 X:07 Y:00 P:65 SP:FB CYC: 36 SL:63 +DB8A 8D 00 02 STA $0200 = 7E A:03 X:07 Y:00 P:65 SP:FB CYC: 42 SL:63 +DB8D A9 A9 LDA #$A9 A:03 X:07 Y:00 P:65 SP:FB CYC: 54 SL:63 +DB8F 8D 00 01 STA $0100 = 00 A:A9 X:07 Y:00 P:E5 SP:FB CYC: 60 SL:63 +DB92 A9 55 LDA #$55 A:A9 X:07 Y:00 P:E5 SP:FB CYC: 72 SL:63 +DB94 8D 01 01 STA $0101 = 00 A:55 X:07 Y:00 P:65 SP:FB CYC: 78 SL:63 +DB97 A9 60 LDA #$60 A:55 X:07 Y:00 P:65 SP:FB CYC: 90 SL:63 +DB99 8D 02 01 STA $0102 = 00 A:60 X:07 Y:00 P:65 SP:FB CYC: 96 SL:63 +DB9C A9 A9 LDA #$A9 A:60 X:07 Y:00 P:65 SP:FB CYC:108 SL:63 +DB9E 8D 00 03 STA $0300 = 01 A:A9 X:07 Y:00 P:E5 SP:FB CYC:114 SL:63 +DBA1 A9 AA LDA #$AA A:A9 X:07 Y:00 P:E5 SP:FB CYC:126 SL:63 +DBA3 8D 01 03 STA $0301 = 00 A:AA X:07 Y:00 P:E5 SP:FB CYC:132 SL:63 +DBA6 A9 60 LDA #$60 A:AA X:07 Y:00 P:E5 SP:FB CYC:144 SL:63 +DBA8 8D 02 03 STA $0302 = 00 A:60 X:07 Y:00 P:65 SP:FB CYC:150 SL:63 +DBAB 20 B5 DB JSR $DBB5 A:60 X:07 Y:00 P:65 SP:FB CYC:162 SL:63 +DBB5 6C FF 02 JMP ($02FF) = A900 A:60 X:07 Y:00 P:65 SP:F9 CYC:180 SL:63 +0300 A9 AA LDA #$AA A:60 X:07 Y:00 P:65 SP:F9 CYC:195 SL:63 +0302 60 RTS A:AA X:07 Y:00 P:E5 SP:F9 CYC:201 SL:63 +DBAE C9 AA CMP #$AA A:AA X:07 Y:00 P:E5 SP:FB CYC:219 SL:63 +DBB0 F0 02 BEQ $DBB4 A:AA X:07 Y:00 P:67 SP:FB CYC:225 SL:63 +DBB4 60 RTS A:AA X:07 Y:00 P:67 SP:FB CYC:234 SL:63 +C626 20 4A DF JSR $DF4A A:AA X:07 Y:00 P:67 SP:FD CYC:252 SL:63 +DF4A A9 89 LDA #$89 A:AA X:07 Y:00 P:67 SP:FB CYC:270 SL:63 +DF4C 8D 00 03 STA $0300 = A9 A:89 X:07 Y:00 P:E5 SP:FB CYC:276 SL:63 +DF4F A9 A3 LDA #$A3 A:89 X:07 Y:00 P:E5 SP:FB CYC:288 SL:63 +DF51 85 33 STA $33 = 00 A:A3 X:07 Y:00 P:E5 SP:FB CYC:294 SL:63 +DF53 A9 12 LDA #$12 A:A3 X:07 Y:00 P:E5 SP:FB CYC:303 SL:63 +DF55 8D 45 02 STA $0245 = 12 A:12 X:07 Y:00 P:65 SP:FB CYC:309 SL:63 +DF58 A2 65 LDX #$65 A:12 X:07 Y:00 P:65 SP:FB CYC:321 SL:63 +DF5A A0 00 LDY #$00 A:12 X:65 Y:00 P:65 SP:FB CYC:327 SL:63 +DF5C 38 SEC A:12 X:65 Y:00 P:67 SP:FB CYC:333 SL:63 +DF5D A9 00 LDA #$00 A:12 X:65 Y:00 P:67 SP:FB CYC:339 SL:63 +DF5F B8 CLV A:00 X:65 Y:00 P:67 SP:FB CYC: 4 SL:64 +DF60 B9 00 03 LDA $0300,Y @ 0300 = 89 A:00 X:65 Y:00 P:27 SP:FB CYC: 10 SL:64 +DF63 F0 0C BEQ $DF71 A:89 X:65 Y:00 P:A5 SP:FB CYC: 22 SL:64 +DF65 90 0A BCC $DF71 A:89 X:65 Y:00 P:A5 SP:FB CYC: 28 SL:64 +DF67 70 08 BVS $DF71 A:89 X:65 Y:00 P:A5 SP:FB CYC: 34 SL:64 +DF69 C9 89 CMP #$89 A:89 X:65 Y:00 P:A5 SP:FB CYC: 40 SL:64 +DF6B D0 04 BNE $DF71 A:89 X:65 Y:00 P:27 SP:FB CYC: 46 SL:64 +DF6D E0 65 CPX #$65 A:89 X:65 Y:00 P:27 SP:FB CYC: 52 SL:64 +DF6F F0 04 BEQ $DF75 A:89 X:65 Y:00 P:27 SP:FB CYC: 58 SL:64 +DF75 A9 FF LDA #$FF A:89 X:65 Y:00 P:27 SP:FB CYC: 67 SL:64 +DF77 85 01 STA $01 = FF A:FF X:65 Y:00 P:A5 SP:FB CYC: 73 SL:64 +DF79 24 01 BIT $01 = FF A:FF X:65 Y:00 P:A5 SP:FB CYC: 82 SL:64 +DF7B A0 34 LDY #$34 A:FF X:65 Y:00 P:E5 SP:FB CYC: 91 SL:64 +DF7D B9 FF FF LDA $FFFF,Y @ 0033 = A3 A:FF X:65 Y:34 P:65 SP:FB CYC: 97 SL:64 +DF80 C9 A3 CMP #$A3 A:A3 X:65 Y:34 P:E5 SP:FB CYC:112 SL:64 +DF82 D0 02 BNE $DF86 A:A3 X:65 Y:34 P:67 SP:FB CYC:118 SL:64 +DF84 B0 04 BCS $DF8A A:A3 X:65 Y:34 P:67 SP:FB CYC:124 SL:64 +DF8A A9 46 LDA #$46 A:A3 X:65 Y:34 P:67 SP:FB CYC:133 SL:64 +DF8C 85 FF STA $FF = 46 A:46 X:65 Y:34 P:65 SP:FB CYC:139 SL:64 +DF8E A0 FF LDY #$FF A:46 X:65 Y:34 P:65 SP:FB CYC:148 SL:64 +DF90 B9 46 01 LDA $0146,Y @ 0245 = 12 A:46 X:65 Y:FF P:E5 SP:FB CYC:154 SL:64 +DF93 C9 12 CMP #$12 A:12 X:65 Y:FF P:65 SP:FB CYC:169 SL:64 +DF95 F0 04 BEQ $DF9B A:12 X:65 Y:FF P:67 SP:FB CYC:175 SL:64 +DF9B A2 39 LDX #$39 A:12 X:65 Y:FF P:67 SP:FB CYC:184 SL:64 +DF9D 18 CLC A:12 X:39 Y:FF P:65 SP:FB CYC:190 SL:64 +DF9E A9 FF LDA #$FF A:12 X:39 Y:FF P:64 SP:FB CYC:196 SL:64 +DFA0 85 01 STA $01 = FF A:FF X:39 Y:FF P:E4 SP:FB CYC:202 SL:64 +DFA2 24 01 BIT $01 = FF A:FF X:39 Y:FF P:E4 SP:FB CYC:211 SL:64 +DFA4 A9 AA LDA #$AA A:FF X:39 Y:FF P:E4 SP:FB CYC:220 SL:64 +DFA6 8D 00 04 STA $0400 = 87 A:AA X:39 Y:FF P:E4 SP:FB CYC:226 SL:64 +DFA9 A9 55 LDA #$55 A:AA X:39 Y:FF P:E4 SP:FB CYC:238 SL:64 +DFAB A0 00 LDY #$00 A:55 X:39 Y:FF P:64 SP:FB CYC:244 SL:64 +DFAD 19 00 04 ORA $0400,Y @ 0400 = AA A:55 X:39 Y:00 P:66 SP:FB CYC:250 SL:64 +DFB0 B0 08 BCS $DFBA A:FF X:39 Y:00 P:E4 SP:FB CYC:262 SL:64 +DFB2 10 06 BPL $DFBA A:FF X:39 Y:00 P:E4 SP:FB CYC:268 SL:64 +DFB4 C9 FF CMP #$FF A:FF X:39 Y:00 P:E4 SP:FB CYC:274 SL:64 +DFB6 D0 02 BNE $DFBA A:FF X:39 Y:00 P:67 SP:FB CYC:280 SL:64 +DFB8 70 02 BVS $DFBC A:FF X:39 Y:00 P:67 SP:FB CYC:286 SL:64 +DFBC E8 INX A:FF X:39 Y:00 P:67 SP:FB CYC:295 SL:64 +DFBD 38 SEC A:FF X:3A Y:00 P:65 SP:FB CYC:301 SL:64 +DFBE B8 CLV A:FF X:3A Y:00 P:65 SP:FB CYC:307 SL:64 +DFBF A9 00 LDA #$00 A:FF X:3A Y:00 P:25 SP:FB CYC:313 SL:64 +DFC1 19 00 04 ORA $0400,Y @ 0400 = AA A:00 X:3A Y:00 P:27 SP:FB CYC:319 SL:64 +DFC4 F0 06 BEQ $DFCC A:AA X:3A Y:00 P:A5 SP:FB CYC:331 SL:64 +DFC6 70 04 BVS $DFCC A:AA X:3A Y:00 P:A5 SP:FB CYC:337 SL:64 +DFC8 90 02 BCC $DFCC A:AA X:3A Y:00 P:A5 SP:FB CYC: 2 SL:65 +DFCA 30 02 BMI $DFCE A:AA X:3A Y:00 P:A5 SP:FB CYC: 8 SL:65 +DFCE E8 INX A:AA X:3A Y:00 P:A5 SP:FB CYC: 17 SL:65 +DFCF 18 CLC A:AA X:3B Y:00 P:25 SP:FB CYC: 23 SL:65 +DFD0 24 01 BIT $01 = FF A:AA X:3B Y:00 P:24 SP:FB CYC: 29 SL:65 +DFD2 A9 55 LDA #$55 A:AA X:3B Y:00 P:E4 SP:FB CYC: 38 SL:65 +DFD4 39 00 04 AND $0400,Y @ 0400 = AA A:55 X:3B Y:00 P:64 SP:FB CYC: 44 SL:65 +DFD7 D0 06 BNE $DFDF A:00 X:3B Y:00 P:66 SP:FB CYC: 56 SL:65 +DFD9 50 04 BVC $DFDF A:00 X:3B Y:00 P:66 SP:FB CYC: 62 SL:65 +DFDB B0 02 BCS $DFDF A:00 X:3B Y:00 P:66 SP:FB CYC: 68 SL:65 +DFDD 10 02 BPL $DFE1 A:00 X:3B Y:00 P:66 SP:FB CYC: 74 SL:65 +DFE1 E8 INX A:00 X:3B Y:00 P:66 SP:FB CYC: 83 SL:65 +DFE2 38 SEC A:00 X:3C Y:00 P:64 SP:FB CYC: 89 SL:65 +DFE3 B8 CLV A:00 X:3C Y:00 P:65 SP:FB CYC: 95 SL:65 +DFE4 A9 EF LDA #$EF A:00 X:3C Y:00 P:25 SP:FB CYC:101 SL:65 +DFE6 8D 00 04 STA $0400 = AA A:EF X:3C Y:00 P:A5 SP:FB CYC:107 SL:65 +DFE9 A9 F8 LDA #$F8 A:EF X:3C Y:00 P:A5 SP:FB CYC:119 SL:65 +DFEB 39 00 04 AND $0400,Y @ 0400 = EF A:F8 X:3C Y:00 P:A5 SP:FB CYC:125 SL:65 +DFEE 90 08 BCC $DFF8 A:E8 X:3C Y:00 P:A5 SP:FB CYC:137 SL:65 +DFF0 10 06 BPL $DFF8 A:E8 X:3C Y:00 P:A5 SP:FB CYC:143 SL:65 +DFF2 C9 E8 CMP #$E8 A:E8 X:3C Y:00 P:A5 SP:FB CYC:149 SL:65 +DFF4 D0 02 BNE $DFF8 A:E8 X:3C Y:00 P:27 SP:FB CYC:155 SL:65 +DFF6 50 02 BVC $DFFA A:E8 X:3C Y:00 P:27 SP:FB CYC:161 SL:65 +DFFA E8 INX A:E8 X:3C Y:00 P:27 SP:FB CYC:170 SL:65 +DFFB 18 CLC A:E8 X:3D Y:00 P:25 SP:FB CYC:176 SL:65 +DFFC 24 01 BIT $01 = FF A:E8 X:3D Y:00 P:24 SP:FB CYC:182 SL:65 +DFFE A9 AA LDA #$AA A:E8 X:3D Y:00 P:E4 SP:FB CYC:191 SL:65 +E000 8D 00 04 STA $0400 = EF A:AA X:3D Y:00 P:E4 SP:FB CYC:197 SL:65 +E003 A9 5F LDA #$5F A:AA X:3D Y:00 P:E4 SP:FB CYC:209 SL:65 +E005 59 00 04 EOR $0400,Y @ 0400 = AA A:5F X:3D Y:00 P:64 SP:FB CYC:215 SL:65 +E008 B0 08 BCS $E012 A:F5 X:3D Y:00 P:E4 SP:FB CYC:227 SL:65 +E00A 10 06 BPL $E012 A:F5 X:3D Y:00 P:E4 SP:FB CYC:233 SL:65 +E00C C9 F5 CMP #$F5 A:F5 X:3D Y:00 P:E4 SP:FB CYC:239 SL:65 +E00E D0 02 BNE $E012 A:F5 X:3D Y:00 P:67 SP:FB CYC:245 SL:65 +E010 70 02 BVS $E014 A:F5 X:3D Y:00 P:67 SP:FB CYC:251 SL:65 +E014 E8 INX A:F5 X:3D Y:00 P:67 SP:FB CYC:260 SL:65 +E015 38 SEC A:F5 X:3E Y:00 P:65 SP:FB CYC:266 SL:65 +E016 B8 CLV A:F5 X:3E Y:00 P:65 SP:FB CYC:272 SL:65 +E017 A9 70 LDA #$70 A:F5 X:3E Y:00 P:25 SP:FB CYC:278 SL:65 +E019 8D 00 04 STA $0400 = AA A:70 X:3E Y:00 P:25 SP:FB CYC:284 SL:65 +E01C 59 00 04 EOR $0400,Y @ 0400 = 70 A:70 X:3E Y:00 P:25 SP:FB CYC:296 SL:65 +E01F D0 06 BNE $E027 A:00 X:3E Y:00 P:27 SP:FB CYC:308 SL:65 +E021 70 04 BVS $E027 A:00 X:3E Y:00 P:27 SP:FB CYC:314 SL:65 +E023 90 02 BCC $E027 A:00 X:3E Y:00 P:27 SP:FB CYC:320 SL:65 +E025 10 02 BPL $E029 A:00 X:3E Y:00 P:27 SP:FB CYC:326 SL:65 +E029 E8 INX A:00 X:3E Y:00 P:27 SP:FB CYC:335 SL:65 +E02A 18 CLC A:00 X:3F Y:00 P:25 SP:FB CYC: 0 SL:66 +E02B 24 01 BIT $01 = FF A:00 X:3F Y:00 P:24 SP:FB CYC: 6 SL:66 +E02D A9 69 LDA #$69 A:00 X:3F Y:00 P:E6 SP:FB CYC: 15 SL:66 +E02F 8D 00 04 STA $0400 = 70 A:69 X:3F Y:00 P:64 SP:FB CYC: 21 SL:66 +E032 A9 00 LDA #$00 A:69 X:3F Y:00 P:64 SP:FB CYC: 33 SL:66 +E034 79 00 04 ADC $0400,Y @ 0400 = 69 A:00 X:3F Y:00 P:66 SP:FB CYC: 39 SL:66 +E037 30 08 BMI $E041 A:69 X:3F Y:00 P:24 SP:FB CYC: 51 SL:66 +E039 B0 06 BCS $E041 A:69 X:3F Y:00 P:24 SP:FB CYC: 57 SL:66 +E03B C9 69 CMP #$69 A:69 X:3F Y:00 P:24 SP:FB CYC: 63 SL:66 +E03D D0 02 BNE $E041 A:69 X:3F Y:00 P:27 SP:FB CYC: 69 SL:66 +E03F 50 02 BVC $E043 A:69 X:3F Y:00 P:27 SP:FB CYC: 75 SL:66 +E043 E8 INX A:69 X:3F Y:00 P:27 SP:FB CYC: 84 SL:66 +E044 38 SEC A:69 X:40 Y:00 P:25 SP:FB CYC: 90 SL:66 +E045 24 01 BIT $01 = FF A:69 X:40 Y:00 P:25 SP:FB CYC: 96 SL:66 +E047 A9 00 LDA #$00 A:69 X:40 Y:00 P:E5 SP:FB CYC:105 SL:66 +E049 79 00 04 ADC $0400,Y @ 0400 = 69 A:00 X:40 Y:00 P:67 SP:FB CYC:111 SL:66 +E04C 30 08 BMI $E056 A:6A X:40 Y:00 P:24 SP:FB CYC:123 SL:66 +E04E B0 06 BCS $E056 A:6A X:40 Y:00 P:24 SP:FB CYC:129 SL:66 +E050 C9 6A CMP #$6A A:6A X:40 Y:00 P:24 SP:FB CYC:135 SL:66 +E052 D0 02 BNE $E056 A:6A X:40 Y:00 P:27 SP:FB CYC:141 SL:66 +E054 50 02 BVC $E058 A:6A X:40 Y:00 P:27 SP:FB CYC:147 SL:66 +E058 E8 INX A:6A X:40 Y:00 P:27 SP:FB CYC:156 SL:66 +E059 38 SEC A:6A X:41 Y:00 P:25 SP:FB CYC:162 SL:66 +E05A B8 CLV A:6A X:41 Y:00 P:25 SP:FB CYC:168 SL:66 +E05B A9 7F LDA #$7F A:6A X:41 Y:00 P:25 SP:FB CYC:174 SL:66 +E05D 8D 00 04 STA $0400 = 69 A:7F X:41 Y:00 P:25 SP:FB CYC:180 SL:66 +E060 79 00 04 ADC $0400,Y @ 0400 = 7F A:7F X:41 Y:00 P:25 SP:FB CYC:192 SL:66 +E063 10 08 BPL $E06D A:FF X:41 Y:00 P:E4 SP:FB CYC:204 SL:66 +E065 B0 06 BCS $E06D A:FF X:41 Y:00 P:E4 SP:FB CYC:210 SL:66 +E067 C9 FF CMP #$FF A:FF X:41 Y:00 P:E4 SP:FB CYC:216 SL:66 +E069 D0 02 BNE $E06D A:FF X:41 Y:00 P:67 SP:FB CYC:222 SL:66 +E06B 70 02 BVS $E06F A:FF X:41 Y:00 P:67 SP:FB CYC:228 SL:66 +E06F E8 INX A:FF X:41 Y:00 P:67 SP:FB CYC:237 SL:66 +E070 18 CLC A:FF X:42 Y:00 P:65 SP:FB CYC:243 SL:66 +E071 24 01 BIT $01 = FF A:FF X:42 Y:00 P:64 SP:FB CYC:249 SL:66 +E073 A9 80 LDA #$80 A:FF X:42 Y:00 P:E4 SP:FB CYC:258 SL:66 +E075 8D 00 04 STA $0400 = 7F A:80 X:42 Y:00 P:E4 SP:FB CYC:264 SL:66 +E078 A9 7F LDA #$7F A:80 X:42 Y:00 P:E4 SP:FB CYC:276 SL:66 +E07A 79 00 04 ADC $0400,Y @ 0400 = 80 A:7F X:42 Y:00 P:64 SP:FB CYC:282 SL:66 +E07D 10 08 BPL $E087 A:FF X:42 Y:00 P:A4 SP:FB CYC:294 SL:66 +E07F B0 06 BCS $E087 A:FF X:42 Y:00 P:A4 SP:FB CYC:300 SL:66 +E081 C9 FF CMP #$FF A:FF X:42 Y:00 P:A4 SP:FB CYC:306 SL:66 +E083 D0 02 BNE $E087 A:FF X:42 Y:00 P:27 SP:FB CYC:312 SL:66 +E085 50 02 BVC $E089 A:FF X:42 Y:00 P:27 SP:FB CYC:318 SL:66 +E089 E8 INX A:FF X:42 Y:00 P:27 SP:FB CYC:327 SL:66 +E08A 38 SEC A:FF X:43 Y:00 P:25 SP:FB CYC:333 SL:66 +E08B B8 CLV A:FF X:43 Y:00 P:25 SP:FB CYC:339 SL:66 +E08C A9 80 LDA #$80 A:FF X:43 Y:00 P:25 SP:FB CYC: 4 SL:67 +E08E 8D 00 04 STA $0400 = 80 A:80 X:43 Y:00 P:A5 SP:FB CYC: 10 SL:67 +E091 A9 7F LDA #$7F A:80 X:43 Y:00 P:A5 SP:FB CYC: 22 SL:67 +E093 79 00 04 ADC $0400,Y @ 0400 = 80 A:7F X:43 Y:00 P:25 SP:FB CYC: 28 SL:67 +E096 D0 06 BNE $E09E A:00 X:43 Y:00 P:27 SP:FB CYC: 40 SL:67 +E098 30 04 BMI $E09E A:00 X:43 Y:00 P:27 SP:FB CYC: 46 SL:67 +E09A 70 02 BVS $E09E A:00 X:43 Y:00 P:27 SP:FB CYC: 52 SL:67 +E09C B0 02 BCS $E0A0 A:00 X:43 Y:00 P:27 SP:FB CYC: 58 SL:67 +E0A0 E8 INX A:00 X:43 Y:00 P:27 SP:FB CYC: 67 SL:67 +E0A1 24 01 BIT $01 = FF A:00 X:44 Y:00 P:25 SP:FB CYC: 73 SL:67 +E0A3 A9 40 LDA #$40 A:00 X:44 Y:00 P:E7 SP:FB CYC: 82 SL:67 +E0A5 8D 00 04 STA $0400 = 80 A:40 X:44 Y:00 P:65 SP:FB CYC: 88 SL:67 +E0A8 D9 00 04 CMP $0400,Y @ 0400 = 40 A:40 X:44 Y:00 P:65 SP:FB CYC:100 SL:67 +E0AB 30 06 BMI $E0B3 A:40 X:44 Y:00 P:67 SP:FB CYC:112 SL:67 +E0AD 90 04 BCC $E0B3 A:40 X:44 Y:00 P:67 SP:FB CYC:118 SL:67 +E0AF D0 02 BNE $E0B3 A:40 X:44 Y:00 P:67 SP:FB CYC:124 SL:67 +E0B1 70 02 BVS $E0B5 A:40 X:44 Y:00 P:67 SP:FB CYC:130 SL:67 +E0B5 E8 INX A:40 X:44 Y:00 P:67 SP:FB CYC:139 SL:67 +E0B6 B8 CLV A:40 X:45 Y:00 P:65 SP:FB CYC:145 SL:67 +E0B7 CE 00 04 DEC $0400 = 40 A:40 X:45 Y:00 P:25 SP:FB CYC:151 SL:67 +E0BA D9 00 04 CMP $0400,Y @ 0400 = 3F A:40 X:45 Y:00 P:25 SP:FB CYC:169 SL:67 +E0BD F0 06 BEQ $E0C5 A:40 X:45 Y:00 P:25 SP:FB CYC:181 SL:67 +E0BF 30 04 BMI $E0C5 A:40 X:45 Y:00 P:25 SP:FB CYC:187 SL:67 +E0C1 90 02 BCC $E0C5 A:40 X:45 Y:00 P:25 SP:FB CYC:193 SL:67 +E0C3 50 02 BVC $E0C7 A:40 X:45 Y:00 P:25 SP:FB CYC:199 SL:67 +E0C7 E8 INX A:40 X:45 Y:00 P:25 SP:FB CYC:208 SL:67 +E0C8 EE 00 04 INC $0400 = 3F A:40 X:46 Y:00 P:25 SP:FB CYC:214 SL:67 +E0CB EE 00 04 INC $0400 = 40 A:40 X:46 Y:00 P:25 SP:FB CYC:232 SL:67 +E0CE D9 00 04 CMP $0400,Y @ 0400 = 41 A:40 X:46 Y:00 P:25 SP:FB CYC:250 SL:67 +E0D1 F0 02 BEQ $E0D5 A:40 X:46 Y:00 P:A4 SP:FB CYC:262 SL:67 +E0D3 30 02 BMI $E0D7 A:40 X:46 Y:00 P:A4 SP:FB CYC:268 SL:67 +E0D7 E8 INX A:40 X:46 Y:00 P:A4 SP:FB CYC:277 SL:67 +E0D8 A9 00 LDA #$00 A:40 X:47 Y:00 P:24 SP:FB CYC:283 SL:67 +E0DA 8D 00 04 STA $0400 = 41 A:00 X:47 Y:00 P:26 SP:FB CYC:289 SL:67 +E0DD A9 80 LDA #$80 A:00 X:47 Y:00 P:26 SP:FB CYC:301 SL:67 +E0DF D9 00 04 CMP $0400,Y @ 0400 = 00 A:80 X:47 Y:00 P:A4 SP:FB CYC:307 SL:67 +E0E2 F0 04 BEQ $E0E8 A:80 X:47 Y:00 P:A5 SP:FB CYC:319 SL:67 +E0E4 10 02 BPL $E0E8 A:80 X:47 Y:00 P:A5 SP:FB CYC:325 SL:67 +E0E6 B0 02 BCS $E0EA A:80 X:47 Y:00 P:A5 SP:FB CYC:331 SL:67 +E0EA E8 INX A:80 X:47 Y:00 P:A5 SP:FB CYC:340 SL:67 +E0EB A0 80 LDY #$80 A:80 X:48 Y:00 P:25 SP:FB CYC: 5 SL:68 +E0ED 8C 00 04 STY $0400 = 00 A:80 X:48 Y:80 P:A5 SP:FB CYC: 11 SL:68 +E0F0 A0 00 LDY #$00 A:80 X:48 Y:80 P:A5 SP:FB CYC: 23 SL:68 +E0F2 D9 00 04 CMP $0400,Y @ 0400 = 80 A:80 X:48 Y:00 P:27 SP:FB CYC: 29 SL:68 +E0F5 D0 04 BNE $E0FB A:80 X:48 Y:00 P:27 SP:FB CYC: 41 SL:68 +E0F7 30 02 BMI $E0FB A:80 X:48 Y:00 P:27 SP:FB CYC: 47 SL:68 +E0F9 B0 02 BCS $E0FD A:80 X:48 Y:00 P:27 SP:FB CYC: 53 SL:68 +E0FD E8 INX A:80 X:48 Y:00 P:27 SP:FB CYC: 62 SL:68 +E0FE EE 00 04 INC $0400 = 80 A:80 X:49 Y:00 P:25 SP:FB CYC: 68 SL:68 +E101 D9 00 04 CMP $0400,Y @ 0400 = 81 A:80 X:49 Y:00 P:A5 SP:FB CYC: 86 SL:68 +E104 B0 04 BCS $E10A A:80 X:49 Y:00 P:A4 SP:FB CYC: 98 SL:68 +E106 F0 02 BEQ $E10A A:80 X:49 Y:00 P:A4 SP:FB CYC:104 SL:68 +E108 30 02 BMI $E10C A:80 X:49 Y:00 P:A4 SP:FB CYC:110 SL:68 +E10C E8 INX A:80 X:49 Y:00 P:A4 SP:FB CYC:119 SL:68 +E10D CE 00 04 DEC $0400 = 81 A:80 X:4A Y:00 P:24 SP:FB CYC:125 SL:68 +E110 CE 00 04 DEC $0400 = 80 A:80 X:4A Y:00 P:A4 SP:FB CYC:143 SL:68 +E113 D9 00 04 CMP $0400,Y @ 0400 = 7F A:80 X:4A Y:00 P:24 SP:FB CYC:161 SL:68 +E116 90 04 BCC $E11C A:80 X:4A Y:00 P:25 SP:FB CYC:173 SL:68 +E118 F0 02 BEQ $E11C A:80 X:4A Y:00 P:25 SP:FB CYC:179 SL:68 +E11A 10 02 BPL $E11E A:80 X:4A Y:00 P:25 SP:FB CYC:185 SL:68 +E11E E8 INX A:80 X:4A Y:00 P:25 SP:FB CYC:194 SL:68 +E11F 24 01 BIT $01 = FF A:80 X:4B Y:00 P:25 SP:FB CYC:200 SL:68 +E121 A9 40 LDA #$40 A:80 X:4B Y:00 P:E5 SP:FB CYC:209 SL:68 +E123 8D 00 04 STA $0400 = 7F A:40 X:4B Y:00 P:65 SP:FB CYC:215 SL:68 +E126 38 SEC A:40 X:4B Y:00 P:65 SP:FB CYC:227 SL:68 +E127 F9 00 04 SBC $0400,Y @ 0400 = 40 A:40 X:4B Y:00 P:65 SP:FB CYC:233 SL:68 +E12A 30 0A BMI $E136 A:00 X:4B Y:00 P:27 SP:FB CYC:245 SL:68 +E12C 90 08 BCC $E136 A:00 X:4B Y:00 P:27 SP:FB CYC:251 SL:68 +E12E D0 06 BNE $E136 A:00 X:4B Y:00 P:27 SP:FB CYC:257 SL:68 +E130 70 04 BVS $E136 A:00 X:4B Y:00 P:27 SP:FB CYC:263 SL:68 +E132 C9 00 CMP #$00 A:00 X:4B Y:00 P:27 SP:FB CYC:269 SL:68 +E134 F0 02 BEQ $E138 A:00 X:4B Y:00 P:27 SP:FB CYC:275 SL:68 +E138 E8 INX A:00 X:4B Y:00 P:27 SP:FB CYC:284 SL:68 +E139 B8 CLV A:00 X:4C Y:00 P:25 SP:FB CYC:290 SL:68 +E13A 38 SEC A:00 X:4C Y:00 P:25 SP:FB CYC:296 SL:68 +E13B A9 40 LDA #$40 A:00 X:4C Y:00 P:25 SP:FB CYC:302 SL:68 +E13D CE 00 04 DEC $0400 = 40 A:40 X:4C Y:00 P:25 SP:FB CYC:308 SL:68 +E140 F9 00 04 SBC $0400,Y @ 0400 = 3F A:40 X:4C Y:00 P:25 SP:FB CYC:326 SL:68 +E143 F0 0A BEQ $E14F A:01 X:4C Y:00 P:25 SP:FB CYC:338 SL:68 +E145 30 08 BMI $E14F A:01 X:4C Y:00 P:25 SP:FB CYC: 3 SL:69 +E147 90 06 BCC $E14F A:01 X:4C Y:00 P:25 SP:FB CYC: 9 SL:69 +E149 70 04 BVS $E14F A:01 X:4C Y:00 P:25 SP:FB CYC: 15 SL:69 +E14B C9 01 CMP #$01 A:01 X:4C Y:00 P:25 SP:FB CYC: 21 SL:69 +E14D F0 02 BEQ $E151 A:01 X:4C Y:00 P:27 SP:FB CYC: 27 SL:69 +E151 E8 INX A:01 X:4C Y:00 P:27 SP:FB CYC: 36 SL:69 +E152 A9 40 LDA #$40 A:01 X:4D Y:00 P:25 SP:FB CYC: 42 SL:69 +E154 38 SEC A:40 X:4D Y:00 P:25 SP:FB CYC: 48 SL:69 +E155 24 01 BIT $01 = FF A:40 X:4D Y:00 P:25 SP:FB CYC: 54 SL:69 +E157 EE 00 04 INC $0400 = 3F A:40 X:4D Y:00 P:E5 SP:FB CYC: 63 SL:69 +E15A EE 00 04 INC $0400 = 40 A:40 X:4D Y:00 P:65 SP:FB CYC: 81 SL:69 +E15D F9 00 04 SBC $0400,Y @ 0400 = 41 A:40 X:4D Y:00 P:65 SP:FB CYC: 99 SL:69 +E160 B0 0A BCS $E16C A:FF X:4D Y:00 P:A4 SP:FB CYC:111 SL:69 +E162 F0 08 BEQ $E16C A:FF X:4D Y:00 P:A4 SP:FB CYC:117 SL:69 +E164 10 06 BPL $E16C A:FF X:4D Y:00 P:A4 SP:FB CYC:123 SL:69 +E166 70 04 BVS $E16C A:FF X:4D Y:00 P:A4 SP:FB CYC:129 SL:69 +E168 C9 FF CMP #$FF A:FF X:4D Y:00 P:A4 SP:FB CYC:135 SL:69 +E16A F0 02 BEQ $E16E A:FF X:4D Y:00 P:27 SP:FB CYC:141 SL:69 +E16E E8 INX A:FF X:4D Y:00 P:27 SP:FB CYC:150 SL:69 +E16F 18 CLC A:FF X:4E Y:00 P:25 SP:FB CYC:156 SL:69 +E170 A9 00 LDA #$00 A:FF X:4E Y:00 P:24 SP:FB CYC:162 SL:69 +E172 8D 00 04 STA $0400 = 41 A:00 X:4E Y:00 P:26 SP:FB CYC:168 SL:69 +E175 A9 80 LDA #$80 A:00 X:4E Y:00 P:26 SP:FB CYC:180 SL:69 +E177 F9 00 04 SBC $0400,Y @ 0400 = 00 A:80 X:4E Y:00 P:A4 SP:FB CYC:186 SL:69 +E17A 90 04 BCC $E180 A:7F X:4E Y:00 P:65 SP:FB CYC:198 SL:69 +E17C C9 7F CMP #$7F A:7F X:4E Y:00 P:65 SP:FB CYC:204 SL:69 +E17E F0 02 BEQ $E182 A:7F X:4E Y:00 P:67 SP:FB CYC:210 SL:69 +E182 E8 INX A:7F X:4E Y:00 P:67 SP:FB CYC:219 SL:69 +E183 38 SEC A:7F X:4F Y:00 P:65 SP:FB CYC:225 SL:69 +E184 A9 7F LDA #$7F A:7F X:4F Y:00 P:65 SP:FB CYC:231 SL:69 +E186 8D 00 04 STA $0400 = 00 A:7F X:4F Y:00 P:65 SP:FB CYC:237 SL:69 +E189 A9 81 LDA #$81 A:7F X:4F Y:00 P:65 SP:FB CYC:249 SL:69 +E18B F9 00 04 SBC $0400,Y @ 0400 = 7F A:81 X:4F Y:00 P:E5 SP:FB CYC:255 SL:69 +E18E 50 06 BVC $E196 A:02 X:4F Y:00 P:65 SP:FB CYC:267 SL:69 +E190 90 04 BCC $E196 A:02 X:4F Y:00 P:65 SP:FB CYC:273 SL:69 +E192 C9 02 CMP #$02 A:02 X:4F Y:00 P:65 SP:FB CYC:279 SL:69 +E194 F0 02 BEQ $E198 A:02 X:4F Y:00 P:67 SP:FB CYC:285 SL:69 +E198 E8 INX A:02 X:4F Y:00 P:67 SP:FB CYC:294 SL:69 +E199 A9 00 LDA #$00 A:02 X:50 Y:00 P:65 SP:FB CYC:300 SL:69 +E19B A9 87 LDA #$87 A:00 X:50 Y:00 P:67 SP:FB CYC:306 SL:69 +E19D 99 00 04 STA $0400,Y @ 0400 = 7F A:87 X:50 Y:00 P:E5 SP:FB CYC:312 SL:69 +E1A0 AD 00 04 LDA $0400 = 87 A:87 X:50 Y:00 P:E5 SP:FB CYC:327 SL:69 +E1A3 C9 87 CMP #$87 A:87 X:50 Y:00 P:E5 SP:FB CYC:339 SL:69 +E1A5 F0 02 BEQ $E1A9 A:87 X:50 Y:00 P:67 SP:FB CYC: 4 SL:70 +E1A9 60 RTS A:87 X:50 Y:00 P:67 SP:FB CYC: 13 SL:70 +C629 20 B8 DB JSR $DBB8 A:87 X:50 Y:00 P:67 SP:FD CYC: 31 SL:70 +DBB8 A9 FF LDA #$FF A:87 X:50 Y:00 P:67 SP:FB CYC: 49 SL:70 +DBBA 85 01 STA $01 = FF A:FF X:50 Y:00 P:E5 SP:FB CYC: 55 SL:70 +DBBC A9 AA LDA #$AA A:FF X:50 Y:00 P:E5 SP:FB CYC: 64 SL:70 +DBBE 85 33 STA $33 = A3 A:AA X:50 Y:00 P:E5 SP:FB CYC: 70 SL:70 +DBC0 A9 BB LDA #$BB A:AA X:50 Y:00 P:E5 SP:FB CYC: 79 SL:70 +DBC2 85 89 STA $89 = 00 A:BB X:50 Y:00 P:E5 SP:FB CYC: 85 SL:70 +DBC4 A2 00 LDX #$00 A:BB X:50 Y:00 P:E5 SP:FB CYC: 94 SL:70 +DBC6 A9 66 LDA #$66 A:BB X:00 Y:00 P:67 SP:FB CYC:100 SL:70 +DBC8 24 01 BIT $01 = FF A:66 X:00 Y:00 P:65 SP:FB CYC:106 SL:70 +DBCA 38 SEC A:66 X:00 Y:00 P:E5 SP:FB CYC:115 SL:70 +DBCB A0 00 LDY #$00 A:66 X:00 Y:00 P:E5 SP:FB CYC:121 SL:70 +DBCD B4 33 LDY $33,X @ 33 = AA A:66 X:00 Y:00 P:67 SP:FB CYC:127 SL:70 +DBCF 10 12 BPL $DBE3 A:66 X:00 Y:AA P:E5 SP:FB CYC:139 SL:70 +DBD1 F0 10 BEQ $DBE3 A:66 X:00 Y:AA P:E5 SP:FB CYC:145 SL:70 +DBD3 50 0E BVC $DBE3 A:66 X:00 Y:AA P:E5 SP:FB CYC:151 SL:70 +DBD5 90 0C BCC $DBE3 A:66 X:00 Y:AA P:E5 SP:FB CYC:157 SL:70 +DBD7 C9 66 CMP #$66 A:66 X:00 Y:AA P:E5 SP:FB CYC:163 SL:70 +DBD9 D0 08 BNE $DBE3 A:66 X:00 Y:AA P:67 SP:FB CYC:169 SL:70 +DBDB E0 00 CPX #$00 A:66 X:00 Y:AA P:67 SP:FB CYC:175 SL:70 +DBDD D0 04 BNE $DBE3 A:66 X:00 Y:AA P:67 SP:FB CYC:181 SL:70 +DBDF C0 AA CPY #$AA A:66 X:00 Y:AA P:67 SP:FB CYC:187 SL:70 +DBE1 F0 04 BEQ $DBE7 A:66 X:00 Y:AA P:67 SP:FB CYC:193 SL:70 +DBE7 A2 8A LDX #$8A A:66 X:00 Y:AA P:67 SP:FB CYC:202 SL:70 +DBE9 A9 66 LDA #$66 A:66 X:8A Y:AA P:E5 SP:FB CYC:208 SL:70 +DBEB B8 CLV A:66 X:8A Y:AA P:65 SP:FB CYC:214 SL:70 +DBEC 18 CLC A:66 X:8A Y:AA P:25 SP:FB CYC:220 SL:70 +DBED A0 00 LDY #$00 A:66 X:8A Y:AA P:24 SP:FB CYC:226 SL:70 +DBEF B4 FF LDY $FF,X @ 89 = BB A:66 X:8A Y:00 P:26 SP:FB CYC:232 SL:70 +DBF1 10 12 BPL $DC05 A:66 X:8A Y:BB P:A4 SP:FB CYC:244 SL:70 +DBF3 F0 10 BEQ $DC05 A:66 X:8A Y:BB P:A4 SP:FB CYC:250 SL:70 +DBF5 70 0E BVS $DC05 A:66 X:8A Y:BB P:A4 SP:FB CYC:256 SL:70 +DBF7 B0 0C BCS $DC05 A:66 X:8A Y:BB P:A4 SP:FB CYC:262 SL:70 +DBF9 C0 BB CPY #$BB A:66 X:8A Y:BB P:A4 SP:FB CYC:268 SL:70 +DBFB D0 08 BNE $DC05 A:66 X:8A Y:BB P:27 SP:FB CYC:274 SL:70 +DBFD C9 66 CMP #$66 A:66 X:8A Y:BB P:27 SP:FB CYC:280 SL:70 +DBFF D0 04 BNE $DC05 A:66 X:8A Y:BB P:27 SP:FB CYC:286 SL:70 +DC01 E0 8A CPX #$8A A:66 X:8A Y:BB P:27 SP:FB CYC:292 SL:70 +DC03 F0 04 BEQ $DC09 A:66 X:8A Y:BB P:27 SP:FB CYC:298 SL:70 +DC09 24 01 BIT $01 = FF A:66 X:8A Y:BB P:27 SP:FB CYC:307 SL:70 +DC0B 38 SEC A:66 X:8A Y:BB P:E5 SP:FB CYC:316 SL:70 +DC0C A0 44 LDY #$44 A:66 X:8A Y:BB P:E5 SP:FB CYC:322 SL:70 +DC0E A2 00 LDX #$00 A:66 X:8A Y:44 P:65 SP:FB CYC:328 SL:70 +DC10 94 33 STY $33,X @ 33 = AA A:66 X:00 Y:44 P:67 SP:FB CYC:334 SL:70 +DC12 A5 33 LDA $33 = 44 A:66 X:00 Y:44 P:67 SP:FB CYC: 5 SL:71 +DC14 90 18 BCC $DC2E A:44 X:00 Y:44 P:65 SP:FB CYC: 14 SL:71 +DC16 C9 44 CMP #$44 A:44 X:00 Y:44 P:65 SP:FB CYC: 20 SL:71 +DC18 D0 14 BNE $DC2E A:44 X:00 Y:44 P:67 SP:FB CYC: 26 SL:71 +DC1A 50 12 BVC $DC2E A:44 X:00 Y:44 P:67 SP:FB CYC: 32 SL:71 +DC1C 18 CLC A:44 X:00 Y:44 P:67 SP:FB CYC: 38 SL:71 +DC1D B8 CLV A:44 X:00 Y:44 P:66 SP:FB CYC: 44 SL:71 +DC1E A0 99 LDY #$99 A:44 X:00 Y:44 P:26 SP:FB CYC: 50 SL:71 +DC20 A2 80 LDX #$80 A:44 X:00 Y:99 P:A4 SP:FB CYC: 56 SL:71 +DC22 94 85 STY $85,X @ 05 = 00 A:44 X:80 Y:99 P:A4 SP:FB CYC: 62 SL:71 +DC24 A5 05 LDA $05 = 99 A:44 X:80 Y:99 P:A4 SP:FB CYC: 74 SL:71 +DC26 B0 06 BCS $DC2E A:99 X:80 Y:99 P:A4 SP:FB CYC: 83 SL:71 +DC28 C9 99 CMP #$99 A:99 X:80 Y:99 P:A4 SP:FB CYC: 89 SL:71 +DC2A D0 02 BNE $DC2E A:99 X:80 Y:99 P:27 SP:FB CYC: 95 SL:71 +DC2C 50 04 BVC $DC32 A:99 X:80 Y:99 P:27 SP:FB CYC:101 SL:71 +DC32 A0 0B LDY #$0B A:99 X:80 Y:99 P:27 SP:FB CYC:110 SL:71 +DC34 A9 AA LDA #$AA A:99 X:80 Y:0B P:25 SP:FB CYC:116 SL:71 +DC36 A2 78 LDX #$78 A:AA X:80 Y:0B P:A5 SP:FB CYC:122 SL:71 +DC38 85 78 STA $78 = 00 A:AA X:78 Y:0B P:25 SP:FB CYC:128 SL:71 +DC3A 20 B6 F7 JSR $F7B6 A:AA X:78 Y:0B P:25 SP:FB CYC:137 SL:71 +F7B6 18 CLC A:AA X:78 Y:0B P:25 SP:F9 CYC:155 SL:71 +F7B7 A9 FF LDA #$FF A:AA X:78 Y:0B P:24 SP:F9 CYC:161 SL:71 +F7B9 85 01 STA $01 = FF A:FF X:78 Y:0B P:A4 SP:F9 CYC:167 SL:71 +F7BB 24 01 BIT $01 = FF A:FF X:78 Y:0B P:A4 SP:F9 CYC:176 SL:71 +F7BD A9 55 LDA #$55 A:FF X:78 Y:0B P:E4 SP:F9 CYC:185 SL:71 +F7BF 60 RTS A:55 X:78 Y:0B P:64 SP:F9 CYC:191 SL:71 +DC3D 15 00 ORA $00,X @ 78 = AA A:55 X:78 Y:0B P:64 SP:FB CYC:209 SL:71 +DC3F 20 C0 F7 JSR $F7C0 A:FF X:78 Y:0B P:E4 SP:FB CYC:221 SL:71 +F7C0 B0 09 BCS $F7CB A:FF X:78 Y:0B P:E4 SP:F9 CYC:239 SL:71 +F7C2 10 07 BPL $F7CB A:FF X:78 Y:0B P:E4 SP:F9 CYC:245 SL:71 +F7C4 C9 FF CMP #$FF A:FF X:78 Y:0B P:E4 SP:F9 CYC:251 SL:71 +F7C6 D0 03 BNE $F7CB A:FF X:78 Y:0B P:67 SP:F9 CYC:257 SL:71 +F7C8 50 01 BVC $F7CB A:FF X:78 Y:0B P:67 SP:F9 CYC:263 SL:71 +F7CA 60 RTS A:FF X:78 Y:0B P:67 SP:F9 CYC:269 SL:71 +DC42 C8 INY A:FF X:78 Y:0B P:67 SP:FB CYC:287 SL:71 +DC43 A9 00 LDA #$00 A:FF X:78 Y:0C P:65 SP:FB CYC:293 SL:71 +DC45 85 78 STA $78 = AA A:00 X:78 Y:0C P:67 SP:FB CYC:299 SL:71 +DC47 20 CE F7 JSR $F7CE A:00 X:78 Y:0C P:67 SP:FB CYC:308 SL:71 +F7CE 38 SEC A:00 X:78 Y:0C P:67 SP:F9 CYC:326 SL:71 +F7CF B8 CLV A:00 X:78 Y:0C P:67 SP:F9 CYC:332 SL:71 +F7D0 A9 00 LDA #$00 A:00 X:78 Y:0C P:27 SP:F9 CYC:338 SL:71 +F7D2 60 RTS A:00 X:78 Y:0C P:27 SP:F9 CYC: 3 SL:72 +DC4A 15 00 ORA $00,X @ 78 = 00 A:00 X:78 Y:0C P:27 SP:FB CYC: 21 SL:72 +DC4C 20 D3 F7 JSR $F7D3 A:00 X:78 Y:0C P:27 SP:FB CYC: 33 SL:72 +F7D3 D0 07 BNE $F7DC A:00 X:78 Y:0C P:27 SP:F9 CYC: 51 SL:72 +F7D5 70 05 BVS $F7DC A:00 X:78 Y:0C P:27 SP:F9 CYC: 57 SL:72 +F7D7 90 03 BCC $F7DC A:00 X:78 Y:0C P:27 SP:F9 CYC: 63 SL:72 +F7D9 30 01 BMI $F7DC A:00 X:78 Y:0C P:27 SP:F9 CYC: 69 SL:72 +F7DB 60 RTS A:00 X:78 Y:0C P:27 SP:F9 CYC: 75 SL:72 +DC4F C8 INY A:00 X:78 Y:0C P:27 SP:FB CYC: 93 SL:72 +DC50 A9 AA LDA #$AA A:00 X:78 Y:0D P:25 SP:FB CYC: 99 SL:72 +DC52 85 78 STA $78 = 00 A:AA X:78 Y:0D P:A5 SP:FB CYC:105 SL:72 +DC54 20 DF F7 JSR $F7DF A:AA X:78 Y:0D P:A5 SP:FB CYC:114 SL:72 +F7DF 18 CLC A:AA X:78 Y:0D P:A5 SP:F9 CYC:132 SL:72 +F7E0 24 01 BIT $01 = FF A:AA X:78 Y:0D P:A4 SP:F9 CYC:138 SL:72 +F7E2 A9 55 LDA #$55 A:AA X:78 Y:0D P:E4 SP:F9 CYC:147 SL:72 +F7E4 60 RTS A:55 X:78 Y:0D P:64 SP:F9 CYC:153 SL:72 +DC57 35 00 AND $00,X @ 78 = AA A:55 X:78 Y:0D P:64 SP:FB CYC:171 SL:72 +DC59 20 E5 F7 JSR $F7E5 A:00 X:78 Y:0D P:66 SP:FB CYC:183 SL:72 +F7E5 D0 07 BNE $F7EE A:00 X:78 Y:0D P:66 SP:F9 CYC:201 SL:72 +F7E7 50 05 BVC $F7EE A:00 X:78 Y:0D P:66 SP:F9 CYC:207 SL:72 +F7E9 B0 03 BCS $F7EE A:00 X:78 Y:0D P:66 SP:F9 CYC:213 SL:72 +F7EB 30 01 BMI $F7EE A:00 X:78 Y:0D P:66 SP:F9 CYC:219 SL:72 +F7ED 60 RTS A:00 X:78 Y:0D P:66 SP:F9 CYC:225 SL:72 +DC5C C8 INY A:00 X:78 Y:0D P:66 SP:FB CYC:243 SL:72 +DC5D A9 EF LDA #$EF A:00 X:78 Y:0E P:64 SP:FB CYC:249 SL:72 +DC5F 85 78 STA $78 = AA A:EF X:78 Y:0E P:E4 SP:FB CYC:255 SL:72 +DC61 20 F1 F7 JSR $F7F1 A:EF X:78 Y:0E P:E4 SP:FB CYC:264 SL:72 +F7F1 38 SEC A:EF X:78 Y:0E P:E4 SP:F9 CYC:282 SL:72 +F7F2 B8 CLV A:EF X:78 Y:0E P:E5 SP:F9 CYC:288 SL:72 +F7F3 A9 F8 LDA #$F8 A:EF X:78 Y:0E P:A5 SP:F9 CYC:294 SL:72 +F7F5 60 RTS A:F8 X:78 Y:0E P:A5 SP:F9 CYC:300 SL:72 +DC64 35 00 AND $00,X @ 78 = EF A:F8 X:78 Y:0E P:A5 SP:FB CYC:318 SL:72 +DC66 20 F6 F7 JSR $F7F6 A:E8 X:78 Y:0E P:A5 SP:FB CYC:330 SL:72 +F7F6 90 09 BCC $F801 A:E8 X:78 Y:0E P:A5 SP:F9 CYC: 7 SL:73 +F7F8 10 07 BPL $F801 A:E8 X:78 Y:0E P:A5 SP:F9 CYC: 13 SL:73 +F7FA C9 E8 CMP #$E8 A:E8 X:78 Y:0E P:A5 SP:F9 CYC: 19 SL:73 +F7FC D0 03 BNE $F801 A:E8 X:78 Y:0E P:27 SP:F9 CYC: 25 SL:73 +F7FE 70 01 BVS $F801 A:E8 X:78 Y:0E P:27 SP:F9 CYC: 31 SL:73 +F800 60 RTS A:E8 X:78 Y:0E P:27 SP:F9 CYC: 37 SL:73 +DC69 C8 INY A:E8 X:78 Y:0E P:27 SP:FB CYC: 55 SL:73 +DC6A A9 AA LDA #$AA A:E8 X:78 Y:0F P:25 SP:FB CYC: 61 SL:73 +DC6C 85 78 STA $78 = EF A:AA X:78 Y:0F P:A5 SP:FB CYC: 67 SL:73 +DC6E 20 04 F8 JSR $F804 A:AA X:78 Y:0F P:A5 SP:FB CYC: 76 SL:73 +F804 18 CLC A:AA X:78 Y:0F P:A5 SP:F9 CYC: 94 SL:73 +F805 24 01 BIT $01 = FF A:AA X:78 Y:0F P:A4 SP:F9 CYC:100 SL:73 +F807 A9 5F LDA #$5F A:AA X:78 Y:0F P:E4 SP:F9 CYC:109 SL:73 +F809 60 RTS A:5F X:78 Y:0F P:64 SP:F9 CYC:115 SL:73 +DC71 55 00 EOR $00,X @ 78 = AA A:5F X:78 Y:0F P:64 SP:FB CYC:133 SL:73 +DC73 20 0A F8 JSR $F80A A:F5 X:78 Y:0F P:E4 SP:FB CYC:145 SL:73 +F80A B0 09 BCS $F815 A:F5 X:78 Y:0F P:E4 SP:F9 CYC:163 SL:73 +F80C 10 07 BPL $F815 A:F5 X:78 Y:0F P:E4 SP:F9 CYC:169 SL:73 +F80E C9 F5 CMP #$F5 A:F5 X:78 Y:0F P:E4 SP:F9 CYC:175 SL:73 +F810 D0 03 BNE $F815 A:F5 X:78 Y:0F P:67 SP:F9 CYC:181 SL:73 +F812 50 01 BVC $F815 A:F5 X:78 Y:0F P:67 SP:F9 CYC:187 SL:73 +F814 60 RTS A:F5 X:78 Y:0F P:67 SP:F9 CYC:193 SL:73 +DC76 C8 INY A:F5 X:78 Y:0F P:67 SP:FB CYC:211 SL:73 +DC77 A9 70 LDA #$70 A:F5 X:78 Y:10 P:65 SP:FB CYC:217 SL:73 +DC79 85 78 STA $78 = AA A:70 X:78 Y:10 P:65 SP:FB CYC:223 SL:73 +DC7B 20 18 F8 JSR $F818 A:70 X:78 Y:10 P:65 SP:FB CYC:232 SL:73 +F818 38 SEC A:70 X:78 Y:10 P:65 SP:F9 CYC:250 SL:73 +F819 B8 CLV A:70 X:78 Y:10 P:65 SP:F9 CYC:256 SL:73 +F81A A9 70 LDA #$70 A:70 X:78 Y:10 P:25 SP:F9 CYC:262 SL:73 +F81C 60 RTS A:70 X:78 Y:10 P:25 SP:F9 CYC:268 SL:73 +DC7E 55 00 EOR $00,X @ 78 = 70 A:70 X:78 Y:10 P:25 SP:FB CYC:286 SL:73 +DC80 20 1D F8 JSR $F81D A:00 X:78 Y:10 P:27 SP:FB CYC:298 SL:73 +F81D D0 07 BNE $F826 A:00 X:78 Y:10 P:27 SP:F9 CYC:316 SL:73 +F81F 70 05 BVS $F826 A:00 X:78 Y:10 P:27 SP:F9 CYC:322 SL:73 +F821 90 03 BCC $F826 A:00 X:78 Y:10 P:27 SP:F9 CYC:328 SL:73 +F823 30 01 BMI $F826 A:00 X:78 Y:10 P:27 SP:F9 CYC:334 SL:73 +F825 60 RTS A:00 X:78 Y:10 P:27 SP:F9 CYC:340 SL:73 +DC83 C8 INY A:00 X:78 Y:10 P:27 SP:FB CYC: 17 SL:74 +DC84 A9 69 LDA #$69 A:00 X:78 Y:11 P:25 SP:FB CYC: 23 SL:74 +DC86 85 78 STA $78 = 70 A:69 X:78 Y:11 P:25 SP:FB CYC: 29 SL:74 +DC88 20 29 F8 JSR $F829 A:69 X:78 Y:11 P:25 SP:FB CYC: 38 SL:74 +F829 18 CLC A:69 X:78 Y:11 P:25 SP:F9 CYC: 56 SL:74 +F82A 24 01 BIT $01 = FF A:69 X:78 Y:11 P:24 SP:F9 CYC: 62 SL:74 +F82C A9 00 LDA #$00 A:69 X:78 Y:11 P:E4 SP:F9 CYC: 71 SL:74 +F82E 60 RTS A:00 X:78 Y:11 P:66 SP:F9 CYC: 77 SL:74 +DC8B 75 00 ADC $00,X @ 78 = 69 A:00 X:78 Y:11 P:66 SP:FB CYC: 95 SL:74 +DC8D 20 2F F8 JSR $F82F A:69 X:78 Y:11 P:24 SP:FB CYC:107 SL:74 +F82F 30 09 BMI $F83A A:69 X:78 Y:11 P:24 SP:F9 CYC:125 SL:74 +F831 B0 07 BCS $F83A A:69 X:78 Y:11 P:24 SP:F9 CYC:131 SL:74 +F833 C9 69 CMP #$69 A:69 X:78 Y:11 P:24 SP:F9 CYC:137 SL:74 +F835 D0 03 BNE $F83A A:69 X:78 Y:11 P:27 SP:F9 CYC:143 SL:74 +F837 70 01 BVS $F83A A:69 X:78 Y:11 P:27 SP:F9 CYC:149 SL:74 +F839 60 RTS A:69 X:78 Y:11 P:27 SP:F9 CYC:155 SL:74 +DC90 C8 INY A:69 X:78 Y:11 P:27 SP:FB CYC:173 SL:74 +DC91 20 3D F8 JSR $F83D A:69 X:78 Y:12 P:25 SP:FB CYC:179 SL:74 +F83D 38 SEC A:69 X:78 Y:12 P:25 SP:F9 CYC:197 SL:74 +F83E 24 01 BIT $01 = FF A:69 X:78 Y:12 P:25 SP:F9 CYC:203 SL:74 +F840 A9 00 LDA #$00 A:69 X:78 Y:12 P:E5 SP:F9 CYC:212 SL:74 +F842 60 RTS A:00 X:78 Y:12 P:67 SP:F9 CYC:218 SL:74 +DC94 75 00 ADC $00,X @ 78 = 69 A:00 X:78 Y:12 P:67 SP:FB CYC:236 SL:74 +DC96 20 43 F8 JSR $F843 A:6A X:78 Y:12 P:24 SP:FB CYC:248 SL:74 +F843 30 09 BMI $F84E A:6A X:78 Y:12 P:24 SP:F9 CYC:266 SL:74 +F845 B0 07 BCS $F84E A:6A X:78 Y:12 P:24 SP:F9 CYC:272 SL:74 +F847 C9 6A CMP #$6A A:6A X:78 Y:12 P:24 SP:F9 CYC:278 SL:74 +F849 D0 03 BNE $F84E A:6A X:78 Y:12 P:27 SP:F9 CYC:284 SL:74 +F84B 70 01 BVS $F84E A:6A X:78 Y:12 P:27 SP:F9 CYC:290 SL:74 +F84D 60 RTS A:6A X:78 Y:12 P:27 SP:F9 CYC:296 SL:74 +DC99 C8 INY A:6A X:78 Y:12 P:27 SP:FB CYC:314 SL:74 +DC9A A9 7F LDA #$7F A:6A X:78 Y:13 P:25 SP:FB CYC:320 SL:74 +DC9C 85 78 STA $78 = 69 A:7F X:78 Y:13 P:25 SP:FB CYC:326 SL:74 +DC9E 20 51 F8 JSR $F851 A:7F X:78 Y:13 P:25 SP:FB CYC:335 SL:74 +F851 38 SEC A:7F X:78 Y:13 P:25 SP:F9 CYC: 12 SL:75 +F852 B8 CLV A:7F X:78 Y:13 P:25 SP:F9 CYC: 18 SL:75 +F853 A9 7F LDA #$7F A:7F X:78 Y:13 P:25 SP:F9 CYC: 24 SL:75 +F855 60 RTS A:7F X:78 Y:13 P:25 SP:F9 CYC: 30 SL:75 +DCA1 75 00 ADC $00,X @ 78 = 7F A:7F X:78 Y:13 P:25 SP:FB CYC: 48 SL:75 +DCA3 20 56 F8 JSR $F856 A:FF X:78 Y:13 P:E4 SP:FB CYC: 60 SL:75 +F856 10 09 BPL $F861 A:FF X:78 Y:13 P:E4 SP:F9 CYC: 78 SL:75 +F858 B0 07 BCS $F861 A:FF X:78 Y:13 P:E4 SP:F9 CYC: 84 SL:75 +F85A C9 FF CMP #$FF A:FF X:78 Y:13 P:E4 SP:F9 CYC: 90 SL:75 +F85C D0 03 BNE $F861 A:FF X:78 Y:13 P:67 SP:F9 CYC: 96 SL:75 +F85E 50 01 BVC $F861 A:FF X:78 Y:13 P:67 SP:F9 CYC:102 SL:75 +F860 60 RTS A:FF X:78 Y:13 P:67 SP:F9 CYC:108 SL:75 +DCA6 C8 INY A:FF X:78 Y:13 P:67 SP:FB CYC:126 SL:75 +DCA7 A9 80 LDA #$80 A:FF X:78 Y:14 P:65 SP:FB CYC:132 SL:75 +DCA9 85 78 STA $78 = 7F A:80 X:78 Y:14 P:E5 SP:FB CYC:138 SL:75 +DCAB 20 64 F8 JSR $F864 A:80 X:78 Y:14 P:E5 SP:FB CYC:147 SL:75 +F864 18 CLC A:80 X:78 Y:14 P:E5 SP:F9 CYC:165 SL:75 +F865 24 01 BIT $01 = FF A:80 X:78 Y:14 P:E4 SP:F9 CYC:171 SL:75 +F867 A9 7F LDA #$7F A:80 X:78 Y:14 P:E4 SP:F9 CYC:180 SL:75 +F869 60 RTS A:7F X:78 Y:14 P:64 SP:F9 CYC:186 SL:75 +DCAE 75 00 ADC $00,X @ 78 = 80 A:7F X:78 Y:14 P:64 SP:FB CYC:204 SL:75 +DCB0 20 6A F8 JSR $F86A A:FF X:78 Y:14 P:A4 SP:FB CYC:216 SL:75 +F86A 10 09 BPL $F875 A:FF X:78 Y:14 P:A4 SP:F9 CYC:234 SL:75 +F86C B0 07 BCS $F875 A:FF X:78 Y:14 P:A4 SP:F9 CYC:240 SL:75 +F86E C9 FF CMP #$FF A:FF X:78 Y:14 P:A4 SP:F9 CYC:246 SL:75 +F870 D0 03 BNE $F875 A:FF X:78 Y:14 P:27 SP:F9 CYC:252 SL:75 +F872 70 01 BVS $F875 A:FF X:78 Y:14 P:27 SP:F9 CYC:258 SL:75 +F874 60 RTS A:FF X:78 Y:14 P:27 SP:F9 CYC:264 SL:75 +DCB3 C8 INY A:FF X:78 Y:14 P:27 SP:FB CYC:282 SL:75 +DCB4 20 78 F8 JSR $F878 A:FF X:78 Y:15 P:25 SP:FB CYC:288 SL:75 +F878 38 SEC A:FF X:78 Y:15 P:25 SP:F9 CYC:306 SL:75 +F879 B8 CLV A:FF X:78 Y:15 P:25 SP:F9 CYC:312 SL:75 +F87A A9 7F LDA #$7F A:FF X:78 Y:15 P:25 SP:F9 CYC:318 SL:75 +F87C 60 RTS A:7F X:78 Y:15 P:25 SP:F9 CYC:324 SL:75 +DCB7 75 00 ADC $00,X @ 78 = 80 A:7F X:78 Y:15 P:25 SP:FB CYC: 1 SL:76 +DCB9 20 7D F8 JSR $F87D A:00 X:78 Y:15 P:27 SP:FB CYC: 13 SL:76 +F87D D0 07 BNE $F886 A:00 X:78 Y:15 P:27 SP:F9 CYC: 31 SL:76 +F87F 30 05 BMI $F886 A:00 X:78 Y:15 P:27 SP:F9 CYC: 37 SL:76 +F881 70 03 BVS $F886 A:00 X:78 Y:15 P:27 SP:F9 CYC: 43 SL:76 +F883 90 01 BCC $F886 A:00 X:78 Y:15 P:27 SP:F9 CYC: 49 SL:76 +F885 60 RTS A:00 X:78 Y:15 P:27 SP:F9 CYC: 55 SL:76 +DCBC C8 INY A:00 X:78 Y:15 P:27 SP:FB CYC: 73 SL:76 +DCBD A9 40 LDA #$40 A:00 X:78 Y:16 P:25 SP:FB CYC: 79 SL:76 +DCBF 85 78 STA $78 = 80 A:40 X:78 Y:16 P:25 SP:FB CYC: 85 SL:76 +DCC1 20 89 F8 JSR $F889 A:40 X:78 Y:16 P:25 SP:FB CYC: 94 SL:76 +F889 24 01 BIT $01 = FF A:40 X:78 Y:16 P:25 SP:F9 CYC:112 SL:76 +F88B A9 40 LDA #$40 A:40 X:78 Y:16 P:E5 SP:F9 CYC:121 SL:76 +F88D 60 RTS A:40 X:78 Y:16 P:65 SP:F9 CYC:127 SL:76 +DCC4 D5 00 CMP $00,X @ 78 = 40 A:40 X:78 Y:16 P:65 SP:FB CYC:145 SL:76 +DCC6 20 8E F8 JSR $F88E A:40 X:78 Y:16 P:67 SP:FB CYC:157 SL:76 +F88E 30 07 BMI $F897 A:40 X:78 Y:16 P:67 SP:F9 CYC:175 SL:76 +F890 90 05 BCC $F897 A:40 X:78 Y:16 P:67 SP:F9 CYC:181 SL:76 +F892 D0 03 BNE $F897 A:40 X:78 Y:16 P:67 SP:F9 CYC:187 SL:76 +F894 50 01 BVC $F897 A:40 X:78 Y:16 P:67 SP:F9 CYC:193 SL:76 +F896 60 RTS A:40 X:78 Y:16 P:67 SP:F9 CYC:199 SL:76 +DCC9 C8 INY A:40 X:78 Y:16 P:67 SP:FB CYC:217 SL:76 +DCCA 48 PHA A:40 X:78 Y:17 P:65 SP:FB CYC:223 SL:76 +DCCB A9 3F LDA #$3F A:40 X:78 Y:17 P:65 SP:FA CYC:232 SL:76 +DCCD 85 78 STA $78 = 40 A:3F X:78 Y:17 P:65 SP:FA CYC:238 SL:76 +DCCF 68 PLA A:3F X:78 Y:17 P:65 SP:FA CYC:247 SL:76 +DCD0 20 9A F8 JSR $F89A A:40 X:78 Y:17 P:65 SP:FB CYC:259 SL:76 +F89A B8 CLV A:40 X:78 Y:17 P:65 SP:F9 CYC:277 SL:76 +F89B 60 RTS A:40 X:78 Y:17 P:25 SP:F9 CYC:283 SL:76 +DCD3 D5 00 CMP $00,X @ 78 = 3F A:40 X:78 Y:17 P:25 SP:FB CYC:301 SL:76 +DCD5 20 9C F8 JSR $F89C A:40 X:78 Y:17 P:25 SP:FB CYC:313 SL:76 +F89C F0 07 BEQ $F8A5 A:40 X:78 Y:17 P:25 SP:F9 CYC:331 SL:76 +F89E 30 05 BMI $F8A5 A:40 X:78 Y:17 P:25 SP:F9 CYC:337 SL:76 +F8A0 90 03 BCC $F8A5 A:40 X:78 Y:17 P:25 SP:F9 CYC: 2 SL:77 +F8A2 70 01 BVS $F8A5 A:40 X:78 Y:17 P:25 SP:F9 CYC: 8 SL:77 +F8A4 60 RTS A:40 X:78 Y:17 P:25 SP:F9 CYC: 14 SL:77 +DCD8 C8 INY A:40 X:78 Y:17 P:25 SP:FB CYC: 32 SL:77 +DCD9 48 PHA A:40 X:78 Y:18 P:25 SP:FB CYC: 38 SL:77 +DCDA A9 41 LDA #$41 A:40 X:78 Y:18 P:25 SP:FA CYC: 47 SL:77 +DCDC 85 78 STA $78 = 3F A:41 X:78 Y:18 P:25 SP:FA CYC: 53 SL:77 +DCDE 68 PLA A:41 X:78 Y:18 P:25 SP:FA CYC: 62 SL:77 +DCDF D5 00 CMP $00,X @ 78 = 41 A:40 X:78 Y:18 P:25 SP:FB CYC: 74 SL:77 +DCE1 20 A8 F8 JSR $F8A8 A:40 X:78 Y:18 P:A4 SP:FB CYC: 86 SL:77 +F8A8 F0 05 BEQ $F8AF A:40 X:78 Y:18 P:A4 SP:F9 CYC:104 SL:77 +F8AA 10 03 BPL $F8AF A:40 X:78 Y:18 P:A4 SP:F9 CYC:110 SL:77 +F8AC 10 01 BPL $F8AF A:40 X:78 Y:18 P:A4 SP:F9 CYC:116 SL:77 +F8AE 60 RTS A:40 X:78 Y:18 P:A4 SP:F9 CYC:122 SL:77 +DCE4 C8 INY A:40 X:78 Y:18 P:A4 SP:FB CYC:140 SL:77 +DCE5 48 PHA A:40 X:78 Y:19 P:24 SP:FB CYC:146 SL:77 +DCE6 A9 00 LDA #$00 A:40 X:78 Y:19 P:24 SP:FA CYC:155 SL:77 +DCE8 85 78 STA $78 = 41 A:00 X:78 Y:19 P:26 SP:FA CYC:161 SL:77 +DCEA 68 PLA A:00 X:78 Y:19 P:26 SP:FA CYC:170 SL:77 +DCEB 20 B2 F8 JSR $F8B2 A:40 X:78 Y:19 P:24 SP:FB CYC:182 SL:77 +F8B2 A9 80 LDA #$80 A:40 X:78 Y:19 P:24 SP:F9 CYC:200 SL:77 +F8B4 60 RTS A:80 X:78 Y:19 P:A4 SP:F9 CYC:206 SL:77 +DCEE D5 00 CMP $00,X @ 78 = 00 A:80 X:78 Y:19 P:A4 SP:FB CYC:224 SL:77 +DCF0 20 B5 F8 JSR $F8B5 A:80 X:78 Y:19 P:A5 SP:FB CYC:236 SL:77 +F8B5 F0 05 BEQ $F8BC A:80 X:78 Y:19 P:A5 SP:F9 CYC:254 SL:77 +F8B7 10 03 BPL $F8BC A:80 X:78 Y:19 P:A5 SP:F9 CYC:260 SL:77 +F8B9 90 01 BCC $F8BC A:80 X:78 Y:19 P:A5 SP:F9 CYC:266 SL:77 +F8BB 60 RTS A:80 X:78 Y:19 P:A5 SP:F9 CYC:272 SL:77 +DCF3 C8 INY A:80 X:78 Y:19 P:A5 SP:FB CYC:290 SL:77 +DCF4 48 PHA A:80 X:78 Y:1A P:25 SP:FB CYC:296 SL:77 +DCF5 A9 80 LDA #$80 A:80 X:78 Y:1A P:25 SP:FA CYC:305 SL:77 +DCF7 85 78 STA $78 = 00 A:80 X:78 Y:1A P:A5 SP:FA CYC:311 SL:77 +DCF9 68 PLA A:80 X:78 Y:1A P:A5 SP:FA CYC:320 SL:77 +DCFA D5 00 CMP $00,X @ 78 = 80 A:80 X:78 Y:1A P:A5 SP:FB CYC:332 SL:77 +DCFC 20 BF F8 JSR $F8BF A:80 X:78 Y:1A P:27 SP:FB CYC: 3 SL:78 +F8BF D0 05 BNE $F8C6 A:80 X:78 Y:1A P:27 SP:F9 CYC: 21 SL:78 +F8C1 30 03 BMI $F8C6 A:80 X:78 Y:1A P:27 SP:F9 CYC: 27 SL:78 +F8C3 90 01 BCC $F8C6 A:80 X:78 Y:1A P:27 SP:F9 CYC: 33 SL:78 +F8C5 60 RTS A:80 X:78 Y:1A P:27 SP:F9 CYC: 39 SL:78 +DCFF C8 INY A:80 X:78 Y:1A P:27 SP:FB CYC: 57 SL:78 +DD00 48 PHA A:80 X:78 Y:1B P:25 SP:FB CYC: 63 SL:78 +DD01 A9 81 LDA #$81 A:80 X:78 Y:1B P:25 SP:FA CYC: 72 SL:78 +DD03 85 78 STA $78 = 80 A:81 X:78 Y:1B P:A5 SP:FA CYC: 78 SL:78 +DD05 68 PLA A:81 X:78 Y:1B P:A5 SP:FA CYC: 87 SL:78 +DD06 D5 00 CMP $00,X @ 78 = 81 A:80 X:78 Y:1B P:A5 SP:FB CYC: 99 SL:78 +DD08 20 C9 F8 JSR $F8C9 A:80 X:78 Y:1B P:A4 SP:FB CYC:111 SL:78 +F8C9 B0 05 BCS $F8D0 A:80 X:78 Y:1B P:A4 SP:F9 CYC:129 SL:78 +F8CB F0 03 BEQ $F8D0 A:80 X:78 Y:1B P:A4 SP:F9 CYC:135 SL:78 +F8CD 10 01 BPL $F8D0 A:80 X:78 Y:1B P:A4 SP:F9 CYC:141 SL:78 +F8CF 60 RTS A:80 X:78 Y:1B P:A4 SP:F9 CYC:147 SL:78 +DD0B C8 INY A:80 X:78 Y:1B P:A4 SP:FB CYC:165 SL:78 +DD0C 48 PHA A:80 X:78 Y:1C P:24 SP:FB CYC:171 SL:78 +DD0D A9 7F LDA #$7F A:80 X:78 Y:1C P:24 SP:FA CYC:180 SL:78 +DD0F 85 78 STA $78 = 81 A:7F X:78 Y:1C P:24 SP:FA CYC:186 SL:78 +DD11 68 PLA A:7F X:78 Y:1C P:24 SP:FA CYC:195 SL:78 +DD12 D5 00 CMP $00,X @ 78 = 7F A:80 X:78 Y:1C P:A4 SP:FB CYC:207 SL:78 +DD14 20 D3 F8 JSR $F8D3 A:80 X:78 Y:1C P:25 SP:FB CYC:219 SL:78 +F8D3 90 05 BCC $F8DA A:80 X:78 Y:1C P:25 SP:F9 CYC:237 SL:78 +F8D5 F0 03 BEQ $F8DA A:80 X:78 Y:1C P:25 SP:F9 CYC:243 SL:78 +F8D7 30 01 BMI $F8DA A:80 X:78 Y:1C P:25 SP:F9 CYC:249 SL:78 +F8D9 60 RTS A:80 X:78 Y:1C P:25 SP:F9 CYC:255 SL:78 +DD17 C8 INY A:80 X:78 Y:1C P:25 SP:FB CYC:273 SL:78 +DD18 A9 40 LDA #$40 A:80 X:78 Y:1D P:25 SP:FB CYC:279 SL:78 +DD1A 85 78 STA $78 = 7F A:40 X:78 Y:1D P:25 SP:FB CYC:285 SL:78 +DD1C 20 31 F9 JSR $F931 A:40 X:78 Y:1D P:25 SP:FB CYC:294 SL:78 +F931 24 01 BIT $01 = FF A:40 X:78 Y:1D P:25 SP:F9 CYC:312 SL:78 +F933 A9 40 LDA #$40 A:40 X:78 Y:1D P:E5 SP:F9 CYC:321 SL:78 +F935 38 SEC A:40 X:78 Y:1D P:65 SP:F9 CYC:327 SL:78 +F936 60 RTS A:40 X:78 Y:1D P:65 SP:F9 CYC:333 SL:78 +DD1F F5 00 SBC $00,X @ 78 = 40 A:40 X:78 Y:1D P:65 SP:FB CYC: 10 SL:79 +DD21 20 37 F9 JSR $F937 A:00 X:78 Y:1D P:27 SP:FB CYC: 22 SL:79 +F937 30 0B BMI $F944 A:00 X:78 Y:1D P:27 SP:F9 CYC: 40 SL:79 +F939 90 09 BCC $F944 A:00 X:78 Y:1D P:27 SP:F9 CYC: 46 SL:79 +F93B D0 07 BNE $F944 A:00 X:78 Y:1D P:27 SP:F9 CYC: 52 SL:79 +F93D 70 05 BVS $F944 A:00 X:78 Y:1D P:27 SP:F9 CYC: 58 SL:79 +F93F C9 00 CMP #$00 A:00 X:78 Y:1D P:27 SP:F9 CYC: 64 SL:79 +F941 D0 01 BNE $F944 A:00 X:78 Y:1D P:27 SP:F9 CYC: 70 SL:79 +F943 60 RTS A:00 X:78 Y:1D P:27 SP:F9 CYC: 76 SL:79 +DD24 C8 INY A:00 X:78 Y:1D P:27 SP:FB CYC: 94 SL:79 +DD25 A9 3F LDA #$3F A:00 X:78 Y:1E P:25 SP:FB CYC:100 SL:79 +DD27 85 78 STA $78 = 40 A:3F X:78 Y:1E P:25 SP:FB CYC:106 SL:79 +DD29 20 47 F9 JSR $F947 A:3F X:78 Y:1E P:25 SP:FB CYC:115 SL:79 +F947 B8 CLV A:3F X:78 Y:1E P:25 SP:F9 CYC:133 SL:79 +F948 38 SEC A:3F X:78 Y:1E P:25 SP:F9 CYC:139 SL:79 +F949 A9 40 LDA #$40 A:3F X:78 Y:1E P:25 SP:F9 CYC:145 SL:79 +F94B 60 RTS A:40 X:78 Y:1E P:25 SP:F9 CYC:151 SL:79 +DD2C F5 00 SBC $00,X @ 78 = 3F A:40 X:78 Y:1E P:25 SP:FB CYC:169 SL:79 +DD2E 20 4C F9 JSR $F94C A:01 X:78 Y:1E P:25 SP:FB CYC:181 SL:79 +F94C F0 0B BEQ $F959 A:01 X:78 Y:1E P:25 SP:F9 CYC:199 SL:79 +F94E 30 09 BMI $F959 A:01 X:78 Y:1E P:25 SP:F9 CYC:205 SL:79 +F950 90 07 BCC $F959 A:01 X:78 Y:1E P:25 SP:F9 CYC:211 SL:79 +F952 70 05 BVS $F959 A:01 X:78 Y:1E P:25 SP:F9 CYC:217 SL:79 +F954 C9 01 CMP #$01 A:01 X:78 Y:1E P:25 SP:F9 CYC:223 SL:79 +F956 D0 01 BNE $F959 A:01 X:78 Y:1E P:27 SP:F9 CYC:229 SL:79 +F958 60 RTS A:01 X:78 Y:1E P:27 SP:F9 CYC:235 SL:79 +DD31 C8 INY A:01 X:78 Y:1E P:27 SP:FB CYC:253 SL:79 +DD32 A9 41 LDA #$41 A:01 X:78 Y:1F P:25 SP:FB CYC:259 SL:79 +DD34 85 78 STA $78 = 3F A:41 X:78 Y:1F P:25 SP:FB CYC:265 SL:79 +DD36 20 5C F9 JSR $F95C A:41 X:78 Y:1F P:25 SP:FB CYC:274 SL:79 +F95C A9 40 LDA #$40 A:41 X:78 Y:1F P:25 SP:F9 CYC:292 SL:79 +F95E 38 SEC A:40 X:78 Y:1F P:25 SP:F9 CYC:298 SL:79 +F95F 24 01 BIT $01 = FF A:40 X:78 Y:1F P:25 SP:F9 CYC:304 SL:79 +F961 60 RTS A:40 X:78 Y:1F P:E5 SP:F9 CYC:313 SL:79 +DD39 F5 00 SBC $00,X @ 78 = 41 A:40 X:78 Y:1F P:E5 SP:FB CYC:331 SL:79 +DD3B 20 62 F9 JSR $F962 A:FF X:78 Y:1F P:A4 SP:FB CYC: 2 SL:80 +F962 B0 0B BCS $F96F A:FF X:78 Y:1F P:A4 SP:F9 CYC: 20 SL:80 +F964 F0 09 BEQ $F96F A:FF X:78 Y:1F P:A4 SP:F9 CYC: 26 SL:80 +F966 10 07 BPL $F96F A:FF X:78 Y:1F P:A4 SP:F9 CYC: 32 SL:80 +F968 70 05 BVS $F96F A:FF X:78 Y:1F P:A4 SP:F9 CYC: 38 SL:80 +F96A C9 FF CMP #$FF A:FF X:78 Y:1F P:A4 SP:F9 CYC: 44 SL:80 +F96C D0 01 BNE $F96F A:FF X:78 Y:1F P:27 SP:F9 CYC: 50 SL:80 +F96E 60 RTS A:FF X:78 Y:1F P:27 SP:F9 CYC: 56 SL:80 +DD3E C8 INY A:FF X:78 Y:1F P:27 SP:FB CYC: 74 SL:80 +DD3F A9 00 LDA #$00 A:FF X:78 Y:20 P:25 SP:FB CYC: 80 SL:80 +DD41 85 78 STA $78 = 41 A:00 X:78 Y:20 P:27 SP:FB CYC: 86 SL:80 +DD43 20 72 F9 JSR $F972 A:00 X:78 Y:20 P:27 SP:FB CYC: 95 SL:80 +F972 18 CLC A:00 X:78 Y:20 P:27 SP:F9 CYC:113 SL:80 +F973 A9 80 LDA #$80 A:00 X:78 Y:20 P:26 SP:F9 CYC:119 SL:80 +F975 60 RTS A:80 X:78 Y:20 P:A4 SP:F9 CYC:125 SL:80 +DD46 F5 00 SBC $00,X @ 78 = 00 A:80 X:78 Y:20 P:A4 SP:FB CYC:143 SL:80 +DD48 20 76 F9 JSR $F976 A:7F X:78 Y:20 P:65 SP:FB CYC:155 SL:80 +F976 90 05 BCC $F97D A:7F X:78 Y:20 P:65 SP:F9 CYC:173 SL:80 +F978 C9 7F CMP #$7F A:7F X:78 Y:20 P:65 SP:F9 CYC:179 SL:80 +F97A D0 01 BNE $F97D A:7F X:78 Y:20 P:67 SP:F9 CYC:185 SL:80 +F97C 60 RTS A:7F X:78 Y:20 P:67 SP:F9 CYC:191 SL:80 +DD4B C8 INY A:7F X:78 Y:20 P:67 SP:FB CYC:209 SL:80 +DD4C A9 7F LDA #$7F A:7F X:78 Y:21 P:65 SP:FB CYC:215 SL:80 +DD4E 85 78 STA $78 = 00 A:7F X:78 Y:21 P:65 SP:FB CYC:221 SL:80 +DD50 20 80 F9 JSR $F980 A:7F X:78 Y:21 P:65 SP:FB CYC:230 SL:80 +F980 38 SEC A:7F X:78 Y:21 P:65 SP:F9 CYC:248 SL:80 +F981 A9 81 LDA #$81 A:7F X:78 Y:21 P:65 SP:F9 CYC:254 SL:80 +F983 60 RTS A:81 X:78 Y:21 P:E5 SP:F9 CYC:260 SL:80 +DD53 F5 00 SBC $00,X @ 78 = 7F A:81 X:78 Y:21 P:E5 SP:FB CYC:278 SL:80 +DD55 20 84 F9 JSR $F984 A:02 X:78 Y:21 P:65 SP:FB CYC:290 SL:80 +F984 50 07 BVC $F98D A:02 X:78 Y:21 P:65 SP:F9 CYC:308 SL:80 +F986 90 05 BCC $F98D A:02 X:78 Y:21 P:65 SP:F9 CYC:314 SL:80 +F988 C9 02 CMP #$02 A:02 X:78 Y:21 P:65 SP:F9 CYC:320 SL:80 +F98A D0 01 BNE $F98D A:02 X:78 Y:21 P:67 SP:F9 CYC:326 SL:80 +F98C 60 RTS A:02 X:78 Y:21 P:67 SP:F9 CYC:332 SL:80 +DD58 A9 AA LDA #$AA A:02 X:78 Y:21 P:67 SP:FB CYC: 9 SL:81 +DD5A 85 33 STA $33 = 44 A:AA X:78 Y:21 P:E5 SP:FB CYC: 15 SL:81 +DD5C A9 BB LDA #$BB A:AA X:78 Y:21 P:E5 SP:FB CYC: 24 SL:81 +DD5E 85 89 STA $89 = BB A:BB X:78 Y:21 P:E5 SP:FB CYC: 30 SL:81 +DD60 A2 00 LDX #$00 A:BB X:78 Y:21 P:E5 SP:FB CYC: 39 SL:81 +DD62 A0 66 LDY #$66 A:BB X:00 Y:21 P:67 SP:FB CYC: 45 SL:81 +DD64 24 01 BIT $01 = FF A:BB X:00 Y:66 P:65 SP:FB CYC: 51 SL:81 +DD66 38 SEC A:BB X:00 Y:66 P:E5 SP:FB CYC: 60 SL:81 +DD67 A9 00 LDA #$00 A:BB X:00 Y:66 P:E5 SP:FB CYC: 66 SL:81 +DD69 B5 33 LDA $33,X @ 33 = AA A:00 X:00 Y:66 P:67 SP:FB CYC: 72 SL:81 +DD6B 10 12 BPL $DD7F A:AA X:00 Y:66 P:E5 SP:FB CYC: 84 SL:81 +DD6D F0 10 BEQ $DD7F A:AA X:00 Y:66 P:E5 SP:FB CYC: 90 SL:81 +DD6F 50 0E BVC $DD7F A:AA X:00 Y:66 P:E5 SP:FB CYC: 96 SL:81 +DD71 90 0C BCC $DD7F A:AA X:00 Y:66 P:E5 SP:FB CYC:102 SL:81 +DD73 C0 66 CPY #$66 A:AA X:00 Y:66 P:E5 SP:FB CYC:108 SL:81 +DD75 D0 08 BNE $DD7F A:AA X:00 Y:66 P:67 SP:FB CYC:114 SL:81 +DD77 E0 00 CPX #$00 A:AA X:00 Y:66 P:67 SP:FB CYC:120 SL:81 +DD79 D0 04 BNE $DD7F A:AA X:00 Y:66 P:67 SP:FB CYC:126 SL:81 +DD7B C9 AA CMP #$AA A:AA X:00 Y:66 P:67 SP:FB CYC:132 SL:81 +DD7D F0 04 BEQ $DD83 A:AA X:00 Y:66 P:67 SP:FB CYC:138 SL:81 +DD83 A2 8A LDX #$8A A:AA X:00 Y:66 P:67 SP:FB CYC:147 SL:81 +DD85 A0 66 LDY #$66 A:AA X:8A Y:66 P:E5 SP:FB CYC:153 SL:81 +DD87 B8 CLV A:AA X:8A Y:66 P:65 SP:FB CYC:159 SL:81 +DD88 18 CLC A:AA X:8A Y:66 P:25 SP:FB CYC:165 SL:81 +DD89 A9 00 LDA #$00 A:AA X:8A Y:66 P:24 SP:FB CYC:171 SL:81 +DD8B B5 FF LDA $FF,X @ 89 = BB A:00 X:8A Y:66 P:26 SP:FB CYC:177 SL:81 +DD8D 10 12 BPL $DDA1 A:BB X:8A Y:66 P:A4 SP:FB CYC:189 SL:81 +DD8F F0 10 BEQ $DDA1 A:BB X:8A Y:66 P:A4 SP:FB CYC:195 SL:81 +DD91 70 0E BVS $DDA1 A:BB X:8A Y:66 P:A4 SP:FB CYC:201 SL:81 +DD93 B0 0C BCS $DDA1 A:BB X:8A Y:66 P:A4 SP:FB CYC:207 SL:81 +DD95 C9 BB CMP #$BB A:BB X:8A Y:66 P:A4 SP:FB CYC:213 SL:81 +DD97 D0 08 BNE $DDA1 A:BB X:8A Y:66 P:27 SP:FB CYC:219 SL:81 +DD99 C0 66 CPY #$66 A:BB X:8A Y:66 P:27 SP:FB CYC:225 SL:81 +DD9B D0 04 BNE $DDA1 A:BB X:8A Y:66 P:27 SP:FB CYC:231 SL:81 +DD9D E0 8A CPX #$8A A:BB X:8A Y:66 P:27 SP:FB CYC:237 SL:81 +DD9F F0 04 BEQ $DDA5 A:BB X:8A Y:66 P:27 SP:FB CYC:243 SL:81 +DDA5 24 01 BIT $01 = FF A:BB X:8A Y:66 P:27 SP:FB CYC:252 SL:81 +DDA7 38 SEC A:BB X:8A Y:66 P:E5 SP:FB CYC:261 SL:81 +DDA8 A9 44 LDA #$44 A:BB X:8A Y:66 P:E5 SP:FB CYC:267 SL:81 +DDAA A2 00 LDX #$00 A:44 X:8A Y:66 P:65 SP:FB CYC:273 SL:81 +DDAC 95 33 STA $33,X @ 33 = AA A:44 X:00 Y:66 P:67 SP:FB CYC:279 SL:81 +DDAE A5 33 LDA $33 = 44 A:44 X:00 Y:66 P:67 SP:FB CYC:291 SL:81 +DDB0 90 18 BCC $DDCA A:44 X:00 Y:66 P:65 SP:FB CYC:300 SL:81 +DDB2 C9 44 CMP #$44 A:44 X:00 Y:66 P:65 SP:FB CYC:306 SL:81 +DDB4 D0 14 BNE $DDCA A:44 X:00 Y:66 P:67 SP:FB CYC:312 SL:81 +DDB6 50 12 BVC $DDCA A:44 X:00 Y:66 P:67 SP:FB CYC:318 SL:81 +DDB8 18 CLC A:44 X:00 Y:66 P:67 SP:FB CYC:324 SL:81 +DDB9 B8 CLV A:44 X:00 Y:66 P:66 SP:FB CYC:330 SL:81 +DDBA A9 99 LDA #$99 A:44 X:00 Y:66 P:26 SP:FB CYC:336 SL:81 +DDBC A2 80 LDX #$80 A:99 X:00 Y:66 P:A4 SP:FB CYC: 1 SL:82 +DDBE 95 85 STA $85,X @ 05 = 99 A:99 X:80 Y:66 P:A4 SP:FB CYC: 7 SL:82 +DDC0 A5 05 LDA $05 = 99 A:99 X:80 Y:66 P:A4 SP:FB CYC: 19 SL:82 +DDC2 B0 06 BCS $DDCA A:99 X:80 Y:66 P:A4 SP:FB CYC: 28 SL:82 +DDC4 C9 99 CMP #$99 A:99 X:80 Y:66 P:A4 SP:FB CYC: 34 SL:82 +DDC6 D0 02 BNE $DDCA A:99 X:80 Y:66 P:27 SP:FB CYC: 40 SL:82 +DDC8 50 04 BVC $DDCE A:99 X:80 Y:66 P:27 SP:FB CYC: 46 SL:82 +DDCE A0 25 LDY #$25 A:99 X:80 Y:66 P:27 SP:FB CYC: 55 SL:82 +DDD0 A2 78 LDX #$78 A:99 X:80 Y:25 P:25 SP:FB CYC: 61 SL:82 +DDD2 20 90 F9 JSR $F990 A:99 X:78 Y:25 P:25 SP:FB CYC: 67 SL:82 +F990 A2 55 LDX #$55 A:99 X:78 Y:25 P:25 SP:F9 CYC: 85 SL:82 +F992 A9 FF LDA #$FF A:99 X:55 Y:25 P:25 SP:F9 CYC: 91 SL:82 +F994 85 01 STA $01 = FF A:FF X:55 Y:25 P:A5 SP:F9 CYC: 97 SL:82 +F996 EA NOP A:FF X:55 Y:25 P:A5 SP:F9 CYC:106 SL:82 +F997 24 01 BIT $01 = FF A:FF X:55 Y:25 P:A5 SP:F9 CYC:112 SL:82 +F999 38 SEC A:FF X:55 Y:25 P:E5 SP:F9 CYC:121 SL:82 +F99A A9 01 LDA #$01 A:FF X:55 Y:25 P:E5 SP:F9 CYC:127 SL:82 +F99C 60 RTS A:01 X:55 Y:25 P:65 SP:F9 CYC:133 SL:82 +DDD5 95 00 STA $00,X @ 55 = 00 A:01 X:55 Y:25 P:65 SP:FB CYC:151 SL:82 +DDD7 56 00 LSR $00,X @ 55 = 01 A:01 X:55 Y:25 P:65 SP:FB CYC:163 SL:82 +DDD9 B5 00 LDA $00,X @ 55 = 00 A:01 X:55 Y:25 P:67 SP:FB CYC:181 SL:82 +DDDB 20 9D F9 JSR $F99D A:00 X:55 Y:25 P:67 SP:FB CYC:193 SL:82 +F99D 90 1B BCC $F9BA A:00 X:55 Y:25 P:67 SP:F9 CYC:211 SL:82 +F99F D0 19 BNE $F9BA A:00 X:55 Y:25 P:67 SP:F9 CYC:217 SL:82 +F9A1 30 17 BMI $F9BA A:00 X:55 Y:25 P:67 SP:F9 CYC:223 SL:82 +F9A3 50 15 BVC $F9BA A:00 X:55 Y:25 P:67 SP:F9 CYC:229 SL:82 +F9A5 C9 00 CMP #$00 A:00 X:55 Y:25 P:67 SP:F9 CYC:235 SL:82 +F9A7 D0 11 BNE $F9BA A:00 X:55 Y:25 P:67 SP:F9 CYC:241 SL:82 +F9A9 B8 CLV A:00 X:55 Y:25 P:67 SP:F9 CYC:247 SL:82 +F9AA A9 AA LDA #$AA A:00 X:55 Y:25 P:27 SP:F9 CYC:253 SL:82 +F9AC 60 RTS A:AA X:55 Y:25 P:A5 SP:F9 CYC:259 SL:82 +DDDE C8 INY A:AA X:55 Y:25 P:A5 SP:FB CYC:277 SL:82 +DDDF 95 00 STA $00,X @ 55 = 00 A:AA X:55 Y:26 P:25 SP:FB CYC:283 SL:82 +DDE1 56 00 LSR $00,X @ 55 = AA A:AA X:55 Y:26 P:25 SP:FB CYC:295 SL:82 +DDE3 B5 00 LDA $00,X @ 55 = 55 A:AA X:55 Y:26 P:24 SP:FB CYC:313 SL:82 +DDE5 20 AD F9 JSR $F9AD A:55 X:55 Y:26 P:24 SP:FB CYC:325 SL:82 +F9AD B0 0B BCS $F9BA A:55 X:55 Y:26 P:24 SP:F9 CYC: 2 SL:83 +F9AF F0 09 BEQ $F9BA A:55 X:55 Y:26 P:24 SP:F9 CYC: 8 SL:83 +F9B1 30 07 BMI $F9BA A:55 X:55 Y:26 P:24 SP:F9 CYC: 14 SL:83 +F9B3 70 05 BVS $F9BA A:55 X:55 Y:26 P:24 SP:F9 CYC: 20 SL:83 +F9B5 C9 55 CMP #$55 A:55 X:55 Y:26 P:24 SP:F9 CYC: 26 SL:83 +F9B7 D0 01 BNE $F9BA A:55 X:55 Y:26 P:27 SP:F9 CYC: 32 SL:83 +F9B9 60 RTS A:55 X:55 Y:26 P:27 SP:F9 CYC: 38 SL:83 +DDE8 C8 INY A:55 X:55 Y:26 P:27 SP:FB CYC: 56 SL:83 +DDE9 20 BD F9 JSR $F9BD A:55 X:55 Y:27 P:25 SP:FB CYC: 62 SL:83 +F9BD 24 01 BIT $01 = FF A:55 X:55 Y:27 P:25 SP:F9 CYC: 80 SL:83 +F9BF 38 SEC A:55 X:55 Y:27 P:E5 SP:F9 CYC: 89 SL:83 +F9C0 A9 80 LDA #$80 A:55 X:55 Y:27 P:E5 SP:F9 CYC: 95 SL:83 +F9C2 60 RTS A:80 X:55 Y:27 P:E5 SP:F9 CYC:101 SL:83 +DDEC 95 00 STA $00,X @ 55 = 55 A:80 X:55 Y:27 P:E5 SP:FB CYC:119 SL:83 +DDEE 16 00 ASL $00,X @ 55 = 80 A:80 X:55 Y:27 P:E5 SP:FB CYC:131 SL:83 +DDF0 B5 00 LDA $00,X @ 55 = 00 A:80 X:55 Y:27 P:67 SP:FB CYC:149 SL:83 +DDF2 20 C3 F9 JSR $F9C3 A:00 X:55 Y:27 P:67 SP:FB CYC:161 SL:83 +F9C3 90 1C BCC $F9E1 A:00 X:55 Y:27 P:67 SP:F9 CYC:179 SL:83 +F9C5 D0 1A BNE $F9E1 A:00 X:55 Y:27 P:67 SP:F9 CYC:185 SL:83 +F9C7 30 18 BMI $F9E1 A:00 X:55 Y:27 P:67 SP:F9 CYC:191 SL:83 +F9C9 50 16 BVC $F9E1 A:00 X:55 Y:27 P:67 SP:F9 CYC:197 SL:83 +F9CB C9 00 CMP #$00 A:00 X:55 Y:27 P:67 SP:F9 CYC:203 SL:83 +F9CD D0 12 BNE $F9E1 A:00 X:55 Y:27 P:67 SP:F9 CYC:209 SL:83 +F9CF B8 CLV A:00 X:55 Y:27 P:67 SP:F9 CYC:215 SL:83 +F9D0 A9 55 LDA #$55 A:00 X:55 Y:27 P:27 SP:F9 CYC:221 SL:83 +F9D2 38 SEC A:55 X:55 Y:27 P:25 SP:F9 CYC:227 SL:83 +F9D3 60 RTS A:55 X:55 Y:27 P:25 SP:F9 CYC:233 SL:83 +DDF5 C8 INY A:55 X:55 Y:27 P:25 SP:FB CYC:251 SL:83 +DDF6 95 00 STA $00,X @ 55 = 00 A:55 X:55 Y:28 P:25 SP:FB CYC:257 SL:83 +DDF8 16 00 ASL $00,X @ 55 = 55 A:55 X:55 Y:28 P:25 SP:FB CYC:269 SL:83 +DDFA B5 00 LDA $00,X @ 55 = AA A:55 X:55 Y:28 P:A4 SP:FB CYC:287 SL:83 +DDFC 20 D4 F9 JSR $F9D4 A:AA X:55 Y:28 P:A4 SP:FB CYC:299 SL:83 +F9D4 B0 0B BCS $F9E1 A:AA X:55 Y:28 P:A4 SP:F9 CYC:317 SL:83 +F9D6 F0 09 BEQ $F9E1 A:AA X:55 Y:28 P:A4 SP:F9 CYC:323 SL:83 +F9D8 10 07 BPL $F9E1 A:AA X:55 Y:28 P:A4 SP:F9 CYC:329 SL:83 +F9DA 70 05 BVS $F9E1 A:AA X:55 Y:28 P:A4 SP:F9 CYC:335 SL:83 +F9DC C9 AA CMP #$AA A:AA X:55 Y:28 P:A4 SP:F9 CYC: 0 SL:84 +F9DE D0 01 BNE $F9E1 A:AA X:55 Y:28 P:27 SP:F9 CYC: 6 SL:84 +F9E0 60 RTS A:AA X:55 Y:28 P:27 SP:F9 CYC: 12 SL:84 +DDFF C8 INY A:AA X:55 Y:28 P:27 SP:FB CYC: 30 SL:84 +DE00 20 E4 F9 JSR $F9E4 A:AA X:55 Y:29 P:25 SP:FB CYC: 36 SL:84 +F9E4 24 01 BIT $01 = FF A:AA X:55 Y:29 P:25 SP:F9 CYC: 54 SL:84 +F9E6 38 SEC A:AA X:55 Y:29 P:E5 SP:F9 CYC: 63 SL:84 +F9E7 A9 01 LDA #$01 A:AA X:55 Y:29 P:E5 SP:F9 CYC: 69 SL:84 +F9E9 60 RTS A:01 X:55 Y:29 P:65 SP:F9 CYC: 75 SL:84 +DE03 95 00 STA $00,X @ 55 = AA A:01 X:55 Y:29 P:65 SP:FB CYC: 93 SL:84 +DE05 76 00 ROR $00,X @ 55 = 01 A:01 X:55 Y:29 P:65 SP:FB CYC:105 SL:84 +DE07 B5 00 LDA $00,X @ 55 = 80 A:01 X:55 Y:29 P:E5 SP:FB CYC:123 SL:84 +DE09 20 EA F9 JSR $F9EA A:80 X:55 Y:29 P:E5 SP:FB CYC:135 SL:84 +F9EA 90 1C BCC $FA08 A:80 X:55 Y:29 P:E5 SP:F9 CYC:153 SL:84 +F9EC F0 1A BEQ $FA08 A:80 X:55 Y:29 P:E5 SP:F9 CYC:159 SL:84 +F9EE 10 18 BPL $FA08 A:80 X:55 Y:29 P:E5 SP:F9 CYC:165 SL:84 +F9F0 50 16 BVC $FA08 A:80 X:55 Y:29 P:E5 SP:F9 CYC:171 SL:84 +F9F2 C9 80 CMP #$80 A:80 X:55 Y:29 P:E5 SP:F9 CYC:177 SL:84 +F9F4 D0 12 BNE $FA08 A:80 X:55 Y:29 P:67 SP:F9 CYC:183 SL:84 +F9F6 B8 CLV A:80 X:55 Y:29 P:67 SP:F9 CYC:189 SL:84 +F9F7 18 CLC A:80 X:55 Y:29 P:27 SP:F9 CYC:195 SL:84 +F9F8 A9 55 LDA #$55 A:80 X:55 Y:29 P:26 SP:F9 CYC:201 SL:84 +F9FA 60 RTS A:55 X:55 Y:29 P:24 SP:F9 CYC:207 SL:84 +DE0C C8 INY A:55 X:55 Y:29 P:24 SP:FB CYC:225 SL:84 +DE0D 95 00 STA $00,X @ 55 = 80 A:55 X:55 Y:2A P:24 SP:FB CYC:231 SL:84 +DE0F 76 00 ROR $00,X @ 55 = 55 A:55 X:55 Y:2A P:24 SP:FB CYC:243 SL:84 +DE11 B5 00 LDA $00,X @ 55 = 2A A:55 X:55 Y:2A P:25 SP:FB CYC:261 SL:84 +DE13 20 FB F9 JSR $F9FB A:2A X:55 Y:2A P:25 SP:FB CYC:273 SL:84 +F9FB 90 0B BCC $FA08 A:2A X:55 Y:2A P:25 SP:F9 CYC:291 SL:84 +F9FD F0 09 BEQ $FA08 A:2A X:55 Y:2A P:25 SP:F9 CYC:297 SL:84 +F9FF 30 07 BMI $FA08 A:2A X:55 Y:2A P:25 SP:F9 CYC:303 SL:84 +FA01 70 05 BVS $FA08 A:2A X:55 Y:2A P:25 SP:F9 CYC:309 SL:84 +FA03 C9 2A CMP #$2A A:2A X:55 Y:2A P:25 SP:F9 CYC:315 SL:84 +FA05 D0 01 BNE $FA08 A:2A X:55 Y:2A P:27 SP:F9 CYC:321 SL:84 +FA07 60 RTS A:2A X:55 Y:2A P:27 SP:F9 CYC:327 SL:84 +DE16 C8 INY A:2A X:55 Y:2A P:27 SP:FB CYC: 4 SL:85 +DE17 20 0A FA JSR $FA0A A:2A X:55 Y:2B P:25 SP:FB CYC: 10 SL:85 +FA0A 24 01 BIT $01 = FF A:2A X:55 Y:2B P:25 SP:F9 CYC: 28 SL:85 +FA0C 38 SEC A:2A X:55 Y:2B P:E5 SP:F9 CYC: 37 SL:85 +FA0D A9 80 LDA #$80 A:2A X:55 Y:2B P:E5 SP:F9 CYC: 43 SL:85 +FA0F 60 RTS A:80 X:55 Y:2B P:E5 SP:F9 CYC: 49 SL:85 +DE1A 95 00 STA $00,X @ 55 = 2A A:80 X:55 Y:2B P:E5 SP:FB CYC: 67 SL:85 +DE1C 36 00 ROL $00,X @ 55 = 80 A:80 X:55 Y:2B P:E5 SP:FB CYC: 79 SL:85 +DE1E B5 00 LDA $00,X @ 55 = 01 A:80 X:55 Y:2B P:65 SP:FB CYC: 97 SL:85 +DE20 20 10 FA JSR $FA10 A:01 X:55 Y:2B P:65 SP:FB CYC:109 SL:85 +FA10 90 1C BCC $FA2E A:01 X:55 Y:2B P:65 SP:F9 CYC:127 SL:85 +FA12 F0 1A BEQ $FA2E A:01 X:55 Y:2B P:65 SP:F9 CYC:133 SL:85 +FA14 30 18 BMI $FA2E A:01 X:55 Y:2B P:65 SP:F9 CYC:139 SL:85 +FA16 50 16 BVC $FA2E A:01 X:55 Y:2B P:65 SP:F9 CYC:145 SL:85 +FA18 C9 01 CMP #$01 A:01 X:55 Y:2B P:65 SP:F9 CYC:151 SL:85 +FA1A D0 12 BNE $FA2E A:01 X:55 Y:2B P:67 SP:F9 CYC:157 SL:85 +FA1C B8 CLV A:01 X:55 Y:2B P:67 SP:F9 CYC:163 SL:85 +FA1D 18 CLC A:01 X:55 Y:2B P:27 SP:F9 CYC:169 SL:85 +FA1E A9 55 LDA #$55 A:01 X:55 Y:2B P:26 SP:F9 CYC:175 SL:85 +FA20 60 RTS A:55 X:55 Y:2B P:24 SP:F9 CYC:181 SL:85 +DE23 C8 INY A:55 X:55 Y:2B P:24 SP:FB CYC:199 SL:85 +DE24 95 00 STA $00,X @ 55 = 01 A:55 X:55 Y:2C P:24 SP:FB CYC:205 SL:85 +DE26 36 00 ROL $00,X @ 55 = 55 A:55 X:55 Y:2C P:24 SP:FB CYC:217 SL:85 +DE28 B5 00 LDA $00,X @ 55 = AA A:55 X:55 Y:2C P:A4 SP:FB CYC:235 SL:85 +DE2A 20 21 FA JSR $FA21 A:AA X:55 Y:2C P:A4 SP:FB CYC:247 SL:85 +FA21 B0 0B BCS $FA2E A:AA X:55 Y:2C P:A4 SP:F9 CYC:265 SL:85 +FA23 F0 09 BEQ $FA2E A:AA X:55 Y:2C P:A4 SP:F9 CYC:271 SL:85 +FA25 10 07 BPL $FA2E A:AA X:55 Y:2C P:A4 SP:F9 CYC:277 SL:85 +FA27 70 05 BVS $FA2E A:AA X:55 Y:2C P:A4 SP:F9 CYC:283 SL:85 +FA29 C9 AA CMP #$AA A:AA X:55 Y:2C P:A4 SP:F9 CYC:289 SL:85 +FA2B D0 01 BNE $FA2E A:AA X:55 Y:2C P:27 SP:F9 CYC:295 SL:85 +FA2D 60 RTS A:AA X:55 Y:2C P:27 SP:F9 CYC:301 SL:85 +DE2D A9 FF LDA #$FF A:AA X:55 Y:2C P:27 SP:FB CYC:319 SL:85 +DE2F 95 00 STA $00,X @ 55 = AA A:FF X:55 Y:2C P:A5 SP:FB CYC:325 SL:85 +DE31 85 01 STA $01 = FF A:FF X:55 Y:2C P:A5 SP:FB CYC:337 SL:85 +DE33 24 01 BIT $01 = FF A:FF X:55 Y:2C P:A5 SP:FB CYC: 5 SL:86 +DE35 38 SEC A:FF X:55 Y:2C P:E5 SP:FB CYC: 14 SL:86 +DE36 F6 00 INC $00,X @ 55 = FF A:FF X:55 Y:2C P:E5 SP:FB CYC: 20 SL:86 +DE38 D0 0C BNE $DE46 A:FF X:55 Y:2C P:67 SP:FB CYC: 38 SL:86 +DE3A 30 0A BMI $DE46 A:FF X:55 Y:2C P:67 SP:FB CYC: 44 SL:86 +DE3C 50 08 BVC $DE46 A:FF X:55 Y:2C P:67 SP:FB CYC: 50 SL:86 +DE3E 90 06 BCC $DE46 A:FF X:55 Y:2C P:67 SP:FB CYC: 56 SL:86 +DE40 B5 00 LDA $00,X @ 55 = 00 A:FF X:55 Y:2C P:67 SP:FB CYC: 62 SL:86 +DE42 C9 00 CMP #$00 A:00 X:55 Y:2C P:67 SP:FB CYC: 74 SL:86 +DE44 F0 04 BEQ $DE4A A:00 X:55 Y:2C P:67 SP:FB CYC: 80 SL:86 +DE4A A9 7F LDA #$7F A:00 X:55 Y:2C P:67 SP:FB CYC: 89 SL:86 +DE4C 95 00 STA $00,X @ 55 = 00 A:7F X:55 Y:2C P:65 SP:FB CYC: 95 SL:86 +DE4E B8 CLV A:7F X:55 Y:2C P:65 SP:FB CYC:107 SL:86 +DE4F 18 CLC A:7F X:55 Y:2C P:25 SP:FB CYC:113 SL:86 +DE50 F6 00 INC $00,X @ 55 = 7F A:7F X:55 Y:2C P:24 SP:FB CYC:119 SL:86 +DE52 F0 0C BEQ $DE60 A:7F X:55 Y:2C P:A4 SP:FB CYC:137 SL:86 +DE54 10 0A BPL $DE60 A:7F X:55 Y:2C P:A4 SP:FB CYC:143 SL:86 +DE56 70 08 BVS $DE60 A:7F X:55 Y:2C P:A4 SP:FB CYC:149 SL:86 +DE58 B0 06 BCS $DE60 A:7F X:55 Y:2C P:A4 SP:FB CYC:155 SL:86 +DE5A B5 00 LDA $00,X @ 55 = 80 A:7F X:55 Y:2C P:A4 SP:FB CYC:161 SL:86 +DE5C C9 80 CMP #$80 A:80 X:55 Y:2C P:A4 SP:FB CYC:173 SL:86 +DE5E F0 04 BEQ $DE64 A:80 X:55 Y:2C P:27 SP:FB CYC:179 SL:86 +DE64 A9 00 LDA #$00 A:80 X:55 Y:2C P:27 SP:FB CYC:188 SL:86 +DE66 95 00 STA $00,X @ 55 = 80 A:00 X:55 Y:2C P:27 SP:FB CYC:194 SL:86 +DE68 24 01 BIT $01 = FF A:00 X:55 Y:2C P:27 SP:FB CYC:206 SL:86 +DE6A 38 SEC A:00 X:55 Y:2C P:E7 SP:FB CYC:215 SL:86 +DE6B D6 00 DEC $00,X @ 55 = 00 A:00 X:55 Y:2C P:E7 SP:FB CYC:221 SL:86 +DE6D F0 0C BEQ $DE7B A:00 X:55 Y:2C P:E5 SP:FB CYC:239 SL:86 +DE6F 10 0A BPL $DE7B A:00 X:55 Y:2C P:E5 SP:FB CYC:245 SL:86 +DE71 50 08 BVC $DE7B A:00 X:55 Y:2C P:E5 SP:FB CYC:251 SL:86 +DE73 90 06 BCC $DE7B A:00 X:55 Y:2C P:E5 SP:FB CYC:257 SL:86 +DE75 B5 00 LDA $00,X @ 55 = FF A:00 X:55 Y:2C P:E5 SP:FB CYC:263 SL:86 +DE77 C9 FF CMP #$FF A:FF X:55 Y:2C P:E5 SP:FB CYC:275 SL:86 +DE79 F0 04 BEQ $DE7F A:FF X:55 Y:2C P:67 SP:FB CYC:281 SL:86 +DE7F A9 80 LDA #$80 A:FF X:55 Y:2C P:67 SP:FB CYC:290 SL:86 +DE81 95 00 STA $00,X @ 55 = FF A:80 X:55 Y:2C P:E5 SP:FB CYC:296 SL:86 +DE83 B8 CLV A:80 X:55 Y:2C P:E5 SP:FB CYC:308 SL:86 +DE84 18 CLC A:80 X:55 Y:2C P:A5 SP:FB CYC:314 SL:86 +DE85 D6 00 DEC $00,X @ 55 = 80 A:80 X:55 Y:2C P:A4 SP:FB CYC:320 SL:86 +DE87 F0 0C BEQ $DE95 A:80 X:55 Y:2C P:24 SP:FB CYC:338 SL:86 +DE89 30 0A BMI $DE95 A:80 X:55 Y:2C P:24 SP:FB CYC: 3 SL:87 +DE8B 70 08 BVS $DE95 A:80 X:55 Y:2C P:24 SP:FB CYC: 9 SL:87 +DE8D B0 06 BCS $DE95 A:80 X:55 Y:2C P:24 SP:FB CYC: 15 SL:87 +DE8F B5 00 LDA $00,X @ 55 = 7F A:80 X:55 Y:2C P:24 SP:FB CYC: 21 SL:87 +DE91 C9 7F CMP #$7F A:7F X:55 Y:2C P:24 SP:FB CYC: 33 SL:87 +DE93 F0 04 BEQ $DE99 A:7F X:55 Y:2C P:27 SP:FB CYC: 39 SL:87 +DE99 A9 01 LDA #$01 A:7F X:55 Y:2C P:27 SP:FB CYC: 48 SL:87 +DE9B 95 00 STA $00,X @ 55 = 7F A:01 X:55 Y:2C P:25 SP:FB CYC: 54 SL:87 +DE9D D6 00 DEC $00,X @ 55 = 01 A:01 X:55 Y:2C P:25 SP:FB CYC: 66 SL:87 +DE9F F0 04 BEQ $DEA5 A:01 X:55 Y:2C P:27 SP:FB CYC: 84 SL:87 +DEA5 A9 33 LDA #$33 A:01 X:55 Y:2C P:27 SP:FB CYC: 93 SL:87 +DEA7 85 78 STA $78 = 7F A:33 X:55 Y:2C P:25 SP:FB CYC: 99 SL:87 +DEA9 A9 44 LDA #$44 A:33 X:55 Y:2C P:25 SP:FB CYC:108 SL:87 +DEAB A0 78 LDY #$78 A:44 X:55 Y:2C P:25 SP:FB CYC:114 SL:87 +DEAD A2 00 LDX #$00 A:44 X:55 Y:78 P:25 SP:FB CYC:120 SL:87 +DEAF 38 SEC A:44 X:00 Y:78 P:27 SP:FB CYC:126 SL:87 +DEB0 24 01 BIT $01 = FF A:44 X:00 Y:78 P:27 SP:FB CYC:132 SL:87 +DEB2 B6 00 LDX $00,Y @ 78 = 33 A:44 X:00 Y:78 P:E5 SP:FB CYC:141 SL:87 +DEB4 90 12 BCC $DEC8 A:44 X:33 Y:78 P:65 SP:FB CYC:153 SL:87 +DEB6 50 10 BVC $DEC8 A:44 X:33 Y:78 P:65 SP:FB CYC:159 SL:87 +DEB8 30 0E BMI $DEC8 A:44 X:33 Y:78 P:65 SP:FB CYC:165 SL:87 +DEBA F0 0C BEQ $DEC8 A:44 X:33 Y:78 P:65 SP:FB CYC:171 SL:87 +DEBC E0 33 CPX #$33 A:44 X:33 Y:78 P:65 SP:FB CYC:177 SL:87 +DEBE D0 08 BNE $DEC8 A:44 X:33 Y:78 P:67 SP:FB CYC:183 SL:87 +DEC0 C0 78 CPY #$78 A:44 X:33 Y:78 P:67 SP:FB CYC:189 SL:87 +DEC2 D0 04 BNE $DEC8 A:44 X:33 Y:78 P:67 SP:FB CYC:195 SL:87 +DEC4 C9 44 CMP #$44 A:44 X:33 Y:78 P:67 SP:FB CYC:201 SL:87 +DEC6 F0 04 BEQ $DECC A:44 X:33 Y:78 P:67 SP:FB CYC:207 SL:87 +DECC A9 97 LDA #$97 A:44 X:33 Y:78 P:67 SP:FB CYC:216 SL:87 +DECE 85 7F STA $7F = 00 A:97 X:33 Y:78 P:E5 SP:FB CYC:222 SL:87 +DED0 A9 47 LDA #$47 A:97 X:33 Y:78 P:E5 SP:FB CYC:231 SL:87 +DED2 A0 FF LDY #$FF A:47 X:33 Y:78 P:65 SP:FB CYC:237 SL:87 +DED4 A2 00 LDX #$00 A:47 X:33 Y:FF P:E5 SP:FB CYC:243 SL:87 +DED6 18 CLC A:47 X:00 Y:FF P:67 SP:FB CYC:249 SL:87 +DED7 B8 CLV A:47 X:00 Y:FF P:66 SP:FB CYC:255 SL:87 +DED8 B6 80 LDX $80,Y @ 7F = 97 A:47 X:00 Y:FF P:26 SP:FB CYC:261 SL:87 +DEDA B0 12 BCS $DEEE A:47 X:97 Y:FF P:A4 SP:FB CYC:273 SL:87 +DEDC 70 10 BVS $DEEE A:47 X:97 Y:FF P:A4 SP:FB CYC:279 SL:87 +DEDE 10 0E BPL $DEEE A:47 X:97 Y:FF P:A4 SP:FB CYC:285 SL:87 +DEE0 F0 0C BEQ $DEEE A:47 X:97 Y:FF P:A4 SP:FB CYC:291 SL:87 +DEE2 E0 97 CPX #$97 A:47 X:97 Y:FF P:A4 SP:FB CYC:297 SL:87 +DEE4 D0 08 BNE $DEEE A:47 X:97 Y:FF P:27 SP:FB CYC:303 SL:87 +DEE6 C0 FF CPY #$FF A:47 X:97 Y:FF P:27 SP:FB CYC:309 SL:87 +DEE8 D0 04 BNE $DEEE A:47 X:97 Y:FF P:27 SP:FB CYC:315 SL:87 +DEEA C9 47 CMP #$47 A:47 X:97 Y:FF P:27 SP:FB CYC:321 SL:87 +DEEC F0 04 BEQ $DEF2 A:47 X:97 Y:FF P:27 SP:FB CYC:327 SL:87 +DEF2 A9 00 LDA #$00 A:47 X:97 Y:FF P:27 SP:FB CYC:336 SL:87 +DEF4 85 7F STA $7F = 97 A:00 X:97 Y:FF P:27 SP:FB CYC: 1 SL:88 +DEF6 A9 47 LDA #$47 A:00 X:97 Y:FF P:27 SP:FB CYC: 10 SL:88 +DEF8 A0 FF LDY #$FF A:47 X:97 Y:FF P:25 SP:FB CYC: 16 SL:88 +DEFA A2 69 LDX #$69 A:47 X:97 Y:FF P:A5 SP:FB CYC: 22 SL:88 +DEFC 18 CLC A:47 X:69 Y:FF P:25 SP:FB CYC: 28 SL:88 +DEFD B8 CLV A:47 X:69 Y:FF P:24 SP:FB CYC: 34 SL:88 +DEFE 96 80 STX $80,Y @ 7F = 00 A:47 X:69 Y:FF P:24 SP:FB CYC: 40 SL:88 +DF00 B0 18 BCS $DF1A A:47 X:69 Y:FF P:24 SP:FB CYC: 52 SL:88 +DF02 70 16 BVS $DF1A A:47 X:69 Y:FF P:24 SP:FB CYC: 58 SL:88 +DF04 30 14 BMI $DF1A A:47 X:69 Y:FF P:24 SP:FB CYC: 64 SL:88 +DF06 F0 12 BEQ $DF1A A:47 X:69 Y:FF P:24 SP:FB CYC: 70 SL:88 +DF08 E0 69 CPX #$69 A:47 X:69 Y:FF P:24 SP:FB CYC: 76 SL:88 +DF0A D0 0E BNE $DF1A A:47 X:69 Y:FF P:27 SP:FB CYC: 82 SL:88 +DF0C C0 FF CPY #$FF A:47 X:69 Y:FF P:27 SP:FB CYC: 88 SL:88 +DF0E D0 0A BNE $DF1A A:47 X:69 Y:FF P:27 SP:FB CYC: 94 SL:88 +DF10 C9 47 CMP #$47 A:47 X:69 Y:FF P:27 SP:FB CYC:100 SL:88 +DF12 D0 06 BNE $DF1A A:47 X:69 Y:FF P:27 SP:FB CYC:106 SL:88 +DF14 A5 7F LDA $7F = 69 A:47 X:69 Y:FF P:27 SP:FB CYC:112 SL:88 +DF16 C9 69 CMP #$69 A:69 X:69 Y:FF P:25 SP:FB CYC:121 SL:88 +DF18 F0 04 BEQ $DF1E A:69 X:69 Y:FF P:27 SP:FB CYC:127 SL:88 +DF1E A9 F5 LDA #$F5 A:69 X:69 Y:FF P:27 SP:FB CYC:136 SL:88 +DF20 85 4F STA $4F = 00 A:F5 X:69 Y:FF P:A5 SP:FB CYC:142 SL:88 +DF22 A9 47 LDA #$47 A:F5 X:69 Y:FF P:A5 SP:FB CYC:151 SL:88 +DF24 A0 4F LDY #$4F A:47 X:69 Y:FF P:25 SP:FB CYC:157 SL:88 +DF26 24 01 BIT $01 = FF A:47 X:69 Y:4F P:25 SP:FB CYC:163 SL:88 +DF28 A2 00 LDX #$00 A:47 X:69 Y:4F P:E5 SP:FB CYC:172 SL:88 +DF2A 38 SEC A:47 X:00 Y:4F P:67 SP:FB CYC:178 SL:88 +DF2B 96 00 STX $00,Y @ 4F = F5 A:47 X:00 Y:4F P:67 SP:FB CYC:184 SL:88 +DF2D 90 16 BCC $DF45 A:47 X:00 Y:4F P:67 SP:FB CYC:196 SL:88 +DF2F 50 14 BVC $DF45 A:47 X:00 Y:4F P:67 SP:FB CYC:202 SL:88 +DF31 30 12 BMI $DF45 A:47 X:00 Y:4F P:67 SP:FB CYC:208 SL:88 +DF33 D0 10 BNE $DF45 A:47 X:00 Y:4F P:67 SP:FB CYC:214 SL:88 +DF35 E0 00 CPX #$00 A:47 X:00 Y:4F P:67 SP:FB CYC:220 SL:88 +DF37 D0 0C BNE $DF45 A:47 X:00 Y:4F P:67 SP:FB CYC:226 SL:88 +DF39 C0 4F CPY #$4F A:47 X:00 Y:4F P:67 SP:FB CYC:232 SL:88 +DF3B D0 08 BNE $DF45 A:47 X:00 Y:4F P:67 SP:FB CYC:238 SL:88 +DF3D C9 47 CMP #$47 A:47 X:00 Y:4F P:67 SP:FB CYC:244 SL:88 +DF3F D0 04 BNE $DF45 A:47 X:00 Y:4F P:67 SP:FB CYC:250 SL:88 +DF41 A5 4F LDA $4F = 00 A:47 X:00 Y:4F P:67 SP:FB CYC:256 SL:88 +DF43 F0 04 BEQ $DF49 A:00 X:00 Y:4F P:67 SP:FB CYC:265 SL:88 +DF49 60 RTS A:00 X:00 Y:4F P:67 SP:FB CYC:274 SL:88 +C62C 20 AA E1 JSR $E1AA A:00 X:00 Y:4F P:67 SP:FD CYC:292 SL:88 +E1AA A9 FF LDA #$FF A:00 X:00 Y:4F P:67 SP:FB CYC:310 SL:88 +E1AC 85 01 STA $01 = FF A:FF X:00 Y:4F P:E5 SP:FB CYC:316 SL:88 +E1AE A9 AA LDA #$AA A:FF X:00 Y:4F P:E5 SP:FB CYC:325 SL:88 +E1B0 8D 33 06 STA $0633 = 00 A:AA X:00 Y:4F P:E5 SP:FB CYC:331 SL:88 +E1B3 A9 BB LDA #$BB A:AA X:00 Y:4F P:E5 SP:FB CYC: 2 SL:89 +E1B5 8D 89 06 STA $0689 = 00 A:BB X:00 Y:4F P:E5 SP:FB CYC: 8 SL:89 +E1B8 A2 00 LDX #$00 A:BB X:00 Y:4F P:E5 SP:FB CYC: 20 SL:89 +E1BA A9 66 LDA #$66 A:BB X:00 Y:4F P:67 SP:FB CYC: 26 SL:89 +E1BC 24 01 BIT $01 = FF A:66 X:00 Y:4F P:65 SP:FB CYC: 32 SL:89 +E1BE 38 SEC A:66 X:00 Y:4F P:E5 SP:FB CYC: 41 SL:89 +E1BF A0 00 LDY #$00 A:66 X:00 Y:4F P:E5 SP:FB CYC: 47 SL:89 +E1C1 BC 33 06 LDY $0633,X @ 0633 = AA A:66 X:00 Y:00 P:67 SP:FB CYC: 53 SL:89 +E1C4 10 12 BPL $E1D8 A:66 X:00 Y:AA P:E5 SP:FB CYC: 65 SL:89 +E1C6 F0 10 BEQ $E1D8 A:66 X:00 Y:AA P:E5 SP:FB CYC: 71 SL:89 +E1C8 50 0E BVC $E1D8 A:66 X:00 Y:AA P:E5 SP:FB CYC: 77 SL:89 +E1CA 90 0C BCC $E1D8 A:66 X:00 Y:AA P:E5 SP:FB CYC: 83 SL:89 +E1CC C9 66 CMP #$66 A:66 X:00 Y:AA P:E5 SP:FB CYC: 89 SL:89 +E1CE D0 08 BNE $E1D8 A:66 X:00 Y:AA P:67 SP:FB CYC: 95 SL:89 +E1D0 E0 00 CPX #$00 A:66 X:00 Y:AA P:67 SP:FB CYC:101 SL:89 +E1D2 D0 04 BNE $E1D8 A:66 X:00 Y:AA P:67 SP:FB CYC:107 SL:89 +E1D4 C0 AA CPY #$AA A:66 X:00 Y:AA P:67 SP:FB CYC:113 SL:89 +E1D6 F0 04 BEQ $E1DC A:66 X:00 Y:AA P:67 SP:FB CYC:119 SL:89 +E1DC A2 8A LDX #$8A A:66 X:00 Y:AA P:67 SP:FB CYC:128 SL:89 +E1DE A9 66 LDA #$66 A:66 X:8A Y:AA P:E5 SP:FB CYC:134 SL:89 +E1E0 B8 CLV A:66 X:8A Y:AA P:65 SP:FB CYC:140 SL:89 +E1E1 18 CLC A:66 X:8A Y:AA P:25 SP:FB CYC:146 SL:89 +E1E2 A0 00 LDY #$00 A:66 X:8A Y:AA P:24 SP:FB CYC:152 SL:89 +E1E4 BC FF 05 LDY $05FF,X @ 0689 = BB A:66 X:8A Y:00 P:26 SP:FB CYC:158 SL:89 +E1E7 10 12 BPL $E1FB A:66 X:8A Y:BB P:A4 SP:FB CYC:173 SL:89 +E1E9 F0 10 BEQ $E1FB A:66 X:8A Y:BB P:A4 SP:FB CYC:179 SL:89 +E1EB 70 0E BVS $E1FB A:66 X:8A Y:BB P:A4 SP:FB CYC:185 SL:89 +E1ED B0 0C BCS $E1FB A:66 X:8A Y:BB P:A4 SP:FB CYC:191 SL:89 +E1EF C0 BB CPY #$BB A:66 X:8A Y:BB P:A4 SP:FB CYC:197 SL:89 +E1F1 D0 08 BNE $E1FB A:66 X:8A Y:BB P:27 SP:FB CYC:203 SL:89 +E1F3 C9 66 CMP #$66 A:66 X:8A Y:BB P:27 SP:FB CYC:209 SL:89 +E1F5 D0 04 BNE $E1FB A:66 X:8A Y:BB P:27 SP:FB CYC:215 SL:89 +E1F7 E0 8A CPX #$8A A:66 X:8A Y:BB P:27 SP:FB CYC:221 SL:89 +E1F9 F0 04 BEQ $E1FF A:66 X:8A Y:BB P:27 SP:FB CYC:227 SL:89 +E1FF A0 53 LDY #$53 A:66 X:8A Y:BB P:27 SP:FB CYC:236 SL:89 +E201 A9 AA LDA #$AA A:66 X:8A Y:53 P:25 SP:FB CYC:242 SL:89 +E203 A2 78 LDX #$78 A:AA X:8A Y:53 P:A5 SP:FB CYC:248 SL:89 +E205 8D 78 06 STA $0678 = 00 A:AA X:78 Y:53 P:25 SP:FB CYC:254 SL:89 +E208 20 B6 F7 JSR $F7B6 A:AA X:78 Y:53 P:25 SP:FB CYC:266 SL:89 +F7B6 18 CLC A:AA X:78 Y:53 P:25 SP:F9 CYC:284 SL:89 +F7B7 A9 FF LDA #$FF A:AA X:78 Y:53 P:24 SP:F9 CYC:290 SL:89 +F7B9 85 01 STA $01 = FF A:FF X:78 Y:53 P:A4 SP:F9 CYC:296 SL:89 +F7BB 24 01 BIT $01 = FF A:FF X:78 Y:53 P:A4 SP:F9 CYC:305 SL:89 +F7BD A9 55 LDA #$55 A:FF X:78 Y:53 P:E4 SP:F9 CYC:314 SL:89 +F7BF 60 RTS A:55 X:78 Y:53 P:64 SP:F9 CYC:320 SL:89 +E20B 1D 00 06 ORA $0600,X @ 0678 = AA A:55 X:78 Y:53 P:64 SP:FB CYC:338 SL:89 +E20E 20 C0 F7 JSR $F7C0 A:FF X:78 Y:53 P:E4 SP:FB CYC: 9 SL:90 +F7C0 B0 09 BCS $F7CB A:FF X:78 Y:53 P:E4 SP:F9 CYC: 27 SL:90 +F7C2 10 07 BPL $F7CB A:FF X:78 Y:53 P:E4 SP:F9 CYC: 33 SL:90 +F7C4 C9 FF CMP #$FF A:FF X:78 Y:53 P:E4 SP:F9 CYC: 39 SL:90 +F7C6 D0 03 BNE $F7CB A:FF X:78 Y:53 P:67 SP:F9 CYC: 45 SL:90 +F7C8 50 01 BVC $F7CB A:FF X:78 Y:53 P:67 SP:F9 CYC: 51 SL:90 +F7CA 60 RTS A:FF X:78 Y:53 P:67 SP:F9 CYC: 57 SL:90 +E211 C8 INY A:FF X:78 Y:53 P:67 SP:FB CYC: 75 SL:90 +E212 A9 00 LDA #$00 A:FF X:78 Y:54 P:65 SP:FB CYC: 81 SL:90 +E214 8D 78 06 STA $0678 = AA A:00 X:78 Y:54 P:67 SP:FB CYC: 87 SL:90 +E217 20 CE F7 JSR $F7CE A:00 X:78 Y:54 P:67 SP:FB CYC: 99 SL:90 +F7CE 38 SEC A:00 X:78 Y:54 P:67 SP:F9 CYC:117 SL:90 +F7CF B8 CLV A:00 X:78 Y:54 P:67 SP:F9 CYC:123 SL:90 +F7D0 A9 00 LDA #$00 A:00 X:78 Y:54 P:27 SP:F9 CYC:129 SL:90 +F7D2 60 RTS A:00 X:78 Y:54 P:27 SP:F9 CYC:135 SL:90 +E21A 1D 00 06 ORA $0600,X @ 0678 = 00 A:00 X:78 Y:54 P:27 SP:FB CYC:153 SL:90 +E21D 20 D3 F7 JSR $F7D3 A:00 X:78 Y:54 P:27 SP:FB CYC:165 SL:90 +F7D3 D0 07 BNE $F7DC A:00 X:78 Y:54 P:27 SP:F9 CYC:183 SL:90 +F7D5 70 05 BVS $F7DC A:00 X:78 Y:54 P:27 SP:F9 CYC:189 SL:90 +F7D7 90 03 BCC $F7DC A:00 X:78 Y:54 P:27 SP:F9 CYC:195 SL:90 +F7D9 30 01 BMI $F7DC A:00 X:78 Y:54 P:27 SP:F9 CYC:201 SL:90 +F7DB 60 RTS A:00 X:78 Y:54 P:27 SP:F9 CYC:207 SL:90 +E220 C8 INY A:00 X:78 Y:54 P:27 SP:FB CYC:225 SL:90 +E221 A9 AA LDA #$AA A:00 X:78 Y:55 P:25 SP:FB CYC:231 SL:90 +E223 8D 78 06 STA $0678 = 00 A:AA X:78 Y:55 P:A5 SP:FB CYC:237 SL:90 +E226 20 DF F7 JSR $F7DF A:AA X:78 Y:55 P:A5 SP:FB CYC:249 SL:90 +F7DF 18 CLC A:AA X:78 Y:55 P:A5 SP:F9 CYC:267 SL:90 +F7E0 24 01 BIT $01 = FF A:AA X:78 Y:55 P:A4 SP:F9 CYC:273 SL:90 +F7E2 A9 55 LDA #$55 A:AA X:78 Y:55 P:E4 SP:F9 CYC:282 SL:90 +F7E4 60 RTS A:55 X:78 Y:55 P:64 SP:F9 CYC:288 SL:90 +E229 3D 00 06 AND $0600,X @ 0678 = AA A:55 X:78 Y:55 P:64 SP:FB CYC:306 SL:90 +E22C 20 E5 F7 JSR $F7E5 A:00 X:78 Y:55 P:66 SP:FB CYC:318 SL:90 +F7E5 D0 07 BNE $F7EE A:00 X:78 Y:55 P:66 SP:F9 CYC:336 SL:90 +F7E7 50 05 BVC $F7EE A:00 X:78 Y:55 P:66 SP:F9 CYC: 1 SL:91 +F7E9 B0 03 BCS $F7EE A:00 X:78 Y:55 P:66 SP:F9 CYC: 7 SL:91 +F7EB 30 01 BMI $F7EE A:00 X:78 Y:55 P:66 SP:F9 CYC: 13 SL:91 +F7ED 60 RTS A:00 X:78 Y:55 P:66 SP:F9 CYC: 19 SL:91 +E22F C8 INY A:00 X:78 Y:55 P:66 SP:FB CYC: 37 SL:91 +E230 A9 EF LDA #$EF A:00 X:78 Y:56 P:64 SP:FB CYC: 43 SL:91 +E232 8D 78 06 STA $0678 = AA A:EF X:78 Y:56 P:E4 SP:FB CYC: 49 SL:91 +E235 20 F1 F7 JSR $F7F1 A:EF X:78 Y:56 P:E4 SP:FB CYC: 61 SL:91 +F7F1 38 SEC A:EF X:78 Y:56 P:E4 SP:F9 CYC: 79 SL:91 +F7F2 B8 CLV A:EF X:78 Y:56 P:E5 SP:F9 CYC: 85 SL:91 +F7F3 A9 F8 LDA #$F8 A:EF X:78 Y:56 P:A5 SP:F9 CYC: 91 SL:91 +F7F5 60 RTS A:F8 X:78 Y:56 P:A5 SP:F9 CYC: 97 SL:91 +E238 3D 00 06 AND $0600,X @ 0678 = EF A:F8 X:78 Y:56 P:A5 SP:FB CYC:115 SL:91 +E23B 20 F6 F7 JSR $F7F6 A:E8 X:78 Y:56 P:A5 SP:FB CYC:127 SL:91 +F7F6 90 09 BCC $F801 A:E8 X:78 Y:56 P:A5 SP:F9 CYC:145 SL:91 +F7F8 10 07 BPL $F801 A:E8 X:78 Y:56 P:A5 SP:F9 CYC:151 SL:91 +F7FA C9 E8 CMP #$E8 A:E8 X:78 Y:56 P:A5 SP:F9 CYC:157 SL:91 +F7FC D0 03 BNE $F801 A:E8 X:78 Y:56 P:27 SP:F9 CYC:163 SL:91 +F7FE 70 01 BVS $F801 A:E8 X:78 Y:56 P:27 SP:F9 CYC:169 SL:91 +F800 60 RTS A:E8 X:78 Y:56 P:27 SP:F9 CYC:175 SL:91 +E23E C8 INY A:E8 X:78 Y:56 P:27 SP:FB CYC:193 SL:91 +E23F A9 AA LDA #$AA A:E8 X:78 Y:57 P:25 SP:FB CYC:199 SL:91 +E241 8D 78 06 STA $0678 = EF A:AA X:78 Y:57 P:A5 SP:FB CYC:205 SL:91 +E244 20 04 F8 JSR $F804 A:AA X:78 Y:57 P:A5 SP:FB CYC:217 SL:91 +F804 18 CLC A:AA X:78 Y:57 P:A5 SP:F9 CYC:235 SL:91 +F805 24 01 BIT $01 = FF A:AA X:78 Y:57 P:A4 SP:F9 CYC:241 SL:91 +F807 A9 5F LDA #$5F A:AA X:78 Y:57 P:E4 SP:F9 CYC:250 SL:91 +F809 60 RTS A:5F X:78 Y:57 P:64 SP:F9 CYC:256 SL:91 +E247 5D 00 06 EOR $0600,X @ 0678 = AA A:5F X:78 Y:57 P:64 SP:FB CYC:274 SL:91 +E24A 20 0A F8 JSR $F80A A:F5 X:78 Y:57 P:E4 SP:FB CYC:286 SL:91 +F80A B0 09 BCS $F815 A:F5 X:78 Y:57 P:E4 SP:F9 CYC:304 SL:91 +F80C 10 07 BPL $F815 A:F5 X:78 Y:57 P:E4 SP:F9 CYC:310 SL:91 +F80E C9 F5 CMP #$F5 A:F5 X:78 Y:57 P:E4 SP:F9 CYC:316 SL:91 +F810 D0 03 BNE $F815 A:F5 X:78 Y:57 P:67 SP:F9 CYC:322 SL:91 +F812 50 01 BVC $F815 A:F5 X:78 Y:57 P:67 SP:F9 CYC:328 SL:91 +F814 60 RTS A:F5 X:78 Y:57 P:67 SP:F9 CYC:334 SL:91 +E24D C8 INY A:F5 X:78 Y:57 P:67 SP:FB CYC: 11 SL:92 +E24E A9 70 LDA #$70 A:F5 X:78 Y:58 P:65 SP:FB CYC: 17 SL:92 +E250 8D 78 06 STA $0678 = AA A:70 X:78 Y:58 P:65 SP:FB CYC: 23 SL:92 +E253 20 18 F8 JSR $F818 A:70 X:78 Y:58 P:65 SP:FB CYC: 35 SL:92 +F818 38 SEC A:70 X:78 Y:58 P:65 SP:F9 CYC: 53 SL:92 +F819 B8 CLV A:70 X:78 Y:58 P:65 SP:F9 CYC: 59 SL:92 +F81A A9 70 LDA #$70 A:70 X:78 Y:58 P:25 SP:F9 CYC: 65 SL:92 +F81C 60 RTS A:70 X:78 Y:58 P:25 SP:F9 CYC: 71 SL:92 +E256 5D 00 06 EOR $0600,X @ 0678 = 70 A:70 X:78 Y:58 P:25 SP:FB CYC: 89 SL:92 +E259 20 1D F8 JSR $F81D A:00 X:78 Y:58 P:27 SP:FB CYC:101 SL:92 +F81D D0 07 BNE $F826 A:00 X:78 Y:58 P:27 SP:F9 CYC:119 SL:92 +F81F 70 05 BVS $F826 A:00 X:78 Y:58 P:27 SP:F9 CYC:125 SL:92 +F821 90 03 BCC $F826 A:00 X:78 Y:58 P:27 SP:F9 CYC:131 SL:92 +F823 30 01 BMI $F826 A:00 X:78 Y:58 P:27 SP:F9 CYC:137 SL:92 +F825 60 RTS A:00 X:78 Y:58 P:27 SP:F9 CYC:143 SL:92 +E25C C8 INY A:00 X:78 Y:58 P:27 SP:FB CYC:161 SL:92 +E25D A9 69 LDA #$69 A:00 X:78 Y:59 P:25 SP:FB CYC:167 SL:92 +E25F 8D 78 06 STA $0678 = 70 A:69 X:78 Y:59 P:25 SP:FB CYC:173 SL:92 +E262 20 29 F8 JSR $F829 A:69 X:78 Y:59 P:25 SP:FB CYC:185 SL:92 +F829 18 CLC A:69 X:78 Y:59 P:25 SP:F9 CYC:203 SL:92 +F82A 24 01 BIT $01 = FF A:69 X:78 Y:59 P:24 SP:F9 CYC:209 SL:92 +F82C A9 00 LDA #$00 A:69 X:78 Y:59 P:E4 SP:F9 CYC:218 SL:92 +F82E 60 RTS A:00 X:78 Y:59 P:66 SP:F9 CYC:224 SL:92 +E265 7D 00 06 ADC $0600,X @ 0678 = 69 A:00 X:78 Y:59 P:66 SP:FB CYC:242 SL:92 +E268 20 2F F8 JSR $F82F A:69 X:78 Y:59 P:24 SP:FB CYC:254 SL:92 +F82F 30 09 BMI $F83A A:69 X:78 Y:59 P:24 SP:F9 CYC:272 SL:92 +F831 B0 07 BCS $F83A A:69 X:78 Y:59 P:24 SP:F9 CYC:278 SL:92 +F833 C9 69 CMP #$69 A:69 X:78 Y:59 P:24 SP:F9 CYC:284 SL:92 +F835 D0 03 BNE $F83A A:69 X:78 Y:59 P:27 SP:F9 CYC:290 SL:92 +F837 70 01 BVS $F83A A:69 X:78 Y:59 P:27 SP:F9 CYC:296 SL:92 +F839 60 RTS A:69 X:78 Y:59 P:27 SP:F9 CYC:302 SL:92 +E26B C8 INY A:69 X:78 Y:59 P:27 SP:FB CYC:320 SL:92 +E26C 20 3D F8 JSR $F83D A:69 X:78 Y:5A P:25 SP:FB CYC:326 SL:92 +F83D 38 SEC A:69 X:78 Y:5A P:25 SP:F9 CYC: 3 SL:93 +F83E 24 01 BIT $01 = FF A:69 X:78 Y:5A P:25 SP:F9 CYC: 9 SL:93 +F840 A9 00 LDA #$00 A:69 X:78 Y:5A P:E5 SP:F9 CYC: 18 SL:93 +F842 60 RTS A:00 X:78 Y:5A P:67 SP:F9 CYC: 24 SL:93 +E26F 7D 00 06 ADC $0600,X @ 0678 = 69 A:00 X:78 Y:5A P:67 SP:FB CYC: 42 SL:93 +E272 20 43 F8 JSR $F843 A:6A X:78 Y:5A P:24 SP:FB CYC: 54 SL:93 +F843 30 09 BMI $F84E A:6A X:78 Y:5A P:24 SP:F9 CYC: 72 SL:93 +F845 B0 07 BCS $F84E A:6A X:78 Y:5A P:24 SP:F9 CYC: 78 SL:93 +F847 C9 6A CMP #$6A A:6A X:78 Y:5A P:24 SP:F9 CYC: 84 SL:93 +F849 D0 03 BNE $F84E A:6A X:78 Y:5A P:27 SP:F9 CYC: 90 SL:93 +F84B 70 01 BVS $F84E A:6A X:78 Y:5A P:27 SP:F9 CYC: 96 SL:93 +F84D 60 RTS A:6A X:78 Y:5A P:27 SP:F9 CYC:102 SL:93 +E275 C8 INY A:6A X:78 Y:5A P:27 SP:FB CYC:120 SL:93 +E276 A9 7F LDA #$7F A:6A X:78 Y:5B P:25 SP:FB CYC:126 SL:93 +E278 8D 78 06 STA $0678 = 69 A:7F X:78 Y:5B P:25 SP:FB CYC:132 SL:93 +E27B 20 51 F8 JSR $F851 A:7F X:78 Y:5B P:25 SP:FB CYC:144 SL:93 +F851 38 SEC A:7F X:78 Y:5B P:25 SP:F9 CYC:162 SL:93 +F852 B8 CLV A:7F X:78 Y:5B P:25 SP:F9 CYC:168 SL:93 +F853 A9 7F LDA #$7F A:7F X:78 Y:5B P:25 SP:F9 CYC:174 SL:93 +F855 60 RTS A:7F X:78 Y:5B P:25 SP:F9 CYC:180 SL:93 +E27E 7D 00 06 ADC $0600,X @ 0678 = 7F A:7F X:78 Y:5B P:25 SP:FB CYC:198 SL:93 +E281 20 56 F8 JSR $F856 A:FF X:78 Y:5B P:E4 SP:FB CYC:210 SL:93 +F856 10 09 BPL $F861 A:FF X:78 Y:5B P:E4 SP:F9 CYC:228 SL:93 +F858 B0 07 BCS $F861 A:FF X:78 Y:5B P:E4 SP:F9 CYC:234 SL:93 +F85A C9 FF CMP #$FF A:FF X:78 Y:5B P:E4 SP:F9 CYC:240 SL:93 +F85C D0 03 BNE $F861 A:FF X:78 Y:5B P:67 SP:F9 CYC:246 SL:93 +F85E 50 01 BVC $F861 A:FF X:78 Y:5B P:67 SP:F9 CYC:252 SL:93 +F860 60 RTS A:FF X:78 Y:5B P:67 SP:F9 CYC:258 SL:93 +E284 C8 INY A:FF X:78 Y:5B P:67 SP:FB CYC:276 SL:93 +E285 A9 80 LDA #$80 A:FF X:78 Y:5C P:65 SP:FB CYC:282 SL:93 +E287 8D 78 06 STA $0678 = 7F A:80 X:78 Y:5C P:E5 SP:FB CYC:288 SL:93 +E28A 20 64 F8 JSR $F864 A:80 X:78 Y:5C P:E5 SP:FB CYC:300 SL:93 +F864 18 CLC A:80 X:78 Y:5C P:E5 SP:F9 CYC:318 SL:93 +F865 24 01 BIT $01 = FF A:80 X:78 Y:5C P:E4 SP:F9 CYC:324 SL:93 +F867 A9 7F LDA #$7F A:80 X:78 Y:5C P:E4 SP:F9 CYC:333 SL:93 +F869 60 RTS A:7F X:78 Y:5C P:64 SP:F9 CYC:339 SL:93 +E28D 7D 00 06 ADC $0600,X @ 0678 = 80 A:7F X:78 Y:5C P:64 SP:FB CYC: 16 SL:94 +E290 20 6A F8 JSR $F86A A:FF X:78 Y:5C P:A4 SP:FB CYC: 28 SL:94 +F86A 10 09 BPL $F875 A:FF X:78 Y:5C P:A4 SP:F9 CYC: 46 SL:94 +F86C B0 07 BCS $F875 A:FF X:78 Y:5C P:A4 SP:F9 CYC: 52 SL:94 +F86E C9 FF CMP #$FF A:FF X:78 Y:5C P:A4 SP:F9 CYC: 58 SL:94 +F870 D0 03 BNE $F875 A:FF X:78 Y:5C P:27 SP:F9 CYC: 64 SL:94 +F872 70 01 BVS $F875 A:FF X:78 Y:5C P:27 SP:F9 CYC: 70 SL:94 +F874 60 RTS A:FF X:78 Y:5C P:27 SP:F9 CYC: 76 SL:94 +E293 C8 INY A:FF X:78 Y:5C P:27 SP:FB CYC: 94 SL:94 +E294 20 78 F8 JSR $F878 A:FF X:78 Y:5D P:25 SP:FB CYC:100 SL:94 +F878 38 SEC A:FF X:78 Y:5D P:25 SP:F9 CYC:118 SL:94 +F879 B8 CLV A:FF X:78 Y:5D P:25 SP:F9 CYC:124 SL:94 +F87A A9 7F LDA #$7F A:FF X:78 Y:5D P:25 SP:F9 CYC:130 SL:94 +F87C 60 RTS A:7F X:78 Y:5D P:25 SP:F9 CYC:136 SL:94 +E297 7D 00 06 ADC $0600,X @ 0678 = 80 A:7F X:78 Y:5D P:25 SP:FB CYC:154 SL:94 +E29A 20 7D F8 JSR $F87D A:00 X:78 Y:5D P:27 SP:FB CYC:166 SL:94 +F87D D0 07 BNE $F886 A:00 X:78 Y:5D P:27 SP:F9 CYC:184 SL:94 +F87F 30 05 BMI $F886 A:00 X:78 Y:5D P:27 SP:F9 CYC:190 SL:94 +F881 70 03 BVS $F886 A:00 X:78 Y:5D P:27 SP:F9 CYC:196 SL:94 +F883 90 01 BCC $F886 A:00 X:78 Y:5D P:27 SP:F9 CYC:202 SL:94 +F885 60 RTS A:00 X:78 Y:5D P:27 SP:F9 CYC:208 SL:94 +E29D C8 INY A:00 X:78 Y:5D P:27 SP:FB CYC:226 SL:94 +E29E A9 40 LDA #$40 A:00 X:78 Y:5E P:25 SP:FB CYC:232 SL:94 +E2A0 8D 78 06 STA $0678 = 80 A:40 X:78 Y:5E P:25 SP:FB CYC:238 SL:94 +E2A3 20 89 F8 JSR $F889 A:40 X:78 Y:5E P:25 SP:FB CYC:250 SL:94 +F889 24 01 BIT $01 = FF A:40 X:78 Y:5E P:25 SP:F9 CYC:268 SL:94 +F88B A9 40 LDA #$40 A:40 X:78 Y:5E P:E5 SP:F9 CYC:277 SL:94 +F88D 60 RTS A:40 X:78 Y:5E P:65 SP:F9 CYC:283 SL:94 +E2A6 DD 00 06 CMP $0600,X @ 0678 = 40 A:40 X:78 Y:5E P:65 SP:FB CYC:301 SL:94 +E2A9 20 8E F8 JSR $F88E A:40 X:78 Y:5E P:67 SP:FB CYC:313 SL:94 +F88E 30 07 BMI $F897 A:40 X:78 Y:5E P:67 SP:F9 CYC:331 SL:94 +F890 90 05 BCC $F897 A:40 X:78 Y:5E P:67 SP:F9 CYC:337 SL:94 +F892 D0 03 BNE $F897 A:40 X:78 Y:5E P:67 SP:F9 CYC: 2 SL:95 +F894 50 01 BVC $F897 A:40 X:78 Y:5E P:67 SP:F9 CYC: 8 SL:95 +F896 60 RTS A:40 X:78 Y:5E P:67 SP:F9 CYC: 14 SL:95 +E2AC C8 INY A:40 X:78 Y:5E P:67 SP:FB CYC: 32 SL:95 +E2AD 48 PHA A:40 X:78 Y:5F P:65 SP:FB CYC: 38 SL:95 +E2AE A9 3F LDA #$3F A:40 X:78 Y:5F P:65 SP:FA CYC: 47 SL:95 +E2B0 8D 78 06 STA $0678 = 40 A:3F X:78 Y:5F P:65 SP:FA CYC: 53 SL:95 +E2B3 68 PLA A:3F X:78 Y:5F P:65 SP:FA CYC: 65 SL:95 +E2B4 20 9A F8 JSR $F89A A:40 X:78 Y:5F P:65 SP:FB CYC: 77 SL:95 +F89A B8 CLV A:40 X:78 Y:5F P:65 SP:F9 CYC: 95 SL:95 +F89B 60 RTS A:40 X:78 Y:5F P:25 SP:F9 CYC:101 SL:95 +E2B7 DD 00 06 CMP $0600,X @ 0678 = 3F A:40 X:78 Y:5F P:25 SP:FB CYC:119 SL:95 +E2BA 20 9C F8 JSR $F89C A:40 X:78 Y:5F P:25 SP:FB CYC:131 SL:95 +F89C F0 07 BEQ $F8A5 A:40 X:78 Y:5F P:25 SP:F9 CYC:149 SL:95 +F89E 30 05 BMI $F8A5 A:40 X:78 Y:5F P:25 SP:F9 CYC:155 SL:95 +F8A0 90 03 BCC $F8A5 A:40 X:78 Y:5F P:25 SP:F9 CYC:161 SL:95 +F8A2 70 01 BVS $F8A5 A:40 X:78 Y:5F P:25 SP:F9 CYC:167 SL:95 +F8A4 60 RTS A:40 X:78 Y:5F P:25 SP:F9 CYC:173 SL:95 +E2BD C8 INY A:40 X:78 Y:5F P:25 SP:FB CYC:191 SL:95 +E2BE 48 PHA A:40 X:78 Y:60 P:25 SP:FB CYC:197 SL:95 +E2BF A9 41 LDA #$41 A:40 X:78 Y:60 P:25 SP:FA CYC:206 SL:95 +E2C1 8D 78 06 STA $0678 = 3F A:41 X:78 Y:60 P:25 SP:FA CYC:212 SL:95 +E2C4 68 PLA A:41 X:78 Y:60 P:25 SP:FA CYC:224 SL:95 +E2C5 DD 00 06 CMP $0600,X @ 0678 = 41 A:40 X:78 Y:60 P:25 SP:FB CYC:236 SL:95 +E2C8 20 A8 F8 JSR $F8A8 A:40 X:78 Y:60 P:A4 SP:FB CYC:248 SL:95 +F8A8 F0 05 BEQ $F8AF A:40 X:78 Y:60 P:A4 SP:F9 CYC:266 SL:95 +F8AA 10 03 BPL $F8AF A:40 X:78 Y:60 P:A4 SP:F9 CYC:272 SL:95 +F8AC 10 01 BPL $F8AF A:40 X:78 Y:60 P:A4 SP:F9 CYC:278 SL:95 +F8AE 60 RTS A:40 X:78 Y:60 P:A4 SP:F9 CYC:284 SL:95 +E2CB C8 INY A:40 X:78 Y:60 P:A4 SP:FB CYC:302 SL:95 +E2CC 48 PHA A:40 X:78 Y:61 P:24 SP:FB CYC:308 SL:95 +E2CD A9 00 LDA #$00 A:40 X:78 Y:61 P:24 SP:FA CYC:317 SL:95 +E2CF 8D 78 06 STA $0678 = 41 A:00 X:78 Y:61 P:26 SP:FA CYC:323 SL:95 +E2D2 68 PLA A:00 X:78 Y:61 P:26 SP:FA CYC:335 SL:95 +E2D3 20 B2 F8 JSR $F8B2 A:40 X:78 Y:61 P:24 SP:FB CYC: 6 SL:96 +F8B2 A9 80 LDA #$80 A:40 X:78 Y:61 P:24 SP:F9 CYC: 24 SL:96 +F8B4 60 RTS A:80 X:78 Y:61 P:A4 SP:F9 CYC: 30 SL:96 +E2D6 DD 00 06 CMP $0600,X @ 0678 = 00 A:80 X:78 Y:61 P:A4 SP:FB CYC: 48 SL:96 +E2D9 20 B5 F8 JSR $F8B5 A:80 X:78 Y:61 P:A5 SP:FB CYC: 60 SL:96 +F8B5 F0 05 BEQ $F8BC A:80 X:78 Y:61 P:A5 SP:F9 CYC: 78 SL:96 +F8B7 10 03 BPL $F8BC A:80 X:78 Y:61 P:A5 SP:F9 CYC: 84 SL:96 +F8B9 90 01 BCC $F8BC A:80 X:78 Y:61 P:A5 SP:F9 CYC: 90 SL:96 +F8BB 60 RTS A:80 X:78 Y:61 P:A5 SP:F9 CYC: 96 SL:96 +E2DC C8 INY A:80 X:78 Y:61 P:A5 SP:FB CYC:114 SL:96 +E2DD 48 PHA A:80 X:78 Y:62 P:25 SP:FB CYC:120 SL:96 +E2DE A9 80 LDA #$80 A:80 X:78 Y:62 P:25 SP:FA CYC:129 SL:96 +E2E0 8D 78 06 STA $0678 = 00 A:80 X:78 Y:62 P:A5 SP:FA CYC:135 SL:96 +E2E3 68 PLA A:80 X:78 Y:62 P:A5 SP:FA CYC:147 SL:96 +E2E4 DD 00 06 CMP $0600,X @ 0678 = 80 A:80 X:78 Y:62 P:A5 SP:FB CYC:159 SL:96 +E2E7 20 BF F8 JSR $F8BF A:80 X:78 Y:62 P:27 SP:FB CYC:171 SL:96 +F8BF D0 05 BNE $F8C6 A:80 X:78 Y:62 P:27 SP:F9 CYC:189 SL:96 +F8C1 30 03 BMI $F8C6 A:80 X:78 Y:62 P:27 SP:F9 CYC:195 SL:96 +F8C3 90 01 BCC $F8C6 A:80 X:78 Y:62 P:27 SP:F9 CYC:201 SL:96 +F8C5 60 RTS A:80 X:78 Y:62 P:27 SP:F9 CYC:207 SL:96 +E2EA C8 INY A:80 X:78 Y:62 P:27 SP:FB CYC:225 SL:96 +E2EB 48 PHA A:80 X:78 Y:63 P:25 SP:FB CYC:231 SL:96 +E2EC A9 81 LDA #$81 A:80 X:78 Y:63 P:25 SP:FA CYC:240 SL:96 +E2EE 8D 78 06 STA $0678 = 80 A:81 X:78 Y:63 P:A5 SP:FA CYC:246 SL:96 +E2F1 68 PLA A:81 X:78 Y:63 P:A5 SP:FA CYC:258 SL:96 +E2F2 DD 00 06 CMP $0600,X @ 0678 = 81 A:80 X:78 Y:63 P:A5 SP:FB CYC:270 SL:96 +E2F5 20 C9 F8 JSR $F8C9 A:80 X:78 Y:63 P:A4 SP:FB CYC:282 SL:96 +F8C9 B0 05 BCS $F8D0 A:80 X:78 Y:63 P:A4 SP:F9 CYC:300 SL:96 +F8CB F0 03 BEQ $F8D0 A:80 X:78 Y:63 P:A4 SP:F9 CYC:306 SL:96 +F8CD 10 01 BPL $F8D0 A:80 X:78 Y:63 P:A4 SP:F9 CYC:312 SL:96 +F8CF 60 RTS A:80 X:78 Y:63 P:A4 SP:F9 CYC:318 SL:96 +E2F8 C8 INY A:80 X:78 Y:63 P:A4 SP:FB CYC:336 SL:96 +E2F9 48 PHA A:80 X:78 Y:64 P:24 SP:FB CYC: 1 SL:97 +E2FA A9 7F LDA #$7F A:80 X:78 Y:64 P:24 SP:FA CYC: 10 SL:97 +E2FC 8D 78 06 STA $0678 = 81 A:7F X:78 Y:64 P:24 SP:FA CYC: 16 SL:97 +E2FF 68 PLA A:7F X:78 Y:64 P:24 SP:FA CYC: 28 SL:97 +E300 DD 00 06 CMP $0600,X @ 0678 = 7F A:80 X:78 Y:64 P:A4 SP:FB CYC: 40 SL:97 +E303 20 D3 F8 JSR $F8D3 A:80 X:78 Y:64 P:25 SP:FB CYC: 52 SL:97 +F8D3 90 05 BCC $F8DA A:80 X:78 Y:64 P:25 SP:F9 CYC: 70 SL:97 +F8D5 F0 03 BEQ $F8DA A:80 X:78 Y:64 P:25 SP:F9 CYC: 76 SL:97 +F8D7 30 01 BMI $F8DA A:80 X:78 Y:64 P:25 SP:F9 CYC: 82 SL:97 +F8D9 60 RTS A:80 X:78 Y:64 P:25 SP:F9 CYC: 88 SL:97 +E306 C8 INY A:80 X:78 Y:64 P:25 SP:FB CYC:106 SL:97 +E307 A9 40 LDA #$40 A:80 X:78 Y:65 P:25 SP:FB CYC:112 SL:97 +E309 8D 78 06 STA $0678 = 7F A:40 X:78 Y:65 P:25 SP:FB CYC:118 SL:97 +E30C 20 31 F9 JSR $F931 A:40 X:78 Y:65 P:25 SP:FB CYC:130 SL:97 +F931 24 01 BIT $01 = FF A:40 X:78 Y:65 P:25 SP:F9 CYC:148 SL:97 +F933 A9 40 LDA #$40 A:40 X:78 Y:65 P:E5 SP:F9 CYC:157 SL:97 +F935 38 SEC A:40 X:78 Y:65 P:65 SP:F9 CYC:163 SL:97 +F936 60 RTS A:40 X:78 Y:65 P:65 SP:F9 CYC:169 SL:97 +E30F FD 00 06 SBC $0600,X @ 0678 = 40 A:40 X:78 Y:65 P:65 SP:FB CYC:187 SL:97 +E312 20 37 F9 JSR $F937 A:00 X:78 Y:65 P:27 SP:FB CYC:199 SL:97 +F937 30 0B BMI $F944 A:00 X:78 Y:65 P:27 SP:F9 CYC:217 SL:97 +F939 90 09 BCC $F944 A:00 X:78 Y:65 P:27 SP:F9 CYC:223 SL:97 +F93B D0 07 BNE $F944 A:00 X:78 Y:65 P:27 SP:F9 CYC:229 SL:97 +F93D 70 05 BVS $F944 A:00 X:78 Y:65 P:27 SP:F9 CYC:235 SL:97 +F93F C9 00 CMP #$00 A:00 X:78 Y:65 P:27 SP:F9 CYC:241 SL:97 +F941 D0 01 BNE $F944 A:00 X:78 Y:65 P:27 SP:F9 CYC:247 SL:97 +F943 60 RTS A:00 X:78 Y:65 P:27 SP:F9 CYC:253 SL:97 +E315 C8 INY A:00 X:78 Y:65 P:27 SP:FB CYC:271 SL:97 +E316 A9 3F LDA #$3F A:00 X:78 Y:66 P:25 SP:FB CYC:277 SL:97 +E318 8D 78 06 STA $0678 = 40 A:3F X:78 Y:66 P:25 SP:FB CYC:283 SL:97 +E31B 20 47 F9 JSR $F947 A:3F X:78 Y:66 P:25 SP:FB CYC:295 SL:97 +F947 B8 CLV A:3F X:78 Y:66 P:25 SP:F9 CYC:313 SL:97 +F948 38 SEC A:3F X:78 Y:66 P:25 SP:F9 CYC:319 SL:97 +F949 A9 40 LDA #$40 A:3F X:78 Y:66 P:25 SP:F9 CYC:325 SL:97 +F94B 60 RTS A:40 X:78 Y:66 P:25 SP:F9 CYC:331 SL:97 +E31E FD 00 06 SBC $0600,X @ 0678 = 3F A:40 X:78 Y:66 P:25 SP:FB CYC: 8 SL:98 +E321 20 4C F9 JSR $F94C A:01 X:78 Y:66 P:25 SP:FB CYC: 20 SL:98 +F94C F0 0B BEQ $F959 A:01 X:78 Y:66 P:25 SP:F9 CYC: 38 SL:98 +F94E 30 09 BMI $F959 A:01 X:78 Y:66 P:25 SP:F9 CYC: 44 SL:98 +F950 90 07 BCC $F959 A:01 X:78 Y:66 P:25 SP:F9 CYC: 50 SL:98 +F952 70 05 BVS $F959 A:01 X:78 Y:66 P:25 SP:F9 CYC: 56 SL:98 +F954 C9 01 CMP #$01 A:01 X:78 Y:66 P:25 SP:F9 CYC: 62 SL:98 +F956 D0 01 BNE $F959 A:01 X:78 Y:66 P:27 SP:F9 CYC: 68 SL:98 +F958 60 RTS A:01 X:78 Y:66 P:27 SP:F9 CYC: 74 SL:98 +E324 C8 INY A:01 X:78 Y:66 P:27 SP:FB CYC: 92 SL:98 +E325 A9 41 LDA #$41 A:01 X:78 Y:67 P:25 SP:FB CYC: 98 SL:98 +E327 8D 78 06 STA $0678 = 3F A:41 X:78 Y:67 P:25 SP:FB CYC:104 SL:98 +E32A 20 5C F9 JSR $F95C A:41 X:78 Y:67 P:25 SP:FB CYC:116 SL:98 +F95C A9 40 LDA #$40 A:41 X:78 Y:67 P:25 SP:F9 CYC:134 SL:98 +F95E 38 SEC A:40 X:78 Y:67 P:25 SP:F9 CYC:140 SL:98 +F95F 24 01 BIT $01 = FF A:40 X:78 Y:67 P:25 SP:F9 CYC:146 SL:98 +F961 60 RTS A:40 X:78 Y:67 P:E5 SP:F9 CYC:155 SL:98 +E32D FD 00 06 SBC $0600,X @ 0678 = 41 A:40 X:78 Y:67 P:E5 SP:FB CYC:173 SL:98 +E330 20 62 F9 JSR $F962 A:FF X:78 Y:67 P:A4 SP:FB CYC:185 SL:98 +F962 B0 0B BCS $F96F A:FF X:78 Y:67 P:A4 SP:F9 CYC:203 SL:98 +F964 F0 09 BEQ $F96F A:FF X:78 Y:67 P:A4 SP:F9 CYC:209 SL:98 +F966 10 07 BPL $F96F A:FF X:78 Y:67 P:A4 SP:F9 CYC:215 SL:98 +F968 70 05 BVS $F96F A:FF X:78 Y:67 P:A4 SP:F9 CYC:221 SL:98 +F96A C9 FF CMP #$FF A:FF X:78 Y:67 P:A4 SP:F9 CYC:227 SL:98 +F96C D0 01 BNE $F96F A:FF X:78 Y:67 P:27 SP:F9 CYC:233 SL:98 +F96E 60 RTS A:FF X:78 Y:67 P:27 SP:F9 CYC:239 SL:98 +E333 C8 INY A:FF X:78 Y:67 P:27 SP:FB CYC:257 SL:98 +E334 A9 00 LDA #$00 A:FF X:78 Y:68 P:25 SP:FB CYC:263 SL:98 +E336 8D 78 06 STA $0678 = 41 A:00 X:78 Y:68 P:27 SP:FB CYC:269 SL:98 +E339 20 72 F9 JSR $F972 A:00 X:78 Y:68 P:27 SP:FB CYC:281 SL:98 +F972 18 CLC A:00 X:78 Y:68 P:27 SP:F9 CYC:299 SL:98 +F973 A9 80 LDA #$80 A:00 X:78 Y:68 P:26 SP:F9 CYC:305 SL:98 +F975 60 RTS A:80 X:78 Y:68 P:A4 SP:F9 CYC:311 SL:98 +E33C FD 00 06 SBC $0600,X @ 0678 = 00 A:80 X:78 Y:68 P:A4 SP:FB CYC:329 SL:98 +E33F 20 76 F9 JSR $F976 A:7F X:78 Y:68 P:65 SP:FB CYC: 0 SL:99 +F976 90 05 BCC $F97D A:7F X:78 Y:68 P:65 SP:F9 CYC: 18 SL:99 +F978 C9 7F CMP #$7F A:7F X:78 Y:68 P:65 SP:F9 CYC: 24 SL:99 +F97A D0 01 BNE $F97D A:7F X:78 Y:68 P:67 SP:F9 CYC: 30 SL:99 +F97C 60 RTS A:7F X:78 Y:68 P:67 SP:F9 CYC: 36 SL:99 +E342 C8 INY A:7F X:78 Y:68 P:67 SP:FB CYC: 54 SL:99 +E343 A9 7F LDA #$7F A:7F X:78 Y:69 P:65 SP:FB CYC: 60 SL:99 +E345 8D 78 06 STA $0678 = 00 A:7F X:78 Y:69 P:65 SP:FB CYC: 66 SL:99 +E348 20 80 F9 JSR $F980 A:7F X:78 Y:69 P:65 SP:FB CYC: 78 SL:99 +F980 38 SEC A:7F X:78 Y:69 P:65 SP:F9 CYC: 96 SL:99 +F981 A9 81 LDA #$81 A:7F X:78 Y:69 P:65 SP:F9 CYC:102 SL:99 +F983 60 RTS A:81 X:78 Y:69 P:E5 SP:F9 CYC:108 SL:99 +E34B FD 00 06 SBC $0600,X @ 0678 = 7F A:81 X:78 Y:69 P:E5 SP:FB CYC:126 SL:99 +E34E 20 84 F9 JSR $F984 A:02 X:78 Y:69 P:65 SP:FB CYC:138 SL:99 +F984 50 07 BVC $F98D A:02 X:78 Y:69 P:65 SP:F9 CYC:156 SL:99 +F986 90 05 BCC $F98D A:02 X:78 Y:69 P:65 SP:F9 CYC:162 SL:99 +F988 C9 02 CMP #$02 A:02 X:78 Y:69 P:65 SP:F9 CYC:168 SL:99 +F98A D0 01 BNE $F98D A:02 X:78 Y:69 P:67 SP:F9 CYC:174 SL:99 +F98C 60 RTS A:02 X:78 Y:69 P:67 SP:F9 CYC:180 SL:99 +E351 A9 AA LDA #$AA A:02 X:78 Y:69 P:67 SP:FB CYC:198 SL:99 +E353 8D 33 06 STA $0633 = AA A:AA X:78 Y:69 P:E5 SP:FB CYC:204 SL:99 +E356 A9 BB LDA #$BB A:AA X:78 Y:69 P:E5 SP:FB CYC:216 SL:99 +E358 8D 89 06 STA $0689 = BB A:BB X:78 Y:69 P:E5 SP:FB CYC:222 SL:99 +E35B A2 00 LDX #$00 A:BB X:78 Y:69 P:E5 SP:FB CYC:234 SL:99 +E35D A0 66 LDY #$66 A:BB X:00 Y:69 P:67 SP:FB CYC:240 SL:99 +E35F 24 01 BIT $01 = FF A:BB X:00 Y:66 P:65 SP:FB CYC:246 SL:99 +E361 38 SEC A:BB X:00 Y:66 P:E5 SP:FB CYC:255 SL:99 +E362 A9 00 LDA #$00 A:BB X:00 Y:66 P:E5 SP:FB CYC:261 SL:99 +E364 BD 33 06 LDA $0633,X @ 0633 = AA A:00 X:00 Y:66 P:67 SP:FB CYC:267 SL:99 +E367 10 12 BPL $E37B A:AA X:00 Y:66 P:E5 SP:FB CYC:279 SL:99 +E369 F0 10 BEQ $E37B A:AA X:00 Y:66 P:E5 SP:FB CYC:285 SL:99 +E36B 50 0E BVC $E37B A:AA X:00 Y:66 P:E5 SP:FB CYC:291 SL:99 +E36D 90 0C BCC $E37B A:AA X:00 Y:66 P:E5 SP:FB CYC:297 SL:99 +E36F C0 66 CPY #$66 A:AA X:00 Y:66 P:E5 SP:FB CYC:303 SL:99 +E371 D0 08 BNE $E37B A:AA X:00 Y:66 P:67 SP:FB CYC:309 SL:99 +E373 E0 00 CPX #$00 A:AA X:00 Y:66 P:67 SP:FB CYC:315 SL:99 +E375 D0 04 BNE $E37B A:AA X:00 Y:66 P:67 SP:FB CYC:321 SL:99 +E377 C9 AA CMP #$AA A:AA X:00 Y:66 P:67 SP:FB CYC:327 SL:99 +E379 F0 04 BEQ $E37F A:AA X:00 Y:66 P:67 SP:FB CYC:333 SL:99 +E37F A2 8A LDX #$8A A:AA X:00 Y:66 P:67 SP:FB CYC: 1 SL:100 +E381 A0 66 LDY #$66 A:AA X:8A Y:66 P:E5 SP:FB CYC: 7 SL:100 +E383 B8 CLV A:AA X:8A Y:66 P:65 SP:FB CYC: 13 SL:100 +E384 18 CLC A:AA X:8A Y:66 P:25 SP:FB CYC: 19 SL:100 +E385 A9 00 LDA #$00 A:AA X:8A Y:66 P:24 SP:FB CYC: 25 SL:100 +E387 BD FF 05 LDA $05FF,X @ 0689 = BB A:00 X:8A Y:66 P:26 SP:FB CYC: 31 SL:100 +E38A 10 12 BPL $E39E A:BB X:8A Y:66 P:A4 SP:FB CYC: 46 SL:100 +E38C F0 10 BEQ $E39E A:BB X:8A Y:66 P:A4 SP:FB CYC: 52 SL:100 +E38E 70 0E BVS $E39E A:BB X:8A Y:66 P:A4 SP:FB CYC: 58 SL:100 +E390 B0 0C BCS $E39E A:BB X:8A Y:66 P:A4 SP:FB CYC: 64 SL:100 +E392 C9 BB CMP #$BB A:BB X:8A Y:66 P:A4 SP:FB CYC: 70 SL:100 +E394 D0 08 BNE $E39E A:BB X:8A Y:66 P:27 SP:FB CYC: 76 SL:100 +E396 C0 66 CPY #$66 A:BB X:8A Y:66 P:27 SP:FB CYC: 82 SL:100 +E398 D0 04 BNE $E39E A:BB X:8A Y:66 P:27 SP:FB CYC: 88 SL:100 +E39A E0 8A CPX #$8A A:BB X:8A Y:66 P:27 SP:FB CYC: 94 SL:100 +E39C F0 04 BEQ $E3A2 A:BB X:8A Y:66 P:27 SP:FB CYC:100 SL:100 +E3A2 24 01 BIT $01 = FF A:BB X:8A Y:66 P:27 SP:FB CYC:109 SL:100 +E3A4 38 SEC A:BB X:8A Y:66 P:E5 SP:FB CYC:118 SL:100 +E3A5 A9 44 LDA #$44 A:BB X:8A Y:66 P:E5 SP:FB CYC:124 SL:100 +E3A7 A2 00 LDX #$00 A:44 X:8A Y:66 P:65 SP:FB CYC:130 SL:100 +E3A9 9D 33 06 STA $0633,X @ 0633 = AA A:44 X:00 Y:66 P:67 SP:FB CYC:136 SL:100 +E3AC AD 33 06 LDA $0633 = 44 A:44 X:00 Y:66 P:67 SP:FB CYC:151 SL:100 +E3AF 90 1A BCC $E3CB A:44 X:00 Y:66 P:65 SP:FB CYC:163 SL:100 +E3B1 C9 44 CMP #$44 A:44 X:00 Y:66 P:65 SP:FB CYC:169 SL:100 +E3B3 D0 16 BNE $E3CB A:44 X:00 Y:66 P:67 SP:FB CYC:175 SL:100 +E3B5 50 14 BVC $E3CB A:44 X:00 Y:66 P:67 SP:FB CYC:181 SL:100 +E3B7 18 CLC A:44 X:00 Y:66 P:67 SP:FB CYC:187 SL:100 +E3B8 B8 CLV A:44 X:00 Y:66 P:66 SP:FB CYC:193 SL:100 +E3B9 A9 99 LDA #$99 A:44 X:00 Y:66 P:26 SP:FB CYC:199 SL:100 +E3BB A2 80 LDX #$80 A:99 X:00 Y:66 P:A4 SP:FB CYC:205 SL:100 +E3BD 9D 85 05 STA $0585,X @ 0605 = 00 A:99 X:80 Y:66 P:A4 SP:FB CYC:211 SL:100 +E3C0 AD 05 06 LDA $0605 = 99 A:99 X:80 Y:66 P:A4 SP:FB CYC:226 SL:100 +E3C3 B0 06 BCS $E3CB A:99 X:80 Y:66 P:A4 SP:FB CYC:238 SL:100 +E3C5 C9 99 CMP #$99 A:99 X:80 Y:66 P:A4 SP:FB CYC:244 SL:100 +E3C7 D0 02 BNE $E3CB A:99 X:80 Y:66 P:27 SP:FB CYC:250 SL:100 +E3C9 50 04 BVC $E3CF A:99 X:80 Y:66 P:27 SP:FB CYC:256 SL:100 +E3CF A0 6D LDY #$6D A:99 X:80 Y:66 P:27 SP:FB CYC:265 SL:100 +E3D1 A2 6D LDX #$6D A:99 X:80 Y:6D P:25 SP:FB CYC:271 SL:100 +E3D3 20 90 F9 JSR $F990 A:99 X:6D Y:6D P:25 SP:FB CYC:277 SL:100 +F990 A2 55 LDX #$55 A:99 X:6D Y:6D P:25 SP:F9 CYC:295 SL:100 +F992 A9 FF LDA #$FF A:99 X:55 Y:6D P:25 SP:F9 CYC:301 SL:100 +F994 85 01 STA $01 = FF A:FF X:55 Y:6D P:A5 SP:F9 CYC:307 SL:100 +F996 EA NOP A:FF X:55 Y:6D P:A5 SP:F9 CYC:316 SL:100 +F997 24 01 BIT $01 = FF A:FF X:55 Y:6D P:A5 SP:F9 CYC:322 SL:100 +F999 38 SEC A:FF X:55 Y:6D P:E5 SP:F9 CYC:331 SL:100 +F99A A9 01 LDA #$01 A:FF X:55 Y:6D P:E5 SP:F9 CYC:337 SL:100 +F99C 60 RTS A:01 X:55 Y:6D P:65 SP:F9 CYC: 2 SL:101 +E3D6 9D 00 06 STA $0600,X @ 0655 = 00 A:01 X:55 Y:6D P:65 SP:FB CYC: 20 SL:101 +E3D9 5E 00 06 LSR $0600,X @ 0655 = 01 A:01 X:55 Y:6D P:65 SP:FB CYC: 35 SL:101 +E3DC BD 00 06 LDA $0600,X @ 0655 = 00 A:01 X:55 Y:6D P:67 SP:FB CYC: 56 SL:101 +E3DF 20 9D F9 JSR $F99D A:00 X:55 Y:6D P:67 SP:FB CYC: 68 SL:101 +F99D 90 1B BCC $F9BA A:00 X:55 Y:6D P:67 SP:F9 CYC: 86 SL:101 +F99F D0 19 BNE $F9BA A:00 X:55 Y:6D P:67 SP:F9 CYC: 92 SL:101 +F9A1 30 17 BMI $F9BA A:00 X:55 Y:6D P:67 SP:F9 CYC: 98 SL:101 +F9A3 50 15 BVC $F9BA A:00 X:55 Y:6D P:67 SP:F9 CYC:104 SL:101 +F9A5 C9 00 CMP #$00 A:00 X:55 Y:6D P:67 SP:F9 CYC:110 SL:101 +F9A7 D0 11 BNE $F9BA A:00 X:55 Y:6D P:67 SP:F9 CYC:116 SL:101 +F9A9 B8 CLV A:00 X:55 Y:6D P:67 SP:F9 CYC:122 SL:101 +F9AA A9 AA LDA #$AA A:00 X:55 Y:6D P:27 SP:F9 CYC:128 SL:101 +F9AC 60 RTS A:AA X:55 Y:6D P:A5 SP:F9 CYC:134 SL:101 +E3E2 C8 INY A:AA X:55 Y:6D P:A5 SP:FB CYC:152 SL:101 +E3E3 9D 00 06 STA $0600,X @ 0655 = 00 A:AA X:55 Y:6E P:25 SP:FB CYC:158 SL:101 +E3E6 5E 00 06 LSR $0600,X @ 0655 = AA A:AA X:55 Y:6E P:25 SP:FB CYC:173 SL:101 +E3E9 BD 00 06 LDA $0600,X @ 0655 = 55 A:AA X:55 Y:6E P:24 SP:FB CYC:194 SL:101 +E3EC 20 AD F9 JSR $F9AD A:55 X:55 Y:6E P:24 SP:FB CYC:206 SL:101 +F9AD B0 0B BCS $F9BA A:55 X:55 Y:6E P:24 SP:F9 CYC:224 SL:101 +F9AF F0 09 BEQ $F9BA A:55 X:55 Y:6E P:24 SP:F9 CYC:230 SL:101 +F9B1 30 07 BMI $F9BA A:55 X:55 Y:6E P:24 SP:F9 CYC:236 SL:101 +F9B3 70 05 BVS $F9BA A:55 X:55 Y:6E P:24 SP:F9 CYC:242 SL:101 +F9B5 C9 55 CMP #$55 A:55 X:55 Y:6E P:24 SP:F9 CYC:248 SL:101 +F9B7 D0 01 BNE $F9BA A:55 X:55 Y:6E P:27 SP:F9 CYC:254 SL:101 +F9B9 60 RTS A:55 X:55 Y:6E P:27 SP:F9 CYC:260 SL:101 +E3EF C8 INY A:55 X:55 Y:6E P:27 SP:FB CYC:278 SL:101 +E3F0 20 BD F9 JSR $F9BD A:55 X:55 Y:6F P:25 SP:FB CYC:284 SL:101 +F9BD 24 01 BIT $01 = FF A:55 X:55 Y:6F P:25 SP:F9 CYC:302 SL:101 +F9BF 38 SEC A:55 X:55 Y:6F P:E5 SP:F9 CYC:311 SL:101 +F9C0 A9 80 LDA #$80 A:55 X:55 Y:6F P:E5 SP:F9 CYC:317 SL:101 +F9C2 60 RTS A:80 X:55 Y:6F P:E5 SP:F9 CYC:323 SL:101 +E3F3 9D 00 06 STA $0600,X @ 0655 = 55 A:80 X:55 Y:6F P:E5 SP:FB CYC: 0 SL:102 +E3F6 1E 00 06 ASL $0600,X @ 0655 = 80 A:80 X:55 Y:6F P:E5 SP:FB CYC: 15 SL:102 +E3F9 BD 00 06 LDA $0600,X @ 0655 = 00 A:80 X:55 Y:6F P:67 SP:FB CYC: 36 SL:102 +E3FC 20 C3 F9 JSR $F9C3 A:00 X:55 Y:6F P:67 SP:FB CYC: 48 SL:102 +F9C3 90 1C BCC $F9E1 A:00 X:55 Y:6F P:67 SP:F9 CYC: 66 SL:102 +F9C5 D0 1A BNE $F9E1 A:00 X:55 Y:6F P:67 SP:F9 CYC: 72 SL:102 +F9C7 30 18 BMI $F9E1 A:00 X:55 Y:6F P:67 SP:F9 CYC: 78 SL:102 +F9C9 50 16 BVC $F9E1 A:00 X:55 Y:6F P:67 SP:F9 CYC: 84 SL:102 +F9CB C9 00 CMP #$00 A:00 X:55 Y:6F P:67 SP:F9 CYC: 90 SL:102 +F9CD D0 12 BNE $F9E1 A:00 X:55 Y:6F P:67 SP:F9 CYC: 96 SL:102 +F9CF B8 CLV A:00 X:55 Y:6F P:67 SP:F9 CYC:102 SL:102 +F9D0 A9 55 LDA #$55 A:00 X:55 Y:6F P:27 SP:F9 CYC:108 SL:102 +F9D2 38 SEC A:55 X:55 Y:6F P:25 SP:F9 CYC:114 SL:102 +F9D3 60 RTS A:55 X:55 Y:6F P:25 SP:F9 CYC:120 SL:102 +E3FF C8 INY A:55 X:55 Y:6F P:25 SP:FB CYC:138 SL:102 +E400 9D 00 06 STA $0600,X @ 0655 = 00 A:55 X:55 Y:70 P:25 SP:FB CYC:144 SL:102 +E403 1E 00 06 ASL $0600,X @ 0655 = 55 A:55 X:55 Y:70 P:25 SP:FB CYC:159 SL:102 +E406 BD 00 06 LDA $0600,X @ 0655 = AA A:55 X:55 Y:70 P:A4 SP:FB CYC:180 SL:102 +E409 20 D4 F9 JSR $F9D4 A:AA X:55 Y:70 P:A4 SP:FB CYC:192 SL:102 +F9D4 B0 0B BCS $F9E1 A:AA X:55 Y:70 P:A4 SP:F9 CYC:210 SL:102 +F9D6 F0 09 BEQ $F9E1 A:AA X:55 Y:70 P:A4 SP:F9 CYC:216 SL:102 +F9D8 10 07 BPL $F9E1 A:AA X:55 Y:70 P:A4 SP:F9 CYC:222 SL:102 +F9DA 70 05 BVS $F9E1 A:AA X:55 Y:70 P:A4 SP:F9 CYC:228 SL:102 +F9DC C9 AA CMP #$AA A:AA X:55 Y:70 P:A4 SP:F9 CYC:234 SL:102 +F9DE D0 01 BNE $F9E1 A:AA X:55 Y:70 P:27 SP:F9 CYC:240 SL:102 +F9E0 60 RTS A:AA X:55 Y:70 P:27 SP:F9 CYC:246 SL:102 +E40C C8 INY A:AA X:55 Y:70 P:27 SP:FB CYC:264 SL:102 +E40D 20 E4 F9 JSR $F9E4 A:AA X:55 Y:71 P:25 SP:FB CYC:270 SL:102 +F9E4 24 01 BIT $01 = FF A:AA X:55 Y:71 P:25 SP:F9 CYC:288 SL:102 +F9E6 38 SEC A:AA X:55 Y:71 P:E5 SP:F9 CYC:297 SL:102 +F9E7 A9 01 LDA #$01 A:AA X:55 Y:71 P:E5 SP:F9 CYC:303 SL:102 +F9E9 60 RTS A:01 X:55 Y:71 P:65 SP:F9 CYC:309 SL:102 +E410 9D 00 06 STA $0600,X @ 0655 = AA A:01 X:55 Y:71 P:65 SP:FB CYC:327 SL:102 +E413 7E 00 06 ROR $0600,X @ 0655 = 01 A:01 X:55 Y:71 P:65 SP:FB CYC: 1 SL:103 +E416 BD 00 06 LDA $0600,X @ 0655 = 80 A:01 X:55 Y:71 P:E5 SP:FB CYC: 22 SL:103 +E419 20 EA F9 JSR $F9EA A:80 X:55 Y:71 P:E5 SP:FB CYC: 34 SL:103 +F9EA 90 1C BCC $FA08 A:80 X:55 Y:71 P:E5 SP:F9 CYC: 52 SL:103 +F9EC F0 1A BEQ $FA08 A:80 X:55 Y:71 P:E5 SP:F9 CYC: 58 SL:103 +F9EE 10 18 BPL $FA08 A:80 X:55 Y:71 P:E5 SP:F9 CYC: 64 SL:103 +F9F0 50 16 BVC $FA08 A:80 X:55 Y:71 P:E5 SP:F9 CYC: 70 SL:103 +F9F2 C9 80 CMP #$80 A:80 X:55 Y:71 P:E5 SP:F9 CYC: 76 SL:103 +F9F4 D0 12 BNE $FA08 A:80 X:55 Y:71 P:67 SP:F9 CYC: 82 SL:103 +F9F6 B8 CLV A:80 X:55 Y:71 P:67 SP:F9 CYC: 88 SL:103 +F9F7 18 CLC A:80 X:55 Y:71 P:27 SP:F9 CYC: 94 SL:103 +F9F8 A9 55 LDA #$55 A:80 X:55 Y:71 P:26 SP:F9 CYC:100 SL:103 +F9FA 60 RTS A:55 X:55 Y:71 P:24 SP:F9 CYC:106 SL:103 +E41C C8 INY A:55 X:55 Y:71 P:24 SP:FB CYC:124 SL:103 +E41D 9D 00 06 STA $0600,X @ 0655 = 80 A:55 X:55 Y:72 P:24 SP:FB CYC:130 SL:103 +E420 7E 00 06 ROR $0600,X @ 0655 = 55 A:55 X:55 Y:72 P:24 SP:FB CYC:145 SL:103 +E423 BD 00 06 LDA $0600,X @ 0655 = 2A A:55 X:55 Y:72 P:25 SP:FB CYC:166 SL:103 +E426 20 FB F9 JSR $F9FB A:2A X:55 Y:72 P:25 SP:FB CYC:178 SL:103 +F9FB 90 0B BCC $FA08 A:2A X:55 Y:72 P:25 SP:F9 CYC:196 SL:103 +F9FD F0 09 BEQ $FA08 A:2A X:55 Y:72 P:25 SP:F9 CYC:202 SL:103 +F9FF 30 07 BMI $FA08 A:2A X:55 Y:72 P:25 SP:F9 CYC:208 SL:103 +FA01 70 05 BVS $FA08 A:2A X:55 Y:72 P:25 SP:F9 CYC:214 SL:103 +FA03 C9 2A CMP #$2A A:2A X:55 Y:72 P:25 SP:F9 CYC:220 SL:103 +FA05 D0 01 BNE $FA08 A:2A X:55 Y:72 P:27 SP:F9 CYC:226 SL:103 +FA07 60 RTS A:2A X:55 Y:72 P:27 SP:F9 CYC:232 SL:103 +E429 C8 INY A:2A X:55 Y:72 P:27 SP:FB CYC:250 SL:103 +E42A 20 0A FA JSR $FA0A A:2A X:55 Y:73 P:25 SP:FB CYC:256 SL:103 +FA0A 24 01 BIT $01 = FF A:2A X:55 Y:73 P:25 SP:F9 CYC:274 SL:103 +FA0C 38 SEC A:2A X:55 Y:73 P:E5 SP:F9 CYC:283 SL:103 +FA0D A9 80 LDA #$80 A:2A X:55 Y:73 P:E5 SP:F9 CYC:289 SL:103 +FA0F 60 RTS A:80 X:55 Y:73 P:E5 SP:F9 CYC:295 SL:103 +E42D 9D 00 06 STA $0600,X @ 0655 = 2A A:80 X:55 Y:73 P:E5 SP:FB CYC:313 SL:103 +E430 3E 00 06 ROL $0600,X @ 0655 = 80 A:80 X:55 Y:73 P:E5 SP:FB CYC:328 SL:103 +E433 BD 00 06 LDA $0600,X @ 0655 = 01 A:80 X:55 Y:73 P:65 SP:FB CYC: 8 SL:104 +E436 20 10 FA JSR $FA10 A:01 X:55 Y:73 P:65 SP:FB CYC: 20 SL:104 +FA10 90 1C BCC $FA2E A:01 X:55 Y:73 P:65 SP:F9 CYC: 38 SL:104 +FA12 F0 1A BEQ $FA2E A:01 X:55 Y:73 P:65 SP:F9 CYC: 44 SL:104 +FA14 30 18 BMI $FA2E A:01 X:55 Y:73 P:65 SP:F9 CYC: 50 SL:104 +FA16 50 16 BVC $FA2E A:01 X:55 Y:73 P:65 SP:F9 CYC: 56 SL:104 +FA18 C9 01 CMP #$01 A:01 X:55 Y:73 P:65 SP:F9 CYC: 62 SL:104 +FA1A D0 12 BNE $FA2E A:01 X:55 Y:73 P:67 SP:F9 CYC: 68 SL:104 +FA1C B8 CLV A:01 X:55 Y:73 P:67 SP:F9 CYC: 74 SL:104 +FA1D 18 CLC A:01 X:55 Y:73 P:27 SP:F9 CYC: 80 SL:104 +FA1E A9 55 LDA #$55 A:01 X:55 Y:73 P:26 SP:F9 CYC: 86 SL:104 +FA20 60 RTS A:55 X:55 Y:73 P:24 SP:F9 CYC: 92 SL:104 +E439 C8 INY A:55 X:55 Y:73 P:24 SP:FB CYC:110 SL:104 +E43A 9D 00 06 STA $0600,X @ 0655 = 01 A:55 X:55 Y:74 P:24 SP:FB CYC:116 SL:104 +E43D 3E 00 06 ROL $0600,X @ 0655 = 55 A:55 X:55 Y:74 P:24 SP:FB CYC:131 SL:104 +E440 BD 00 06 LDA $0600,X @ 0655 = AA A:55 X:55 Y:74 P:A4 SP:FB CYC:152 SL:104 +E443 20 21 FA JSR $FA21 A:AA X:55 Y:74 P:A4 SP:FB CYC:164 SL:104 +FA21 B0 0B BCS $FA2E A:AA X:55 Y:74 P:A4 SP:F9 CYC:182 SL:104 +FA23 F0 09 BEQ $FA2E A:AA X:55 Y:74 P:A4 SP:F9 CYC:188 SL:104 +FA25 10 07 BPL $FA2E A:AA X:55 Y:74 P:A4 SP:F9 CYC:194 SL:104 +FA27 70 05 BVS $FA2E A:AA X:55 Y:74 P:A4 SP:F9 CYC:200 SL:104 +FA29 C9 AA CMP #$AA A:AA X:55 Y:74 P:A4 SP:F9 CYC:206 SL:104 +FA2B D0 01 BNE $FA2E A:AA X:55 Y:74 P:27 SP:F9 CYC:212 SL:104 +FA2D 60 RTS A:AA X:55 Y:74 P:27 SP:F9 CYC:218 SL:104 +E446 A9 FF LDA #$FF A:AA X:55 Y:74 P:27 SP:FB CYC:236 SL:104 +E448 9D 00 06 STA $0600,X @ 0655 = AA A:FF X:55 Y:74 P:A5 SP:FB CYC:242 SL:104 +E44B 85 01 STA $01 = FF A:FF X:55 Y:74 P:A5 SP:FB CYC:257 SL:104 +E44D 24 01 BIT $01 = FF A:FF X:55 Y:74 P:A5 SP:FB CYC:266 SL:104 +E44F 38 SEC A:FF X:55 Y:74 P:E5 SP:FB CYC:275 SL:104 +E450 FE 00 06 INC $0600,X @ 0655 = FF A:FF X:55 Y:74 P:E5 SP:FB CYC:281 SL:104 +E453 D0 0D BNE $E462 A:FF X:55 Y:74 P:67 SP:FB CYC:302 SL:104 +E455 30 0B BMI $E462 A:FF X:55 Y:74 P:67 SP:FB CYC:308 SL:104 +E457 50 09 BVC $E462 A:FF X:55 Y:74 P:67 SP:FB CYC:314 SL:104 +E459 90 07 BCC $E462 A:FF X:55 Y:74 P:67 SP:FB CYC:320 SL:104 +E45B BD 00 06 LDA $0600,X @ 0655 = 00 A:FF X:55 Y:74 P:67 SP:FB CYC:326 SL:104 +E45E C9 00 CMP #$00 A:00 X:55 Y:74 P:67 SP:FB CYC:338 SL:104 +E460 F0 04 BEQ $E466 A:00 X:55 Y:74 P:67 SP:FB CYC: 3 SL:105 +E466 A9 7F LDA #$7F A:00 X:55 Y:74 P:67 SP:FB CYC: 12 SL:105 +E468 9D 00 06 STA $0600,X @ 0655 = 00 A:7F X:55 Y:74 P:65 SP:FB CYC: 18 SL:105 +E46B B8 CLV A:7F X:55 Y:74 P:65 SP:FB CYC: 33 SL:105 +E46C 18 CLC A:7F X:55 Y:74 P:25 SP:FB CYC: 39 SL:105 +E46D FE 00 06 INC $0600,X @ 0655 = 7F A:7F X:55 Y:74 P:24 SP:FB CYC: 45 SL:105 +E470 F0 0D BEQ $E47F A:7F X:55 Y:74 P:A4 SP:FB CYC: 66 SL:105 +E472 10 0B BPL $E47F A:7F X:55 Y:74 P:A4 SP:FB CYC: 72 SL:105 +E474 70 09 BVS $E47F A:7F X:55 Y:74 P:A4 SP:FB CYC: 78 SL:105 +E476 B0 07 BCS $E47F A:7F X:55 Y:74 P:A4 SP:FB CYC: 84 SL:105 +E478 BD 00 06 LDA $0600,X @ 0655 = 80 A:7F X:55 Y:74 P:A4 SP:FB CYC: 90 SL:105 +E47B C9 80 CMP #$80 A:80 X:55 Y:74 P:A4 SP:FB CYC:102 SL:105 +E47D F0 04 BEQ $E483 A:80 X:55 Y:74 P:27 SP:FB CYC:108 SL:105 +E483 A9 00 LDA #$00 A:80 X:55 Y:74 P:27 SP:FB CYC:117 SL:105 +E485 9D 00 06 STA $0600,X @ 0655 = 80 A:00 X:55 Y:74 P:27 SP:FB CYC:123 SL:105 +E488 24 01 BIT $01 = FF A:00 X:55 Y:74 P:27 SP:FB CYC:138 SL:105 +E48A 38 SEC A:00 X:55 Y:74 P:E7 SP:FB CYC:147 SL:105 +E48B DE 00 06 DEC $0600,X @ 0655 = 00 A:00 X:55 Y:74 P:E7 SP:FB CYC:153 SL:105 +E48E F0 0D BEQ $E49D A:00 X:55 Y:74 P:E5 SP:FB CYC:174 SL:105 +E490 10 0B BPL $E49D A:00 X:55 Y:74 P:E5 SP:FB CYC:180 SL:105 +E492 50 09 BVC $E49D A:00 X:55 Y:74 P:E5 SP:FB CYC:186 SL:105 +E494 90 07 BCC $E49D A:00 X:55 Y:74 P:E5 SP:FB CYC:192 SL:105 +E496 BD 00 06 LDA $0600,X @ 0655 = FF A:00 X:55 Y:74 P:E5 SP:FB CYC:198 SL:105 +E499 C9 FF CMP #$FF A:FF X:55 Y:74 P:E5 SP:FB CYC:210 SL:105 +E49B F0 04 BEQ $E4A1 A:FF X:55 Y:74 P:67 SP:FB CYC:216 SL:105 +E4A1 A9 80 LDA #$80 A:FF X:55 Y:74 P:67 SP:FB CYC:225 SL:105 +E4A3 9D 00 06 STA $0600,X @ 0655 = FF A:80 X:55 Y:74 P:E5 SP:FB CYC:231 SL:105 +E4A6 B8 CLV A:80 X:55 Y:74 P:E5 SP:FB CYC:246 SL:105 +E4A7 18 CLC A:80 X:55 Y:74 P:A5 SP:FB CYC:252 SL:105 +E4A8 DE 00 06 DEC $0600,X @ 0655 = 80 A:80 X:55 Y:74 P:A4 SP:FB CYC:258 SL:105 +E4AB F0 0D BEQ $E4BA A:80 X:55 Y:74 P:24 SP:FB CYC:279 SL:105 +E4AD 30 0B BMI $E4BA A:80 X:55 Y:74 P:24 SP:FB CYC:285 SL:105 +E4AF 70 09 BVS $E4BA A:80 X:55 Y:74 P:24 SP:FB CYC:291 SL:105 +E4B1 B0 07 BCS $E4BA A:80 X:55 Y:74 P:24 SP:FB CYC:297 SL:105 +E4B3 BD 00 06 LDA $0600,X @ 0655 = 7F A:80 X:55 Y:74 P:24 SP:FB CYC:303 SL:105 +E4B6 C9 7F CMP #$7F A:7F X:55 Y:74 P:24 SP:FB CYC:315 SL:105 +E4B8 F0 04 BEQ $E4BE A:7F X:55 Y:74 P:27 SP:FB CYC:321 SL:105 +E4BE A9 01 LDA #$01 A:7F X:55 Y:74 P:27 SP:FB CYC:330 SL:105 +E4C0 9D 00 06 STA $0600,X @ 0655 = 7F A:01 X:55 Y:74 P:25 SP:FB CYC:336 SL:105 +E4C3 DE 00 06 DEC $0600,X @ 0655 = 01 A:01 X:55 Y:74 P:25 SP:FB CYC: 10 SL:106 +E4C6 F0 04 BEQ $E4CC A:01 X:55 Y:74 P:27 SP:FB CYC: 31 SL:106 +E4CC A9 33 LDA #$33 A:01 X:55 Y:74 P:27 SP:FB CYC: 40 SL:106 +E4CE 8D 78 06 STA $0678 = 7F A:33 X:55 Y:74 P:25 SP:FB CYC: 46 SL:106 +E4D1 A9 44 LDA #$44 A:33 X:55 Y:74 P:25 SP:FB CYC: 58 SL:106 +E4D3 A0 78 LDY #$78 A:44 X:55 Y:74 P:25 SP:FB CYC: 64 SL:106 +E4D5 A2 00 LDX #$00 A:44 X:55 Y:78 P:25 SP:FB CYC: 70 SL:106 +E4D7 38 SEC A:44 X:00 Y:78 P:27 SP:FB CYC: 76 SL:106 +E4D8 24 01 BIT $01 = FF A:44 X:00 Y:78 P:27 SP:FB CYC: 82 SL:106 +E4DA BE 00 06 LDX $0600,Y @ 0678 = 33 A:44 X:00 Y:78 P:E5 SP:FB CYC: 91 SL:106 +E4DD 90 12 BCC $E4F1 A:44 X:33 Y:78 P:65 SP:FB CYC:103 SL:106 +E4DF 50 10 BVC $E4F1 A:44 X:33 Y:78 P:65 SP:FB CYC:109 SL:106 +E4E1 30 0E BMI $E4F1 A:44 X:33 Y:78 P:65 SP:FB CYC:115 SL:106 +E4E3 F0 0C BEQ $E4F1 A:44 X:33 Y:78 P:65 SP:FB CYC:121 SL:106 +E4E5 E0 33 CPX #$33 A:44 X:33 Y:78 P:65 SP:FB CYC:127 SL:106 +E4E7 D0 08 BNE $E4F1 A:44 X:33 Y:78 P:67 SP:FB CYC:133 SL:106 +E4E9 C0 78 CPY #$78 A:44 X:33 Y:78 P:67 SP:FB CYC:139 SL:106 +E4EB D0 04 BNE $E4F1 A:44 X:33 Y:78 P:67 SP:FB CYC:145 SL:106 +E4ED C9 44 CMP #$44 A:44 X:33 Y:78 P:67 SP:FB CYC:151 SL:106 +E4EF F0 04 BEQ $E4F5 A:44 X:33 Y:78 P:67 SP:FB CYC:157 SL:106 +E4F5 A9 97 LDA #$97 A:44 X:33 Y:78 P:67 SP:FB CYC:166 SL:106 +E4F7 8D 7F 06 STA $067F = 00 A:97 X:33 Y:78 P:E5 SP:FB CYC:172 SL:106 +E4FA A9 47 LDA #$47 A:97 X:33 Y:78 P:E5 SP:FB CYC:184 SL:106 +E4FC A0 FF LDY #$FF A:47 X:33 Y:78 P:65 SP:FB CYC:190 SL:106 +E4FE A2 00 LDX #$00 A:47 X:33 Y:FF P:E5 SP:FB CYC:196 SL:106 +E500 18 CLC A:47 X:00 Y:FF P:67 SP:FB CYC:202 SL:106 +E501 B8 CLV A:47 X:00 Y:FF P:66 SP:FB CYC:208 SL:106 +E502 BE 80 05 LDX $0580,Y @ 067F = 97 A:47 X:00 Y:FF P:26 SP:FB CYC:214 SL:106 +E505 B0 12 BCS $E519 A:47 X:97 Y:FF P:A4 SP:FB CYC:229 SL:106 +E507 70 10 BVS $E519 A:47 X:97 Y:FF P:A4 SP:FB CYC:235 SL:106 +E509 10 0E BPL $E519 A:47 X:97 Y:FF P:A4 SP:FB CYC:241 SL:106 +E50B F0 0C BEQ $E519 A:47 X:97 Y:FF P:A4 SP:FB CYC:247 SL:106 +E50D E0 97 CPX #$97 A:47 X:97 Y:FF P:A4 SP:FB CYC:253 SL:106 +E50F D0 08 BNE $E519 A:47 X:97 Y:FF P:27 SP:FB CYC:259 SL:106 +E511 C0 FF CPY #$FF A:47 X:97 Y:FF P:27 SP:FB CYC:265 SL:106 +E513 D0 04 BNE $E519 A:47 X:97 Y:FF P:27 SP:FB CYC:271 SL:106 +E515 C9 47 CMP #$47 A:47 X:97 Y:FF P:27 SP:FB CYC:277 SL:106 +E517 F0 04 BEQ $E51D A:47 X:97 Y:FF P:27 SP:FB CYC:283 SL:106 +E51D 60 RTS A:47 X:97 Y:FF P:27 SP:FB CYC:292 SL:106 +C62F 20 A3 C6 JSR $C6A3 A:47 X:97 Y:FF P:27 SP:FD CYC:310 SL:106 +C6A3 A0 4E LDY #$4E A:47 X:97 Y:FF P:27 SP:FB CYC:328 SL:106 +C6A5 A9 FF LDA #$FF A:47 X:97 Y:4E P:25 SP:FB CYC:334 SL:106 +C6A7 85 01 STA $01 = FF A:FF X:97 Y:4E P:A5 SP:FB CYC:340 SL:106 +C6A9 20 B0 C6 JSR $C6B0 A:FF X:97 Y:4E P:A5 SP:FB CYC: 8 SL:107 +C6B0 A9 FF LDA #$FF A:FF X:97 Y:4E P:A5 SP:F9 CYC: 26 SL:107 +C6B2 48 PHA A:FF X:97 Y:4E P:A5 SP:F9 CYC: 32 SL:107 +C6B3 A9 AA LDA #$AA A:FF X:97 Y:4E P:A5 SP:F8 CYC: 41 SL:107 +C6B5 D0 05 BNE $C6BC A:AA X:97 Y:4E P:A5 SP:F8 CYC: 47 SL:107 +C6BC 28 PLP A:AA X:97 Y:4E P:A5 SP:F8 CYC: 56 SL:107 +C6BD 04 A9 *NOP $A9 = 00 A:AA X:97 Y:4E P:EF SP:F9 CYC: 68 SL:107 +C6BF 44 A9 *NOP $A9 = 00 A:AA X:97 Y:4E P:EF SP:F9 CYC: 77 SL:107 +C6C1 64 A9 *NOP $A9 = 00 A:AA X:97 Y:4E P:EF SP:F9 CYC: 86 SL:107 +C6C3 EA NOP A:AA X:97 Y:4E P:EF SP:F9 CYC: 95 SL:107 +C6C4 EA NOP A:AA X:97 Y:4E P:EF SP:F9 CYC:101 SL:107 +C6C5 EA NOP A:AA X:97 Y:4E P:EF SP:F9 CYC:107 SL:107 +C6C6 EA NOP A:AA X:97 Y:4E P:EF SP:F9 CYC:113 SL:107 +C6C7 08 PHP A:AA X:97 Y:4E P:EF SP:F9 CYC:119 SL:107 +C6C8 48 PHA A:AA X:97 Y:4E P:EF SP:F8 CYC:128 SL:107 +C6C9 0C A9 A9 *NOP $A9A9 = A9 A:AA X:97 Y:4E P:EF SP:F7 CYC:137 SL:107 +C6CC EA NOP A:AA X:97 Y:4E P:EF SP:F7 CYC:149 SL:107 +C6CD EA NOP A:AA X:97 Y:4E P:EF SP:F7 CYC:155 SL:107 +C6CE EA NOP A:AA X:97 Y:4E P:EF SP:F7 CYC:161 SL:107 +C6CF EA NOP A:AA X:97 Y:4E P:EF SP:F7 CYC:167 SL:107 +C6D0 08 PHP A:AA X:97 Y:4E P:EF SP:F7 CYC:173 SL:107 +C6D1 48 PHA A:AA X:97 Y:4E P:EF SP:F6 CYC:182 SL:107 +C6D2 14 A9 *NOP $A9,X @ 40 = 00 A:AA X:97 Y:4E P:EF SP:F5 CYC:191 SL:107 +C6D4 34 A9 *NOP $A9,X @ 40 = 00 A:AA X:97 Y:4E P:EF SP:F5 CYC:203 SL:107 +C6D6 54 A9 *NOP $A9,X @ 40 = 00 A:AA X:97 Y:4E P:EF SP:F5 CYC:215 SL:107 +C6D8 74 A9 *NOP $A9,X @ 40 = 00 A:AA X:97 Y:4E P:EF SP:F5 CYC:227 SL:107 +C6DA D4 A9 *NOP $A9,X @ 40 = 00 A:AA X:97 Y:4E P:EF SP:F5 CYC:239 SL:107 +C6DC F4 A9 *NOP $A9,X @ 40 = 00 A:AA X:97 Y:4E P:EF SP:F5 CYC:251 SL:107 +C6DE EA NOP A:AA X:97 Y:4E P:EF SP:F5 CYC:263 SL:107 +C6DF EA NOP A:AA X:97 Y:4E P:EF SP:F5 CYC:269 SL:107 +C6E0 EA NOP A:AA X:97 Y:4E P:EF SP:F5 CYC:275 SL:107 +C6E1 EA NOP A:AA X:97 Y:4E P:EF SP:F5 CYC:281 SL:107 +C6E2 08 PHP A:AA X:97 Y:4E P:EF SP:F5 CYC:287 SL:107 +C6E3 48 PHA A:AA X:97 Y:4E P:EF SP:F4 CYC:296 SL:107 +C6E4 1A *NOP A:AA X:97 Y:4E P:EF SP:F3 CYC:305 SL:107 +C6E5 3A *NOP A:AA X:97 Y:4E P:EF SP:F3 CYC:311 SL:107 +C6E6 5A *NOP A:AA X:97 Y:4E P:EF SP:F3 CYC:317 SL:107 +C6E7 7A *NOP A:AA X:97 Y:4E P:EF SP:F3 CYC:323 SL:107 +C6E8 DA *NOP A:AA X:97 Y:4E P:EF SP:F3 CYC:329 SL:107 +C6E9 FA *NOP A:AA X:97 Y:4E P:EF SP:F3 CYC:335 SL:107 +C6EA 80 89 *NOP #$89 A:AA X:97 Y:4E P:EF SP:F3 CYC: 0 SL:108 +C6EC EA NOP A:AA X:97 Y:4E P:EF SP:F3 CYC: 6 SL:108 +C6ED EA NOP A:AA X:97 Y:4E P:EF SP:F3 CYC: 12 SL:108 +C6EE EA NOP A:AA X:97 Y:4E P:EF SP:F3 CYC: 18 SL:108 +C6EF EA NOP A:AA X:97 Y:4E P:EF SP:F3 CYC: 24 SL:108 +C6F0 08 PHP A:AA X:97 Y:4E P:EF SP:F3 CYC: 30 SL:108 +C6F1 48 PHA A:AA X:97 Y:4E P:EF SP:F2 CYC: 39 SL:108 +C6F2 1C A9 A9 *NOP $A9A9,X @ AA40 = 00 A:AA X:97 Y:4E P:EF SP:F1 CYC: 48 SL:108 +C6F5 3C A9 A9 *NOP $A9A9,X @ AA40 = 00 A:AA X:97 Y:4E P:EF SP:F1 CYC: 63 SL:108 +C6F8 5C A9 A9 *NOP $A9A9,X @ AA40 = 00 A:AA X:97 Y:4E P:EF SP:F1 CYC: 78 SL:108 +C6FB 7C A9 A9 *NOP $A9A9,X @ AA40 = 00 A:AA X:97 Y:4E P:EF SP:F1 CYC: 93 SL:108 +C6FE DC A9 A9 *NOP $A9A9,X @ AA40 = 00 A:AA X:97 Y:4E P:EF SP:F1 CYC:108 SL:108 +C701 FC A9 A9 *NOP $A9A9,X @ AA40 = 00 A:AA X:97 Y:4E P:EF SP:F1 CYC:123 SL:108 +C704 EA NOP A:AA X:97 Y:4E P:EF SP:F1 CYC:138 SL:108 +C705 EA NOP A:AA X:97 Y:4E P:EF SP:F1 CYC:144 SL:108 +C706 EA NOP A:AA X:97 Y:4E P:EF SP:F1 CYC:150 SL:108 +C707 EA NOP A:AA X:97 Y:4E P:EF SP:F1 CYC:156 SL:108 +C708 08 PHP A:AA X:97 Y:4E P:EF SP:F1 CYC:162 SL:108 +C709 48 PHA A:AA X:97 Y:4E P:EF SP:F0 CYC:171 SL:108 +C70A A2 05 LDX #$05 A:AA X:97 Y:4E P:EF SP:EF CYC:180 SL:108 +C70C 68 PLA A:AA X:05 Y:4E P:6D SP:EF CYC:186 SL:108 +C70D C9 55 CMP #$55 A:AA X:05 Y:4E P:ED SP:F0 CYC:198 SL:108 +C70F F0 0A BEQ $C71B A:AA X:05 Y:4E P:6D SP:F0 CYC:204 SL:108 +C711 C9 AA CMP #$AA A:AA X:05 Y:4E P:6D SP:F0 CYC:210 SL:108 +C713 F0 06 BEQ $C71B A:AA X:05 Y:4E P:6F SP:F0 CYC:216 SL:108 +C71B 68 PLA A:AA X:05 Y:4E P:6F SP:F0 CYC:225 SL:108 +C71C 29 CB AND #$CB A:FF X:05 Y:4E P:ED SP:F1 CYC:237 SL:108 +C71E C9 00 CMP #$00 A:CB X:05 Y:4E P:ED SP:F1 CYC:243 SL:108 +C720 F0 06 BEQ $C728 A:CB X:05 Y:4E P:ED SP:F1 CYC:249 SL:108 +C722 C9 CB CMP #$CB A:CB X:05 Y:4E P:ED SP:F1 CYC:255 SL:108 +C724 F0 02 BEQ $C728 A:CB X:05 Y:4E P:6F SP:F1 CYC:261 SL:108 +C728 C8 INY A:CB X:05 Y:4E P:6F SP:F1 CYC:270 SL:108 +C729 CA DEX A:CB X:05 Y:4F P:6D SP:F1 CYC:276 SL:108 +C72A D0 E0 BNE $C70C A:CB X:04 Y:4F P:6D SP:F1 CYC:282 SL:108 +C70C 68 PLA A:CB X:04 Y:4F P:6D SP:F1 CYC:291 SL:108 +C70D C9 55 CMP #$55 A:AA X:04 Y:4F P:ED SP:F2 CYC:303 SL:108 +C70F F0 0A BEQ $C71B A:AA X:04 Y:4F P:6D SP:F2 CYC:309 SL:108 +C711 C9 AA CMP #$AA A:AA X:04 Y:4F P:6D SP:F2 CYC:315 SL:108 +C713 F0 06 BEQ $C71B A:AA X:04 Y:4F P:6F SP:F2 CYC:321 SL:108 +C71B 68 PLA A:AA X:04 Y:4F P:6F SP:F2 CYC:330 SL:108 +C71C 29 CB AND #$CB A:FF X:04 Y:4F P:ED SP:F3 CYC: 1 SL:109 +C71E C9 00 CMP #$00 A:CB X:04 Y:4F P:ED SP:F3 CYC: 7 SL:109 +C720 F0 06 BEQ $C728 A:CB X:04 Y:4F P:ED SP:F3 CYC: 13 SL:109 +C722 C9 CB CMP #$CB A:CB X:04 Y:4F P:ED SP:F3 CYC: 19 SL:109 +C724 F0 02 BEQ $C728 A:CB X:04 Y:4F P:6F SP:F3 CYC: 25 SL:109 +C728 C8 INY A:CB X:04 Y:4F P:6F SP:F3 CYC: 34 SL:109 +C729 CA DEX A:CB X:04 Y:50 P:6D SP:F3 CYC: 40 SL:109 +C72A D0 E0 BNE $C70C A:CB X:03 Y:50 P:6D SP:F3 CYC: 46 SL:109 +C70C 68 PLA A:CB X:03 Y:50 P:6D SP:F3 CYC: 55 SL:109 +C70D C9 55 CMP #$55 A:AA X:03 Y:50 P:ED SP:F4 CYC: 67 SL:109 +C70F F0 0A BEQ $C71B A:AA X:03 Y:50 P:6D SP:F4 CYC: 73 SL:109 +C711 C9 AA CMP #$AA A:AA X:03 Y:50 P:6D SP:F4 CYC: 79 SL:109 +C713 F0 06 BEQ $C71B A:AA X:03 Y:50 P:6F SP:F4 CYC: 85 SL:109 +C71B 68 PLA A:AA X:03 Y:50 P:6F SP:F4 CYC: 94 SL:109 +C71C 29 CB AND #$CB A:FF X:03 Y:50 P:ED SP:F5 CYC:106 SL:109 +C71E C9 00 CMP #$00 A:CB X:03 Y:50 P:ED SP:F5 CYC:112 SL:109 +C720 F0 06 BEQ $C728 A:CB X:03 Y:50 P:ED SP:F5 CYC:118 SL:109 +C722 C9 CB CMP #$CB A:CB X:03 Y:50 P:ED SP:F5 CYC:124 SL:109 +C724 F0 02 BEQ $C728 A:CB X:03 Y:50 P:6F SP:F5 CYC:130 SL:109 +C728 C8 INY A:CB X:03 Y:50 P:6F SP:F5 CYC:139 SL:109 +C729 CA DEX A:CB X:03 Y:51 P:6D SP:F5 CYC:145 SL:109 +C72A D0 E0 BNE $C70C A:CB X:02 Y:51 P:6D SP:F5 CYC:151 SL:109 +C70C 68 PLA A:CB X:02 Y:51 P:6D SP:F5 CYC:160 SL:109 +C70D C9 55 CMP #$55 A:AA X:02 Y:51 P:ED SP:F6 CYC:172 SL:109 +C70F F0 0A BEQ $C71B A:AA X:02 Y:51 P:6D SP:F6 CYC:178 SL:109 +C711 C9 AA CMP #$AA A:AA X:02 Y:51 P:6D SP:F6 CYC:184 SL:109 +C713 F0 06 BEQ $C71B A:AA X:02 Y:51 P:6F SP:F6 CYC:190 SL:109 +C71B 68 PLA A:AA X:02 Y:51 P:6F SP:F6 CYC:199 SL:109 +C71C 29 CB AND #$CB A:FF X:02 Y:51 P:ED SP:F7 CYC:211 SL:109 +C71E C9 00 CMP #$00 A:CB X:02 Y:51 P:ED SP:F7 CYC:217 SL:109 +C720 F0 06 BEQ $C728 A:CB X:02 Y:51 P:ED SP:F7 CYC:223 SL:109 +C722 C9 CB CMP #$CB A:CB X:02 Y:51 P:ED SP:F7 CYC:229 SL:109 +C724 F0 02 BEQ $C728 A:CB X:02 Y:51 P:6F SP:F7 CYC:235 SL:109 +C728 C8 INY A:CB X:02 Y:51 P:6F SP:F7 CYC:244 SL:109 +C729 CA DEX A:CB X:02 Y:52 P:6D SP:F7 CYC:250 SL:109 +C72A D0 E0 BNE $C70C A:CB X:01 Y:52 P:6D SP:F7 CYC:256 SL:109 +C70C 68 PLA A:CB X:01 Y:52 P:6D SP:F7 CYC:265 SL:109 +C70D C9 55 CMP #$55 A:AA X:01 Y:52 P:ED SP:F8 CYC:277 SL:109 +C70F F0 0A BEQ $C71B A:AA X:01 Y:52 P:6D SP:F8 CYC:283 SL:109 +C711 C9 AA CMP #$AA A:AA X:01 Y:52 P:6D SP:F8 CYC:289 SL:109 +C713 F0 06 BEQ $C71B A:AA X:01 Y:52 P:6F SP:F8 CYC:295 SL:109 +C71B 68 PLA A:AA X:01 Y:52 P:6F SP:F8 CYC:304 SL:109 +C71C 29 CB AND #$CB A:FF X:01 Y:52 P:ED SP:F9 CYC:316 SL:109 +C71E C9 00 CMP #$00 A:CB X:01 Y:52 P:ED SP:F9 CYC:322 SL:109 +C720 F0 06 BEQ $C728 A:CB X:01 Y:52 P:ED SP:F9 CYC:328 SL:109 +C722 C9 CB CMP #$CB A:CB X:01 Y:52 P:ED SP:F9 CYC:334 SL:109 +C724 F0 02 BEQ $C728 A:CB X:01 Y:52 P:6F SP:F9 CYC:340 SL:109 +C728 C8 INY A:CB X:01 Y:52 P:6F SP:F9 CYC: 8 SL:110 +C729 CA DEX A:CB X:01 Y:53 P:6D SP:F9 CYC: 14 SL:110 +C72A D0 E0 BNE $C70C A:CB X:00 Y:53 P:6F SP:F9 CYC: 20 SL:110 +C72C 60 RTS A:CB X:00 Y:53 P:6F SP:F9 CYC: 26 SL:110 +C6AC 20 B7 C6 JSR $C6B7 A:CB X:00 Y:53 P:6F SP:FB CYC: 44 SL:110 +C6B7 A9 34 LDA #$34 A:CB X:00 Y:53 P:6F SP:F9 CYC: 62 SL:110 +C6B9 48 PHA A:34 X:00 Y:53 P:6D SP:F9 CYC: 68 SL:110 +C6BA A9 55 LDA #$55 A:34 X:00 Y:53 P:6D SP:F8 CYC: 77 SL:110 +C6BC 28 PLP A:55 X:00 Y:53 P:6D SP:F8 CYC: 83 SL:110 +C6BD 04 A9 *NOP $A9 = 00 A:55 X:00 Y:53 P:24 SP:F9 CYC: 95 SL:110 +C6BF 44 A9 *NOP $A9 = 00 A:55 X:00 Y:53 P:24 SP:F9 CYC:104 SL:110 +C6C1 64 A9 *NOP $A9 = 00 A:55 X:00 Y:53 P:24 SP:F9 CYC:113 SL:110 +C6C3 EA NOP A:55 X:00 Y:53 P:24 SP:F9 CYC:122 SL:110 +C6C4 EA NOP A:55 X:00 Y:53 P:24 SP:F9 CYC:128 SL:110 +C6C5 EA NOP A:55 X:00 Y:53 P:24 SP:F9 CYC:134 SL:110 +C6C6 EA NOP A:55 X:00 Y:53 P:24 SP:F9 CYC:140 SL:110 +C6C7 08 PHP A:55 X:00 Y:53 P:24 SP:F9 CYC:146 SL:110 +C6C8 48 PHA A:55 X:00 Y:53 P:24 SP:F8 CYC:155 SL:110 +C6C9 0C A9 A9 *NOP $A9A9 = A9 A:55 X:00 Y:53 P:24 SP:F7 CYC:164 SL:110 +C6CC EA NOP A:55 X:00 Y:53 P:24 SP:F7 CYC:176 SL:110 +C6CD EA NOP A:55 X:00 Y:53 P:24 SP:F7 CYC:182 SL:110 +C6CE EA NOP A:55 X:00 Y:53 P:24 SP:F7 CYC:188 SL:110 +C6CF EA NOP A:55 X:00 Y:53 P:24 SP:F7 CYC:194 SL:110 +C6D0 08 PHP A:55 X:00 Y:53 P:24 SP:F7 CYC:200 SL:110 +C6D1 48 PHA A:55 X:00 Y:53 P:24 SP:F6 CYC:209 SL:110 +C6D2 14 A9 *NOP $A9,X @ A9 = 00 A:55 X:00 Y:53 P:24 SP:F5 CYC:218 SL:110 +C6D4 34 A9 *NOP $A9,X @ A9 = 00 A:55 X:00 Y:53 P:24 SP:F5 CYC:230 SL:110 +C6D6 54 A9 *NOP $A9,X @ A9 = 00 A:55 X:00 Y:53 P:24 SP:F5 CYC:242 SL:110 +C6D8 74 A9 *NOP $A9,X @ A9 = 00 A:55 X:00 Y:53 P:24 SP:F5 CYC:254 SL:110 +C6DA D4 A9 *NOP $A9,X @ A9 = 00 A:55 X:00 Y:53 P:24 SP:F5 CYC:266 SL:110 +C6DC F4 A9 *NOP $A9,X @ A9 = 00 A:55 X:00 Y:53 P:24 SP:F5 CYC:278 SL:110 +C6DE EA NOP A:55 X:00 Y:53 P:24 SP:F5 CYC:290 SL:110 +C6DF EA NOP A:55 X:00 Y:53 P:24 SP:F5 CYC:296 SL:110 +C6E0 EA NOP A:55 X:00 Y:53 P:24 SP:F5 CYC:302 SL:110 +C6E1 EA NOP A:55 X:00 Y:53 P:24 SP:F5 CYC:308 SL:110 +C6E2 08 PHP A:55 X:00 Y:53 P:24 SP:F5 CYC:314 SL:110 +C6E3 48 PHA A:55 X:00 Y:53 P:24 SP:F4 CYC:323 SL:110 +C6E4 1A *NOP A:55 X:00 Y:53 P:24 SP:F3 CYC:332 SL:110 +C6E5 3A *NOP A:55 X:00 Y:53 P:24 SP:F3 CYC:338 SL:110 +C6E6 5A *NOP A:55 X:00 Y:53 P:24 SP:F3 CYC: 3 SL:111 +C6E7 7A *NOP A:55 X:00 Y:53 P:24 SP:F3 CYC: 9 SL:111 +C6E8 DA *NOP A:55 X:00 Y:53 P:24 SP:F3 CYC: 15 SL:111 +C6E9 FA *NOP A:55 X:00 Y:53 P:24 SP:F3 CYC: 21 SL:111 +C6EA 80 89 *NOP #$89 A:55 X:00 Y:53 P:24 SP:F3 CYC: 27 SL:111 +C6EC EA NOP A:55 X:00 Y:53 P:24 SP:F3 CYC: 33 SL:111 +C6ED EA NOP A:55 X:00 Y:53 P:24 SP:F3 CYC: 39 SL:111 +C6EE EA NOP A:55 X:00 Y:53 P:24 SP:F3 CYC: 45 SL:111 +C6EF EA NOP A:55 X:00 Y:53 P:24 SP:F3 CYC: 51 SL:111 +C6F0 08 PHP A:55 X:00 Y:53 P:24 SP:F3 CYC: 57 SL:111 +C6F1 48 PHA A:55 X:00 Y:53 P:24 SP:F2 CYC: 66 SL:111 +C6F2 1C A9 A9 *NOP $A9A9,X @ A9A9 = A9 A:55 X:00 Y:53 P:24 SP:F1 CYC: 75 SL:111 +C6F5 3C A9 A9 *NOP $A9A9,X @ A9A9 = A9 A:55 X:00 Y:53 P:24 SP:F1 CYC: 87 SL:111 +C6F8 5C A9 A9 *NOP $A9A9,X @ A9A9 = A9 A:55 X:00 Y:53 P:24 SP:F1 CYC: 99 SL:111 +C6FB 7C A9 A9 *NOP $A9A9,X @ A9A9 = A9 A:55 X:00 Y:53 P:24 SP:F1 CYC:111 SL:111 +C6FE DC A9 A9 *NOP $A9A9,X @ A9A9 = A9 A:55 X:00 Y:53 P:24 SP:F1 CYC:123 SL:111 +C701 FC A9 A9 *NOP $A9A9,X @ A9A9 = A9 A:55 X:00 Y:53 P:24 SP:F1 CYC:135 SL:111 +C704 EA NOP A:55 X:00 Y:53 P:24 SP:F1 CYC:147 SL:111 +C705 EA NOP A:55 X:00 Y:53 P:24 SP:F1 CYC:153 SL:111 +C706 EA NOP A:55 X:00 Y:53 P:24 SP:F1 CYC:159 SL:111 +C707 EA NOP A:55 X:00 Y:53 P:24 SP:F1 CYC:165 SL:111 +C708 08 PHP A:55 X:00 Y:53 P:24 SP:F1 CYC:171 SL:111 +C709 48 PHA A:55 X:00 Y:53 P:24 SP:F0 CYC:180 SL:111 +C70A A2 05 LDX #$05 A:55 X:00 Y:53 P:24 SP:EF CYC:189 SL:111 +C70C 68 PLA A:55 X:05 Y:53 P:24 SP:EF CYC:195 SL:111 +C70D C9 55 CMP #$55 A:55 X:05 Y:53 P:24 SP:F0 CYC:207 SL:111 +C70F F0 0A BEQ $C71B A:55 X:05 Y:53 P:27 SP:F0 CYC:213 SL:111 +C71B 68 PLA A:55 X:05 Y:53 P:27 SP:F0 CYC:222 SL:111 +C71C 29 CB AND #$CB A:34 X:05 Y:53 P:25 SP:F1 CYC:234 SL:111 +C71E C9 00 CMP #$00 A:00 X:05 Y:53 P:27 SP:F1 CYC:240 SL:111 +C720 F0 06 BEQ $C728 A:00 X:05 Y:53 P:27 SP:F1 CYC:246 SL:111 +C728 C8 INY A:00 X:05 Y:53 P:27 SP:F1 CYC:255 SL:111 +C729 CA DEX A:00 X:05 Y:54 P:25 SP:F1 CYC:261 SL:111 +C72A D0 E0 BNE $C70C A:00 X:04 Y:54 P:25 SP:F1 CYC:267 SL:111 +C70C 68 PLA A:00 X:04 Y:54 P:25 SP:F1 CYC:276 SL:111 +C70D C9 55 CMP #$55 A:55 X:04 Y:54 P:25 SP:F2 CYC:288 SL:111 +C70F F0 0A BEQ $C71B A:55 X:04 Y:54 P:27 SP:F2 CYC:294 SL:111 +C71B 68 PLA A:55 X:04 Y:54 P:27 SP:F2 CYC:303 SL:111 +C71C 29 CB AND #$CB A:34 X:04 Y:54 P:25 SP:F3 CYC:315 SL:111 +C71E C9 00 CMP #$00 A:00 X:04 Y:54 P:27 SP:F3 CYC:321 SL:111 +C720 F0 06 BEQ $C728 A:00 X:04 Y:54 P:27 SP:F3 CYC:327 SL:111 +C728 C8 INY A:00 X:04 Y:54 P:27 SP:F3 CYC:336 SL:111 +C729 CA DEX A:00 X:04 Y:55 P:25 SP:F3 CYC: 1 SL:112 +C72A D0 E0 BNE $C70C A:00 X:03 Y:55 P:25 SP:F3 CYC: 7 SL:112 +C70C 68 PLA A:00 X:03 Y:55 P:25 SP:F3 CYC: 16 SL:112 +C70D C9 55 CMP #$55 A:55 X:03 Y:55 P:25 SP:F4 CYC: 28 SL:112 +C70F F0 0A BEQ $C71B A:55 X:03 Y:55 P:27 SP:F4 CYC: 34 SL:112 +C71B 68 PLA A:55 X:03 Y:55 P:27 SP:F4 CYC: 43 SL:112 +C71C 29 CB AND #$CB A:34 X:03 Y:55 P:25 SP:F5 CYC: 55 SL:112 +C71E C9 00 CMP #$00 A:00 X:03 Y:55 P:27 SP:F5 CYC: 61 SL:112 +C720 F0 06 BEQ $C728 A:00 X:03 Y:55 P:27 SP:F5 CYC: 67 SL:112 +C728 C8 INY A:00 X:03 Y:55 P:27 SP:F5 CYC: 76 SL:112 +C729 CA DEX A:00 X:03 Y:56 P:25 SP:F5 CYC: 82 SL:112 +C72A D0 E0 BNE $C70C A:00 X:02 Y:56 P:25 SP:F5 CYC: 88 SL:112 +C70C 68 PLA A:00 X:02 Y:56 P:25 SP:F5 CYC: 97 SL:112 +C70D C9 55 CMP #$55 A:55 X:02 Y:56 P:25 SP:F6 CYC:109 SL:112 +C70F F0 0A BEQ $C71B A:55 X:02 Y:56 P:27 SP:F6 CYC:115 SL:112 +C71B 68 PLA A:55 X:02 Y:56 P:27 SP:F6 CYC:124 SL:112 +C71C 29 CB AND #$CB A:34 X:02 Y:56 P:25 SP:F7 CYC:136 SL:112 +C71E C9 00 CMP #$00 A:00 X:02 Y:56 P:27 SP:F7 CYC:142 SL:112 +C720 F0 06 BEQ $C728 A:00 X:02 Y:56 P:27 SP:F7 CYC:148 SL:112 +C728 C8 INY A:00 X:02 Y:56 P:27 SP:F7 CYC:157 SL:112 +C729 CA DEX A:00 X:02 Y:57 P:25 SP:F7 CYC:163 SL:112 +C72A D0 E0 BNE $C70C A:00 X:01 Y:57 P:25 SP:F7 CYC:169 SL:112 +C70C 68 PLA A:00 X:01 Y:57 P:25 SP:F7 CYC:178 SL:112 +C70D C9 55 CMP #$55 A:55 X:01 Y:57 P:25 SP:F8 CYC:190 SL:112 +C70F F0 0A BEQ $C71B A:55 X:01 Y:57 P:27 SP:F8 CYC:196 SL:112 +C71B 68 PLA A:55 X:01 Y:57 P:27 SP:F8 CYC:205 SL:112 +C71C 29 CB AND #$CB A:34 X:01 Y:57 P:25 SP:F9 CYC:217 SL:112 +C71E C9 00 CMP #$00 A:00 X:01 Y:57 P:27 SP:F9 CYC:223 SL:112 +C720 F0 06 BEQ $C728 A:00 X:01 Y:57 P:27 SP:F9 CYC:229 SL:112 +C728 C8 INY A:00 X:01 Y:57 P:27 SP:F9 CYC:238 SL:112 +C729 CA DEX A:00 X:01 Y:58 P:25 SP:F9 CYC:244 SL:112 +C72A D0 E0 BNE $C70C A:00 X:00 Y:58 P:27 SP:F9 CYC:250 SL:112 +C72C 60 RTS A:00 X:00 Y:58 P:27 SP:F9 CYC:256 SL:112 +C6AF 60 RTS A:00 X:00 Y:58 P:27 SP:FB CYC:274 SL:112 +C632 20 1E E5 JSR $E51E A:00 X:00 Y:58 P:27 SP:FD CYC:292 SL:112 +E51E A9 55 LDA #$55 A:00 X:00 Y:58 P:27 SP:FB CYC:310 SL:112 +E520 8D 80 05 STA $0580 = 00 A:55 X:00 Y:58 P:25 SP:FB CYC:316 SL:112 +E523 A9 AA LDA #$AA A:55 X:00 Y:58 P:25 SP:FB CYC:328 SL:112 +E525 8D 32 04 STA $0432 = 00 A:AA X:00 Y:58 P:A5 SP:FB CYC:334 SL:112 +E528 A9 80 LDA #$80 A:AA X:00 Y:58 P:A5 SP:FB CYC: 5 SL:113 +E52A 85 43 STA $43 = 00 A:80 X:00 Y:58 P:A5 SP:FB CYC: 11 SL:113 +E52C A9 05 LDA #$05 A:80 X:00 Y:58 P:A5 SP:FB CYC: 20 SL:113 +E52E 85 44 STA $44 = 00 A:05 X:00 Y:58 P:25 SP:FB CYC: 26 SL:113 +E530 A9 32 LDA #$32 A:05 X:00 Y:58 P:25 SP:FB CYC: 35 SL:113 +E532 85 45 STA $45 = 00 A:32 X:00 Y:58 P:25 SP:FB CYC: 41 SL:113 +E534 A9 04 LDA #$04 A:32 X:00 Y:58 P:25 SP:FB CYC: 50 SL:113 +E536 85 46 STA $46 = 00 A:04 X:00 Y:58 P:25 SP:FB CYC: 56 SL:113 +E538 A2 03 LDX #$03 A:04 X:00 Y:58 P:25 SP:FB CYC: 65 SL:113 +E53A A0 77 LDY #$77 A:04 X:03 Y:58 P:25 SP:FB CYC: 71 SL:113 +E53C A9 FF LDA #$FF A:04 X:03 Y:77 P:25 SP:FB CYC: 77 SL:113 +E53E 85 01 STA $01 = FF A:FF X:03 Y:77 P:A5 SP:FB CYC: 83 SL:113 +E540 24 01 BIT $01 = FF A:FF X:03 Y:77 P:A5 SP:FB CYC: 92 SL:113 +E542 38 SEC A:FF X:03 Y:77 P:E5 SP:FB CYC:101 SL:113 +E543 A9 00 LDA #$00 A:FF X:03 Y:77 P:E5 SP:FB CYC:107 SL:113 +E545 A3 40 *LAX ($40,X) @ 43 = 0580 = 55 A:00 X:03 Y:77 P:67 SP:FB CYC:113 SL:113 +E547 EA NOP A:55 X:55 Y:77 P:65 SP:FB CYC:131 SL:113 +E548 EA NOP A:55 X:55 Y:77 P:65 SP:FB CYC:137 SL:113 +E549 EA NOP A:55 X:55 Y:77 P:65 SP:FB CYC:143 SL:113 +E54A EA NOP A:55 X:55 Y:77 P:65 SP:FB CYC:149 SL:113 +E54B F0 12 BEQ $E55F A:55 X:55 Y:77 P:65 SP:FB CYC:155 SL:113 +E54D 30 10 BMI $E55F A:55 X:55 Y:77 P:65 SP:FB CYC:161 SL:113 +E54F 50 0E BVC $E55F A:55 X:55 Y:77 P:65 SP:FB CYC:167 SL:113 +E551 90 0C BCC $E55F A:55 X:55 Y:77 P:65 SP:FB CYC:173 SL:113 +E553 C9 55 CMP #$55 A:55 X:55 Y:77 P:65 SP:FB CYC:179 SL:113 +E555 D0 08 BNE $E55F A:55 X:55 Y:77 P:67 SP:FB CYC:185 SL:113 +E557 E0 55 CPX #$55 A:55 X:55 Y:77 P:67 SP:FB CYC:191 SL:113 +E559 D0 04 BNE $E55F A:55 X:55 Y:77 P:67 SP:FB CYC:197 SL:113 +E55B C0 77 CPY #$77 A:55 X:55 Y:77 P:67 SP:FB CYC:203 SL:113 +E55D F0 04 BEQ $E563 A:55 X:55 Y:77 P:67 SP:FB CYC:209 SL:113 +E563 A2 05 LDX #$05 A:55 X:55 Y:77 P:67 SP:FB CYC:218 SL:113 +E565 A0 33 LDY #$33 A:55 X:05 Y:77 P:65 SP:FB CYC:224 SL:113 +E567 B8 CLV A:55 X:05 Y:33 P:65 SP:FB CYC:230 SL:113 +E568 18 CLC A:55 X:05 Y:33 P:25 SP:FB CYC:236 SL:113 +E569 A9 00 LDA #$00 A:55 X:05 Y:33 P:24 SP:FB CYC:242 SL:113 +E56B A3 40 *LAX ($40,X) @ 45 = 0432 = AA A:00 X:05 Y:33 P:26 SP:FB CYC:248 SL:113 +E56D EA NOP A:AA X:AA Y:33 P:A4 SP:FB CYC:266 SL:113 +E56E EA NOP A:AA X:AA Y:33 P:A4 SP:FB CYC:272 SL:113 +E56F EA NOP A:AA X:AA Y:33 P:A4 SP:FB CYC:278 SL:113 +E570 EA NOP A:AA X:AA Y:33 P:A4 SP:FB CYC:284 SL:113 +E571 F0 12 BEQ $E585 A:AA X:AA Y:33 P:A4 SP:FB CYC:290 SL:113 +E573 10 10 BPL $E585 A:AA X:AA Y:33 P:A4 SP:FB CYC:296 SL:113 +E575 70 0E BVS $E585 A:AA X:AA Y:33 P:A4 SP:FB CYC:302 SL:113 +E577 B0 0C BCS $E585 A:AA X:AA Y:33 P:A4 SP:FB CYC:308 SL:113 +E579 C9 AA CMP #$AA A:AA X:AA Y:33 P:A4 SP:FB CYC:314 SL:113 +E57B D0 08 BNE $E585 A:AA X:AA Y:33 P:27 SP:FB CYC:320 SL:113 +E57D E0 AA CPX #$AA A:AA X:AA Y:33 P:27 SP:FB CYC:326 SL:113 +E57F D0 04 BNE $E585 A:AA X:AA Y:33 P:27 SP:FB CYC:332 SL:113 +E581 C0 33 CPY #$33 A:AA X:AA Y:33 P:27 SP:FB CYC:338 SL:113 +E583 F0 04 BEQ $E589 A:AA X:AA Y:33 P:27 SP:FB CYC: 3 SL:114 +E589 A9 87 LDA #$87 A:AA X:AA Y:33 P:27 SP:FB CYC: 12 SL:114 +E58B 85 67 STA $67 = 00 A:87 X:AA Y:33 P:A5 SP:FB CYC: 18 SL:114 +E58D A9 32 LDA #$32 A:87 X:AA Y:33 P:A5 SP:FB CYC: 27 SL:114 +E58F 85 68 STA $68 = 00 A:32 X:AA Y:33 P:25 SP:FB CYC: 33 SL:114 +E591 A0 57 LDY #$57 A:32 X:AA Y:33 P:25 SP:FB CYC: 42 SL:114 +E593 24 01 BIT $01 = FF A:32 X:AA Y:57 P:25 SP:FB CYC: 48 SL:114 +E595 38 SEC A:32 X:AA Y:57 P:E5 SP:FB CYC: 57 SL:114 +E596 A9 00 LDA #$00 A:32 X:AA Y:57 P:E5 SP:FB CYC: 63 SL:114 +E598 A7 67 *LAX $67 = 87 A:00 X:AA Y:57 P:67 SP:FB CYC: 69 SL:114 +E59A EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC: 78 SL:114 +E59B EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC: 84 SL:114 +E59C EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC: 90 SL:114 +E59D EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC: 96 SL:114 +E59E F0 12 BEQ $E5B2 A:87 X:87 Y:57 P:E5 SP:FB CYC:102 SL:114 +E5A0 10 10 BPL $E5B2 A:87 X:87 Y:57 P:E5 SP:FB CYC:108 SL:114 +E5A2 50 0E BVC $E5B2 A:87 X:87 Y:57 P:E5 SP:FB CYC:114 SL:114 +E5A4 90 0C BCC $E5B2 A:87 X:87 Y:57 P:E5 SP:FB CYC:120 SL:114 +E5A6 C9 87 CMP #$87 A:87 X:87 Y:57 P:E5 SP:FB CYC:126 SL:114 +E5A8 D0 08 BNE $E5B2 A:87 X:87 Y:57 P:67 SP:FB CYC:132 SL:114 +E5AA E0 87 CPX #$87 A:87 X:87 Y:57 P:67 SP:FB CYC:138 SL:114 +E5AC D0 04 BNE $E5B2 A:87 X:87 Y:57 P:67 SP:FB CYC:144 SL:114 +E5AE C0 57 CPY #$57 A:87 X:87 Y:57 P:67 SP:FB CYC:150 SL:114 +E5B0 F0 04 BEQ $E5B6 A:87 X:87 Y:57 P:67 SP:FB CYC:156 SL:114 +E5B6 A0 53 LDY #$53 A:87 X:87 Y:57 P:67 SP:FB CYC:165 SL:114 +E5B8 B8 CLV A:87 X:87 Y:53 P:65 SP:FB CYC:171 SL:114 +E5B9 18 CLC A:87 X:87 Y:53 P:25 SP:FB CYC:177 SL:114 +E5BA A9 00 LDA #$00 A:87 X:87 Y:53 P:24 SP:FB CYC:183 SL:114 +E5BC A7 68 *LAX $68 = 32 A:00 X:87 Y:53 P:26 SP:FB CYC:189 SL:114 +E5BE EA NOP A:32 X:32 Y:53 P:24 SP:FB CYC:198 SL:114 +E5BF EA NOP A:32 X:32 Y:53 P:24 SP:FB CYC:204 SL:114 +E5C0 EA NOP A:32 X:32 Y:53 P:24 SP:FB CYC:210 SL:114 +E5C1 EA NOP A:32 X:32 Y:53 P:24 SP:FB CYC:216 SL:114 +E5C2 F0 12 BEQ $E5D6 A:32 X:32 Y:53 P:24 SP:FB CYC:222 SL:114 +E5C4 30 10 BMI $E5D6 A:32 X:32 Y:53 P:24 SP:FB CYC:228 SL:114 +E5C6 70 0E BVS $E5D6 A:32 X:32 Y:53 P:24 SP:FB CYC:234 SL:114 +E5C8 B0 0C BCS $E5D6 A:32 X:32 Y:53 P:24 SP:FB CYC:240 SL:114 +E5CA C9 32 CMP #$32 A:32 X:32 Y:53 P:24 SP:FB CYC:246 SL:114 +E5CC D0 08 BNE $E5D6 A:32 X:32 Y:53 P:27 SP:FB CYC:252 SL:114 +E5CE E0 32 CPX #$32 A:32 X:32 Y:53 P:27 SP:FB CYC:258 SL:114 +E5D0 D0 04 BNE $E5D6 A:32 X:32 Y:53 P:27 SP:FB CYC:264 SL:114 +E5D2 C0 53 CPY #$53 A:32 X:32 Y:53 P:27 SP:FB CYC:270 SL:114 +E5D4 F0 04 BEQ $E5DA A:32 X:32 Y:53 P:27 SP:FB CYC:276 SL:114 +E5DA A9 87 LDA #$87 A:32 X:32 Y:53 P:27 SP:FB CYC:285 SL:114 +E5DC 8D 77 05 STA $0577 = 00 A:87 X:32 Y:53 P:A5 SP:FB CYC:291 SL:114 +E5DF A9 32 LDA #$32 A:87 X:32 Y:53 P:A5 SP:FB CYC:303 SL:114 +E5E1 8D 78 05 STA $0578 = 00 A:32 X:32 Y:53 P:25 SP:FB CYC:309 SL:114 +E5E4 A0 57 LDY #$57 A:32 X:32 Y:53 P:25 SP:FB CYC:321 SL:114 +E5E6 24 01 BIT $01 = FF A:32 X:32 Y:57 P:25 SP:FB CYC:327 SL:114 +E5E8 38 SEC A:32 X:32 Y:57 P:E5 SP:FB CYC:336 SL:114 +E5E9 A9 00 LDA #$00 A:32 X:32 Y:57 P:E5 SP:FB CYC: 1 SL:115 +E5EB AF 77 05 *LAX $0577 = 87 A:00 X:32 Y:57 P:67 SP:FB CYC: 7 SL:115 +E5EE EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC: 19 SL:115 +E5EF EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC: 25 SL:115 +E5F0 EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC: 31 SL:115 +E5F1 EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC: 37 SL:115 +E5F2 F0 12 BEQ $E606 A:87 X:87 Y:57 P:E5 SP:FB CYC: 43 SL:115 +E5F4 10 10 BPL $E606 A:87 X:87 Y:57 P:E5 SP:FB CYC: 49 SL:115 +E5F6 50 0E BVC $E606 A:87 X:87 Y:57 P:E5 SP:FB CYC: 55 SL:115 +E5F8 90 0C BCC $E606 A:87 X:87 Y:57 P:E5 SP:FB CYC: 61 SL:115 +E5FA C9 87 CMP #$87 A:87 X:87 Y:57 P:E5 SP:FB CYC: 67 SL:115 +E5FC D0 08 BNE $E606 A:87 X:87 Y:57 P:67 SP:FB CYC: 73 SL:115 +E5FE E0 87 CPX #$87 A:87 X:87 Y:57 P:67 SP:FB CYC: 79 SL:115 +E600 D0 04 BNE $E606 A:87 X:87 Y:57 P:67 SP:FB CYC: 85 SL:115 +E602 C0 57 CPY #$57 A:87 X:87 Y:57 P:67 SP:FB CYC: 91 SL:115 +E604 F0 04 BEQ $E60A A:87 X:87 Y:57 P:67 SP:FB CYC: 97 SL:115 +E60A A0 53 LDY #$53 A:87 X:87 Y:57 P:67 SP:FB CYC:106 SL:115 +E60C B8 CLV A:87 X:87 Y:53 P:65 SP:FB CYC:112 SL:115 +E60D 18 CLC A:87 X:87 Y:53 P:25 SP:FB CYC:118 SL:115 +E60E A9 00 LDA #$00 A:87 X:87 Y:53 P:24 SP:FB CYC:124 SL:115 +E610 AF 78 05 *LAX $0578 = 32 A:00 X:87 Y:53 P:26 SP:FB CYC:130 SL:115 +E613 EA NOP A:32 X:32 Y:53 P:24 SP:FB CYC:142 SL:115 +E614 EA NOP A:32 X:32 Y:53 P:24 SP:FB CYC:148 SL:115 +E615 EA NOP A:32 X:32 Y:53 P:24 SP:FB CYC:154 SL:115 +E616 EA NOP A:32 X:32 Y:53 P:24 SP:FB CYC:160 SL:115 +E617 F0 12 BEQ $E62B A:32 X:32 Y:53 P:24 SP:FB CYC:166 SL:115 +E619 30 10 BMI $E62B A:32 X:32 Y:53 P:24 SP:FB CYC:172 SL:115 +E61B 70 0E BVS $E62B A:32 X:32 Y:53 P:24 SP:FB CYC:178 SL:115 +E61D B0 0C BCS $E62B A:32 X:32 Y:53 P:24 SP:FB CYC:184 SL:115 +E61F C9 32 CMP #$32 A:32 X:32 Y:53 P:24 SP:FB CYC:190 SL:115 +E621 D0 08 BNE $E62B A:32 X:32 Y:53 P:27 SP:FB CYC:196 SL:115 +E623 E0 32 CPX #$32 A:32 X:32 Y:53 P:27 SP:FB CYC:202 SL:115 +E625 D0 04 BNE $E62B A:32 X:32 Y:53 P:27 SP:FB CYC:208 SL:115 +E627 C0 53 CPY #$53 A:32 X:32 Y:53 P:27 SP:FB CYC:214 SL:115 +E629 F0 04 BEQ $E62F A:32 X:32 Y:53 P:27 SP:FB CYC:220 SL:115 +E62F A9 FF LDA #$FF A:32 X:32 Y:53 P:27 SP:FB CYC:229 SL:115 +E631 85 43 STA $43 = 80 A:FF X:32 Y:53 P:A5 SP:FB CYC:235 SL:115 +E633 A9 04 LDA #$04 A:FF X:32 Y:53 P:A5 SP:FB CYC:244 SL:115 +E635 85 44 STA $44 = 05 A:04 X:32 Y:53 P:25 SP:FB CYC:250 SL:115 +E637 A9 32 LDA #$32 A:04 X:32 Y:53 P:25 SP:FB CYC:259 SL:115 +E639 85 45 STA $45 = 32 A:32 X:32 Y:53 P:25 SP:FB CYC:265 SL:115 +E63B A9 04 LDA #$04 A:32 X:32 Y:53 P:25 SP:FB CYC:274 SL:115 +E63D 85 46 STA $46 = 04 A:04 X:32 Y:53 P:25 SP:FB CYC:280 SL:115 +E63F A9 55 LDA #$55 A:04 X:32 Y:53 P:25 SP:FB CYC:289 SL:115 +E641 8D 80 05 STA $0580 = 55 A:55 X:32 Y:53 P:25 SP:FB CYC:295 SL:115 +E644 A9 AA LDA #$AA A:55 X:32 Y:53 P:25 SP:FB CYC:307 SL:115 +E646 8D 32 04 STA $0432 = AA A:AA X:32 Y:53 P:A5 SP:FB CYC:313 SL:115 +E649 A2 03 LDX #$03 A:AA X:32 Y:53 P:A5 SP:FB CYC:325 SL:115 +E64B A0 81 LDY #$81 A:AA X:03 Y:53 P:25 SP:FB CYC:331 SL:115 +E64D 24 01 BIT $01 = FF A:AA X:03 Y:81 P:A5 SP:FB CYC:337 SL:115 +E64F 38 SEC A:AA X:03 Y:81 P:E5 SP:FB CYC: 5 SL:116 +E650 A9 00 LDA #$00 A:AA X:03 Y:81 P:E5 SP:FB CYC: 11 SL:116 +E652 B3 43 *LAX ($43),Y = 04FF @ 0580 = 55 A:00 X:03 Y:81 P:67 SP:FB CYC: 17 SL:116 +E654 EA NOP A:55 X:55 Y:81 P:65 SP:FB CYC: 35 SL:116 +E655 EA NOP A:55 X:55 Y:81 P:65 SP:FB CYC: 41 SL:116 +E656 EA NOP A:55 X:55 Y:81 P:65 SP:FB CYC: 47 SL:116 +E657 EA NOP A:55 X:55 Y:81 P:65 SP:FB CYC: 53 SL:116 +E658 F0 12 BEQ $E66C A:55 X:55 Y:81 P:65 SP:FB CYC: 59 SL:116 +E65A 30 10 BMI $E66C A:55 X:55 Y:81 P:65 SP:FB CYC: 65 SL:116 +E65C 50 0E BVC $E66C A:55 X:55 Y:81 P:65 SP:FB CYC: 71 SL:116 +E65E 90 0C BCC $E66C A:55 X:55 Y:81 P:65 SP:FB CYC: 77 SL:116 +E660 C9 55 CMP #$55 A:55 X:55 Y:81 P:65 SP:FB CYC: 83 SL:116 +E662 D0 08 BNE $E66C A:55 X:55 Y:81 P:67 SP:FB CYC: 89 SL:116 +E664 E0 55 CPX #$55 A:55 X:55 Y:81 P:67 SP:FB CYC: 95 SL:116 +E666 D0 04 BNE $E66C A:55 X:55 Y:81 P:67 SP:FB CYC:101 SL:116 +E668 C0 81 CPY #$81 A:55 X:55 Y:81 P:67 SP:FB CYC:107 SL:116 +E66A F0 04 BEQ $E670 A:55 X:55 Y:81 P:67 SP:FB CYC:113 SL:116 +E670 A2 05 LDX #$05 A:55 X:55 Y:81 P:67 SP:FB CYC:122 SL:116 +E672 A0 00 LDY #$00 A:55 X:05 Y:81 P:65 SP:FB CYC:128 SL:116 +E674 B8 CLV A:55 X:05 Y:00 P:67 SP:FB CYC:134 SL:116 +E675 18 CLC A:55 X:05 Y:00 P:27 SP:FB CYC:140 SL:116 +E676 A9 00 LDA #$00 A:55 X:05 Y:00 P:26 SP:FB CYC:146 SL:116 +E678 B3 45 *LAX ($45),Y = 0432 @ 0432 = AA A:00 X:05 Y:00 P:26 SP:FB CYC:152 SL:116 +E67A EA NOP A:AA X:AA Y:00 P:A4 SP:FB CYC:167 SL:116 +E67B EA NOP A:AA X:AA Y:00 P:A4 SP:FB CYC:173 SL:116 +E67C EA NOP A:AA X:AA Y:00 P:A4 SP:FB CYC:179 SL:116 +E67D EA NOP A:AA X:AA Y:00 P:A4 SP:FB CYC:185 SL:116 +E67E F0 12 BEQ $E692 A:AA X:AA Y:00 P:A4 SP:FB CYC:191 SL:116 +E680 10 10 BPL $E692 A:AA X:AA Y:00 P:A4 SP:FB CYC:197 SL:116 +E682 70 0E BVS $E692 A:AA X:AA Y:00 P:A4 SP:FB CYC:203 SL:116 +E684 B0 0C BCS $E692 A:AA X:AA Y:00 P:A4 SP:FB CYC:209 SL:116 +E686 C9 AA CMP #$AA A:AA X:AA Y:00 P:A4 SP:FB CYC:215 SL:116 +E688 D0 08 BNE $E692 A:AA X:AA Y:00 P:27 SP:FB CYC:221 SL:116 +E68A E0 AA CPX #$AA A:AA X:AA Y:00 P:27 SP:FB CYC:227 SL:116 +E68C D0 04 BNE $E692 A:AA X:AA Y:00 P:27 SP:FB CYC:233 SL:116 +E68E C0 00 CPY #$00 A:AA X:AA Y:00 P:27 SP:FB CYC:239 SL:116 +E690 F0 04 BEQ $E696 A:AA X:AA Y:00 P:27 SP:FB CYC:245 SL:116 +E696 A9 87 LDA #$87 A:AA X:AA Y:00 P:27 SP:FB CYC:254 SL:116 +E698 85 67 STA $67 = 87 A:87 X:AA Y:00 P:A5 SP:FB CYC:260 SL:116 +E69A A9 32 LDA #$32 A:87 X:AA Y:00 P:A5 SP:FB CYC:269 SL:116 +E69C 85 68 STA $68 = 32 A:32 X:AA Y:00 P:25 SP:FB CYC:275 SL:116 +E69E A0 57 LDY #$57 A:32 X:AA Y:00 P:25 SP:FB CYC:284 SL:116 +E6A0 24 01 BIT $01 = FF A:32 X:AA Y:57 P:25 SP:FB CYC:290 SL:116 +E6A2 38 SEC A:32 X:AA Y:57 P:E5 SP:FB CYC:299 SL:116 +E6A3 A9 00 LDA #$00 A:32 X:AA Y:57 P:E5 SP:FB CYC:305 SL:116 +E6A5 B7 10 *LAX $10,Y @ 67 = 87 A:00 X:AA Y:57 P:67 SP:FB CYC:311 SL:116 +E6A7 EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC:323 SL:116 +E6A8 EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC:329 SL:116 +E6A9 EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC:335 SL:116 +E6AA EA NOP A:87 X:87 Y:57 P:E5 SP:FB CYC: 0 SL:117 +E6AB F0 12 BEQ $E6BF A:87 X:87 Y:57 P:E5 SP:FB CYC: 6 SL:117 +E6AD 10 10 BPL $E6BF A:87 X:87 Y:57 P:E5 SP:FB CYC: 12 SL:117 +E6AF 50 0E BVC $E6BF A:87 X:87 Y:57 P:E5 SP:FB CYC: 18 SL:117 +E6B1 90 0C BCC $E6BF A:87 X:87 Y:57 P:E5 SP:FB CYC: 24 SL:117 +E6B3 C9 87 CMP #$87 A:87 X:87 Y:57 P:E5 SP:FB CYC: 30 SL:117 +E6B5 D0 08 BNE $E6BF A:87 X:87 Y:57 P:67 SP:FB CYC: 36 SL:117 +E6B7 E0 87 CPX #$87 A:87 X:87 Y:57 P:67 SP:FB CYC: 42 SL:117 +E6B9 D0 04 BNE $E6BF A:87 X:87 Y:57 P:67 SP:FB CYC: 48 SL:117 +E6BB C0 57 CPY #$57 A:87 X:87 Y:57 P:67 SP:FB CYC: 54 SL:117 +E6BD F0 04 BEQ $E6C3 A:87 X:87 Y:57 P:67 SP:FB CYC: 60 SL:117 +E6C3 A0 FF LDY #$FF A:87 X:87 Y:57 P:67 SP:FB CYC: 69 SL:117 +E6C5 B8 CLV A:87 X:87 Y:FF P:E5 SP:FB CYC: 75 SL:117 +E6C6 18 CLC A:87 X:87 Y:FF P:A5 SP:FB CYC: 81 SL:117 +E6C7 A9 00 LDA #$00 A:87 X:87 Y:FF P:A4 SP:FB CYC: 87 SL:117 +E6C9 B7 69 *LAX $69,Y @ 68 = 32 A:00 X:87 Y:FF P:26 SP:FB CYC: 93 SL:117 +E6CB EA NOP A:32 X:32 Y:FF P:24 SP:FB CYC:105 SL:117 +E6CC EA NOP A:32 X:32 Y:FF P:24 SP:FB CYC:111 SL:117 +E6CD EA NOP A:32 X:32 Y:FF P:24 SP:FB CYC:117 SL:117 +E6CE EA NOP A:32 X:32 Y:FF P:24 SP:FB CYC:123 SL:117 +E6CF F0 12 BEQ $E6E3 A:32 X:32 Y:FF P:24 SP:FB CYC:129 SL:117 +E6D1 30 10 BMI $E6E3 A:32 X:32 Y:FF P:24 SP:FB CYC:135 SL:117 +E6D3 70 0E BVS $E6E3 A:32 X:32 Y:FF P:24 SP:FB CYC:141 SL:117 +E6D5 B0 0C BCS $E6E3 A:32 X:32 Y:FF P:24 SP:FB CYC:147 SL:117 +E6D7 C9 32 CMP #$32 A:32 X:32 Y:FF P:24 SP:FB CYC:153 SL:117 +E6D9 D0 08 BNE $E6E3 A:32 X:32 Y:FF P:27 SP:FB CYC:159 SL:117 +E6DB E0 32 CPX #$32 A:32 X:32 Y:FF P:27 SP:FB CYC:165 SL:117 +E6DD D0 04 BNE $E6E3 A:32 X:32 Y:FF P:27 SP:FB CYC:171 SL:117 +E6DF C0 FF CPY #$FF A:32 X:32 Y:FF P:27 SP:FB CYC:177 SL:117 +E6E1 F0 04 BEQ $E6E7 A:32 X:32 Y:FF P:27 SP:FB CYC:183 SL:117 +E6E7 A9 87 LDA #$87 A:32 X:32 Y:FF P:27 SP:FB CYC:192 SL:117 +E6E9 8D 87 05 STA $0587 = 00 A:87 X:32 Y:FF P:A5 SP:FB CYC:198 SL:117 +E6EC A9 32 LDA #$32 A:87 X:32 Y:FF P:A5 SP:FB CYC:210 SL:117 +E6EE 8D 88 05 STA $0588 = 00 A:32 X:32 Y:FF P:25 SP:FB CYC:216 SL:117 +E6F1 A0 30 LDY #$30 A:32 X:32 Y:FF P:25 SP:FB CYC:228 SL:117 +E6F3 24 01 BIT $01 = FF A:32 X:32 Y:30 P:25 SP:FB CYC:234 SL:117 +E6F5 38 SEC A:32 X:32 Y:30 P:E5 SP:FB CYC:243 SL:117 +E6F6 A9 00 LDA #$00 A:32 X:32 Y:30 P:E5 SP:FB CYC:249 SL:117 +E6F8 BF 57 05 *LAX $0557,Y @ 0587 = 87 A:00 X:32 Y:30 P:67 SP:FB CYC:255 SL:117 +E6FB EA NOP A:87 X:87 Y:30 P:E5 SP:FB CYC:267 SL:117 +E6FC EA NOP A:87 X:87 Y:30 P:E5 SP:FB CYC:273 SL:117 +E6FD EA NOP A:87 X:87 Y:30 P:E5 SP:FB CYC:279 SL:117 +E6FE EA NOP A:87 X:87 Y:30 P:E5 SP:FB CYC:285 SL:117 +E6FF F0 12 BEQ $E713 A:87 X:87 Y:30 P:E5 SP:FB CYC:291 SL:117 +E701 10 10 BPL $E713 A:87 X:87 Y:30 P:E5 SP:FB CYC:297 SL:117 +E703 50 0E BVC $E713 A:87 X:87 Y:30 P:E5 SP:FB CYC:303 SL:117 +E705 90 0C BCC $E713 A:87 X:87 Y:30 P:E5 SP:FB CYC:309 SL:117 +E707 C9 87 CMP #$87 A:87 X:87 Y:30 P:E5 SP:FB CYC:315 SL:117 +E709 D0 08 BNE $E713 A:87 X:87 Y:30 P:67 SP:FB CYC:321 SL:117 +E70B E0 87 CPX #$87 A:87 X:87 Y:30 P:67 SP:FB CYC:327 SL:117 +E70D D0 04 BNE $E713 A:87 X:87 Y:30 P:67 SP:FB CYC:333 SL:117 +E70F C0 30 CPY #$30 A:87 X:87 Y:30 P:67 SP:FB CYC:339 SL:117 +E711 F0 04 BEQ $E717 A:87 X:87 Y:30 P:67 SP:FB CYC: 4 SL:118 +E717 A0 40 LDY #$40 A:87 X:87 Y:30 P:67 SP:FB CYC: 13 SL:118 +E719 B8 CLV A:87 X:87 Y:40 P:65 SP:FB CYC: 19 SL:118 +E71A 18 CLC A:87 X:87 Y:40 P:25 SP:FB CYC: 25 SL:118 +E71B A9 00 LDA #$00 A:87 X:87 Y:40 P:24 SP:FB CYC: 31 SL:118 +E71D BF 48 05 *LAX $0548,Y @ 0588 = 32 A:00 X:87 Y:40 P:26 SP:FB CYC: 37 SL:118 +E720 EA NOP A:32 X:32 Y:40 P:24 SP:FB CYC: 49 SL:118 +E721 EA NOP A:32 X:32 Y:40 P:24 SP:FB CYC: 55 SL:118 +E722 EA NOP A:32 X:32 Y:40 P:24 SP:FB CYC: 61 SL:118 +E723 EA NOP A:32 X:32 Y:40 P:24 SP:FB CYC: 67 SL:118 +E724 F0 12 BEQ $E738 A:32 X:32 Y:40 P:24 SP:FB CYC: 73 SL:118 +E726 30 10 BMI $E738 A:32 X:32 Y:40 P:24 SP:FB CYC: 79 SL:118 +E728 70 0E BVS $E738 A:32 X:32 Y:40 P:24 SP:FB CYC: 85 SL:118 +E72A B0 0C BCS $E738 A:32 X:32 Y:40 P:24 SP:FB CYC: 91 SL:118 +E72C C9 32 CMP #$32 A:32 X:32 Y:40 P:24 SP:FB CYC: 97 SL:118 +E72E D0 08 BNE $E738 A:32 X:32 Y:40 P:27 SP:FB CYC:103 SL:118 +E730 E0 32 CPX #$32 A:32 X:32 Y:40 P:27 SP:FB CYC:109 SL:118 +E732 D0 04 BNE $E738 A:32 X:32 Y:40 P:27 SP:FB CYC:115 SL:118 +E734 C0 40 CPY #$40 A:32 X:32 Y:40 P:27 SP:FB CYC:121 SL:118 +E736 F0 04 BEQ $E73C A:32 X:32 Y:40 P:27 SP:FB CYC:127 SL:118 +E73C 60 RTS A:32 X:32 Y:40 P:27 SP:FB CYC:136 SL:118 +C635 20 3D E7 JSR $E73D A:32 X:32 Y:40 P:27 SP:FD CYC:154 SL:118 +E73D A9 C0 LDA #$C0 A:32 X:32 Y:40 P:27 SP:FB CYC:172 SL:118 +E73F 85 01 STA $01 = FF A:C0 X:32 Y:40 P:A5 SP:FB CYC:178 SL:118 +E741 A9 00 LDA #$00 A:C0 X:32 Y:40 P:A5 SP:FB CYC:187 SL:118 +E743 8D 89 04 STA $0489 = 00 A:00 X:32 Y:40 P:27 SP:FB CYC:193 SL:118 +E746 A9 89 LDA #$89 A:00 X:32 Y:40 P:27 SP:FB CYC:205 SL:118 +E748 85 60 STA $60 = 00 A:89 X:32 Y:40 P:A5 SP:FB CYC:211 SL:118 +E74A A9 04 LDA #$04 A:89 X:32 Y:40 P:A5 SP:FB CYC:220 SL:118 +E74C 85 61 STA $61 = 00 A:04 X:32 Y:40 P:25 SP:FB CYC:226 SL:118 +E74E A0 44 LDY #$44 A:04 X:32 Y:40 P:25 SP:FB CYC:235 SL:118 +E750 A2 17 LDX #$17 A:04 X:32 Y:44 P:25 SP:FB CYC:241 SL:118 +E752 A9 3E LDA #$3E A:04 X:17 Y:44 P:25 SP:FB CYC:247 SL:118 +E754 24 01 BIT $01 = C0 A:3E X:17 Y:44 P:25 SP:FB CYC:253 SL:118 +E756 18 CLC A:3E X:17 Y:44 P:E7 SP:FB CYC:262 SL:118 +E757 83 49 *SAX ($49,X) @ 60 = 0489 = 00 A:3E X:17 Y:44 P:E6 SP:FB CYC:268 SL:118 +E759 EA NOP A:3E X:17 Y:44 P:E6 SP:FB CYC:286 SL:118 +E75A EA NOP A:3E X:17 Y:44 P:E6 SP:FB CYC:292 SL:118 +E75B EA NOP A:3E X:17 Y:44 P:E6 SP:FB CYC:298 SL:118 +E75C EA NOP A:3E X:17 Y:44 P:E6 SP:FB CYC:304 SL:118 +E75D D0 19 BNE $E778 A:3E X:17 Y:44 P:E6 SP:FB CYC:310 SL:118 +E75F B0 17 BCS $E778 A:3E X:17 Y:44 P:E6 SP:FB CYC:316 SL:118 +E761 50 15 BVC $E778 A:3E X:17 Y:44 P:E6 SP:FB CYC:322 SL:118 +E763 10 13 BPL $E778 A:3E X:17 Y:44 P:E6 SP:FB CYC:328 SL:118 +E765 C9 3E CMP #$3E A:3E X:17 Y:44 P:E6 SP:FB CYC:334 SL:118 +E767 D0 0F BNE $E778 A:3E X:17 Y:44 P:67 SP:FB CYC:340 SL:118 +E769 C0 44 CPY #$44 A:3E X:17 Y:44 P:67 SP:FB CYC: 5 SL:119 +E76B D0 0B BNE $E778 A:3E X:17 Y:44 P:67 SP:FB CYC: 11 SL:119 +E76D E0 17 CPX #$17 A:3E X:17 Y:44 P:67 SP:FB CYC: 17 SL:119 +E76F D0 07 BNE $E778 A:3E X:17 Y:44 P:67 SP:FB CYC: 23 SL:119 +E771 AD 89 04 LDA $0489 = 16 A:3E X:17 Y:44 P:67 SP:FB CYC: 29 SL:119 +E774 C9 16 CMP #$16 A:16 X:17 Y:44 P:65 SP:FB CYC: 41 SL:119 +E776 F0 04 BEQ $E77C A:16 X:17 Y:44 P:67 SP:FB CYC: 47 SL:119 +E77C A0 44 LDY #$44 A:16 X:17 Y:44 P:67 SP:FB CYC: 56 SL:119 +E77E A2 7A LDX #$7A A:16 X:17 Y:44 P:65 SP:FB CYC: 62 SL:119 +E780 A9 66 LDA #$66 A:16 X:7A Y:44 P:65 SP:FB CYC: 68 SL:119 +E782 38 SEC A:66 X:7A Y:44 P:65 SP:FB CYC: 74 SL:119 +E783 B8 CLV A:66 X:7A Y:44 P:65 SP:FB CYC: 80 SL:119 +E784 83 E6 *SAX ($E6,X) @ 60 = 0489 = 16 A:66 X:7A Y:44 P:25 SP:FB CYC: 86 SL:119 +E786 EA NOP A:66 X:7A Y:44 P:25 SP:FB CYC:104 SL:119 +E787 EA NOP A:66 X:7A Y:44 P:25 SP:FB CYC:110 SL:119 +E788 EA NOP A:66 X:7A Y:44 P:25 SP:FB CYC:116 SL:119 +E789 EA NOP A:66 X:7A Y:44 P:25 SP:FB CYC:122 SL:119 +E78A F0 19 BEQ $E7A5 A:66 X:7A Y:44 P:25 SP:FB CYC:128 SL:119 +E78C 90 17 BCC $E7A5 A:66 X:7A Y:44 P:25 SP:FB CYC:134 SL:119 +E78E 70 15 BVS $E7A5 A:66 X:7A Y:44 P:25 SP:FB CYC:140 SL:119 +E790 30 13 BMI $E7A5 A:66 X:7A Y:44 P:25 SP:FB CYC:146 SL:119 +E792 C9 66 CMP #$66 A:66 X:7A Y:44 P:25 SP:FB CYC:152 SL:119 +E794 D0 0F BNE $E7A5 A:66 X:7A Y:44 P:27 SP:FB CYC:158 SL:119 +E796 C0 44 CPY #$44 A:66 X:7A Y:44 P:27 SP:FB CYC:164 SL:119 +E798 D0 0B BNE $E7A5 A:66 X:7A Y:44 P:27 SP:FB CYC:170 SL:119 +E79A E0 7A CPX #$7A A:66 X:7A Y:44 P:27 SP:FB CYC:176 SL:119 +E79C D0 07 BNE $E7A5 A:66 X:7A Y:44 P:27 SP:FB CYC:182 SL:119 +E79E AD 89 04 LDA $0489 = 62 A:66 X:7A Y:44 P:27 SP:FB CYC:188 SL:119 +E7A1 C9 62 CMP #$62 A:62 X:7A Y:44 P:25 SP:FB CYC:200 SL:119 +E7A3 F0 04 BEQ $E7A9 A:62 X:7A Y:44 P:27 SP:FB CYC:206 SL:119 +E7A9 A9 FF LDA #$FF A:62 X:7A Y:44 P:27 SP:FB CYC:215 SL:119 +E7AB 85 49 STA $49 = 00 A:FF X:7A Y:44 P:A5 SP:FB CYC:221 SL:119 +E7AD A0 44 LDY #$44 A:FF X:7A Y:44 P:A5 SP:FB CYC:230 SL:119 +E7AF A2 AA LDX #$AA A:FF X:7A Y:44 P:25 SP:FB CYC:236 SL:119 +E7B1 A9 55 LDA #$55 A:FF X:AA Y:44 P:A5 SP:FB CYC:242 SL:119 +E7B3 24 01 BIT $01 = C0 A:55 X:AA Y:44 P:25 SP:FB CYC:248 SL:119 +E7B5 18 CLC A:55 X:AA Y:44 P:E5 SP:FB CYC:257 SL:119 +E7B6 87 49 *SAX $49 = FF A:55 X:AA Y:44 P:E4 SP:FB CYC:263 SL:119 +E7B8 EA NOP A:55 X:AA Y:44 P:E4 SP:FB CYC:272 SL:119 +E7B9 EA NOP A:55 X:AA Y:44 P:E4 SP:FB CYC:278 SL:119 +E7BA EA NOP A:55 X:AA Y:44 P:E4 SP:FB CYC:284 SL:119 +E7BB EA NOP A:55 X:AA Y:44 P:E4 SP:FB CYC:290 SL:119 +E7BC F0 18 BEQ $E7D6 A:55 X:AA Y:44 P:E4 SP:FB CYC:296 SL:119 +E7BE B0 16 BCS $E7D6 A:55 X:AA Y:44 P:E4 SP:FB CYC:302 SL:119 +E7C0 50 14 BVC $E7D6 A:55 X:AA Y:44 P:E4 SP:FB CYC:308 SL:119 +E7C2 10 12 BPL $E7D6 A:55 X:AA Y:44 P:E4 SP:FB CYC:314 SL:119 +E7C4 C9 55 CMP #$55 A:55 X:AA Y:44 P:E4 SP:FB CYC:320 SL:119 +E7C6 D0 0E BNE $E7D6 A:55 X:AA Y:44 P:67 SP:FB CYC:326 SL:119 +E7C8 C0 44 CPY #$44 A:55 X:AA Y:44 P:67 SP:FB CYC:332 SL:119 +E7CA D0 0A BNE $E7D6 A:55 X:AA Y:44 P:67 SP:FB CYC:338 SL:119 +E7CC E0 AA CPX #$AA A:55 X:AA Y:44 P:67 SP:FB CYC: 3 SL:120 +E7CE D0 06 BNE $E7D6 A:55 X:AA Y:44 P:67 SP:FB CYC: 9 SL:120 +E7D0 A5 49 LDA $49 = 00 A:55 X:AA Y:44 P:67 SP:FB CYC: 15 SL:120 +E7D2 C9 00 CMP #$00 A:00 X:AA Y:44 P:67 SP:FB CYC: 24 SL:120 +E7D4 F0 04 BEQ $E7DA A:00 X:AA Y:44 P:67 SP:FB CYC: 30 SL:120 +E7DA A9 00 LDA #$00 A:00 X:AA Y:44 P:67 SP:FB CYC: 39 SL:120 +E7DC 85 56 STA $56 = 00 A:00 X:AA Y:44 P:67 SP:FB CYC: 45 SL:120 +E7DE A0 58 LDY #$58 A:00 X:AA Y:44 P:67 SP:FB CYC: 54 SL:120 +E7E0 A2 EF LDX #$EF A:00 X:AA Y:58 P:65 SP:FB CYC: 60 SL:120 +E7E2 A9 66 LDA #$66 A:00 X:EF Y:58 P:E5 SP:FB CYC: 66 SL:120 +E7E4 38 SEC A:66 X:EF Y:58 P:65 SP:FB CYC: 72 SL:120 +E7E5 B8 CLV A:66 X:EF Y:58 P:65 SP:FB CYC: 78 SL:120 +E7E6 87 56 *SAX $56 = 00 A:66 X:EF Y:58 P:25 SP:FB CYC: 84 SL:120 +E7E8 EA NOP A:66 X:EF Y:58 P:25 SP:FB CYC: 93 SL:120 +E7E9 EA NOP A:66 X:EF Y:58 P:25 SP:FB CYC: 99 SL:120 +E7EA EA NOP A:66 X:EF Y:58 P:25 SP:FB CYC:105 SL:120 +E7EB EA NOP A:66 X:EF Y:58 P:25 SP:FB CYC:111 SL:120 +E7EC F0 18 BEQ $E806 A:66 X:EF Y:58 P:25 SP:FB CYC:117 SL:120 +E7EE 90 16 BCC $E806 A:66 X:EF Y:58 P:25 SP:FB CYC:123 SL:120 +E7F0 70 14 BVS $E806 A:66 X:EF Y:58 P:25 SP:FB CYC:129 SL:120 +E7F2 30 12 BMI $E806 A:66 X:EF Y:58 P:25 SP:FB CYC:135 SL:120 +E7F4 C9 66 CMP #$66 A:66 X:EF Y:58 P:25 SP:FB CYC:141 SL:120 +E7F6 D0 0E BNE $E806 A:66 X:EF Y:58 P:27 SP:FB CYC:147 SL:120 +E7F8 C0 58 CPY #$58 A:66 X:EF Y:58 P:27 SP:FB CYC:153 SL:120 +E7FA D0 0A BNE $E806 A:66 X:EF Y:58 P:27 SP:FB CYC:159 SL:120 +E7FC E0 EF CPX #$EF A:66 X:EF Y:58 P:27 SP:FB CYC:165 SL:120 +E7FE D0 06 BNE $E806 A:66 X:EF Y:58 P:27 SP:FB CYC:171 SL:120 +E800 A5 56 LDA $56 = 66 A:66 X:EF Y:58 P:27 SP:FB CYC:177 SL:120 +E802 C9 66 CMP #$66 A:66 X:EF Y:58 P:25 SP:FB CYC:186 SL:120 +E804 F0 04 BEQ $E80A A:66 X:EF Y:58 P:27 SP:FB CYC:192 SL:120 +E80A A9 FF LDA #$FF A:66 X:EF Y:58 P:27 SP:FB CYC:201 SL:120 +E80C 8D 49 05 STA $0549 = 00 A:FF X:EF Y:58 P:A5 SP:FB CYC:207 SL:120 +E80F A0 E5 LDY #$E5 A:FF X:EF Y:58 P:A5 SP:FB CYC:219 SL:120 +E811 A2 AF LDX #$AF A:FF X:EF Y:E5 P:A5 SP:FB CYC:225 SL:120 +E813 A9 F5 LDA #$F5 A:FF X:AF Y:E5 P:A5 SP:FB CYC:231 SL:120 +E815 24 01 BIT $01 = C0 A:F5 X:AF Y:E5 P:A5 SP:FB CYC:237 SL:120 +E817 18 CLC A:F5 X:AF Y:E5 P:E5 SP:FB CYC:246 SL:120 +E818 8F 49 05 *SAX $0549 = FF A:F5 X:AF Y:E5 P:E4 SP:FB CYC:252 SL:120 +E81B EA NOP A:F5 X:AF Y:E5 P:E4 SP:FB CYC:264 SL:120 +E81C EA NOP A:F5 X:AF Y:E5 P:E4 SP:FB CYC:270 SL:120 +E81D EA NOP A:F5 X:AF Y:E5 P:E4 SP:FB CYC:276 SL:120 +E81E EA NOP A:F5 X:AF Y:E5 P:E4 SP:FB CYC:282 SL:120 +E81F F0 19 BEQ $E83A A:F5 X:AF Y:E5 P:E4 SP:FB CYC:288 SL:120 +E821 B0 17 BCS $E83A A:F5 X:AF Y:E5 P:E4 SP:FB CYC:294 SL:120 +E823 50 15 BVC $E83A A:F5 X:AF Y:E5 P:E4 SP:FB CYC:300 SL:120 +E825 10 13 BPL $E83A A:F5 X:AF Y:E5 P:E4 SP:FB CYC:306 SL:120 +E827 C9 F5 CMP #$F5 A:F5 X:AF Y:E5 P:E4 SP:FB CYC:312 SL:120 +E829 D0 0F BNE $E83A A:F5 X:AF Y:E5 P:67 SP:FB CYC:318 SL:120 +E82B C0 E5 CPY #$E5 A:F5 X:AF Y:E5 P:67 SP:FB CYC:324 SL:120 +E82D D0 0B BNE $E83A A:F5 X:AF Y:E5 P:67 SP:FB CYC:330 SL:120 +E82F E0 AF CPX #$AF A:F5 X:AF Y:E5 P:67 SP:FB CYC:336 SL:120 +E831 D0 07 BNE $E83A A:F5 X:AF Y:E5 P:67 SP:FB CYC: 1 SL:121 +E833 AD 49 05 LDA $0549 = A5 A:F5 X:AF Y:E5 P:67 SP:FB CYC: 7 SL:121 +E836 C9 A5 CMP #$A5 A:A5 X:AF Y:E5 P:E5 SP:FB CYC: 19 SL:121 +E838 F0 04 BEQ $E83E A:A5 X:AF Y:E5 P:67 SP:FB CYC: 25 SL:121 +E83E A9 00 LDA #$00 A:A5 X:AF Y:E5 P:67 SP:FB CYC: 34 SL:121 +E840 8D 56 05 STA $0556 = 00 A:00 X:AF Y:E5 P:67 SP:FB CYC: 40 SL:121 +E843 A0 58 LDY #$58 A:00 X:AF Y:E5 P:67 SP:FB CYC: 52 SL:121 +E845 A2 B3 LDX #$B3 A:00 X:AF Y:58 P:65 SP:FB CYC: 58 SL:121 +E847 A9 97 LDA #$97 A:00 X:B3 Y:58 P:E5 SP:FB CYC: 64 SL:121 +E849 38 SEC A:97 X:B3 Y:58 P:E5 SP:FB CYC: 70 SL:121 +E84A B8 CLV A:97 X:B3 Y:58 P:E5 SP:FB CYC: 76 SL:121 +E84B 8F 56 05 *SAX $0556 = 00 A:97 X:B3 Y:58 P:A5 SP:FB CYC: 82 SL:121 +E84E EA NOP A:97 X:B3 Y:58 P:A5 SP:FB CYC: 94 SL:121 +E84F EA NOP A:97 X:B3 Y:58 P:A5 SP:FB CYC:100 SL:121 +E850 EA NOP A:97 X:B3 Y:58 P:A5 SP:FB CYC:106 SL:121 +E851 EA NOP A:97 X:B3 Y:58 P:A5 SP:FB CYC:112 SL:121 +E852 F0 19 BEQ $E86D A:97 X:B3 Y:58 P:A5 SP:FB CYC:118 SL:121 +E854 90 17 BCC $E86D A:97 X:B3 Y:58 P:A5 SP:FB CYC:124 SL:121 +E856 70 15 BVS $E86D A:97 X:B3 Y:58 P:A5 SP:FB CYC:130 SL:121 +E858 10 13 BPL $E86D A:97 X:B3 Y:58 P:A5 SP:FB CYC:136 SL:121 +E85A C9 97 CMP #$97 A:97 X:B3 Y:58 P:A5 SP:FB CYC:142 SL:121 +E85C D0 0F BNE $E86D A:97 X:B3 Y:58 P:27 SP:FB CYC:148 SL:121 +E85E C0 58 CPY #$58 A:97 X:B3 Y:58 P:27 SP:FB CYC:154 SL:121 +E860 D0 0B BNE $E86D A:97 X:B3 Y:58 P:27 SP:FB CYC:160 SL:121 +E862 E0 B3 CPX #$B3 A:97 X:B3 Y:58 P:27 SP:FB CYC:166 SL:121 +E864 D0 07 BNE $E86D A:97 X:B3 Y:58 P:27 SP:FB CYC:172 SL:121 +E866 AD 56 05 LDA $0556 = 93 A:97 X:B3 Y:58 P:27 SP:FB CYC:178 SL:121 +E869 C9 93 CMP #$93 A:93 X:B3 Y:58 P:A5 SP:FB CYC:190 SL:121 +E86B F0 04 BEQ $E871 A:93 X:B3 Y:58 P:27 SP:FB CYC:196 SL:121 +E871 A9 FF LDA #$FF A:93 X:B3 Y:58 P:27 SP:FB CYC:205 SL:121 +E873 85 49 STA $49 = 00 A:FF X:B3 Y:58 P:A5 SP:FB CYC:211 SL:121 +E875 A0 FF LDY #$FF A:FF X:B3 Y:58 P:A5 SP:FB CYC:220 SL:121 +E877 A2 AA LDX #$AA A:FF X:B3 Y:FF P:A5 SP:FB CYC:226 SL:121 +E879 A9 55 LDA #$55 A:FF X:AA Y:FF P:A5 SP:FB CYC:232 SL:121 +E87B 24 01 BIT $01 = C0 A:55 X:AA Y:FF P:25 SP:FB CYC:238 SL:121 +E87D 18 CLC A:55 X:AA Y:FF P:E5 SP:FB CYC:247 SL:121 +E87E 97 4A *SAX $4A,Y @ 49 = FF A:55 X:AA Y:FF P:E4 SP:FB CYC:253 SL:121 +E880 EA NOP A:55 X:AA Y:FF P:E4 SP:FB CYC:265 SL:121 +E881 EA NOP A:55 X:AA Y:FF P:E4 SP:FB CYC:271 SL:121 +E882 EA NOP A:55 X:AA Y:FF P:E4 SP:FB CYC:277 SL:121 +E883 EA NOP A:55 X:AA Y:FF P:E4 SP:FB CYC:283 SL:121 +E884 F0 18 BEQ $E89E A:55 X:AA Y:FF P:E4 SP:FB CYC:289 SL:121 +E886 B0 16 BCS $E89E A:55 X:AA Y:FF P:E4 SP:FB CYC:295 SL:121 +E888 50 14 BVC $E89E A:55 X:AA Y:FF P:E4 SP:FB CYC:301 SL:121 +E88A 10 12 BPL $E89E A:55 X:AA Y:FF P:E4 SP:FB CYC:307 SL:121 +E88C C9 55 CMP #$55 A:55 X:AA Y:FF P:E4 SP:FB CYC:313 SL:121 +E88E D0 0E BNE $E89E A:55 X:AA Y:FF P:67 SP:FB CYC:319 SL:121 +E890 C0 FF CPY #$FF A:55 X:AA Y:FF P:67 SP:FB CYC:325 SL:121 +E892 D0 0A BNE $E89E A:55 X:AA Y:FF P:67 SP:FB CYC:331 SL:121 +E894 E0 AA CPX #$AA A:55 X:AA Y:FF P:67 SP:FB CYC:337 SL:121 +E896 D0 06 BNE $E89E A:55 X:AA Y:FF P:67 SP:FB CYC: 2 SL:122 +E898 A5 49 LDA $49 = 00 A:55 X:AA Y:FF P:67 SP:FB CYC: 8 SL:122 +E89A C9 00 CMP #$00 A:00 X:AA Y:FF P:67 SP:FB CYC: 17 SL:122 +E89C F0 04 BEQ $E8A2 A:00 X:AA Y:FF P:67 SP:FB CYC: 23 SL:122 +E8A2 A9 00 LDA #$00 A:00 X:AA Y:FF P:67 SP:FB CYC: 32 SL:122 +E8A4 85 56 STA $56 = 66 A:00 X:AA Y:FF P:67 SP:FB CYC: 38 SL:122 +E8A6 A0 06 LDY #$06 A:00 X:AA Y:FF P:67 SP:FB CYC: 47 SL:122 +E8A8 A2 EF LDX #$EF A:00 X:AA Y:06 P:65 SP:FB CYC: 53 SL:122 +E8AA A9 66 LDA #$66 A:00 X:EF Y:06 P:E5 SP:FB CYC: 59 SL:122 +E8AC 38 SEC A:66 X:EF Y:06 P:65 SP:FB CYC: 65 SL:122 +E8AD B8 CLV A:66 X:EF Y:06 P:65 SP:FB CYC: 71 SL:122 +E8AE 97 50 *SAX $50,Y @ 56 = 00 A:66 X:EF Y:06 P:25 SP:FB CYC: 77 SL:122 +E8B0 EA NOP A:66 X:EF Y:06 P:25 SP:FB CYC: 89 SL:122 +E8B1 EA NOP A:66 X:EF Y:06 P:25 SP:FB CYC: 95 SL:122 +E8B2 EA NOP A:66 X:EF Y:06 P:25 SP:FB CYC:101 SL:122 +E8B3 EA NOP A:66 X:EF Y:06 P:25 SP:FB CYC:107 SL:122 +E8B4 F0 18 BEQ $E8CE A:66 X:EF Y:06 P:25 SP:FB CYC:113 SL:122 +E8B6 90 16 BCC $E8CE A:66 X:EF Y:06 P:25 SP:FB CYC:119 SL:122 +E8B8 70 14 BVS $E8CE A:66 X:EF Y:06 P:25 SP:FB CYC:125 SL:122 +E8BA 30 12 BMI $E8CE A:66 X:EF Y:06 P:25 SP:FB CYC:131 SL:122 +E8BC C9 66 CMP #$66 A:66 X:EF Y:06 P:25 SP:FB CYC:137 SL:122 +E8BE D0 0E BNE $E8CE A:66 X:EF Y:06 P:27 SP:FB CYC:143 SL:122 +E8C0 C0 06 CPY #$06 A:66 X:EF Y:06 P:27 SP:FB CYC:149 SL:122 +E8C2 D0 0A BNE $E8CE A:66 X:EF Y:06 P:27 SP:FB CYC:155 SL:122 +E8C4 E0 EF CPX #$EF A:66 X:EF Y:06 P:27 SP:FB CYC:161 SL:122 +E8C6 D0 06 BNE $E8CE A:66 X:EF Y:06 P:27 SP:FB CYC:167 SL:122 +E8C8 A5 56 LDA $56 = 66 A:66 X:EF Y:06 P:27 SP:FB CYC:173 SL:122 +E8CA C9 66 CMP #$66 A:66 X:EF Y:06 P:25 SP:FB CYC:182 SL:122 +E8CC F0 04 BEQ $E8D2 A:66 X:EF Y:06 P:27 SP:FB CYC:188 SL:122 +E8D2 60 RTS A:66 X:EF Y:06 P:27 SP:FB CYC:197 SL:122 +C638 20 D3 E8 JSR $E8D3 A:66 X:EF Y:06 P:27 SP:FD CYC:215 SL:122 +E8D3 A0 90 LDY #$90 A:66 X:EF Y:06 P:27 SP:FB CYC:233 SL:122 +E8D5 20 31 F9 JSR $F931 A:66 X:EF Y:90 P:A5 SP:FB CYC:239 SL:122 +F931 24 01 BIT $01 = C0 A:66 X:EF Y:90 P:A5 SP:F9 CYC:257 SL:122 +F933 A9 40 LDA #$40 A:66 X:EF Y:90 P:E5 SP:F9 CYC:266 SL:122 +F935 38 SEC A:40 X:EF Y:90 P:65 SP:F9 CYC:272 SL:122 +F936 60 RTS A:40 X:EF Y:90 P:65 SP:F9 CYC:278 SL:122 +E8D8 EB 40 *SBC #$40 A:40 X:EF Y:90 P:65 SP:FB CYC:296 SL:122 +E8DA EA NOP A:00 X:EF Y:90 P:27 SP:FB CYC:302 SL:122 +E8DB EA NOP A:00 X:EF Y:90 P:27 SP:FB CYC:308 SL:122 +E8DC EA NOP A:00 X:EF Y:90 P:27 SP:FB CYC:314 SL:122 +E8DD EA NOP A:00 X:EF Y:90 P:27 SP:FB CYC:320 SL:122 +E8DE 20 37 F9 JSR $F937 A:00 X:EF Y:90 P:27 SP:FB CYC:326 SL:122 +F937 30 0B BMI $F944 A:00 X:EF Y:90 P:27 SP:F9 CYC: 3 SL:123 +F939 90 09 BCC $F944 A:00 X:EF Y:90 P:27 SP:F9 CYC: 9 SL:123 +F93B D0 07 BNE $F944 A:00 X:EF Y:90 P:27 SP:F9 CYC: 15 SL:123 +F93D 70 05 BVS $F944 A:00 X:EF Y:90 P:27 SP:F9 CYC: 21 SL:123 +F93F C9 00 CMP #$00 A:00 X:EF Y:90 P:27 SP:F9 CYC: 27 SL:123 +F941 D0 01 BNE $F944 A:00 X:EF Y:90 P:27 SP:F9 CYC: 33 SL:123 +F943 60 RTS A:00 X:EF Y:90 P:27 SP:F9 CYC: 39 SL:123 +E8E1 C8 INY A:00 X:EF Y:90 P:27 SP:FB CYC: 57 SL:123 +E8E2 20 47 F9 JSR $F947 A:00 X:EF Y:91 P:A5 SP:FB CYC: 63 SL:123 +F947 B8 CLV A:00 X:EF Y:91 P:A5 SP:F9 CYC: 81 SL:123 +F948 38 SEC A:00 X:EF Y:91 P:A5 SP:F9 CYC: 87 SL:123 +F949 A9 40 LDA #$40 A:00 X:EF Y:91 P:A5 SP:F9 CYC: 93 SL:123 +F94B 60 RTS A:40 X:EF Y:91 P:25 SP:F9 CYC: 99 SL:123 +E8E5 EB 3F *SBC #$3F A:40 X:EF Y:91 P:25 SP:FB CYC:117 SL:123 +E8E7 EA NOP A:01 X:EF Y:91 P:25 SP:FB CYC:123 SL:123 +E8E8 EA NOP A:01 X:EF Y:91 P:25 SP:FB CYC:129 SL:123 +E8E9 EA NOP A:01 X:EF Y:91 P:25 SP:FB CYC:135 SL:123 +E8EA EA NOP A:01 X:EF Y:91 P:25 SP:FB CYC:141 SL:123 +E8EB 20 4C F9 JSR $F94C A:01 X:EF Y:91 P:25 SP:FB CYC:147 SL:123 +F94C F0 0B BEQ $F959 A:01 X:EF Y:91 P:25 SP:F9 CYC:165 SL:123 +F94E 30 09 BMI $F959 A:01 X:EF Y:91 P:25 SP:F9 CYC:171 SL:123 +F950 90 07 BCC $F959 A:01 X:EF Y:91 P:25 SP:F9 CYC:177 SL:123 +F952 70 05 BVS $F959 A:01 X:EF Y:91 P:25 SP:F9 CYC:183 SL:123 +F954 C9 01 CMP #$01 A:01 X:EF Y:91 P:25 SP:F9 CYC:189 SL:123 +F956 D0 01 BNE $F959 A:01 X:EF Y:91 P:27 SP:F9 CYC:195 SL:123 +F958 60 RTS A:01 X:EF Y:91 P:27 SP:F9 CYC:201 SL:123 +E8EE C8 INY A:01 X:EF Y:91 P:27 SP:FB CYC:219 SL:123 +E8EF 20 5C F9 JSR $F95C A:01 X:EF Y:92 P:A5 SP:FB CYC:225 SL:123 +F95C A9 40 LDA #$40 A:01 X:EF Y:92 P:A5 SP:F9 CYC:243 SL:123 +F95E 38 SEC A:40 X:EF Y:92 P:25 SP:F9 CYC:249 SL:123 +F95F 24 01 BIT $01 = C0 A:40 X:EF Y:92 P:25 SP:F9 CYC:255 SL:123 +F961 60 RTS A:40 X:EF Y:92 P:E5 SP:F9 CYC:264 SL:123 +E8F2 EB 41 *SBC #$41 A:40 X:EF Y:92 P:E5 SP:FB CYC:282 SL:123 +E8F4 EA NOP A:FF X:EF Y:92 P:A4 SP:FB CYC:288 SL:123 +E8F5 EA NOP A:FF X:EF Y:92 P:A4 SP:FB CYC:294 SL:123 +E8F6 EA NOP A:FF X:EF Y:92 P:A4 SP:FB CYC:300 SL:123 +E8F7 EA NOP A:FF X:EF Y:92 P:A4 SP:FB CYC:306 SL:123 +E8F8 20 62 F9 JSR $F962 A:FF X:EF Y:92 P:A4 SP:FB CYC:312 SL:123 +F962 B0 0B BCS $F96F A:FF X:EF Y:92 P:A4 SP:F9 CYC:330 SL:123 +F964 F0 09 BEQ $F96F A:FF X:EF Y:92 P:A4 SP:F9 CYC:336 SL:123 +F966 10 07 BPL $F96F A:FF X:EF Y:92 P:A4 SP:F9 CYC: 1 SL:124 +F968 70 05 BVS $F96F A:FF X:EF Y:92 P:A4 SP:F9 CYC: 7 SL:124 +F96A C9 FF CMP #$FF A:FF X:EF Y:92 P:A4 SP:F9 CYC: 13 SL:124 +F96C D0 01 BNE $F96F A:FF X:EF Y:92 P:27 SP:F9 CYC: 19 SL:124 +F96E 60 RTS A:FF X:EF Y:92 P:27 SP:F9 CYC: 25 SL:124 +E8FB C8 INY A:FF X:EF Y:92 P:27 SP:FB CYC: 43 SL:124 +E8FC 20 72 F9 JSR $F972 A:FF X:EF Y:93 P:A5 SP:FB CYC: 49 SL:124 +F972 18 CLC A:FF X:EF Y:93 P:A5 SP:F9 CYC: 67 SL:124 +F973 A9 80 LDA #$80 A:FF X:EF Y:93 P:A4 SP:F9 CYC: 73 SL:124 +F975 60 RTS A:80 X:EF Y:93 P:A4 SP:F9 CYC: 79 SL:124 +E8FF EB 00 *SBC #$00 A:80 X:EF Y:93 P:A4 SP:FB CYC: 97 SL:124 +E901 EA NOP A:7F X:EF Y:93 P:65 SP:FB CYC:103 SL:124 +E902 EA NOP A:7F X:EF Y:93 P:65 SP:FB CYC:109 SL:124 +E903 EA NOP A:7F X:EF Y:93 P:65 SP:FB CYC:115 SL:124 +E904 EA NOP A:7F X:EF Y:93 P:65 SP:FB CYC:121 SL:124 +E905 20 76 F9 JSR $F976 A:7F X:EF Y:93 P:65 SP:FB CYC:127 SL:124 +F976 90 05 BCC $F97D A:7F X:EF Y:93 P:65 SP:F9 CYC:145 SL:124 +F978 C9 7F CMP #$7F A:7F X:EF Y:93 P:65 SP:F9 CYC:151 SL:124 +F97A D0 01 BNE $F97D A:7F X:EF Y:93 P:67 SP:F9 CYC:157 SL:124 +F97C 60 RTS A:7F X:EF Y:93 P:67 SP:F9 CYC:163 SL:124 +E908 C8 INY A:7F X:EF Y:93 P:67 SP:FB CYC:181 SL:124 +E909 20 80 F9 JSR $F980 A:7F X:EF Y:94 P:E5 SP:FB CYC:187 SL:124 +F980 38 SEC A:7F X:EF Y:94 P:E5 SP:F9 CYC:205 SL:124 +F981 A9 81 LDA #$81 A:7F X:EF Y:94 P:E5 SP:F9 CYC:211 SL:124 +F983 60 RTS A:81 X:EF Y:94 P:E5 SP:F9 CYC:217 SL:124 +E90C EB 7F *SBC #$7F A:81 X:EF Y:94 P:E5 SP:FB CYC:235 SL:124 +E90E EA NOP A:02 X:EF Y:94 P:65 SP:FB CYC:241 SL:124 +E90F EA NOP A:02 X:EF Y:94 P:65 SP:FB CYC:247 SL:124 +E910 EA NOP A:02 X:EF Y:94 P:65 SP:FB CYC:253 SL:124 +E911 EA NOP A:02 X:EF Y:94 P:65 SP:FB CYC:259 SL:124 +E912 20 84 F9 JSR $F984 A:02 X:EF Y:94 P:65 SP:FB CYC:265 SL:124 +F984 50 07 BVC $F98D A:02 X:EF Y:94 P:65 SP:F9 CYC:283 SL:124 +F986 90 05 BCC $F98D A:02 X:EF Y:94 P:65 SP:F9 CYC:289 SL:124 +F988 C9 02 CMP #$02 A:02 X:EF Y:94 P:65 SP:F9 CYC:295 SL:124 +F98A D0 01 BNE $F98D A:02 X:EF Y:94 P:67 SP:F9 CYC:301 SL:124 +F98C 60 RTS A:02 X:EF Y:94 P:67 SP:F9 CYC:307 SL:124 +E915 60 RTS A:02 X:EF Y:94 P:67 SP:FB CYC:325 SL:124 +C63B 20 16 E9 JSR $E916 A:02 X:EF Y:94 P:67 SP:FD CYC: 2 SL:125 +E916 A9 FF LDA #$FF A:02 X:EF Y:94 P:67 SP:FB CYC: 20 SL:125 +E918 85 01 STA $01 = C0 A:FF X:EF Y:94 P:E5 SP:FB CYC: 26 SL:125 +E91A A0 95 LDY #$95 A:FF X:EF Y:94 P:E5 SP:FB CYC: 35 SL:125 +E91C A2 02 LDX #$02 A:FF X:EF Y:95 P:E5 SP:FB CYC: 41 SL:125 +E91E A9 47 LDA #$47 A:FF X:02 Y:95 P:65 SP:FB CYC: 47 SL:125 +E920 85 47 STA $47 = 00 A:47 X:02 Y:95 P:65 SP:FB CYC: 53 SL:125 +E922 A9 06 LDA #$06 A:47 X:02 Y:95 P:65 SP:FB CYC: 62 SL:125 +E924 85 48 STA $48 = 00 A:06 X:02 Y:95 P:65 SP:FB CYC: 68 SL:125 +E926 A9 EB LDA #$EB A:06 X:02 Y:95 P:65 SP:FB CYC: 77 SL:125 +E928 8D 47 06 STA $0647 = 00 A:EB X:02 Y:95 P:E5 SP:FB CYC: 83 SL:125 +E92B 20 31 FA JSR $FA31 A:EB X:02 Y:95 P:E5 SP:FB CYC: 95 SL:125 +FA31 24 01 BIT $01 = FF A:EB X:02 Y:95 P:E5 SP:F9 CYC:113 SL:125 +FA33 18 CLC A:EB X:02 Y:95 P:E5 SP:F9 CYC:122 SL:125 +FA34 A9 40 LDA #$40 A:EB X:02 Y:95 P:E4 SP:F9 CYC:128 SL:125 +FA36 60 RTS A:40 X:02 Y:95 P:64 SP:F9 CYC:134 SL:125 +E92E C3 45 *DCP ($45,X) @ 47 = 0647 = EB A:40 X:02 Y:95 P:64 SP:FB CYC:152 SL:125 +E930 EA NOP A:40 X:02 Y:95 P:64 SP:FB CYC:176 SL:125 +E931 EA NOP A:40 X:02 Y:95 P:64 SP:FB CYC:182 SL:125 +E932 EA NOP A:40 X:02 Y:95 P:64 SP:FB CYC:188 SL:125 +E933 EA NOP A:40 X:02 Y:95 P:64 SP:FB CYC:194 SL:125 +E934 20 37 FA JSR $FA37 A:40 X:02 Y:95 P:64 SP:FB CYC:200 SL:125 +FA37 50 2C BVC $FA65 A:40 X:02 Y:95 P:64 SP:F9 CYC:218 SL:125 +FA39 B0 2A BCS $FA65 A:40 X:02 Y:95 P:64 SP:F9 CYC:224 SL:125 +FA3B 30 28 BMI $FA65 A:40 X:02 Y:95 P:64 SP:F9 CYC:230 SL:125 +FA3D C9 40 CMP #$40 A:40 X:02 Y:95 P:64 SP:F9 CYC:236 SL:125 +FA3F D0 24 BNE $FA65 A:40 X:02 Y:95 P:67 SP:F9 CYC:242 SL:125 +FA41 60 RTS A:40 X:02 Y:95 P:67 SP:F9 CYC:248 SL:125 +E937 AD 47 06 LDA $0647 = EA A:40 X:02 Y:95 P:67 SP:FB CYC:266 SL:125 +E93A C9 EA CMP #$EA A:EA X:02 Y:95 P:E5 SP:FB CYC:278 SL:125 +E93C F0 02 BEQ $E940 A:EA X:02 Y:95 P:67 SP:FB CYC:284 SL:125 +E940 C8 INY A:EA X:02 Y:95 P:67 SP:FB CYC:293 SL:125 +E941 A9 00 LDA #$00 A:EA X:02 Y:96 P:E5 SP:FB CYC:299 SL:125 +E943 8D 47 06 STA $0647 = EA A:00 X:02 Y:96 P:67 SP:FB CYC:305 SL:125 +E946 20 42 FA JSR $FA42 A:00 X:02 Y:96 P:67 SP:FB CYC:317 SL:125 +FA42 B8 CLV A:00 X:02 Y:96 P:67 SP:F9 CYC:335 SL:125 +FA43 38 SEC A:00 X:02 Y:96 P:27 SP:F9 CYC: 0 SL:126 +FA44 A9 FF LDA #$FF A:00 X:02 Y:96 P:27 SP:F9 CYC: 6 SL:126 +FA46 60 RTS A:FF X:02 Y:96 P:A5 SP:F9 CYC: 12 SL:126 +E949 C3 45 *DCP ($45,X) @ 47 = 0647 = 00 A:FF X:02 Y:96 P:A5 SP:FB CYC: 30 SL:126 +E94B EA NOP A:FF X:02 Y:96 P:27 SP:FB CYC: 54 SL:126 +E94C EA NOP A:FF X:02 Y:96 P:27 SP:FB CYC: 60 SL:126 +E94D EA NOP A:FF X:02 Y:96 P:27 SP:FB CYC: 66 SL:126 +E94E EA NOP A:FF X:02 Y:96 P:27 SP:FB CYC: 72 SL:126 +E94F 20 47 FA JSR $FA47 A:FF X:02 Y:96 P:27 SP:FB CYC: 78 SL:126 +FA47 70 1C BVS $FA65 A:FF X:02 Y:96 P:27 SP:F9 CYC: 96 SL:126 +FA49 D0 1A BNE $FA65 A:FF X:02 Y:96 P:27 SP:F9 CYC:102 SL:126 +FA4B 30 18 BMI $FA65 A:FF X:02 Y:96 P:27 SP:F9 CYC:108 SL:126 +FA4D 90 16 BCC $FA65 A:FF X:02 Y:96 P:27 SP:F9 CYC:114 SL:126 +FA4F C9 FF CMP #$FF A:FF X:02 Y:96 P:27 SP:F9 CYC:120 SL:126 +FA51 D0 12 BNE $FA65 A:FF X:02 Y:96 P:27 SP:F9 CYC:126 SL:126 +FA53 60 RTS A:FF X:02 Y:96 P:27 SP:F9 CYC:132 SL:126 +E952 AD 47 06 LDA $0647 = FF A:FF X:02 Y:96 P:27 SP:FB CYC:150 SL:126 +E955 C9 FF CMP #$FF A:FF X:02 Y:96 P:A5 SP:FB CYC:162 SL:126 +E957 F0 02 BEQ $E95B A:FF X:02 Y:96 P:27 SP:FB CYC:168 SL:126 +E95B C8 INY A:FF X:02 Y:96 P:27 SP:FB CYC:177 SL:126 +E95C A9 37 LDA #$37 A:FF X:02 Y:97 P:A5 SP:FB CYC:183 SL:126 +E95E 8D 47 06 STA $0647 = FF A:37 X:02 Y:97 P:25 SP:FB CYC:189 SL:126 +E961 20 54 FA JSR $FA54 A:37 X:02 Y:97 P:25 SP:FB CYC:201 SL:126 +FA54 24 01 BIT $01 = FF A:37 X:02 Y:97 P:25 SP:F9 CYC:219 SL:126 +FA56 A9 F0 LDA #$F0 A:37 X:02 Y:97 P:E5 SP:F9 CYC:228 SL:126 +FA58 60 RTS A:F0 X:02 Y:97 P:E5 SP:F9 CYC:234 SL:126 +E964 C3 45 *DCP ($45,X) @ 47 = 0647 = 37 A:F0 X:02 Y:97 P:E5 SP:FB CYC:252 SL:126 +E966 EA NOP A:F0 X:02 Y:97 P:E5 SP:FB CYC:276 SL:126 +E967 EA NOP A:F0 X:02 Y:97 P:E5 SP:FB CYC:282 SL:126 +E968 EA NOP A:F0 X:02 Y:97 P:E5 SP:FB CYC:288 SL:126 +E969 EA NOP A:F0 X:02 Y:97 P:E5 SP:FB CYC:294 SL:126 +E96A 20 59 FA JSR $FA59 A:F0 X:02 Y:97 P:E5 SP:FB CYC:300 SL:126 +FA59 50 0A BVC $FA65 A:F0 X:02 Y:97 P:E5 SP:F9 CYC:318 SL:126 +FA5B F0 08 BEQ $FA65 A:F0 X:02 Y:97 P:E5 SP:F9 CYC:324 SL:126 +FA5D 10 06 BPL $FA65 A:F0 X:02 Y:97 P:E5 SP:F9 CYC:330 SL:126 +FA5F 90 04 BCC $FA65 A:F0 X:02 Y:97 P:E5 SP:F9 CYC:336 SL:126 +FA61 C9 F0 CMP #$F0 A:F0 X:02 Y:97 P:E5 SP:F9 CYC: 1 SL:127 +FA63 F0 02 BEQ $FA67 A:F0 X:02 Y:97 P:67 SP:F9 CYC: 7 SL:127 +FA67 60 RTS A:F0 X:02 Y:97 P:67 SP:F9 CYC: 16 SL:127 +E96D AD 47 06 LDA $0647 = 36 A:F0 X:02 Y:97 P:67 SP:FB CYC: 34 SL:127 +E970 C9 36 CMP #$36 A:36 X:02 Y:97 P:65 SP:FB CYC: 46 SL:127 +E972 F0 02 BEQ $E976 A:36 X:02 Y:97 P:67 SP:FB CYC: 52 SL:127 +E976 C8 INY A:36 X:02 Y:97 P:67 SP:FB CYC: 61 SL:127 +E977 A9 EB LDA #$EB A:36 X:02 Y:98 P:E5 SP:FB CYC: 67 SL:127 +E979 85 47 STA $47 = 47 A:EB X:02 Y:98 P:E5 SP:FB CYC: 73 SL:127 +E97B 20 31 FA JSR $FA31 A:EB X:02 Y:98 P:E5 SP:FB CYC: 82 SL:127 +FA31 24 01 BIT $01 = FF A:EB X:02 Y:98 P:E5 SP:F9 CYC:100 SL:127 +FA33 18 CLC A:EB X:02 Y:98 P:E5 SP:F9 CYC:109 SL:127 +FA34 A9 40 LDA #$40 A:EB X:02 Y:98 P:E4 SP:F9 CYC:115 SL:127 +FA36 60 RTS A:40 X:02 Y:98 P:64 SP:F9 CYC:121 SL:127 +E97E C7 47 *DCP $47 = EB A:40 X:02 Y:98 P:64 SP:FB CYC:139 SL:127 +E980 EA NOP A:40 X:02 Y:98 P:64 SP:FB CYC:154 SL:127 +E981 EA NOP A:40 X:02 Y:98 P:64 SP:FB CYC:160 SL:127 +E982 EA NOP A:40 X:02 Y:98 P:64 SP:FB CYC:166 SL:127 +E983 EA NOP A:40 X:02 Y:98 P:64 SP:FB CYC:172 SL:127 +E984 20 37 FA JSR $FA37 A:40 X:02 Y:98 P:64 SP:FB CYC:178 SL:127 +FA37 50 2C BVC $FA65 A:40 X:02 Y:98 P:64 SP:F9 CYC:196 SL:127 +FA39 B0 2A BCS $FA65 A:40 X:02 Y:98 P:64 SP:F9 CYC:202 SL:127 +FA3B 30 28 BMI $FA65 A:40 X:02 Y:98 P:64 SP:F9 CYC:208 SL:127 +FA3D C9 40 CMP #$40 A:40 X:02 Y:98 P:64 SP:F9 CYC:214 SL:127 +FA3F D0 24 BNE $FA65 A:40 X:02 Y:98 P:67 SP:F9 CYC:220 SL:127 +FA41 60 RTS A:40 X:02 Y:98 P:67 SP:F9 CYC:226 SL:127 +E987 A5 47 LDA $47 = EA A:40 X:02 Y:98 P:67 SP:FB CYC:244 SL:127 +E989 C9 EA CMP #$EA A:EA X:02 Y:98 P:E5 SP:FB CYC:253 SL:127 +E98B F0 02 BEQ $E98F A:EA X:02 Y:98 P:67 SP:FB CYC:259 SL:127 +E98F C8 INY A:EA X:02 Y:98 P:67 SP:FB CYC:268 SL:127 +E990 A9 00 LDA #$00 A:EA X:02 Y:99 P:E5 SP:FB CYC:274 SL:127 +E992 85 47 STA $47 = EA A:00 X:02 Y:99 P:67 SP:FB CYC:280 SL:127 +E994 20 42 FA JSR $FA42 A:00 X:02 Y:99 P:67 SP:FB CYC:289 SL:127 +FA42 B8 CLV A:00 X:02 Y:99 P:67 SP:F9 CYC:307 SL:127 +FA43 38 SEC A:00 X:02 Y:99 P:27 SP:F9 CYC:313 SL:127 +FA44 A9 FF LDA #$FF A:00 X:02 Y:99 P:27 SP:F9 CYC:319 SL:127 +FA46 60 RTS A:FF X:02 Y:99 P:A5 SP:F9 CYC:325 SL:127 +E997 C7 47 *DCP $47 = 00 A:FF X:02 Y:99 P:A5 SP:FB CYC: 2 SL:128 +E999 EA NOP A:FF X:02 Y:99 P:27 SP:FB CYC: 17 SL:128 +E99A EA NOP A:FF X:02 Y:99 P:27 SP:FB CYC: 23 SL:128 +E99B EA NOP A:FF X:02 Y:99 P:27 SP:FB CYC: 29 SL:128 +E99C EA NOP A:FF X:02 Y:99 P:27 SP:FB CYC: 35 SL:128 +E99D 20 47 FA JSR $FA47 A:FF X:02 Y:99 P:27 SP:FB CYC: 41 SL:128 +FA47 70 1C BVS $FA65 A:FF X:02 Y:99 P:27 SP:F9 CYC: 59 SL:128 +FA49 D0 1A BNE $FA65 A:FF X:02 Y:99 P:27 SP:F9 CYC: 65 SL:128 +FA4B 30 18 BMI $FA65 A:FF X:02 Y:99 P:27 SP:F9 CYC: 71 SL:128 +FA4D 90 16 BCC $FA65 A:FF X:02 Y:99 P:27 SP:F9 CYC: 77 SL:128 +FA4F C9 FF CMP #$FF A:FF X:02 Y:99 P:27 SP:F9 CYC: 83 SL:128 +FA51 D0 12 BNE $FA65 A:FF X:02 Y:99 P:27 SP:F9 CYC: 89 SL:128 +FA53 60 RTS A:FF X:02 Y:99 P:27 SP:F9 CYC: 95 SL:128 +E9A0 A5 47 LDA $47 = FF A:FF X:02 Y:99 P:27 SP:FB CYC:113 SL:128 +E9A2 C9 FF CMP #$FF A:FF X:02 Y:99 P:A5 SP:FB CYC:122 SL:128 +E9A4 F0 02 BEQ $E9A8 A:FF X:02 Y:99 P:27 SP:FB CYC:128 SL:128 +E9A8 C8 INY A:FF X:02 Y:99 P:27 SP:FB CYC:137 SL:128 +E9A9 A9 37 LDA #$37 A:FF X:02 Y:9A P:A5 SP:FB CYC:143 SL:128 +E9AB 85 47 STA $47 = FF A:37 X:02 Y:9A P:25 SP:FB CYC:149 SL:128 +E9AD 20 54 FA JSR $FA54 A:37 X:02 Y:9A P:25 SP:FB CYC:158 SL:128 +FA54 24 01 BIT $01 = FF A:37 X:02 Y:9A P:25 SP:F9 CYC:176 SL:128 +FA56 A9 F0 LDA #$F0 A:37 X:02 Y:9A P:E5 SP:F9 CYC:185 SL:128 +FA58 60 RTS A:F0 X:02 Y:9A P:E5 SP:F9 CYC:191 SL:128 +E9B0 C7 47 *DCP $47 = 37 A:F0 X:02 Y:9A P:E5 SP:FB CYC:209 SL:128 +E9B2 EA NOP A:F0 X:02 Y:9A P:E5 SP:FB CYC:224 SL:128 +E9B3 EA NOP A:F0 X:02 Y:9A P:E5 SP:FB CYC:230 SL:128 +E9B4 EA NOP A:F0 X:02 Y:9A P:E5 SP:FB CYC:236 SL:128 +E9B5 EA NOP A:F0 X:02 Y:9A P:E5 SP:FB CYC:242 SL:128 +E9B6 20 59 FA JSR $FA59 A:F0 X:02 Y:9A P:E5 SP:FB CYC:248 SL:128 +FA59 50 0A BVC $FA65 A:F0 X:02 Y:9A P:E5 SP:F9 CYC:266 SL:128 +FA5B F0 08 BEQ $FA65 A:F0 X:02 Y:9A P:E5 SP:F9 CYC:272 SL:128 +FA5D 10 06 BPL $FA65 A:F0 X:02 Y:9A P:E5 SP:F9 CYC:278 SL:128 +FA5F 90 04 BCC $FA65 A:F0 X:02 Y:9A P:E5 SP:F9 CYC:284 SL:128 +FA61 C9 F0 CMP #$F0 A:F0 X:02 Y:9A P:E5 SP:F9 CYC:290 SL:128 +FA63 F0 02 BEQ $FA67 A:F0 X:02 Y:9A P:67 SP:F9 CYC:296 SL:128 +FA67 60 RTS A:F0 X:02 Y:9A P:67 SP:F9 CYC:305 SL:128 +E9B9 A5 47 LDA $47 = 36 A:F0 X:02 Y:9A P:67 SP:FB CYC:323 SL:128 +E9BB C9 36 CMP #$36 A:36 X:02 Y:9A P:65 SP:FB CYC:332 SL:128 +E9BD F0 02 BEQ $E9C1 A:36 X:02 Y:9A P:67 SP:FB CYC:338 SL:128 +E9C1 C8 INY A:36 X:02 Y:9A P:67 SP:FB CYC: 6 SL:129 +E9C2 A9 EB LDA #$EB A:36 X:02 Y:9B P:E5 SP:FB CYC: 12 SL:129 +E9C4 8D 47 06 STA $0647 = 36 A:EB X:02 Y:9B P:E5 SP:FB CYC: 18 SL:129 +E9C7 20 31 FA JSR $FA31 A:EB X:02 Y:9B P:E5 SP:FB CYC: 30 SL:129 +FA31 24 01 BIT $01 = FF A:EB X:02 Y:9B P:E5 SP:F9 CYC: 48 SL:129 +FA33 18 CLC A:EB X:02 Y:9B P:E5 SP:F9 CYC: 57 SL:129 +FA34 A9 40 LDA #$40 A:EB X:02 Y:9B P:E4 SP:F9 CYC: 63 SL:129 +FA36 60 RTS A:40 X:02 Y:9B P:64 SP:F9 CYC: 69 SL:129 +E9CA CF 47 06 *DCP $0647 = EB A:40 X:02 Y:9B P:64 SP:FB CYC: 87 SL:129 +E9CD EA NOP A:40 X:02 Y:9B P:64 SP:FB CYC:105 SL:129 +E9CE EA NOP A:40 X:02 Y:9B P:64 SP:FB CYC:111 SL:129 +E9CF EA NOP A:40 X:02 Y:9B P:64 SP:FB CYC:117 SL:129 +E9D0 EA NOP A:40 X:02 Y:9B P:64 SP:FB CYC:123 SL:129 +E9D1 20 37 FA JSR $FA37 A:40 X:02 Y:9B P:64 SP:FB CYC:129 SL:129 +FA37 50 2C BVC $FA65 A:40 X:02 Y:9B P:64 SP:F9 CYC:147 SL:129 +FA39 B0 2A BCS $FA65 A:40 X:02 Y:9B P:64 SP:F9 CYC:153 SL:129 +FA3B 30 28 BMI $FA65 A:40 X:02 Y:9B P:64 SP:F9 CYC:159 SL:129 +FA3D C9 40 CMP #$40 A:40 X:02 Y:9B P:64 SP:F9 CYC:165 SL:129 +FA3F D0 24 BNE $FA65 A:40 X:02 Y:9B P:67 SP:F9 CYC:171 SL:129 +FA41 60 RTS A:40 X:02 Y:9B P:67 SP:F9 CYC:177 SL:129 +E9D4 AD 47 06 LDA $0647 = EA A:40 X:02 Y:9B P:67 SP:FB CYC:195 SL:129 +E9D7 C9 EA CMP #$EA A:EA X:02 Y:9B P:E5 SP:FB CYC:207 SL:129 +E9D9 F0 02 BEQ $E9DD A:EA X:02 Y:9B P:67 SP:FB CYC:213 SL:129 +E9DD C8 INY A:EA X:02 Y:9B P:67 SP:FB CYC:222 SL:129 +E9DE A9 00 LDA #$00 A:EA X:02 Y:9C P:E5 SP:FB CYC:228 SL:129 +E9E0 8D 47 06 STA $0647 = EA A:00 X:02 Y:9C P:67 SP:FB CYC:234 SL:129 +E9E3 20 42 FA JSR $FA42 A:00 X:02 Y:9C P:67 SP:FB CYC:246 SL:129 +FA42 B8 CLV A:00 X:02 Y:9C P:67 SP:F9 CYC:264 SL:129 +FA43 38 SEC A:00 X:02 Y:9C P:27 SP:F9 CYC:270 SL:129 +FA44 A9 FF LDA #$FF A:00 X:02 Y:9C P:27 SP:F9 CYC:276 SL:129 +FA46 60 RTS A:FF X:02 Y:9C P:A5 SP:F9 CYC:282 SL:129 +E9E6 CF 47 06 *DCP $0647 = 00 A:FF X:02 Y:9C P:A5 SP:FB CYC:300 SL:129 +E9E9 EA NOP A:FF X:02 Y:9C P:27 SP:FB CYC:318 SL:129 +E9EA EA NOP A:FF X:02 Y:9C P:27 SP:FB CYC:324 SL:129 +E9EB EA NOP A:FF X:02 Y:9C P:27 SP:FB CYC:330 SL:129 +E9EC EA NOP A:FF X:02 Y:9C P:27 SP:FB CYC:336 SL:129 +E9ED 20 47 FA JSR $FA47 A:FF X:02 Y:9C P:27 SP:FB CYC: 1 SL:130 +FA47 70 1C BVS $FA65 A:FF X:02 Y:9C P:27 SP:F9 CYC: 19 SL:130 +FA49 D0 1A BNE $FA65 A:FF X:02 Y:9C P:27 SP:F9 CYC: 25 SL:130 +FA4B 30 18 BMI $FA65 A:FF X:02 Y:9C P:27 SP:F9 CYC: 31 SL:130 +FA4D 90 16 BCC $FA65 A:FF X:02 Y:9C P:27 SP:F9 CYC: 37 SL:130 +FA4F C9 FF CMP #$FF A:FF X:02 Y:9C P:27 SP:F9 CYC: 43 SL:130 +FA51 D0 12 BNE $FA65 A:FF X:02 Y:9C P:27 SP:F9 CYC: 49 SL:130 +FA53 60 RTS A:FF X:02 Y:9C P:27 SP:F9 CYC: 55 SL:130 +E9F0 AD 47 06 LDA $0647 = FF A:FF X:02 Y:9C P:27 SP:FB CYC: 73 SL:130 +E9F3 C9 FF CMP #$FF A:FF X:02 Y:9C P:A5 SP:FB CYC: 85 SL:130 +E9F5 F0 02 BEQ $E9F9 A:FF X:02 Y:9C P:27 SP:FB CYC: 91 SL:130 +E9F9 C8 INY A:FF X:02 Y:9C P:27 SP:FB CYC:100 SL:130 +E9FA A9 37 LDA #$37 A:FF X:02 Y:9D P:A5 SP:FB CYC:106 SL:130 +E9FC 8D 47 06 STA $0647 = FF A:37 X:02 Y:9D P:25 SP:FB CYC:112 SL:130 +E9FF 20 54 FA JSR $FA54 A:37 X:02 Y:9D P:25 SP:FB CYC:124 SL:130 +FA54 24 01 BIT $01 = FF A:37 X:02 Y:9D P:25 SP:F9 CYC:142 SL:130 +FA56 A9 F0 LDA #$F0 A:37 X:02 Y:9D P:E5 SP:F9 CYC:151 SL:130 +FA58 60 RTS A:F0 X:02 Y:9D P:E5 SP:F9 CYC:157 SL:130 +EA02 CF 47 06 *DCP $0647 = 37 A:F0 X:02 Y:9D P:E5 SP:FB CYC:175 SL:130 +EA05 EA NOP A:F0 X:02 Y:9D P:E5 SP:FB CYC:193 SL:130 +EA06 EA NOP A:F0 X:02 Y:9D P:E5 SP:FB CYC:199 SL:130 +EA07 EA NOP A:F0 X:02 Y:9D P:E5 SP:FB CYC:205 SL:130 +EA08 EA NOP A:F0 X:02 Y:9D P:E5 SP:FB CYC:211 SL:130 +EA09 20 59 FA JSR $FA59 A:F0 X:02 Y:9D P:E5 SP:FB CYC:217 SL:130 +FA59 50 0A BVC $FA65 A:F0 X:02 Y:9D P:E5 SP:F9 CYC:235 SL:130 +FA5B F0 08 BEQ $FA65 A:F0 X:02 Y:9D P:E5 SP:F9 CYC:241 SL:130 +FA5D 10 06 BPL $FA65 A:F0 X:02 Y:9D P:E5 SP:F9 CYC:247 SL:130 +FA5F 90 04 BCC $FA65 A:F0 X:02 Y:9D P:E5 SP:F9 CYC:253 SL:130 +FA61 C9 F0 CMP #$F0 A:F0 X:02 Y:9D P:E5 SP:F9 CYC:259 SL:130 +FA63 F0 02 BEQ $FA67 A:F0 X:02 Y:9D P:67 SP:F9 CYC:265 SL:130 +FA67 60 RTS A:F0 X:02 Y:9D P:67 SP:F9 CYC:274 SL:130 +EA0C AD 47 06 LDA $0647 = 36 A:F0 X:02 Y:9D P:67 SP:FB CYC:292 SL:130 +EA0F C9 36 CMP #$36 A:36 X:02 Y:9D P:65 SP:FB CYC:304 SL:130 +EA11 F0 02 BEQ $EA15 A:36 X:02 Y:9D P:67 SP:FB CYC:310 SL:130 +EA15 A9 EB LDA #$EB A:36 X:02 Y:9D P:67 SP:FB CYC:319 SL:130 +EA17 8D 47 06 STA $0647 = 36 A:EB X:02 Y:9D P:E5 SP:FB CYC:325 SL:130 +EA1A A9 48 LDA #$48 A:EB X:02 Y:9D P:E5 SP:FB CYC:337 SL:130 +EA1C 85 45 STA $45 = 32 A:48 X:02 Y:9D P:65 SP:FB CYC: 2 SL:131 +EA1E A9 05 LDA #$05 A:48 X:02 Y:9D P:65 SP:FB CYC: 11 SL:131 +EA20 85 46 STA $46 = 04 A:05 X:02 Y:9D P:65 SP:FB CYC: 17 SL:131 +EA22 A0 FF LDY #$FF A:05 X:02 Y:9D P:65 SP:FB CYC: 26 SL:131 +EA24 20 31 FA JSR $FA31 A:05 X:02 Y:FF P:E5 SP:FB CYC: 32 SL:131 +FA31 24 01 BIT $01 = FF A:05 X:02 Y:FF P:E5 SP:F9 CYC: 50 SL:131 +FA33 18 CLC A:05 X:02 Y:FF P:E5 SP:F9 CYC: 59 SL:131 +FA34 A9 40 LDA #$40 A:05 X:02 Y:FF P:E4 SP:F9 CYC: 65 SL:131 +FA36 60 RTS A:40 X:02 Y:FF P:64 SP:F9 CYC: 71 SL:131 +EA27 D3 45 *DCP ($45),Y = 0548 @ 0647 = EB A:40 X:02 Y:FF P:64 SP:FB CYC: 89 SL:131 +EA29 EA NOP A:40 X:02 Y:FF P:64 SP:FB CYC:113 SL:131 +EA2A EA NOP A:40 X:02 Y:FF P:64 SP:FB CYC:119 SL:131 +EA2B 08 PHP A:40 X:02 Y:FF P:64 SP:FB CYC:125 SL:131 +EA2C 48 PHA A:40 X:02 Y:FF P:64 SP:FA CYC:134 SL:131 +EA2D A0 9E LDY #$9E A:40 X:02 Y:FF P:64 SP:F9 CYC:143 SL:131 +EA2F 68 PLA A:40 X:02 Y:9E P:E4 SP:F9 CYC:149 SL:131 +EA30 28 PLP A:40 X:02 Y:9E P:64 SP:FA CYC:161 SL:131 +EA31 20 37 FA JSR $FA37 A:40 X:02 Y:9E P:64 SP:FB CYC:173 SL:131 +FA37 50 2C BVC $FA65 A:40 X:02 Y:9E P:64 SP:F9 CYC:191 SL:131 +FA39 B0 2A BCS $FA65 A:40 X:02 Y:9E P:64 SP:F9 CYC:197 SL:131 +FA3B 30 28 BMI $FA65 A:40 X:02 Y:9E P:64 SP:F9 CYC:203 SL:131 +FA3D C9 40 CMP #$40 A:40 X:02 Y:9E P:64 SP:F9 CYC:209 SL:131 +FA3F D0 24 BNE $FA65 A:40 X:02 Y:9E P:67 SP:F9 CYC:215 SL:131 +FA41 60 RTS A:40 X:02 Y:9E P:67 SP:F9 CYC:221 SL:131 +EA34 AD 47 06 LDA $0647 = EA A:40 X:02 Y:9E P:67 SP:FB CYC:239 SL:131 +EA37 C9 EA CMP #$EA A:EA X:02 Y:9E P:E5 SP:FB CYC:251 SL:131 +EA39 F0 02 BEQ $EA3D A:EA X:02 Y:9E P:67 SP:FB CYC:257 SL:131 +EA3D A0 FF LDY #$FF A:EA X:02 Y:9E P:67 SP:FB CYC:266 SL:131 +EA3F A9 00 LDA #$00 A:EA X:02 Y:FF P:E5 SP:FB CYC:272 SL:131 +EA41 8D 47 06 STA $0647 = EA A:00 X:02 Y:FF P:67 SP:FB CYC:278 SL:131 +EA44 20 42 FA JSR $FA42 A:00 X:02 Y:FF P:67 SP:FB CYC:290 SL:131 +FA42 B8 CLV A:00 X:02 Y:FF P:67 SP:F9 CYC:308 SL:131 +FA43 38 SEC A:00 X:02 Y:FF P:27 SP:F9 CYC:314 SL:131 +FA44 A9 FF LDA #$FF A:00 X:02 Y:FF P:27 SP:F9 CYC:320 SL:131 +FA46 60 RTS A:FF X:02 Y:FF P:A5 SP:F9 CYC:326 SL:131 +EA47 D3 45 *DCP ($45),Y = 0548 @ 0647 = 00 A:FF X:02 Y:FF P:A5 SP:FB CYC: 3 SL:132 +EA49 EA NOP A:FF X:02 Y:FF P:27 SP:FB CYC: 27 SL:132 +EA4A EA NOP A:FF X:02 Y:FF P:27 SP:FB CYC: 33 SL:132 +EA4B 08 PHP A:FF X:02 Y:FF P:27 SP:FB CYC: 39 SL:132 +EA4C 48 PHA A:FF X:02 Y:FF P:27 SP:FA CYC: 48 SL:132 +EA4D A0 9F LDY #$9F A:FF X:02 Y:FF P:27 SP:F9 CYC: 57 SL:132 +EA4F 68 PLA A:FF X:02 Y:9F P:A5 SP:F9 CYC: 63 SL:132 +EA50 28 PLP A:FF X:02 Y:9F P:A5 SP:FA CYC: 75 SL:132 +EA51 20 47 FA JSR $FA47 A:FF X:02 Y:9F P:27 SP:FB CYC: 87 SL:132 +FA47 70 1C BVS $FA65 A:FF X:02 Y:9F P:27 SP:F9 CYC:105 SL:132 +FA49 D0 1A BNE $FA65 A:FF X:02 Y:9F P:27 SP:F9 CYC:111 SL:132 +FA4B 30 18 BMI $FA65 A:FF X:02 Y:9F P:27 SP:F9 CYC:117 SL:132 +FA4D 90 16 BCC $FA65 A:FF X:02 Y:9F P:27 SP:F9 CYC:123 SL:132 +FA4F C9 FF CMP #$FF A:FF X:02 Y:9F P:27 SP:F9 CYC:129 SL:132 +FA51 D0 12 BNE $FA65 A:FF X:02 Y:9F P:27 SP:F9 CYC:135 SL:132 +FA53 60 RTS A:FF X:02 Y:9F P:27 SP:F9 CYC:141 SL:132 +EA54 AD 47 06 LDA $0647 = FF A:FF X:02 Y:9F P:27 SP:FB CYC:159 SL:132 +EA57 C9 FF CMP #$FF A:FF X:02 Y:9F P:A5 SP:FB CYC:171 SL:132 +EA59 F0 02 BEQ $EA5D A:FF X:02 Y:9F P:27 SP:FB CYC:177 SL:132 +EA5D A0 FF LDY #$FF A:FF X:02 Y:9F P:27 SP:FB CYC:186 SL:132 +EA5F A9 37 LDA #$37 A:FF X:02 Y:FF P:A5 SP:FB CYC:192 SL:132 +EA61 8D 47 06 STA $0647 = FF A:37 X:02 Y:FF P:25 SP:FB CYC:198 SL:132 +EA64 20 54 FA JSR $FA54 A:37 X:02 Y:FF P:25 SP:FB CYC:210 SL:132 +FA54 24 01 BIT $01 = FF A:37 X:02 Y:FF P:25 SP:F9 CYC:228 SL:132 +FA56 A9 F0 LDA #$F0 A:37 X:02 Y:FF P:E5 SP:F9 CYC:237 SL:132 +FA58 60 RTS A:F0 X:02 Y:FF P:E5 SP:F9 CYC:243 SL:132 +EA67 D3 45 *DCP ($45),Y = 0548 @ 0647 = 37 A:F0 X:02 Y:FF P:E5 SP:FB CYC:261 SL:132 +EA69 EA NOP A:F0 X:02 Y:FF P:E5 SP:FB CYC:285 SL:132 +EA6A EA NOP A:F0 X:02 Y:FF P:E5 SP:FB CYC:291 SL:132 +EA6B 08 PHP A:F0 X:02 Y:FF P:E5 SP:FB CYC:297 SL:132 +EA6C 48 PHA A:F0 X:02 Y:FF P:E5 SP:FA CYC:306 SL:132 +EA6D A0 A0 LDY #$A0 A:F0 X:02 Y:FF P:E5 SP:F9 CYC:315 SL:132 +EA6F 68 PLA A:F0 X:02 Y:A0 P:E5 SP:F9 CYC:321 SL:132 +EA70 28 PLP A:F0 X:02 Y:A0 P:E5 SP:FA CYC:333 SL:132 +EA71 20 59 FA JSR $FA59 A:F0 X:02 Y:A0 P:E5 SP:FB CYC: 4 SL:133 +FA59 50 0A BVC $FA65 A:F0 X:02 Y:A0 P:E5 SP:F9 CYC: 22 SL:133 +FA5B F0 08 BEQ $FA65 A:F0 X:02 Y:A0 P:E5 SP:F9 CYC: 28 SL:133 +FA5D 10 06 BPL $FA65 A:F0 X:02 Y:A0 P:E5 SP:F9 CYC: 34 SL:133 +FA5F 90 04 BCC $FA65 A:F0 X:02 Y:A0 P:E5 SP:F9 CYC: 40 SL:133 +FA61 C9 F0 CMP #$F0 A:F0 X:02 Y:A0 P:E5 SP:F9 CYC: 46 SL:133 +FA63 F0 02 BEQ $FA67 A:F0 X:02 Y:A0 P:67 SP:F9 CYC: 52 SL:133 +FA67 60 RTS A:F0 X:02 Y:A0 P:67 SP:F9 CYC: 61 SL:133 +EA74 AD 47 06 LDA $0647 = 36 A:F0 X:02 Y:A0 P:67 SP:FB CYC: 79 SL:133 +EA77 C9 36 CMP #$36 A:36 X:02 Y:A0 P:65 SP:FB CYC: 91 SL:133 +EA79 F0 02 BEQ $EA7D A:36 X:02 Y:A0 P:67 SP:FB CYC: 97 SL:133 +EA7D A0 A1 LDY #$A1 A:36 X:02 Y:A0 P:67 SP:FB CYC:106 SL:133 +EA7F A2 FF LDX #$FF A:36 X:02 Y:A1 P:E5 SP:FB CYC:112 SL:133 +EA81 A9 EB LDA #$EB A:36 X:FF Y:A1 P:E5 SP:FB CYC:118 SL:133 +EA83 85 47 STA $47 = 36 A:EB X:FF Y:A1 P:E5 SP:FB CYC:124 SL:133 +EA85 20 31 FA JSR $FA31 A:EB X:FF Y:A1 P:E5 SP:FB CYC:133 SL:133 +FA31 24 01 BIT $01 = FF A:EB X:FF Y:A1 P:E5 SP:F9 CYC:151 SL:133 +FA33 18 CLC A:EB X:FF Y:A1 P:E5 SP:F9 CYC:160 SL:133 +FA34 A9 40 LDA #$40 A:EB X:FF Y:A1 P:E4 SP:F9 CYC:166 SL:133 +FA36 60 RTS A:40 X:FF Y:A1 P:64 SP:F9 CYC:172 SL:133 +EA88 D7 48 *DCP $48,X @ 47 = EB A:40 X:FF Y:A1 P:64 SP:FB CYC:190 SL:133 +EA8A EA NOP A:40 X:FF Y:A1 P:64 SP:FB CYC:208 SL:133 +EA8B EA NOP A:40 X:FF Y:A1 P:64 SP:FB CYC:214 SL:133 +EA8C EA NOP A:40 X:FF Y:A1 P:64 SP:FB CYC:220 SL:133 +EA8D EA NOP A:40 X:FF Y:A1 P:64 SP:FB CYC:226 SL:133 +EA8E 20 37 FA JSR $FA37 A:40 X:FF Y:A1 P:64 SP:FB CYC:232 SL:133 +FA37 50 2C BVC $FA65 A:40 X:FF Y:A1 P:64 SP:F9 CYC:250 SL:133 +FA39 B0 2A BCS $FA65 A:40 X:FF Y:A1 P:64 SP:F9 CYC:256 SL:133 +FA3B 30 28 BMI $FA65 A:40 X:FF Y:A1 P:64 SP:F9 CYC:262 SL:133 +FA3D C9 40 CMP #$40 A:40 X:FF Y:A1 P:64 SP:F9 CYC:268 SL:133 +FA3F D0 24 BNE $FA65 A:40 X:FF Y:A1 P:67 SP:F9 CYC:274 SL:133 +FA41 60 RTS A:40 X:FF Y:A1 P:67 SP:F9 CYC:280 SL:133 +EA91 A5 47 LDA $47 = EA A:40 X:FF Y:A1 P:67 SP:FB CYC:298 SL:133 +EA93 C9 EA CMP #$EA A:EA X:FF Y:A1 P:E5 SP:FB CYC:307 SL:133 +EA95 F0 02 BEQ $EA99 A:EA X:FF Y:A1 P:67 SP:FB CYC:313 SL:133 +EA99 C8 INY A:EA X:FF Y:A1 P:67 SP:FB CYC:322 SL:133 +EA9A A9 00 LDA #$00 A:EA X:FF Y:A2 P:E5 SP:FB CYC:328 SL:133 +EA9C 85 47 STA $47 = EA A:00 X:FF Y:A2 P:67 SP:FB CYC:334 SL:133 +EA9E 20 42 FA JSR $FA42 A:00 X:FF Y:A2 P:67 SP:FB CYC: 2 SL:134 +FA42 B8 CLV A:00 X:FF Y:A2 P:67 SP:F9 CYC: 20 SL:134 +FA43 38 SEC A:00 X:FF Y:A2 P:27 SP:F9 CYC: 26 SL:134 +FA44 A9 FF LDA #$FF A:00 X:FF Y:A2 P:27 SP:F9 CYC: 32 SL:134 +FA46 60 RTS A:FF X:FF Y:A2 P:A5 SP:F9 CYC: 38 SL:134 +EAA1 D7 48 *DCP $48,X @ 47 = 00 A:FF X:FF Y:A2 P:A5 SP:FB CYC: 56 SL:134 +EAA3 EA NOP A:FF X:FF Y:A2 P:27 SP:FB CYC: 74 SL:134 +EAA4 EA NOP A:FF X:FF Y:A2 P:27 SP:FB CYC: 80 SL:134 +EAA5 EA NOP A:FF X:FF Y:A2 P:27 SP:FB CYC: 86 SL:134 +EAA6 EA NOP A:FF X:FF Y:A2 P:27 SP:FB CYC: 92 SL:134 +EAA7 20 47 FA JSR $FA47 A:FF X:FF Y:A2 P:27 SP:FB CYC: 98 SL:134 +FA47 70 1C BVS $FA65 A:FF X:FF Y:A2 P:27 SP:F9 CYC:116 SL:134 +FA49 D0 1A BNE $FA65 A:FF X:FF Y:A2 P:27 SP:F9 CYC:122 SL:134 +FA4B 30 18 BMI $FA65 A:FF X:FF Y:A2 P:27 SP:F9 CYC:128 SL:134 +FA4D 90 16 BCC $FA65 A:FF X:FF Y:A2 P:27 SP:F9 CYC:134 SL:134 +FA4F C9 FF CMP #$FF A:FF X:FF Y:A2 P:27 SP:F9 CYC:140 SL:134 +FA51 D0 12 BNE $FA65 A:FF X:FF Y:A2 P:27 SP:F9 CYC:146 SL:134 +FA53 60 RTS A:FF X:FF Y:A2 P:27 SP:F9 CYC:152 SL:134 +EAAA A5 47 LDA $47 = FF A:FF X:FF Y:A2 P:27 SP:FB CYC:170 SL:134 +EAAC C9 FF CMP #$FF A:FF X:FF Y:A2 P:A5 SP:FB CYC:179 SL:134 +EAAE F0 02 BEQ $EAB2 A:FF X:FF Y:A2 P:27 SP:FB CYC:185 SL:134 +EAB2 C8 INY A:FF X:FF Y:A2 P:27 SP:FB CYC:194 SL:134 +EAB3 A9 37 LDA #$37 A:FF X:FF Y:A3 P:A5 SP:FB CYC:200 SL:134 +EAB5 85 47 STA $47 = FF A:37 X:FF Y:A3 P:25 SP:FB CYC:206 SL:134 +EAB7 20 54 FA JSR $FA54 A:37 X:FF Y:A3 P:25 SP:FB CYC:215 SL:134 +FA54 24 01 BIT $01 = FF A:37 X:FF Y:A3 P:25 SP:F9 CYC:233 SL:134 +FA56 A9 F0 LDA #$F0 A:37 X:FF Y:A3 P:E5 SP:F9 CYC:242 SL:134 +FA58 60 RTS A:F0 X:FF Y:A3 P:E5 SP:F9 CYC:248 SL:134 +EABA D7 48 *DCP $48,X @ 47 = 37 A:F0 X:FF Y:A3 P:E5 SP:FB CYC:266 SL:134 +EABC EA NOP A:F0 X:FF Y:A3 P:E5 SP:FB CYC:284 SL:134 +EABD EA NOP A:F0 X:FF Y:A3 P:E5 SP:FB CYC:290 SL:134 +EABE EA NOP A:F0 X:FF Y:A3 P:E5 SP:FB CYC:296 SL:134 +EABF EA NOP A:F0 X:FF Y:A3 P:E5 SP:FB CYC:302 SL:134 +EAC0 20 59 FA JSR $FA59 A:F0 X:FF Y:A3 P:E5 SP:FB CYC:308 SL:134 +FA59 50 0A BVC $FA65 A:F0 X:FF Y:A3 P:E5 SP:F9 CYC:326 SL:134 +FA5B F0 08 BEQ $FA65 A:F0 X:FF Y:A3 P:E5 SP:F9 CYC:332 SL:134 +FA5D 10 06 BPL $FA65 A:F0 X:FF Y:A3 P:E5 SP:F9 CYC:338 SL:134 +FA5F 90 04 BCC $FA65 A:F0 X:FF Y:A3 P:E5 SP:F9 CYC: 3 SL:135 +FA61 C9 F0 CMP #$F0 A:F0 X:FF Y:A3 P:E5 SP:F9 CYC: 9 SL:135 +FA63 F0 02 BEQ $FA67 A:F0 X:FF Y:A3 P:67 SP:F9 CYC: 15 SL:135 +FA67 60 RTS A:F0 X:FF Y:A3 P:67 SP:F9 CYC: 24 SL:135 +EAC3 A5 47 LDA $47 = 36 A:F0 X:FF Y:A3 P:67 SP:FB CYC: 42 SL:135 +EAC5 C9 36 CMP #$36 A:36 X:FF Y:A3 P:65 SP:FB CYC: 51 SL:135 +EAC7 F0 02 BEQ $EACB A:36 X:FF Y:A3 P:67 SP:FB CYC: 57 SL:135 +EACB A9 EB LDA #$EB A:36 X:FF Y:A3 P:67 SP:FB CYC: 66 SL:135 +EACD 8D 47 06 STA $0647 = 36 A:EB X:FF Y:A3 P:E5 SP:FB CYC: 72 SL:135 +EAD0 A0 FF LDY #$FF A:EB X:FF Y:A3 P:E5 SP:FB CYC: 84 SL:135 +EAD2 20 31 FA JSR $FA31 A:EB X:FF Y:FF P:E5 SP:FB CYC: 90 SL:135 +FA31 24 01 BIT $01 = FF A:EB X:FF Y:FF P:E5 SP:F9 CYC:108 SL:135 +FA33 18 CLC A:EB X:FF Y:FF P:E5 SP:F9 CYC:117 SL:135 +FA34 A9 40 LDA #$40 A:EB X:FF Y:FF P:E4 SP:F9 CYC:123 SL:135 +FA36 60 RTS A:40 X:FF Y:FF P:64 SP:F9 CYC:129 SL:135 +EAD5 DB 48 05 *DCP $0548,Y @ 0647 = EB A:40 X:FF Y:FF P:64 SP:FB CYC:147 SL:135 +EAD8 EA NOP A:40 X:FF Y:FF P:64 SP:FB CYC:168 SL:135 +EAD9 EA NOP A:40 X:FF Y:FF P:64 SP:FB CYC:174 SL:135 +EADA 08 PHP A:40 X:FF Y:FF P:64 SP:FB CYC:180 SL:135 +EADB 48 PHA A:40 X:FF Y:FF P:64 SP:FA CYC:189 SL:135 +EADC A0 A4 LDY #$A4 A:40 X:FF Y:FF P:64 SP:F9 CYC:198 SL:135 +EADE 68 PLA A:40 X:FF Y:A4 P:E4 SP:F9 CYC:204 SL:135 +EADF 28 PLP A:40 X:FF Y:A4 P:64 SP:FA CYC:216 SL:135 +EAE0 20 37 FA JSR $FA37 A:40 X:FF Y:A4 P:64 SP:FB CYC:228 SL:135 +FA37 50 2C BVC $FA65 A:40 X:FF Y:A4 P:64 SP:F9 CYC:246 SL:135 +FA39 B0 2A BCS $FA65 A:40 X:FF Y:A4 P:64 SP:F9 CYC:252 SL:135 +FA3B 30 28 BMI $FA65 A:40 X:FF Y:A4 P:64 SP:F9 CYC:258 SL:135 +FA3D C9 40 CMP #$40 A:40 X:FF Y:A4 P:64 SP:F9 CYC:264 SL:135 +FA3F D0 24 BNE $FA65 A:40 X:FF Y:A4 P:67 SP:F9 CYC:270 SL:135 +FA41 60 RTS A:40 X:FF Y:A4 P:67 SP:F9 CYC:276 SL:135 +EAE3 AD 47 06 LDA $0647 = EA A:40 X:FF Y:A4 P:67 SP:FB CYC:294 SL:135 +EAE6 C9 EA CMP #$EA A:EA X:FF Y:A4 P:E5 SP:FB CYC:306 SL:135 +EAE8 F0 02 BEQ $EAEC A:EA X:FF Y:A4 P:67 SP:FB CYC:312 SL:135 +EAEC A0 FF LDY #$FF A:EA X:FF Y:A4 P:67 SP:FB CYC:321 SL:135 +EAEE A9 00 LDA #$00 A:EA X:FF Y:FF P:E5 SP:FB CYC:327 SL:135 +EAF0 8D 47 06 STA $0647 = EA A:00 X:FF Y:FF P:67 SP:FB CYC:333 SL:135 +EAF3 20 42 FA JSR $FA42 A:00 X:FF Y:FF P:67 SP:FB CYC: 4 SL:136 +FA42 B8 CLV A:00 X:FF Y:FF P:67 SP:F9 CYC: 22 SL:136 +FA43 38 SEC A:00 X:FF Y:FF P:27 SP:F9 CYC: 28 SL:136 +FA44 A9 FF LDA #$FF A:00 X:FF Y:FF P:27 SP:F9 CYC: 34 SL:136 +FA46 60 RTS A:FF X:FF Y:FF P:A5 SP:F9 CYC: 40 SL:136 +EAF6 DB 48 05 *DCP $0548,Y @ 0647 = 00 A:FF X:FF Y:FF P:A5 SP:FB CYC: 58 SL:136 +EAF9 EA NOP A:FF X:FF Y:FF P:27 SP:FB CYC: 79 SL:136 +EAFA EA NOP A:FF X:FF Y:FF P:27 SP:FB CYC: 85 SL:136 +EAFB 08 PHP A:FF X:FF Y:FF P:27 SP:FB CYC: 91 SL:136 +EAFC 48 PHA A:FF X:FF Y:FF P:27 SP:FA CYC:100 SL:136 +EAFD A0 A5 LDY #$A5 A:FF X:FF Y:FF P:27 SP:F9 CYC:109 SL:136 +EAFF 68 PLA A:FF X:FF Y:A5 P:A5 SP:F9 CYC:115 SL:136 +EB00 28 PLP A:FF X:FF Y:A5 P:A5 SP:FA CYC:127 SL:136 +EB01 20 47 FA JSR $FA47 A:FF X:FF Y:A5 P:27 SP:FB CYC:139 SL:136 +FA47 70 1C BVS $FA65 A:FF X:FF Y:A5 P:27 SP:F9 CYC:157 SL:136 +FA49 D0 1A BNE $FA65 A:FF X:FF Y:A5 P:27 SP:F9 CYC:163 SL:136 +FA4B 30 18 BMI $FA65 A:FF X:FF Y:A5 P:27 SP:F9 CYC:169 SL:136 +FA4D 90 16 BCC $FA65 A:FF X:FF Y:A5 P:27 SP:F9 CYC:175 SL:136 +FA4F C9 FF CMP #$FF A:FF X:FF Y:A5 P:27 SP:F9 CYC:181 SL:136 +FA51 D0 12 BNE $FA65 A:FF X:FF Y:A5 P:27 SP:F9 CYC:187 SL:136 +FA53 60 RTS A:FF X:FF Y:A5 P:27 SP:F9 CYC:193 SL:136 +EB04 AD 47 06 LDA $0647 = FF A:FF X:FF Y:A5 P:27 SP:FB CYC:211 SL:136 +EB07 C9 FF CMP #$FF A:FF X:FF Y:A5 P:A5 SP:FB CYC:223 SL:136 +EB09 F0 02 BEQ $EB0D A:FF X:FF Y:A5 P:27 SP:FB CYC:229 SL:136 +EB0D A0 FF LDY #$FF A:FF X:FF Y:A5 P:27 SP:FB CYC:238 SL:136 +EB0F A9 37 LDA #$37 A:FF X:FF Y:FF P:A5 SP:FB CYC:244 SL:136 +EB11 8D 47 06 STA $0647 = FF A:37 X:FF Y:FF P:25 SP:FB CYC:250 SL:136 +EB14 20 54 FA JSR $FA54 A:37 X:FF Y:FF P:25 SP:FB CYC:262 SL:136 +FA54 24 01 BIT $01 = FF A:37 X:FF Y:FF P:25 SP:F9 CYC:280 SL:136 +FA56 A9 F0 LDA #$F0 A:37 X:FF Y:FF P:E5 SP:F9 CYC:289 SL:136 +FA58 60 RTS A:F0 X:FF Y:FF P:E5 SP:F9 CYC:295 SL:136 +EB17 DB 48 05 *DCP $0548,Y @ 0647 = 37 A:F0 X:FF Y:FF P:E5 SP:FB CYC:313 SL:136 +EB1A EA NOP A:F0 X:FF Y:FF P:E5 SP:FB CYC:334 SL:136 +EB1B EA NOP A:F0 X:FF Y:FF P:E5 SP:FB CYC:340 SL:136 +EB1C 08 PHP A:F0 X:FF Y:FF P:E5 SP:FB CYC: 5 SL:137 +EB1D 48 PHA A:F0 X:FF Y:FF P:E5 SP:FA CYC: 14 SL:137 +EB1E A0 A6 LDY #$A6 A:F0 X:FF Y:FF P:E5 SP:F9 CYC: 23 SL:137 +EB20 68 PLA A:F0 X:FF Y:A6 P:E5 SP:F9 CYC: 29 SL:137 +EB21 28 PLP A:F0 X:FF Y:A6 P:E5 SP:FA CYC: 41 SL:137 +EB22 20 59 FA JSR $FA59 A:F0 X:FF Y:A6 P:E5 SP:FB CYC: 53 SL:137 +FA59 50 0A BVC $FA65 A:F0 X:FF Y:A6 P:E5 SP:F9 CYC: 71 SL:137 +FA5B F0 08 BEQ $FA65 A:F0 X:FF Y:A6 P:E5 SP:F9 CYC: 77 SL:137 +FA5D 10 06 BPL $FA65 A:F0 X:FF Y:A6 P:E5 SP:F9 CYC: 83 SL:137 +FA5F 90 04 BCC $FA65 A:F0 X:FF Y:A6 P:E5 SP:F9 CYC: 89 SL:137 +FA61 C9 F0 CMP #$F0 A:F0 X:FF Y:A6 P:E5 SP:F9 CYC: 95 SL:137 +FA63 F0 02 BEQ $FA67 A:F0 X:FF Y:A6 P:67 SP:F9 CYC:101 SL:137 +FA67 60 RTS A:F0 X:FF Y:A6 P:67 SP:F9 CYC:110 SL:137 +EB25 AD 47 06 LDA $0647 = 36 A:F0 X:FF Y:A6 P:67 SP:FB CYC:128 SL:137 +EB28 C9 36 CMP #$36 A:36 X:FF Y:A6 P:65 SP:FB CYC:140 SL:137 +EB2A F0 02 BEQ $EB2E A:36 X:FF Y:A6 P:67 SP:FB CYC:146 SL:137 +EB2E A0 A7 LDY #$A7 A:36 X:FF Y:A6 P:67 SP:FB CYC:155 SL:137 +EB30 A2 FF LDX #$FF A:36 X:FF Y:A7 P:E5 SP:FB CYC:161 SL:137 +EB32 A9 EB LDA #$EB A:36 X:FF Y:A7 P:E5 SP:FB CYC:167 SL:137 +EB34 8D 47 06 STA $0647 = 36 A:EB X:FF Y:A7 P:E5 SP:FB CYC:173 SL:137 +EB37 20 31 FA JSR $FA31 A:EB X:FF Y:A7 P:E5 SP:FB CYC:185 SL:137 +FA31 24 01 BIT $01 = FF A:EB X:FF Y:A7 P:E5 SP:F9 CYC:203 SL:137 +FA33 18 CLC A:EB X:FF Y:A7 P:E5 SP:F9 CYC:212 SL:137 +FA34 A9 40 LDA #$40 A:EB X:FF Y:A7 P:E4 SP:F9 CYC:218 SL:137 +FA36 60 RTS A:40 X:FF Y:A7 P:64 SP:F9 CYC:224 SL:137 +EB3A DF 48 05 *DCP $0548,X @ 0647 = EB A:40 X:FF Y:A7 P:64 SP:FB CYC:242 SL:137 +EB3D EA NOP A:40 X:FF Y:A7 P:64 SP:FB CYC:263 SL:137 +EB3E EA NOP A:40 X:FF Y:A7 P:64 SP:FB CYC:269 SL:137 +EB3F EA NOP A:40 X:FF Y:A7 P:64 SP:FB CYC:275 SL:137 +EB40 EA NOP A:40 X:FF Y:A7 P:64 SP:FB CYC:281 SL:137 +EB41 20 37 FA JSR $FA37 A:40 X:FF Y:A7 P:64 SP:FB CYC:287 SL:137 +FA37 50 2C BVC $FA65 A:40 X:FF Y:A7 P:64 SP:F9 CYC:305 SL:137 +FA39 B0 2A BCS $FA65 A:40 X:FF Y:A7 P:64 SP:F9 CYC:311 SL:137 +FA3B 30 28 BMI $FA65 A:40 X:FF Y:A7 P:64 SP:F9 CYC:317 SL:137 +FA3D C9 40 CMP #$40 A:40 X:FF Y:A7 P:64 SP:F9 CYC:323 SL:137 +FA3F D0 24 BNE $FA65 A:40 X:FF Y:A7 P:67 SP:F9 CYC:329 SL:137 +FA41 60 RTS A:40 X:FF Y:A7 P:67 SP:F9 CYC:335 SL:137 +EB44 AD 47 06 LDA $0647 = EA A:40 X:FF Y:A7 P:67 SP:FB CYC: 12 SL:138 +EB47 C9 EA CMP #$EA A:EA X:FF Y:A7 P:E5 SP:FB CYC: 24 SL:138 +EB49 F0 02 BEQ $EB4D A:EA X:FF Y:A7 P:67 SP:FB CYC: 30 SL:138 +EB4D C8 INY A:EA X:FF Y:A7 P:67 SP:FB CYC: 39 SL:138 +EB4E A9 00 LDA #$00 A:EA X:FF Y:A8 P:E5 SP:FB CYC: 45 SL:138 +EB50 8D 47 06 STA $0647 = EA A:00 X:FF Y:A8 P:67 SP:FB CYC: 51 SL:138 +EB53 20 42 FA JSR $FA42 A:00 X:FF Y:A8 P:67 SP:FB CYC: 63 SL:138 +FA42 B8 CLV A:00 X:FF Y:A8 P:67 SP:F9 CYC: 81 SL:138 +FA43 38 SEC A:00 X:FF Y:A8 P:27 SP:F9 CYC: 87 SL:138 +FA44 A9 FF LDA #$FF A:00 X:FF Y:A8 P:27 SP:F9 CYC: 93 SL:138 +FA46 60 RTS A:FF X:FF Y:A8 P:A5 SP:F9 CYC: 99 SL:138 +EB56 DF 48 05 *DCP $0548,X @ 0647 = 00 A:FF X:FF Y:A8 P:A5 SP:FB CYC:117 SL:138 +EB59 EA NOP A:FF X:FF Y:A8 P:27 SP:FB CYC:138 SL:138 +EB5A EA NOP A:FF X:FF Y:A8 P:27 SP:FB CYC:144 SL:138 +EB5B EA NOP A:FF X:FF Y:A8 P:27 SP:FB CYC:150 SL:138 +EB5C EA NOP A:FF X:FF Y:A8 P:27 SP:FB CYC:156 SL:138 +EB5D 20 47 FA JSR $FA47 A:FF X:FF Y:A8 P:27 SP:FB CYC:162 SL:138 +FA47 70 1C BVS $FA65 A:FF X:FF Y:A8 P:27 SP:F9 CYC:180 SL:138 +FA49 D0 1A BNE $FA65 A:FF X:FF Y:A8 P:27 SP:F9 CYC:186 SL:138 +FA4B 30 18 BMI $FA65 A:FF X:FF Y:A8 P:27 SP:F9 CYC:192 SL:138 +FA4D 90 16 BCC $FA65 A:FF X:FF Y:A8 P:27 SP:F9 CYC:198 SL:138 +FA4F C9 FF CMP #$FF A:FF X:FF Y:A8 P:27 SP:F9 CYC:204 SL:138 +FA51 D0 12 BNE $FA65 A:FF X:FF Y:A8 P:27 SP:F9 CYC:210 SL:138 +FA53 60 RTS A:FF X:FF Y:A8 P:27 SP:F9 CYC:216 SL:138 +EB60 AD 47 06 LDA $0647 = FF A:FF X:FF Y:A8 P:27 SP:FB CYC:234 SL:138 +EB63 C9 FF CMP #$FF A:FF X:FF Y:A8 P:A5 SP:FB CYC:246 SL:138 +EB65 F0 02 BEQ $EB69 A:FF X:FF Y:A8 P:27 SP:FB CYC:252 SL:138 +EB69 C8 INY A:FF X:FF Y:A8 P:27 SP:FB CYC:261 SL:138 +EB6A A9 37 LDA #$37 A:FF X:FF Y:A9 P:A5 SP:FB CYC:267 SL:138 +EB6C 8D 47 06 STA $0647 = FF A:37 X:FF Y:A9 P:25 SP:FB CYC:273 SL:138 +EB6F 20 54 FA JSR $FA54 A:37 X:FF Y:A9 P:25 SP:FB CYC:285 SL:138 +FA54 24 01 BIT $01 = FF A:37 X:FF Y:A9 P:25 SP:F9 CYC:303 SL:138 +FA56 A9 F0 LDA #$F0 A:37 X:FF Y:A9 P:E5 SP:F9 CYC:312 SL:138 +FA58 60 RTS A:F0 X:FF Y:A9 P:E5 SP:F9 CYC:318 SL:138 +EB72 DF 48 05 *DCP $0548,X @ 0647 = 37 A:F0 X:FF Y:A9 P:E5 SP:FB CYC:336 SL:138 +EB75 EA NOP A:F0 X:FF Y:A9 P:E5 SP:FB CYC: 16 SL:139 +EB76 EA NOP A:F0 X:FF Y:A9 P:E5 SP:FB CYC: 22 SL:139 +EB77 EA NOP A:F0 X:FF Y:A9 P:E5 SP:FB CYC: 28 SL:139 +EB78 EA NOP A:F0 X:FF Y:A9 P:E5 SP:FB CYC: 34 SL:139 +EB79 20 59 FA JSR $FA59 A:F0 X:FF Y:A9 P:E5 SP:FB CYC: 40 SL:139 +FA59 50 0A BVC $FA65 A:F0 X:FF Y:A9 P:E5 SP:F9 CYC: 58 SL:139 +FA5B F0 08 BEQ $FA65 A:F0 X:FF Y:A9 P:E5 SP:F9 CYC: 64 SL:139 +FA5D 10 06 BPL $FA65 A:F0 X:FF Y:A9 P:E5 SP:F9 CYC: 70 SL:139 +FA5F 90 04 BCC $FA65 A:F0 X:FF Y:A9 P:E5 SP:F9 CYC: 76 SL:139 +FA61 C9 F0 CMP #$F0 A:F0 X:FF Y:A9 P:E5 SP:F9 CYC: 82 SL:139 +FA63 F0 02 BEQ $FA67 A:F0 X:FF Y:A9 P:67 SP:F9 CYC: 88 SL:139 +FA67 60 RTS A:F0 X:FF Y:A9 P:67 SP:F9 CYC: 97 SL:139 +EB7C AD 47 06 LDA $0647 = 36 A:F0 X:FF Y:A9 P:67 SP:FB CYC:115 SL:139 +EB7F C9 36 CMP #$36 A:36 X:FF Y:A9 P:65 SP:FB CYC:127 SL:139 +EB81 F0 02 BEQ $EB85 A:36 X:FF Y:A9 P:67 SP:FB CYC:133 SL:139 +EB85 60 RTS A:36 X:FF Y:A9 P:67 SP:FB CYC:142 SL:139 +C63E 20 86 EB JSR $EB86 A:36 X:FF Y:A9 P:67 SP:FD CYC:160 SL:139 +EB86 A9 FF LDA #$FF A:36 X:FF Y:A9 P:67 SP:FB CYC:178 SL:139 +EB88 85 01 STA $01 = FF A:FF X:FF Y:A9 P:E5 SP:FB CYC:184 SL:139 +EB8A A0 AA LDY #$AA A:FF X:FF Y:A9 P:E5 SP:FB CYC:193 SL:139 +EB8C A2 02 LDX #$02 A:FF X:FF Y:AA P:E5 SP:FB CYC:199 SL:139 +EB8E A9 47 LDA #$47 A:FF X:02 Y:AA P:65 SP:FB CYC:205 SL:139 +EB90 85 47 STA $47 = 36 A:47 X:02 Y:AA P:65 SP:FB CYC:211 SL:139 +EB92 A9 06 LDA #$06 A:47 X:02 Y:AA P:65 SP:FB CYC:220 SL:139 +EB94 85 48 STA $48 = 06 A:06 X:02 Y:AA P:65 SP:FB CYC:226 SL:139 +EB96 A9 EB LDA #$EB A:06 X:02 Y:AA P:65 SP:FB CYC:235 SL:139 +EB98 8D 47 06 STA $0647 = 36 A:EB X:02 Y:AA P:E5 SP:FB CYC:241 SL:139 +EB9B 20 B1 FA JSR $FAB1 A:EB X:02 Y:AA P:E5 SP:FB CYC:253 SL:139 +FAB1 24 01 BIT $01 = FF A:EB X:02 Y:AA P:E5 SP:F9 CYC:271 SL:139 +FAB3 18 CLC A:EB X:02 Y:AA P:E5 SP:F9 CYC:280 SL:139 +FAB4 A9 40 LDA #$40 A:EB X:02 Y:AA P:E4 SP:F9 CYC:286 SL:139 +FAB6 60 RTS A:40 X:02 Y:AA P:64 SP:F9 CYC:292 SL:139 +EB9E E3 45 *ISB ($45,X) @ 47 = 0647 = EB A:40 X:02 Y:AA P:64 SP:FB CYC:310 SL:139 +EBA0 EA NOP A:53 X:02 Y:AA P:24 SP:FB CYC:334 SL:139 +EBA1 EA NOP A:53 X:02 Y:AA P:24 SP:FB CYC:340 SL:139 +EBA2 EA NOP A:53 X:02 Y:AA P:24 SP:FB CYC: 5 SL:140 +EBA3 EA NOP A:53 X:02 Y:AA P:24 SP:FB CYC: 11 SL:140 +EBA4 20 B7 FA JSR $FAB7 A:53 X:02 Y:AA P:24 SP:FB CYC: 17 SL:140 +FAB7 70 2D BVS $FAE6 A:53 X:02 Y:AA P:24 SP:F9 CYC: 35 SL:140 +FAB9 B0 2B BCS $FAE6 A:53 X:02 Y:AA P:24 SP:F9 CYC: 41 SL:140 +FABB 30 29 BMI $FAE6 A:53 X:02 Y:AA P:24 SP:F9 CYC: 47 SL:140 +FABD C9 53 CMP #$53 A:53 X:02 Y:AA P:24 SP:F9 CYC: 53 SL:140 +FABF D0 25 BNE $FAE6 A:53 X:02 Y:AA P:27 SP:F9 CYC: 59 SL:140 +FAC1 60 RTS A:53 X:02 Y:AA P:27 SP:F9 CYC: 65 SL:140 +EBA7 AD 47 06 LDA $0647 = EC A:53 X:02 Y:AA P:27 SP:FB CYC: 83 SL:140 +EBAA C9 EC CMP #$EC A:EC X:02 Y:AA P:A5 SP:FB CYC: 95 SL:140 +EBAC F0 02 BEQ $EBB0 A:EC X:02 Y:AA P:27 SP:FB CYC:101 SL:140 +EBB0 C8 INY A:EC X:02 Y:AA P:27 SP:FB CYC:110 SL:140 +EBB1 A9 FF LDA #$FF A:EC X:02 Y:AB P:A5 SP:FB CYC:116 SL:140 +EBB3 8D 47 06 STA $0647 = EC A:FF X:02 Y:AB P:A5 SP:FB CYC:122 SL:140 +EBB6 20 C2 FA JSR $FAC2 A:FF X:02 Y:AB P:A5 SP:FB CYC:134 SL:140 +FAC2 B8 CLV A:FF X:02 Y:AB P:A5 SP:F9 CYC:152 SL:140 +FAC3 38 SEC A:FF X:02 Y:AB P:A5 SP:F9 CYC:158 SL:140 +FAC4 A9 FF LDA #$FF A:FF X:02 Y:AB P:A5 SP:F9 CYC:164 SL:140 +FAC6 60 RTS A:FF X:02 Y:AB P:A5 SP:F9 CYC:170 SL:140 +EBB9 E3 45 *ISB ($45,X) @ 47 = 0647 = FF A:FF X:02 Y:AB P:A5 SP:FB CYC:188 SL:140 +EBBB EA NOP A:FF X:02 Y:AB P:A5 SP:FB CYC:212 SL:140 +EBBC EA NOP A:FF X:02 Y:AB P:A5 SP:FB CYC:218 SL:140 +EBBD EA NOP A:FF X:02 Y:AB P:A5 SP:FB CYC:224 SL:140 +EBBE EA NOP A:FF X:02 Y:AB P:A5 SP:FB CYC:230 SL:140 +EBBF 20 C7 FA JSR $FAC7 A:FF X:02 Y:AB P:A5 SP:FB CYC:236 SL:140 +FAC7 70 1D BVS $FAE6 A:FF X:02 Y:AB P:A5 SP:F9 CYC:254 SL:140 +FAC9 F0 1B BEQ $FAE6 A:FF X:02 Y:AB P:A5 SP:F9 CYC:260 SL:140 +FACB 10 19 BPL $FAE6 A:FF X:02 Y:AB P:A5 SP:F9 CYC:266 SL:140 +FACD 90 17 BCC $FAE6 A:FF X:02 Y:AB P:A5 SP:F9 CYC:272 SL:140 +FACF C9 FF CMP #$FF A:FF X:02 Y:AB P:A5 SP:F9 CYC:278 SL:140 +FAD1 D0 13 BNE $FAE6 A:FF X:02 Y:AB P:27 SP:F9 CYC:284 SL:140 +FAD3 60 RTS A:FF X:02 Y:AB P:27 SP:F9 CYC:290 SL:140 +EBC2 AD 47 06 LDA $0647 = 00 A:FF X:02 Y:AB P:27 SP:FB CYC:308 SL:140 +EBC5 C9 00 CMP #$00 A:00 X:02 Y:AB P:27 SP:FB CYC:320 SL:140 +EBC7 F0 02 BEQ $EBCB A:00 X:02 Y:AB P:27 SP:FB CYC:326 SL:140 +EBCB C8 INY A:00 X:02 Y:AB P:27 SP:FB CYC:335 SL:140 +EBCC A9 37 LDA #$37 A:00 X:02 Y:AC P:A5 SP:FB CYC: 0 SL:141 +EBCE 8D 47 06 STA $0647 = 00 A:37 X:02 Y:AC P:25 SP:FB CYC: 6 SL:141 +EBD1 20 D4 FA JSR $FAD4 A:37 X:02 Y:AC P:25 SP:FB CYC: 18 SL:141 +FAD4 24 01 BIT $01 = FF A:37 X:02 Y:AC P:25 SP:F9 CYC: 36 SL:141 +FAD6 38 SEC A:37 X:02 Y:AC P:E5 SP:F9 CYC: 45 SL:141 +FAD7 A9 F0 LDA #$F0 A:37 X:02 Y:AC P:E5 SP:F9 CYC: 51 SL:141 +FAD9 60 RTS A:F0 X:02 Y:AC P:E5 SP:F9 CYC: 57 SL:141 +EBD4 E3 45 *ISB ($45,X) @ 47 = 0647 = 37 A:F0 X:02 Y:AC P:E5 SP:FB CYC: 75 SL:141 +EBD6 EA NOP A:B8 X:02 Y:AC P:A5 SP:FB CYC: 99 SL:141 +EBD7 EA NOP A:B8 X:02 Y:AC P:A5 SP:FB CYC:105 SL:141 +EBD8 EA NOP A:B8 X:02 Y:AC P:A5 SP:FB CYC:111 SL:141 +EBD9 EA NOP A:B8 X:02 Y:AC P:A5 SP:FB CYC:117 SL:141 +EBDA 20 DA FA JSR $FADA A:B8 X:02 Y:AC P:A5 SP:FB CYC:123 SL:141 +FADA 70 0A BVS $FAE6 A:B8 X:02 Y:AC P:A5 SP:F9 CYC:141 SL:141 +FADC F0 08 BEQ $FAE6 A:B8 X:02 Y:AC P:A5 SP:F9 CYC:147 SL:141 +FADE 10 06 BPL $FAE6 A:B8 X:02 Y:AC P:A5 SP:F9 CYC:153 SL:141 +FAE0 90 04 BCC $FAE6 A:B8 X:02 Y:AC P:A5 SP:F9 CYC:159 SL:141 +FAE2 C9 B8 CMP #$B8 A:B8 X:02 Y:AC P:A5 SP:F9 CYC:165 SL:141 +FAE4 F0 02 BEQ $FAE8 A:B8 X:02 Y:AC P:27 SP:F9 CYC:171 SL:141 +FAE8 60 RTS A:B8 X:02 Y:AC P:27 SP:F9 CYC:180 SL:141 +EBDD AD 47 06 LDA $0647 = 38 A:B8 X:02 Y:AC P:27 SP:FB CYC:198 SL:141 +EBE0 C9 38 CMP #$38 A:38 X:02 Y:AC P:25 SP:FB CYC:210 SL:141 +EBE2 F0 02 BEQ $EBE6 A:38 X:02 Y:AC P:27 SP:FB CYC:216 SL:141 +EBE6 C8 INY A:38 X:02 Y:AC P:27 SP:FB CYC:225 SL:141 +EBE7 A9 EB LDA #$EB A:38 X:02 Y:AD P:A5 SP:FB CYC:231 SL:141 +EBE9 85 47 STA $47 = 47 A:EB X:02 Y:AD P:A5 SP:FB CYC:237 SL:141 +EBEB 20 B1 FA JSR $FAB1 A:EB X:02 Y:AD P:A5 SP:FB CYC:246 SL:141 +FAB1 24 01 BIT $01 = FF A:EB X:02 Y:AD P:A5 SP:F9 CYC:264 SL:141 +FAB3 18 CLC A:EB X:02 Y:AD P:E5 SP:F9 CYC:273 SL:141 +FAB4 A9 40 LDA #$40 A:EB X:02 Y:AD P:E4 SP:F9 CYC:279 SL:141 +FAB6 60 RTS A:40 X:02 Y:AD P:64 SP:F9 CYC:285 SL:141 +EBEE E7 47 *ISB $47 = EB A:40 X:02 Y:AD P:64 SP:FB CYC:303 SL:141 +EBF0 EA NOP A:53 X:02 Y:AD P:24 SP:FB CYC:318 SL:141 +EBF1 EA NOP A:53 X:02 Y:AD P:24 SP:FB CYC:324 SL:141 +EBF2 EA NOP A:53 X:02 Y:AD P:24 SP:FB CYC:330 SL:141 +EBF3 EA NOP A:53 X:02 Y:AD P:24 SP:FB CYC:336 SL:141 +EBF4 20 B7 FA JSR $FAB7 A:53 X:02 Y:AD P:24 SP:FB CYC: 1 SL:142 +FAB7 70 2D BVS $FAE6 A:53 X:02 Y:AD P:24 SP:F9 CYC: 19 SL:142 +FAB9 B0 2B BCS $FAE6 A:53 X:02 Y:AD P:24 SP:F9 CYC: 25 SL:142 +FABB 30 29 BMI $FAE6 A:53 X:02 Y:AD P:24 SP:F9 CYC: 31 SL:142 +FABD C9 53 CMP #$53 A:53 X:02 Y:AD P:24 SP:F9 CYC: 37 SL:142 +FABF D0 25 BNE $FAE6 A:53 X:02 Y:AD P:27 SP:F9 CYC: 43 SL:142 +FAC1 60 RTS A:53 X:02 Y:AD P:27 SP:F9 CYC: 49 SL:142 +EBF7 A5 47 LDA $47 = EC A:53 X:02 Y:AD P:27 SP:FB CYC: 67 SL:142 +EBF9 C9 EC CMP #$EC A:EC X:02 Y:AD P:A5 SP:FB CYC: 76 SL:142 +EBFB F0 02 BEQ $EBFF A:EC X:02 Y:AD P:27 SP:FB CYC: 82 SL:142 +EBFF C8 INY A:EC X:02 Y:AD P:27 SP:FB CYC: 91 SL:142 +EC00 A9 FF LDA #$FF A:EC X:02 Y:AE P:A5 SP:FB CYC: 97 SL:142 +EC02 85 47 STA $47 = EC A:FF X:02 Y:AE P:A5 SP:FB CYC:103 SL:142 +EC04 20 C2 FA JSR $FAC2 A:FF X:02 Y:AE P:A5 SP:FB CYC:112 SL:142 +FAC2 B8 CLV A:FF X:02 Y:AE P:A5 SP:F9 CYC:130 SL:142 +FAC3 38 SEC A:FF X:02 Y:AE P:A5 SP:F9 CYC:136 SL:142 +FAC4 A9 FF LDA #$FF A:FF X:02 Y:AE P:A5 SP:F9 CYC:142 SL:142 +FAC6 60 RTS A:FF X:02 Y:AE P:A5 SP:F9 CYC:148 SL:142 +EC07 E7 47 *ISB $47 = FF A:FF X:02 Y:AE P:A5 SP:FB CYC:166 SL:142 +EC09 EA NOP A:FF X:02 Y:AE P:A5 SP:FB CYC:181 SL:142 +EC0A EA NOP A:FF X:02 Y:AE P:A5 SP:FB CYC:187 SL:142 +EC0B EA NOP A:FF X:02 Y:AE P:A5 SP:FB CYC:193 SL:142 +EC0C EA NOP A:FF X:02 Y:AE P:A5 SP:FB CYC:199 SL:142 +EC0D 20 C7 FA JSR $FAC7 A:FF X:02 Y:AE P:A5 SP:FB CYC:205 SL:142 +FAC7 70 1D BVS $FAE6 A:FF X:02 Y:AE P:A5 SP:F9 CYC:223 SL:142 +FAC9 F0 1B BEQ $FAE6 A:FF X:02 Y:AE P:A5 SP:F9 CYC:229 SL:142 +FACB 10 19 BPL $FAE6 A:FF X:02 Y:AE P:A5 SP:F9 CYC:235 SL:142 +FACD 90 17 BCC $FAE6 A:FF X:02 Y:AE P:A5 SP:F9 CYC:241 SL:142 +FACF C9 FF CMP #$FF A:FF X:02 Y:AE P:A5 SP:F9 CYC:247 SL:142 +FAD1 D0 13 BNE $FAE6 A:FF X:02 Y:AE P:27 SP:F9 CYC:253 SL:142 +FAD3 60 RTS A:FF X:02 Y:AE P:27 SP:F9 CYC:259 SL:142 +EC10 A5 47 LDA $47 = 00 A:FF X:02 Y:AE P:27 SP:FB CYC:277 SL:142 +EC12 C9 00 CMP #$00 A:00 X:02 Y:AE P:27 SP:FB CYC:286 SL:142 +EC14 F0 02 BEQ $EC18 A:00 X:02 Y:AE P:27 SP:FB CYC:292 SL:142 +EC18 C8 INY A:00 X:02 Y:AE P:27 SP:FB CYC:301 SL:142 +EC19 A9 37 LDA #$37 A:00 X:02 Y:AF P:A5 SP:FB CYC:307 SL:142 +EC1B 85 47 STA $47 = 00 A:37 X:02 Y:AF P:25 SP:FB CYC:313 SL:142 +EC1D 20 D4 FA JSR $FAD4 A:37 X:02 Y:AF P:25 SP:FB CYC:322 SL:142 +FAD4 24 01 BIT $01 = FF A:37 X:02 Y:AF P:25 SP:F9 CYC:340 SL:142 +FAD6 38 SEC A:37 X:02 Y:AF P:E5 SP:F9 CYC: 8 SL:143 +FAD7 A9 F0 LDA #$F0 A:37 X:02 Y:AF P:E5 SP:F9 CYC: 14 SL:143 +FAD9 60 RTS A:F0 X:02 Y:AF P:E5 SP:F9 CYC: 20 SL:143 +EC20 E7 47 *ISB $47 = 37 A:F0 X:02 Y:AF P:E5 SP:FB CYC: 38 SL:143 +EC22 EA NOP A:B8 X:02 Y:AF P:A5 SP:FB CYC: 53 SL:143 +EC23 EA NOP A:B8 X:02 Y:AF P:A5 SP:FB CYC: 59 SL:143 +EC24 EA NOP A:B8 X:02 Y:AF P:A5 SP:FB CYC: 65 SL:143 +EC25 EA NOP A:B8 X:02 Y:AF P:A5 SP:FB CYC: 71 SL:143 +EC26 20 DA FA JSR $FADA A:B8 X:02 Y:AF P:A5 SP:FB CYC: 77 SL:143 +FADA 70 0A BVS $FAE6 A:B8 X:02 Y:AF P:A5 SP:F9 CYC: 95 SL:143 +FADC F0 08 BEQ $FAE6 A:B8 X:02 Y:AF P:A5 SP:F9 CYC:101 SL:143 +FADE 10 06 BPL $FAE6 A:B8 X:02 Y:AF P:A5 SP:F9 CYC:107 SL:143 +FAE0 90 04 BCC $FAE6 A:B8 X:02 Y:AF P:A5 SP:F9 CYC:113 SL:143 +FAE2 C9 B8 CMP #$B8 A:B8 X:02 Y:AF P:A5 SP:F9 CYC:119 SL:143 +FAE4 F0 02 BEQ $FAE8 A:B8 X:02 Y:AF P:27 SP:F9 CYC:125 SL:143 +FAE8 60 RTS A:B8 X:02 Y:AF P:27 SP:F9 CYC:134 SL:143 +EC29 A5 47 LDA $47 = 38 A:B8 X:02 Y:AF P:27 SP:FB CYC:152 SL:143 +EC2B C9 38 CMP #$38 A:38 X:02 Y:AF P:25 SP:FB CYC:161 SL:143 +EC2D F0 02 BEQ $EC31 A:38 X:02 Y:AF P:27 SP:FB CYC:167 SL:143 +EC31 C8 INY A:38 X:02 Y:AF P:27 SP:FB CYC:176 SL:143 +EC32 A9 EB LDA #$EB A:38 X:02 Y:B0 P:A5 SP:FB CYC:182 SL:143 +EC34 8D 47 06 STA $0647 = 38 A:EB X:02 Y:B0 P:A5 SP:FB CYC:188 SL:143 +EC37 20 B1 FA JSR $FAB1 A:EB X:02 Y:B0 P:A5 SP:FB CYC:200 SL:143 +FAB1 24 01 BIT $01 = FF A:EB X:02 Y:B0 P:A5 SP:F9 CYC:218 SL:143 +FAB3 18 CLC A:EB X:02 Y:B0 P:E5 SP:F9 CYC:227 SL:143 +FAB4 A9 40 LDA #$40 A:EB X:02 Y:B0 P:E4 SP:F9 CYC:233 SL:143 +FAB6 60 RTS A:40 X:02 Y:B0 P:64 SP:F9 CYC:239 SL:143 +EC3A EF 47 06 *ISB $0647 = EB A:40 X:02 Y:B0 P:64 SP:FB CYC:257 SL:143 +EC3D EA NOP A:53 X:02 Y:B0 P:24 SP:FB CYC:275 SL:143 +EC3E EA NOP A:53 X:02 Y:B0 P:24 SP:FB CYC:281 SL:143 +EC3F EA NOP A:53 X:02 Y:B0 P:24 SP:FB CYC:287 SL:143 +EC40 EA NOP A:53 X:02 Y:B0 P:24 SP:FB CYC:293 SL:143 +EC41 20 B7 FA JSR $FAB7 A:53 X:02 Y:B0 P:24 SP:FB CYC:299 SL:143 +FAB7 70 2D BVS $FAE6 A:53 X:02 Y:B0 P:24 SP:F9 CYC:317 SL:143 +FAB9 B0 2B BCS $FAE6 A:53 X:02 Y:B0 P:24 SP:F9 CYC:323 SL:143 +FABB 30 29 BMI $FAE6 A:53 X:02 Y:B0 P:24 SP:F9 CYC:329 SL:143 +FABD C9 53 CMP #$53 A:53 X:02 Y:B0 P:24 SP:F9 CYC:335 SL:143 +FABF D0 25 BNE $FAE6 A:53 X:02 Y:B0 P:27 SP:F9 CYC: 0 SL:144 +FAC1 60 RTS A:53 X:02 Y:B0 P:27 SP:F9 CYC: 6 SL:144 +EC44 AD 47 06 LDA $0647 = EC A:53 X:02 Y:B0 P:27 SP:FB CYC: 24 SL:144 +EC47 C9 EC CMP #$EC A:EC X:02 Y:B0 P:A5 SP:FB CYC: 36 SL:144 +EC49 F0 02 BEQ $EC4D A:EC X:02 Y:B0 P:27 SP:FB CYC: 42 SL:144 +EC4D C8 INY A:EC X:02 Y:B0 P:27 SP:FB CYC: 51 SL:144 +EC4E A9 FF LDA #$FF A:EC X:02 Y:B1 P:A5 SP:FB CYC: 57 SL:144 +EC50 8D 47 06 STA $0647 = EC A:FF X:02 Y:B1 P:A5 SP:FB CYC: 63 SL:144 +EC53 20 C2 FA JSR $FAC2 A:FF X:02 Y:B1 P:A5 SP:FB CYC: 75 SL:144 +FAC2 B8 CLV A:FF X:02 Y:B1 P:A5 SP:F9 CYC: 93 SL:144 +FAC3 38 SEC A:FF X:02 Y:B1 P:A5 SP:F9 CYC: 99 SL:144 +FAC4 A9 FF LDA #$FF A:FF X:02 Y:B1 P:A5 SP:F9 CYC:105 SL:144 +FAC6 60 RTS A:FF X:02 Y:B1 P:A5 SP:F9 CYC:111 SL:144 +EC56 EF 47 06 *ISB $0647 = FF A:FF X:02 Y:B1 P:A5 SP:FB CYC:129 SL:144 +EC59 EA NOP A:FF X:02 Y:B1 P:A5 SP:FB CYC:147 SL:144 +EC5A EA NOP A:FF X:02 Y:B1 P:A5 SP:FB CYC:153 SL:144 +EC5B EA NOP A:FF X:02 Y:B1 P:A5 SP:FB CYC:159 SL:144 +EC5C EA NOP A:FF X:02 Y:B1 P:A5 SP:FB CYC:165 SL:144 +EC5D 20 C7 FA JSR $FAC7 A:FF X:02 Y:B1 P:A5 SP:FB CYC:171 SL:144 +FAC7 70 1D BVS $FAE6 A:FF X:02 Y:B1 P:A5 SP:F9 CYC:189 SL:144 +FAC9 F0 1B BEQ $FAE6 A:FF X:02 Y:B1 P:A5 SP:F9 CYC:195 SL:144 +FACB 10 19 BPL $FAE6 A:FF X:02 Y:B1 P:A5 SP:F9 CYC:201 SL:144 +FACD 90 17 BCC $FAE6 A:FF X:02 Y:B1 P:A5 SP:F9 CYC:207 SL:144 +FACF C9 FF CMP #$FF A:FF X:02 Y:B1 P:A5 SP:F9 CYC:213 SL:144 +FAD1 D0 13 BNE $FAE6 A:FF X:02 Y:B1 P:27 SP:F9 CYC:219 SL:144 +FAD3 60 RTS A:FF X:02 Y:B1 P:27 SP:F9 CYC:225 SL:144 +EC60 AD 47 06 LDA $0647 = 00 A:FF X:02 Y:B1 P:27 SP:FB CYC:243 SL:144 +EC63 C9 00 CMP #$00 A:00 X:02 Y:B1 P:27 SP:FB CYC:255 SL:144 +EC65 F0 02 BEQ $EC69 A:00 X:02 Y:B1 P:27 SP:FB CYC:261 SL:144 +EC69 C8 INY A:00 X:02 Y:B1 P:27 SP:FB CYC:270 SL:144 +EC6A A9 37 LDA #$37 A:00 X:02 Y:B2 P:A5 SP:FB CYC:276 SL:144 +EC6C 8D 47 06 STA $0647 = 00 A:37 X:02 Y:B2 P:25 SP:FB CYC:282 SL:144 +EC6F 20 D4 FA JSR $FAD4 A:37 X:02 Y:B2 P:25 SP:FB CYC:294 SL:144 +FAD4 24 01 BIT $01 = FF A:37 X:02 Y:B2 P:25 SP:F9 CYC:312 SL:144 +FAD6 38 SEC A:37 X:02 Y:B2 P:E5 SP:F9 CYC:321 SL:144 +FAD7 A9 F0 LDA #$F0 A:37 X:02 Y:B2 P:E5 SP:F9 CYC:327 SL:144 +FAD9 60 RTS A:F0 X:02 Y:B2 P:E5 SP:F9 CYC:333 SL:144 +EC72 EF 47 06 *ISB $0647 = 37 A:F0 X:02 Y:B2 P:E5 SP:FB CYC: 10 SL:145 +EC75 EA NOP A:B8 X:02 Y:B2 P:A5 SP:FB CYC: 28 SL:145 +EC76 EA NOP A:B8 X:02 Y:B2 P:A5 SP:FB CYC: 34 SL:145 +EC77 EA NOP A:B8 X:02 Y:B2 P:A5 SP:FB CYC: 40 SL:145 +EC78 EA NOP A:B8 X:02 Y:B2 P:A5 SP:FB CYC: 46 SL:145 +EC79 20 DA FA JSR $FADA A:B8 X:02 Y:B2 P:A5 SP:FB CYC: 52 SL:145 +FADA 70 0A BVS $FAE6 A:B8 X:02 Y:B2 P:A5 SP:F9 CYC: 70 SL:145 +FADC F0 08 BEQ $FAE6 A:B8 X:02 Y:B2 P:A5 SP:F9 CYC: 76 SL:145 +FADE 10 06 BPL $FAE6 A:B8 X:02 Y:B2 P:A5 SP:F9 CYC: 82 SL:145 +FAE0 90 04 BCC $FAE6 A:B8 X:02 Y:B2 P:A5 SP:F9 CYC: 88 SL:145 +FAE2 C9 B8 CMP #$B8 A:B8 X:02 Y:B2 P:A5 SP:F9 CYC: 94 SL:145 +FAE4 F0 02 BEQ $FAE8 A:B8 X:02 Y:B2 P:27 SP:F9 CYC:100 SL:145 +FAE8 60 RTS A:B8 X:02 Y:B2 P:27 SP:F9 CYC:109 SL:145 +EC7C AD 47 06 LDA $0647 = 38 A:B8 X:02 Y:B2 P:27 SP:FB CYC:127 SL:145 +EC7F C9 38 CMP #$38 A:38 X:02 Y:B2 P:25 SP:FB CYC:139 SL:145 +EC81 F0 02 BEQ $EC85 A:38 X:02 Y:B2 P:27 SP:FB CYC:145 SL:145 +EC85 A9 EB LDA #$EB A:38 X:02 Y:B2 P:27 SP:FB CYC:154 SL:145 +EC87 8D 47 06 STA $0647 = 38 A:EB X:02 Y:B2 P:A5 SP:FB CYC:160 SL:145 +EC8A A9 48 LDA #$48 A:EB X:02 Y:B2 P:A5 SP:FB CYC:172 SL:145 +EC8C 85 45 STA $45 = 48 A:48 X:02 Y:B2 P:25 SP:FB CYC:178 SL:145 +EC8E A9 05 LDA #$05 A:48 X:02 Y:B2 P:25 SP:FB CYC:187 SL:145 +EC90 85 46 STA $46 = 05 A:05 X:02 Y:B2 P:25 SP:FB CYC:193 SL:145 +EC92 A0 FF LDY #$FF A:05 X:02 Y:B2 P:25 SP:FB CYC:202 SL:145 +EC94 20 B1 FA JSR $FAB1 A:05 X:02 Y:FF P:A5 SP:FB CYC:208 SL:145 +FAB1 24 01 BIT $01 = FF A:05 X:02 Y:FF P:A5 SP:F9 CYC:226 SL:145 +FAB3 18 CLC A:05 X:02 Y:FF P:E5 SP:F9 CYC:235 SL:145 +FAB4 A9 40 LDA #$40 A:05 X:02 Y:FF P:E4 SP:F9 CYC:241 SL:145 +FAB6 60 RTS A:40 X:02 Y:FF P:64 SP:F9 CYC:247 SL:145 +EC97 F3 45 *ISB ($45),Y = 0548 @ 0647 = EB A:40 X:02 Y:FF P:64 SP:FB CYC:265 SL:145 +EC99 EA NOP A:53 X:02 Y:FF P:24 SP:FB CYC:289 SL:145 +EC9A EA NOP A:53 X:02 Y:FF P:24 SP:FB CYC:295 SL:145 +EC9B 08 PHP A:53 X:02 Y:FF P:24 SP:FB CYC:301 SL:145 +EC9C 48 PHA A:53 X:02 Y:FF P:24 SP:FA CYC:310 SL:145 +EC9D A0 B3 LDY #$B3 A:53 X:02 Y:FF P:24 SP:F9 CYC:319 SL:145 +EC9F 68 PLA A:53 X:02 Y:B3 P:A4 SP:F9 CYC:325 SL:145 +ECA0 28 PLP A:53 X:02 Y:B3 P:24 SP:FA CYC:337 SL:145 +ECA1 20 B7 FA JSR $FAB7 A:53 X:02 Y:B3 P:24 SP:FB CYC: 8 SL:146 +FAB7 70 2D BVS $FAE6 A:53 X:02 Y:B3 P:24 SP:F9 CYC: 26 SL:146 +FAB9 B0 2B BCS $FAE6 A:53 X:02 Y:B3 P:24 SP:F9 CYC: 32 SL:146 +FABB 30 29 BMI $FAE6 A:53 X:02 Y:B3 P:24 SP:F9 CYC: 38 SL:146 +FABD C9 53 CMP #$53 A:53 X:02 Y:B3 P:24 SP:F9 CYC: 44 SL:146 +FABF D0 25 BNE $FAE6 A:53 X:02 Y:B3 P:27 SP:F9 CYC: 50 SL:146 +FAC1 60 RTS A:53 X:02 Y:B3 P:27 SP:F9 CYC: 56 SL:146 +ECA4 AD 47 06 LDA $0647 = EC A:53 X:02 Y:B3 P:27 SP:FB CYC: 74 SL:146 +ECA7 C9 EC CMP #$EC A:EC X:02 Y:B3 P:A5 SP:FB CYC: 86 SL:146 +ECA9 F0 02 BEQ $ECAD A:EC X:02 Y:B3 P:27 SP:FB CYC: 92 SL:146 +ECAD A0 FF LDY #$FF A:EC X:02 Y:B3 P:27 SP:FB CYC:101 SL:146 +ECAF A9 FF LDA #$FF A:EC X:02 Y:FF P:A5 SP:FB CYC:107 SL:146 +ECB1 8D 47 06 STA $0647 = EC A:FF X:02 Y:FF P:A5 SP:FB CYC:113 SL:146 +ECB4 20 C2 FA JSR $FAC2 A:FF X:02 Y:FF P:A5 SP:FB CYC:125 SL:146 +FAC2 B8 CLV A:FF X:02 Y:FF P:A5 SP:F9 CYC:143 SL:146 +FAC3 38 SEC A:FF X:02 Y:FF P:A5 SP:F9 CYC:149 SL:146 +FAC4 A9 FF LDA #$FF A:FF X:02 Y:FF P:A5 SP:F9 CYC:155 SL:146 +FAC6 60 RTS A:FF X:02 Y:FF P:A5 SP:F9 CYC:161 SL:146 +ECB7 F3 45 *ISB ($45),Y = 0548 @ 0647 = FF A:FF X:02 Y:FF P:A5 SP:FB CYC:179 SL:146 +ECB9 EA NOP A:FF X:02 Y:FF P:A5 SP:FB CYC:203 SL:146 +ECBA EA NOP A:FF X:02 Y:FF P:A5 SP:FB CYC:209 SL:146 +ECBB 08 PHP A:FF X:02 Y:FF P:A5 SP:FB CYC:215 SL:146 +ECBC 48 PHA A:FF X:02 Y:FF P:A5 SP:FA CYC:224 SL:146 +ECBD A0 B4 LDY #$B4 A:FF X:02 Y:FF P:A5 SP:F9 CYC:233 SL:146 +ECBF 68 PLA A:FF X:02 Y:B4 P:A5 SP:F9 CYC:239 SL:146 +ECC0 28 PLP A:FF X:02 Y:B4 P:A5 SP:FA CYC:251 SL:146 +ECC1 20 C7 FA JSR $FAC7 A:FF X:02 Y:B4 P:A5 SP:FB CYC:263 SL:146 +FAC7 70 1D BVS $FAE6 A:FF X:02 Y:B4 P:A5 SP:F9 CYC:281 SL:146 +FAC9 F0 1B BEQ $FAE6 A:FF X:02 Y:B4 P:A5 SP:F9 CYC:287 SL:146 +FACB 10 19 BPL $FAE6 A:FF X:02 Y:B4 P:A5 SP:F9 CYC:293 SL:146 +FACD 90 17 BCC $FAE6 A:FF X:02 Y:B4 P:A5 SP:F9 CYC:299 SL:146 +FACF C9 FF CMP #$FF A:FF X:02 Y:B4 P:A5 SP:F9 CYC:305 SL:146 +FAD1 D0 13 BNE $FAE6 A:FF X:02 Y:B4 P:27 SP:F9 CYC:311 SL:146 +FAD3 60 RTS A:FF X:02 Y:B4 P:27 SP:F9 CYC:317 SL:146 +ECC4 AD 47 06 LDA $0647 = 00 A:FF X:02 Y:B4 P:27 SP:FB CYC:335 SL:146 +ECC7 C9 00 CMP #$00 A:00 X:02 Y:B4 P:27 SP:FB CYC: 6 SL:147 +ECC9 F0 02 BEQ $ECCD A:00 X:02 Y:B4 P:27 SP:FB CYC: 12 SL:147 +ECCD A0 FF LDY #$FF A:00 X:02 Y:B4 P:27 SP:FB CYC: 21 SL:147 +ECCF A9 37 LDA #$37 A:00 X:02 Y:FF P:A5 SP:FB CYC: 27 SL:147 +ECD1 8D 47 06 STA $0647 = 00 A:37 X:02 Y:FF P:25 SP:FB CYC: 33 SL:147 +ECD4 20 D4 FA JSR $FAD4 A:37 X:02 Y:FF P:25 SP:FB CYC: 45 SL:147 +FAD4 24 01 BIT $01 = FF A:37 X:02 Y:FF P:25 SP:F9 CYC: 63 SL:147 +FAD6 38 SEC A:37 X:02 Y:FF P:E5 SP:F9 CYC: 72 SL:147 +FAD7 A9 F0 LDA #$F0 A:37 X:02 Y:FF P:E5 SP:F9 CYC: 78 SL:147 +FAD9 60 RTS A:F0 X:02 Y:FF P:E5 SP:F9 CYC: 84 SL:147 +ECD7 F3 45 *ISB ($45),Y = 0548 @ 0647 = 37 A:F0 X:02 Y:FF P:E5 SP:FB CYC:102 SL:147 +ECD9 EA NOP A:B8 X:02 Y:FF P:A5 SP:FB CYC:126 SL:147 +ECDA EA NOP A:B8 X:02 Y:FF P:A5 SP:FB CYC:132 SL:147 +ECDB 08 PHP A:B8 X:02 Y:FF P:A5 SP:FB CYC:138 SL:147 +ECDC 48 PHA A:B8 X:02 Y:FF P:A5 SP:FA CYC:147 SL:147 +ECDD A0 B5 LDY #$B5 A:B8 X:02 Y:FF P:A5 SP:F9 CYC:156 SL:147 +ECDF 68 PLA A:B8 X:02 Y:B5 P:A5 SP:F9 CYC:162 SL:147 +ECE0 28 PLP A:B8 X:02 Y:B5 P:A5 SP:FA CYC:174 SL:147 +ECE1 20 DA FA JSR $FADA A:B8 X:02 Y:B5 P:A5 SP:FB CYC:186 SL:147 +FADA 70 0A BVS $FAE6 A:B8 X:02 Y:B5 P:A5 SP:F9 CYC:204 SL:147 +FADC F0 08 BEQ $FAE6 A:B8 X:02 Y:B5 P:A5 SP:F9 CYC:210 SL:147 +FADE 10 06 BPL $FAE6 A:B8 X:02 Y:B5 P:A5 SP:F9 CYC:216 SL:147 +FAE0 90 04 BCC $FAE6 A:B8 X:02 Y:B5 P:A5 SP:F9 CYC:222 SL:147 +FAE2 C9 B8 CMP #$B8 A:B8 X:02 Y:B5 P:A5 SP:F9 CYC:228 SL:147 +FAE4 F0 02 BEQ $FAE8 A:B8 X:02 Y:B5 P:27 SP:F9 CYC:234 SL:147 +FAE8 60 RTS A:B8 X:02 Y:B5 P:27 SP:F9 CYC:243 SL:147 +ECE4 AD 47 06 LDA $0647 = 38 A:B8 X:02 Y:B5 P:27 SP:FB CYC:261 SL:147 +ECE7 C9 38 CMP #$38 A:38 X:02 Y:B5 P:25 SP:FB CYC:273 SL:147 +ECE9 F0 02 BEQ $ECED A:38 X:02 Y:B5 P:27 SP:FB CYC:279 SL:147 +ECED A0 B6 LDY #$B6 A:38 X:02 Y:B5 P:27 SP:FB CYC:288 SL:147 +ECEF A2 FF LDX #$FF A:38 X:02 Y:B6 P:A5 SP:FB CYC:294 SL:147 +ECF1 A9 EB LDA #$EB A:38 X:FF Y:B6 P:A5 SP:FB CYC:300 SL:147 +ECF3 85 47 STA $47 = 38 A:EB X:FF Y:B6 P:A5 SP:FB CYC:306 SL:147 +ECF5 20 B1 FA JSR $FAB1 A:EB X:FF Y:B6 P:A5 SP:FB CYC:315 SL:147 +FAB1 24 01 BIT $01 = FF A:EB X:FF Y:B6 P:A5 SP:F9 CYC:333 SL:147 +FAB3 18 CLC A:EB X:FF Y:B6 P:E5 SP:F9 CYC: 1 SL:148 +FAB4 A9 40 LDA #$40 A:EB X:FF Y:B6 P:E4 SP:F9 CYC: 7 SL:148 +FAB6 60 RTS A:40 X:FF Y:B6 P:64 SP:F9 CYC: 13 SL:148 +ECF8 F7 48 *ISB $48,X @ 47 = EB A:40 X:FF Y:B6 P:64 SP:FB CYC: 31 SL:148 +ECFA EA NOP A:53 X:FF Y:B6 P:24 SP:FB CYC: 49 SL:148 +ECFB EA NOP A:53 X:FF Y:B6 P:24 SP:FB CYC: 55 SL:148 +ECFC EA NOP A:53 X:FF Y:B6 P:24 SP:FB CYC: 61 SL:148 +ECFD EA NOP A:53 X:FF Y:B6 P:24 SP:FB CYC: 67 SL:148 +ECFE 20 B7 FA JSR $FAB7 A:53 X:FF Y:B6 P:24 SP:FB CYC: 73 SL:148 +FAB7 70 2D BVS $FAE6 A:53 X:FF Y:B6 P:24 SP:F9 CYC: 91 SL:148 +FAB9 B0 2B BCS $FAE6 A:53 X:FF Y:B6 P:24 SP:F9 CYC: 97 SL:148 +FABB 30 29 BMI $FAE6 A:53 X:FF Y:B6 P:24 SP:F9 CYC:103 SL:148 +FABD C9 53 CMP #$53 A:53 X:FF Y:B6 P:24 SP:F9 CYC:109 SL:148 +FABF D0 25 BNE $FAE6 A:53 X:FF Y:B6 P:27 SP:F9 CYC:115 SL:148 +FAC1 60 RTS A:53 X:FF Y:B6 P:27 SP:F9 CYC:121 SL:148 +ED01 A5 47 LDA $47 = EC A:53 X:FF Y:B6 P:27 SP:FB CYC:139 SL:148 +ED03 C9 EC CMP #$EC A:EC X:FF Y:B6 P:A5 SP:FB CYC:148 SL:148 +ED05 F0 02 BEQ $ED09 A:EC X:FF Y:B6 P:27 SP:FB CYC:154 SL:148 +ED09 C8 INY A:EC X:FF Y:B6 P:27 SP:FB CYC:163 SL:148 +ED0A A9 FF LDA #$FF A:EC X:FF Y:B7 P:A5 SP:FB CYC:169 SL:148 +ED0C 85 47 STA $47 = EC A:FF X:FF Y:B7 P:A5 SP:FB CYC:175 SL:148 +ED0E 20 C2 FA JSR $FAC2 A:FF X:FF Y:B7 P:A5 SP:FB CYC:184 SL:148 +FAC2 B8 CLV A:FF X:FF Y:B7 P:A5 SP:F9 CYC:202 SL:148 +FAC3 38 SEC A:FF X:FF Y:B7 P:A5 SP:F9 CYC:208 SL:148 +FAC4 A9 FF LDA #$FF A:FF X:FF Y:B7 P:A5 SP:F9 CYC:214 SL:148 +FAC6 60 RTS A:FF X:FF Y:B7 P:A5 SP:F9 CYC:220 SL:148 +ED11 F7 48 *ISB $48,X @ 47 = FF A:FF X:FF Y:B7 P:A5 SP:FB CYC:238 SL:148 +ED13 EA NOP A:FF X:FF Y:B7 P:A5 SP:FB CYC:256 SL:148 +ED14 EA NOP A:FF X:FF Y:B7 P:A5 SP:FB CYC:262 SL:148 +ED15 EA NOP A:FF X:FF Y:B7 P:A5 SP:FB CYC:268 SL:148 +ED16 EA NOP A:FF X:FF Y:B7 P:A5 SP:FB CYC:274 SL:148 +ED17 20 C7 FA JSR $FAC7 A:FF X:FF Y:B7 P:A5 SP:FB CYC:280 SL:148 +FAC7 70 1D BVS $FAE6 A:FF X:FF Y:B7 P:A5 SP:F9 CYC:298 SL:148 +FAC9 F0 1B BEQ $FAE6 A:FF X:FF Y:B7 P:A5 SP:F9 CYC:304 SL:148 +FACB 10 19 BPL $FAE6 A:FF X:FF Y:B7 P:A5 SP:F9 CYC:310 SL:148 +FACD 90 17 BCC $FAE6 A:FF X:FF Y:B7 P:A5 SP:F9 CYC:316 SL:148 +FACF C9 FF CMP #$FF A:FF X:FF Y:B7 P:A5 SP:F9 CYC:322 SL:148 +FAD1 D0 13 BNE $FAE6 A:FF X:FF Y:B7 P:27 SP:F9 CYC:328 SL:148 +FAD3 60 RTS A:FF X:FF Y:B7 P:27 SP:F9 CYC:334 SL:148 +ED1A A5 47 LDA $47 = 00 A:FF X:FF Y:B7 P:27 SP:FB CYC: 11 SL:149 +ED1C C9 00 CMP #$00 A:00 X:FF Y:B7 P:27 SP:FB CYC: 20 SL:149 +ED1E F0 02 BEQ $ED22 A:00 X:FF Y:B7 P:27 SP:FB CYC: 26 SL:149 +ED22 C8 INY A:00 X:FF Y:B7 P:27 SP:FB CYC: 35 SL:149 +ED23 A9 37 LDA #$37 A:00 X:FF Y:B8 P:A5 SP:FB CYC: 41 SL:149 +ED25 85 47 STA $47 = 00 A:37 X:FF Y:B8 P:25 SP:FB CYC: 47 SL:149 +ED27 20 D4 FA JSR $FAD4 A:37 X:FF Y:B8 P:25 SP:FB CYC: 56 SL:149 +FAD4 24 01 BIT $01 = FF A:37 X:FF Y:B8 P:25 SP:F9 CYC: 74 SL:149 +FAD6 38 SEC A:37 X:FF Y:B8 P:E5 SP:F9 CYC: 83 SL:149 +FAD7 A9 F0 LDA #$F0 A:37 X:FF Y:B8 P:E5 SP:F9 CYC: 89 SL:149 +FAD9 60 RTS A:F0 X:FF Y:B8 P:E5 SP:F9 CYC: 95 SL:149 +ED2A F7 48 *ISB $48,X @ 47 = 37 A:F0 X:FF Y:B8 P:E5 SP:FB CYC:113 SL:149 +ED2C EA NOP A:B8 X:FF Y:B8 P:A5 SP:FB CYC:131 SL:149 +ED2D EA NOP A:B8 X:FF Y:B8 P:A5 SP:FB CYC:137 SL:149 +ED2E EA NOP A:B8 X:FF Y:B8 P:A5 SP:FB CYC:143 SL:149 +ED2F EA NOP A:B8 X:FF Y:B8 P:A5 SP:FB CYC:149 SL:149 +ED30 20 DA FA JSR $FADA A:B8 X:FF Y:B8 P:A5 SP:FB CYC:155 SL:149 +FADA 70 0A BVS $FAE6 A:B8 X:FF Y:B8 P:A5 SP:F9 CYC:173 SL:149 +FADC F0 08 BEQ $FAE6 A:B8 X:FF Y:B8 P:A5 SP:F9 CYC:179 SL:149 +FADE 10 06 BPL $FAE6 A:B8 X:FF Y:B8 P:A5 SP:F9 CYC:185 SL:149 +FAE0 90 04 BCC $FAE6 A:B8 X:FF Y:B8 P:A5 SP:F9 CYC:191 SL:149 +FAE2 C9 B8 CMP #$B8 A:B8 X:FF Y:B8 P:A5 SP:F9 CYC:197 SL:149 +FAE4 F0 02 BEQ $FAE8 A:B8 X:FF Y:B8 P:27 SP:F9 CYC:203 SL:149 +FAE8 60 RTS A:B8 X:FF Y:B8 P:27 SP:F9 CYC:212 SL:149 +ED33 A5 47 LDA $47 = 38 A:B8 X:FF Y:B8 P:27 SP:FB CYC:230 SL:149 +ED35 C9 38 CMP #$38 A:38 X:FF Y:B8 P:25 SP:FB CYC:239 SL:149 +ED37 F0 02 BEQ $ED3B A:38 X:FF Y:B8 P:27 SP:FB CYC:245 SL:149 +ED3B A9 EB LDA #$EB A:38 X:FF Y:B8 P:27 SP:FB CYC:254 SL:149 +ED3D 8D 47 06 STA $0647 = 38 A:EB X:FF Y:B8 P:A5 SP:FB CYC:260 SL:149 +ED40 A0 FF LDY #$FF A:EB X:FF Y:B8 P:A5 SP:FB CYC:272 SL:149 +ED42 20 B1 FA JSR $FAB1 A:EB X:FF Y:FF P:A5 SP:FB CYC:278 SL:149 +FAB1 24 01 BIT $01 = FF A:EB X:FF Y:FF P:A5 SP:F9 CYC:296 SL:149 +FAB3 18 CLC A:EB X:FF Y:FF P:E5 SP:F9 CYC:305 SL:149 +FAB4 A9 40 LDA #$40 A:EB X:FF Y:FF P:E4 SP:F9 CYC:311 SL:149 +FAB6 60 RTS A:40 X:FF Y:FF P:64 SP:F9 CYC:317 SL:149 +ED45 FB 48 05 *ISB $0548,Y @ 0647 = EB A:40 X:FF Y:FF P:64 SP:FB CYC:335 SL:149 +ED48 EA NOP A:53 X:FF Y:FF P:24 SP:FB CYC: 15 SL:150 +ED49 EA NOP A:53 X:FF Y:FF P:24 SP:FB CYC: 21 SL:150 +ED4A 08 PHP A:53 X:FF Y:FF P:24 SP:FB CYC: 27 SL:150 +ED4B 48 PHA A:53 X:FF Y:FF P:24 SP:FA CYC: 36 SL:150 +ED4C A0 B9 LDY #$B9 A:53 X:FF Y:FF P:24 SP:F9 CYC: 45 SL:150 +ED4E 68 PLA A:53 X:FF Y:B9 P:A4 SP:F9 CYC: 51 SL:150 +ED4F 28 PLP A:53 X:FF Y:B9 P:24 SP:FA CYC: 63 SL:150 +ED50 20 B7 FA JSR $FAB7 A:53 X:FF Y:B9 P:24 SP:FB CYC: 75 SL:150 +FAB7 70 2D BVS $FAE6 A:53 X:FF Y:B9 P:24 SP:F9 CYC: 93 SL:150 +FAB9 B0 2B BCS $FAE6 A:53 X:FF Y:B9 P:24 SP:F9 CYC: 99 SL:150 +FABB 30 29 BMI $FAE6 A:53 X:FF Y:B9 P:24 SP:F9 CYC:105 SL:150 +FABD C9 53 CMP #$53 A:53 X:FF Y:B9 P:24 SP:F9 CYC:111 SL:150 +FABF D0 25 BNE $FAE6 A:53 X:FF Y:B9 P:27 SP:F9 CYC:117 SL:150 +FAC1 60 RTS A:53 X:FF Y:B9 P:27 SP:F9 CYC:123 SL:150 +ED53 AD 47 06 LDA $0647 = EC A:53 X:FF Y:B9 P:27 SP:FB CYC:141 SL:150 +ED56 C9 EC CMP #$EC A:EC X:FF Y:B9 P:A5 SP:FB CYC:153 SL:150 +ED58 F0 02 BEQ $ED5C A:EC X:FF Y:B9 P:27 SP:FB CYC:159 SL:150 +ED5C A0 FF LDY #$FF A:EC X:FF Y:B9 P:27 SP:FB CYC:168 SL:150 +ED5E A9 FF LDA #$FF A:EC X:FF Y:FF P:A5 SP:FB CYC:174 SL:150 +ED60 8D 47 06 STA $0647 = EC A:FF X:FF Y:FF P:A5 SP:FB CYC:180 SL:150 +ED63 20 C2 FA JSR $FAC2 A:FF X:FF Y:FF P:A5 SP:FB CYC:192 SL:150 +FAC2 B8 CLV A:FF X:FF Y:FF P:A5 SP:F9 CYC:210 SL:150 +FAC3 38 SEC A:FF X:FF Y:FF P:A5 SP:F9 CYC:216 SL:150 +FAC4 A9 FF LDA #$FF A:FF X:FF Y:FF P:A5 SP:F9 CYC:222 SL:150 +FAC6 60 RTS A:FF X:FF Y:FF P:A5 SP:F9 CYC:228 SL:150 +ED66 FB 48 05 *ISB $0548,Y @ 0647 = FF A:FF X:FF Y:FF P:A5 SP:FB CYC:246 SL:150 +ED69 EA NOP A:FF X:FF Y:FF P:A5 SP:FB CYC:267 SL:150 +ED6A EA NOP A:FF X:FF Y:FF P:A5 SP:FB CYC:273 SL:150 +ED6B 08 PHP A:FF X:FF Y:FF P:A5 SP:FB CYC:279 SL:150 +ED6C 48 PHA A:FF X:FF Y:FF P:A5 SP:FA CYC:288 SL:150 +ED6D A0 BA LDY #$BA A:FF X:FF Y:FF P:A5 SP:F9 CYC:297 SL:150 +ED6F 68 PLA A:FF X:FF Y:BA P:A5 SP:F9 CYC:303 SL:150 +ED70 28 PLP A:FF X:FF Y:BA P:A5 SP:FA CYC:315 SL:150 +ED71 20 C7 FA JSR $FAC7 A:FF X:FF Y:BA P:A5 SP:FB CYC:327 SL:150 +FAC7 70 1D BVS $FAE6 A:FF X:FF Y:BA P:A5 SP:F9 CYC: 4 SL:151 +FAC9 F0 1B BEQ $FAE6 A:FF X:FF Y:BA P:A5 SP:F9 CYC: 10 SL:151 +FACB 10 19 BPL $FAE6 A:FF X:FF Y:BA P:A5 SP:F9 CYC: 16 SL:151 +FACD 90 17 BCC $FAE6 A:FF X:FF Y:BA P:A5 SP:F9 CYC: 22 SL:151 +FACF C9 FF CMP #$FF A:FF X:FF Y:BA P:A5 SP:F9 CYC: 28 SL:151 +FAD1 D0 13 BNE $FAE6 A:FF X:FF Y:BA P:27 SP:F9 CYC: 34 SL:151 +FAD3 60 RTS A:FF X:FF Y:BA P:27 SP:F9 CYC: 40 SL:151 +ED74 AD 47 06 LDA $0647 = 00 A:FF X:FF Y:BA P:27 SP:FB CYC: 58 SL:151 +ED77 C9 00 CMP #$00 A:00 X:FF Y:BA P:27 SP:FB CYC: 70 SL:151 +ED79 F0 02 BEQ $ED7D A:00 X:FF Y:BA P:27 SP:FB CYC: 76 SL:151 +ED7D A0 FF LDY #$FF A:00 X:FF Y:BA P:27 SP:FB CYC: 85 SL:151 +ED7F A9 37 LDA #$37 A:00 X:FF Y:FF P:A5 SP:FB CYC: 91 SL:151 +ED81 8D 47 06 STA $0647 = 00 A:37 X:FF Y:FF P:25 SP:FB CYC: 97 SL:151 +ED84 20 D4 FA JSR $FAD4 A:37 X:FF Y:FF P:25 SP:FB CYC:109 SL:151 +FAD4 24 01 BIT $01 = FF A:37 X:FF Y:FF P:25 SP:F9 CYC:127 SL:151 +FAD6 38 SEC A:37 X:FF Y:FF P:E5 SP:F9 CYC:136 SL:151 +FAD7 A9 F0 LDA #$F0 A:37 X:FF Y:FF P:E5 SP:F9 CYC:142 SL:151 +FAD9 60 RTS A:F0 X:FF Y:FF P:E5 SP:F9 CYC:148 SL:151 +ED87 FB 48 05 *ISB $0548,Y @ 0647 = 37 A:F0 X:FF Y:FF P:E5 SP:FB CYC:166 SL:151 +ED8A EA NOP A:B8 X:FF Y:FF P:A5 SP:FB CYC:187 SL:151 +ED8B EA NOP A:B8 X:FF Y:FF P:A5 SP:FB CYC:193 SL:151 +ED8C 08 PHP A:B8 X:FF Y:FF P:A5 SP:FB CYC:199 SL:151 +ED8D 48 PHA A:B8 X:FF Y:FF P:A5 SP:FA CYC:208 SL:151 +ED8E A0 BB LDY #$BB A:B8 X:FF Y:FF P:A5 SP:F9 CYC:217 SL:151 +ED90 68 PLA A:B8 X:FF Y:BB P:A5 SP:F9 CYC:223 SL:151 +ED91 28 PLP A:B8 X:FF Y:BB P:A5 SP:FA CYC:235 SL:151 +ED92 20 DA FA JSR $FADA A:B8 X:FF Y:BB P:A5 SP:FB CYC:247 SL:151 +FADA 70 0A BVS $FAE6 A:B8 X:FF Y:BB P:A5 SP:F9 CYC:265 SL:151 +FADC F0 08 BEQ $FAE6 A:B8 X:FF Y:BB P:A5 SP:F9 CYC:271 SL:151 +FADE 10 06 BPL $FAE6 A:B8 X:FF Y:BB P:A5 SP:F9 CYC:277 SL:151 +FAE0 90 04 BCC $FAE6 A:B8 X:FF Y:BB P:A5 SP:F9 CYC:283 SL:151 +FAE2 C9 B8 CMP #$B8 A:B8 X:FF Y:BB P:A5 SP:F9 CYC:289 SL:151 +FAE4 F0 02 BEQ $FAE8 A:B8 X:FF Y:BB P:27 SP:F9 CYC:295 SL:151 +FAE8 60 RTS A:B8 X:FF Y:BB P:27 SP:F9 CYC:304 SL:151 +ED95 AD 47 06 LDA $0647 = 38 A:B8 X:FF Y:BB P:27 SP:FB CYC:322 SL:151 +ED98 C9 38 CMP #$38 A:38 X:FF Y:BB P:25 SP:FB CYC:334 SL:151 +ED9A F0 02 BEQ $ED9E A:38 X:FF Y:BB P:27 SP:FB CYC:340 SL:151 +ED9E A0 BC LDY #$BC A:38 X:FF Y:BB P:27 SP:FB CYC: 8 SL:152 +EDA0 A2 FF LDX #$FF A:38 X:FF Y:BC P:A5 SP:FB CYC: 14 SL:152 +EDA2 A9 EB LDA #$EB A:38 X:FF Y:BC P:A5 SP:FB CYC: 20 SL:152 +EDA4 8D 47 06 STA $0647 = 38 A:EB X:FF Y:BC P:A5 SP:FB CYC: 26 SL:152 +EDA7 20 B1 FA JSR $FAB1 A:EB X:FF Y:BC P:A5 SP:FB CYC: 38 SL:152 +FAB1 24 01 BIT $01 = FF A:EB X:FF Y:BC P:A5 SP:F9 CYC: 56 SL:152 +FAB3 18 CLC A:EB X:FF Y:BC P:E5 SP:F9 CYC: 65 SL:152 +FAB4 A9 40 LDA #$40 A:EB X:FF Y:BC P:E4 SP:F9 CYC: 71 SL:152 +FAB6 60 RTS A:40 X:FF Y:BC P:64 SP:F9 CYC: 77 SL:152 +EDAA FF 48 05 *ISB $0548,X @ 0647 = EB A:40 X:FF Y:BC P:64 SP:FB CYC: 95 SL:152 +EDAD EA NOP A:53 X:FF Y:BC P:24 SP:FB CYC:116 SL:152 +EDAE EA NOP A:53 X:FF Y:BC P:24 SP:FB CYC:122 SL:152 +EDAF EA NOP A:53 X:FF Y:BC P:24 SP:FB CYC:128 SL:152 +EDB0 EA NOP A:53 X:FF Y:BC P:24 SP:FB CYC:134 SL:152 +EDB1 20 B7 FA JSR $FAB7 A:53 X:FF Y:BC P:24 SP:FB CYC:140 SL:152 +FAB7 70 2D BVS $FAE6 A:53 X:FF Y:BC P:24 SP:F9 CYC:158 SL:152 +FAB9 B0 2B BCS $FAE6 A:53 X:FF Y:BC P:24 SP:F9 CYC:164 SL:152 +FABB 30 29 BMI $FAE6 A:53 X:FF Y:BC P:24 SP:F9 CYC:170 SL:152 +FABD C9 53 CMP #$53 A:53 X:FF Y:BC P:24 SP:F9 CYC:176 SL:152 +FABF D0 25 BNE $FAE6 A:53 X:FF Y:BC P:27 SP:F9 CYC:182 SL:152 +FAC1 60 RTS A:53 X:FF Y:BC P:27 SP:F9 CYC:188 SL:152 +EDB4 AD 47 06 LDA $0647 = EC A:53 X:FF Y:BC P:27 SP:FB CYC:206 SL:152 +EDB7 C9 EC CMP #$EC A:EC X:FF Y:BC P:A5 SP:FB CYC:218 SL:152 +EDB9 F0 02 BEQ $EDBD A:EC X:FF Y:BC P:27 SP:FB CYC:224 SL:152 +EDBD C8 INY A:EC X:FF Y:BC P:27 SP:FB CYC:233 SL:152 +EDBE A9 FF LDA #$FF A:EC X:FF Y:BD P:A5 SP:FB CYC:239 SL:152 +EDC0 8D 47 06 STA $0647 = EC A:FF X:FF Y:BD P:A5 SP:FB CYC:245 SL:152 +EDC3 20 C2 FA JSR $FAC2 A:FF X:FF Y:BD P:A5 SP:FB CYC:257 SL:152 +FAC2 B8 CLV A:FF X:FF Y:BD P:A5 SP:F9 CYC:275 SL:152 +FAC3 38 SEC A:FF X:FF Y:BD P:A5 SP:F9 CYC:281 SL:152 +FAC4 A9 FF LDA #$FF A:FF X:FF Y:BD P:A5 SP:F9 CYC:287 SL:152 +FAC6 60 RTS A:FF X:FF Y:BD P:A5 SP:F9 CYC:293 SL:152 +EDC6 FF 48 05 *ISB $0548,X @ 0647 = FF A:FF X:FF Y:BD P:A5 SP:FB CYC:311 SL:152 +EDC9 EA NOP A:FF X:FF Y:BD P:A5 SP:FB CYC:332 SL:152 +EDCA EA NOP A:FF X:FF Y:BD P:A5 SP:FB CYC:338 SL:152 +EDCB EA NOP A:FF X:FF Y:BD P:A5 SP:FB CYC: 3 SL:153 +EDCC EA NOP A:FF X:FF Y:BD P:A5 SP:FB CYC: 9 SL:153 +EDCD 20 C7 FA JSR $FAC7 A:FF X:FF Y:BD P:A5 SP:FB CYC: 15 SL:153 +FAC7 70 1D BVS $FAE6 A:FF X:FF Y:BD P:A5 SP:F9 CYC: 33 SL:153 +FAC9 F0 1B BEQ $FAE6 A:FF X:FF Y:BD P:A5 SP:F9 CYC: 39 SL:153 +FACB 10 19 BPL $FAE6 A:FF X:FF Y:BD P:A5 SP:F9 CYC: 45 SL:153 +FACD 90 17 BCC $FAE6 A:FF X:FF Y:BD P:A5 SP:F9 CYC: 51 SL:153 +FACF C9 FF CMP #$FF A:FF X:FF Y:BD P:A5 SP:F9 CYC: 57 SL:153 +FAD1 D0 13 BNE $FAE6 A:FF X:FF Y:BD P:27 SP:F9 CYC: 63 SL:153 +FAD3 60 RTS A:FF X:FF Y:BD P:27 SP:F9 CYC: 69 SL:153 +EDD0 AD 47 06 LDA $0647 = 00 A:FF X:FF Y:BD P:27 SP:FB CYC: 87 SL:153 +EDD3 C9 00 CMP #$00 A:00 X:FF Y:BD P:27 SP:FB CYC: 99 SL:153 +EDD5 F0 02 BEQ $EDD9 A:00 X:FF Y:BD P:27 SP:FB CYC:105 SL:153 +EDD9 C8 INY A:00 X:FF Y:BD P:27 SP:FB CYC:114 SL:153 +EDDA A9 37 LDA #$37 A:00 X:FF Y:BE P:A5 SP:FB CYC:120 SL:153 +EDDC 8D 47 06 STA $0647 = 00 A:37 X:FF Y:BE P:25 SP:FB CYC:126 SL:153 +EDDF 20 D4 FA JSR $FAD4 A:37 X:FF Y:BE P:25 SP:FB CYC:138 SL:153 +FAD4 24 01 BIT $01 = FF A:37 X:FF Y:BE P:25 SP:F9 CYC:156 SL:153 +FAD6 38 SEC A:37 X:FF Y:BE P:E5 SP:F9 CYC:165 SL:153 +FAD7 A9 F0 LDA #$F0 A:37 X:FF Y:BE P:E5 SP:F9 CYC:171 SL:153 +FAD9 60 RTS A:F0 X:FF Y:BE P:E5 SP:F9 CYC:177 SL:153 +EDE2 FF 48 05 *ISB $0548,X @ 0647 = 37 A:F0 X:FF Y:BE P:E5 SP:FB CYC:195 SL:153 +EDE5 EA NOP A:B8 X:FF Y:BE P:A5 SP:FB CYC:216 SL:153 +EDE6 EA NOP A:B8 X:FF Y:BE P:A5 SP:FB CYC:222 SL:153 +EDE7 EA NOP A:B8 X:FF Y:BE P:A5 SP:FB CYC:228 SL:153 +EDE8 EA NOP A:B8 X:FF Y:BE P:A5 SP:FB CYC:234 SL:153 +EDE9 20 DA FA JSR $FADA A:B8 X:FF Y:BE P:A5 SP:FB CYC:240 SL:153 +FADA 70 0A BVS $FAE6 A:B8 X:FF Y:BE P:A5 SP:F9 CYC:258 SL:153 +FADC F0 08 BEQ $FAE6 A:B8 X:FF Y:BE P:A5 SP:F9 CYC:264 SL:153 +FADE 10 06 BPL $FAE6 A:B8 X:FF Y:BE P:A5 SP:F9 CYC:270 SL:153 +FAE0 90 04 BCC $FAE6 A:B8 X:FF Y:BE P:A5 SP:F9 CYC:276 SL:153 +FAE2 C9 B8 CMP #$B8 A:B8 X:FF Y:BE P:A5 SP:F9 CYC:282 SL:153 +FAE4 F0 02 BEQ $FAE8 A:B8 X:FF Y:BE P:27 SP:F9 CYC:288 SL:153 +FAE8 60 RTS A:B8 X:FF Y:BE P:27 SP:F9 CYC:297 SL:153 +EDEC AD 47 06 LDA $0647 = 38 A:B8 X:FF Y:BE P:27 SP:FB CYC:315 SL:153 +EDEF C9 38 CMP #$38 A:38 X:FF Y:BE P:25 SP:FB CYC:327 SL:153 +EDF1 F0 02 BEQ $EDF5 A:38 X:FF Y:BE P:27 SP:FB CYC:333 SL:153 +EDF5 60 RTS A:38 X:FF Y:BE P:27 SP:FB CYC: 1 SL:154 +C641 20 F6 ED JSR $EDF6 A:38 X:FF Y:BE P:27 SP:FD CYC: 19 SL:154 +EDF6 A9 FF LDA #$FF A:38 X:FF Y:BE P:27 SP:FB CYC: 37 SL:154 +EDF8 85 01 STA $01 = FF A:FF X:FF Y:BE P:A5 SP:FB CYC: 43 SL:154 +EDFA A0 BF LDY #$BF A:FF X:FF Y:BE P:A5 SP:FB CYC: 52 SL:154 +EDFC A2 02 LDX #$02 A:FF X:FF Y:BF P:A5 SP:FB CYC: 58 SL:154 +EDFE A9 47 LDA #$47 A:FF X:02 Y:BF P:25 SP:FB CYC: 64 SL:154 +EE00 85 47 STA $47 = 38 A:47 X:02 Y:BF P:25 SP:FB CYC: 70 SL:154 +EE02 A9 06 LDA #$06 A:47 X:02 Y:BF P:25 SP:FB CYC: 79 SL:154 +EE04 85 48 STA $48 = 06 A:06 X:02 Y:BF P:25 SP:FB CYC: 85 SL:154 +EE06 A9 A5 LDA #$A5 A:06 X:02 Y:BF P:25 SP:FB CYC: 94 SL:154 +EE08 8D 47 06 STA $0647 = 38 A:A5 X:02 Y:BF P:A5 SP:FB CYC:100 SL:154 +EE0B 20 7B FA JSR $FA7B A:A5 X:02 Y:BF P:A5 SP:FB CYC:112 SL:154 +FA7B 24 01 BIT $01 = FF A:A5 X:02 Y:BF P:A5 SP:F9 CYC:130 SL:154 +FA7D 18 CLC A:A5 X:02 Y:BF P:E5 SP:F9 CYC:139 SL:154 +FA7E A9 B3 LDA #$B3 A:A5 X:02 Y:BF P:E4 SP:F9 CYC:145 SL:154 +FA80 60 RTS A:B3 X:02 Y:BF P:E4 SP:F9 CYC:151 SL:154 +EE0E 03 45 *SLO ($45,X) @ 47 = 0647 = A5 A:B3 X:02 Y:BF P:E4 SP:FB CYC:169 SL:154 +EE10 EA NOP A:FB X:02 Y:BF P:E5 SP:FB CYC:193 SL:154 +EE11 EA NOP A:FB X:02 Y:BF P:E5 SP:FB CYC:199 SL:154 +EE12 EA NOP A:FB X:02 Y:BF P:E5 SP:FB CYC:205 SL:154 +EE13 EA NOP A:FB X:02 Y:BF P:E5 SP:FB CYC:211 SL:154 +EE14 20 81 FA JSR $FA81 A:FB X:02 Y:BF P:E5 SP:FB CYC:217 SL:154 +FA81 50 63 BVC $FAE6 A:FB X:02 Y:BF P:E5 SP:F9 CYC:235 SL:154 +FA83 90 61 BCC $FAE6 A:FB X:02 Y:BF P:E5 SP:F9 CYC:241 SL:154 +FA85 10 5F BPL $FAE6 A:FB X:02 Y:BF P:E5 SP:F9 CYC:247 SL:154 +FA87 C9 FB CMP #$FB A:FB X:02 Y:BF P:E5 SP:F9 CYC:253 SL:154 +FA89 D0 5B BNE $FAE6 A:FB X:02 Y:BF P:67 SP:F9 CYC:259 SL:154 +FA8B 60 RTS A:FB X:02 Y:BF P:67 SP:F9 CYC:265 SL:154 +EE17 AD 47 06 LDA $0647 = 4A A:FB X:02 Y:BF P:67 SP:FB CYC:283 SL:154 +EE1A C9 4A CMP #$4A A:4A X:02 Y:BF P:65 SP:FB CYC:295 SL:154 +EE1C F0 02 BEQ $EE20 A:4A X:02 Y:BF P:67 SP:FB CYC:301 SL:154 +EE20 C8 INY A:4A X:02 Y:BF P:67 SP:FB CYC:310 SL:154 +EE21 A9 29 LDA #$29 A:4A X:02 Y:C0 P:E5 SP:FB CYC:316 SL:154 +EE23 8D 47 06 STA $0647 = 4A A:29 X:02 Y:C0 P:65 SP:FB CYC:322 SL:154 +EE26 20 8C FA JSR $FA8C A:29 X:02 Y:C0 P:65 SP:FB CYC:334 SL:154 +FA8C B8 CLV A:29 X:02 Y:C0 P:65 SP:F9 CYC: 11 SL:155 +FA8D 18 CLC A:29 X:02 Y:C0 P:25 SP:F9 CYC: 17 SL:155 +FA8E A9 C3 LDA #$C3 A:29 X:02 Y:C0 P:24 SP:F9 CYC: 23 SL:155 +FA90 60 RTS A:C3 X:02 Y:C0 P:A4 SP:F9 CYC: 29 SL:155 +EE29 03 45 *SLO ($45,X) @ 47 = 0647 = 29 A:C3 X:02 Y:C0 P:A4 SP:FB CYC: 47 SL:155 +EE2B EA NOP A:D3 X:02 Y:C0 P:A4 SP:FB CYC: 71 SL:155 +EE2C EA NOP A:D3 X:02 Y:C0 P:A4 SP:FB CYC: 77 SL:155 +EE2D EA NOP A:D3 X:02 Y:C0 P:A4 SP:FB CYC: 83 SL:155 +EE2E EA NOP A:D3 X:02 Y:C0 P:A4 SP:FB CYC: 89 SL:155 +EE2F 20 91 FA JSR $FA91 A:D3 X:02 Y:C0 P:A4 SP:FB CYC: 95 SL:155 +FA91 70 53 BVS $FAE6 A:D3 X:02 Y:C0 P:A4 SP:F9 CYC:113 SL:155 +FA93 F0 51 BEQ $FAE6 A:D3 X:02 Y:C0 P:A4 SP:F9 CYC:119 SL:155 +FA95 10 4F BPL $FAE6 A:D3 X:02 Y:C0 P:A4 SP:F9 CYC:125 SL:155 +FA97 B0 4D BCS $FAE6 A:D3 X:02 Y:C0 P:A4 SP:F9 CYC:131 SL:155 +FA99 C9 D3 CMP #$D3 A:D3 X:02 Y:C0 P:A4 SP:F9 CYC:137 SL:155 +FA9B D0 49 BNE $FAE6 A:D3 X:02 Y:C0 P:27 SP:F9 CYC:143 SL:155 +FA9D 60 RTS A:D3 X:02 Y:C0 P:27 SP:F9 CYC:149 SL:155 +EE32 AD 47 06 LDA $0647 = 52 A:D3 X:02 Y:C0 P:27 SP:FB CYC:167 SL:155 +EE35 C9 52 CMP #$52 A:52 X:02 Y:C0 P:25 SP:FB CYC:179 SL:155 +EE37 F0 02 BEQ $EE3B A:52 X:02 Y:C0 P:27 SP:FB CYC:185 SL:155 +EE3B C8 INY A:52 X:02 Y:C0 P:27 SP:FB CYC:194 SL:155 +EE3C A9 37 LDA #$37 A:52 X:02 Y:C1 P:A5 SP:FB CYC:200 SL:155 +EE3E 8D 47 06 STA $0647 = 52 A:37 X:02 Y:C1 P:25 SP:FB CYC:206 SL:155 +EE41 20 9E FA JSR $FA9E A:37 X:02 Y:C1 P:25 SP:FB CYC:218 SL:155 +FA9E 24 01 BIT $01 = FF A:37 X:02 Y:C1 P:25 SP:F9 CYC:236 SL:155 +FAA0 38 SEC A:37 X:02 Y:C1 P:E5 SP:F9 CYC:245 SL:155 +FAA1 A9 10 LDA #$10 A:37 X:02 Y:C1 P:E5 SP:F9 CYC:251 SL:155 +FAA3 60 RTS A:10 X:02 Y:C1 P:65 SP:F9 CYC:257 SL:155 +EE44 03 45 *SLO ($45,X) @ 47 = 0647 = 37 A:10 X:02 Y:C1 P:65 SP:FB CYC:275 SL:155 +EE46 EA NOP A:7E X:02 Y:C1 P:64 SP:FB CYC:299 SL:155 +EE47 EA NOP A:7E X:02 Y:C1 P:64 SP:FB CYC:305 SL:155 +EE48 EA NOP A:7E X:02 Y:C1 P:64 SP:FB CYC:311 SL:155 +EE49 EA NOP A:7E X:02 Y:C1 P:64 SP:FB CYC:317 SL:155 +EE4A 20 A4 FA JSR $FAA4 A:7E X:02 Y:C1 P:64 SP:FB CYC:323 SL:155 +FAA4 50 40 BVC $FAE6 A:7E X:02 Y:C1 P:64 SP:F9 CYC: 0 SL:156 +FAA6 F0 3E BEQ $FAE6 A:7E X:02 Y:C1 P:64 SP:F9 CYC: 6 SL:156 +FAA8 30 3C BMI $FAE6 A:7E X:02 Y:C1 P:64 SP:F9 CYC: 12 SL:156 +FAAA B0 3A BCS $FAE6 A:7E X:02 Y:C1 P:64 SP:F9 CYC: 18 SL:156 +FAAC C9 7E CMP #$7E A:7E X:02 Y:C1 P:64 SP:F9 CYC: 24 SL:156 +FAAE D0 36 BNE $FAE6 A:7E X:02 Y:C1 P:67 SP:F9 CYC: 30 SL:156 +FAB0 60 RTS A:7E X:02 Y:C1 P:67 SP:F9 CYC: 36 SL:156 +EE4D AD 47 06 LDA $0647 = 6E A:7E X:02 Y:C1 P:67 SP:FB CYC: 54 SL:156 +EE50 C9 6E CMP #$6E A:6E X:02 Y:C1 P:65 SP:FB CYC: 66 SL:156 +EE52 F0 02 BEQ $EE56 A:6E X:02 Y:C1 P:67 SP:FB CYC: 72 SL:156 +EE56 C8 INY A:6E X:02 Y:C1 P:67 SP:FB CYC: 81 SL:156 +EE57 A9 A5 LDA #$A5 A:6E X:02 Y:C2 P:E5 SP:FB CYC: 87 SL:156 +EE59 85 47 STA $47 = 47 A:A5 X:02 Y:C2 P:E5 SP:FB CYC: 93 SL:156 +EE5B 20 7B FA JSR $FA7B A:A5 X:02 Y:C2 P:E5 SP:FB CYC:102 SL:156 +FA7B 24 01 BIT $01 = FF A:A5 X:02 Y:C2 P:E5 SP:F9 CYC:120 SL:156 +FA7D 18 CLC A:A5 X:02 Y:C2 P:E5 SP:F9 CYC:129 SL:156 +FA7E A9 B3 LDA #$B3 A:A5 X:02 Y:C2 P:E4 SP:F9 CYC:135 SL:156 +FA80 60 RTS A:B3 X:02 Y:C2 P:E4 SP:F9 CYC:141 SL:156 +EE5E 07 47 *SLO $47 = A5 A:B3 X:02 Y:C2 P:E4 SP:FB CYC:159 SL:156 +EE60 EA NOP A:FB X:02 Y:C2 P:E5 SP:FB CYC:174 SL:156 +EE61 EA NOP A:FB X:02 Y:C2 P:E5 SP:FB CYC:180 SL:156 +EE62 EA NOP A:FB X:02 Y:C2 P:E5 SP:FB CYC:186 SL:156 +EE63 EA NOP A:FB X:02 Y:C2 P:E5 SP:FB CYC:192 SL:156 +EE64 20 81 FA JSR $FA81 A:FB X:02 Y:C2 P:E5 SP:FB CYC:198 SL:156 +FA81 50 63 BVC $FAE6 A:FB X:02 Y:C2 P:E5 SP:F9 CYC:216 SL:156 +FA83 90 61 BCC $FAE6 A:FB X:02 Y:C2 P:E5 SP:F9 CYC:222 SL:156 +FA85 10 5F BPL $FAE6 A:FB X:02 Y:C2 P:E5 SP:F9 CYC:228 SL:156 +FA87 C9 FB CMP #$FB A:FB X:02 Y:C2 P:E5 SP:F9 CYC:234 SL:156 +FA89 D0 5B BNE $FAE6 A:FB X:02 Y:C2 P:67 SP:F9 CYC:240 SL:156 +FA8B 60 RTS A:FB X:02 Y:C2 P:67 SP:F9 CYC:246 SL:156 +EE67 A5 47 LDA $47 = 4A A:FB X:02 Y:C2 P:67 SP:FB CYC:264 SL:156 +EE69 C9 4A CMP #$4A A:4A X:02 Y:C2 P:65 SP:FB CYC:273 SL:156 +EE6B F0 02 BEQ $EE6F A:4A X:02 Y:C2 P:67 SP:FB CYC:279 SL:156 +EE6F C8 INY A:4A X:02 Y:C2 P:67 SP:FB CYC:288 SL:156 +EE70 A9 29 LDA #$29 A:4A X:02 Y:C3 P:E5 SP:FB CYC:294 SL:156 +EE72 85 47 STA $47 = 4A A:29 X:02 Y:C3 P:65 SP:FB CYC:300 SL:156 +EE74 20 8C FA JSR $FA8C A:29 X:02 Y:C3 P:65 SP:FB CYC:309 SL:156 +FA8C B8 CLV A:29 X:02 Y:C3 P:65 SP:F9 CYC:327 SL:156 +FA8D 18 CLC A:29 X:02 Y:C3 P:25 SP:F9 CYC:333 SL:156 +FA8E A9 C3 LDA #$C3 A:29 X:02 Y:C3 P:24 SP:F9 CYC:339 SL:156 +FA90 60 RTS A:C3 X:02 Y:C3 P:A4 SP:F9 CYC: 4 SL:157 +EE77 07 47 *SLO $47 = 29 A:C3 X:02 Y:C3 P:A4 SP:FB CYC: 22 SL:157 +EE79 EA NOP A:D3 X:02 Y:C3 P:A4 SP:FB CYC: 37 SL:157 +EE7A EA NOP A:D3 X:02 Y:C3 P:A4 SP:FB CYC: 43 SL:157 +EE7B EA NOP A:D3 X:02 Y:C3 P:A4 SP:FB CYC: 49 SL:157 +EE7C EA NOP A:D3 X:02 Y:C3 P:A4 SP:FB CYC: 55 SL:157 +EE7D 20 91 FA JSR $FA91 A:D3 X:02 Y:C3 P:A4 SP:FB CYC: 61 SL:157 +FA91 70 53 BVS $FAE6 A:D3 X:02 Y:C3 P:A4 SP:F9 CYC: 79 SL:157 +FA93 F0 51 BEQ $FAE6 A:D3 X:02 Y:C3 P:A4 SP:F9 CYC: 85 SL:157 +FA95 10 4F BPL $FAE6 A:D3 X:02 Y:C3 P:A4 SP:F9 CYC: 91 SL:157 +FA97 B0 4D BCS $FAE6 A:D3 X:02 Y:C3 P:A4 SP:F9 CYC: 97 SL:157 +FA99 C9 D3 CMP #$D3 A:D3 X:02 Y:C3 P:A4 SP:F9 CYC:103 SL:157 +FA9B D0 49 BNE $FAE6 A:D3 X:02 Y:C3 P:27 SP:F9 CYC:109 SL:157 +FA9D 60 RTS A:D3 X:02 Y:C3 P:27 SP:F9 CYC:115 SL:157 +EE80 A5 47 LDA $47 = 52 A:D3 X:02 Y:C3 P:27 SP:FB CYC:133 SL:157 +EE82 C9 52 CMP #$52 A:52 X:02 Y:C3 P:25 SP:FB CYC:142 SL:157 +EE84 F0 02 BEQ $EE88 A:52 X:02 Y:C3 P:27 SP:FB CYC:148 SL:157 +EE88 C8 INY A:52 X:02 Y:C3 P:27 SP:FB CYC:157 SL:157 +EE89 A9 37 LDA #$37 A:52 X:02 Y:C4 P:A5 SP:FB CYC:163 SL:157 +EE8B 85 47 STA $47 = 52 A:37 X:02 Y:C4 P:25 SP:FB CYC:169 SL:157 +EE8D 20 9E FA JSR $FA9E A:37 X:02 Y:C4 P:25 SP:FB CYC:178 SL:157 +FA9E 24 01 BIT $01 = FF A:37 X:02 Y:C4 P:25 SP:F9 CYC:196 SL:157 +FAA0 38 SEC A:37 X:02 Y:C4 P:E5 SP:F9 CYC:205 SL:157 +FAA1 A9 10 LDA #$10 A:37 X:02 Y:C4 P:E5 SP:F9 CYC:211 SL:157 +FAA3 60 RTS A:10 X:02 Y:C4 P:65 SP:F9 CYC:217 SL:157 +EE90 07 47 *SLO $47 = 37 A:10 X:02 Y:C4 P:65 SP:FB CYC:235 SL:157 +EE92 EA NOP A:7E X:02 Y:C4 P:64 SP:FB CYC:250 SL:157 +EE93 EA NOP A:7E X:02 Y:C4 P:64 SP:FB CYC:256 SL:157 +EE94 EA NOP A:7E X:02 Y:C4 P:64 SP:FB CYC:262 SL:157 +EE95 EA NOP A:7E X:02 Y:C4 P:64 SP:FB CYC:268 SL:157 +EE96 20 A4 FA JSR $FAA4 A:7E X:02 Y:C4 P:64 SP:FB CYC:274 SL:157 +FAA4 50 40 BVC $FAE6 A:7E X:02 Y:C4 P:64 SP:F9 CYC:292 SL:157 +FAA6 F0 3E BEQ $FAE6 A:7E X:02 Y:C4 P:64 SP:F9 CYC:298 SL:157 +FAA8 30 3C BMI $FAE6 A:7E X:02 Y:C4 P:64 SP:F9 CYC:304 SL:157 +FAAA B0 3A BCS $FAE6 A:7E X:02 Y:C4 P:64 SP:F9 CYC:310 SL:157 +FAAC C9 7E CMP #$7E A:7E X:02 Y:C4 P:64 SP:F9 CYC:316 SL:157 +FAAE D0 36 BNE $FAE6 A:7E X:02 Y:C4 P:67 SP:F9 CYC:322 SL:157 +FAB0 60 RTS A:7E X:02 Y:C4 P:67 SP:F9 CYC:328 SL:157 +EE99 A5 47 LDA $47 = 6E A:7E X:02 Y:C4 P:67 SP:FB CYC: 5 SL:158 +EE9B C9 6E CMP #$6E A:6E X:02 Y:C4 P:65 SP:FB CYC: 14 SL:158 +EE9D F0 02 BEQ $EEA1 A:6E X:02 Y:C4 P:67 SP:FB CYC: 20 SL:158 +EEA1 C8 INY A:6E X:02 Y:C4 P:67 SP:FB CYC: 29 SL:158 +EEA2 A9 A5 LDA #$A5 A:6E X:02 Y:C5 P:E5 SP:FB CYC: 35 SL:158 +EEA4 8D 47 06 STA $0647 = 6E A:A5 X:02 Y:C5 P:E5 SP:FB CYC: 41 SL:158 +EEA7 20 7B FA JSR $FA7B A:A5 X:02 Y:C5 P:E5 SP:FB CYC: 53 SL:158 +FA7B 24 01 BIT $01 = FF A:A5 X:02 Y:C5 P:E5 SP:F9 CYC: 71 SL:158 +FA7D 18 CLC A:A5 X:02 Y:C5 P:E5 SP:F9 CYC: 80 SL:158 +FA7E A9 B3 LDA #$B3 A:A5 X:02 Y:C5 P:E4 SP:F9 CYC: 86 SL:158 +FA80 60 RTS A:B3 X:02 Y:C5 P:E4 SP:F9 CYC: 92 SL:158 +EEAA 0F 47 06 *SLO $0647 = A5 A:B3 X:02 Y:C5 P:E4 SP:FB CYC:110 SL:158 +EEAD EA NOP A:FB X:02 Y:C5 P:E5 SP:FB CYC:128 SL:158 +EEAE EA NOP A:FB X:02 Y:C5 P:E5 SP:FB CYC:134 SL:158 +EEAF EA NOP A:FB X:02 Y:C5 P:E5 SP:FB CYC:140 SL:158 +EEB0 EA NOP A:FB X:02 Y:C5 P:E5 SP:FB CYC:146 SL:158 +EEB1 20 81 FA JSR $FA81 A:FB X:02 Y:C5 P:E5 SP:FB CYC:152 SL:158 +FA81 50 63 BVC $FAE6 A:FB X:02 Y:C5 P:E5 SP:F9 CYC:170 SL:158 +FA83 90 61 BCC $FAE6 A:FB X:02 Y:C5 P:E5 SP:F9 CYC:176 SL:158 +FA85 10 5F BPL $FAE6 A:FB X:02 Y:C5 P:E5 SP:F9 CYC:182 SL:158 +FA87 C9 FB CMP #$FB A:FB X:02 Y:C5 P:E5 SP:F9 CYC:188 SL:158 +FA89 D0 5B BNE $FAE6 A:FB X:02 Y:C5 P:67 SP:F9 CYC:194 SL:158 +FA8B 60 RTS A:FB X:02 Y:C5 P:67 SP:F9 CYC:200 SL:158 +EEB4 AD 47 06 LDA $0647 = 4A A:FB X:02 Y:C5 P:67 SP:FB CYC:218 SL:158 +EEB7 C9 4A CMP #$4A A:4A X:02 Y:C5 P:65 SP:FB CYC:230 SL:158 +EEB9 F0 02 BEQ $EEBD A:4A X:02 Y:C5 P:67 SP:FB CYC:236 SL:158 +EEBD C8 INY A:4A X:02 Y:C5 P:67 SP:FB CYC:245 SL:158 +EEBE A9 29 LDA #$29 A:4A X:02 Y:C6 P:E5 SP:FB CYC:251 SL:158 +EEC0 8D 47 06 STA $0647 = 4A A:29 X:02 Y:C6 P:65 SP:FB CYC:257 SL:158 +EEC3 20 8C FA JSR $FA8C A:29 X:02 Y:C6 P:65 SP:FB CYC:269 SL:158 +FA8C B8 CLV A:29 X:02 Y:C6 P:65 SP:F9 CYC:287 SL:158 +FA8D 18 CLC A:29 X:02 Y:C6 P:25 SP:F9 CYC:293 SL:158 +FA8E A9 C3 LDA #$C3 A:29 X:02 Y:C6 P:24 SP:F9 CYC:299 SL:158 +FA90 60 RTS A:C3 X:02 Y:C6 P:A4 SP:F9 CYC:305 SL:158 +EEC6 0F 47 06 *SLO $0647 = 29 A:C3 X:02 Y:C6 P:A4 SP:FB CYC:323 SL:158 +EEC9 EA NOP A:D3 X:02 Y:C6 P:A4 SP:FB CYC: 0 SL:159 +EECA EA NOP A:D3 X:02 Y:C6 P:A4 SP:FB CYC: 6 SL:159 +EECB EA NOP A:D3 X:02 Y:C6 P:A4 SP:FB CYC: 12 SL:159 +EECC EA NOP A:D3 X:02 Y:C6 P:A4 SP:FB CYC: 18 SL:159 +EECD 20 91 FA JSR $FA91 A:D3 X:02 Y:C6 P:A4 SP:FB CYC: 24 SL:159 +FA91 70 53 BVS $FAE6 A:D3 X:02 Y:C6 P:A4 SP:F9 CYC: 42 SL:159 +FA93 F0 51 BEQ $FAE6 A:D3 X:02 Y:C6 P:A4 SP:F9 CYC: 48 SL:159 +FA95 10 4F BPL $FAE6 A:D3 X:02 Y:C6 P:A4 SP:F9 CYC: 54 SL:159 +FA97 B0 4D BCS $FAE6 A:D3 X:02 Y:C6 P:A4 SP:F9 CYC: 60 SL:159 +FA99 C9 D3 CMP #$D3 A:D3 X:02 Y:C6 P:A4 SP:F9 CYC: 66 SL:159 +FA9B D0 49 BNE $FAE6 A:D3 X:02 Y:C6 P:27 SP:F9 CYC: 72 SL:159 +FA9D 60 RTS A:D3 X:02 Y:C6 P:27 SP:F9 CYC: 78 SL:159 +EED0 AD 47 06 LDA $0647 = 52 A:D3 X:02 Y:C6 P:27 SP:FB CYC: 96 SL:159 +EED3 C9 52 CMP #$52 A:52 X:02 Y:C6 P:25 SP:FB CYC:108 SL:159 +EED5 F0 02 BEQ $EED9 A:52 X:02 Y:C6 P:27 SP:FB CYC:114 SL:159 +EED9 C8 INY A:52 X:02 Y:C6 P:27 SP:FB CYC:123 SL:159 +EEDA A9 37 LDA #$37 A:52 X:02 Y:C7 P:A5 SP:FB CYC:129 SL:159 +EEDC 8D 47 06 STA $0647 = 52 A:37 X:02 Y:C7 P:25 SP:FB CYC:135 SL:159 +EEDF 20 9E FA JSR $FA9E A:37 X:02 Y:C7 P:25 SP:FB CYC:147 SL:159 +FA9E 24 01 BIT $01 = FF A:37 X:02 Y:C7 P:25 SP:F9 CYC:165 SL:159 +FAA0 38 SEC A:37 X:02 Y:C7 P:E5 SP:F9 CYC:174 SL:159 +FAA1 A9 10 LDA #$10 A:37 X:02 Y:C7 P:E5 SP:F9 CYC:180 SL:159 +FAA3 60 RTS A:10 X:02 Y:C7 P:65 SP:F9 CYC:186 SL:159 +EEE2 0F 47 06 *SLO $0647 = 37 A:10 X:02 Y:C7 P:65 SP:FB CYC:204 SL:159 +EEE5 EA NOP A:7E X:02 Y:C7 P:64 SP:FB CYC:222 SL:159 +EEE6 EA NOP A:7E X:02 Y:C7 P:64 SP:FB CYC:228 SL:159 +EEE7 EA NOP A:7E X:02 Y:C7 P:64 SP:FB CYC:234 SL:159 +EEE8 EA NOP A:7E X:02 Y:C7 P:64 SP:FB CYC:240 SL:159 +EEE9 20 A4 FA JSR $FAA4 A:7E X:02 Y:C7 P:64 SP:FB CYC:246 SL:159 +FAA4 50 40 BVC $FAE6 A:7E X:02 Y:C7 P:64 SP:F9 CYC:264 SL:159 +FAA6 F0 3E BEQ $FAE6 A:7E X:02 Y:C7 P:64 SP:F9 CYC:270 SL:159 +FAA8 30 3C BMI $FAE6 A:7E X:02 Y:C7 P:64 SP:F9 CYC:276 SL:159 +FAAA B0 3A BCS $FAE6 A:7E X:02 Y:C7 P:64 SP:F9 CYC:282 SL:159 +FAAC C9 7E CMP #$7E A:7E X:02 Y:C7 P:64 SP:F9 CYC:288 SL:159 +FAAE D0 36 BNE $FAE6 A:7E X:02 Y:C7 P:67 SP:F9 CYC:294 SL:159 +FAB0 60 RTS A:7E X:02 Y:C7 P:67 SP:F9 CYC:300 SL:159 +EEEC AD 47 06 LDA $0647 = 6E A:7E X:02 Y:C7 P:67 SP:FB CYC:318 SL:159 +EEEF C9 6E CMP #$6E A:6E X:02 Y:C7 P:65 SP:FB CYC:330 SL:159 +EEF1 F0 02 BEQ $EEF5 A:6E X:02 Y:C7 P:67 SP:FB CYC:336 SL:159 +EEF5 A9 A5 LDA #$A5 A:6E X:02 Y:C7 P:67 SP:FB CYC: 4 SL:160 +EEF7 8D 47 06 STA $0647 = 6E A:A5 X:02 Y:C7 P:E5 SP:FB CYC: 10 SL:160 +EEFA A9 48 LDA #$48 A:A5 X:02 Y:C7 P:E5 SP:FB CYC: 22 SL:160 +EEFC 85 45 STA $45 = 48 A:48 X:02 Y:C7 P:65 SP:FB CYC: 28 SL:160 +EEFE A9 05 LDA #$05 A:48 X:02 Y:C7 P:65 SP:FB CYC: 37 SL:160 +EF00 85 46 STA $46 = 05 A:05 X:02 Y:C7 P:65 SP:FB CYC: 43 SL:160 +EF02 A0 FF LDY #$FF A:05 X:02 Y:C7 P:65 SP:FB CYC: 52 SL:160 +EF04 20 7B FA JSR $FA7B A:05 X:02 Y:FF P:E5 SP:FB CYC: 58 SL:160 +FA7B 24 01 BIT $01 = FF A:05 X:02 Y:FF P:E5 SP:F9 CYC: 76 SL:160 +FA7D 18 CLC A:05 X:02 Y:FF P:E5 SP:F9 CYC: 85 SL:160 +FA7E A9 B3 LDA #$B3 A:05 X:02 Y:FF P:E4 SP:F9 CYC: 91 SL:160 +FA80 60 RTS A:B3 X:02 Y:FF P:E4 SP:F9 CYC: 97 SL:160 +EF07 13 45 *SLO ($45),Y = 0548 @ 0647 = A5 A:B3 X:02 Y:FF P:E4 SP:FB CYC:115 SL:160 +EF09 EA NOP A:FB X:02 Y:FF P:E5 SP:FB CYC:139 SL:160 +EF0A EA NOP A:FB X:02 Y:FF P:E5 SP:FB CYC:145 SL:160 +EF0B 08 PHP A:FB X:02 Y:FF P:E5 SP:FB CYC:151 SL:160 +EF0C 48 PHA A:FB X:02 Y:FF P:E5 SP:FA CYC:160 SL:160 +EF0D A0 C8 LDY #$C8 A:FB X:02 Y:FF P:E5 SP:F9 CYC:169 SL:160 +EF0F 68 PLA A:FB X:02 Y:C8 P:E5 SP:F9 CYC:175 SL:160 +EF10 28 PLP A:FB X:02 Y:C8 P:E5 SP:FA CYC:187 SL:160 +EF11 20 81 FA JSR $FA81 A:FB X:02 Y:C8 P:E5 SP:FB CYC:199 SL:160 +FA81 50 63 BVC $FAE6 A:FB X:02 Y:C8 P:E5 SP:F9 CYC:217 SL:160 +FA83 90 61 BCC $FAE6 A:FB X:02 Y:C8 P:E5 SP:F9 CYC:223 SL:160 +FA85 10 5F BPL $FAE6 A:FB X:02 Y:C8 P:E5 SP:F9 CYC:229 SL:160 +FA87 C9 FB CMP #$FB A:FB X:02 Y:C8 P:E5 SP:F9 CYC:235 SL:160 +FA89 D0 5B BNE $FAE6 A:FB X:02 Y:C8 P:67 SP:F9 CYC:241 SL:160 +FA8B 60 RTS A:FB X:02 Y:C8 P:67 SP:F9 CYC:247 SL:160 +EF14 AD 47 06 LDA $0647 = 4A A:FB X:02 Y:C8 P:67 SP:FB CYC:265 SL:160 +EF17 C9 4A CMP #$4A A:4A X:02 Y:C8 P:65 SP:FB CYC:277 SL:160 +EF19 F0 02 BEQ $EF1D A:4A X:02 Y:C8 P:67 SP:FB CYC:283 SL:160 +EF1D A0 FF LDY #$FF A:4A X:02 Y:C8 P:67 SP:FB CYC:292 SL:160 +EF1F A9 29 LDA #$29 A:4A X:02 Y:FF P:E5 SP:FB CYC:298 SL:160 +EF21 8D 47 06 STA $0647 = 4A A:29 X:02 Y:FF P:65 SP:FB CYC:304 SL:160 +EF24 20 8C FA JSR $FA8C A:29 X:02 Y:FF P:65 SP:FB CYC:316 SL:160 +FA8C B8 CLV A:29 X:02 Y:FF P:65 SP:F9 CYC:334 SL:160 +FA8D 18 CLC A:29 X:02 Y:FF P:25 SP:F9 CYC:340 SL:160 +FA8E A9 C3 LDA #$C3 A:29 X:02 Y:FF P:24 SP:F9 CYC: 5 SL:161 +FA90 60 RTS A:C3 X:02 Y:FF P:A4 SP:F9 CYC: 11 SL:161 +EF27 13 45 *SLO ($45),Y = 0548 @ 0647 = 29 A:C3 X:02 Y:FF P:A4 SP:FB CYC: 29 SL:161 +EF29 EA NOP A:D3 X:02 Y:FF P:A4 SP:FB CYC: 53 SL:161 +EF2A EA NOP A:D3 X:02 Y:FF P:A4 SP:FB CYC: 59 SL:161 +EF2B 08 PHP A:D3 X:02 Y:FF P:A4 SP:FB CYC: 65 SL:161 +EF2C 48 PHA A:D3 X:02 Y:FF P:A4 SP:FA CYC: 74 SL:161 +EF2D A0 C9 LDY #$C9 A:D3 X:02 Y:FF P:A4 SP:F9 CYC: 83 SL:161 +EF2F 68 PLA A:D3 X:02 Y:C9 P:A4 SP:F9 CYC: 89 SL:161 +EF30 28 PLP A:D3 X:02 Y:C9 P:A4 SP:FA CYC:101 SL:161 +EF31 20 91 FA JSR $FA91 A:D3 X:02 Y:C9 P:A4 SP:FB CYC:113 SL:161 +FA91 70 53 BVS $FAE6 A:D3 X:02 Y:C9 P:A4 SP:F9 CYC:131 SL:161 +FA93 F0 51 BEQ $FAE6 A:D3 X:02 Y:C9 P:A4 SP:F9 CYC:137 SL:161 +FA95 10 4F BPL $FAE6 A:D3 X:02 Y:C9 P:A4 SP:F9 CYC:143 SL:161 +FA97 B0 4D BCS $FAE6 A:D3 X:02 Y:C9 P:A4 SP:F9 CYC:149 SL:161 +FA99 C9 D3 CMP #$D3 A:D3 X:02 Y:C9 P:A4 SP:F9 CYC:155 SL:161 +FA9B D0 49 BNE $FAE6 A:D3 X:02 Y:C9 P:27 SP:F9 CYC:161 SL:161 +FA9D 60 RTS A:D3 X:02 Y:C9 P:27 SP:F9 CYC:167 SL:161 +EF34 AD 47 06 LDA $0647 = 52 A:D3 X:02 Y:C9 P:27 SP:FB CYC:185 SL:161 +EF37 C9 52 CMP #$52 A:52 X:02 Y:C9 P:25 SP:FB CYC:197 SL:161 +EF39 F0 02 BEQ $EF3D A:52 X:02 Y:C9 P:27 SP:FB CYC:203 SL:161 +EF3D A0 FF LDY #$FF A:52 X:02 Y:C9 P:27 SP:FB CYC:212 SL:161 +EF3F A9 37 LDA #$37 A:52 X:02 Y:FF P:A5 SP:FB CYC:218 SL:161 +EF41 8D 47 06 STA $0647 = 52 A:37 X:02 Y:FF P:25 SP:FB CYC:224 SL:161 +EF44 20 9E FA JSR $FA9E A:37 X:02 Y:FF P:25 SP:FB CYC:236 SL:161 +FA9E 24 01 BIT $01 = FF A:37 X:02 Y:FF P:25 SP:F9 CYC:254 SL:161 +FAA0 38 SEC A:37 X:02 Y:FF P:E5 SP:F9 CYC:263 SL:161 +FAA1 A9 10 LDA #$10 A:37 X:02 Y:FF P:E5 SP:F9 CYC:269 SL:161 +FAA3 60 RTS A:10 X:02 Y:FF P:65 SP:F9 CYC:275 SL:161 +EF47 13 45 *SLO ($45),Y = 0548 @ 0647 = 37 A:10 X:02 Y:FF P:65 SP:FB CYC:293 SL:161 +EF49 EA NOP A:7E X:02 Y:FF P:64 SP:FB CYC:317 SL:161 +EF4A EA NOP A:7E X:02 Y:FF P:64 SP:FB CYC:323 SL:161 +EF4B 08 PHP A:7E X:02 Y:FF P:64 SP:FB CYC:329 SL:161 +EF4C 48 PHA A:7E X:02 Y:FF P:64 SP:FA CYC:338 SL:161 +EF4D A0 CA LDY #$CA A:7E X:02 Y:FF P:64 SP:F9 CYC: 6 SL:162 +EF4F 68 PLA A:7E X:02 Y:CA P:E4 SP:F9 CYC: 12 SL:162 +EF50 28 PLP A:7E X:02 Y:CA P:64 SP:FA CYC: 24 SL:162 +EF51 20 A4 FA JSR $FAA4 A:7E X:02 Y:CA P:64 SP:FB CYC: 36 SL:162 +FAA4 50 40 BVC $FAE6 A:7E X:02 Y:CA P:64 SP:F9 CYC: 54 SL:162 +FAA6 F0 3E BEQ $FAE6 A:7E X:02 Y:CA P:64 SP:F9 CYC: 60 SL:162 +FAA8 30 3C BMI $FAE6 A:7E X:02 Y:CA P:64 SP:F9 CYC: 66 SL:162 +FAAA B0 3A BCS $FAE6 A:7E X:02 Y:CA P:64 SP:F9 CYC: 72 SL:162 +FAAC C9 7E CMP #$7E A:7E X:02 Y:CA P:64 SP:F9 CYC: 78 SL:162 +FAAE D0 36 BNE $FAE6 A:7E X:02 Y:CA P:67 SP:F9 CYC: 84 SL:162 +FAB0 60 RTS A:7E X:02 Y:CA P:67 SP:F9 CYC: 90 SL:162 +EF54 AD 47 06 LDA $0647 = 6E A:7E X:02 Y:CA P:67 SP:FB CYC:108 SL:162 +EF57 C9 6E CMP #$6E A:6E X:02 Y:CA P:65 SP:FB CYC:120 SL:162 +EF59 F0 02 BEQ $EF5D A:6E X:02 Y:CA P:67 SP:FB CYC:126 SL:162 +EF5D A0 CB LDY #$CB A:6E X:02 Y:CA P:67 SP:FB CYC:135 SL:162 +EF5F A2 FF LDX #$FF A:6E X:02 Y:CB P:E5 SP:FB CYC:141 SL:162 +EF61 A9 A5 LDA #$A5 A:6E X:FF Y:CB P:E5 SP:FB CYC:147 SL:162 +EF63 85 47 STA $47 = 6E A:A5 X:FF Y:CB P:E5 SP:FB CYC:153 SL:162 +EF65 20 7B FA JSR $FA7B A:A5 X:FF Y:CB P:E5 SP:FB CYC:162 SL:162 +FA7B 24 01 BIT $01 = FF A:A5 X:FF Y:CB P:E5 SP:F9 CYC:180 SL:162 +FA7D 18 CLC A:A5 X:FF Y:CB P:E5 SP:F9 CYC:189 SL:162 +FA7E A9 B3 LDA #$B3 A:A5 X:FF Y:CB P:E4 SP:F9 CYC:195 SL:162 +FA80 60 RTS A:B3 X:FF Y:CB P:E4 SP:F9 CYC:201 SL:162 +EF68 17 48 *SLO $48,X @ 47 = A5 A:B3 X:FF Y:CB P:E4 SP:FB CYC:219 SL:162 +EF6A EA NOP A:FB X:FF Y:CB P:E5 SP:FB CYC:237 SL:162 +EF6B EA NOP A:FB X:FF Y:CB P:E5 SP:FB CYC:243 SL:162 +EF6C EA NOP A:FB X:FF Y:CB P:E5 SP:FB CYC:249 SL:162 +EF6D EA NOP A:FB X:FF Y:CB P:E5 SP:FB CYC:255 SL:162 +EF6E 20 81 FA JSR $FA81 A:FB X:FF Y:CB P:E5 SP:FB CYC:261 SL:162 +FA81 50 63 BVC $FAE6 A:FB X:FF Y:CB P:E5 SP:F9 CYC:279 SL:162 +FA83 90 61 BCC $FAE6 A:FB X:FF Y:CB P:E5 SP:F9 CYC:285 SL:162 +FA85 10 5F BPL $FAE6 A:FB X:FF Y:CB P:E5 SP:F9 CYC:291 SL:162 +FA87 C9 FB CMP #$FB A:FB X:FF Y:CB P:E5 SP:F9 CYC:297 SL:162 +FA89 D0 5B BNE $FAE6 A:FB X:FF Y:CB P:67 SP:F9 CYC:303 SL:162 +FA8B 60 RTS A:FB X:FF Y:CB P:67 SP:F9 CYC:309 SL:162 +EF71 A5 47 LDA $47 = 4A A:FB X:FF Y:CB P:67 SP:FB CYC:327 SL:162 +EF73 C9 4A CMP #$4A A:4A X:FF Y:CB P:65 SP:FB CYC:336 SL:162 +EF75 F0 02 BEQ $EF79 A:4A X:FF Y:CB P:67 SP:FB CYC: 1 SL:163 +EF79 C8 INY A:4A X:FF Y:CB P:67 SP:FB CYC: 10 SL:163 +EF7A A9 29 LDA #$29 A:4A X:FF Y:CC P:E5 SP:FB CYC: 16 SL:163 +EF7C 85 47 STA $47 = 4A A:29 X:FF Y:CC P:65 SP:FB CYC: 22 SL:163 +EF7E 20 8C FA JSR $FA8C A:29 X:FF Y:CC P:65 SP:FB CYC: 31 SL:163 +FA8C B8 CLV A:29 X:FF Y:CC P:65 SP:F9 CYC: 49 SL:163 +FA8D 18 CLC A:29 X:FF Y:CC P:25 SP:F9 CYC: 55 SL:163 +FA8E A9 C3 LDA #$C3 A:29 X:FF Y:CC P:24 SP:F9 CYC: 61 SL:163 +FA90 60 RTS A:C3 X:FF Y:CC P:A4 SP:F9 CYC: 67 SL:163 +EF81 17 48 *SLO $48,X @ 47 = 29 A:C3 X:FF Y:CC P:A4 SP:FB CYC: 85 SL:163 +EF83 EA NOP A:D3 X:FF Y:CC P:A4 SP:FB CYC:103 SL:163 +EF84 EA NOP A:D3 X:FF Y:CC P:A4 SP:FB CYC:109 SL:163 +EF85 EA NOP A:D3 X:FF Y:CC P:A4 SP:FB CYC:115 SL:163 +EF86 EA NOP A:D3 X:FF Y:CC P:A4 SP:FB CYC:121 SL:163 +EF87 20 91 FA JSR $FA91 A:D3 X:FF Y:CC P:A4 SP:FB CYC:127 SL:163 +FA91 70 53 BVS $FAE6 A:D3 X:FF Y:CC P:A4 SP:F9 CYC:145 SL:163 +FA93 F0 51 BEQ $FAE6 A:D3 X:FF Y:CC P:A4 SP:F9 CYC:151 SL:163 +FA95 10 4F BPL $FAE6 A:D3 X:FF Y:CC P:A4 SP:F9 CYC:157 SL:163 +FA97 B0 4D BCS $FAE6 A:D3 X:FF Y:CC P:A4 SP:F9 CYC:163 SL:163 +FA99 C9 D3 CMP #$D3 A:D3 X:FF Y:CC P:A4 SP:F9 CYC:169 SL:163 +FA9B D0 49 BNE $FAE6 A:D3 X:FF Y:CC P:27 SP:F9 CYC:175 SL:163 +FA9D 60 RTS A:D3 X:FF Y:CC P:27 SP:F9 CYC:181 SL:163 +EF8A A5 47 LDA $47 = 52 A:D3 X:FF Y:CC P:27 SP:FB CYC:199 SL:163 +EF8C C9 52 CMP #$52 A:52 X:FF Y:CC P:25 SP:FB CYC:208 SL:163 +EF8E F0 02 BEQ $EF92 A:52 X:FF Y:CC P:27 SP:FB CYC:214 SL:163 +EF92 C8 INY A:52 X:FF Y:CC P:27 SP:FB CYC:223 SL:163 +EF93 A9 37 LDA #$37 A:52 X:FF Y:CD P:A5 SP:FB CYC:229 SL:163 +EF95 85 47 STA $47 = 52 A:37 X:FF Y:CD P:25 SP:FB CYC:235 SL:163 +EF97 20 9E FA JSR $FA9E A:37 X:FF Y:CD P:25 SP:FB CYC:244 SL:163 +FA9E 24 01 BIT $01 = FF A:37 X:FF Y:CD P:25 SP:F9 CYC:262 SL:163 +FAA0 38 SEC A:37 X:FF Y:CD P:E5 SP:F9 CYC:271 SL:163 +FAA1 A9 10 LDA #$10 A:37 X:FF Y:CD P:E5 SP:F9 CYC:277 SL:163 +FAA3 60 RTS A:10 X:FF Y:CD P:65 SP:F9 CYC:283 SL:163 +EF9A 17 48 *SLO $48,X @ 47 = 37 A:10 X:FF Y:CD P:65 SP:FB CYC:301 SL:163 +EF9C EA NOP A:7E X:FF Y:CD P:64 SP:FB CYC:319 SL:163 +EF9D EA NOP A:7E X:FF Y:CD P:64 SP:FB CYC:325 SL:163 +EF9E EA NOP A:7E X:FF Y:CD P:64 SP:FB CYC:331 SL:163 +EF9F EA NOP A:7E X:FF Y:CD P:64 SP:FB CYC:337 SL:163 +EFA0 20 A4 FA JSR $FAA4 A:7E X:FF Y:CD P:64 SP:FB CYC: 2 SL:164 +FAA4 50 40 BVC $FAE6 A:7E X:FF Y:CD P:64 SP:F9 CYC: 20 SL:164 +FAA6 F0 3E BEQ $FAE6 A:7E X:FF Y:CD P:64 SP:F9 CYC: 26 SL:164 +FAA8 30 3C BMI $FAE6 A:7E X:FF Y:CD P:64 SP:F9 CYC: 32 SL:164 +FAAA B0 3A BCS $FAE6 A:7E X:FF Y:CD P:64 SP:F9 CYC: 38 SL:164 +FAAC C9 7E CMP #$7E A:7E X:FF Y:CD P:64 SP:F9 CYC: 44 SL:164 +FAAE D0 36 BNE $FAE6 A:7E X:FF Y:CD P:67 SP:F9 CYC: 50 SL:164 +FAB0 60 RTS A:7E X:FF Y:CD P:67 SP:F9 CYC: 56 SL:164 +EFA3 A5 47 LDA $47 = 6E A:7E X:FF Y:CD P:67 SP:FB CYC: 74 SL:164 +EFA5 C9 6E CMP #$6E A:6E X:FF Y:CD P:65 SP:FB CYC: 83 SL:164 +EFA7 F0 02 BEQ $EFAB A:6E X:FF Y:CD P:67 SP:FB CYC: 89 SL:164 +EFAB A9 A5 LDA #$A5 A:6E X:FF Y:CD P:67 SP:FB CYC: 98 SL:164 +EFAD 8D 47 06 STA $0647 = 6E A:A5 X:FF Y:CD P:E5 SP:FB CYC:104 SL:164 +EFB0 A0 FF LDY #$FF A:A5 X:FF Y:CD P:E5 SP:FB CYC:116 SL:164 +EFB2 20 7B FA JSR $FA7B A:A5 X:FF Y:FF P:E5 SP:FB CYC:122 SL:164 +FA7B 24 01 BIT $01 = FF A:A5 X:FF Y:FF P:E5 SP:F9 CYC:140 SL:164 +FA7D 18 CLC A:A5 X:FF Y:FF P:E5 SP:F9 CYC:149 SL:164 +FA7E A9 B3 LDA #$B3 A:A5 X:FF Y:FF P:E4 SP:F9 CYC:155 SL:164 +FA80 60 RTS A:B3 X:FF Y:FF P:E4 SP:F9 CYC:161 SL:164 +EFB5 1B 48 05 *SLO $0548,Y @ 0647 = A5 A:B3 X:FF Y:FF P:E4 SP:FB CYC:179 SL:164 +EFB8 EA NOP A:FB X:FF Y:FF P:E5 SP:FB CYC:200 SL:164 +EFB9 EA NOP A:FB X:FF Y:FF P:E5 SP:FB CYC:206 SL:164 +EFBA 08 PHP A:FB X:FF Y:FF P:E5 SP:FB CYC:212 SL:164 +EFBB 48 PHA A:FB X:FF Y:FF P:E5 SP:FA CYC:221 SL:164 +EFBC A0 CE LDY #$CE A:FB X:FF Y:FF P:E5 SP:F9 CYC:230 SL:164 +EFBE 68 PLA A:FB X:FF Y:CE P:E5 SP:F9 CYC:236 SL:164 +EFBF 28 PLP A:FB X:FF Y:CE P:E5 SP:FA CYC:248 SL:164 +EFC0 20 81 FA JSR $FA81 A:FB X:FF Y:CE P:E5 SP:FB CYC:260 SL:164 +FA81 50 63 BVC $FAE6 A:FB X:FF Y:CE P:E5 SP:F9 CYC:278 SL:164 +FA83 90 61 BCC $FAE6 A:FB X:FF Y:CE P:E5 SP:F9 CYC:284 SL:164 +FA85 10 5F BPL $FAE6 A:FB X:FF Y:CE P:E5 SP:F9 CYC:290 SL:164 +FA87 C9 FB CMP #$FB A:FB X:FF Y:CE P:E5 SP:F9 CYC:296 SL:164 +FA89 D0 5B BNE $FAE6 A:FB X:FF Y:CE P:67 SP:F9 CYC:302 SL:164 +FA8B 60 RTS A:FB X:FF Y:CE P:67 SP:F9 CYC:308 SL:164 +EFC3 AD 47 06 LDA $0647 = 4A A:FB X:FF Y:CE P:67 SP:FB CYC:326 SL:164 +EFC6 C9 4A CMP #$4A A:4A X:FF Y:CE P:65 SP:FB CYC:338 SL:164 +EFC8 F0 02 BEQ $EFCC A:4A X:FF Y:CE P:67 SP:FB CYC: 3 SL:165 +EFCC A0 FF LDY #$FF A:4A X:FF Y:CE P:67 SP:FB CYC: 12 SL:165 +EFCE A9 29 LDA #$29 A:4A X:FF Y:FF P:E5 SP:FB CYC: 18 SL:165 +EFD0 8D 47 06 STA $0647 = 4A A:29 X:FF Y:FF P:65 SP:FB CYC: 24 SL:165 +EFD3 20 8C FA JSR $FA8C A:29 X:FF Y:FF P:65 SP:FB CYC: 36 SL:165 +FA8C B8 CLV A:29 X:FF Y:FF P:65 SP:F9 CYC: 54 SL:165 +FA8D 18 CLC A:29 X:FF Y:FF P:25 SP:F9 CYC: 60 SL:165 +FA8E A9 C3 LDA #$C3 A:29 X:FF Y:FF P:24 SP:F9 CYC: 66 SL:165 +FA90 60 RTS A:C3 X:FF Y:FF P:A4 SP:F9 CYC: 72 SL:165 +EFD6 1B 48 05 *SLO $0548,Y @ 0647 = 29 A:C3 X:FF Y:FF P:A4 SP:FB CYC: 90 SL:165 +EFD9 EA NOP A:D3 X:FF Y:FF P:A4 SP:FB CYC:111 SL:165 +EFDA EA NOP A:D3 X:FF Y:FF P:A4 SP:FB CYC:117 SL:165 +EFDB 08 PHP A:D3 X:FF Y:FF P:A4 SP:FB CYC:123 SL:165 +EFDC 48 PHA A:D3 X:FF Y:FF P:A4 SP:FA CYC:132 SL:165 +EFDD A0 CF LDY #$CF A:D3 X:FF Y:FF P:A4 SP:F9 CYC:141 SL:165 +EFDF 68 PLA A:D3 X:FF Y:CF P:A4 SP:F9 CYC:147 SL:165 +EFE0 28 PLP A:D3 X:FF Y:CF P:A4 SP:FA CYC:159 SL:165 +EFE1 20 91 FA JSR $FA91 A:D3 X:FF Y:CF P:A4 SP:FB CYC:171 SL:165 +FA91 70 53 BVS $FAE6 A:D3 X:FF Y:CF P:A4 SP:F9 CYC:189 SL:165 +FA93 F0 51 BEQ $FAE6 A:D3 X:FF Y:CF P:A4 SP:F9 CYC:195 SL:165 +FA95 10 4F BPL $FAE6 A:D3 X:FF Y:CF P:A4 SP:F9 CYC:201 SL:165 +FA97 B0 4D BCS $FAE6 A:D3 X:FF Y:CF P:A4 SP:F9 CYC:207 SL:165 +FA99 C9 D3 CMP #$D3 A:D3 X:FF Y:CF P:A4 SP:F9 CYC:213 SL:165 +FA9B D0 49 BNE $FAE6 A:D3 X:FF Y:CF P:27 SP:F9 CYC:219 SL:165 +FA9D 60 RTS A:D3 X:FF Y:CF P:27 SP:F9 CYC:225 SL:165 +EFE4 AD 47 06 LDA $0647 = 52 A:D3 X:FF Y:CF P:27 SP:FB CYC:243 SL:165 +EFE7 C9 52 CMP #$52 A:52 X:FF Y:CF P:25 SP:FB CYC:255 SL:165 +EFE9 F0 02 BEQ $EFED A:52 X:FF Y:CF P:27 SP:FB CYC:261 SL:165 +EFED A0 FF LDY #$FF A:52 X:FF Y:CF P:27 SP:FB CYC:270 SL:165 +EFEF A9 37 LDA #$37 A:52 X:FF Y:FF P:A5 SP:FB CYC:276 SL:165 +EFF1 8D 47 06 STA $0647 = 52 A:37 X:FF Y:FF P:25 SP:FB CYC:282 SL:165 +EFF4 20 9E FA JSR $FA9E A:37 X:FF Y:FF P:25 SP:FB CYC:294 SL:165 +FA9E 24 01 BIT $01 = FF A:37 X:FF Y:FF P:25 SP:F9 CYC:312 SL:165 +FAA0 38 SEC A:37 X:FF Y:FF P:E5 SP:F9 CYC:321 SL:165 +FAA1 A9 10 LDA #$10 A:37 X:FF Y:FF P:E5 SP:F9 CYC:327 SL:165 +FAA3 60 RTS A:10 X:FF Y:FF P:65 SP:F9 CYC:333 SL:165 +EFF7 1B 48 05 *SLO $0548,Y @ 0647 = 37 A:10 X:FF Y:FF P:65 SP:FB CYC: 10 SL:166 +EFFA EA NOP A:7E X:FF Y:FF P:64 SP:FB CYC: 31 SL:166 +EFFB EA NOP A:7E X:FF Y:FF P:64 SP:FB CYC: 37 SL:166 +EFFC 08 PHP A:7E X:FF Y:FF P:64 SP:FB CYC: 43 SL:166 +EFFD 48 PHA A:7E X:FF Y:FF P:64 SP:FA CYC: 52 SL:166 +EFFE A0 D0 LDY #$D0 A:7E X:FF Y:FF P:64 SP:F9 CYC: 61 SL:166 +F000 68 PLA A:7E X:FF Y:D0 P:E4 SP:F9 CYC: 67 SL:166 +F001 28 PLP A:7E X:FF Y:D0 P:64 SP:FA CYC: 79 SL:166 +F002 20 A4 FA JSR $FAA4 A:7E X:FF Y:D0 P:64 SP:FB CYC: 91 SL:166 +FAA4 50 40 BVC $FAE6 A:7E X:FF Y:D0 P:64 SP:F9 CYC:109 SL:166 +FAA6 F0 3E BEQ $FAE6 A:7E X:FF Y:D0 P:64 SP:F9 CYC:115 SL:166 +FAA8 30 3C BMI $FAE6 A:7E X:FF Y:D0 P:64 SP:F9 CYC:121 SL:166 +FAAA B0 3A BCS $FAE6 A:7E X:FF Y:D0 P:64 SP:F9 CYC:127 SL:166 +FAAC C9 7E CMP #$7E A:7E X:FF Y:D0 P:64 SP:F9 CYC:133 SL:166 +FAAE D0 36 BNE $FAE6 A:7E X:FF Y:D0 P:67 SP:F9 CYC:139 SL:166 +FAB0 60 RTS A:7E X:FF Y:D0 P:67 SP:F9 CYC:145 SL:166 +F005 AD 47 06 LDA $0647 = 6E A:7E X:FF Y:D0 P:67 SP:FB CYC:163 SL:166 +F008 C9 6E CMP #$6E A:6E X:FF Y:D0 P:65 SP:FB CYC:175 SL:166 +F00A F0 02 BEQ $F00E A:6E X:FF Y:D0 P:67 SP:FB CYC:181 SL:166 +F00E A0 D1 LDY #$D1 A:6E X:FF Y:D0 P:67 SP:FB CYC:190 SL:166 +F010 A2 FF LDX #$FF A:6E X:FF Y:D1 P:E5 SP:FB CYC:196 SL:166 +F012 A9 A5 LDA #$A5 A:6E X:FF Y:D1 P:E5 SP:FB CYC:202 SL:166 +F014 8D 47 06 STA $0647 = 6E A:A5 X:FF Y:D1 P:E5 SP:FB CYC:208 SL:166 +F017 20 7B FA JSR $FA7B A:A5 X:FF Y:D1 P:E5 SP:FB CYC:220 SL:166 +FA7B 24 01 BIT $01 = FF A:A5 X:FF Y:D1 P:E5 SP:F9 CYC:238 SL:166 +FA7D 18 CLC A:A5 X:FF Y:D1 P:E5 SP:F9 CYC:247 SL:166 +FA7E A9 B3 LDA #$B3 A:A5 X:FF Y:D1 P:E4 SP:F9 CYC:253 SL:166 +FA80 60 RTS A:B3 X:FF Y:D1 P:E4 SP:F9 CYC:259 SL:166 +F01A 1F 48 05 *SLO $0548,X @ 0647 = A5 A:B3 X:FF Y:D1 P:E4 SP:FB CYC:277 SL:166 +F01D EA NOP A:FB X:FF Y:D1 P:E5 SP:FB CYC:298 SL:166 +F01E EA NOP A:FB X:FF Y:D1 P:E5 SP:FB CYC:304 SL:166 +F01F EA NOP A:FB X:FF Y:D1 P:E5 SP:FB CYC:310 SL:166 +F020 EA NOP A:FB X:FF Y:D1 P:E5 SP:FB CYC:316 SL:166 +F021 20 81 FA JSR $FA81 A:FB X:FF Y:D1 P:E5 SP:FB CYC:322 SL:166 +FA81 50 63 BVC $FAE6 A:FB X:FF Y:D1 P:E5 SP:F9 CYC:340 SL:166 +FA83 90 61 BCC $FAE6 A:FB X:FF Y:D1 P:E5 SP:F9 CYC: 5 SL:167 +FA85 10 5F BPL $FAE6 A:FB X:FF Y:D1 P:E5 SP:F9 CYC: 11 SL:167 +FA87 C9 FB CMP #$FB A:FB X:FF Y:D1 P:E5 SP:F9 CYC: 17 SL:167 +FA89 D0 5B BNE $FAE6 A:FB X:FF Y:D1 P:67 SP:F9 CYC: 23 SL:167 +FA8B 60 RTS A:FB X:FF Y:D1 P:67 SP:F9 CYC: 29 SL:167 +F024 AD 47 06 LDA $0647 = 4A A:FB X:FF Y:D1 P:67 SP:FB CYC: 47 SL:167 +F027 C9 4A CMP #$4A A:4A X:FF Y:D1 P:65 SP:FB CYC: 59 SL:167 +F029 F0 02 BEQ $F02D A:4A X:FF Y:D1 P:67 SP:FB CYC: 65 SL:167 +F02D C8 INY A:4A X:FF Y:D1 P:67 SP:FB CYC: 74 SL:167 +F02E A9 29 LDA #$29 A:4A X:FF Y:D2 P:E5 SP:FB CYC: 80 SL:167 +F030 8D 47 06 STA $0647 = 4A A:29 X:FF Y:D2 P:65 SP:FB CYC: 86 SL:167 +F033 20 8C FA JSR $FA8C A:29 X:FF Y:D2 P:65 SP:FB CYC: 98 SL:167 +FA8C B8 CLV A:29 X:FF Y:D2 P:65 SP:F9 CYC:116 SL:167 +FA8D 18 CLC A:29 X:FF Y:D2 P:25 SP:F9 CYC:122 SL:167 +FA8E A9 C3 LDA #$C3 A:29 X:FF Y:D2 P:24 SP:F9 CYC:128 SL:167 +FA90 60 RTS A:C3 X:FF Y:D2 P:A4 SP:F9 CYC:134 SL:167 +F036 1F 48 05 *SLO $0548,X @ 0647 = 29 A:C3 X:FF Y:D2 P:A4 SP:FB CYC:152 SL:167 +F039 EA NOP A:D3 X:FF Y:D2 P:A4 SP:FB CYC:173 SL:167 +F03A EA NOP A:D3 X:FF Y:D2 P:A4 SP:FB CYC:179 SL:167 +F03B EA NOP A:D3 X:FF Y:D2 P:A4 SP:FB CYC:185 SL:167 +F03C EA NOP A:D3 X:FF Y:D2 P:A4 SP:FB CYC:191 SL:167 +F03D 20 91 FA JSR $FA91 A:D3 X:FF Y:D2 P:A4 SP:FB CYC:197 SL:167 +FA91 70 53 BVS $FAE6 A:D3 X:FF Y:D2 P:A4 SP:F9 CYC:215 SL:167 +FA93 F0 51 BEQ $FAE6 A:D3 X:FF Y:D2 P:A4 SP:F9 CYC:221 SL:167 +FA95 10 4F BPL $FAE6 A:D3 X:FF Y:D2 P:A4 SP:F9 CYC:227 SL:167 +FA97 B0 4D BCS $FAE6 A:D3 X:FF Y:D2 P:A4 SP:F9 CYC:233 SL:167 +FA99 C9 D3 CMP #$D3 A:D3 X:FF Y:D2 P:A4 SP:F9 CYC:239 SL:167 +FA9B D0 49 BNE $FAE6 A:D3 X:FF Y:D2 P:27 SP:F9 CYC:245 SL:167 +FA9D 60 RTS A:D3 X:FF Y:D2 P:27 SP:F9 CYC:251 SL:167 +F040 AD 47 06 LDA $0647 = 52 A:D3 X:FF Y:D2 P:27 SP:FB CYC:269 SL:167 +F043 C9 52 CMP #$52 A:52 X:FF Y:D2 P:25 SP:FB CYC:281 SL:167 +F045 F0 02 BEQ $F049 A:52 X:FF Y:D2 P:27 SP:FB CYC:287 SL:167 +F049 C8 INY A:52 X:FF Y:D2 P:27 SP:FB CYC:296 SL:167 +F04A A9 37 LDA #$37 A:52 X:FF Y:D3 P:A5 SP:FB CYC:302 SL:167 +F04C 8D 47 06 STA $0647 = 52 A:37 X:FF Y:D3 P:25 SP:FB CYC:308 SL:167 +F04F 20 9E FA JSR $FA9E A:37 X:FF Y:D3 P:25 SP:FB CYC:320 SL:167 +FA9E 24 01 BIT $01 = FF A:37 X:FF Y:D3 P:25 SP:F9 CYC:338 SL:167 +FAA0 38 SEC A:37 X:FF Y:D3 P:E5 SP:F9 CYC: 6 SL:168 +FAA1 A9 10 LDA #$10 A:37 X:FF Y:D3 P:E5 SP:F9 CYC: 12 SL:168 +FAA3 60 RTS A:10 X:FF Y:D3 P:65 SP:F9 CYC: 18 SL:168 +F052 1F 48 05 *SLO $0548,X @ 0647 = 37 A:10 X:FF Y:D3 P:65 SP:FB CYC: 36 SL:168 +F055 EA NOP A:7E X:FF Y:D3 P:64 SP:FB CYC: 57 SL:168 +F056 EA NOP A:7E X:FF Y:D3 P:64 SP:FB CYC: 63 SL:168 +F057 EA NOP A:7E X:FF Y:D3 P:64 SP:FB CYC: 69 SL:168 +F058 EA NOP A:7E X:FF Y:D3 P:64 SP:FB CYC: 75 SL:168 +F059 20 A4 FA JSR $FAA4 A:7E X:FF Y:D3 P:64 SP:FB CYC: 81 SL:168 +FAA4 50 40 BVC $FAE6 A:7E X:FF Y:D3 P:64 SP:F9 CYC: 99 SL:168 +FAA6 F0 3E BEQ $FAE6 A:7E X:FF Y:D3 P:64 SP:F9 CYC:105 SL:168 +FAA8 30 3C BMI $FAE6 A:7E X:FF Y:D3 P:64 SP:F9 CYC:111 SL:168 +FAAA B0 3A BCS $FAE6 A:7E X:FF Y:D3 P:64 SP:F9 CYC:117 SL:168 +FAAC C9 7E CMP #$7E A:7E X:FF Y:D3 P:64 SP:F9 CYC:123 SL:168 +FAAE D0 36 BNE $FAE6 A:7E X:FF Y:D3 P:67 SP:F9 CYC:129 SL:168 +FAB0 60 RTS A:7E X:FF Y:D3 P:67 SP:F9 CYC:135 SL:168 +F05C AD 47 06 LDA $0647 = 6E A:7E X:FF Y:D3 P:67 SP:FB CYC:153 SL:168 +F05F C9 6E CMP #$6E A:6E X:FF Y:D3 P:65 SP:FB CYC:165 SL:168 +F061 F0 02 BEQ $F065 A:6E X:FF Y:D3 P:67 SP:FB CYC:171 SL:168 +F065 60 RTS A:6E X:FF Y:D3 P:67 SP:FB CYC:180 SL:168 +C644 20 66 F0 JSR $F066 A:6E X:FF Y:D3 P:67 SP:FD CYC:198 SL:168 +F066 A9 FF LDA #$FF A:6E X:FF Y:D3 P:67 SP:FB CYC:216 SL:168 +F068 85 01 STA $01 = FF A:FF X:FF Y:D3 P:E5 SP:FB CYC:222 SL:168 +F06A A0 D4 LDY #$D4 A:FF X:FF Y:D3 P:E5 SP:FB CYC:231 SL:168 +F06C A2 02 LDX #$02 A:FF X:FF Y:D4 P:E5 SP:FB CYC:237 SL:168 +F06E A9 47 LDA #$47 A:FF X:02 Y:D4 P:65 SP:FB CYC:243 SL:168 +F070 85 47 STA $47 = 6E A:47 X:02 Y:D4 P:65 SP:FB CYC:249 SL:168 +F072 A9 06 LDA #$06 A:47 X:02 Y:D4 P:65 SP:FB CYC:258 SL:168 +F074 85 48 STA $48 = 06 A:06 X:02 Y:D4 P:65 SP:FB CYC:264 SL:168 +F076 A9 A5 LDA #$A5 A:06 X:02 Y:D4 P:65 SP:FB CYC:273 SL:168 +F078 8D 47 06 STA $0647 = 6E A:A5 X:02 Y:D4 P:E5 SP:FB CYC:279 SL:168 +F07B 20 53 FB JSR $FB53 A:A5 X:02 Y:D4 P:E5 SP:FB CYC:291 SL:168 +FB53 24 01 BIT $01 = FF A:A5 X:02 Y:D4 P:E5 SP:F9 CYC:309 SL:168 +FB55 18 CLC A:A5 X:02 Y:D4 P:E5 SP:F9 CYC:318 SL:168 +FB56 A9 B3 LDA #$B3 A:A5 X:02 Y:D4 P:E4 SP:F9 CYC:324 SL:168 +FB58 60 RTS A:B3 X:02 Y:D4 P:E4 SP:F9 CYC:330 SL:168 +F07E 23 45 *RLA ($45,X) @ 47 = 0647 = A5 A:B3 X:02 Y:D4 P:E4 SP:FB CYC: 7 SL:169 +F080 EA NOP A:02 X:02 Y:D4 P:65 SP:FB CYC: 31 SL:169 +F081 EA NOP A:02 X:02 Y:D4 P:65 SP:FB CYC: 37 SL:169 +F082 EA NOP A:02 X:02 Y:D4 P:65 SP:FB CYC: 43 SL:169 +F083 EA NOP A:02 X:02 Y:D4 P:65 SP:FB CYC: 49 SL:169 +F084 20 59 FB JSR $FB59 A:02 X:02 Y:D4 P:65 SP:FB CYC: 55 SL:169 +FB59 50 1A BVC $FB75 A:02 X:02 Y:D4 P:65 SP:F9 CYC: 73 SL:169 +FB5B 90 18 BCC $FB75 A:02 X:02 Y:D4 P:65 SP:F9 CYC: 79 SL:169 +FB5D 30 16 BMI $FB75 A:02 X:02 Y:D4 P:65 SP:F9 CYC: 85 SL:169 +FB5F C9 02 CMP #$02 A:02 X:02 Y:D4 P:65 SP:F9 CYC: 91 SL:169 +FB61 D0 12 BNE $FB75 A:02 X:02 Y:D4 P:67 SP:F9 CYC: 97 SL:169 +FB63 60 RTS A:02 X:02 Y:D4 P:67 SP:F9 CYC:103 SL:169 +F087 AD 47 06 LDA $0647 = 4A A:02 X:02 Y:D4 P:67 SP:FB CYC:121 SL:169 +F08A C9 4A CMP #$4A A:4A X:02 Y:D4 P:65 SP:FB CYC:133 SL:169 +F08C F0 02 BEQ $F090 A:4A X:02 Y:D4 P:67 SP:FB CYC:139 SL:169 +F090 C8 INY A:4A X:02 Y:D4 P:67 SP:FB CYC:148 SL:169 +F091 A9 29 LDA #$29 A:4A X:02 Y:D5 P:E5 SP:FB CYC:154 SL:169 +F093 8D 47 06 STA $0647 = 4A A:29 X:02 Y:D5 P:65 SP:FB CYC:160 SL:169 +F096 20 64 FB JSR $FB64 A:29 X:02 Y:D5 P:65 SP:FB CYC:172 SL:169 +FB64 B8 CLV A:29 X:02 Y:D5 P:65 SP:F9 CYC:190 SL:169 +FB65 18 CLC A:29 X:02 Y:D5 P:25 SP:F9 CYC:196 SL:169 +FB66 A9 42 LDA #$42 A:29 X:02 Y:D5 P:24 SP:F9 CYC:202 SL:169 +FB68 60 RTS A:42 X:02 Y:D5 P:24 SP:F9 CYC:208 SL:169 +F099 23 45 *RLA ($45,X) @ 47 = 0647 = 29 A:42 X:02 Y:D5 P:24 SP:FB CYC:226 SL:169 +F09B EA NOP A:42 X:02 Y:D5 P:24 SP:FB CYC:250 SL:169 +F09C EA NOP A:42 X:02 Y:D5 P:24 SP:FB CYC:256 SL:169 +F09D EA NOP A:42 X:02 Y:D5 P:24 SP:FB CYC:262 SL:169 +F09E EA NOP A:42 X:02 Y:D5 P:24 SP:FB CYC:268 SL:169 +F09F 20 69 FB JSR $FB69 A:42 X:02 Y:D5 P:24 SP:FB CYC:274 SL:169 +FB69 70 0A BVS $FB75 A:42 X:02 Y:D5 P:24 SP:F9 CYC:292 SL:169 +FB6B F0 08 BEQ $FB75 A:42 X:02 Y:D5 P:24 SP:F9 CYC:298 SL:169 +FB6D 30 06 BMI $FB75 A:42 X:02 Y:D5 P:24 SP:F9 CYC:304 SL:169 +FB6F B0 04 BCS $FB75 A:42 X:02 Y:D5 P:24 SP:F9 CYC:310 SL:169 +FB71 C9 42 CMP #$42 A:42 X:02 Y:D5 P:24 SP:F9 CYC:316 SL:169 +FB73 F0 02 BEQ $FB77 A:42 X:02 Y:D5 P:27 SP:F9 CYC:322 SL:169 +FB77 60 RTS A:42 X:02 Y:D5 P:27 SP:F9 CYC:331 SL:169 +F0A2 AD 47 06 LDA $0647 = 52 A:42 X:02 Y:D5 P:27 SP:FB CYC: 8 SL:170 +F0A5 C9 52 CMP #$52 A:52 X:02 Y:D5 P:25 SP:FB CYC: 20 SL:170 +F0A7 F0 02 BEQ $F0AB A:52 X:02 Y:D5 P:27 SP:FB CYC: 26 SL:170 +F0AB C8 INY A:52 X:02 Y:D5 P:27 SP:FB CYC: 35 SL:170 +F0AC A9 37 LDA #$37 A:52 X:02 Y:D6 P:A5 SP:FB CYC: 41 SL:170 +F0AE 8D 47 06 STA $0647 = 52 A:37 X:02 Y:D6 P:25 SP:FB CYC: 47 SL:170 +F0B1 20 68 FA JSR $FA68 A:37 X:02 Y:D6 P:25 SP:FB CYC: 59 SL:170 +FA68 24 01 BIT $01 = FF A:37 X:02 Y:D6 P:25 SP:F9 CYC: 77 SL:170 +FA6A 38 SEC A:37 X:02 Y:D6 P:E5 SP:F9 CYC: 86 SL:170 +FA6B A9 75 LDA #$75 A:37 X:02 Y:D6 P:E5 SP:F9 CYC: 92 SL:170 +FA6D 60 RTS A:75 X:02 Y:D6 P:65 SP:F9 CYC: 98 SL:170 +F0B4 23 45 *RLA ($45,X) @ 47 = 0647 = 37 A:75 X:02 Y:D6 P:65 SP:FB CYC:116 SL:170 +F0B6 EA NOP A:65 X:02 Y:D6 P:64 SP:FB CYC:140 SL:170 +F0B7 EA NOP A:65 X:02 Y:D6 P:64 SP:FB CYC:146 SL:170 +F0B8 EA NOP A:65 X:02 Y:D6 P:64 SP:FB CYC:152 SL:170 +F0B9 EA NOP A:65 X:02 Y:D6 P:64 SP:FB CYC:158 SL:170 +F0BA 20 6E FA JSR $FA6E A:65 X:02 Y:D6 P:64 SP:FB CYC:164 SL:170 +FA6E 50 76 BVC $FAE6 A:65 X:02 Y:D6 P:64 SP:F9 CYC:182 SL:170 +FA70 F0 74 BEQ $FAE6 A:65 X:02 Y:D6 P:64 SP:F9 CYC:188 SL:170 +FA72 30 72 BMI $FAE6 A:65 X:02 Y:D6 P:64 SP:F9 CYC:194 SL:170 +FA74 B0 70 BCS $FAE6 A:65 X:02 Y:D6 P:64 SP:F9 CYC:200 SL:170 +FA76 C9 65 CMP #$65 A:65 X:02 Y:D6 P:64 SP:F9 CYC:206 SL:170 +FA78 D0 6C BNE $FAE6 A:65 X:02 Y:D6 P:67 SP:F9 CYC:212 SL:170 +FA7A 60 RTS A:65 X:02 Y:D6 P:67 SP:F9 CYC:218 SL:170 +F0BD AD 47 06 LDA $0647 = 6F A:65 X:02 Y:D6 P:67 SP:FB CYC:236 SL:170 +F0C0 C9 6F CMP #$6F A:6F X:02 Y:D6 P:65 SP:FB CYC:248 SL:170 +F0C2 F0 02 BEQ $F0C6 A:6F X:02 Y:D6 P:67 SP:FB CYC:254 SL:170 +F0C6 C8 INY A:6F X:02 Y:D6 P:67 SP:FB CYC:263 SL:170 +F0C7 A9 A5 LDA #$A5 A:6F X:02 Y:D7 P:E5 SP:FB CYC:269 SL:170 +F0C9 85 47 STA $47 = 47 A:A5 X:02 Y:D7 P:E5 SP:FB CYC:275 SL:170 +F0CB 20 53 FB JSR $FB53 A:A5 X:02 Y:D7 P:E5 SP:FB CYC:284 SL:170 +FB53 24 01 BIT $01 = FF A:A5 X:02 Y:D7 P:E5 SP:F9 CYC:302 SL:170 +FB55 18 CLC A:A5 X:02 Y:D7 P:E5 SP:F9 CYC:311 SL:170 +FB56 A9 B3 LDA #$B3 A:A5 X:02 Y:D7 P:E4 SP:F9 CYC:317 SL:170 +FB58 60 RTS A:B3 X:02 Y:D7 P:E4 SP:F9 CYC:323 SL:170 +F0CE 27 47 *RLA $47 = A5 A:B3 X:02 Y:D7 P:E4 SP:FB CYC: 0 SL:171 +F0D0 EA NOP A:02 X:02 Y:D7 P:65 SP:FB CYC: 15 SL:171 +F0D1 EA NOP A:02 X:02 Y:D7 P:65 SP:FB CYC: 21 SL:171 +F0D2 EA NOP A:02 X:02 Y:D7 P:65 SP:FB CYC: 27 SL:171 +F0D3 EA NOP A:02 X:02 Y:D7 P:65 SP:FB CYC: 33 SL:171 +F0D4 20 59 FB JSR $FB59 A:02 X:02 Y:D7 P:65 SP:FB CYC: 39 SL:171 +FB59 50 1A BVC $FB75 A:02 X:02 Y:D7 P:65 SP:F9 CYC: 57 SL:171 +FB5B 90 18 BCC $FB75 A:02 X:02 Y:D7 P:65 SP:F9 CYC: 63 SL:171 +FB5D 30 16 BMI $FB75 A:02 X:02 Y:D7 P:65 SP:F9 CYC: 69 SL:171 +FB5F C9 02 CMP #$02 A:02 X:02 Y:D7 P:65 SP:F9 CYC: 75 SL:171 +FB61 D0 12 BNE $FB75 A:02 X:02 Y:D7 P:67 SP:F9 CYC: 81 SL:171 +FB63 60 RTS A:02 X:02 Y:D7 P:67 SP:F9 CYC: 87 SL:171 +F0D7 A5 47 LDA $47 = 4A A:02 X:02 Y:D7 P:67 SP:FB CYC:105 SL:171 +F0D9 C9 4A CMP #$4A A:4A X:02 Y:D7 P:65 SP:FB CYC:114 SL:171 +F0DB F0 02 BEQ $F0DF A:4A X:02 Y:D7 P:67 SP:FB CYC:120 SL:171 +F0DF C8 INY A:4A X:02 Y:D7 P:67 SP:FB CYC:129 SL:171 +F0E0 A9 29 LDA #$29 A:4A X:02 Y:D8 P:E5 SP:FB CYC:135 SL:171 +F0E2 85 47 STA $47 = 4A A:29 X:02 Y:D8 P:65 SP:FB CYC:141 SL:171 +F0E4 20 64 FB JSR $FB64 A:29 X:02 Y:D8 P:65 SP:FB CYC:150 SL:171 +FB64 B8 CLV A:29 X:02 Y:D8 P:65 SP:F9 CYC:168 SL:171 +FB65 18 CLC A:29 X:02 Y:D8 P:25 SP:F9 CYC:174 SL:171 +FB66 A9 42 LDA #$42 A:29 X:02 Y:D8 P:24 SP:F9 CYC:180 SL:171 +FB68 60 RTS A:42 X:02 Y:D8 P:24 SP:F9 CYC:186 SL:171 +F0E7 27 47 *RLA $47 = 29 A:42 X:02 Y:D8 P:24 SP:FB CYC:204 SL:171 +F0E9 EA NOP A:42 X:02 Y:D8 P:24 SP:FB CYC:219 SL:171 +F0EA EA NOP A:42 X:02 Y:D8 P:24 SP:FB CYC:225 SL:171 +F0EB EA NOP A:42 X:02 Y:D8 P:24 SP:FB CYC:231 SL:171 +F0EC EA NOP A:42 X:02 Y:D8 P:24 SP:FB CYC:237 SL:171 +F0ED 20 69 FB JSR $FB69 A:42 X:02 Y:D8 P:24 SP:FB CYC:243 SL:171 +FB69 70 0A BVS $FB75 A:42 X:02 Y:D8 P:24 SP:F9 CYC:261 SL:171 +FB6B F0 08 BEQ $FB75 A:42 X:02 Y:D8 P:24 SP:F9 CYC:267 SL:171 +FB6D 30 06 BMI $FB75 A:42 X:02 Y:D8 P:24 SP:F9 CYC:273 SL:171 +FB6F B0 04 BCS $FB75 A:42 X:02 Y:D8 P:24 SP:F9 CYC:279 SL:171 +FB71 C9 42 CMP #$42 A:42 X:02 Y:D8 P:24 SP:F9 CYC:285 SL:171 +FB73 F0 02 BEQ $FB77 A:42 X:02 Y:D8 P:27 SP:F9 CYC:291 SL:171 +FB77 60 RTS A:42 X:02 Y:D8 P:27 SP:F9 CYC:300 SL:171 +F0F0 A5 47 LDA $47 = 52 A:42 X:02 Y:D8 P:27 SP:FB CYC:318 SL:171 +F0F2 C9 52 CMP #$52 A:52 X:02 Y:D8 P:25 SP:FB CYC:327 SL:171 +F0F4 F0 02 BEQ $F0F8 A:52 X:02 Y:D8 P:27 SP:FB CYC:333 SL:171 +F0F8 C8 INY A:52 X:02 Y:D8 P:27 SP:FB CYC: 1 SL:172 +F0F9 A9 37 LDA #$37 A:52 X:02 Y:D9 P:A5 SP:FB CYC: 7 SL:172 +F0FB 85 47 STA $47 = 52 A:37 X:02 Y:D9 P:25 SP:FB CYC: 13 SL:172 +F0FD 20 68 FA JSR $FA68 A:37 X:02 Y:D9 P:25 SP:FB CYC: 22 SL:172 +FA68 24 01 BIT $01 = FF A:37 X:02 Y:D9 P:25 SP:F9 CYC: 40 SL:172 +FA6A 38 SEC A:37 X:02 Y:D9 P:E5 SP:F9 CYC: 49 SL:172 +FA6B A9 75 LDA #$75 A:37 X:02 Y:D9 P:E5 SP:F9 CYC: 55 SL:172 +FA6D 60 RTS A:75 X:02 Y:D9 P:65 SP:F9 CYC: 61 SL:172 +F100 27 47 *RLA $47 = 37 A:75 X:02 Y:D9 P:65 SP:FB CYC: 79 SL:172 +F102 EA NOP A:65 X:02 Y:D9 P:64 SP:FB CYC: 94 SL:172 +F103 EA NOP A:65 X:02 Y:D9 P:64 SP:FB CYC:100 SL:172 +F104 EA NOP A:65 X:02 Y:D9 P:64 SP:FB CYC:106 SL:172 +F105 EA NOP A:65 X:02 Y:D9 P:64 SP:FB CYC:112 SL:172 +F106 20 6E FA JSR $FA6E A:65 X:02 Y:D9 P:64 SP:FB CYC:118 SL:172 +FA6E 50 76 BVC $FAE6 A:65 X:02 Y:D9 P:64 SP:F9 CYC:136 SL:172 +FA70 F0 74 BEQ $FAE6 A:65 X:02 Y:D9 P:64 SP:F9 CYC:142 SL:172 +FA72 30 72 BMI $FAE6 A:65 X:02 Y:D9 P:64 SP:F9 CYC:148 SL:172 +FA74 B0 70 BCS $FAE6 A:65 X:02 Y:D9 P:64 SP:F9 CYC:154 SL:172 +FA76 C9 65 CMP #$65 A:65 X:02 Y:D9 P:64 SP:F9 CYC:160 SL:172 +FA78 D0 6C BNE $FAE6 A:65 X:02 Y:D9 P:67 SP:F9 CYC:166 SL:172 +FA7A 60 RTS A:65 X:02 Y:D9 P:67 SP:F9 CYC:172 SL:172 +F109 A5 47 LDA $47 = 6F A:65 X:02 Y:D9 P:67 SP:FB CYC:190 SL:172 +F10B C9 6F CMP #$6F A:6F X:02 Y:D9 P:65 SP:FB CYC:199 SL:172 +F10D F0 02 BEQ $F111 A:6F X:02 Y:D9 P:67 SP:FB CYC:205 SL:172 +F111 C8 INY A:6F X:02 Y:D9 P:67 SP:FB CYC:214 SL:172 +F112 A9 A5 LDA #$A5 A:6F X:02 Y:DA P:E5 SP:FB CYC:220 SL:172 +F114 8D 47 06 STA $0647 = 6F A:A5 X:02 Y:DA P:E5 SP:FB CYC:226 SL:172 +F117 20 53 FB JSR $FB53 A:A5 X:02 Y:DA P:E5 SP:FB CYC:238 SL:172 +FB53 24 01 BIT $01 = FF A:A5 X:02 Y:DA P:E5 SP:F9 CYC:256 SL:172 +FB55 18 CLC A:A5 X:02 Y:DA P:E5 SP:F9 CYC:265 SL:172 +FB56 A9 B3 LDA #$B3 A:A5 X:02 Y:DA P:E4 SP:F9 CYC:271 SL:172 +FB58 60 RTS A:B3 X:02 Y:DA P:E4 SP:F9 CYC:277 SL:172 +F11A 2F 47 06 *RLA $0647 = A5 A:B3 X:02 Y:DA P:E4 SP:FB CYC:295 SL:172 +F11D EA NOP A:02 X:02 Y:DA P:65 SP:FB CYC:313 SL:172 +F11E EA NOP A:02 X:02 Y:DA P:65 SP:FB CYC:319 SL:172 +F11F EA NOP A:02 X:02 Y:DA P:65 SP:FB CYC:325 SL:172 +F120 EA NOP A:02 X:02 Y:DA P:65 SP:FB CYC:331 SL:172 +F121 20 59 FB JSR $FB59 A:02 X:02 Y:DA P:65 SP:FB CYC:337 SL:172 +FB59 50 1A BVC $FB75 A:02 X:02 Y:DA P:65 SP:F9 CYC: 14 SL:173 +FB5B 90 18 BCC $FB75 A:02 X:02 Y:DA P:65 SP:F9 CYC: 20 SL:173 +FB5D 30 16 BMI $FB75 A:02 X:02 Y:DA P:65 SP:F9 CYC: 26 SL:173 +FB5F C9 02 CMP #$02 A:02 X:02 Y:DA P:65 SP:F9 CYC: 32 SL:173 +FB61 D0 12 BNE $FB75 A:02 X:02 Y:DA P:67 SP:F9 CYC: 38 SL:173 +FB63 60 RTS A:02 X:02 Y:DA P:67 SP:F9 CYC: 44 SL:173 +F124 AD 47 06 LDA $0647 = 4A A:02 X:02 Y:DA P:67 SP:FB CYC: 62 SL:173 +F127 C9 4A CMP #$4A A:4A X:02 Y:DA P:65 SP:FB CYC: 74 SL:173 +F129 F0 02 BEQ $F12D A:4A X:02 Y:DA P:67 SP:FB CYC: 80 SL:173 +F12D C8 INY A:4A X:02 Y:DA P:67 SP:FB CYC: 89 SL:173 +F12E A9 29 LDA #$29 A:4A X:02 Y:DB P:E5 SP:FB CYC: 95 SL:173 +F130 8D 47 06 STA $0647 = 4A A:29 X:02 Y:DB P:65 SP:FB CYC:101 SL:173 +F133 20 64 FB JSR $FB64 A:29 X:02 Y:DB P:65 SP:FB CYC:113 SL:173 +FB64 B8 CLV A:29 X:02 Y:DB P:65 SP:F9 CYC:131 SL:173 +FB65 18 CLC A:29 X:02 Y:DB P:25 SP:F9 CYC:137 SL:173 +FB66 A9 42 LDA #$42 A:29 X:02 Y:DB P:24 SP:F9 CYC:143 SL:173 +FB68 60 RTS A:42 X:02 Y:DB P:24 SP:F9 CYC:149 SL:173 +F136 2F 47 06 *RLA $0647 = 29 A:42 X:02 Y:DB P:24 SP:FB CYC:167 SL:173 +F139 EA NOP A:42 X:02 Y:DB P:24 SP:FB CYC:185 SL:173 +F13A EA NOP A:42 X:02 Y:DB P:24 SP:FB CYC:191 SL:173 +F13B EA NOP A:42 X:02 Y:DB P:24 SP:FB CYC:197 SL:173 +F13C EA NOP A:42 X:02 Y:DB P:24 SP:FB CYC:203 SL:173 +F13D 20 69 FB JSR $FB69 A:42 X:02 Y:DB P:24 SP:FB CYC:209 SL:173 +FB69 70 0A BVS $FB75 A:42 X:02 Y:DB P:24 SP:F9 CYC:227 SL:173 +FB6B F0 08 BEQ $FB75 A:42 X:02 Y:DB P:24 SP:F9 CYC:233 SL:173 +FB6D 30 06 BMI $FB75 A:42 X:02 Y:DB P:24 SP:F9 CYC:239 SL:173 +FB6F B0 04 BCS $FB75 A:42 X:02 Y:DB P:24 SP:F9 CYC:245 SL:173 +FB71 C9 42 CMP #$42 A:42 X:02 Y:DB P:24 SP:F9 CYC:251 SL:173 +FB73 F0 02 BEQ $FB77 A:42 X:02 Y:DB P:27 SP:F9 CYC:257 SL:173 +FB77 60 RTS A:42 X:02 Y:DB P:27 SP:F9 CYC:266 SL:173 +F140 AD 47 06 LDA $0647 = 52 A:42 X:02 Y:DB P:27 SP:FB CYC:284 SL:173 +F143 C9 52 CMP #$52 A:52 X:02 Y:DB P:25 SP:FB CYC:296 SL:173 +F145 F0 02 BEQ $F149 A:52 X:02 Y:DB P:27 SP:FB CYC:302 SL:173 +F149 C8 INY A:52 X:02 Y:DB P:27 SP:FB CYC:311 SL:173 +F14A A9 37 LDA #$37 A:52 X:02 Y:DC P:A5 SP:FB CYC:317 SL:173 +F14C 8D 47 06 STA $0647 = 52 A:37 X:02 Y:DC P:25 SP:FB CYC:323 SL:173 +F14F 20 68 FA JSR $FA68 A:37 X:02 Y:DC P:25 SP:FB CYC:335 SL:173 +FA68 24 01 BIT $01 = FF A:37 X:02 Y:DC P:25 SP:F9 CYC: 12 SL:174 +FA6A 38 SEC A:37 X:02 Y:DC P:E5 SP:F9 CYC: 21 SL:174 +FA6B A9 75 LDA #$75 A:37 X:02 Y:DC P:E5 SP:F9 CYC: 27 SL:174 +FA6D 60 RTS A:75 X:02 Y:DC P:65 SP:F9 CYC: 33 SL:174 +F152 2F 47 06 *RLA $0647 = 37 A:75 X:02 Y:DC P:65 SP:FB CYC: 51 SL:174 +F155 EA NOP A:65 X:02 Y:DC P:64 SP:FB CYC: 69 SL:174 +F156 EA NOP A:65 X:02 Y:DC P:64 SP:FB CYC: 75 SL:174 +F157 EA NOP A:65 X:02 Y:DC P:64 SP:FB CYC: 81 SL:174 +F158 EA NOP A:65 X:02 Y:DC P:64 SP:FB CYC: 87 SL:174 +F159 20 6E FA JSR $FA6E A:65 X:02 Y:DC P:64 SP:FB CYC: 93 SL:174 +FA6E 50 76 BVC $FAE6 A:65 X:02 Y:DC P:64 SP:F9 CYC:111 SL:174 +FA70 F0 74 BEQ $FAE6 A:65 X:02 Y:DC P:64 SP:F9 CYC:117 SL:174 +FA72 30 72 BMI $FAE6 A:65 X:02 Y:DC P:64 SP:F9 CYC:123 SL:174 +FA74 B0 70 BCS $FAE6 A:65 X:02 Y:DC P:64 SP:F9 CYC:129 SL:174 +FA76 C9 65 CMP #$65 A:65 X:02 Y:DC P:64 SP:F9 CYC:135 SL:174 +FA78 D0 6C BNE $FAE6 A:65 X:02 Y:DC P:67 SP:F9 CYC:141 SL:174 +FA7A 60 RTS A:65 X:02 Y:DC P:67 SP:F9 CYC:147 SL:174 +F15C AD 47 06 LDA $0647 = 6F A:65 X:02 Y:DC P:67 SP:FB CYC:165 SL:174 +F15F C9 6F CMP #$6F A:6F X:02 Y:DC P:65 SP:FB CYC:177 SL:174 +F161 F0 02 BEQ $F165 A:6F X:02 Y:DC P:67 SP:FB CYC:183 SL:174 +F165 A9 A5 LDA #$A5 A:6F X:02 Y:DC P:67 SP:FB CYC:192 SL:174 +F167 8D 47 06 STA $0647 = 6F A:A5 X:02 Y:DC P:E5 SP:FB CYC:198 SL:174 +F16A A9 48 LDA #$48 A:A5 X:02 Y:DC P:E5 SP:FB CYC:210 SL:174 +F16C 85 45 STA $45 = 48 A:48 X:02 Y:DC P:65 SP:FB CYC:216 SL:174 +F16E A9 05 LDA #$05 A:48 X:02 Y:DC P:65 SP:FB CYC:225 SL:174 +F170 85 46 STA $46 = 05 A:05 X:02 Y:DC P:65 SP:FB CYC:231 SL:174 +F172 A0 FF LDY #$FF A:05 X:02 Y:DC P:65 SP:FB CYC:240 SL:174 +F174 20 53 FB JSR $FB53 A:05 X:02 Y:FF P:E5 SP:FB CYC:246 SL:174 +FB53 24 01 BIT $01 = FF A:05 X:02 Y:FF P:E5 SP:F9 CYC:264 SL:174 +FB55 18 CLC A:05 X:02 Y:FF P:E5 SP:F9 CYC:273 SL:174 +FB56 A9 B3 LDA #$B3 A:05 X:02 Y:FF P:E4 SP:F9 CYC:279 SL:174 +FB58 60 RTS A:B3 X:02 Y:FF P:E4 SP:F9 CYC:285 SL:174 +F177 33 45 *RLA ($45),Y = 0548 @ 0647 = A5 A:B3 X:02 Y:FF P:E4 SP:FB CYC:303 SL:174 +F179 EA NOP A:02 X:02 Y:FF P:65 SP:FB CYC:327 SL:174 +F17A EA NOP A:02 X:02 Y:FF P:65 SP:FB CYC:333 SL:174 +F17B 08 PHP A:02 X:02 Y:FF P:65 SP:FB CYC:339 SL:174 +F17C 48 PHA A:02 X:02 Y:FF P:65 SP:FA CYC: 7 SL:175 +F17D A0 DD LDY #$DD A:02 X:02 Y:FF P:65 SP:F9 CYC: 16 SL:175 +F17F 68 PLA A:02 X:02 Y:DD P:E5 SP:F9 CYC: 22 SL:175 +F180 28 PLP A:02 X:02 Y:DD P:65 SP:FA CYC: 34 SL:175 +F181 20 59 FB JSR $FB59 A:02 X:02 Y:DD P:65 SP:FB CYC: 46 SL:175 +FB59 50 1A BVC $FB75 A:02 X:02 Y:DD P:65 SP:F9 CYC: 64 SL:175 +FB5B 90 18 BCC $FB75 A:02 X:02 Y:DD P:65 SP:F9 CYC: 70 SL:175 +FB5D 30 16 BMI $FB75 A:02 X:02 Y:DD P:65 SP:F9 CYC: 76 SL:175 +FB5F C9 02 CMP #$02 A:02 X:02 Y:DD P:65 SP:F9 CYC: 82 SL:175 +FB61 D0 12 BNE $FB75 A:02 X:02 Y:DD P:67 SP:F9 CYC: 88 SL:175 +FB63 60 RTS A:02 X:02 Y:DD P:67 SP:F9 CYC: 94 SL:175 +F184 AD 47 06 LDA $0647 = 4A A:02 X:02 Y:DD P:67 SP:FB CYC:112 SL:175 +F187 C9 4A CMP #$4A A:4A X:02 Y:DD P:65 SP:FB CYC:124 SL:175 +F189 F0 02 BEQ $F18D A:4A X:02 Y:DD P:67 SP:FB CYC:130 SL:175 +F18D A0 FF LDY #$FF A:4A X:02 Y:DD P:67 SP:FB CYC:139 SL:175 +F18F A9 29 LDA #$29 A:4A X:02 Y:FF P:E5 SP:FB CYC:145 SL:175 +F191 8D 47 06 STA $0647 = 4A A:29 X:02 Y:FF P:65 SP:FB CYC:151 SL:175 +F194 20 64 FB JSR $FB64 A:29 X:02 Y:FF P:65 SP:FB CYC:163 SL:175 +FB64 B8 CLV A:29 X:02 Y:FF P:65 SP:F9 CYC:181 SL:175 +FB65 18 CLC A:29 X:02 Y:FF P:25 SP:F9 CYC:187 SL:175 +FB66 A9 42 LDA #$42 A:29 X:02 Y:FF P:24 SP:F9 CYC:193 SL:175 +FB68 60 RTS A:42 X:02 Y:FF P:24 SP:F9 CYC:199 SL:175 +F197 33 45 *RLA ($45),Y = 0548 @ 0647 = 29 A:42 X:02 Y:FF P:24 SP:FB CYC:217 SL:175 +F199 EA NOP A:42 X:02 Y:FF P:24 SP:FB CYC:241 SL:175 +F19A EA NOP A:42 X:02 Y:FF P:24 SP:FB CYC:247 SL:175 +F19B 08 PHP A:42 X:02 Y:FF P:24 SP:FB CYC:253 SL:175 +F19C 48 PHA A:42 X:02 Y:FF P:24 SP:FA CYC:262 SL:175 +F19D A0 DE LDY #$DE A:42 X:02 Y:FF P:24 SP:F9 CYC:271 SL:175 +F19F 68 PLA A:42 X:02 Y:DE P:A4 SP:F9 CYC:277 SL:175 +F1A0 28 PLP A:42 X:02 Y:DE P:24 SP:FA CYC:289 SL:175 +F1A1 20 69 FB JSR $FB69 A:42 X:02 Y:DE P:24 SP:FB CYC:301 SL:175 +FB69 70 0A BVS $FB75 A:42 X:02 Y:DE P:24 SP:F9 CYC:319 SL:175 +FB6B F0 08 BEQ $FB75 A:42 X:02 Y:DE P:24 SP:F9 CYC:325 SL:175 +FB6D 30 06 BMI $FB75 A:42 X:02 Y:DE P:24 SP:F9 CYC:331 SL:175 +FB6F B0 04 BCS $FB75 A:42 X:02 Y:DE P:24 SP:F9 CYC:337 SL:175 +FB71 C9 42 CMP #$42 A:42 X:02 Y:DE P:24 SP:F9 CYC: 2 SL:176 +FB73 F0 02 BEQ $FB77 A:42 X:02 Y:DE P:27 SP:F9 CYC: 8 SL:176 +FB77 60 RTS A:42 X:02 Y:DE P:27 SP:F9 CYC: 17 SL:176 +F1A4 AD 47 06 LDA $0647 = 52 A:42 X:02 Y:DE P:27 SP:FB CYC: 35 SL:176 +F1A7 C9 52 CMP #$52 A:52 X:02 Y:DE P:25 SP:FB CYC: 47 SL:176 +F1A9 F0 02 BEQ $F1AD A:52 X:02 Y:DE P:27 SP:FB CYC: 53 SL:176 +F1AD A0 FF LDY #$FF A:52 X:02 Y:DE P:27 SP:FB CYC: 62 SL:176 +F1AF A9 37 LDA #$37 A:52 X:02 Y:FF P:A5 SP:FB CYC: 68 SL:176 +F1B1 8D 47 06 STA $0647 = 52 A:37 X:02 Y:FF P:25 SP:FB CYC: 74 SL:176 +F1B4 20 68 FA JSR $FA68 A:37 X:02 Y:FF P:25 SP:FB CYC: 86 SL:176 +FA68 24 01 BIT $01 = FF A:37 X:02 Y:FF P:25 SP:F9 CYC:104 SL:176 +FA6A 38 SEC A:37 X:02 Y:FF P:E5 SP:F9 CYC:113 SL:176 +FA6B A9 75 LDA #$75 A:37 X:02 Y:FF P:E5 SP:F9 CYC:119 SL:176 +FA6D 60 RTS A:75 X:02 Y:FF P:65 SP:F9 CYC:125 SL:176 +F1B7 33 45 *RLA ($45),Y = 0548 @ 0647 = 37 A:75 X:02 Y:FF P:65 SP:FB CYC:143 SL:176 +F1B9 EA NOP A:65 X:02 Y:FF P:64 SP:FB CYC:167 SL:176 +F1BA EA NOP A:65 X:02 Y:FF P:64 SP:FB CYC:173 SL:176 +F1BB 08 PHP A:65 X:02 Y:FF P:64 SP:FB CYC:179 SL:176 +F1BC 48 PHA A:65 X:02 Y:FF P:64 SP:FA CYC:188 SL:176 +F1BD A0 DF LDY #$DF A:65 X:02 Y:FF P:64 SP:F9 CYC:197 SL:176 +F1BF 68 PLA A:65 X:02 Y:DF P:E4 SP:F9 CYC:203 SL:176 +F1C0 28 PLP A:65 X:02 Y:DF P:64 SP:FA CYC:215 SL:176 +F1C1 20 6E FA JSR $FA6E A:65 X:02 Y:DF P:64 SP:FB CYC:227 SL:176 +FA6E 50 76 BVC $FAE6 A:65 X:02 Y:DF P:64 SP:F9 CYC:245 SL:176 +FA70 F0 74 BEQ $FAE6 A:65 X:02 Y:DF P:64 SP:F9 CYC:251 SL:176 +FA72 30 72 BMI $FAE6 A:65 X:02 Y:DF P:64 SP:F9 CYC:257 SL:176 +FA74 B0 70 BCS $FAE6 A:65 X:02 Y:DF P:64 SP:F9 CYC:263 SL:176 +FA76 C9 65 CMP #$65 A:65 X:02 Y:DF P:64 SP:F9 CYC:269 SL:176 +FA78 D0 6C BNE $FAE6 A:65 X:02 Y:DF P:67 SP:F9 CYC:275 SL:176 +FA7A 60 RTS A:65 X:02 Y:DF P:67 SP:F9 CYC:281 SL:176 +F1C4 AD 47 06 LDA $0647 = 6F A:65 X:02 Y:DF P:67 SP:FB CYC:299 SL:176 +F1C7 C9 6F CMP #$6F A:6F X:02 Y:DF P:65 SP:FB CYC:311 SL:176 +F1C9 F0 02 BEQ $F1CD A:6F X:02 Y:DF P:67 SP:FB CYC:317 SL:176 +F1CD A0 E0 LDY #$E0 A:6F X:02 Y:DF P:67 SP:FB CYC:326 SL:176 +F1CF A2 FF LDX #$FF A:6F X:02 Y:E0 P:E5 SP:FB CYC:332 SL:176 +F1D1 A9 A5 LDA #$A5 A:6F X:FF Y:E0 P:E5 SP:FB CYC:338 SL:176 +F1D3 85 47 STA $47 = 6F A:A5 X:FF Y:E0 P:E5 SP:FB CYC: 3 SL:177 +F1D5 20 53 FB JSR $FB53 A:A5 X:FF Y:E0 P:E5 SP:FB CYC: 12 SL:177 +FB53 24 01 BIT $01 = FF A:A5 X:FF Y:E0 P:E5 SP:F9 CYC: 30 SL:177 +FB55 18 CLC A:A5 X:FF Y:E0 P:E5 SP:F9 CYC: 39 SL:177 +FB56 A9 B3 LDA #$B3 A:A5 X:FF Y:E0 P:E4 SP:F9 CYC: 45 SL:177 +FB58 60 RTS A:B3 X:FF Y:E0 P:E4 SP:F9 CYC: 51 SL:177 +F1D8 37 48 *RLA $48,X @ 47 = A5 A:B3 X:FF Y:E0 P:E4 SP:FB CYC: 69 SL:177 +F1DA EA NOP A:02 X:FF Y:E0 P:65 SP:FB CYC: 87 SL:177 +F1DB EA NOP A:02 X:FF Y:E0 P:65 SP:FB CYC: 93 SL:177 +F1DC EA NOP A:02 X:FF Y:E0 P:65 SP:FB CYC: 99 SL:177 +F1DD EA NOP A:02 X:FF Y:E0 P:65 SP:FB CYC:105 SL:177 +F1DE 20 59 FB JSR $FB59 A:02 X:FF Y:E0 P:65 SP:FB CYC:111 SL:177 +FB59 50 1A BVC $FB75 A:02 X:FF Y:E0 P:65 SP:F9 CYC:129 SL:177 +FB5B 90 18 BCC $FB75 A:02 X:FF Y:E0 P:65 SP:F9 CYC:135 SL:177 +FB5D 30 16 BMI $FB75 A:02 X:FF Y:E0 P:65 SP:F9 CYC:141 SL:177 +FB5F C9 02 CMP #$02 A:02 X:FF Y:E0 P:65 SP:F9 CYC:147 SL:177 +FB61 D0 12 BNE $FB75 A:02 X:FF Y:E0 P:67 SP:F9 CYC:153 SL:177 +FB63 60 RTS A:02 X:FF Y:E0 P:67 SP:F9 CYC:159 SL:177 +F1E1 A5 47 LDA $47 = 4A A:02 X:FF Y:E0 P:67 SP:FB CYC:177 SL:177 +F1E3 C9 4A CMP #$4A A:4A X:FF Y:E0 P:65 SP:FB CYC:186 SL:177 +F1E5 F0 02 BEQ $F1E9 A:4A X:FF Y:E0 P:67 SP:FB CYC:192 SL:177 +F1E9 C8 INY A:4A X:FF Y:E0 P:67 SP:FB CYC:201 SL:177 +F1EA A9 29 LDA #$29 A:4A X:FF Y:E1 P:E5 SP:FB CYC:207 SL:177 +F1EC 85 47 STA $47 = 4A A:29 X:FF Y:E1 P:65 SP:FB CYC:213 SL:177 +F1EE 20 64 FB JSR $FB64 A:29 X:FF Y:E1 P:65 SP:FB CYC:222 SL:177 +FB64 B8 CLV A:29 X:FF Y:E1 P:65 SP:F9 CYC:240 SL:177 +FB65 18 CLC A:29 X:FF Y:E1 P:25 SP:F9 CYC:246 SL:177 +FB66 A9 42 LDA #$42 A:29 X:FF Y:E1 P:24 SP:F9 CYC:252 SL:177 +FB68 60 RTS A:42 X:FF Y:E1 P:24 SP:F9 CYC:258 SL:177 +F1F1 37 48 *RLA $48,X @ 47 = 29 A:42 X:FF Y:E1 P:24 SP:FB CYC:276 SL:177 +F1F3 EA NOP A:42 X:FF Y:E1 P:24 SP:FB CYC:294 SL:177 +F1F4 EA NOP A:42 X:FF Y:E1 P:24 SP:FB CYC:300 SL:177 +F1F5 EA NOP A:42 X:FF Y:E1 P:24 SP:FB CYC:306 SL:177 +F1F6 EA NOP A:42 X:FF Y:E1 P:24 SP:FB CYC:312 SL:177 +F1F7 20 69 FB JSR $FB69 A:42 X:FF Y:E1 P:24 SP:FB CYC:318 SL:177 +FB69 70 0A BVS $FB75 A:42 X:FF Y:E1 P:24 SP:F9 CYC:336 SL:177 +FB6B F0 08 BEQ $FB75 A:42 X:FF Y:E1 P:24 SP:F9 CYC: 1 SL:178 +FB6D 30 06 BMI $FB75 A:42 X:FF Y:E1 P:24 SP:F9 CYC: 7 SL:178 +FB6F B0 04 BCS $FB75 A:42 X:FF Y:E1 P:24 SP:F9 CYC: 13 SL:178 +FB71 C9 42 CMP #$42 A:42 X:FF Y:E1 P:24 SP:F9 CYC: 19 SL:178 +FB73 F0 02 BEQ $FB77 A:42 X:FF Y:E1 P:27 SP:F9 CYC: 25 SL:178 +FB77 60 RTS A:42 X:FF Y:E1 P:27 SP:F9 CYC: 34 SL:178 +F1FA A5 47 LDA $47 = 52 A:42 X:FF Y:E1 P:27 SP:FB CYC: 52 SL:178 +F1FC C9 52 CMP #$52 A:52 X:FF Y:E1 P:25 SP:FB CYC: 61 SL:178 +F1FE F0 02 BEQ $F202 A:52 X:FF Y:E1 P:27 SP:FB CYC: 67 SL:178 +F202 C8 INY A:52 X:FF Y:E1 P:27 SP:FB CYC: 76 SL:178 +F203 A9 37 LDA #$37 A:52 X:FF Y:E2 P:A5 SP:FB CYC: 82 SL:178 +F205 85 47 STA $47 = 52 A:37 X:FF Y:E2 P:25 SP:FB CYC: 88 SL:178 +F207 20 68 FA JSR $FA68 A:37 X:FF Y:E2 P:25 SP:FB CYC: 97 SL:178 +FA68 24 01 BIT $01 = FF A:37 X:FF Y:E2 P:25 SP:F9 CYC:115 SL:178 +FA6A 38 SEC A:37 X:FF Y:E2 P:E5 SP:F9 CYC:124 SL:178 +FA6B A9 75 LDA #$75 A:37 X:FF Y:E2 P:E5 SP:F9 CYC:130 SL:178 +FA6D 60 RTS A:75 X:FF Y:E2 P:65 SP:F9 CYC:136 SL:178 +F20A 37 48 *RLA $48,X @ 47 = 37 A:75 X:FF Y:E2 P:65 SP:FB CYC:154 SL:178 +F20C EA NOP A:65 X:FF Y:E2 P:64 SP:FB CYC:172 SL:178 +F20D EA NOP A:65 X:FF Y:E2 P:64 SP:FB CYC:178 SL:178 +F20E EA NOP A:65 X:FF Y:E2 P:64 SP:FB CYC:184 SL:178 +F20F EA NOP A:65 X:FF Y:E2 P:64 SP:FB CYC:190 SL:178 +F210 20 6E FA JSR $FA6E A:65 X:FF Y:E2 P:64 SP:FB CYC:196 SL:178 +FA6E 50 76 BVC $FAE6 A:65 X:FF Y:E2 P:64 SP:F9 CYC:214 SL:178 +FA70 F0 74 BEQ $FAE6 A:65 X:FF Y:E2 P:64 SP:F9 CYC:220 SL:178 +FA72 30 72 BMI $FAE6 A:65 X:FF Y:E2 P:64 SP:F9 CYC:226 SL:178 +FA74 B0 70 BCS $FAE6 A:65 X:FF Y:E2 P:64 SP:F9 CYC:232 SL:178 +FA76 C9 65 CMP #$65 A:65 X:FF Y:E2 P:64 SP:F9 CYC:238 SL:178 +FA78 D0 6C BNE $FAE6 A:65 X:FF Y:E2 P:67 SP:F9 CYC:244 SL:178 +FA7A 60 RTS A:65 X:FF Y:E2 P:67 SP:F9 CYC:250 SL:178 +F213 A5 47 LDA $47 = 6F A:65 X:FF Y:E2 P:67 SP:FB CYC:268 SL:178 +F215 C9 6F CMP #$6F A:6F X:FF Y:E2 P:65 SP:FB CYC:277 SL:178 +F217 F0 02 BEQ $F21B A:6F X:FF Y:E2 P:67 SP:FB CYC:283 SL:178 +F21B A9 A5 LDA #$A5 A:6F X:FF Y:E2 P:67 SP:FB CYC:292 SL:178 +F21D 8D 47 06 STA $0647 = 6F A:A5 X:FF Y:E2 P:E5 SP:FB CYC:298 SL:178 +F220 A0 FF LDY #$FF A:A5 X:FF Y:E2 P:E5 SP:FB CYC:310 SL:178 +F222 20 53 FB JSR $FB53 A:A5 X:FF Y:FF P:E5 SP:FB CYC:316 SL:178 +FB53 24 01 BIT $01 = FF A:A5 X:FF Y:FF P:E5 SP:F9 CYC:334 SL:178 +FB55 18 CLC A:A5 X:FF Y:FF P:E5 SP:F9 CYC: 2 SL:179 +FB56 A9 B3 LDA #$B3 A:A5 X:FF Y:FF P:E4 SP:F9 CYC: 8 SL:179 +FB58 60 RTS A:B3 X:FF Y:FF P:E4 SP:F9 CYC: 14 SL:179 +F225 3B 48 05 *RLA $0548,Y @ 0647 = A5 A:B3 X:FF Y:FF P:E4 SP:FB CYC: 32 SL:179 +F228 EA NOP A:02 X:FF Y:FF P:65 SP:FB CYC: 53 SL:179 +F229 EA NOP A:02 X:FF Y:FF P:65 SP:FB CYC: 59 SL:179 +F22A 08 PHP A:02 X:FF Y:FF P:65 SP:FB CYC: 65 SL:179 +F22B 48 PHA A:02 X:FF Y:FF P:65 SP:FA CYC: 74 SL:179 +F22C A0 E3 LDY #$E3 A:02 X:FF Y:FF P:65 SP:F9 CYC: 83 SL:179 +F22E 68 PLA A:02 X:FF Y:E3 P:E5 SP:F9 CYC: 89 SL:179 +F22F 28 PLP A:02 X:FF Y:E3 P:65 SP:FA CYC:101 SL:179 +F230 20 59 FB JSR $FB59 A:02 X:FF Y:E3 P:65 SP:FB CYC:113 SL:179 +FB59 50 1A BVC $FB75 A:02 X:FF Y:E3 P:65 SP:F9 CYC:131 SL:179 +FB5B 90 18 BCC $FB75 A:02 X:FF Y:E3 P:65 SP:F9 CYC:137 SL:179 +FB5D 30 16 BMI $FB75 A:02 X:FF Y:E3 P:65 SP:F9 CYC:143 SL:179 +FB5F C9 02 CMP #$02 A:02 X:FF Y:E3 P:65 SP:F9 CYC:149 SL:179 +FB61 D0 12 BNE $FB75 A:02 X:FF Y:E3 P:67 SP:F9 CYC:155 SL:179 +FB63 60 RTS A:02 X:FF Y:E3 P:67 SP:F9 CYC:161 SL:179 +F233 AD 47 06 LDA $0647 = 4A A:02 X:FF Y:E3 P:67 SP:FB CYC:179 SL:179 +F236 C9 4A CMP #$4A A:4A X:FF Y:E3 P:65 SP:FB CYC:191 SL:179 +F238 F0 02 BEQ $F23C A:4A X:FF Y:E3 P:67 SP:FB CYC:197 SL:179 +F23C A0 FF LDY #$FF A:4A X:FF Y:E3 P:67 SP:FB CYC:206 SL:179 +F23E A9 29 LDA #$29 A:4A X:FF Y:FF P:E5 SP:FB CYC:212 SL:179 +F240 8D 47 06 STA $0647 = 4A A:29 X:FF Y:FF P:65 SP:FB CYC:218 SL:179 +F243 20 64 FB JSR $FB64 A:29 X:FF Y:FF P:65 SP:FB CYC:230 SL:179 +FB64 B8 CLV A:29 X:FF Y:FF P:65 SP:F9 CYC:248 SL:179 +FB65 18 CLC A:29 X:FF Y:FF P:25 SP:F9 CYC:254 SL:179 +FB66 A9 42 LDA #$42 A:29 X:FF Y:FF P:24 SP:F9 CYC:260 SL:179 +FB68 60 RTS A:42 X:FF Y:FF P:24 SP:F9 CYC:266 SL:179 +F246 3B 48 05 *RLA $0548,Y @ 0647 = 29 A:42 X:FF Y:FF P:24 SP:FB CYC:284 SL:179 +F249 EA NOP A:42 X:FF Y:FF P:24 SP:FB CYC:305 SL:179 +F24A EA NOP A:42 X:FF Y:FF P:24 SP:FB CYC:311 SL:179 +F24B 08 PHP A:42 X:FF Y:FF P:24 SP:FB CYC:317 SL:179 +F24C 48 PHA A:42 X:FF Y:FF P:24 SP:FA CYC:326 SL:179 +F24D A0 E4 LDY #$E4 A:42 X:FF Y:FF P:24 SP:F9 CYC:335 SL:179 +F24F 68 PLA A:42 X:FF Y:E4 P:A4 SP:F9 CYC: 0 SL:180 +F250 28 PLP A:42 X:FF Y:E4 P:24 SP:FA CYC: 12 SL:180 +F251 20 69 FB JSR $FB69 A:42 X:FF Y:E4 P:24 SP:FB CYC: 24 SL:180 +FB69 70 0A BVS $FB75 A:42 X:FF Y:E4 P:24 SP:F9 CYC: 42 SL:180 +FB6B F0 08 BEQ $FB75 A:42 X:FF Y:E4 P:24 SP:F9 CYC: 48 SL:180 +FB6D 30 06 BMI $FB75 A:42 X:FF Y:E4 P:24 SP:F9 CYC: 54 SL:180 +FB6F B0 04 BCS $FB75 A:42 X:FF Y:E4 P:24 SP:F9 CYC: 60 SL:180 +FB71 C9 42 CMP #$42 A:42 X:FF Y:E4 P:24 SP:F9 CYC: 66 SL:180 +FB73 F0 02 BEQ $FB77 A:42 X:FF Y:E4 P:27 SP:F9 CYC: 72 SL:180 +FB77 60 RTS A:42 X:FF Y:E4 P:27 SP:F9 CYC: 81 SL:180 +F254 AD 47 06 LDA $0647 = 52 A:42 X:FF Y:E4 P:27 SP:FB CYC: 99 SL:180 +F257 C9 52 CMP #$52 A:52 X:FF Y:E4 P:25 SP:FB CYC:111 SL:180 +F259 F0 02 BEQ $F25D A:52 X:FF Y:E4 P:27 SP:FB CYC:117 SL:180 +F25D A0 FF LDY #$FF A:52 X:FF Y:E4 P:27 SP:FB CYC:126 SL:180 +F25F A9 37 LDA #$37 A:52 X:FF Y:FF P:A5 SP:FB CYC:132 SL:180 +F261 8D 47 06 STA $0647 = 52 A:37 X:FF Y:FF P:25 SP:FB CYC:138 SL:180 +F264 20 68 FA JSR $FA68 A:37 X:FF Y:FF P:25 SP:FB CYC:150 SL:180 +FA68 24 01 BIT $01 = FF A:37 X:FF Y:FF P:25 SP:F9 CYC:168 SL:180 +FA6A 38 SEC A:37 X:FF Y:FF P:E5 SP:F9 CYC:177 SL:180 +FA6B A9 75 LDA #$75 A:37 X:FF Y:FF P:E5 SP:F9 CYC:183 SL:180 +FA6D 60 RTS A:75 X:FF Y:FF P:65 SP:F9 CYC:189 SL:180 +F267 3B 48 05 *RLA $0548,Y @ 0647 = 37 A:75 X:FF Y:FF P:65 SP:FB CYC:207 SL:180 +F26A EA NOP A:65 X:FF Y:FF P:64 SP:FB CYC:228 SL:180 +F26B EA NOP A:65 X:FF Y:FF P:64 SP:FB CYC:234 SL:180 +F26C 08 PHP A:65 X:FF Y:FF P:64 SP:FB CYC:240 SL:180 +F26D 48 PHA A:65 X:FF Y:FF P:64 SP:FA CYC:249 SL:180 +F26E A0 E5 LDY #$E5 A:65 X:FF Y:FF P:64 SP:F9 CYC:258 SL:180 +F270 68 PLA A:65 X:FF Y:E5 P:E4 SP:F9 CYC:264 SL:180 +F271 28 PLP A:65 X:FF Y:E5 P:64 SP:FA CYC:276 SL:180 +F272 20 6E FA JSR $FA6E A:65 X:FF Y:E5 P:64 SP:FB CYC:288 SL:180 +FA6E 50 76 BVC $FAE6 A:65 X:FF Y:E5 P:64 SP:F9 CYC:306 SL:180 +FA70 F0 74 BEQ $FAE6 A:65 X:FF Y:E5 P:64 SP:F9 CYC:312 SL:180 +FA72 30 72 BMI $FAE6 A:65 X:FF Y:E5 P:64 SP:F9 CYC:318 SL:180 +FA74 B0 70 BCS $FAE6 A:65 X:FF Y:E5 P:64 SP:F9 CYC:324 SL:180 +FA76 C9 65 CMP #$65 A:65 X:FF Y:E5 P:64 SP:F9 CYC:330 SL:180 +FA78 D0 6C BNE $FAE6 A:65 X:FF Y:E5 P:67 SP:F9 CYC:336 SL:180 +FA7A 60 RTS A:65 X:FF Y:E5 P:67 SP:F9 CYC: 1 SL:181 +F275 AD 47 06 LDA $0647 = 6F A:65 X:FF Y:E5 P:67 SP:FB CYC: 19 SL:181 +F278 C9 6F CMP #$6F A:6F X:FF Y:E5 P:65 SP:FB CYC: 31 SL:181 +F27A F0 02 BEQ $F27E A:6F X:FF Y:E5 P:67 SP:FB CYC: 37 SL:181 +F27E A0 E6 LDY #$E6 A:6F X:FF Y:E5 P:67 SP:FB CYC: 46 SL:181 +F280 A2 FF LDX #$FF A:6F X:FF Y:E6 P:E5 SP:FB CYC: 52 SL:181 +F282 A9 A5 LDA #$A5 A:6F X:FF Y:E6 P:E5 SP:FB CYC: 58 SL:181 +F284 8D 47 06 STA $0647 = 6F A:A5 X:FF Y:E6 P:E5 SP:FB CYC: 64 SL:181 +F287 20 53 FB JSR $FB53 A:A5 X:FF Y:E6 P:E5 SP:FB CYC: 76 SL:181 +FB53 24 01 BIT $01 = FF A:A5 X:FF Y:E6 P:E5 SP:F9 CYC: 94 SL:181 +FB55 18 CLC A:A5 X:FF Y:E6 P:E5 SP:F9 CYC:103 SL:181 +FB56 A9 B3 LDA #$B3 A:A5 X:FF Y:E6 P:E4 SP:F9 CYC:109 SL:181 +FB58 60 RTS A:B3 X:FF Y:E6 P:E4 SP:F9 CYC:115 SL:181 +F28A 3F 48 05 *RLA $0548,X @ 0647 = A5 A:B3 X:FF Y:E6 P:E4 SP:FB CYC:133 SL:181 +F28D EA NOP A:02 X:FF Y:E6 P:65 SP:FB CYC:154 SL:181 +F28E EA NOP A:02 X:FF Y:E6 P:65 SP:FB CYC:160 SL:181 +F28F EA NOP A:02 X:FF Y:E6 P:65 SP:FB CYC:166 SL:181 +F290 EA NOP A:02 X:FF Y:E6 P:65 SP:FB CYC:172 SL:181 +F291 20 59 FB JSR $FB59 A:02 X:FF Y:E6 P:65 SP:FB CYC:178 SL:181 +FB59 50 1A BVC $FB75 A:02 X:FF Y:E6 P:65 SP:F9 CYC:196 SL:181 +FB5B 90 18 BCC $FB75 A:02 X:FF Y:E6 P:65 SP:F9 CYC:202 SL:181 +FB5D 30 16 BMI $FB75 A:02 X:FF Y:E6 P:65 SP:F9 CYC:208 SL:181 +FB5F C9 02 CMP #$02 A:02 X:FF Y:E6 P:65 SP:F9 CYC:214 SL:181 +FB61 D0 12 BNE $FB75 A:02 X:FF Y:E6 P:67 SP:F9 CYC:220 SL:181 +FB63 60 RTS A:02 X:FF Y:E6 P:67 SP:F9 CYC:226 SL:181 +F294 AD 47 06 LDA $0647 = 4A A:02 X:FF Y:E6 P:67 SP:FB CYC:244 SL:181 +F297 C9 4A CMP #$4A A:4A X:FF Y:E6 P:65 SP:FB CYC:256 SL:181 +F299 F0 02 BEQ $F29D A:4A X:FF Y:E6 P:67 SP:FB CYC:262 SL:181 +F29D C8 INY A:4A X:FF Y:E6 P:67 SP:FB CYC:271 SL:181 +F29E A9 29 LDA #$29 A:4A X:FF Y:E7 P:E5 SP:FB CYC:277 SL:181 +F2A0 8D 47 06 STA $0647 = 4A A:29 X:FF Y:E7 P:65 SP:FB CYC:283 SL:181 +F2A3 20 64 FB JSR $FB64 A:29 X:FF Y:E7 P:65 SP:FB CYC:295 SL:181 +FB64 B8 CLV A:29 X:FF Y:E7 P:65 SP:F9 CYC:313 SL:181 +FB65 18 CLC A:29 X:FF Y:E7 P:25 SP:F9 CYC:319 SL:181 +FB66 A9 42 LDA #$42 A:29 X:FF Y:E7 P:24 SP:F9 CYC:325 SL:181 +FB68 60 RTS A:42 X:FF Y:E7 P:24 SP:F9 CYC:331 SL:181 +F2A6 3F 48 05 *RLA $0548,X @ 0647 = 29 A:42 X:FF Y:E7 P:24 SP:FB CYC: 8 SL:182 +F2A9 EA NOP A:42 X:FF Y:E7 P:24 SP:FB CYC: 29 SL:182 +F2AA EA NOP A:42 X:FF Y:E7 P:24 SP:FB CYC: 35 SL:182 +F2AB EA NOP A:42 X:FF Y:E7 P:24 SP:FB CYC: 41 SL:182 +F2AC EA NOP A:42 X:FF Y:E7 P:24 SP:FB CYC: 47 SL:182 +F2AD 20 69 FB JSR $FB69 A:42 X:FF Y:E7 P:24 SP:FB CYC: 53 SL:182 +FB69 70 0A BVS $FB75 A:42 X:FF Y:E7 P:24 SP:F9 CYC: 71 SL:182 +FB6B F0 08 BEQ $FB75 A:42 X:FF Y:E7 P:24 SP:F9 CYC: 77 SL:182 +FB6D 30 06 BMI $FB75 A:42 X:FF Y:E7 P:24 SP:F9 CYC: 83 SL:182 +FB6F B0 04 BCS $FB75 A:42 X:FF Y:E7 P:24 SP:F9 CYC: 89 SL:182 +FB71 C9 42 CMP #$42 A:42 X:FF Y:E7 P:24 SP:F9 CYC: 95 SL:182 +FB73 F0 02 BEQ $FB77 A:42 X:FF Y:E7 P:27 SP:F9 CYC:101 SL:182 +FB77 60 RTS A:42 X:FF Y:E7 P:27 SP:F9 CYC:110 SL:182 +F2B0 AD 47 06 LDA $0647 = 52 A:42 X:FF Y:E7 P:27 SP:FB CYC:128 SL:182 +F2B3 C9 52 CMP #$52 A:52 X:FF Y:E7 P:25 SP:FB CYC:140 SL:182 +F2B5 F0 02 BEQ $F2B9 A:52 X:FF Y:E7 P:27 SP:FB CYC:146 SL:182 +F2B9 C8 INY A:52 X:FF Y:E7 P:27 SP:FB CYC:155 SL:182 +F2BA A9 37 LDA #$37 A:52 X:FF Y:E8 P:A5 SP:FB CYC:161 SL:182 +F2BC 8D 47 06 STA $0647 = 52 A:37 X:FF Y:E8 P:25 SP:FB CYC:167 SL:182 +F2BF 20 68 FA JSR $FA68 A:37 X:FF Y:E8 P:25 SP:FB CYC:179 SL:182 +FA68 24 01 BIT $01 = FF A:37 X:FF Y:E8 P:25 SP:F9 CYC:197 SL:182 +FA6A 38 SEC A:37 X:FF Y:E8 P:E5 SP:F9 CYC:206 SL:182 +FA6B A9 75 LDA #$75 A:37 X:FF Y:E8 P:E5 SP:F9 CYC:212 SL:182 +FA6D 60 RTS A:75 X:FF Y:E8 P:65 SP:F9 CYC:218 SL:182 +F2C2 3F 48 05 *RLA $0548,X @ 0647 = 37 A:75 X:FF Y:E8 P:65 SP:FB CYC:236 SL:182 +F2C5 EA NOP A:65 X:FF Y:E8 P:64 SP:FB CYC:257 SL:182 +F2C6 EA NOP A:65 X:FF Y:E8 P:64 SP:FB CYC:263 SL:182 +F2C7 EA NOP A:65 X:FF Y:E8 P:64 SP:FB CYC:269 SL:182 +F2C8 EA NOP A:65 X:FF Y:E8 P:64 SP:FB CYC:275 SL:182 +F2C9 20 6E FA JSR $FA6E A:65 X:FF Y:E8 P:64 SP:FB CYC:281 SL:182 +FA6E 50 76 BVC $FAE6 A:65 X:FF Y:E8 P:64 SP:F9 CYC:299 SL:182 +FA70 F0 74 BEQ $FAE6 A:65 X:FF Y:E8 P:64 SP:F9 CYC:305 SL:182 +FA72 30 72 BMI $FAE6 A:65 X:FF Y:E8 P:64 SP:F9 CYC:311 SL:182 +FA74 B0 70 BCS $FAE6 A:65 X:FF Y:E8 P:64 SP:F9 CYC:317 SL:182 +FA76 C9 65 CMP #$65 A:65 X:FF Y:E8 P:64 SP:F9 CYC:323 SL:182 +FA78 D0 6C BNE $FAE6 A:65 X:FF Y:E8 P:67 SP:F9 CYC:329 SL:182 +FA7A 60 RTS A:65 X:FF Y:E8 P:67 SP:F9 CYC:335 SL:182 +F2CC AD 47 06 LDA $0647 = 6F A:65 X:FF Y:E8 P:67 SP:FB CYC: 12 SL:183 +F2CF C9 6F CMP #$6F A:6F X:FF Y:E8 P:65 SP:FB CYC: 24 SL:183 +F2D1 F0 02 BEQ $F2D5 A:6F X:FF Y:E8 P:67 SP:FB CYC: 30 SL:183 +F2D5 60 RTS A:6F X:FF Y:E8 P:67 SP:FB CYC: 39 SL:183 +C647 20 D6 F2 JSR $F2D6 A:6F X:FF Y:E8 P:67 SP:FD CYC: 57 SL:183 +F2D6 A9 FF LDA #$FF A:6F X:FF Y:E8 P:67 SP:FB CYC: 75 SL:183 +F2D8 85 01 STA $01 = FF A:FF X:FF Y:E8 P:E5 SP:FB CYC: 81 SL:183 +F2DA A0 E9 LDY #$E9 A:FF X:FF Y:E8 P:E5 SP:FB CYC: 90 SL:183 +F2DC A2 02 LDX #$02 A:FF X:FF Y:E9 P:E5 SP:FB CYC: 96 SL:183 +F2DE A9 47 LDA #$47 A:FF X:02 Y:E9 P:65 SP:FB CYC:102 SL:183 +F2E0 85 47 STA $47 = 6F A:47 X:02 Y:E9 P:65 SP:FB CYC:108 SL:183 +F2E2 A9 06 LDA #$06 A:47 X:02 Y:E9 P:65 SP:FB CYC:117 SL:183 +F2E4 85 48 STA $48 = 06 A:06 X:02 Y:E9 P:65 SP:FB CYC:123 SL:183 +F2E6 A9 A5 LDA #$A5 A:06 X:02 Y:E9 P:65 SP:FB CYC:132 SL:183 +F2E8 8D 47 06 STA $0647 = 6F A:A5 X:02 Y:E9 P:E5 SP:FB CYC:138 SL:183 +F2EB 20 1D FB JSR $FB1D A:A5 X:02 Y:E9 P:E5 SP:FB CYC:150 SL:183 +FB1D 24 01 BIT $01 = FF A:A5 X:02 Y:E9 P:E5 SP:F9 CYC:168 SL:183 +FB1F 18 CLC A:A5 X:02 Y:E9 P:E5 SP:F9 CYC:177 SL:183 +FB20 A9 B3 LDA #$B3 A:A5 X:02 Y:E9 P:E4 SP:F9 CYC:183 SL:183 +FB22 60 RTS A:B3 X:02 Y:E9 P:E4 SP:F9 CYC:189 SL:183 +F2EE 43 45 *SRE ($45,X) @ 47 = 0647 = A5 A:B3 X:02 Y:E9 P:E4 SP:FB CYC:207 SL:183 +F2F0 EA NOP A:E1 X:02 Y:E9 P:E5 SP:FB CYC:231 SL:183 +F2F1 EA NOP A:E1 X:02 Y:E9 P:E5 SP:FB CYC:237 SL:183 +F2F2 EA NOP A:E1 X:02 Y:E9 P:E5 SP:FB CYC:243 SL:183 +F2F3 EA NOP A:E1 X:02 Y:E9 P:E5 SP:FB CYC:249 SL:183 +F2F4 20 23 FB JSR $FB23 A:E1 X:02 Y:E9 P:E5 SP:FB CYC:255 SL:183 +FB23 50 50 BVC $FB75 A:E1 X:02 Y:E9 P:E5 SP:F9 CYC:273 SL:183 +FB25 90 4E BCC $FB75 A:E1 X:02 Y:E9 P:E5 SP:F9 CYC:279 SL:183 +FB27 10 4C BPL $FB75 A:E1 X:02 Y:E9 P:E5 SP:F9 CYC:285 SL:183 +FB29 C9 E1 CMP #$E1 A:E1 X:02 Y:E9 P:E5 SP:F9 CYC:291 SL:183 +FB2B D0 48 BNE $FB75 A:E1 X:02 Y:E9 P:67 SP:F9 CYC:297 SL:183 +FB2D 60 RTS A:E1 X:02 Y:E9 P:67 SP:F9 CYC:303 SL:183 +F2F7 AD 47 06 LDA $0647 = 52 A:E1 X:02 Y:E9 P:67 SP:FB CYC:321 SL:183 +F2FA C9 52 CMP #$52 A:52 X:02 Y:E9 P:65 SP:FB CYC:333 SL:183 +F2FC F0 02 BEQ $F300 A:52 X:02 Y:E9 P:67 SP:FB CYC:339 SL:183 +F300 C8 INY A:52 X:02 Y:E9 P:67 SP:FB CYC: 10 SL:184 +F301 A9 29 LDA #$29 A:52 X:02 Y:EA P:E5 SP:FB CYC: 16 SL:184 +F303 8D 47 06 STA $0647 = 52 A:29 X:02 Y:EA P:65 SP:FB CYC: 22 SL:184 +F306 20 2E FB JSR $FB2E A:29 X:02 Y:EA P:65 SP:FB CYC: 34 SL:184 +FB2E B8 CLV A:29 X:02 Y:EA P:65 SP:F9 CYC: 52 SL:184 +FB2F 18 CLC A:29 X:02 Y:EA P:25 SP:F9 CYC: 58 SL:184 +FB30 A9 42 LDA #$42 A:29 X:02 Y:EA P:24 SP:F9 CYC: 64 SL:184 +FB32 60 RTS A:42 X:02 Y:EA P:24 SP:F9 CYC: 70 SL:184 +F309 43 45 *SRE ($45,X) @ 47 = 0647 = 29 A:42 X:02 Y:EA P:24 SP:FB CYC: 88 SL:184 +F30B EA NOP A:56 X:02 Y:EA P:25 SP:FB CYC:112 SL:184 +F30C EA NOP A:56 X:02 Y:EA P:25 SP:FB CYC:118 SL:184 +F30D EA NOP A:56 X:02 Y:EA P:25 SP:FB CYC:124 SL:184 +F30E EA NOP A:56 X:02 Y:EA P:25 SP:FB CYC:130 SL:184 +F30F 20 33 FB JSR $FB33 A:56 X:02 Y:EA P:25 SP:FB CYC:136 SL:184 +FB33 70 40 BVS $FB75 A:56 X:02 Y:EA P:25 SP:F9 CYC:154 SL:184 +FB35 F0 3E BEQ $FB75 A:56 X:02 Y:EA P:25 SP:F9 CYC:160 SL:184 +FB37 30 3C BMI $FB75 A:56 X:02 Y:EA P:25 SP:F9 CYC:166 SL:184 +FB39 90 3A BCC $FB75 A:56 X:02 Y:EA P:25 SP:F9 CYC:172 SL:184 +FB3B C9 56 CMP #$56 A:56 X:02 Y:EA P:25 SP:F9 CYC:178 SL:184 +FB3D D0 36 BNE $FB75 A:56 X:02 Y:EA P:27 SP:F9 CYC:184 SL:184 +FB3F 60 RTS A:56 X:02 Y:EA P:27 SP:F9 CYC:190 SL:184 +F312 AD 47 06 LDA $0647 = 14 A:56 X:02 Y:EA P:27 SP:FB CYC:208 SL:184 +F315 C9 14 CMP #$14 A:14 X:02 Y:EA P:25 SP:FB CYC:220 SL:184 +F317 F0 02 BEQ $F31B A:14 X:02 Y:EA P:27 SP:FB CYC:226 SL:184 +F31B C8 INY A:14 X:02 Y:EA P:27 SP:FB CYC:235 SL:184 +F31C A9 37 LDA #$37 A:14 X:02 Y:EB P:A5 SP:FB CYC:241 SL:184 +F31E 8D 47 06 STA $0647 = 14 A:37 X:02 Y:EB P:25 SP:FB CYC:247 SL:184 +F321 20 40 FB JSR $FB40 A:37 X:02 Y:EB P:25 SP:FB CYC:259 SL:184 +FB40 24 01 BIT $01 = FF A:37 X:02 Y:EB P:25 SP:F9 CYC:277 SL:184 +FB42 38 SEC A:37 X:02 Y:EB P:E5 SP:F9 CYC:286 SL:184 +FB43 A9 75 LDA #$75 A:37 X:02 Y:EB P:E5 SP:F9 CYC:292 SL:184 +FB45 60 RTS A:75 X:02 Y:EB P:65 SP:F9 CYC:298 SL:184 +F324 43 45 *SRE ($45,X) @ 47 = 0647 = 37 A:75 X:02 Y:EB P:65 SP:FB CYC:316 SL:184 +F326 EA NOP A:6E X:02 Y:EB P:65 SP:FB CYC:340 SL:184 +F327 EA NOP A:6E X:02 Y:EB P:65 SP:FB CYC: 5 SL:185 +F328 EA NOP A:6E X:02 Y:EB P:65 SP:FB CYC: 11 SL:185 +F329 EA NOP A:6E X:02 Y:EB P:65 SP:FB CYC: 17 SL:185 +F32A 20 46 FB JSR $FB46 A:6E X:02 Y:EB P:65 SP:FB CYC: 23 SL:185 +FB46 50 2D BVC $FB75 A:6E X:02 Y:EB P:65 SP:F9 CYC: 41 SL:185 +FB48 F0 2B BEQ $FB75 A:6E X:02 Y:EB P:65 SP:F9 CYC: 47 SL:185 +FB4A 30 29 BMI $FB75 A:6E X:02 Y:EB P:65 SP:F9 CYC: 53 SL:185 +FB4C 90 27 BCC $FB75 A:6E X:02 Y:EB P:65 SP:F9 CYC: 59 SL:185 +FB4E C9 6E CMP #$6E A:6E X:02 Y:EB P:65 SP:F9 CYC: 65 SL:185 +FB50 D0 23 BNE $FB75 A:6E X:02 Y:EB P:67 SP:F9 CYC: 71 SL:185 +FB52 60 RTS A:6E X:02 Y:EB P:67 SP:F9 CYC: 77 SL:185 +F32D AD 47 06 LDA $0647 = 1B A:6E X:02 Y:EB P:67 SP:FB CYC: 95 SL:185 +F330 C9 1B CMP #$1B A:1B X:02 Y:EB P:65 SP:FB CYC:107 SL:185 +F332 F0 02 BEQ $F336 A:1B X:02 Y:EB P:67 SP:FB CYC:113 SL:185 +F336 C8 INY A:1B X:02 Y:EB P:67 SP:FB CYC:122 SL:185 +F337 A9 A5 LDA #$A5 A:1B X:02 Y:EC P:E5 SP:FB CYC:128 SL:185 +F339 85 47 STA $47 = 47 A:A5 X:02 Y:EC P:E5 SP:FB CYC:134 SL:185 +F33B 20 1D FB JSR $FB1D A:A5 X:02 Y:EC P:E5 SP:FB CYC:143 SL:185 +FB1D 24 01 BIT $01 = FF A:A5 X:02 Y:EC P:E5 SP:F9 CYC:161 SL:185 +FB1F 18 CLC A:A5 X:02 Y:EC P:E5 SP:F9 CYC:170 SL:185 +FB20 A9 B3 LDA #$B3 A:A5 X:02 Y:EC P:E4 SP:F9 CYC:176 SL:185 +FB22 60 RTS A:B3 X:02 Y:EC P:E4 SP:F9 CYC:182 SL:185 +F33E 47 47 *SRE $47 = A5 A:B3 X:02 Y:EC P:E4 SP:FB CYC:200 SL:185 +F340 EA NOP A:E1 X:02 Y:EC P:E5 SP:FB CYC:215 SL:185 +F341 EA NOP A:E1 X:02 Y:EC P:E5 SP:FB CYC:221 SL:185 +F342 EA NOP A:E1 X:02 Y:EC P:E5 SP:FB CYC:227 SL:185 +F343 EA NOP A:E1 X:02 Y:EC P:E5 SP:FB CYC:233 SL:185 +F344 20 23 FB JSR $FB23 A:E1 X:02 Y:EC P:E5 SP:FB CYC:239 SL:185 +FB23 50 50 BVC $FB75 A:E1 X:02 Y:EC P:E5 SP:F9 CYC:257 SL:185 +FB25 90 4E BCC $FB75 A:E1 X:02 Y:EC P:E5 SP:F9 CYC:263 SL:185 +FB27 10 4C BPL $FB75 A:E1 X:02 Y:EC P:E5 SP:F9 CYC:269 SL:185 +FB29 C9 E1 CMP #$E1 A:E1 X:02 Y:EC P:E5 SP:F9 CYC:275 SL:185 +FB2B D0 48 BNE $FB75 A:E1 X:02 Y:EC P:67 SP:F9 CYC:281 SL:185 +FB2D 60 RTS A:E1 X:02 Y:EC P:67 SP:F9 CYC:287 SL:185 +F347 A5 47 LDA $47 = 52 A:E1 X:02 Y:EC P:67 SP:FB CYC:305 SL:185 +F349 C9 52 CMP #$52 A:52 X:02 Y:EC P:65 SP:FB CYC:314 SL:185 +F34B F0 02 BEQ $F34F A:52 X:02 Y:EC P:67 SP:FB CYC:320 SL:185 +F34F C8 INY A:52 X:02 Y:EC P:67 SP:FB CYC:329 SL:185 +F350 A9 29 LDA #$29 A:52 X:02 Y:ED P:E5 SP:FB CYC:335 SL:185 +F352 85 47 STA $47 = 52 A:29 X:02 Y:ED P:65 SP:FB CYC: 0 SL:186 +F354 20 2E FB JSR $FB2E A:29 X:02 Y:ED P:65 SP:FB CYC: 9 SL:186 +FB2E B8 CLV A:29 X:02 Y:ED P:65 SP:F9 CYC: 27 SL:186 +FB2F 18 CLC A:29 X:02 Y:ED P:25 SP:F9 CYC: 33 SL:186 +FB30 A9 42 LDA #$42 A:29 X:02 Y:ED P:24 SP:F9 CYC: 39 SL:186 +FB32 60 RTS A:42 X:02 Y:ED P:24 SP:F9 CYC: 45 SL:186 +F357 47 47 *SRE $47 = 29 A:42 X:02 Y:ED P:24 SP:FB CYC: 63 SL:186 +F359 EA NOP A:56 X:02 Y:ED P:25 SP:FB CYC: 78 SL:186 +F35A EA NOP A:56 X:02 Y:ED P:25 SP:FB CYC: 84 SL:186 +F35B EA NOP A:56 X:02 Y:ED P:25 SP:FB CYC: 90 SL:186 +F35C EA NOP A:56 X:02 Y:ED P:25 SP:FB CYC: 96 SL:186 +F35D 20 33 FB JSR $FB33 A:56 X:02 Y:ED P:25 SP:FB CYC:102 SL:186 +FB33 70 40 BVS $FB75 A:56 X:02 Y:ED P:25 SP:F9 CYC:120 SL:186 +FB35 F0 3E BEQ $FB75 A:56 X:02 Y:ED P:25 SP:F9 CYC:126 SL:186 +FB37 30 3C BMI $FB75 A:56 X:02 Y:ED P:25 SP:F9 CYC:132 SL:186 +FB39 90 3A BCC $FB75 A:56 X:02 Y:ED P:25 SP:F9 CYC:138 SL:186 +FB3B C9 56 CMP #$56 A:56 X:02 Y:ED P:25 SP:F9 CYC:144 SL:186 +FB3D D0 36 BNE $FB75 A:56 X:02 Y:ED P:27 SP:F9 CYC:150 SL:186 +FB3F 60 RTS A:56 X:02 Y:ED P:27 SP:F9 CYC:156 SL:186 +F360 A5 47 LDA $47 = 14 A:56 X:02 Y:ED P:27 SP:FB CYC:174 SL:186 +F362 C9 14 CMP #$14 A:14 X:02 Y:ED P:25 SP:FB CYC:183 SL:186 +F364 F0 02 BEQ $F368 A:14 X:02 Y:ED P:27 SP:FB CYC:189 SL:186 +F368 C8 INY A:14 X:02 Y:ED P:27 SP:FB CYC:198 SL:186 +F369 A9 37 LDA #$37 A:14 X:02 Y:EE P:A5 SP:FB CYC:204 SL:186 +F36B 85 47 STA $47 = 14 A:37 X:02 Y:EE P:25 SP:FB CYC:210 SL:186 +F36D 20 40 FB JSR $FB40 A:37 X:02 Y:EE P:25 SP:FB CYC:219 SL:186 +FB40 24 01 BIT $01 = FF A:37 X:02 Y:EE P:25 SP:F9 CYC:237 SL:186 +FB42 38 SEC A:37 X:02 Y:EE P:E5 SP:F9 CYC:246 SL:186 +FB43 A9 75 LDA #$75 A:37 X:02 Y:EE P:E5 SP:F9 CYC:252 SL:186 +FB45 60 RTS A:75 X:02 Y:EE P:65 SP:F9 CYC:258 SL:186 +F370 47 47 *SRE $47 = 37 A:75 X:02 Y:EE P:65 SP:FB CYC:276 SL:186 +F372 EA NOP A:6E X:02 Y:EE P:65 SP:FB CYC:291 SL:186 +F373 EA NOP A:6E X:02 Y:EE P:65 SP:FB CYC:297 SL:186 +F374 EA NOP A:6E X:02 Y:EE P:65 SP:FB CYC:303 SL:186 +F375 EA NOP A:6E X:02 Y:EE P:65 SP:FB CYC:309 SL:186 +F376 20 46 FB JSR $FB46 A:6E X:02 Y:EE P:65 SP:FB CYC:315 SL:186 +FB46 50 2D BVC $FB75 A:6E X:02 Y:EE P:65 SP:F9 CYC:333 SL:186 +FB48 F0 2B BEQ $FB75 A:6E X:02 Y:EE P:65 SP:F9 CYC:339 SL:186 +FB4A 30 29 BMI $FB75 A:6E X:02 Y:EE P:65 SP:F9 CYC: 4 SL:187 +FB4C 90 27 BCC $FB75 A:6E X:02 Y:EE P:65 SP:F9 CYC: 10 SL:187 +FB4E C9 6E CMP #$6E A:6E X:02 Y:EE P:65 SP:F9 CYC: 16 SL:187 +FB50 D0 23 BNE $FB75 A:6E X:02 Y:EE P:67 SP:F9 CYC: 22 SL:187 +FB52 60 RTS A:6E X:02 Y:EE P:67 SP:F9 CYC: 28 SL:187 +F379 A5 47 LDA $47 = 1B A:6E X:02 Y:EE P:67 SP:FB CYC: 46 SL:187 +F37B C9 1B CMP #$1B A:1B X:02 Y:EE P:65 SP:FB CYC: 55 SL:187 +F37D F0 02 BEQ $F381 A:1B X:02 Y:EE P:67 SP:FB CYC: 61 SL:187 +F381 C8 INY A:1B X:02 Y:EE P:67 SP:FB CYC: 70 SL:187 +F382 A9 A5 LDA #$A5 A:1B X:02 Y:EF P:E5 SP:FB CYC: 76 SL:187 +F384 8D 47 06 STA $0647 = 1B A:A5 X:02 Y:EF P:E5 SP:FB CYC: 82 SL:187 +F387 20 1D FB JSR $FB1D A:A5 X:02 Y:EF P:E5 SP:FB CYC: 94 SL:187 +FB1D 24 01 BIT $01 = FF A:A5 X:02 Y:EF P:E5 SP:F9 CYC:112 SL:187 +FB1F 18 CLC A:A5 X:02 Y:EF P:E5 SP:F9 CYC:121 SL:187 +FB20 A9 B3 LDA #$B3 A:A5 X:02 Y:EF P:E4 SP:F9 CYC:127 SL:187 +FB22 60 RTS A:B3 X:02 Y:EF P:E4 SP:F9 CYC:133 SL:187 +F38A 4F 47 06 *SRE $0647 = A5 A:B3 X:02 Y:EF P:E4 SP:FB CYC:151 SL:187 +F38D EA NOP A:E1 X:02 Y:EF P:E5 SP:FB CYC:169 SL:187 +F38E EA NOP A:E1 X:02 Y:EF P:E5 SP:FB CYC:175 SL:187 +F38F EA NOP A:E1 X:02 Y:EF P:E5 SP:FB CYC:181 SL:187 +F390 EA NOP A:E1 X:02 Y:EF P:E5 SP:FB CYC:187 SL:187 +F391 20 23 FB JSR $FB23 A:E1 X:02 Y:EF P:E5 SP:FB CYC:193 SL:187 +FB23 50 50 BVC $FB75 A:E1 X:02 Y:EF P:E5 SP:F9 CYC:211 SL:187 +FB25 90 4E BCC $FB75 A:E1 X:02 Y:EF P:E5 SP:F9 CYC:217 SL:187 +FB27 10 4C BPL $FB75 A:E1 X:02 Y:EF P:E5 SP:F9 CYC:223 SL:187 +FB29 C9 E1 CMP #$E1 A:E1 X:02 Y:EF P:E5 SP:F9 CYC:229 SL:187 +FB2B D0 48 BNE $FB75 A:E1 X:02 Y:EF P:67 SP:F9 CYC:235 SL:187 +FB2D 60 RTS A:E1 X:02 Y:EF P:67 SP:F9 CYC:241 SL:187 +F394 AD 47 06 LDA $0647 = 52 A:E1 X:02 Y:EF P:67 SP:FB CYC:259 SL:187 +F397 C9 52 CMP #$52 A:52 X:02 Y:EF P:65 SP:FB CYC:271 SL:187 +F399 F0 02 BEQ $F39D A:52 X:02 Y:EF P:67 SP:FB CYC:277 SL:187 +F39D C8 INY A:52 X:02 Y:EF P:67 SP:FB CYC:286 SL:187 +F39E A9 29 LDA #$29 A:52 X:02 Y:F0 P:E5 SP:FB CYC:292 SL:187 +F3A0 8D 47 06 STA $0647 = 52 A:29 X:02 Y:F0 P:65 SP:FB CYC:298 SL:187 +F3A3 20 2E FB JSR $FB2E A:29 X:02 Y:F0 P:65 SP:FB CYC:310 SL:187 +FB2E B8 CLV A:29 X:02 Y:F0 P:65 SP:F9 CYC:328 SL:187 +FB2F 18 CLC A:29 X:02 Y:F0 P:25 SP:F9 CYC:334 SL:187 +FB30 A9 42 LDA #$42 A:29 X:02 Y:F0 P:24 SP:F9 CYC:340 SL:187 +FB32 60 RTS A:42 X:02 Y:F0 P:24 SP:F9 CYC: 5 SL:188 +F3A6 4F 47 06 *SRE $0647 = 29 A:42 X:02 Y:F0 P:24 SP:FB CYC: 23 SL:188 +F3A9 EA NOP A:56 X:02 Y:F0 P:25 SP:FB CYC: 41 SL:188 +F3AA EA NOP A:56 X:02 Y:F0 P:25 SP:FB CYC: 47 SL:188 +F3AB EA NOP A:56 X:02 Y:F0 P:25 SP:FB CYC: 53 SL:188 +F3AC EA NOP A:56 X:02 Y:F0 P:25 SP:FB CYC: 59 SL:188 +F3AD 20 33 FB JSR $FB33 A:56 X:02 Y:F0 P:25 SP:FB CYC: 65 SL:188 +FB33 70 40 BVS $FB75 A:56 X:02 Y:F0 P:25 SP:F9 CYC: 83 SL:188 +FB35 F0 3E BEQ $FB75 A:56 X:02 Y:F0 P:25 SP:F9 CYC: 89 SL:188 +FB37 30 3C BMI $FB75 A:56 X:02 Y:F0 P:25 SP:F9 CYC: 95 SL:188 +FB39 90 3A BCC $FB75 A:56 X:02 Y:F0 P:25 SP:F9 CYC:101 SL:188 +FB3B C9 56 CMP #$56 A:56 X:02 Y:F0 P:25 SP:F9 CYC:107 SL:188 +FB3D D0 36 BNE $FB75 A:56 X:02 Y:F0 P:27 SP:F9 CYC:113 SL:188 +FB3F 60 RTS A:56 X:02 Y:F0 P:27 SP:F9 CYC:119 SL:188 +F3B0 AD 47 06 LDA $0647 = 14 A:56 X:02 Y:F0 P:27 SP:FB CYC:137 SL:188 +F3B3 C9 14 CMP #$14 A:14 X:02 Y:F0 P:25 SP:FB CYC:149 SL:188 +F3B5 F0 02 BEQ $F3B9 A:14 X:02 Y:F0 P:27 SP:FB CYC:155 SL:188 +F3B9 C8 INY A:14 X:02 Y:F0 P:27 SP:FB CYC:164 SL:188 +F3BA A9 37 LDA #$37 A:14 X:02 Y:F1 P:A5 SP:FB CYC:170 SL:188 +F3BC 8D 47 06 STA $0647 = 14 A:37 X:02 Y:F1 P:25 SP:FB CYC:176 SL:188 +F3BF 20 40 FB JSR $FB40 A:37 X:02 Y:F1 P:25 SP:FB CYC:188 SL:188 +FB40 24 01 BIT $01 = FF A:37 X:02 Y:F1 P:25 SP:F9 CYC:206 SL:188 +FB42 38 SEC A:37 X:02 Y:F1 P:E5 SP:F9 CYC:215 SL:188 +FB43 A9 75 LDA #$75 A:37 X:02 Y:F1 P:E5 SP:F9 CYC:221 SL:188 +FB45 60 RTS A:75 X:02 Y:F1 P:65 SP:F9 CYC:227 SL:188 +F3C2 4F 47 06 *SRE $0647 = 37 A:75 X:02 Y:F1 P:65 SP:FB CYC:245 SL:188 +F3C5 EA NOP A:6E X:02 Y:F1 P:65 SP:FB CYC:263 SL:188 +F3C6 EA NOP A:6E X:02 Y:F1 P:65 SP:FB CYC:269 SL:188 +F3C7 EA NOP A:6E X:02 Y:F1 P:65 SP:FB CYC:275 SL:188 +F3C8 EA NOP A:6E X:02 Y:F1 P:65 SP:FB CYC:281 SL:188 +F3C9 20 46 FB JSR $FB46 A:6E X:02 Y:F1 P:65 SP:FB CYC:287 SL:188 +FB46 50 2D BVC $FB75 A:6E X:02 Y:F1 P:65 SP:F9 CYC:305 SL:188 +FB48 F0 2B BEQ $FB75 A:6E X:02 Y:F1 P:65 SP:F9 CYC:311 SL:188 +FB4A 30 29 BMI $FB75 A:6E X:02 Y:F1 P:65 SP:F9 CYC:317 SL:188 +FB4C 90 27 BCC $FB75 A:6E X:02 Y:F1 P:65 SP:F9 CYC:323 SL:188 +FB4E C9 6E CMP #$6E A:6E X:02 Y:F1 P:65 SP:F9 CYC:329 SL:188 +FB50 D0 23 BNE $FB75 A:6E X:02 Y:F1 P:67 SP:F9 CYC:335 SL:188 +FB52 60 RTS A:6E X:02 Y:F1 P:67 SP:F9 CYC: 0 SL:189 +F3CC AD 47 06 LDA $0647 = 1B A:6E X:02 Y:F1 P:67 SP:FB CYC: 18 SL:189 +F3CF C9 1B CMP #$1B A:1B X:02 Y:F1 P:65 SP:FB CYC: 30 SL:189 +F3D1 F0 02 BEQ $F3D5 A:1B X:02 Y:F1 P:67 SP:FB CYC: 36 SL:189 +F3D5 A9 A5 LDA #$A5 A:1B X:02 Y:F1 P:67 SP:FB CYC: 45 SL:189 +F3D7 8D 47 06 STA $0647 = 1B A:A5 X:02 Y:F1 P:E5 SP:FB CYC: 51 SL:189 +F3DA A9 48 LDA #$48 A:A5 X:02 Y:F1 P:E5 SP:FB CYC: 63 SL:189 +F3DC 85 45 STA $45 = 48 A:48 X:02 Y:F1 P:65 SP:FB CYC: 69 SL:189 +F3DE A9 05 LDA #$05 A:48 X:02 Y:F1 P:65 SP:FB CYC: 78 SL:189 +F3E0 85 46 STA $46 = 05 A:05 X:02 Y:F1 P:65 SP:FB CYC: 84 SL:189 +F3E2 A0 FF LDY #$FF A:05 X:02 Y:F1 P:65 SP:FB CYC: 93 SL:189 +F3E4 20 1D FB JSR $FB1D A:05 X:02 Y:FF P:E5 SP:FB CYC: 99 SL:189 +FB1D 24 01 BIT $01 = FF A:05 X:02 Y:FF P:E5 SP:F9 CYC:117 SL:189 +FB1F 18 CLC A:05 X:02 Y:FF P:E5 SP:F9 CYC:126 SL:189 +FB20 A9 B3 LDA #$B3 A:05 X:02 Y:FF P:E4 SP:F9 CYC:132 SL:189 +FB22 60 RTS A:B3 X:02 Y:FF P:E4 SP:F9 CYC:138 SL:189 +F3E7 53 45 *SRE ($45),Y = 0548 @ 0647 = A5 A:B3 X:02 Y:FF P:E4 SP:FB CYC:156 SL:189 +F3E9 EA NOP A:E1 X:02 Y:FF P:E5 SP:FB CYC:180 SL:189 +F3EA EA NOP A:E1 X:02 Y:FF P:E5 SP:FB CYC:186 SL:189 +F3EB 08 PHP A:E1 X:02 Y:FF P:E5 SP:FB CYC:192 SL:189 +F3EC 48 PHA A:E1 X:02 Y:FF P:E5 SP:FA CYC:201 SL:189 +F3ED A0 F2 LDY #$F2 A:E1 X:02 Y:FF P:E5 SP:F9 CYC:210 SL:189 +F3EF 68 PLA A:E1 X:02 Y:F2 P:E5 SP:F9 CYC:216 SL:189 +F3F0 28 PLP A:E1 X:02 Y:F2 P:E5 SP:FA CYC:228 SL:189 +F3F1 20 23 FB JSR $FB23 A:E1 X:02 Y:F2 P:E5 SP:FB CYC:240 SL:189 +FB23 50 50 BVC $FB75 A:E1 X:02 Y:F2 P:E5 SP:F9 CYC:258 SL:189 +FB25 90 4E BCC $FB75 A:E1 X:02 Y:F2 P:E5 SP:F9 CYC:264 SL:189 +FB27 10 4C BPL $FB75 A:E1 X:02 Y:F2 P:E5 SP:F9 CYC:270 SL:189 +FB29 C9 E1 CMP #$E1 A:E1 X:02 Y:F2 P:E5 SP:F9 CYC:276 SL:189 +FB2B D0 48 BNE $FB75 A:E1 X:02 Y:F2 P:67 SP:F9 CYC:282 SL:189 +FB2D 60 RTS A:E1 X:02 Y:F2 P:67 SP:F9 CYC:288 SL:189 +F3F4 AD 47 06 LDA $0647 = 52 A:E1 X:02 Y:F2 P:67 SP:FB CYC:306 SL:189 +F3F7 C9 52 CMP #$52 A:52 X:02 Y:F2 P:65 SP:FB CYC:318 SL:189 +F3F9 F0 02 BEQ $F3FD A:52 X:02 Y:F2 P:67 SP:FB CYC:324 SL:189 +F3FD A0 FF LDY #$FF A:52 X:02 Y:F2 P:67 SP:FB CYC:333 SL:189 +F3FF A9 29 LDA #$29 A:52 X:02 Y:FF P:E5 SP:FB CYC:339 SL:189 +F401 8D 47 06 STA $0647 = 52 A:29 X:02 Y:FF P:65 SP:FB CYC: 4 SL:190 +F404 20 2E FB JSR $FB2E A:29 X:02 Y:FF P:65 SP:FB CYC: 16 SL:190 +FB2E B8 CLV A:29 X:02 Y:FF P:65 SP:F9 CYC: 34 SL:190 +FB2F 18 CLC A:29 X:02 Y:FF P:25 SP:F9 CYC: 40 SL:190 +FB30 A9 42 LDA #$42 A:29 X:02 Y:FF P:24 SP:F9 CYC: 46 SL:190 +FB32 60 RTS A:42 X:02 Y:FF P:24 SP:F9 CYC: 52 SL:190 +F407 53 45 *SRE ($45),Y = 0548 @ 0647 = 29 A:42 X:02 Y:FF P:24 SP:FB CYC: 70 SL:190 +F409 EA NOP A:56 X:02 Y:FF P:25 SP:FB CYC: 94 SL:190 +F40A EA NOP A:56 X:02 Y:FF P:25 SP:FB CYC:100 SL:190 +F40B 08 PHP A:56 X:02 Y:FF P:25 SP:FB CYC:106 SL:190 +F40C 48 PHA A:56 X:02 Y:FF P:25 SP:FA CYC:115 SL:190 +F40D A0 F3 LDY #$F3 A:56 X:02 Y:FF P:25 SP:F9 CYC:124 SL:190 +F40F 68 PLA A:56 X:02 Y:F3 P:A5 SP:F9 CYC:130 SL:190 +F410 28 PLP A:56 X:02 Y:F3 P:25 SP:FA CYC:142 SL:190 +F411 20 33 FB JSR $FB33 A:56 X:02 Y:F3 P:25 SP:FB CYC:154 SL:190 +FB33 70 40 BVS $FB75 A:56 X:02 Y:F3 P:25 SP:F9 CYC:172 SL:190 +FB35 F0 3E BEQ $FB75 A:56 X:02 Y:F3 P:25 SP:F9 CYC:178 SL:190 +FB37 30 3C BMI $FB75 A:56 X:02 Y:F3 P:25 SP:F9 CYC:184 SL:190 +FB39 90 3A BCC $FB75 A:56 X:02 Y:F3 P:25 SP:F9 CYC:190 SL:190 +FB3B C9 56 CMP #$56 A:56 X:02 Y:F3 P:25 SP:F9 CYC:196 SL:190 +FB3D D0 36 BNE $FB75 A:56 X:02 Y:F3 P:27 SP:F9 CYC:202 SL:190 +FB3F 60 RTS A:56 X:02 Y:F3 P:27 SP:F9 CYC:208 SL:190 +F414 AD 47 06 LDA $0647 = 14 A:56 X:02 Y:F3 P:27 SP:FB CYC:226 SL:190 +F417 C9 14 CMP #$14 A:14 X:02 Y:F3 P:25 SP:FB CYC:238 SL:190 +F419 F0 02 BEQ $F41D A:14 X:02 Y:F3 P:27 SP:FB CYC:244 SL:190 +F41D A0 FF LDY #$FF A:14 X:02 Y:F3 P:27 SP:FB CYC:253 SL:190 +F41F A9 37 LDA #$37 A:14 X:02 Y:FF P:A5 SP:FB CYC:259 SL:190 +F421 8D 47 06 STA $0647 = 14 A:37 X:02 Y:FF P:25 SP:FB CYC:265 SL:190 +F424 20 40 FB JSR $FB40 A:37 X:02 Y:FF P:25 SP:FB CYC:277 SL:190 +FB40 24 01 BIT $01 = FF A:37 X:02 Y:FF P:25 SP:F9 CYC:295 SL:190 +FB42 38 SEC A:37 X:02 Y:FF P:E5 SP:F9 CYC:304 SL:190 +FB43 A9 75 LDA #$75 A:37 X:02 Y:FF P:E5 SP:F9 CYC:310 SL:190 +FB45 60 RTS A:75 X:02 Y:FF P:65 SP:F9 CYC:316 SL:190 +F427 53 45 *SRE ($45),Y = 0548 @ 0647 = 37 A:75 X:02 Y:FF P:65 SP:FB CYC:334 SL:190 +F429 EA NOP A:6E X:02 Y:FF P:65 SP:FB CYC: 17 SL:191 +F42A EA NOP A:6E X:02 Y:FF P:65 SP:FB CYC: 23 SL:191 +F42B 08 PHP A:6E X:02 Y:FF P:65 SP:FB CYC: 29 SL:191 +F42C 48 PHA A:6E X:02 Y:FF P:65 SP:FA CYC: 38 SL:191 +F42D A0 F4 LDY #$F4 A:6E X:02 Y:FF P:65 SP:F9 CYC: 47 SL:191 +F42F 68 PLA A:6E X:02 Y:F4 P:E5 SP:F9 CYC: 53 SL:191 +F430 28 PLP A:6E X:02 Y:F4 P:65 SP:FA CYC: 65 SL:191 +F431 20 46 FB JSR $FB46 A:6E X:02 Y:F4 P:65 SP:FB CYC: 77 SL:191 +FB46 50 2D BVC $FB75 A:6E X:02 Y:F4 P:65 SP:F9 CYC: 95 SL:191 +FB48 F0 2B BEQ $FB75 A:6E X:02 Y:F4 P:65 SP:F9 CYC:101 SL:191 +FB4A 30 29 BMI $FB75 A:6E X:02 Y:F4 P:65 SP:F9 CYC:107 SL:191 +FB4C 90 27 BCC $FB75 A:6E X:02 Y:F4 P:65 SP:F9 CYC:113 SL:191 +FB4E C9 6E CMP #$6E A:6E X:02 Y:F4 P:65 SP:F9 CYC:119 SL:191 +FB50 D0 23 BNE $FB75 A:6E X:02 Y:F4 P:67 SP:F9 CYC:125 SL:191 +FB52 60 RTS A:6E X:02 Y:F4 P:67 SP:F9 CYC:131 SL:191 +F434 AD 47 06 LDA $0647 = 1B A:6E X:02 Y:F4 P:67 SP:FB CYC:149 SL:191 +F437 C9 1B CMP #$1B A:1B X:02 Y:F4 P:65 SP:FB CYC:161 SL:191 +F439 F0 02 BEQ $F43D A:1B X:02 Y:F4 P:67 SP:FB CYC:167 SL:191 +F43D A0 F5 LDY #$F5 A:1B X:02 Y:F4 P:67 SP:FB CYC:176 SL:191 +F43F A2 FF LDX #$FF A:1B X:02 Y:F5 P:E5 SP:FB CYC:182 SL:191 +F441 A9 A5 LDA #$A5 A:1B X:FF Y:F5 P:E5 SP:FB CYC:188 SL:191 +F443 85 47 STA $47 = 1B A:A5 X:FF Y:F5 P:E5 SP:FB CYC:194 SL:191 +F445 20 1D FB JSR $FB1D A:A5 X:FF Y:F5 P:E5 SP:FB CYC:203 SL:191 +FB1D 24 01 BIT $01 = FF A:A5 X:FF Y:F5 P:E5 SP:F9 CYC:221 SL:191 +FB1F 18 CLC A:A5 X:FF Y:F5 P:E5 SP:F9 CYC:230 SL:191 +FB20 A9 B3 LDA #$B3 A:A5 X:FF Y:F5 P:E4 SP:F9 CYC:236 SL:191 +FB22 60 RTS A:B3 X:FF Y:F5 P:E4 SP:F9 CYC:242 SL:191 +F448 57 48 *SRE $48,X @ 47 = A5 A:B3 X:FF Y:F5 P:E4 SP:FB CYC:260 SL:191 +F44A EA NOP A:E1 X:FF Y:F5 P:E5 SP:FB CYC:278 SL:191 +F44B EA NOP A:E1 X:FF Y:F5 P:E5 SP:FB CYC:284 SL:191 +F44C EA NOP A:E1 X:FF Y:F5 P:E5 SP:FB CYC:290 SL:191 +F44D EA NOP A:E1 X:FF Y:F5 P:E5 SP:FB CYC:296 SL:191 +F44E 20 23 FB JSR $FB23 A:E1 X:FF Y:F5 P:E5 SP:FB CYC:302 SL:191 +FB23 50 50 BVC $FB75 A:E1 X:FF Y:F5 P:E5 SP:F9 CYC:320 SL:191 +FB25 90 4E BCC $FB75 A:E1 X:FF Y:F5 P:E5 SP:F9 CYC:326 SL:191 +FB27 10 4C BPL $FB75 A:E1 X:FF Y:F5 P:E5 SP:F9 CYC:332 SL:191 +FB29 C9 E1 CMP #$E1 A:E1 X:FF Y:F5 P:E5 SP:F9 CYC:338 SL:191 +FB2B D0 48 BNE $FB75 A:E1 X:FF Y:F5 P:67 SP:F9 CYC: 3 SL:192 +FB2D 60 RTS A:E1 X:FF Y:F5 P:67 SP:F9 CYC: 9 SL:192 +F451 A5 47 LDA $47 = 52 A:E1 X:FF Y:F5 P:67 SP:FB CYC: 27 SL:192 +F453 C9 52 CMP #$52 A:52 X:FF Y:F5 P:65 SP:FB CYC: 36 SL:192 +F455 F0 02 BEQ $F459 A:52 X:FF Y:F5 P:67 SP:FB CYC: 42 SL:192 +F459 C8 INY A:52 X:FF Y:F5 P:67 SP:FB CYC: 51 SL:192 +F45A A9 29 LDA #$29 A:52 X:FF Y:F6 P:E5 SP:FB CYC: 57 SL:192 +F45C 85 47 STA $47 = 52 A:29 X:FF Y:F6 P:65 SP:FB CYC: 63 SL:192 +F45E 20 2E FB JSR $FB2E A:29 X:FF Y:F6 P:65 SP:FB CYC: 72 SL:192 +FB2E B8 CLV A:29 X:FF Y:F6 P:65 SP:F9 CYC: 90 SL:192 +FB2F 18 CLC A:29 X:FF Y:F6 P:25 SP:F9 CYC: 96 SL:192 +FB30 A9 42 LDA #$42 A:29 X:FF Y:F6 P:24 SP:F9 CYC:102 SL:192 +FB32 60 RTS A:42 X:FF Y:F6 P:24 SP:F9 CYC:108 SL:192 +F461 57 48 *SRE $48,X @ 47 = 29 A:42 X:FF Y:F6 P:24 SP:FB CYC:126 SL:192 +F463 EA NOP A:56 X:FF Y:F6 P:25 SP:FB CYC:144 SL:192 +F464 EA NOP A:56 X:FF Y:F6 P:25 SP:FB CYC:150 SL:192 +F465 EA NOP A:56 X:FF Y:F6 P:25 SP:FB CYC:156 SL:192 +F466 EA NOP A:56 X:FF Y:F6 P:25 SP:FB CYC:162 SL:192 +F467 20 33 FB JSR $FB33 A:56 X:FF Y:F6 P:25 SP:FB CYC:168 SL:192 +FB33 70 40 BVS $FB75 A:56 X:FF Y:F6 P:25 SP:F9 CYC:186 SL:192 +FB35 F0 3E BEQ $FB75 A:56 X:FF Y:F6 P:25 SP:F9 CYC:192 SL:192 +FB37 30 3C BMI $FB75 A:56 X:FF Y:F6 P:25 SP:F9 CYC:198 SL:192 +FB39 90 3A BCC $FB75 A:56 X:FF Y:F6 P:25 SP:F9 CYC:204 SL:192 +FB3B C9 56 CMP #$56 A:56 X:FF Y:F6 P:25 SP:F9 CYC:210 SL:192 +FB3D D0 36 BNE $FB75 A:56 X:FF Y:F6 P:27 SP:F9 CYC:216 SL:192 +FB3F 60 RTS A:56 X:FF Y:F6 P:27 SP:F9 CYC:222 SL:192 +F46A A5 47 LDA $47 = 14 A:56 X:FF Y:F6 P:27 SP:FB CYC:240 SL:192 +F46C C9 14 CMP #$14 A:14 X:FF Y:F6 P:25 SP:FB CYC:249 SL:192 +F46E F0 02 BEQ $F472 A:14 X:FF Y:F6 P:27 SP:FB CYC:255 SL:192 +F472 C8 INY A:14 X:FF Y:F6 P:27 SP:FB CYC:264 SL:192 +F473 A9 37 LDA #$37 A:14 X:FF Y:F7 P:A5 SP:FB CYC:270 SL:192 +F475 85 47 STA $47 = 14 A:37 X:FF Y:F7 P:25 SP:FB CYC:276 SL:192 +F477 20 40 FB JSR $FB40 A:37 X:FF Y:F7 P:25 SP:FB CYC:285 SL:192 +FB40 24 01 BIT $01 = FF A:37 X:FF Y:F7 P:25 SP:F9 CYC:303 SL:192 +FB42 38 SEC A:37 X:FF Y:F7 P:E5 SP:F9 CYC:312 SL:192 +FB43 A9 75 LDA #$75 A:37 X:FF Y:F7 P:E5 SP:F9 CYC:318 SL:192 +FB45 60 RTS A:75 X:FF Y:F7 P:65 SP:F9 CYC:324 SL:192 +F47A 57 48 *SRE $48,X @ 47 = 37 A:75 X:FF Y:F7 P:65 SP:FB CYC: 1 SL:193 +F47C EA NOP A:6E X:FF Y:F7 P:65 SP:FB CYC: 19 SL:193 +F47D EA NOP A:6E X:FF Y:F7 P:65 SP:FB CYC: 25 SL:193 +F47E EA NOP A:6E X:FF Y:F7 P:65 SP:FB CYC: 31 SL:193 +F47F EA NOP A:6E X:FF Y:F7 P:65 SP:FB CYC: 37 SL:193 +F480 20 46 FB JSR $FB46 A:6E X:FF Y:F7 P:65 SP:FB CYC: 43 SL:193 +FB46 50 2D BVC $FB75 A:6E X:FF Y:F7 P:65 SP:F9 CYC: 61 SL:193 +FB48 F0 2B BEQ $FB75 A:6E X:FF Y:F7 P:65 SP:F9 CYC: 67 SL:193 +FB4A 30 29 BMI $FB75 A:6E X:FF Y:F7 P:65 SP:F9 CYC: 73 SL:193 +FB4C 90 27 BCC $FB75 A:6E X:FF Y:F7 P:65 SP:F9 CYC: 79 SL:193 +FB4E C9 6E CMP #$6E A:6E X:FF Y:F7 P:65 SP:F9 CYC: 85 SL:193 +FB50 D0 23 BNE $FB75 A:6E X:FF Y:F7 P:67 SP:F9 CYC: 91 SL:193 +FB52 60 RTS A:6E X:FF Y:F7 P:67 SP:F9 CYC: 97 SL:193 +F483 A5 47 LDA $47 = 1B A:6E X:FF Y:F7 P:67 SP:FB CYC:115 SL:193 +F485 C9 1B CMP #$1B A:1B X:FF Y:F7 P:65 SP:FB CYC:124 SL:193 +F487 F0 02 BEQ $F48B A:1B X:FF Y:F7 P:67 SP:FB CYC:130 SL:193 +F48B A9 A5 LDA #$A5 A:1B X:FF Y:F7 P:67 SP:FB CYC:139 SL:193 +F48D 8D 47 06 STA $0647 = 1B A:A5 X:FF Y:F7 P:E5 SP:FB CYC:145 SL:193 +F490 A0 FF LDY #$FF A:A5 X:FF Y:F7 P:E5 SP:FB CYC:157 SL:193 +F492 20 1D FB JSR $FB1D A:A5 X:FF Y:FF P:E5 SP:FB CYC:163 SL:193 +FB1D 24 01 BIT $01 = FF A:A5 X:FF Y:FF P:E5 SP:F9 CYC:181 SL:193 +FB1F 18 CLC A:A5 X:FF Y:FF P:E5 SP:F9 CYC:190 SL:193 +FB20 A9 B3 LDA #$B3 A:A5 X:FF Y:FF P:E4 SP:F9 CYC:196 SL:193 +FB22 60 RTS A:B3 X:FF Y:FF P:E4 SP:F9 CYC:202 SL:193 +F495 5B 48 05 *SRE $0548,Y @ 0647 = A5 A:B3 X:FF Y:FF P:E4 SP:FB CYC:220 SL:193 +F498 EA NOP A:E1 X:FF Y:FF P:E5 SP:FB CYC:241 SL:193 +F499 EA NOP A:E1 X:FF Y:FF P:E5 SP:FB CYC:247 SL:193 +F49A 08 PHP A:E1 X:FF Y:FF P:E5 SP:FB CYC:253 SL:193 +F49B 48 PHA A:E1 X:FF Y:FF P:E5 SP:FA CYC:262 SL:193 +F49C A0 F8 LDY #$F8 A:E1 X:FF Y:FF P:E5 SP:F9 CYC:271 SL:193 +F49E 68 PLA A:E1 X:FF Y:F8 P:E5 SP:F9 CYC:277 SL:193 +F49F 28 PLP A:E1 X:FF Y:F8 P:E5 SP:FA CYC:289 SL:193 +F4A0 20 23 FB JSR $FB23 A:E1 X:FF Y:F8 P:E5 SP:FB CYC:301 SL:193 +FB23 50 50 BVC $FB75 A:E1 X:FF Y:F8 P:E5 SP:F9 CYC:319 SL:193 +FB25 90 4E BCC $FB75 A:E1 X:FF Y:F8 P:E5 SP:F9 CYC:325 SL:193 +FB27 10 4C BPL $FB75 A:E1 X:FF Y:F8 P:E5 SP:F9 CYC:331 SL:193 +FB29 C9 E1 CMP #$E1 A:E1 X:FF Y:F8 P:E5 SP:F9 CYC:337 SL:193 +FB2B D0 48 BNE $FB75 A:E1 X:FF Y:F8 P:67 SP:F9 CYC: 2 SL:194 +FB2D 60 RTS A:E1 X:FF Y:F8 P:67 SP:F9 CYC: 8 SL:194 +F4A3 AD 47 06 LDA $0647 = 52 A:E1 X:FF Y:F8 P:67 SP:FB CYC: 26 SL:194 +F4A6 C9 52 CMP #$52 A:52 X:FF Y:F8 P:65 SP:FB CYC: 38 SL:194 +F4A8 F0 02 BEQ $F4AC A:52 X:FF Y:F8 P:67 SP:FB CYC: 44 SL:194 +F4AC A0 FF LDY #$FF A:52 X:FF Y:F8 P:67 SP:FB CYC: 53 SL:194 +F4AE A9 29 LDA #$29 A:52 X:FF Y:FF P:E5 SP:FB CYC: 59 SL:194 +F4B0 8D 47 06 STA $0647 = 52 A:29 X:FF Y:FF P:65 SP:FB CYC: 65 SL:194 +F4B3 20 2E FB JSR $FB2E A:29 X:FF Y:FF P:65 SP:FB CYC: 77 SL:194 +FB2E B8 CLV A:29 X:FF Y:FF P:65 SP:F9 CYC: 95 SL:194 +FB2F 18 CLC A:29 X:FF Y:FF P:25 SP:F9 CYC:101 SL:194 +FB30 A9 42 LDA #$42 A:29 X:FF Y:FF P:24 SP:F9 CYC:107 SL:194 +FB32 60 RTS A:42 X:FF Y:FF P:24 SP:F9 CYC:113 SL:194 +F4B6 5B 48 05 *SRE $0548,Y @ 0647 = 29 A:42 X:FF Y:FF P:24 SP:FB CYC:131 SL:194 +F4B9 EA NOP A:56 X:FF Y:FF P:25 SP:FB CYC:152 SL:194 +F4BA EA NOP A:56 X:FF Y:FF P:25 SP:FB CYC:158 SL:194 +F4BB 08 PHP A:56 X:FF Y:FF P:25 SP:FB CYC:164 SL:194 +F4BC 48 PHA A:56 X:FF Y:FF P:25 SP:FA CYC:173 SL:194 +F4BD A0 F9 LDY #$F9 A:56 X:FF Y:FF P:25 SP:F9 CYC:182 SL:194 +F4BF 68 PLA A:56 X:FF Y:F9 P:A5 SP:F9 CYC:188 SL:194 +F4C0 28 PLP A:56 X:FF Y:F9 P:25 SP:FA CYC:200 SL:194 +F4C1 20 33 FB JSR $FB33 A:56 X:FF Y:F9 P:25 SP:FB CYC:212 SL:194 +FB33 70 40 BVS $FB75 A:56 X:FF Y:F9 P:25 SP:F9 CYC:230 SL:194 +FB35 F0 3E BEQ $FB75 A:56 X:FF Y:F9 P:25 SP:F9 CYC:236 SL:194 +FB37 30 3C BMI $FB75 A:56 X:FF Y:F9 P:25 SP:F9 CYC:242 SL:194 +FB39 90 3A BCC $FB75 A:56 X:FF Y:F9 P:25 SP:F9 CYC:248 SL:194 +FB3B C9 56 CMP #$56 A:56 X:FF Y:F9 P:25 SP:F9 CYC:254 SL:194 +FB3D D0 36 BNE $FB75 A:56 X:FF Y:F9 P:27 SP:F9 CYC:260 SL:194 +FB3F 60 RTS A:56 X:FF Y:F9 P:27 SP:F9 CYC:266 SL:194 +F4C4 AD 47 06 LDA $0647 = 14 A:56 X:FF Y:F9 P:27 SP:FB CYC:284 SL:194 +F4C7 C9 14 CMP #$14 A:14 X:FF Y:F9 P:25 SP:FB CYC:296 SL:194 +F4C9 F0 02 BEQ $F4CD A:14 X:FF Y:F9 P:27 SP:FB CYC:302 SL:194 +F4CD A0 FF LDY #$FF A:14 X:FF Y:F9 P:27 SP:FB CYC:311 SL:194 +F4CF A9 37 LDA #$37 A:14 X:FF Y:FF P:A5 SP:FB CYC:317 SL:194 +F4D1 8D 47 06 STA $0647 = 14 A:37 X:FF Y:FF P:25 SP:FB CYC:323 SL:194 +F4D4 20 40 FB JSR $FB40 A:37 X:FF Y:FF P:25 SP:FB CYC:335 SL:194 +FB40 24 01 BIT $01 = FF A:37 X:FF Y:FF P:25 SP:F9 CYC: 12 SL:195 +FB42 38 SEC A:37 X:FF Y:FF P:E5 SP:F9 CYC: 21 SL:195 +FB43 A9 75 LDA #$75 A:37 X:FF Y:FF P:E5 SP:F9 CYC: 27 SL:195 +FB45 60 RTS A:75 X:FF Y:FF P:65 SP:F9 CYC: 33 SL:195 +F4D7 5B 48 05 *SRE $0548,Y @ 0647 = 37 A:75 X:FF Y:FF P:65 SP:FB CYC: 51 SL:195 +F4DA EA NOP A:6E X:FF Y:FF P:65 SP:FB CYC: 72 SL:195 +F4DB EA NOP A:6E X:FF Y:FF P:65 SP:FB CYC: 78 SL:195 +F4DC 08 PHP A:6E X:FF Y:FF P:65 SP:FB CYC: 84 SL:195 +F4DD 48 PHA A:6E X:FF Y:FF P:65 SP:FA CYC: 93 SL:195 +F4DE A0 FA LDY #$FA A:6E X:FF Y:FF P:65 SP:F9 CYC:102 SL:195 +F4E0 68 PLA A:6E X:FF Y:FA P:E5 SP:F9 CYC:108 SL:195 +F4E1 28 PLP A:6E X:FF Y:FA P:65 SP:FA CYC:120 SL:195 +F4E2 20 46 FB JSR $FB46 A:6E X:FF Y:FA P:65 SP:FB CYC:132 SL:195 +FB46 50 2D BVC $FB75 A:6E X:FF Y:FA P:65 SP:F9 CYC:150 SL:195 +FB48 F0 2B BEQ $FB75 A:6E X:FF Y:FA P:65 SP:F9 CYC:156 SL:195 +FB4A 30 29 BMI $FB75 A:6E X:FF Y:FA P:65 SP:F9 CYC:162 SL:195 +FB4C 90 27 BCC $FB75 A:6E X:FF Y:FA P:65 SP:F9 CYC:168 SL:195 +FB4E C9 6E CMP #$6E A:6E X:FF Y:FA P:65 SP:F9 CYC:174 SL:195 +FB50 D0 23 BNE $FB75 A:6E X:FF Y:FA P:67 SP:F9 CYC:180 SL:195 +FB52 60 RTS A:6E X:FF Y:FA P:67 SP:F9 CYC:186 SL:195 +F4E5 AD 47 06 LDA $0647 = 1B A:6E X:FF Y:FA P:67 SP:FB CYC:204 SL:195 +F4E8 C9 1B CMP #$1B A:1B X:FF Y:FA P:65 SP:FB CYC:216 SL:195 +F4EA F0 02 BEQ $F4EE A:1B X:FF Y:FA P:67 SP:FB CYC:222 SL:195 +F4EE A0 FB LDY #$FB A:1B X:FF Y:FA P:67 SP:FB CYC:231 SL:195 +F4F0 A2 FF LDX #$FF A:1B X:FF Y:FB P:E5 SP:FB CYC:237 SL:195 +F4F2 A9 A5 LDA #$A5 A:1B X:FF Y:FB P:E5 SP:FB CYC:243 SL:195 +F4F4 8D 47 06 STA $0647 = 1B A:A5 X:FF Y:FB P:E5 SP:FB CYC:249 SL:195 +F4F7 20 1D FB JSR $FB1D A:A5 X:FF Y:FB P:E5 SP:FB CYC:261 SL:195 +FB1D 24 01 BIT $01 = FF A:A5 X:FF Y:FB P:E5 SP:F9 CYC:279 SL:195 +FB1F 18 CLC A:A5 X:FF Y:FB P:E5 SP:F9 CYC:288 SL:195 +FB20 A9 B3 LDA #$B3 A:A5 X:FF Y:FB P:E4 SP:F9 CYC:294 SL:195 +FB22 60 RTS A:B3 X:FF Y:FB P:E4 SP:F9 CYC:300 SL:195 +F4FA 5F 48 05 *SRE $0548,X @ 0647 = A5 A:B3 X:FF Y:FB P:E4 SP:FB CYC:318 SL:195 +F4FD EA NOP A:E1 X:FF Y:FB P:E5 SP:FB CYC:339 SL:195 +F4FE EA NOP A:E1 X:FF Y:FB P:E5 SP:FB CYC: 4 SL:196 +F4FF EA NOP A:E1 X:FF Y:FB P:E5 SP:FB CYC: 10 SL:196 +F500 EA NOP A:E1 X:FF Y:FB P:E5 SP:FB CYC: 16 SL:196 +F501 20 23 FB JSR $FB23 A:E1 X:FF Y:FB P:E5 SP:FB CYC: 22 SL:196 +FB23 50 50 BVC $FB75 A:E1 X:FF Y:FB P:E5 SP:F9 CYC: 40 SL:196 +FB25 90 4E BCC $FB75 A:E1 X:FF Y:FB P:E5 SP:F9 CYC: 46 SL:196 +FB27 10 4C BPL $FB75 A:E1 X:FF Y:FB P:E5 SP:F9 CYC: 52 SL:196 +FB29 C9 E1 CMP #$E1 A:E1 X:FF Y:FB P:E5 SP:F9 CYC: 58 SL:196 +FB2B D0 48 BNE $FB75 A:E1 X:FF Y:FB P:67 SP:F9 CYC: 64 SL:196 +FB2D 60 RTS A:E1 X:FF Y:FB P:67 SP:F9 CYC: 70 SL:196 +F504 AD 47 06 LDA $0647 = 52 A:E1 X:FF Y:FB P:67 SP:FB CYC: 88 SL:196 +F507 C9 52 CMP #$52 A:52 X:FF Y:FB P:65 SP:FB CYC:100 SL:196 +F509 F0 02 BEQ $F50D A:52 X:FF Y:FB P:67 SP:FB CYC:106 SL:196 +F50D C8 INY A:52 X:FF Y:FB P:67 SP:FB CYC:115 SL:196 +F50E A9 29 LDA #$29 A:52 X:FF Y:FC P:E5 SP:FB CYC:121 SL:196 +F510 8D 47 06 STA $0647 = 52 A:29 X:FF Y:FC P:65 SP:FB CYC:127 SL:196 +F513 20 2E FB JSR $FB2E A:29 X:FF Y:FC P:65 SP:FB CYC:139 SL:196 +FB2E B8 CLV A:29 X:FF Y:FC P:65 SP:F9 CYC:157 SL:196 +FB2F 18 CLC A:29 X:FF Y:FC P:25 SP:F9 CYC:163 SL:196 +FB30 A9 42 LDA #$42 A:29 X:FF Y:FC P:24 SP:F9 CYC:169 SL:196 +FB32 60 RTS A:42 X:FF Y:FC P:24 SP:F9 CYC:175 SL:196 +F516 5F 48 05 *SRE $0548,X @ 0647 = 29 A:42 X:FF Y:FC P:24 SP:FB CYC:193 SL:196 +F519 EA NOP A:56 X:FF Y:FC P:25 SP:FB CYC:214 SL:196 +F51A EA NOP A:56 X:FF Y:FC P:25 SP:FB CYC:220 SL:196 +F51B EA NOP A:56 X:FF Y:FC P:25 SP:FB CYC:226 SL:196 +F51C EA NOP A:56 X:FF Y:FC P:25 SP:FB CYC:232 SL:196 +F51D 20 33 FB JSR $FB33 A:56 X:FF Y:FC P:25 SP:FB CYC:238 SL:196 +FB33 70 40 BVS $FB75 A:56 X:FF Y:FC P:25 SP:F9 CYC:256 SL:196 +FB35 F0 3E BEQ $FB75 A:56 X:FF Y:FC P:25 SP:F9 CYC:262 SL:196 +FB37 30 3C BMI $FB75 A:56 X:FF Y:FC P:25 SP:F9 CYC:268 SL:196 +FB39 90 3A BCC $FB75 A:56 X:FF Y:FC P:25 SP:F9 CYC:274 SL:196 +FB3B C9 56 CMP #$56 A:56 X:FF Y:FC P:25 SP:F9 CYC:280 SL:196 +FB3D D0 36 BNE $FB75 A:56 X:FF Y:FC P:27 SP:F9 CYC:286 SL:196 +FB3F 60 RTS A:56 X:FF Y:FC P:27 SP:F9 CYC:292 SL:196 +F520 AD 47 06 LDA $0647 = 14 A:56 X:FF Y:FC P:27 SP:FB CYC:310 SL:196 +F523 C9 14 CMP #$14 A:14 X:FF Y:FC P:25 SP:FB CYC:322 SL:196 +F525 F0 02 BEQ $F529 A:14 X:FF Y:FC P:27 SP:FB CYC:328 SL:196 +F529 C8 INY A:14 X:FF Y:FC P:27 SP:FB CYC:337 SL:196 +F52A A9 37 LDA #$37 A:14 X:FF Y:FD P:A5 SP:FB CYC: 2 SL:197 +F52C 8D 47 06 STA $0647 = 14 A:37 X:FF Y:FD P:25 SP:FB CYC: 8 SL:197 +F52F 20 40 FB JSR $FB40 A:37 X:FF Y:FD P:25 SP:FB CYC: 20 SL:197 +FB40 24 01 BIT $01 = FF A:37 X:FF Y:FD P:25 SP:F9 CYC: 38 SL:197 +FB42 38 SEC A:37 X:FF Y:FD P:E5 SP:F9 CYC: 47 SL:197 +FB43 A9 75 LDA #$75 A:37 X:FF Y:FD P:E5 SP:F9 CYC: 53 SL:197 +FB45 60 RTS A:75 X:FF Y:FD P:65 SP:F9 CYC: 59 SL:197 +F532 5F 48 05 *SRE $0548,X @ 0647 = 37 A:75 X:FF Y:FD P:65 SP:FB CYC: 77 SL:197 +F535 EA NOP A:6E X:FF Y:FD P:65 SP:FB CYC: 98 SL:197 +F536 EA NOP A:6E X:FF Y:FD P:65 SP:FB CYC:104 SL:197 +F537 EA NOP A:6E X:FF Y:FD P:65 SP:FB CYC:110 SL:197 +F538 EA NOP A:6E X:FF Y:FD P:65 SP:FB CYC:116 SL:197 +F539 20 46 FB JSR $FB46 A:6E X:FF Y:FD P:65 SP:FB CYC:122 SL:197 +FB46 50 2D BVC $FB75 A:6E X:FF Y:FD P:65 SP:F9 CYC:140 SL:197 +FB48 F0 2B BEQ $FB75 A:6E X:FF Y:FD P:65 SP:F9 CYC:146 SL:197 +FB4A 30 29 BMI $FB75 A:6E X:FF Y:FD P:65 SP:F9 CYC:152 SL:197 +FB4C 90 27 BCC $FB75 A:6E X:FF Y:FD P:65 SP:F9 CYC:158 SL:197 +FB4E C9 6E CMP #$6E A:6E X:FF Y:FD P:65 SP:F9 CYC:164 SL:197 +FB50 D0 23 BNE $FB75 A:6E X:FF Y:FD P:67 SP:F9 CYC:170 SL:197 +FB52 60 RTS A:6E X:FF Y:FD P:67 SP:F9 CYC:176 SL:197 +F53C AD 47 06 LDA $0647 = 1B A:6E X:FF Y:FD P:67 SP:FB CYC:194 SL:197 +F53F C9 1B CMP #$1B A:1B X:FF Y:FD P:65 SP:FB CYC:206 SL:197 +F541 F0 02 BEQ $F545 A:1B X:FF Y:FD P:67 SP:FB CYC:212 SL:197 +F545 60 RTS A:1B X:FF Y:FD P:67 SP:FB CYC:221 SL:197 +C64A A5 00 LDA $00 = 00 A:1B X:FF Y:FD P:67 SP:FD CYC:239 SL:197 +C64C 85 11 STA $11 = 00 A:00 X:FF Y:FD P:67 SP:FD CYC:248 SL:197 +C64E A9 00 LDA #$00 A:00 X:FF Y:FD P:67 SP:FD CYC:257 SL:197 +C650 85 00 STA $00 = 00 A:00 X:FF Y:FD P:67 SP:FD CYC:263 SL:197 +C652 20 46 F5 JSR $F546 A:00 X:FF Y:FD P:67 SP:FD CYC:272 SL:197 +F546 A9 FF LDA #$FF A:00 X:FF Y:FD P:67 SP:FB CYC:290 SL:197 +F548 85 01 STA $01 = FF A:FF X:FF Y:FD P:E5 SP:FB CYC:296 SL:197 +F54A A0 01 LDY #$01 A:FF X:FF Y:FD P:E5 SP:FB CYC:305 SL:197 +F54C A2 02 LDX #$02 A:FF X:FF Y:01 P:65 SP:FB CYC:311 SL:197 +F54E A9 47 LDA #$47 A:FF X:02 Y:01 P:65 SP:FB CYC:317 SL:197 +F550 85 47 STA $47 = 1B A:47 X:02 Y:01 P:65 SP:FB CYC:323 SL:197 +F552 A9 06 LDA #$06 A:47 X:02 Y:01 P:65 SP:FB CYC:332 SL:197 +F554 85 48 STA $48 = 06 A:06 X:02 Y:01 P:65 SP:FB CYC:338 SL:197 +F556 A9 A5 LDA #$A5 A:06 X:02 Y:01 P:65 SP:FB CYC: 6 SL:198 +F558 8D 47 06 STA $0647 = 1B A:A5 X:02 Y:01 P:E5 SP:FB CYC: 12 SL:198 +F55B 20 E9 FA JSR $FAE9 A:A5 X:02 Y:01 P:E5 SP:FB CYC: 24 SL:198 +FAE9 24 01 BIT $01 = FF A:A5 X:02 Y:01 P:E5 SP:F9 CYC: 42 SL:198 +FAEB 18 CLC A:A5 X:02 Y:01 P:E5 SP:F9 CYC: 51 SL:198 +FAEC A9 B2 LDA #$B2 A:A5 X:02 Y:01 P:E4 SP:F9 CYC: 57 SL:198 +FAEE 60 RTS A:B2 X:02 Y:01 P:E4 SP:F9 CYC: 63 SL:198 +F55E 63 45 *RRA ($45,X) @ 47 = 0647 = A5 A:B2 X:02 Y:01 P:E4 SP:FB CYC: 81 SL:198 +F560 EA NOP A:05 X:02 Y:01 P:25 SP:FB CYC:105 SL:198 +F561 EA NOP A:05 X:02 Y:01 P:25 SP:FB CYC:111 SL:198 +F562 EA NOP A:05 X:02 Y:01 P:25 SP:FB CYC:117 SL:198 +F563 EA NOP A:05 X:02 Y:01 P:25 SP:FB CYC:123 SL:198 +F564 20 EF FA JSR $FAEF A:05 X:02 Y:01 P:25 SP:FB CYC:129 SL:198 +FAEF 70 2A BVS $FB1B A:05 X:02 Y:01 P:25 SP:F9 CYC:147 SL:198 +FAF1 90 28 BCC $FB1B A:05 X:02 Y:01 P:25 SP:F9 CYC:153 SL:198 +FAF3 30 26 BMI $FB1B A:05 X:02 Y:01 P:25 SP:F9 CYC:159 SL:198 +FAF5 C9 05 CMP #$05 A:05 X:02 Y:01 P:25 SP:F9 CYC:165 SL:198 +FAF7 D0 22 BNE $FB1B A:05 X:02 Y:01 P:27 SP:F9 CYC:171 SL:198 +FAF9 60 RTS A:05 X:02 Y:01 P:27 SP:F9 CYC:177 SL:198 +F567 AD 47 06 LDA $0647 = 52 A:05 X:02 Y:01 P:27 SP:FB CYC:195 SL:198 +F56A C9 52 CMP #$52 A:52 X:02 Y:01 P:25 SP:FB CYC:207 SL:198 +F56C F0 02 BEQ $F570 A:52 X:02 Y:01 P:27 SP:FB CYC:213 SL:198 +F570 C8 INY A:52 X:02 Y:01 P:27 SP:FB CYC:222 SL:198 +F571 A9 29 LDA #$29 A:52 X:02 Y:02 P:25 SP:FB CYC:228 SL:198 +F573 8D 47 06 STA $0647 = 52 A:29 X:02 Y:02 P:25 SP:FB CYC:234 SL:198 +F576 20 FA FA JSR $FAFA A:29 X:02 Y:02 P:25 SP:FB CYC:246 SL:198 +FAFA B8 CLV A:29 X:02 Y:02 P:25 SP:F9 CYC:264 SL:198 +FAFB 18 CLC A:29 X:02 Y:02 P:25 SP:F9 CYC:270 SL:198 +FAFC A9 42 LDA #$42 A:29 X:02 Y:02 P:24 SP:F9 CYC:276 SL:198 +FAFE 60 RTS A:42 X:02 Y:02 P:24 SP:F9 CYC:282 SL:198 +F579 63 45 *RRA ($45,X) @ 47 = 0647 = 29 A:42 X:02 Y:02 P:24 SP:FB CYC:300 SL:198 +F57B EA NOP A:57 X:02 Y:02 P:24 SP:FB CYC:324 SL:198 +F57C EA NOP A:57 X:02 Y:02 P:24 SP:FB CYC:330 SL:198 +F57D EA NOP A:57 X:02 Y:02 P:24 SP:FB CYC:336 SL:198 +F57E EA NOP A:57 X:02 Y:02 P:24 SP:FB CYC: 1 SL:199 +F57F 20 FF FA JSR $FAFF A:57 X:02 Y:02 P:24 SP:FB CYC: 7 SL:199 +FAFF 70 1A BVS $FB1B A:57 X:02 Y:02 P:24 SP:F9 CYC: 25 SL:199 +FB01 30 18 BMI $FB1B A:57 X:02 Y:02 P:24 SP:F9 CYC: 31 SL:199 +FB03 B0 16 BCS $FB1B A:57 X:02 Y:02 P:24 SP:F9 CYC: 37 SL:199 +FB05 C9 57 CMP #$57 A:57 X:02 Y:02 P:24 SP:F9 CYC: 43 SL:199 +FB07 D0 12 BNE $FB1B A:57 X:02 Y:02 P:27 SP:F9 CYC: 49 SL:199 +FB09 60 RTS A:57 X:02 Y:02 P:27 SP:F9 CYC: 55 SL:199 +F582 AD 47 06 LDA $0647 = 14 A:57 X:02 Y:02 P:27 SP:FB CYC: 73 SL:199 +F585 C9 14 CMP #$14 A:14 X:02 Y:02 P:25 SP:FB CYC: 85 SL:199 +F587 F0 02 BEQ $F58B A:14 X:02 Y:02 P:27 SP:FB CYC: 91 SL:199 +F58B C8 INY A:14 X:02 Y:02 P:27 SP:FB CYC:100 SL:199 +F58C A9 37 LDA #$37 A:14 X:02 Y:03 P:25 SP:FB CYC:106 SL:199 +F58E 8D 47 06 STA $0647 = 14 A:37 X:02 Y:03 P:25 SP:FB CYC:112 SL:199 +F591 20 0A FB JSR $FB0A A:37 X:02 Y:03 P:25 SP:FB CYC:124 SL:199 +FB0A 24 01 BIT $01 = FF A:37 X:02 Y:03 P:25 SP:F9 CYC:142 SL:199 +FB0C 38 SEC A:37 X:02 Y:03 P:E5 SP:F9 CYC:151 SL:199 +FB0D A9 75 LDA #$75 A:37 X:02 Y:03 P:E5 SP:F9 CYC:157 SL:199 +FB0F 60 RTS A:75 X:02 Y:03 P:65 SP:F9 CYC:163 SL:199 +F594 63 45 *RRA ($45,X) @ 47 = 0647 = 37 A:75 X:02 Y:03 P:65 SP:FB CYC:181 SL:199 +F596 EA NOP A:11 X:02 Y:03 P:25 SP:FB CYC:205 SL:199 +F597 EA NOP A:11 X:02 Y:03 P:25 SP:FB CYC:211 SL:199 +F598 EA NOP A:11 X:02 Y:03 P:25 SP:FB CYC:217 SL:199 +F599 EA NOP A:11 X:02 Y:03 P:25 SP:FB CYC:223 SL:199 +F59A 20 10 FB JSR $FB10 A:11 X:02 Y:03 P:25 SP:FB CYC:229 SL:199 +FB10 70 09 BVS $FB1B A:11 X:02 Y:03 P:25 SP:F9 CYC:247 SL:199 +FB12 30 07 BMI $FB1B A:11 X:02 Y:03 P:25 SP:F9 CYC:253 SL:199 +FB14 90 05 BCC $FB1B A:11 X:02 Y:03 P:25 SP:F9 CYC:259 SL:199 +FB16 C9 11 CMP #$11 A:11 X:02 Y:03 P:25 SP:F9 CYC:265 SL:199 +FB18 D0 01 BNE $FB1B A:11 X:02 Y:03 P:27 SP:F9 CYC:271 SL:199 +FB1A 60 RTS A:11 X:02 Y:03 P:27 SP:F9 CYC:277 SL:199 +F59D AD 47 06 LDA $0647 = 9B A:11 X:02 Y:03 P:27 SP:FB CYC:295 SL:199 +F5A0 C9 9B CMP #$9B A:9B X:02 Y:03 P:A5 SP:FB CYC:307 SL:199 +F5A2 F0 02 BEQ $F5A6 A:9B X:02 Y:03 P:27 SP:FB CYC:313 SL:199 +F5A6 C8 INY A:9B X:02 Y:03 P:27 SP:FB CYC:322 SL:199 +F5A7 A9 A5 LDA #$A5 A:9B X:02 Y:04 P:25 SP:FB CYC:328 SL:199 +F5A9 85 47 STA $47 = 47 A:A5 X:02 Y:04 P:A5 SP:FB CYC:334 SL:199 +F5AB 20 E9 FA JSR $FAE9 A:A5 X:02 Y:04 P:A5 SP:FB CYC: 2 SL:200 +FAE9 24 01 BIT $01 = FF A:A5 X:02 Y:04 P:A5 SP:F9 CYC: 20 SL:200 +FAEB 18 CLC A:A5 X:02 Y:04 P:E5 SP:F9 CYC: 29 SL:200 +FAEC A9 B2 LDA #$B2 A:A5 X:02 Y:04 P:E4 SP:F9 CYC: 35 SL:200 +FAEE 60 RTS A:B2 X:02 Y:04 P:E4 SP:F9 CYC: 41 SL:200 +F5AE 67 47 *RRA $47 = A5 A:B2 X:02 Y:04 P:E4 SP:FB CYC: 59 SL:200 +F5B0 EA NOP A:05 X:02 Y:04 P:25 SP:FB CYC: 74 SL:200 +F5B1 EA NOP A:05 X:02 Y:04 P:25 SP:FB CYC: 80 SL:200 +F5B2 EA NOP A:05 X:02 Y:04 P:25 SP:FB CYC: 86 SL:200 +F5B3 EA NOP A:05 X:02 Y:04 P:25 SP:FB CYC: 92 SL:200 +F5B4 20 EF FA JSR $FAEF A:05 X:02 Y:04 P:25 SP:FB CYC: 98 SL:200 +FAEF 70 2A BVS $FB1B A:05 X:02 Y:04 P:25 SP:F9 CYC:116 SL:200 +FAF1 90 28 BCC $FB1B A:05 X:02 Y:04 P:25 SP:F9 CYC:122 SL:200 +FAF3 30 26 BMI $FB1B A:05 X:02 Y:04 P:25 SP:F9 CYC:128 SL:200 +FAF5 C9 05 CMP #$05 A:05 X:02 Y:04 P:25 SP:F9 CYC:134 SL:200 +FAF7 D0 22 BNE $FB1B A:05 X:02 Y:04 P:27 SP:F9 CYC:140 SL:200 +FAF9 60 RTS A:05 X:02 Y:04 P:27 SP:F9 CYC:146 SL:200 +F5B7 A5 47 LDA $47 = 52 A:05 X:02 Y:04 P:27 SP:FB CYC:164 SL:200 +F5B9 C9 52 CMP #$52 A:52 X:02 Y:04 P:25 SP:FB CYC:173 SL:200 +F5BB F0 02 BEQ $F5BF A:52 X:02 Y:04 P:27 SP:FB CYC:179 SL:200 +F5BF C8 INY A:52 X:02 Y:04 P:27 SP:FB CYC:188 SL:200 +F5C0 A9 29 LDA #$29 A:52 X:02 Y:05 P:25 SP:FB CYC:194 SL:200 +F5C2 85 47 STA $47 = 52 A:29 X:02 Y:05 P:25 SP:FB CYC:200 SL:200 +F5C4 20 FA FA JSR $FAFA A:29 X:02 Y:05 P:25 SP:FB CYC:209 SL:200 +FAFA B8 CLV A:29 X:02 Y:05 P:25 SP:F9 CYC:227 SL:200 +FAFB 18 CLC A:29 X:02 Y:05 P:25 SP:F9 CYC:233 SL:200 +FAFC A9 42 LDA #$42 A:29 X:02 Y:05 P:24 SP:F9 CYC:239 SL:200 +FAFE 60 RTS A:42 X:02 Y:05 P:24 SP:F9 CYC:245 SL:200 +F5C7 67 47 *RRA $47 = 29 A:42 X:02 Y:05 P:24 SP:FB CYC:263 SL:200 +F5C9 EA NOP A:57 X:02 Y:05 P:24 SP:FB CYC:278 SL:200 +F5CA EA NOP A:57 X:02 Y:05 P:24 SP:FB CYC:284 SL:200 +F5CB EA NOP A:57 X:02 Y:05 P:24 SP:FB CYC:290 SL:200 +F5CC EA NOP A:57 X:02 Y:05 P:24 SP:FB CYC:296 SL:200 +F5CD 20 FF FA JSR $FAFF A:57 X:02 Y:05 P:24 SP:FB CYC:302 SL:200 +FAFF 70 1A BVS $FB1B A:57 X:02 Y:05 P:24 SP:F9 CYC:320 SL:200 +FB01 30 18 BMI $FB1B A:57 X:02 Y:05 P:24 SP:F9 CYC:326 SL:200 +FB03 B0 16 BCS $FB1B A:57 X:02 Y:05 P:24 SP:F9 CYC:332 SL:200 +FB05 C9 57 CMP #$57 A:57 X:02 Y:05 P:24 SP:F9 CYC:338 SL:200 +FB07 D0 12 BNE $FB1B A:57 X:02 Y:05 P:27 SP:F9 CYC: 3 SL:201 +FB09 60 RTS A:57 X:02 Y:05 P:27 SP:F9 CYC: 9 SL:201 +F5D0 A5 47 LDA $47 = 14 A:57 X:02 Y:05 P:27 SP:FB CYC: 27 SL:201 +F5D2 C9 14 CMP #$14 A:14 X:02 Y:05 P:25 SP:FB CYC: 36 SL:201 +F5D4 F0 02 BEQ $F5D8 A:14 X:02 Y:05 P:27 SP:FB CYC: 42 SL:201 +F5D8 C8 INY A:14 X:02 Y:05 P:27 SP:FB CYC: 51 SL:201 +F5D9 A9 37 LDA #$37 A:14 X:02 Y:06 P:25 SP:FB CYC: 57 SL:201 +F5DB 85 47 STA $47 = 14 A:37 X:02 Y:06 P:25 SP:FB CYC: 63 SL:201 +F5DD 20 0A FB JSR $FB0A A:37 X:02 Y:06 P:25 SP:FB CYC: 72 SL:201 +FB0A 24 01 BIT $01 = FF A:37 X:02 Y:06 P:25 SP:F9 CYC: 90 SL:201 +FB0C 38 SEC A:37 X:02 Y:06 P:E5 SP:F9 CYC: 99 SL:201 +FB0D A9 75 LDA #$75 A:37 X:02 Y:06 P:E5 SP:F9 CYC:105 SL:201 +FB0F 60 RTS A:75 X:02 Y:06 P:65 SP:F9 CYC:111 SL:201 +F5E0 67 47 *RRA $47 = 37 A:75 X:02 Y:06 P:65 SP:FB CYC:129 SL:201 +F5E2 EA NOP A:11 X:02 Y:06 P:25 SP:FB CYC:144 SL:201 +F5E3 EA NOP A:11 X:02 Y:06 P:25 SP:FB CYC:150 SL:201 +F5E4 EA NOP A:11 X:02 Y:06 P:25 SP:FB CYC:156 SL:201 +F5E5 EA NOP A:11 X:02 Y:06 P:25 SP:FB CYC:162 SL:201 +F5E6 20 10 FB JSR $FB10 A:11 X:02 Y:06 P:25 SP:FB CYC:168 SL:201 +FB10 70 09 BVS $FB1B A:11 X:02 Y:06 P:25 SP:F9 CYC:186 SL:201 +FB12 30 07 BMI $FB1B A:11 X:02 Y:06 P:25 SP:F9 CYC:192 SL:201 +FB14 90 05 BCC $FB1B A:11 X:02 Y:06 P:25 SP:F9 CYC:198 SL:201 +FB16 C9 11 CMP #$11 A:11 X:02 Y:06 P:25 SP:F9 CYC:204 SL:201 +FB18 D0 01 BNE $FB1B A:11 X:02 Y:06 P:27 SP:F9 CYC:210 SL:201 +FB1A 60 RTS A:11 X:02 Y:06 P:27 SP:F9 CYC:216 SL:201 +F5E9 A5 47 LDA $47 = 9B A:11 X:02 Y:06 P:27 SP:FB CYC:234 SL:201 +F5EB C9 9B CMP #$9B A:9B X:02 Y:06 P:A5 SP:FB CYC:243 SL:201 +F5ED F0 02 BEQ $F5F1 A:9B X:02 Y:06 P:27 SP:FB CYC:249 SL:201 +F5F1 C8 INY A:9B X:02 Y:06 P:27 SP:FB CYC:258 SL:201 +F5F2 A9 A5 LDA #$A5 A:9B X:02 Y:07 P:25 SP:FB CYC:264 SL:201 +F5F4 8D 47 06 STA $0647 = 9B A:A5 X:02 Y:07 P:A5 SP:FB CYC:270 SL:201 +F5F7 20 E9 FA JSR $FAE9 A:A5 X:02 Y:07 P:A5 SP:FB CYC:282 SL:201 +FAE9 24 01 BIT $01 = FF A:A5 X:02 Y:07 P:A5 SP:F9 CYC:300 SL:201 +FAEB 18 CLC A:A5 X:02 Y:07 P:E5 SP:F9 CYC:309 SL:201 +FAEC A9 B2 LDA #$B2 A:A5 X:02 Y:07 P:E4 SP:F9 CYC:315 SL:201 +FAEE 60 RTS A:B2 X:02 Y:07 P:E4 SP:F9 CYC:321 SL:201 +F5FA 6F 47 06 *RRA $0647 = A5 A:B2 X:02 Y:07 P:E4 SP:FB CYC:339 SL:201 +F5FD EA NOP A:05 X:02 Y:07 P:25 SP:FB CYC: 16 SL:202 +F5FE EA NOP A:05 X:02 Y:07 P:25 SP:FB CYC: 22 SL:202 +F5FF EA NOP A:05 X:02 Y:07 P:25 SP:FB CYC: 28 SL:202 +F600 EA NOP A:05 X:02 Y:07 P:25 SP:FB CYC: 34 SL:202 +F601 20 EF FA JSR $FAEF A:05 X:02 Y:07 P:25 SP:FB CYC: 40 SL:202 +FAEF 70 2A BVS $FB1B A:05 X:02 Y:07 P:25 SP:F9 CYC: 58 SL:202 +FAF1 90 28 BCC $FB1B A:05 X:02 Y:07 P:25 SP:F9 CYC: 64 SL:202 +FAF3 30 26 BMI $FB1B A:05 X:02 Y:07 P:25 SP:F9 CYC: 70 SL:202 +FAF5 C9 05 CMP #$05 A:05 X:02 Y:07 P:25 SP:F9 CYC: 76 SL:202 +FAF7 D0 22 BNE $FB1B A:05 X:02 Y:07 P:27 SP:F9 CYC: 82 SL:202 +FAF9 60 RTS A:05 X:02 Y:07 P:27 SP:F9 CYC: 88 SL:202 +F604 AD 47 06 LDA $0647 = 52 A:05 X:02 Y:07 P:27 SP:FB CYC:106 SL:202 +F607 C9 52 CMP #$52 A:52 X:02 Y:07 P:25 SP:FB CYC:118 SL:202 +F609 F0 02 BEQ $F60D A:52 X:02 Y:07 P:27 SP:FB CYC:124 SL:202 +F60D C8 INY A:52 X:02 Y:07 P:27 SP:FB CYC:133 SL:202 +F60E A9 29 LDA #$29 A:52 X:02 Y:08 P:25 SP:FB CYC:139 SL:202 +F610 8D 47 06 STA $0647 = 52 A:29 X:02 Y:08 P:25 SP:FB CYC:145 SL:202 +F613 20 FA FA JSR $FAFA A:29 X:02 Y:08 P:25 SP:FB CYC:157 SL:202 +FAFA B8 CLV A:29 X:02 Y:08 P:25 SP:F9 CYC:175 SL:202 +FAFB 18 CLC A:29 X:02 Y:08 P:25 SP:F9 CYC:181 SL:202 +FAFC A9 42 LDA #$42 A:29 X:02 Y:08 P:24 SP:F9 CYC:187 SL:202 +FAFE 60 RTS A:42 X:02 Y:08 P:24 SP:F9 CYC:193 SL:202 +F616 6F 47 06 *RRA $0647 = 29 A:42 X:02 Y:08 P:24 SP:FB CYC:211 SL:202 +F619 EA NOP A:57 X:02 Y:08 P:24 SP:FB CYC:229 SL:202 +F61A EA NOP A:57 X:02 Y:08 P:24 SP:FB CYC:235 SL:202 +F61B EA NOP A:57 X:02 Y:08 P:24 SP:FB CYC:241 SL:202 +F61C EA NOP A:57 X:02 Y:08 P:24 SP:FB CYC:247 SL:202 +F61D 20 FF FA JSR $FAFF A:57 X:02 Y:08 P:24 SP:FB CYC:253 SL:202 +FAFF 70 1A BVS $FB1B A:57 X:02 Y:08 P:24 SP:F9 CYC:271 SL:202 +FB01 30 18 BMI $FB1B A:57 X:02 Y:08 P:24 SP:F9 CYC:277 SL:202 +FB03 B0 16 BCS $FB1B A:57 X:02 Y:08 P:24 SP:F9 CYC:283 SL:202 +FB05 C9 57 CMP #$57 A:57 X:02 Y:08 P:24 SP:F9 CYC:289 SL:202 +FB07 D0 12 BNE $FB1B A:57 X:02 Y:08 P:27 SP:F9 CYC:295 SL:202 +FB09 60 RTS A:57 X:02 Y:08 P:27 SP:F9 CYC:301 SL:202 +F620 AD 47 06 LDA $0647 = 14 A:57 X:02 Y:08 P:27 SP:FB CYC:319 SL:202 +F623 C9 14 CMP #$14 A:14 X:02 Y:08 P:25 SP:FB CYC:331 SL:202 +F625 F0 02 BEQ $F629 A:14 X:02 Y:08 P:27 SP:FB CYC:337 SL:202 +F629 C8 INY A:14 X:02 Y:08 P:27 SP:FB CYC: 5 SL:203 +F62A A9 37 LDA #$37 A:14 X:02 Y:09 P:25 SP:FB CYC: 11 SL:203 +F62C 8D 47 06 STA $0647 = 14 A:37 X:02 Y:09 P:25 SP:FB CYC: 17 SL:203 +F62F 20 0A FB JSR $FB0A A:37 X:02 Y:09 P:25 SP:FB CYC: 29 SL:203 +FB0A 24 01 BIT $01 = FF A:37 X:02 Y:09 P:25 SP:F9 CYC: 47 SL:203 +FB0C 38 SEC A:37 X:02 Y:09 P:E5 SP:F9 CYC: 56 SL:203 +FB0D A9 75 LDA #$75 A:37 X:02 Y:09 P:E5 SP:F9 CYC: 62 SL:203 +FB0F 60 RTS A:75 X:02 Y:09 P:65 SP:F9 CYC: 68 SL:203 +F632 6F 47 06 *RRA $0647 = 37 A:75 X:02 Y:09 P:65 SP:FB CYC: 86 SL:203 +F635 EA NOP A:11 X:02 Y:09 P:25 SP:FB CYC:104 SL:203 +F636 EA NOP A:11 X:02 Y:09 P:25 SP:FB CYC:110 SL:203 +F637 EA NOP A:11 X:02 Y:09 P:25 SP:FB CYC:116 SL:203 +F638 EA NOP A:11 X:02 Y:09 P:25 SP:FB CYC:122 SL:203 +F639 20 10 FB JSR $FB10 A:11 X:02 Y:09 P:25 SP:FB CYC:128 SL:203 +FB10 70 09 BVS $FB1B A:11 X:02 Y:09 P:25 SP:F9 CYC:146 SL:203 +FB12 30 07 BMI $FB1B A:11 X:02 Y:09 P:25 SP:F9 CYC:152 SL:203 +FB14 90 05 BCC $FB1B A:11 X:02 Y:09 P:25 SP:F9 CYC:158 SL:203 +FB16 C9 11 CMP #$11 A:11 X:02 Y:09 P:25 SP:F9 CYC:164 SL:203 +FB18 D0 01 BNE $FB1B A:11 X:02 Y:09 P:27 SP:F9 CYC:170 SL:203 +FB1A 60 RTS A:11 X:02 Y:09 P:27 SP:F9 CYC:176 SL:203 +F63C AD 47 06 LDA $0647 = 9B A:11 X:02 Y:09 P:27 SP:FB CYC:194 SL:203 +F63F C9 9B CMP #$9B A:9B X:02 Y:09 P:A5 SP:FB CYC:206 SL:203 +F641 F0 02 BEQ $F645 A:9B X:02 Y:09 P:27 SP:FB CYC:212 SL:203 +F645 A9 A5 LDA #$A5 A:9B X:02 Y:09 P:27 SP:FB CYC:221 SL:203 +F647 8D 47 06 STA $0647 = 9B A:A5 X:02 Y:09 P:A5 SP:FB CYC:227 SL:203 +F64A A9 48 LDA #$48 A:A5 X:02 Y:09 P:A5 SP:FB CYC:239 SL:203 +F64C 85 45 STA $45 = 48 A:48 X:02 Y:09 P:25 SP:FB CYC:245 SL:203 +F64E A9 05 LDA #$05 A:48 X:02 Y:09 P:25 SP:FB CYC:254 SL:203 +F650 85 46 STA $46 = 05 A:05 X:02 Y:09 P:25 SP:FB CYC:260 SL:203 +F652 A0 FF LDY #$FF A:05 X:02 Y:09 P:25 SP:FB CYC:269 SL:203 +F654 20 E9 FA JSR $FAE9 A:05 X:02 Y:FF P:A5 SP:FB CYC:275 SL:203 +FAE9 24 01 BIT $01 = FF A:05 X:02 Y:FF P:A5 SP:F9 CYC:293 SL:203 +FAEB 18 CLC A:05 X:02 Y:FF P:E5 SP:F9 CYC:302 SL:203 +FAEC A9 B2 LDA #$B2 A:05 X:02 Y:FF P:E4 SP:F9 CYC:308 SL:203 +FAEE 60 RTS A:B2 X:02 Y:FF P:E4 SP:F9 CYC:314 SL:203 +F657 73 45 *RRA ($45),Y = 0548 @ 0647 = A5 A:B2 X:02 Y:FF P:E4 SP:FB CYC:332 SL:203 +F659 EA NOP A:05 X:02 Y:FF P:25 SP:FB CYC: 15 SL:204 +F65A EA NOP A:05 X:02 Y:FF P:25 SP:FB CYC: 21 SL:204 +F65B 08 PHP A:05 X:02 Y:FF P:25 SP:FB CYC: 27 SL:204 +F65C 48 PHA A:05 X:02 Y:FF P:25 SP:FA CYC: 36 SL:204 +F65D A0 0A LDY #$0A A:05 X:02 Y:FF P:25 SP:F9 CYC: 45 SL:204 +F65F 68 PLA A:05 X:02 Y:0A P:25 SP:F9 CYC: 51 SL:204 +F660 28 PLP A:05 X:02 Y:0A P:25 SP:FA CYC: 63 SL:204 +F661 20 EF FA JSR $FAEF A:05 X:02 Y:0A P:25 SP:FB CYC: 75 SL:204 +FAEF 70 2A BVS $FB1B A:05 X:02 Y:0A P:25 SP:F9 CYC: 93 SL:204 +FAF1 90 28 BCC $FB1B A:05 X:02 Y:0A P:25 SP:F9 CYC: 99 SL:204 +FAF3 30 26 BMI $FB1B A:05 X:02 Y:0A P:25 SP:F9 CYC:105 SL:204 +FAF5 C9 05 CMP #$05 A:05 X:02 Y:0A P:25 SP:F9 CYC:111 SL:204 +FAF7 D0 22 BNE $FB1B A:05 X:02 Y:0A P:27 SP:F9 CYC:117 SL:204 +FAF9 60 RTS A:05 X:02 Y:0A P:27 SP:F9 CYC:123 SL:204 +F664 AD 47 06 LDA $0647 = 52 A:05 X:02 Y:0A P:27 SP:FB CYC:141 SL:204 +F667 C9 52 CMP #$52 A:52 X:02 Y:0A P:25 SP:FB CYC:153 SL:204 +F669 F0 02 BEQ $F66D A:52 X:02 Y:0A P:27 SP:FB CYC:159 SL:204 +F66D A0 FF LDY #$FF A:52 X:02 Y:0A P:27 SP:FB CYC:168 SL:204 +F66F A9 29 LDA #$29 A:52 X:02 Y:FF P:A5 SP:FB CYC:174 SL:204 +F671 8D 47 06 STA $0647 = 52 A:29 X:02 Y:FF P:25 SP:FB CYC:180 SL:204 +F674 20 FA FA JSR $FAFA A:29 X:02 Y:FF P:25 SP:FB CYC:192 SL:204 +FAFA B8 CLV A:29 X:02 Y:FF P:25 SP:F9 CYC:210 SL:204 +FAFB 18 CLC A:29 X:02 Y:FF P:25 SP:F9 CYC:216 SL:204 +FAFC A9 42 LDA #$42 A:29 X:02 Y:FF P:24 SP:F9 CYC:222 SL:204 +FAFE 60 RTS A:42 X:02 Y:FF P:24 SP:F9 CYC:228 SL:204 +F677 73 45 *RRA ($45),Y = 0548 @ 0647 = 29 A:42 X:02 Y:FF P:24 SP:FB CYC:246 SL:204 +F679 EA NOP A:57 X:02 Y:FF P:24 SP:FB CYC:270 SL:204 +F67A EA NOP A:57 X:02 Y:FF P:24 SP:FB CYC:276 SL:204 +F67B 08 PHP A:57 X:02 Y:FF P:24 SP:FB CYC:282 SL:204 +F67C 48 PHA A:57 X:02 Y:FF P:24 SP:FA CYC:291 SL:204 +F67D A0 0B LDY #$0B A:57 X:02 Y:FF P:24 SP:F9 CYC:300 SL:204 +F67F 68 PLA A:57 X:02 Y:0B P:24 SP:F9 CYC:306 SL:204 +F680 28 PLP A:57 X:02 Y:0B P:24 SP:FA CYC:318 SL:204 +F681 20 FF FA JSR $FAFF A:57 X:02 Y:0B P:24 SP:FB CYC:330 SL:204 +FAFF 70 1A BVS $FB1B A:57 X:02 Y:0B P:24 SP:F9 CYC: 7 SL:205 +FB01 30 18 BMI $FB1B A:57 X:02 Y:0B P:24 SP:F9 CYC: 13 SL:205 +FB03 B0 16 BCS $FB1B A:57 X:02 Y:0B P:24 SP:F9 CYC: 19 SL:205 +FB05 C9 57 CMP #$57 A:57 X:02 Y:0B P:24 SP:F9 CYC: 25 SL:205 +FB07 D0 12 BNE $FB1B A:57 X:02 Y:0B P:27 SP:F9 CYC: 31 SL:205 +FB09 60 RTS A:57 X:02 Y:0B P:27 SP:F9 CYC: 37 SL:205 +F684 AD 47 06 LDA $0647 = 14 A:57 X:02 Y:0B P:27 SP:FB CYC: 55 SL:205 +F687 C9 14 CMP #$14 A:14 X:02 Y:0B P:25 SP:FB CYC: 67 SL:205 +F689 F0 02 BEQ $F68D A:14 X:02 Y:0B P:27 SP:FB CYC: 73 SL:205 +F68D A0 FF LDY #$FF A:14 X:02 Y:0B P:27 SP:FB CYC: 82 SL:205 +F68F A9 37 LDA #$37 A:14 X:02 Y:FF P:A5 SP:FB CYC: 88 SL:205 +F691 8D 47 06 STA $0647 = 14 A:37 X:02 Y:FF P:25 SP:FB CYC: 94 SL:205 +F694 20 0A FB JSR $FB0A A:37 X:02 Y:FF P:25 SP:FB CYC:106 SL:205 +FB0A 24 01 BIT $01 = FF A:37 X:02 Y:FF P:25 SP:F9 CYC:124 SL:205 +FB0C 38 SEC A:37 X:02 Y:FF P:E5 SP:F9 CYC:133 SL:205 +FB0D A9 75 LDA #$75 A:37 X:02 Y:FF P:E5 SP:F9 CYC:139 SL:205 +FB0F 60 RTS A:75 X:02 Y:FF P:65 SP:F9 CYC:145 SL:205 +F697 73 45 *RRA ($45),Y = 0548 @ 0647 = 37 A:75 X:02 Y:FF P:65 SP:FB CYC:163 SL:205 +F699 EA NOP A:11 X:02 Y:FF P:25 SP:FB CYC:187 SL:205 +F69A EA NOP A:11 X:02 Y:FF P:25 SP:FB CYC:193 SL:205 +F69B 08 PHP A:11 X:02 Y:FF P:25 SP:FB CYC:199 SL:205 +F69C 48 PHA A:11 X:02 Y:FF P:25 SP:FA CYC:208 SL:205 +F69D A0 0C LDY #$0C A:11 X:02 Y:FF P:25 SP:F9 CYC:217 SL:205 +F69F 68 PLA A:11 X:02 Y:0C P:25 SP:F9 CYC:223 SL:205 +F6A0 28 PLP A:11 X:02 Y:0C P:25 SP:FA CYC:235 SL:205 +F6A1 20 10 FB JSR $FB10 A:11 X:02 Y:0C P:25 SP:FB CYC:247 SL:205 +FB10 70 09 BVS $FB1B A:11 X:02 Y:0C P:25 SP:F9 CYC:265 SL:205 +FB12 30 07 BMI $FB1B A:11 X:02 Y:0C P:25 SP:F9 CYC:271 SL:205 +FB14 90 05 BCC $FB1B A:11 X:02 Y:0C P:25 SP:F9 CYC:277 SL:205 +FB16 C9 11 CMP #$11 A:11 X:02 Y:0C P:25 SP:F9 CYC:283 SL:205 +FB18 D0 01 BNE $FB1B A:11 X:02 Y:0C P:27 SP:F9 CYC:289 SL:205 +FB1A 60 RTS A:11 X:02 Y:0C P:27 SP:F9 CYC:295 SL:205 +F6A4 AD 47 06 LDA $0647 = 9B A:11 X:02 Y:0C P:27 SP:FB CYC:313 SL:205 +F6A7 C9 9B CMP #$9B A:9B X:02 Y:0C P:A5 SP:FB CYC:325 SL:205 +F6A9 F0 02 BEQ $F6AD A:9B X:02 Y:0C P:27 SP:FB CYC:331 SL:205 +F6AD A0 0D LDY #$0D A:9B X:02 Y:0C P:27 SP:FB CYC:340 SL:205 +F6AF A2 FF LDX #$FF A:9B X:02 Y:0D P:25 SP:FB CYC: 5 SL:206 +F6B1 A9 A5 LDA #$A5 A:9B X:FF Y:0D P:A5 SP:FB CYC: 11 SL:206 +F6B3 85 47 STA $47 = 9B A:A5 X:FF Y:0D P:A5 SP:FB CYC: 17 SL:206 +F6B5 20 E9 FA JSR $FAE9 A:A5 X:FF Y:0D P:A5 SP:FB CYC: 26 SL:206 +FAE9 24 01 BIT $01 = FF A:A5 X:FF Y:0D P:A5 SP:F9 CYC: 44 SL:206 +FAEB 18 CLC A:A5 X:FF Y:0D P:E5 SP:F9 CYC: 53 SL:206 +FAEC A9 B2 LDA #$B2 A:A5 X:FF Y:0D P:E4 SP:F9 CYC: 59 SL:206 +FAEE 60 RTS A:B2 X:FF Y:0D P:E4 SP:F9 CYC: 65 SL:206 +F6B8 77 48 *RRA $48,X @ 47 = A5 A:B2 X:FF Y:0D P:E4 SP:FB CYC: 83 SL:206 +F6BA EA NOP A:05 X:FF Y:0D P:25 SP:FB CYC:101 SL:206 +F6BB EA NOP A:05 X:FF Y:0D P:25 SP:FB CYC:107 SL:206 +F6BC EA NOP A:05 X:FF Y:0D P:25 SP:FB CYC:113 SL:206 +F6BD EA NOP A:05 X:FF Y:0D P:25 SP:FB CYC:119 SL:206 +F6BE 20 EF FA JSR $FAEF A:05 X:FF Y:0D P:25 SP:FB CYC:125 SL:206 +FAEF 70 2A BVS $FB1B A:05 X:FF Y:0D P:25 SP:F9 CYC:143 SL:206 +FAF1 90 28 BCC $FB1B A:05 X:FF Y:0D P:25 SP:F9 CYC:149 SL:206 +FAF3 30 26 BMI $FB1B A:05 X:FF Y:0D P:25 SP:F9 CYC:155 SL:206 +FAF5 C9 05 CMP #$05 A:05 X:FF Y:0D P:25 SP:F9 CYC:161 SL:206 +FAF7 D0 22 BNE $FB1B A:05 X:FF Y:0D P:27 SP:F9 CYC:167 SL:206 +FAF9 60 RTS A:05 X:FF Y:0D P:27 SP:F9 CYC:173 SL:206 +F6C1 A5 47 LDA $47 = 52 A:05 X:FF Y:0D P:27 SP:FB CYC:191 SL:206 +F6C3 C9 52 CMP #$52 A:52 X:FF Y:0D P:25 SP:FB CYC:200 SL:206 +F6C5 F0 02 BEQ $F6C9 A:52 X:FF Y:0D P:27 SP:FB CYC:206 SL:206 +F6C9 C8 INY A:52 X:FF Y:0D P:27 SP:FB CYC:215 SL:206 +F6CA A9 29 LDA #$29 A:52 X:FF Y:0E P:25 SP:FB CYC:221 SL:206 +F6CC 85 47 STA $47 = 52 A:29 X:FF Y:0E P:25 SP:FB CYC:227 SL:206 +F6CE 20 FA FA JSR $FAFA A:29 X:FF Y:0E P:25 SP:FB CYC:236 SL:206 +FAFA B8 CLV A:29 X:FF Y:0E P:25 SP:F9 CYC:254 SL:206 +FAFB 18 CLC A:29 X:FF Y:0E P:25 SP:F9 CYC:260 SL:206 +FAFC A9 42 LDA #$42 A:29 X:FF Y:0E P:24 SP:F9 CYC:266 SL:206 +FAFE 60 RTS A:42 X:FF Y:0E P:24 SP:F9 CYC:272 SL:206 +F6D1 77 48 *RRA $48,X @ 47 = 29 A:42 X:FF Y:0E P:24 SP:FB CYC:290 SL:206 +F6D3 EA NOP A:57 X:FF Y:0E P:24 SP:FB CYC:308 SL:206 +F6D4 EA NOP A:57 X:FF Y:0E P:24 SP:FB CYC:314 SL:206 +F6D5 EA NOP A:57 X:FF Y:0E P:24 SP:FB CYC:320 SL:206 +F6D6 EA NOP A:57 X:FF Y:0E P:24 SP:FB CYC:326 SL:206 +F6D7 20 FF FA JSR $FAFF A:57 X:FF Y:0E P:24 SP:FB CYC:332 SL:206 +FAFF 70 1A BVS $FB1B A:57 X:FF Y:0E P:24 SP:F9 CYC: 9 SL:207 +FB01 30 18 BMI $FB1B A:57 X:FF Y:0E P:24 SP:F9 CYC: 15 SL:207 +FB03 B0 16 BCS $FB1B A:57 X:FF Y:0E P:24 SP:F9 CYC: 21 SL:207 +FB05 C9 57 CMP #$57 A:57 X:FF Y:0E P:24 SP:F9 CYC: 27 SL:207 +FB07 D0 12 BNE $FB1B A:57 X:FF Y:0E P:27 SP:F9 CYC: 33 SL:207 +FB09 60 RTS A:57 X:FF Y:0E P:27 SP:F9 CYC: 39 SL:207 +F6DA A5 47 LDA $47 = 14 A:57 X:FF Y:0E P:27 SP:FB CYC: 57 SL:207 +F6DC C9 14 CMP #$14 A:14 X:FF Y:0E P:25 SP:FB CYC: 66 SL:207 +F6DE F0 02 BEQ $F6E2 A:14 X:FF Y:0E P:27 SP:FB CYC: 72 SL:207 +F6E2 C8 INY A:14 X:FF Y:0E P:27 SP:FB CYC: 81 SL:207 +F6E3 A9 37 LDA #$37 A:14 X:FF Y:0F P:25 SP:FB CYC: 87 SL:207 +F6E5 85 47 STA $47 = 14 A:37 X:FF Y:0F P:25 SP:FB CYC: 93 SL:207 +F6E7 20 0A FB JSR $FB0A A:37 X:FF Y:0F P:25 SP:FB CYC:102 SL:207 +FB0A 24 01 BIT $01 = FF A:37 X:FF Y:0F P:25 SP:F9 CYC:120 SL:207 +FB0C 38 SEC A:37 X:FF Y:0F P:E5 SP:F9 CYC:129 SL:207 +FB0D A9 75 LDA #$75 A:37 X:FF Y:0F P:E5 SP:F9 CYC:135 SL:207 +FB0F 60 RTS A:75 X:FF Y:0F P:65 SP:F9 CYC:141 SL:207 +F6EA 77 48 *RRA $48,X @ 47 = 37 A:75 X:FF Y:0F P:65 SP:FB CYC:159 SL:207 +F6EC EA NOP A:11 X:FF Y:0F P:25 SP:FB CYC:177 SL:207 +F6ED EA NOP A:11 X:FF Y:0F P:25 SP:FB CYC:183 SL:207 +F6EE EA NOP A:11 X:FF Y:0F P:25 SP:FB CYC:189 SL:207 +F6EF EA NOP A:11 X:FF Y:0F P:25 SP:FB CYC:195 SL:207 +F6F0 20 10 FB JSR $FB10 A:11 X:FF Y:0F P:25 SP:FB CYC:201 SL:207 +FB10 70 09 BVS $FB1B A:11 X:FF Y:0F P:25 SP:F9 CYC:219 SL:207 +FB12 30 07 BMI $FB1B A:11 X:FF Y:0F P:25 SP:F9 CYC:225 SL:207 +FB14 90 05 BCC $FB1B A:11 X:FF Y:0F P:25 SP:F9 CYC:231 SL:207 +FB16 C9 11 CMP #$11 A:11 X:FF Y:0F P:25 SP:F9 CYC:237 SL:207 +FB18 D0 01 BNE $FB1B A:11 X:FF Y:0F P:27 SP:F9 CYC:243 SL:207 +FB1A 60 RTS A:11 X:FF Y:0F P:27 SP:F9 CYC:249 SL:207 +F6F3 A5 47 LDA $47 = 9B A:11 X:FF Y:0F P:27 SP:FB CYC:267 SL:207 +F6F5 C9 9B CMP #$9B A:9B X:FF Y:0F P:A5 SP:FB CYC:276 SL:207 +F6F7 F0 02 BEQ $F6FB A:9B X:FF Y:0F P:27 SP:FB CYC:282 SL:207 +F6FB A9 A5 LDA #$A5 A:9B X:FF Y:0F P:27 SP:FB CYC:291 SL:207 +F6FD 8D 47 06 STA $0647 = 9B A:A5 X:FF Y:0F P:A5 SP:FB CYC:297 SL:207 +F700 A0 FF LDY #$FF A:A5 X:FF Y:0F P:A5 SP:FB CYC:309 SL:207 +F702 20 E9 FA JSR $FAE9 A:A5 X:FF Y:FF P:A5 SP:FB CYC:315 SL:207 +FAE9 24 01 BIT $01 = FF A:A5 X:FF Y:FF P:A5 SP:F9 CYC:333 SL:207 +FAEB 18 CLC A:A5 X:FF Y:FF P:E5 SP:F9 CYC: 1 SL:208 +FAEC A9 B2 LDA #$B2 A:A5 X:FF Y:FF P:E4 SP:F9 CYC: 7 SL:208 +FAEE 60 RTS A:B2 X:FF Y:FF P:E4 SP:F9 CYC: 13 SL:208 +F705 7B 48 05 *RRA $0548,Y @ 0647 = A5 A:B2 X:FF Y:FF P:E4 SP:FB CYC: 31 SL:208 +F708 EA NOP A:05 X:FF Y:FF P:25 SP:FB CYC: 52 SL:208 +F709 EA NOP A:05 X:FF Y:FF P:25 SP:FB CYC: 58 SL:208 +F70A 08 PHP A:05 X:FF Y:FF P:25 SP:FB CYC: 64 SL:208 +F70B 48 PHA A:05 X:FF Y:FF P:25 SP:FA CYC: 73 SL:208 +F70C A0 10 LDY #$10 A:05 X:FF Y:FF P:25 SP:F9 CYC: 82 SL:208 +F70E 68 PLA A:05 X:FF Y:10 P:25 SP:F9 CYC: 88 SL:208 +F70F 28 PLP A:05 X:FF Y:10 P:25 SP:FA CYC:100 SL:208 +F710 20 EF FA JSR $FAEF A:05 X:FF Y:10 P:25 SP:FB CYC:112 SL:208 +FAEF 70 2A BVS $FB1B A:05 X:FF Y:10 P:25 SP:F9 CYC:130 SL:208 +FAF1 90 28 BCC $FB1B A:05 X:FF Y:10 P:25 SP:F9 CYC:136 SL:208 +FAF3 30 26 BMI $FB1B A:05 X:FF Y:10 P:25 SP:F9 CYC:142 SL:208 +FAF5 C9 05 CMP #$05 A:05 X:FF Y:10 P:25 SP:F9 CYC:148 SL:208 +FAF7 D0 22 BNE $FB1B A:05 X:FF Y:10 P:27 SP:F9 CYC:154 SL:208 +FAF9 60 RTS A:05 X:FF Y:10 P:27 SP:F9 CYC:160 SL:208 +F713 AD 47 06 LDA $0647 = 52 A:05 X:FF Y:10 P:27 SP:FB CYC:178 SL:208 +F716 C9 52 CMP #$52 A:52 X:FF Y:10 P:25 SP:FB CYC:190 SL:208 +F718 F0 02 BEQ $F71C A:52 X:FF Y:10 P:27 SP:FB CYC:196 SL:208 +F71C A0 FF LDY #$FF A:52 X:FF Y:10 P:27 SP:FB CYC:205 SL:208 +F71E A9 29 LDA #$29 A:52 X:FF Y:FF P:A5 SP:FB CYC:211 SL:208 +F720 8D 47 06 STA $0647 = 52 A:29 X:FF Y:FF P:25 SP:FB CYC:217 SL:208 +F723 20 FA FA JSR $FAFA A:29 X:FF Y:FF P:25 SP:FB CYC:229 SL:208 +FAFA B8 CLV A:29 X:FF Y:FF P:25 SP:F9 CYC:247 SL:208 +FAFB 18 CLC A:29 X:FF Y:FF P:25 SP:F9 CYC:253 SL:208 +FAFC A9 42 LDA #$42 A:29 X:FF Y:FF P:24 SP:F9 CYC:259 SL:208 +FAFE 60 RTS A:42 X:FF Y:FF P:24 SP:F9 CYC:265 SL:208 +F726 7B 48 05 *RRA $0548,Y @ 0647 = 29 A:42 X:FF Y:FF P:24 SP:FB CYC:283 SL:208 +F729 EA NOP A:57 X:FF Y:FF P:24 SP:FB CYC:304 SL:208 +F72A EA NOP A:57 X:FF Y:FF P:24 SP:FB CYC:310 SL:208 +F72B 08 PHP A:57 X:FF Y:FF P:24 SP:FB CYC:316 SL:208 +F72C 48 PHA A:57 X:FF Y:FF P:24 SP:FA CYC:325 SL:208 +F72D A0 11 LDY #$11 A:57 X:FF Y:FF P:24 SP:F9 CYC:334 SL:208 +F72F 68 PLA A:57 X:FF Y:11 P:24 SP:F9 CYC:340 SL:208 +F730 28 PLP A:57 X:FF Y:11 P:24 SP:FA CYC: 11 SL:209 +F731 20 FF FA JSR $FAFF A:57 X:FF Y:11 P:24 SP:FB CYC: 23 SL:209 +FAFF 70 1A BVS $FB1B A:57 X:FF Y:11 P:24 SP:F9 CYC: 41 SL:209 +FB01 30 18 BMI $FB1B A:57 X:FF Y:11 P:24 SP:F9 CYC: 47 SL:209 +FB03 B0 16 BCS $FB1B A:57 X:FF Y:11 P:24 SP:F9 CYC: 53 SL:209 +FB05 C9 57 CMP #$57 A:57 X:FF Y:11 P:24 SP:F9 CYC: 59 SL:209 +FB07 D0 12 BNE $FB1B A:57 X:FF Y:11 P:27 SP:F9 CYC: 65 SL:209 +FB09 60 RTS A:57 X:FF Y:11 P:27 SP:F9 CYC: 71 SL:209 +F734 AD 47 06 LDA $0647 = 14 A:57 X:FF Y:11 P:27 SP:FB CYC: 89 SL:209 +F737 C9 14 CMP #$14 A:14 X:FF Y:11 P:25 SP:FB CYC:101 SL:209 +F739 F0 02 BEQ $F73D A:14 X:FF Y:11 P:27 SP:FB CYC:107 SL:209 +F73D A0 FF LDY #$FF A:14 X:FF Y:11 P:27 SP:FB CYC:116 SL:209 +F73F A9 37 LDA #$37 A:14 X:FF Y:FF P:A5 SP:FB CYC:122 SL:209 +F741 8D 47 06 STA $0647 = 14 A:37 X:FF Y:FF P:25 SP:FB CYC:128 SL:209 +F744 20 0A FB JSR $FB0A A:37 X:FF Y:FF P:25 SP:FB CYC:140 SL:209 +FB0A 24 01 BIT $01 = FF A:37 X:FF Y:FF P:25 SP:F9 CYC:158 SL:209 +FB0C 38 SEC A:37 X:FF Y:FF P:E5 SP:F9 CYC:167 SL:209 +FB0D A9 75 LDA #$75 A:37 X:FF Y:FF P:E5 SP:F9 CYC:173 SL:209 +FB0F 60 RTS A:75 X:FF Y:FF P:65 SP:F9 CYC:179 SL:209 +F747 7B 48 05 *RRA $0548,Y @ 0647 = 37 A:75 X:FF Y:FF P:65 SP:FB CYC:197 SL:209 +F74A EA NOP A:11 X:FF Y:FF P:25 SP:FB CYC:218 SL:209 +F74B EA NOP A:11 X:FF Y:FF P:25 SP:FB CYC:224 SL:209 +F74C 08 PHP A:11 X:FF Y:FF P:25 SP:FB CYC:230 SL:209 +F74D 48 PHA A:11 X:FF Y:FF P:25 SP:FA CYC:239 SL:209 +F74E A0 12 LDY #$12 A:11 X:FF Y:FF P:25 SP:F9 CYC:248 SL:209 +F750 68 PLA A:11 X:FF Y:12 P:25 SP:F9 CYC:254 SL:209 +F751 28 PLP A:11 X:FF Y:12 P:25 SP:FA CYC:266 SL:209 +F752 20 10 FB JSR $FB10 A:11 X:FF Y:12 P:25 SP:FB CYC:278 SL:209 +FB10 70 09 BVS $FB1B A:11 X:FF Y:12 P:25 SP:F9 CYC:296 SL:209 +FB12 30 07 BMI $FB1B A:11 X:FF Y:12 P:25 SP:F9 CYC:302 SL:209 +FB14 90 05 BCC $FB1B A:11 X:FF Y:12 P:25 SP:F9 CYC:308 SL:209 +FB16 C9 11 CMP #$11 A:11 X:FF Y:12 P:25 SP:F9 CYC:314 SL:209 +FB18 D0 01 BNE $FB1B A:11 X:FF Y:12 P:27 SP:F9 CYC:320 SL:209 +FB1A 60 RTS A:11 X:FF Y:12 P:27 SP:F9 CYC:326 SL:209 +F755 AD 47 06 LDA $0647 = 9B A:11 X:FF Y:12 P:27 SP:FB CYC: 3 SL:210 +F758 C9 9B CMP #$9B A:9B X:FF Y:12 P:A5 SP:FB CYC: 15 SL:210 +F75A F0 02 BEQ $F75E A:9B X:FF Y:12 P:27 SP:FB CYC: 21 SL:210 +F75E A0 13 LDY #$13 A:9B X:FF Y:12 P:27 SP:FB CYC: 30 SL:210 +F760 A2 FF LDX #$FF A:9B X:FF Y:13 P:25 SP:FB CYC: 36 SL:210 +F762 A9 A5 LDA #$A5 A:9B X:FF Y:13 P:A5 SP:FB CYC: 42 SL:210 +F764 8D 47 06 STA $0647 = 9B A:A5 X:FF Y:13 P:A5 SP:FB CYC: 48 SL:210 +F767 20 E9 FA JSR $FAE9 A:A5 X:FF Y:13 P:A5 SP:FB CYC: 60 SL:210 +FAE9 24 01 BIT $01 = FF A:A5 X:FF Y:13 P:A5 SP:F9 CYC: 78 SL:210 +FAEB 18 CLC A:A5 X:FF Y:13 P:E5 SP:F9 CYC: 87 SL:210 +FAEC A9 B2 LDA #$B2 A:A5 X:FF Y:13 P:E4 SP:F9 CYC: 93 SL:210 +FAEE 60 RTS A:B2 X:FF Y:13 P:E4 SP:F9 CYC: 99 SL:210 +F76A 7F 48 05 *RRA $0548,X @ 0647 = A5 A:B2 X:FF Y:13 P:E4 SP:FB CYC:117 SL:210 +F76D EA NOP A:05 X:FF Y:13 P:25 SP:FB CYC:138 SL:210 +F76E EA NOP A:05 X:FF Y:13 P:25 SP:FB CYC:144 SL:210 +F76F EA NOP A:05 X:FF Y:13 P:25 SP:FB CYC:150 SL:210 +F770 EA NOP A:05 X:FF Y:13 P:25 SP:FB CYC:156 SL:210 +F771 20 EF FA JSR $FAEF A:05 X:FF Y:13 P:25 SP:FB CYC:162 SL:210 +FAEF 70 2A BVS $FB1B A:05 X:FF Y:13 P:25 SP:F9 CYC:180 SL:210 +FAF1 90 28 BCC $FB1B A:05 X:FF Y:13 P:25 SP:F9 CYC:186 SL:210 +FAF3 30 26 BMI $FB1B A:05 X:FF Y:13 P:25 SP:F9 CYC:192 SL:210 +FAF5 C9 05 CMP #$05 A:05 X:FF Y:13 P:25 SP:F9 CYC:198 SL:210 +FAF7 D0 22 BNE $FB1B A:05 X:FF Y:13 P:27 SP:F9 CYC:204 SL:210 +FAF9 60 RTS A:05 X:FF Y:13 P:27 SP:F9 CYC:210 SL:210 +F774 AD 47 06 LDA $0647 = 52 A:05 X:FF Y:13 P:27 SP:FB CYC:228 SL:210 +F777 C9 52 CMP #$52 A:52 X:FF Y:13 P:25 SP:FB CYC:240 SL:210 +F779 F0 02 BEQ $F77D A:52 X:FF Y:13 P:27 SP:FB CYC:246 SL:210 +F77D C8 INY A:52 X:FF Y:13 P:27 SP:FB CYC:255 SL:210 +F77E A9 29 LDA #$29 A:52 X:FF Y:14 P:25 SP:FB CYC:261 SL:210 +F780 8D 47 06 STA $0647 = 52 A:29 X:FF Y:14 P:25 SP:FB CYC:267 SL:210 +F783 20 FA FA JSR $FAFA A:29 X:FF Y:14 P:25 SP:FB CYC:279 SL:210 +FAFA B8 CLV A:29 X:FF Y:14 P:25 SP:F9 CYC:297 SL:210 +FAFB 18 CLC A:29 X:FF Y:14 P:25 SP:F9 CYC:303 SL:210 +FAFC A9 42 LDA #$42 A:29 X:FF Y:14 P:24 SP:F9 CYC:309 SL:210 +FAFE 60 RTS A:42 X:FF Y:14 P:24 SP:F9 CYC:315 SL:210 +F786 7F 48 05 *RRA $0548,X @ 0647 = 29 A:42 X:FF Y:14 P:24 SP:FB CYC:333 SL:210 +F789 EA NOP A:57 X:FF Y:14 P:24 SP:FB CYC: 13 SL:211 +F78A EA NOP A:57 X:FF Y:14 P:24 SP:FB CYC: 19 SL:211 +F78B EA NOP A:57 X:FF Y:14 P:24 SP:FB CYC: 25 SL:211 +F78C EA NOP A:57 X:FF Y:14 P:24 SP:FB CYC: 31 SL:211 +F78D 20 FF FA JSR $FAFF A:57 X:FF Y:14 P:24 SP:FB CYC: 37 SL:211 +FAFF 70 1A BVS $FB1B A:57 X:FF Y:14 P:24 SP:F9 CYC: 55 SL:211 +FB01 30 18 BMI $FB1B A:57 X:FF Y:14 P:24 SP:F9 CYC: 61 SL:211 +FB03 B0 16 BCS $FB1B A:57 X:FF Y:14 P:24 SP:F9 CYC: 67 SL:211 +FB05 C9 57 CMP #$57 A:57 X:FF Y:14 P:24 SP:F9 CYC: 73 SL:211 +FB07 D0 12 BNE $FB1B A:57 X:FF Y:14 P:27 SP:F9 CYC: 79 SL:211 +FB09 60 RTS A:57 X:FF Y:14 P:27 SP:F9 CYC: 85 SL:211 +F790 AD 47 06 LDA $0647 = 14 A:57 X:FF Y:14 P:27 SP:FB CYC:103 SL:211 +F793 C9 14 CMP #$14 A:14 X:FF Y:14 P:25 SP:FB CYC:115 SL:211 +F795 F0 02 BEQ $F799 A:14 X:FF Y:14 P:27 SP:FB CYC:121 SL:211 +F799 C8 INY A:14 X:FF Y:14 P:27 SP:FB CYC:130 SL:211 +F79A A9 37 LDA #$37 A:14 X:FF Y:15 P:25 SP:FB CYC:136 SL:211 +F79C 8D 47 06 STA $0647 = 14 A:37 X:FF Y:15 P:25 SP:FB CYC:142 SL:211 +F79F 20 0A FB JSR $FB0A A:37 X:FF Y:15 P:25 SP:FB CYC:154 SL:211 +FB0A 24 01 BIT $01 = FF A:37 X:FF Y:15 P:25 SP:F9 CYC:172 SL:211 +FB0C 38 SEC A:37 X:FF Y:15 P:E5 SP:F9 CYC:181 SL:211 +FB0D A9 75 LDA #$75 A:37 X:FF Y:15 P:E5 SP:F9 CYC:187 SL:211 +FB0F 60 RTS A:75 X:FF Y:15 P:65 SP:F9 CYC:193 SL:211 +F7A2 7F 48 05 *RRA $0548,X @ 0647 = 37 A:75 X:FF Y:15 P:65 SP:FB CYC:211 SL:211 +F7A5 EA NOP A:11 X:FF Y:15 P:25 SP:FB CYC:232 SL:211 +F7A6 EA NOP A:11 X:FF Y:15 P:25 SP:FB CYC:238 SL:211 +F7A7 EA NOP A:11 X:FF Y:15 P:25 SP:FB CYC:244 SL:211 +F7A8 EA NOP A:11 X:FF Y:15 P:25 SP:FB CYC:250 SL:211 +F7A9 20 10 FB JSR $FB10 A:11 X:FF Y:15 P:25 SP:FB CYC:256 SL:211 +FB10 70 09 BVS $FB1B A:11 X:FF Y:15 P:25 SP:F9 CYC:274 SL:211 +FB12 30 07 BMI $FB1B A:11 X:FF Y:15 P:25 SP:F9 CYC:280 SL:211 +FB14 90 05 BCC $FB1B A:11 X:FF Y:15 P:25 SP:F9 CYC:286 SL:211 +FB16 C9 11 CMP #$11 A:11 X:FF Y:15 P:25 SP:F9 CYC:292 SL:211 +FB18 D0 01 BNE $FB1B A:11 X:FF Y:15 P:27 SP:F9 CYC:298 SL:211 +FB1A 60 RTS A:11 X:FF Y:15 P:27 SP:F9 CYC:304 SL:211 +F7AC AD 47 06 LDA $0647 = 9B A:11 X:FF Y:15 P:27 SP:FB CYC:322 SL:211 +F7AF C9 9B CMP #$9B A:9B X:FF Y:15 P:A5 SP:FB CYC:334 SL:211 +F7B1 F0 02 BEQ $F7B5 A:9B X:FF Y:15 P:27 SP:FB CYC:340 SL:211 +F7B5 60 RTS A:9B X:FF Y:15 P:27 SP:FB CYC: 8 SL:212 +C655 A5 00 LDA $00 = 00 A:9B X:FF Y:15 P:27 SP:FD CYC: 26 SL:212 +C657 05 10 ORA $10 = 00 A:00 X:FF Y:15 P:27 SP:FD CYC: 35 SL:212 +C659 05 11 ORA $11 = 00 A:00 X:FF Y:15 P:27 SP:FD CYC: 44 SL:212 +C65B F0 0E BEQ $C66B A:00 X:FF Y:15 P:27 SP:FD CYC: 53 SL:212 +C66B 20 89 C6 JSR $C689 A:00 X:FF Y:15 P:27 SP:FD CYC: 62 SL:212 +C689 A9 02 LDA #$02 A:00 X:FF Y:15 P:27 SP:FB CYC: 80 SL:212 +C68B 8D 15 40 STA $4015 = FF A:02 X:FF Y:15 P:25 SP:FB CYC: 86 SL:212 +C68E A9 3F LDA #$3F A:02 X:FF Y:15 P:25 SP:FB CYC: 98 SL:212 +C690 8D 04 40 STA $4004 = FF A:3F X:FF Y:15 P:25 SP:FB CYC:104 SL:212 +C693 A9 9A LDA #$9A A:3F X:FF Y:15 P:25 SP:FB CYC:116 SL:212 +C695 8D 05 40 STA $4005 = FF A:9A X:FF Y:15 P:A5 SP:FB CYC:122 SL:212 +C698 A9 FF LDA #$FF A:9A X:FF Y:15 P:A5 SP:FB CYC:134 SL:212 +C69A 8D 06 40 STA $4006 = FF A:FF X:FF Y:15 P:A5 SP:FB CYC:140 SL:212 +C69D A9 00 LDA #$00 A:FF X:FF Y:15 P:A5 SP:FB CYC:152 SL:212 +C69F 8D 07 40 STA $4007 = FF A:00 X:FF Y:15 P:27 SP:FB CYC:158 SL:212 +C6A2 60 RTS A:00 X:FF Y:15 P:27 SP:FB CYC:170 SL:212 +C66E 60 RTS A:00 X:FF Y:15 P:27 SP:FD CYC:188 SL:212 diff --git a/other/nestest.nes b/other/nestest.nes new file mode 100644 index 0000000..fc2a88c Binary files /dev/null and b/other/nestest.nes differ diff --git a/other/nestest.txt b/other/nestest.txt new file mode 100644 index 0000000..6c3657c --- /dev/null +++ b/other/nestest.txt @@ -0,0 +1,724 @@ + The ultimate NES CPU test ROM + ----------------------------- + + +V1.00 - 09/06/04 + +By: Kevin Horton + + +--- + + +What it is: + +This here is a pretty much all inclusive test suite for a NES CPU. +It was designed to test almost every combination of flags, instructions, +and registers. Some of these tests are very difficult, and so far, +Nesten and Nesticle failed it. Nintendulator passes, as does a real +NES (naturally). I haven't tested it with any more emualtors yet. + +I attempted to check the states of all flags after most instructions. +For example, CPY and CMP shouldn't affect the overflow flag, while SBC +and ADC should. Likewise, all forms of wrapping ARE tested for- zeropage +wrapping being the tests most emulators fail. + +i.e. + +LDA #001h +LDA 0ffh,X ;indexed zeropage read + +should read the byte from 000h... NOT 0100h. This is because zeropage +instructions cannot cross a page boundary. + +--- + +How to work it good: + +Simply run the .NES ROM on your emulator of choice. You can select a single +test to run, or you can run ALL tests in sequence by selecting the +appropriate option. + +Pressing Select will change pages and allow testing "invalid" opcodes. +Be aware that these will crash alot of emulators Nesten. + +Once a test completes, the result will be "OK" if the test passes, or a +2 digit hex number which indicates a failure of some kind. A list is +provided below for the failure and its cause. For a more detailed reason +for the failure, you should check out the .ASM file included with this +document. + +If the entire page of tests succeeds, "OK" will be displayed next to the +first entry on the page. If one or more tests fails, "Er" will be displayed +instead. + +--- + +NSF player testing: + +This ROM is set up to be usable inside an NSF player. It outputs the +results of the test audially. + +--- + +Emulator authors: + +This test program, when run on "automation", (i.e. set your program counter +to 0c000h) will perform all tests in sequence and shove the results of +the tests into locations 02h and 03h. + +--- + +Final notes: + +The hex numbers shown on the screen (or stored in the above mentioned +memory locations) are of the LAST test that failed in the group tested. +This means, there could be multiple failures in one or more groups. This +wasn't the best solution, but since there are close to 400 tests performed, +any other way wouldn't have had acceptable memory usage. So long as your +emulator bugs are fixed and the numbers are getting smaller, you're doing +good :-) + + +---------------------------------------- + +Test failure codes and what they mean: + +(byte 02h only) + +000h - tests completed successfully + +branch tests +------------ +001h - BCS failed to branch +002h - BCS branched when it shouldn't have +003h - BCC branched when it shouldn't have +004h - BCC failed to branch +005h - BEQ failed to branch +006h - BEQ branched when it shouldn't have +007h - BNE failed to branch +008h - BNE branched when it shouldn't have +009h - BVS failed to branch +00Ah - BVC branched when it shouldn't have +00Bh - BVC failed to branch +00Ch - BVS branched when it shouldn't have +00Dh - BPL failed to branch +00Eh - BPL branched when it shouldn't have +00Fh - BMI failed to branch +010h - BMI branched when it shouldn't have + +flag tests +---------- +011h - PHP/flags failure (bits set) +012h - PHP/flags failure (bits clear) +013h - PHP/flags failure (misc bit states) +014h - PLP/flags failure (misc bit states) +015h - PLP/flags failure (misc bit states) +016h - PHA/PLA failure (PLA didn't affect Z and N properly) +017h - PHA/PLA failure (PLA didn't affect Z and N properly) + +immediate instruction tests +--------------------------- +018h - ORA # failure +019h - ORA # failure +01Ah - AND # failure +01Bh - AND # failure +01Ch - EOR # failure +01Dh - EOR # failure +01Eh - ADC # failure (overflow/carry problems) +01Fh - ADC # failure (decimal mode was turned on) +020h - ADC # failure +021h - ADC # failure +022h - ADC # failure +023h - LDA # failure (didn't set N and Z correctly) +024h - LDA # failure (didn't set N and Z correctly) +025h - CMP # failure (messed up flags) +026h - CMP # failure (messed up flags) +027h - CMP # failure (messed up flags) +028h - CMP # failure (messed up flags) +029h - CMP # failure (messed up flags) +02Ah - CMP # failure (messed up flags) +02Bh - CPY # failure (messed up flags) +02Ch - CPY # failure (messed up flags) +02Dh - CPY # failure (messed up flags) +02Eh - CPY # failure (messed up flags) +02Fh - CPY # failure (messed up flags) +030h - CPY # failure (messed up flags) +031h - CPY # failure (messed up flags) +032h - CPX # failure (messed up flags) +033h - CPX # failure (messed up flags) +034h - CPX # failure (messed up flags) +035h - CPX # failure (messed up flags) +036h - CPX # failure (messed up flags) +037h - CPX # failure (messed up flags) +038h - CPX # failure (messed up flags) +039h - LDX # failure (didn't set N and Z correctly) +03Ah - LDX # failure (didn't set N and Z correctly) +03Bh - LDY # failure (didn't set N and Z correctly) +03Ch - LDY # failure (didn't set N and Z correctly) +03Dh - compare(s) stored the result in a register (whoops!) +071h - SBC # failure +072h - SBC # failure +073h - SBC # failure +074h - SBC # failure +075h - SBC # failure + + +implied instruction tests +------------------------- +03Eh - INX/DEX/INY/DEY did something bad +03Fh - INY/DEY messed up overflow or carry +040h - INX/DEX messed up overflow or carry +041h - TAY did something bad (changed wrong regs, messed up flags) +042h - TAX did something bad (changed wrong regs, messed up flags) +043h - TYA did something bad (changed wrong regs, messed up flags) +044h - TXA did something bad (changed wrong regs, messed up flags) +045h - TXS didn't set flags right, or TSX touched flags and it shouldn't have + +stack tests +----------- +046h - wrong data popped, or data not in right location on stack +047h - JSR didn't work as expected +048h - RTS/JSR shouldn't have affected flags +049h - RTI/RTS didn't work right when return addys/data were manually pushed + +accumulator tests +----------------- +04Ah - LSR A failed +04Bh - ASL A failed +04Ch - ROR A failed +04Dh - ROL A failed + +(indirect,x) tests +------------------ +058h - LDA didn't load the data it expected to load +059h - STA didn't store the data where it was supposed to +05Ah - ORA failure +05Bh - ORA failure +05Ch - AND failure +05Dh - AND failure +05Eh - EOR failure +05Fh - EOR failure +060h - ADC failure +061h - ADC failure +062h - ADC failure +063h - ADC failure +064h - ADC failure +065h - CMP failure +066h - CMP failure +067h - CMP failure +068h - CMP failure +069h - CMP failure +06Ah - CMP failure +06Bh - CMP failure +06Ch - SBC failure +06Dh - SBC failure +06Eh - SBC failure +06Fh - SBC failure +070h - SBC failure + +zeropage tests +-------------- +076h - LDA didn't set the flags properly +077h - STA affected flags it shouldn't +078h - LDY didn't set the flags properly +079h - STY affected flags it shouldn't +07Ah - LDX didn't set the flags properly +07Bh - STX affected flags it shouldn't +07Ch - BIT failure +07Dh - BIT failure +07Eh - ORA failure +07Fh - ORA failure +080h - AND failure +081h - AND failure +082h - EOR failure +083h - EOR failure +084h - ADC failure +085h - ADC failure +086h - ADC failure +087h - ADC failure +088h - ADC failure +089h - CMP failure +08Ah - CMP failure +08Bh - CMP failure +08Ch - CMP failure +08Dh - CMP failure +08Eh - CMP failure +08Fh - CMP failure +090h - SBC failure +091h - SBC failure +092h - SBC failure +093h - SBC failure +094h - SBC failure +095h - CPX failure +096h - CPX failure +097h - CPX failure +098h - CPX failure +099h - CPX failure +09Ah - CPX failure +09Bh - CPX failure +09Ch - CPY failure +09Dh - CPY failure +09Eh - CPY failure +09Fh - CPY failure +0A0h - CPY failure +0A1h - CPY failure +0A2h - CPY failure +0A3h - LSR failure +0A4h - LSR failure +0A5h - ASL failure +0A6h - ASL failure +0A7h - ROL failure +0A8h - ROL failure +0A9h - ROR failure +0AAh - ROR failure +0ABh - INC failure +0ACh - INC failure +0ADh - DEC failure +0AEh - DEC failure +0AFh - DEC failure + +Absolute tests +-------------- +0B0h - LDA didn't set the flags properly +0B1h - STA affected flags it shouldn't +0B2h - LDY didn't set the flags properly +0B3h - STY affected flags it shouldn't +0B4h - LDX didn't set the flags properly +0B5h - STX affected flags it shouldn't +0B6h - BIT failure +0B7h - BIT failure +0B8h - ORA failure +0B9h - ORA failure +0BAh - AND failure +0BBh - AND failure +0BCh - EOR failure +0BDh - EOR failure +0BEh - ADC failure +0BFh - ADC failure +0C0h - ADC failure +0C1h - ADC failure +0C2h - ADC failure +0C3h - CMP failure +0C4h - CMP failure +0C5h - CMP failure +0C6h - CMP failure +0C7h - CMP failure +0C8h - CMP failure +0C9h - CMP failure +0CAh - SBC failure +0CBh - SBC failure +0CCh - SBC failure +0CDh - SBC failure +0CEh - SBC failure +0CFh - CPX failure +0D0h - CPX failure +0D1h - CPX failure +0D2h - CPX failure +0D3h - CPX failure +0D4h - CPX failure +0D5h - CPX failure +0D6h - CPY failure +0D7h - CPY failure +0D8h - CPY failure +0D9h - CPY failure +0DAh - CPY failure +0DBh - CPY failure +0DCh - CPY failure +0DDh - LSR failure +0DEh - LSR failure +0DFh - ASL failure +0E0h - ASL failure +0E1h - ROR failure +0E2h - ROR failure +0E3h - ROL failure +0E4h - ROL failure +0E5h - INC failure +0E6h - INC failure +0E7h - DEC failure +0E8h - DEC failure +0E9h - DEC failure + +(indirect),y tests +------------------ +0EAh - LDA didn't load what it was supposed to +0EBh - read location should've wrapped around ffffh to 0000h +0ECh - should've wrapped zeropage address +0EDh - ORA failure +0EEh - ORA failure +0EFh - AND failure +0F0h - AND failure +0F1h - EOR failure +0F2h - EOR failure +0F3h - ADC failure +0F4h - ADC failure +0F5h - ADC failure +0F6h - ADC failure +0F7h - ADC failure +0F8h - CMP failure +0F9h - CMP failure +0FAh - CMP failure +0FBh - CMP failure +0FCh - CMP failure +0FDh - CMP failure +0FEh - CMP failure + +(error byte location 03h starts here) + +000h - no error, all tests pass +001h - SBC failure +002h - SBC failure +003h - SBC failure +004h - SBC failure +005h - SBC failure +006h - STA failure +007h - JMP () data reading didn't wrap properly (this fails on a 65C02) + +zeropage,x tests +---------------- +008h - LDY,X failure +009h - LDY,X failure +00Ah - STY,X failure +00Bh - ORA failure +00Ch - ORA failure +00Dh - AND failure +00Eh - AND failure +00Fh - EOR failure +010h - EOR failure +011h - ADC failure +012h - ADC failure +013h - ADC failure +014h - ADC failure +015h - ADC failure +016h - CMP failure +017h - CMP failure +018h - CMP failure +019h - CMP failure +01Ah - CMP failure +01Bh - CMP failure +01Ch - CMP failure +01Dh - SBC failure +01Eh - SBC failure +01Fh - SBC failure +020h - SBC failure +021h - SBC failure +022h - LDA failure +023h - LDA failure +024h - STA failure +025h - LSR failure +026h - LSR failure +027h - ASL failure +028h - ASL failure +029h - ROR failure +02Ah - ROR failure +02Bh - ROL failure +02Ch - ROL failure +02Dh - INC failure +02Eh - INC failure +02Fh - DEC failure +030h - DEC failure +031h - DEC failure +032h - LDX,Y failure +033h - LDX,Y failure +034h - STX,Y failure +035h - STX,Y failure + +absolute,y tests +---------------- +036h - LDA failure +037h - LDA failure to wrap properly from ffffh to 0000h +038h - LDA failure, page cross +039h - ORA failure +03Ah - ORA failure +03Bh - AND failure +03Ch - AND failure +03Dh - EOR failure +03Eh - EOR failure +03Fh - ADC failure +040h - ADC failure +041h - ADC failure +042h - ADC failure +043h - ADC failure +044h - CMP failure +045h - CMP failure +046h - CMP failure +047h - CMP failure +048h - CMP failure +049h - CMP failure +04Ah - CMP failure +04Bh - SBC failure +04Ch - SBC failure +04Dh - SBC failure +04Eh - SBC failure +04Fh - SBC failure +050h - STA failure + +absolute,x tests +---------------- +051h - LDY,X failure +052h - LDY,X failure (didn't page cross) +053h - ORA failure +054h - ORA failure +055h - AND failure +056h - AND failure +057h - EOR failure +058h - EOR failure +059h - ADC failure +05Ah - ADC failure +05Bh - ADC failure +05Ch - ADC failure +05Dh - ADC failure +05Eh - CMP failure +05Fh - CMP failure +060h - CMP failure +061h - CMP failure +062h - CMP failure +063h - CMP failure +064h - CMP failure +065h - SBC failure +066h - SBC failure +067h - SBC failure +068h - SBC failure +069h - SBC failure +06Ah - LDA failure +06Bh - LDA failure (didn't page cross) +06Ch - STA failure +06Dh - LSR failure +06Eh - LSR failure +06Fh - ASL failure +070h - ASL failure +071h - ROR failure +072h - ROR failure +073h - ROL failure +074h - ROL failure +075h - INC failure +076h - INC failure +077h - DEC failure +078h - DEC failure +079h - DEC failure +07Ah - LDX,Y failure +07Bh - LDX,Y failure + +------------------------------------ + +Invalid opcode tests... all errors are reported in byte 03h unless +specified. + +NOP - "invalid" opcode tests (error byte 02h) +--------------------------------------------- +04Eh - absolute,X NOPs less than 3 bytes long +04Fh - implied NOPs affects regs/flags +050h - ZP,X NOPs less than 2 bytes long +051h - absolute NOP less than 3 bytes long +052h - ZP NOPs less than 2 bytes long +053h - absolute,X NOPs less than 3 bytes long +054h - implied NOPs affects regs/flags +055h - ZP,X NOPs less than 2 bytes long +056h - absolute NOP less than 3 bytes long +057h - ZP NOPs less than 2 bytes long + +LAX - "invalid" opcode tests +---------------------------- +07Ch - LAX (indr,x) failure +07Dh - LAX (indr,x) failure +07Eh - LAX zeropage failure +07Fh - LAX zeropage failure +080h - LAX absolute failure +081h - LAX absolute failure +082h - LAX (indr),y failure +083h - LAX (indr),y failure +084h - LAX zp,y failure +085h - LAX zp,y failure +086h - LAX abs,y failure +087h - LAX abs,y failure + +SAX - "invalid" opcode tests +---------------------------- +088h - SAX (indr,x) failure +089h - SAX (indr,x) failure +08Ah - SAX zeropage failure +08Bh - SAX zeropage failure +08Ch - SAX absolute failure +08Dh - SAX absolute failure +08Eh - SAX zp,y failure +08Fh - SAX zp,y failure + +SBC - "invalid" opcode test +--------------------------- +090h - SBC failure +091h - SBC failure +092h - SBC failure +093h - SBC failure +094h - SBC failure + +DCP - "invalid" opcode tests +---------------------------- +095h - DCP (indr,x) failure +096h - DCP (indr,x) failure +097h - DCP (indr,x) failure +098h - DCP zeropage failure +099h - DCP zeropage failure +09Ah - DCP zeropage failure +09Bh - DCP absolute failure +09Ch - DCP absolute failure +09Dh - DCP absolute failure +09Eh - DCP (indr),y failure +09Fh - DCP (indr),y failure +0A0h - DCP (indr),y failure +0A1h - DCP zp,x failure +0A2h - DCP zp,x failure +0A3h - DCP zp,x failure +0A4h - DCP abs,y failure +0A5h - DCP abs,y failure +0A6h - DCP abs,y failure +0A7h - DCP abs,x failure +0A8h - DCP abs,x failure +0A9h - DCP abs,x failure + +ISB - "invalid" opcode tests +---------------------------- +0AAh - DCP (indr,x) failure +0ABh - DCP (indr,x) failure +0ACh - DCP (indr,x) failure +0ADh - DCP zeropage failure +0AEh - DCP zeropage failure +0AFh - DCP zeropage failure +0B0h - DCP absolute failure +0B1h - DCP absolute failure +0B2h - DCP absolute failure +0B3h - DCP (indr),y failure +0B4h - DCP (indr),y failure +0B5h - DCP (indr),y failure +0B6h - DCP zp,x failure +0B7h - DCP zp,x failure +0B8h - DCP zp,x failure +0B9h - DCP abs,y failure +0BAh - DCP abs,y failure +0BBh - DCP abs,y failure +0BCh - DCP abs,x failure +0BDh - DCP abs,x failure +0BEh - DCP abs,x failure + +SLO - "invalid" opcode tests +---------------------------- +0BFh - SLO (indr,x) failure +0C0h - SLO (indr,x) failure +0C1h - SLO (indr,x) failure +0C2h - SLO zeropage failure +0C3h - SLO zeropage failure +0C4h - SLO zeropage failure +0C5h - SLO absolute failure +0C6h - SLO absolute failure +0C7h - SLO absolute failure +0C8h - SLO (indr),y failure +0C9h - SLO (indr),y failure +0CAh - SLO (indr),y failure +0CBh - SLO zp,x failure +0CCh - SLO zp,x failure +0CDh - SLO zp,x failure +0CEh - SLO abs,y failure +0CFh - SLO abs,y failure +0D0h - SLO abs,y failure +0D1h - SLO abs,x failure +0D2h - SLO abs,x failure +0D3h - SLO abs,x failure + +RLA - "invalid" opcode tests +---------------------------- +0D4h - RLA (indr,x) failure +0D5h - RLA (indr,x) failure +0D6h - RLA (indr,x) failure +0D7h - RLA zeropage failure +0D8h - RLA zeropage failure +0D9h - RLA zeropage failure +0DAh - RLA absolute failure +0DBh - RLA absolute failure +0DCh - RLA absolute failure +0DDh - RLA (indr),y failure +0DEh - RLA (indr),y failure +0DFh - RLA (indr),y failure +0E0h - RLA zp,x failure +0E1h - RLA zp,x failure +0E2h - RLA zp,x failure +0E3h - RLA abs,y failure +0E4h - RLA abs,y failure +0E5h - RLA abs,y failure +0E6h - RLA abs,x failure +0E7h - RLA abs,x failure +0E8h - RLA abs,x failure + +SRE - "invalid" opcode tests +---------------------------- +0E8h - SRE (indr,x) failure +0EAh - SRE (indr,x) failure +0EBh - SRE (indr,x) failure +0ECh - SRE zeropage failure +0EDh - SRE zeropage failure +0EEh - SRE zeropage failure +0EFh - SRE absolute failure +0F0h - SRE absolute failure +0F1h - SRE absolute failure +0F2h - SRE (indr),y failure +0F3h - SRE (indr),y failure +0F4h - SRE (indr),y failure +0F5h - SRE zp,x failure +0F6h - SRE zp,x failure +0F7h - SRE zp,x failure +0F8h - SRE abs,y failure +0F9h - SRE abs,y failure +0FAh - SRE abs,y failure +0FBh - SRE abs,x failure +0FCh - SRE abs,x failure +0FDh - SRE abs,x failure + + +RRA - "invalid" opcode tests +---------------------------- +001h - RRA (indr,x) failure +002h - RRA (indr,x) failure +003h - RRA (indr,x) failure +004h - RRA zeropage failure +005h - RRA zeropage failure +006h - RRA zeropage failure +007h - RRA absolute failure +008h - RRA absolute failure +009h - RRA absolute failure +00Ah - RRA (indr),y failure +00Bh - RRA (indr),y failure +00Ch - RRA (indr),y failure +00Dh - RRA zp,x failure +00Eh - RRA zp,x failure +00Fh - RRA zp,x failure +010h - RRA abs,y failure +011h - RRA abs,y failure +012h - RRA abs,y failure +013h - RRA abs,x failure +014h - RRA abs,x failure +015h - RRA abs,x failure + + + + + + + + +001h - +002h - +003h - +004h - +005h - +006h - +007h - +008h - +009h - +00Ah - +00Bh - +00Ch - +00Dh - +00Eh - +00Fh - +010h - + + +Todo: check to see if decimal mode is missing on CPU diff --git a/other/nestopia.nes b/other/nestopia.nes new file mode 100644 index 0000000..2f621f1 Binary files /dev/null and b/other/nestopia.nes differ diff --git a/other/new-game.nes b/other/new-game.nes new file mode 100644 index 0000000..6034fa3 Binary files /dev/null and b/other/new-game.nes differ diff --git a/other/nintendulator.nes b/other/nintendulator.nes new file mode 100644 index 0000000..c55ded4 Binary files /dev/null and b/other/nintendulator.nes differ diff --git a/other/physics.0.1.nes b/other/physics.0.1.nes new file mode 100644 index 0000000..5f0dcd4 Binary files /dev/null and b/other/physics.0.1.nes differ diff --git a/other/pulsar.nes b/other/pulsar.nes new file mode 100644 index 0000000..62a2995 Binary files /dev/null and b/other/pulsar.nes differ diff --git a/other/rastesam4.nes b/other/rastesam4.nes new file mode 100644 index 0000000..44df4f1 Binary files /dev/null and b/other/rastesam4.nes differ diff --git a/other/read2004.nes b/other/read2004.nes new file mode 100644 index 0000000..bbc3488 Binary files /dev/null and b/other/read2004.nes differ diff --git a/other/readme.txt b/other/readme.txt new file mode 100644 index 0000000..27f5c47 --- /dev/null +++ b/other/readme.txt @@ -0,0 +1,15 @@ +up left up +down right down +left left down +right right up +A +B +Start StartGame +Select + +Program KZ-S +Graphics misaki +Special + Thanks Norix + +http://dev.fam.cx/~kz_s/ diff --git a/other/smbdis.asm b/other/smbdis.asm new file mode 100644 index 0000000..be5e1d7 --- /dev/null +++ b/other/smbdis.asm @@ -0,0 +1,16352 @@ +;SMBDIS.ASM - A COMPREHENSIVE SUPER MARIO BROS. DISASSEMBLY +;by doppelganger (doppelheathen@gmail.com) + +;This file is provided for your own use as-is. It will require the character rom data +;and an iNES file header to get it to work. + +;There are so many people I have to thank for this, that taking all the credit for +;myself would be an unforgivable act of arrogance. Without their help this would +;probably not be possible. So I thank all the peeps in the nesdev scene whose insight into +;the 6502 and the NES helped me learn how it works (you guys know who you are, there's no +;way I could have done this without your help), as well as the authors of x816 and SMB +;Utility, and the reverse-engineers who did the original Super Mario Bros. Hacking Project, +;which I compared notes with but did not copy from. Last but certainly not least, I thank +;Nintendo for creating this game and the NES, without which this disassembly would +;only be theory. + +;Assembles with x816. + +;------------------------------------------------------------------------------------- +;DEFINES + +;NES specific hardware defines + +PPU_CTRL_REG1 = $2000 +PPU_CTRL_REG2 = $2001 +PPU_STATUS = $2002 +PPU_SPR_ADDR = $2003 +PPU_SPR_DATA = $2004 +PPU_SCROLL_REG = $2005 +PPU_ADDRESS = $2006 +PPU_DATA = $2007 + +SND_REGISTER = $4000 +SND_SQUARE1_REG = $4000 +SND_SQUARE2_REG = $4004 +SND_TRIANGLE_REG = $4008 +SND_NOISE_REG = $400c +SND_DELTA_REG = $4010 +SND_MASTERCTRL_REG = $4015 + +SPR_DMA = $4014 +JOYPAD_PORT = $4016 +JOYPAD_PORT1 = $4016 +JOYPAD_PORT2 = $4017 + +; GAME SPECIFIC DEFINES + +ObjectOffset = $08 + +FrameCounter = $09 + +SavedJoypadBits = $06fc +SavedJoypad1Bits = $06fc +SavedJoypad2Bits = $06fd +JoypadBitMask = $074a +JoypadOverride = $0758 + +A_B_Buttons = $0a +PreviousA_B_Buttons = $0d +Up_Down_Buttons = $0b +Left_Right_Buttons = $0c + +GameEngineSubroutine = $0e + +Mirror_PPU_CTRL_REG1 = $0778 +Mirror_PPU_CTRL_REG2 = $0779 + +OperMode = $0770 +OperMode_Task = $0772 +ScreenRoutineTask = $073c + +GamePauseStatus = $0776 +GamePauseTimer = $0777 + +DemoAction = $0717 +DemoActionTimer = $0718 + +TimerControl = $0747 +IntervalTimerControl = $077f + +Timers = $0780 +SelectTimer = $0780 +PlayerAnimTimer = $0781 +JumpSwimTimer = $0782 +RunningTimer = $0783 +BlockBounceTimer = $0784 +SideCollisionTimer = $0785 +JumpspringTimer = $0786 +GameTimerCtrlTimer = $0787 +ClimbSideTimer = $0789 +EnemyFrameTimer = $078a +FrenzyEnemyTimer = $078f +BowserFireBreathTimer = $0790 +StompTimer = $0791 +AirBubbleTimer = $0792 +ScrollIntervalTimer = $0795 +EnemyIntervalTimer = $0796 +BrickCoinTimer = $079d +InjuryTimer = $079e +StarInvincibleTimer = $079f +ScreenTimer = $07a0 +WorldEndTimer = $07a1 +DemoTimer = $07a2 + +Sprite_Data = $0200 + +Sprite_Y_Position = $0200 +Sprite_Tilenumber = $0201 +Sprite_Attributes = $0202 +Sprite_X_Position = $0203 + +ScreenEdge_PageLoc = $071a +ScreenEdge_X_Pos = $071c +ScreenLeft_PageLoc = $071a +ScreenRight_PageLoc = $071b +ScreenLeft_X_Pos = $071c +ScreenRight_X_Pos = $071d + +PlayerFacingDir = $33 +DestinationPageLoc = $34 +VictoryWalkControl = $35 +ScrollFractional = $0768 +PrimaryMsgCounter = $0719 +SecondaryMsgCounter = $0749 + +HorizontalScroll = $073f +VerticalScroll = $0740 +ScrollLock = $0723 +ScrollThirtyTwo = $073d +Player_X_Scroll = $06ff +Player_Pos_ForScroll = $0755 +ScrollAmount = $0775 + +AreaData = $e7 +AreaDataLow = $e7 +AreaDataHigh = $e8 +EnemyData = $e9 +EnemyDataLow = $e9 +EnemyDataHigh = $ea + +AreaParserTaskNum = $071f +ColumnSets = $071e +CurrentPageLoc = $0725 +CurrentColumnPos = $0726 +BackloadingFlag = $0728 +BehindAreaParserFlag = $0729 +AreaObjectPageLoc = $072a +AreaObjectPageSel = $072b +AreaDataOffset = $072c +AreaObjOffsetBuffer = $072d +AreaObjectLength = $0730 +StaircaseControl = $0734 +AreaObjectHeight = $0735 +MushroomLedgeHalfLen = $0736 +EnemyDataOffset = $0739 +EnemyObjectPageLoc = $073a +EnemyObjectPageSel = $073b +MetatileBuffer = $06a1 +BlockBufferColumnPos = $06a0 +CurrentNTAddr_Low = $0721 +CurrentNTAddr_High = $0720 +AttributeBuffer = $03f9 + +LoopCommand = $0745 + +DisplayDigits = $07d7 +TopScoreDisplay = $07d7 +ScoreAndCoinDisplay = $07dd +PlayerScoreDisplay = $07dd +GameTimerDisplay = $07f8 +DigitModifier = $0134 + +VerticalFlipFlag = $0109 +FloateyNum_Control = $0110 +ShellChainCounter = $0125 +FloateyNum_Timer = $012c +FloateyNum_X_Pos = $0117 +FloateyNum_Y_Pos = $011e +FlagpoleFNum_Y_Pos = $010d +FlagpoleFNum_YMFDummy = $010e +FlagpoleScore = $010f +FlagpoleCollisionYPos = $070f +StompChainCounter = $0484 + +VRAM_Buffer1_Offset = $0300 +VRAM_Buffer1 = $0301 +VRAM_Buffer2_Offset = $0340 +VRAM_Buffer2 = $0341 +VRAM_Buffer_AddrCtrl = $0773 +Sprite0HitDetectFlag = $0722 +DisableScreenFlag = $0774 +DisableIntermediate = $0769 +ColorRotateOffset = $06d4 + +TerrainControl = $0727 +AreaStyle = $0733 +ForegroundScenery = $0741 +BackgroundScenery = $0742 +CloudTypeOverride = $0743 +BackgroundColorCtrl = $0744 +AreaType = $074e +AreaAddrsLOffset = $074f +AreaPointer = $0750 + +PlayerEntranceCtrl = $0710 +GameTimerSetting = $0715 +AltEntranceControl = $0752 +EntrancePage = $0751 +NumberOfPlayers = $077a +WarpZoneControl = $06d6 +ChangeAreaTimer = $06de + +MultiLoopCorrectCntr = $06d9 +MultiLoopPassCntr = $06da + +FetchNewGameTimerFlag = $0757 +GameTimerExpiredFlag = $0759 + +PrimaryHardMode = $076a +SecondaryHardMode = $06cc +WorldSelectNumber = $076b +WorldSelectEnableFlag = $07fc +ContinueWorld = $07fd + +CurrentPlayer = $0753 +PlayerSize = $0754 +PlayerStatus = $0756 + +OnscreenPlayerInfo = $075a +NumberofLives = $075a ;used by current player +HalfwayPage = $075b +LevelNumber = $075c ;the actual dash number +Hidden1UpFlag = $075d +CoinTally = $075e +WorldNumber = $075f +AreaNumber = $0760 ;internal number used to find areas + +CoinTallyFor1Ups = $0748 + +OffscreenPlayerInfo = $0761 +OffScr_NumberofLives = $0761 ;used by offscreen player +OffScr_HalfwayPage = $0762 +OffScr_LevelNumber = $0763 +OffScr_Hidden1UpFlag = $0764 +OffScr_CoinTally = $0765 +OffScr_WorldNumber = $0766 +OffScr_AreaNumber = $0767 + +BalPlatformAlignment = $03a0 +Platform_X_Scroll = $03a1 +PlatformCollisionFlag = $03a2 +YPlatformTopYPos = $0401 +YPlatformCenterYPos = $58 + +BrickCoinTimerFlag = $06bc +StarFlagTaskControl = $0746 + +PseudoRandomBitReg = $07a7 +WarmBootValidation = $07ff + +SprShuffleAmtOffset = $06e0 +SprShuffleAmt = $06e1 +SprDataOffset = $06e4 +Player_SprDataOffset = $06e4 +Enemy_SprDataOffset = $06e5 +Block_SprDataOffset = $06ec +Alt_SprDataOffset = $06ec +Bubble_SprDataOffset = $06ee +FBall_SprDataOffset = $06f1 +Misc_SprDataOffset = $06f3 +SprDataOffset_Ctrl = $03ee + +Player_State = $1d +Enemy_State = $1e +Fireball_State = $24 +Block_State = $26 +Misc_State = $2a + +Player_MovingDir = $45 +Enemy_MovingDir = $46 + +SprObject_X_Speed = $57 +Player_X_Speed = $57 +Enemy_X_Speed = $58 +Fireball_X_Speed = $5e +Block_X_Speed = $60 +Misc_X_Speed = $64 + +Jumpspring_FixedYPos = $58 +JumpspringAnimCtrl = $070e +JumpspringForce = $06db + +SprObject_PageLoc = $6d +Player_PageLoc = $6d +Enemy_PageLoc = $6e +Fireball_PageLoc = $74 +Block_PageLoc = $76 +Misc_PageLoc = $7a +Bubble_PageLoc = $83 + +SprObject_X_Position = $86 +Player_X_Position = $86 +Enemy_X_Position = $87 +Fireball_X_Position = $8d +Block_X_Position = $8f +Misc_X_Position = $93 +Bubble_X_Position = $9c + +SprObject_Y_Speed = $9f +Player_Y_Speed = $9f +Enemy_Y_Speed = $a0 +Fireball_Y_Speed = $a6 +Block_Y_Speed = $a8 +Misc_Y_Speed = $ac + +SprObject_Y_HighPos = $b5 +Player_Y_HighPos = $b5 +Enemy_Y_HighPos = $b6 +Fireball_Y_HighPos = $bc +Block_Y_HighPos = $be +Misc_Y_HighPos = $c2 +Bubble_Y_HighPos = $cb + +SprObject_Y_Position = $ce +Player_Y_Position = $ce +Enemy_Y_Position = $cf +Fireball_Y_Position = $d5 +Block_Y_Position = $d7 +Misc_Y_Position = $db +Bubble_Y_Position = $e4 + +SprObject_Rel_XPos = $03ad +Player_Rel_XPos = $03ad +Enemy_Rel_XPos = $03ae +Fireball_Rel_XPos = $03af +Bubble_Rel_XPos = $03b0 +Block_Rel_XPos = $03b1 +Misc_Rel_XPos = $03b3 + +SprObject_Rel_YPos = $03b8 +Player_Rel_YPos = $03b8 +Enemy_Rel_YPos = $03b9 +Fireball_Rel_YPos = $03ba +Bubble_Rel_YPos = $03bb +Block_Rel_YPos = $03bc +Misc_Rel_YPos = $03be + +SprObject_SprAttrib = $03c4 +Player_SprAttrib = $03c4 +Enemy_SprAttrib = $03c5 + +SprObject_X_MoveForce = $0400 +Enemy_X_MoveForce = $0401 + +SprObject_YMF_Dummy = $0416 +Player_YMF_Dummy = $0416 +Enemy_YMF_Dummy = $0417 +Bubble_YMF_Dummy = $042c + +SprObject_Y_MoveForce = $0433 +Player_Y_MoveForce = $0433 +Enemy_Y_MoveForce = $0434 +Block_Y_MoveForce = $043c + +DisableCollisionDet = $0716 +Player_CollisionBits = $0490 +Enemy_CollisionBits = $0491 + +SprObj_BoundBoxCtrl = $0499 +Player_BoundBoxCtrl = $0499 +Enemy_BoundBoxCtrl = $049a +Fireball_BoundBoxCtrl = $04a0 +Misc_BoundBoxCtrl = $04a2 + +EnemyFrenzyBuffer = $06cb +EnemyFrenzyQueue = $06cd +Enemy_Flag = $0f +Enemy_ID = $16 + +PlayerGfxOffset = $06d5 +Player_XSpeedAbsolute = $0700 +FrictionAdderHigh = $0701 +FrictionAdderLow = $0702 +RunningSpeed = $0703 +SwimmingFlag = $0704 +Player_X_MoveForce = $0705 +DiffToHaltJump = $0706 +JumpOrigin_Y_HighPos = $0707 +JumpOrigin_Y_Position = $0708 +VerticalForce = $0709 +VerticalForceDown = $070a +PlayerChangeSizeFlag = $070b +PlayerAnimTimerSet = $070c +PlayerAnimCtrl = $070d +DeathMusicLoaded = $0712 +FlagpoleSoundQueue = $0713 +CrouchingFlag = $0714 +MaximumLeftSpeed = $0450 +MaximumRightSpeed = $0456 + +SprObject_OffscrBits = $03d0 +Player_OffscreenBits = $03d0 +Enemy_OffscreenBits = $03d1 +FBall_OffscreenBits = $03d2 +Bubble_OffscreenBits = $03d3 +Block_OffscreenBits = $03d4 +Misc_OffscreenBits = $03d6 +EnemyOffscrBitsMasked = $03d8 + +Cannon_Offset = $046a +Cannon_PageLoc = $046b +Cannon_X_Position = $0471 +Cannon_Y_Position = $0477 +Cannon_Timer = $047d + +Whirlpool_Offset = $046a +Whirlpool_PageLoc = $046b +Whirlpool_LeftExtent = $0471 +Whirlpool_Length = $0477 +Whirlpool_Flag = $047d + +VineFlagOffset = $0398 +VineHeight = $0399 +VineObjOffset = $039a +VineStart_Y_Position = $039d + +Block_Orig_YPos = $03e4 +Block_BBuf_Low = $03e6 +Block_Metatile = $03e8 +Block_PageLoc2 = $03ea +Block_RepFlag = $03ec +Block_ResidualCounter = $03f0 +Block_Orig_XPos = $03f1 + +BoundingBox_UL_XPos = $04ac +BoundingBox_UL_YPos = $04ad +BoundingBox_DR_XPos = $04ae +BoundingBox_DR_YPos = $04af +BoundingBox_UL_Corner = $04ac +BoundingBox_LR_Corner = $04ae +EnemyBoundingBoxCoord = $04b0 + +PowerUpType = $39 + +FireballBouncingFlag = $3a +FireballCounter = $06ce +FireballThrowingTimer = $0711 + +HammerEnemyOffset = $06ae +JumpCoinMiscOffset = $06b7 + +Block_Buffer_1 = $0500 +Block_Buffer_2 = $05d0 + +HammerThrowingTimer = $03a2 +HammerBroJumpTimer = $3c +Misc_Collision_Flag = $06be + +RedPTroopaOrigXPos = $0401 +RedPTroopaCenterYPos = $58 + +XMovePrimaryCounter = $a0 +XMoveSecondaryCounter = $58 + +CheepCheepMoveMFlag = $58 +CheepCheepOrigYPos = $0434 +BitMFilter = $06dd + +LakituReappearTimer = $06d1 +LakituMoveSpeed = $58 +LakituMoveDirection = $a0 + +FirebarSpinState_Low = $58 +FirebarSpinState_High = $a0 +FirebarSpinSpeed = $0388 +FirebarSpinDirection = $34 + +DuplicateObj_Offset = $06cf +NumberofGroupEnemies = $06d3 + +BlooperMoveCounter = $a0 +BlooperMoveSpeed = $58 + +BowserBodyControls = $0363 +BowserFeetCounter = $0364 +BowserMovementSpeed = $0365 +BowserOrigXPos = $0366 +BowserFlameTimerCtrl = $0367 +BowserFront_Offset = $0368 +BridgeCollapseOffset = $0369 +BowserGfxFlag = $036a +BowserHitPoints = $0483 +MaxRangeFromOrigin = $06dc + +BowserFlamePRandomOfs = $0417 + +PiranhaPlantUpYPos = $0417 +PiranhaPlantDownYPos = $0434 +PiranhaPlant_Y_Speed = $58 +PiranhaPlant_MoveFlag = $a0 + +FireworksCounter = $06d7 +ExplosionGfxCounter = $58 +ExplosionTimerCounter = $a0 + +;sound related defines +Squ2_NoteLenBuffer = $07b3 +Squ2_NoteLenCounter = $07b4 +Squ2_EnvelopeDataCtrl = $07b5 +Squ1_NoteLenCounter = $07b6 +Squ1_EnvelopeDataCtrl = $07b7 +Tri_NoteLenBuffer = $07b8 +Tri_NoteLenCounter = $07b9 +Noise_BeatLenCounter = $07ba +Squ1_SfxLenCounter = $07bb +Squ2_SfxLenCounter = $07bd +Sfx_SecondaryCounter = $07be +Noise_SfxLenCounter = $07bf + +PauseSoundQueue = $fa +Square1SoundQueue = $ff +Square2SoundQueue = $fe +NoiseSoundQueue = $fd +AreaMusicQueue = $fb +EventMusicQueue = $fc + +Square1SoundBuffer = $f1 +Square2SoundBuffer = $f2 +NoiseSoundBuffer = $f3 +AreaMusicBuffer = $f4 +EventMusicBuffer = $07b1 +PauseSoundBuffer = $07b2 + +MusicData = $f5 +MusicDataLow = $f5 +MusicDataHigh = $f6 +MusicOffset_Square2 = $f7 +MusicOffset_Square1 = $f8 +MusicOffset_Triangle = $f9 +MusicOffset_Noise = $07b0 + +NoteLenLookupTblOfs = $f0 +DAC_Counter = $07c0 +NoiseDataLoopbackOfs = $07c1 +NoteLengthTblAdder = $07c4 +AreaMusicBuffer_Alt = $07c5 +PauseModeFlag = $07c6 +GroundMusicHeaderOfs = $07c7 +AltRegContentFlag = $07ca + +;------------------------------------------------------------------------------------- +;CONSTANTS + +;sound effects constants +Sfx_SmallJump = %10000000 +Sfx_Flagpole = %01000000 +Sfx_Fireball = %00100000 +Sfx_PipeDown_Injury = %00010000 +Sfx_EnemySmack = %00001000 +Sfx_EnemyStomp = %00000100 +Sfx_Bump = %00000010 +Sfx_BigJump = %00000001 + +Sfx_BowserFall = %10000000 +Sfx_ExtraLife = %01000000 +Sfx_PowerUpGrab = %00100000 +Sfx_TimerTick = %00010000 +Sfx_Blast = %00001000 +Sfx_GrowVine = %00000100 +Sfx_GrowPowerUp = %00000010 +Sfx_CoinGrab = %00000001 + +Sfx_BowserFlame = %00000010 +Sfx_BrickShatter = %00000001 + +;music constants +Silence = %10000000 + +StarPowerMusic = %01000000 +PipeIntroMusic = %00100000 +CloudMusic = %00010000 +CastleMusic = %00001000 +UndergroundMusic = %00000100 +WaterMusic = %00000010 +GroundMusic = %00000001 + +TimeRunningOutMusic = %01000000 +EndOfLevelMusic = %00100000 +AltGameOverMusic = %00010000 +EndOfCastleMusic = %00001000 +VictoryMusic = %00000100 +GameOverMusic = %00000010 +DeathMusic = %00000001 + +;enemy object constants +GreenKoopa = $00 +BuzzyBeetle = $02 +RedKoopa = $03 +HammerBro = $05 +Goomba = $06 +Bloober = $07 +BulletBill_FrenzyVar = $08 +GreyCheepCheep = $0a +RedCheepCheep = $0b +Podoboo = $0c +PiranhaPlant = $0d +GreenParatroopaJump = $0e +RedParatroopa = $0f +GreenParatroopaFly = $10 +Lakitu = $11 +Spiny = $12 +FlyCheepCheepFrenzy = $14 +FlyingCheepCheep = $14 +BowserFlame = $15 +Fireworks = $16 +BBill_CCheep_Frenzy = $17 +Stop_Frenzy = $18 +Bowser = $2d +PowerUpObject = $2e +VineObject = $2f +FlagpoleFlagObject = $30 +StarFlagObject = $31 +JumpspringObject = $32 +BulletBill_CannonVar = $33 +RetainerObject = $35 +TallEnemy = $09 + +;other constants +World1 = 0 +World2 = 1 +World3 = 2 +World4 = 3 +World5 = 4 +World6 = 5 +World7 = 6 +World8 = 7 +Level1 = 0 +Level2 = 1 +Level3 = 2 +Level4 = 3 + +WarmBootOffset = <$07d6 +ColdBootOffset = <$07fe +TitleScreenDataOffset = $1ec0 +SoundMemory = $07b0 +SwimTileRepOffset = PlayerGraphicsTable + $9e +MusicHeaderOffsetData = MusicHeaderData - 1 +MHD = MusicHeaderData + +A_Button = %10000000 +B_Button = %01000000 +Select_Button = %00100000 +Start_Button = %00010000 +Up_Dir = %00001000 +Down_Dir = %00000100 +Left_Dir = %00000010 +Right_Dir = %00000001 + +TitleScreenModeValue = 0 +GameModeValue = 1 +VictoryModeValue = 2 +GameOverModeValue = 3 + +;------------------------------------------------------------------------------------- +;DIRECTIVES + +; .index 8 +; .mem 8 + + .org $8000 + +;------------------------------------------------------------------------------------- + +Start: + sei ;pretty standard 6502 type init here + cld + lda #%00010000 ;init PPU control register 1 + sta PPU_CTRL_REG1 + ldx #$ff ;reset stack pointer + txs +VBlank1: lda PPU_STATUS ;wait two frames + bpl VBlank1 +VBlank2: lda PPU_STATUS + bpl VBlank2 + ldy #ColdBootOffset ;load default cold boot pointer + ldx #$05 ;this is where we check for a warm boot +WBootCheck: lda TopScoreDisplay,x ;check each score digit in the top score + cmp #10 ;to see if we have a valid digit + bcs ColdBoot ;if not, give up and proceed with cold boot + dex + bpl WBootCheck + lda WarmBootValidation ;second checkpoint, check to see if + cmp #$a5 ;another location has a specific value + bne ColdBoot + ldy #WarmBootOffset ;if passed both, load warm boot pointer +ColdBoot: jsr InitializeMemory ;clear memory using pointer in Y + sta SND_DELTA_REG+1 ;reset delta counter load register + sta OperMode ;reset primary mode of operation + lda #$a5 ;set warm boot flag + sta WarmBootValidation + sta PseudoRandomBitReg ;set seed for pseudorandom register + lda #%00001111 + sta SND_MASTERCTRL_REG ;enable all sound channels except dmc + lda #%00000110 + sta PPU_CTRL_REG2 ;turn off clipping for OAM and background + jsr MoveAllSpritesOffscreen + jsr InitializeNameTables ;initialize both name tables + inc DisableScreenFlag ;set flag to disable screen output + lda Mirror_PPU_CTRL_REG1 + ora #%10000000 ;enable NMIs + jsr WritePPUReg1 +EndlessLoop: jmp EndlessLoop ;endless loop, need I say more? + +;------------------------------------------------------------------------------------- +;$00 - vram buffer address table low, also used for pseudorandom bit +;$01 - vram buffer address table high + +VRAM_AddrTable_Low: + .db VRAM_Buffer1, >WaterPaletteData, >GroundPaletteData + .db >UndergroundPaletteData, >CastlePaletteData, >VRAM_Buffer1_Offset + .db >VRAM_Buffer2, >VRAM_Buffer2, >BowserPaletteData + .db >DaySnowPaletteData, >NightSnowPaletteData, >MushroomPaletteData + .db >MarioThanksMessage, >LuigiThanksMessage, >MushroomRetainerSaved + .db >PrincessSaved1, >PrincessSaved2, >WorldSelectMessage1 + .db >WorldSelectMessage2 + +VRAM_Buffer_Offset: + .db $09 + lda Enemy_State,x + cmp #$02 ;if enemy state defeated or otherwise + bcs FloateyPart ;$02 or greater, branch beyond this part +GetAltOffset: ldx SprDataOffset_Ctrl ;load some kind of control bit + ldy Alt_SprDataOffset,x ;get alternate OAM data offset + ldx ObjectOffset ;get enemy object offset again +FloateyPart: lda FloateyNum_Y_Pos,x ;get vertical coordinate for + cmp #$18 ;floatey number, if coordinate in the + bcc SetupNumSpr ;status bar, branch + sbc #$01 + sta FloateyNum_Y_Pos,x ;otherwise subtract one and store as new +SetupNumSpr: lda FloateyNum_Y_Pos,x ;get vertical coordinate + sbc #$08 ;subtract eight and dump into the + jsr DumpTwoSpr ;left and right sprite's Y coordinates + lda FloateyNum_X_Pos,x ;get horizontal coordinate + sta Sprite_X_Position,y ;store into X coordinate of left sprite + clc + adc #$08 ;add eight pixels and store into X + sta Sprite_X_Position+4,y ;coordinate of right sprite + lda #$02 + sta Sprite_Attributes,y ;set palette control in attribute bytes + sta Sprite_Attributes+4,y ;of left and right sprites + lda FloateyNum_Control,x + asl ;multiply our floatey number control by 2 + tax ;and use as offset for look-up table + lda FloateyNumTileData,x + sta Sprite_Tilenumber,y ;display first half of number of points + lda FloateyNumTileData+1,x + sta Sprite_Tilenumber+4,y ;display the second half + ldx ObjectOffset ;get enemy object offset and leave + rts + +;------------------------------------------------------------------------------------- + +ScreenRoutines: + lda ScreenRoutineTask ;run one of the following subroutines + jsr JumpEngine + + .dw InitScreen + .dw SetupIntermediate + .dw WriteTopStatusLine + .dw WriteBottomStatusLine + .dw DisplayTimeUp + .dw ResetSpritesAndScreenTimer + .dw DisplayIntermediate + .dw ResetSpritesAndScreenTimer + .dw AreaParserTaskControl + .dw GetAreaPalette + .dw GetBackgroundColor + .dw GetAlternatePalette1 + .dw DrawTitleScreen + .dw ClearBuffersDrawIcon + .dw WriteTopScore + +;------------------------------------------------------------------------------------- + +InitScreen: + jsr MoveAllSpritesOffscreen ;initialize all sprites including sprite #0 + jsr InitializeNameTables ;and erase both name and attribute tables + lda OperMode + beq NextSubtask ;if mode still 0, do not load + ldx #$03 ;into buffer pointer + jmp SetVRAMAddr_A + +;------------------------------------------------------------------------------------- + +SetupIntermediate: + lda BackgroundColorCtrl ;save current background color control + pha ;and player status to stack + lda PlayerStatus + pha + lda #$00 ;set background color to black + sta PlayerStatus ;and player status to not fiery + lda #$02 ;this is the ONLY time background color control + sta BackgroundColorCtrl ;is set to less than 4 + jsr GetPlayerColors + pla ;we only execute this routine for + sta PlayerStatus ;the intermediate lives display + pla ;and once we're done, we return bg + sta BackgroundColorCtrl ;color ctrl and player status from stack + jmp IncSubtask ;then move onto the next task + +;------------------------------------------------------------------------------------- + +AreaPalette: + .db $01, $02, $03, $04 + +GetAreaPalette: + ldy AreaType ;select appropriate palette to load + ldx AreaPalette,y ;based on area type +SetVRAMAddr_A: stx VRAM_Buffer_AddrCtrl ;store offset into buffer control +NextSubtask: jmp IncSubtask ;move onto next task + +;------------------------------------------------------------------------------------- +;$00 - used as temp counter in GetPlayerColors + +BGColorCtrl_Addr: + .db $00, $09, $0a, $04 + +BackgroundColors: + .db $22, $22, $0f, $0f ;used by area type if bg color ctrl not set + .db $0f, $22, $0f, $0f ;used by background color control if set + +PlayerColors: + .db $22, $16, $27, $18 ;mario's colors + .db $22, $30, $27, $19 ;luigi's colors + .db $22, $37, $27, $16 ;fiery (used by both) + +GetBackgroundColor: + ldy BackgroundColorCtrl ;check background color control + beq NoBGColor ;if not set, increment task and fetch palette + lda BGColorCtrl_Addr-4,y ;put appropriate palette into vram + sta VRAM_Buffer_AddrCtrl ;note that if set to 5-7, $0301 will not be read +NoBGColor: inc ScreenRoutineTask ;increment to next subtask and plod on through + +GetPlayerColors: + ldx VRAM_Buffer1_Offset ;get current buffer offset + ldy #$00 + lda CurrentPlayer ;check which player is on the screen + beq ChkFiery + ldy #$04 ;load offset for luigi +ChkFiery: lda PlayerStatus ;check player status + cmp #$02 + bne StartClrGet ;if fiery, load alternate offset for fiery player + ldy #$08 +StartClrGet: lda #$03 ;do four colors + sta $00 +ClrGetLoop: lda PlayerColors,y ;fetch player colors and store them + sta VRAM_Buffer1+3,x ;in the buffer + iny + inx + dec $00 + bpl ClrGetLoop + ldx VRAM_Buffer1_Offset ;load original offset from before + ldy BackgroundColorCtrl ;if this value is four or greater, it will be set + bne SetBGColor ;therefore use it as offset to background color + ldy AreaType ;otherwise use area type bits from area offset as offset +SetBGColor: lda BackgroundColors,y ;to background color instead + sta VRAM_Buffer1+3,x + lda #$3f ;set for sprite palette address + sta VRAM_Buffer1,x ;save to buffer + lda #$10 + sta VRAM_Buffer1+1,x + lda #$04 ;write length byte to buffer + sta VRAM_Buffer1+2,x + lda #$00 ;now the null terminator + sta VRAM_Buffer1+7,x + txa ;move the buffer pointer ahead 7 bytes + clc ;in case we want to write anything else later + adc #$07 +SetVRAMOffset: sta VRAM_Buffer1_Offset ;store as new vram buffer offset + rts + +;------------------------------------------------------------------------------------- + +GetAlternatePalette1: + lda AreaStyle ;check for mushroom level style + cmp #$01 + bne NoAltPal + lda #$0b ;if found, load appropriate palette +SetVRAMAddr_B: sta VRAM_Buffer_AddrCtrl +NoAltPal: jmp IncSubtask ;now onto the next task + +;------------------------------------------------------------------------------------- + +WriteTopStatusLine: + lda #$00 ;select main status bar + jsr WriteGameText ;output it + jmp IncSubtask ;onto the next task + +;------------------------------------------------------------------------------------- + +WriteBottomStatusLine: + jsr GetSBNybbles ;write player's score and coin tally to screen + ldx VRAM_Buffer1_Offset + lda #$20 ;write address for world-area number on screen + sta VRAM_Buffer1,x + lda #$73 + sta VRAM_Buffer1+1,x + lda #$03 ;write length for it + sta VRAM_Buffer1+2,x + ldy WorldNumber ;first the world number + iny + tya + sta VRAM_Buffer1+3,x + lda #$28 ;next the dash + sta VRAM_Buffer1+4,x + ldy LevelNumber ;next the level number + iny ;increment for proper number display + tya + sta VRAM_Buffer1+5,x + lda #$00 ;put null terminator on + sta VRAM_Buffer1+6,x + txa ;move the buffer offset up by 6 bytes + clc + adc #$06 + sta VRAM_Buffer1_Offset + jmp IncSubtask + +;------------------------------------------------------------------------------------- + +DisplayTimeUp: + lda GameTimerExpiredFlag ;if game timer not expired, increment task + beq NoTimeUp ;control 2 tasks forward, otherwise, stay here + lda #$00 + sta GameTimerExpiredFlag ;reset timer expiration flag + lda #$02 ;output time-up screen to buffer + jmp OutputInter +NoTimeUp: inc ScreenRoutineTask ;increment control task 2 tasks forward + jmp IncSubtask + +;------------------------------------------------------------------------------------- + +DisplayIntermediate: + lda OperMode ;check primary mode of operation + beq NoInter ;if in title screen mode, skip this + cmp #GameOverModeValue ;are we in game over mode? + beq GameOverInter ;if so, proceed to display game over screen + lda AltEntranceControl ;otherwise check for mode of alternate entry + bne NoInter ;and branch if found + ldy AreaType ;check if we are on castle level + cpy #$03 ;and if so, branch (possibly residual) + beq PlayerInter + lda DisableIntermediate ;if this flag is set, skip intermediate lives display + bne NoInter ;and jump to specific task, otherwise +PlayerInter: jsr DrawPlayer_Intermediate ;put player in appropriate place for + lda #$01 ;lives display, then output lives display to buffer +OutputInter: jsr WriteGameText + jsr ResetScreenTimer + lda #$00 + sta DisableScreenFlag ;reenable screen output + rts +GameOverInter: lda #$12 ;set screen timer + sta ScreenTimer + lda #$03 ;output game over screen to buffer + jsr WriteGameText + jmp IncModeTask_B +NoInter: lda #$08 ;set for specific task and leave + sta ScreenRoutineTask + rts + +;------------------------------------------------------------------------------------- + +AreaParserTaskControl: + inc DisableScreenFlag ;turn off screen +TaskLoop: jsr AreaParserTaskHandler ;render column set of current area + lda AreaParserTaskNum ;check number of tasks + bne TaskLoop ;if tasks still not all done, do another one + dec ColumnSets ;do we need to render more column sets? + bpl OutputCol + inc ScreenRoutineTask ;if not, move on to the next task +OutputCol: lda #$06 ;set vram buffer to output rendered column set + sta VRAM_Buffer_AddrCtrl ;on next NMI + rts + +;------------------------------------------------------------------------------------- + +;$00 - vram buffer address table low +;$01 - vram buffer address table high + +DrawTitleScreen: + lda OperMode ;are we in title screen mode? + bne IncModeTask_B ;if not, exit + lda #>TitleScreenDataOffset ;load address $1ec0 into + sta PPU_ADDRESS ;the vram address register + lda #Palette0_MTiles, >Palette1_MTiles, >Palette2_MTiles, >Palette3_MTiles + +Palette0_MTiles: + .db $24, $24, $24, $24 ;blank + .db $27, $27, $27, $27 ;black metatile + .db $24, $24, $24, $35 ;bush left + .db $36, $25, $37, $25 ;bush middle + .db $24, $38, $24, $24 ;bush right + .db $24, $30, $30, $26 ;mountain left + .db $26, $26, $34, $26 ;mountain left bottom/middle center + .db $24, $31, $24, $32 ;mountain middle top + .db $33, $26, $24, $33 ;mountain right + .db $34, $26, $26, $26 ;mountain right bottom + .db $26, $26, $26, $26 ;mountain middle bottom + .db $24, $c0, $24, $c0 ;bridge guardrail + .db $24, $7f, $7f, $24 ;chain + .db $b8, $ba, $b9, $bb ;tall tree top, top half + .db $b8, $bc, $b9, $bd ;short tree top + .db $ba, $bc, $bb, $bd ;tall tree top, bottom half + .db $60, $64, $61, $65 ;warp pipe end left, points up + .db $62, $66, $63, $67 ;warp pipe end right, points up + .db $60, $64, $61, $65 ;decoration pipe end left, points up + .db $62, $66, $63, $67 ;decoration pipe end right, points up + .db $68, $68, $69, $69 ;pipe shaft left + .db $26, $26, $6a, $6a ;pipe shaft right + .db $4b, $4c, $4d, $4e ;tree ledge left edge + .db $4d, $4f, $4d, $4f ;tree ledge middle + .db $4d, $4e, $50, $51 ;tree ledge right edge + .db $6b, $70, $2c, $2d ;mushroom left edge + .db $6c, $71, $6d, $72 ;mushroom middle + .db $6e, $73, $6f, $74 ;mushroom right edge + .db $86, $8a, $87, $8b ;sideways pipe end top + .db $88, $8c, $88, $8c ;sideways pipe shaft top + .db $89, $8d, $69, $69 ;sideways pipe joint top + .db $8e, $91, $8f, $92 ;sideways pipe end bottom + .db $26, $93, $26, $93 ;sideways pipe shaft bottom + .db $90, $94, $69, $69 ;sideways pipe joint bottom + .db $a4, $e9, $ea, $eb ;seaplant + .db $24, $24, $24, $24 ;blank, used on bricks or blocks that are hit + .db $24, $2f, $24, $3d ;flagpole ball + .db $a2, $a2, $a3, $a3 ;flagpole shaft + .db $24, $24, $24, $24 ;blank, used in conjunction with vines + +Palette1_MTiles: + .db $a2, $a2, $a3, $a3 ;vertical rope + .db $99, $24, $99, $24 ;horizontal rope + .db $24, $a2, $3e, $3f ;left pulley + .db $5b, $5c, $24, $a3 ;right pulley + .db $24, $24, $24, $24 ;blank used for balance rope + .db $9d, $47, $9e, $47 ;castle top + .db $47, $47, $27, $27 ;castle window left + .db $47, $47, $47, $47 ;castle brick wall + .db $27, $27, $47, $47 ;castle window right + .db $a9, $47, $aa, $47 ;castle top w/ brick + .db $9b, $27, $9c, $27 ;entrance top + .db $27, $27, $27, $27 ;entrance bottom + .db $52, $52, $52, $52 ;green ledge stump + .db $80, $a0, $81, $a1 ;fence + .db $be, $be, $bf, $bf ;tree trunk + .db $75, $ba, $76, $bb ;mushroom stump top + .db $ba, $ba, $bb, $bb ;mushroom stump bottom + .db $45, $47, $45, $47 ;breakable brick w/ line + .db $47, $47, $47, $47 ;breakable brick + .db $45, $47, $45, $47 ;breakable brick (not used) + .db $b4, $b6, $b5, $b7 ;cracked rock terrain + .db $45, $47, $45, $47 ;brick with line (power-up) + .db $45, $47, $45, $47 ;brick with line (vine) + .db $45, $47, $45, $47 ;brick with line (star) + .db $45, $47, $45, $47 ;brick with line (coins) + .db $45, $47, $45, $47 ;brick with line (1-up) + .db $47, $47, $47, $47 ;brick (power-up) + .db $47, $47, $47, $47 ;brick (vine) + .db $47, $47, $47, $47 ;brick (star) + .db $47, $47, $47, $47 ;brick (coins) + .db $47, $47, $47, $47 ;brick (1-up) + .db $24, $24, $24, $24 ;hidden block (1 coin) + .db $24, $24, $24, $24 ;hidden block (1-up) + .db $ab, $ac, $ad, $ae ;solid block (3-d block) + .db $5d, $5e, $5d, $5e ;solid block (white wall) + .db $c1, $24, $c1, $24 ;bridge + .db $c6, $c8, $c7, $c9 ;bullet bill cannon barrel + .db $ca, $cc, $cb, $cd ;bullet bill cannon top + .db $2a, $2a, $40, $40 ;bullet bill cannon bottom + .db $24, $24, $24, $24 ;blank used for jumpspring + .db $24, $47, $24, $47 ;half brick used for jumpspring + .db $82, $83, $84, $85 ;solid block (water level, green rock) + .db $24, $47, $24, $47 ;half brick (???) + .db $86, $8a, $87, $8b ;water pipe top + .db $8e, $91, $8f, $92 ;water pipe bottom + .db $24, $2f, $24, $3d ;flag ball (residual object) + +Palette2_MTiles: + .db $24, $24, $24, $35 ;cloud left + .db $36, $25, $37, $25 ;cloud middle + .db $24, $38, $24, $24 ;cloud right + .db $24, $24, $39, $24 ;cloud bottom left + .db $3a, $24, $3b, $24 ;cloud bottom middle + .db $3c, $24, $24, $24 ;cloud bottom right + .db $41, $26, $41, $26 ;water/lava top + .db $26, $26, $26, $26 ;water/lava + .db $b0, $b1, $b2, $b3 ;cloud level terrain + .db $77, $79, $77, $79 ;bowser's bridge + +Palette3_MTiles: + .db $53, $55, $54, $56 ;question block (coin) + .db $53, $55, $54, $56 ;question block (power-up) + .db $a5, $a7, $a6, $a8 ;coin + .db $c2, $c4, $c3, $c5 ;underwater coin + .db $57, $59, $58, $5a ;empty block + .db $7b, $7d, $7c, $7e ;axe + +;------------------------------------------------------------------------------------- +;VRAM BUFFER DATA FOR LOCATIONS IN PRG-ROM + +WaterPaletteData: + .db $3f, $00, $20 + .db $0f, $15, $12, $25 + .db $0f, $3a, $1a, $0f + .db $0f, $30, $12, $0f + .db $0f, $27, $12, $0f + .db $22, $16, $27, $18 + .db $0f, $10, $30, $27 + .db $0f, $16, $30, $27 + .db $0f, $0f, $30, $10 + .db $00 + +GroundPaletteData: + .db $3f, $00, $20 + .db $0f, $29, $1a, $0f + .db $0f, $36, $17, $0f + .db $0f, $30, $21, $0f + .db $0f, $27, $17, $0f + .db $0f, $16, $27, $18 + .db $0f, $1a, $30, $27 + .db $0f, $16, $30, $27 + .db $0f, $0f, $36, $17 + .db $00 + +UndergroundPaletteData: + .db $3f, $00, $20 + .db $0f, $29, $1a, $09 + .db $0f, $3c, $1c, $0f + .db $0f, $30, $21, $1c + .db $0f, $27, $17, $1c + .db $0f, $16, $27, $18 + .db $0f, $1c, $36, $17 + .db $0f, $16, $30, $27 + .db $0f, $0c, $3c, $1c + .db $00 + +CastlePaletteData: + .db $3f, $00, $20 + .db $0f, $30, $10, $00 + .db $0f, $30, $10, $00 + .db $0f, $30, $16, $00 + .db $0f, $27, $17, $00 + .db $0f, $16, $27, $18 + .db $0f, $1c, $36, $17 + .db $0f, $16, $30, $27 + .db $0f, $00, $30, $10 + .db $00 + +DaySnowPaletteData: + .db $3f, $00, $04 + .db $22, $30, $00, $10 + .db $00 + +NightSnowPaletteData: + .db $3f, $00, $04 + .db $0f, $30, $00, $10 + .db $00 + +MushroomPaletteData: + .db $3f, $00, $04 + .db $22, $27, $16, $0f + .db $00 + +BowserPaletteData: + .db $3f, $14, $04 + .db $0f, $1a, $30, $27 + .db $00 + +MarioThanksMessage: +;"THANK YOU MARIO!" + .db $25, $48, $10 + .db $1d, $11, $0a, $17, $14, $24 + .db $22, $18, $1e, $24 + .db $16, $0a, $1b, $12, $18, $2b + .db $00 + +LuigiThanksMessage: +;"THANK YOU LUIGI!" + .db $25, $48, $10 + .db $1d, $11, $0a, $17, $14, $24 + .db $22, $18, $1e, $24 + .db $15, $1e, $12, $10, $12, $2b + .db $00 + +MushroomRetainerSaved: +;"BUT OUR PRINCESS IS IN" + .db $25, $c5, $16 + .db $0b, $1e, $1d, $24, $18, $1e, $1b, $24 + .db $19, $1b, $12, $17, $0c, $0e, $1c, $1c, $24 + .db $12, $1c, $24, $12, $17 +;"ANOTHER CASTLE!" + .db $26, $05, $0f + .db $0a, $17, $18, $1d, $11, $0e, $1b, $24 + .db $0c, $0a, $1c, $1d, $15, $0e, $2b, $00 + +PrincessSaved1: +;"YOUR QUEST IS OVER." + .db $25, $a7, $13 + .db $22, $18, $1e, $1b, $24 + .db $1a, $1e, $0e, $1c, $1d, $24 + .db $12, $1c, $24, $18, $1f, $0e, $1b, $af + .db $00 + +PrincessSaved2: +;"WE PRESENT YOU A NEW QUEST." + .db $25, $e3, $1b + .db $20, $0e, $24 + .db $19, $1b, $0e, $1c, $0e, $17, $1d, $24 + .db $22, $18, $1e, $24, $0a, $24, $17, $0e, $20, $24 + .db $1a, $1e, $0e, $1c, $1d, $af + .db $00 + +WorldSelectMessage1: +;"PUSH BUTTON B" + .db $26, $4a, $0d + .db $19, $1e, $1c, $11, $24 + .db $0b, $1e, $1d, $1d, $18, $17, $24, $0b + .db $00 + +WorldSelectMessage2: +;"TO SELECT A WORLD" + .db $26, $88, $11 + .db $1d, $18, $24, $1c, $0e, $15, $0e, $0c, $1d, $24 + .db $0a, $24, $20, $18, $1b, $15, $0d + .db $00 + +;------------------------------------------------------------------------------------- +;$04 - address low to jump address +;$05 - address high to jump address +;$06 - jump address low +;$07 - jump address high + +JumpEngine: + asl ;shift bit from contents of A + tay + pla ;pull saved return address from stack + sta $04 ;save to indirect + pla + sta $05 + iny + lda ($04),y ;load pointer from indirect + sta $06 ;note that if an RTS is performed in next routine + iny ;it will return to the execution before the sub + lda ($04),y ;that called this routine + sta $07 + jmp ($06) ;jump to the address we loaded + +;------------------------------------------------------------------------------------- + +InitializeNameTables: + lda PPU_STATUS ;reset flip-flop + lda Mirror_PPU_CTRL_REG1 ;load mirror of ppu reg $2000 + ora #%00010000 ;set sprites for first 4k and background for second 4k + and #%11110000 ;clear rest of lower nybble, leave higher alone + jsr WritePPUReg1 + lda #$24 ;set vram address to start of name table 1 + jsr WriteNTAddr + lda #$20 ;and then set it to name table 0 +WriteNTAddr: sta PPU_ADDRESS + lda #$00 + sta PPU_ADDRESS + ldx #$04 ;clear name table with blank tile #24 + ldy #$c0 + lda #$24 +InitNTLoop: sta PPU_DATA ;count out exactly 768 tiles + dey + bne InitNTLoop + dex + bne InitNTLoop + ldy #64 ;now to clear the attribute table (with zero this time) + txa + sta VRAM_Buffer1_Offset ;init vram buffer 1 offset + sta VRAM_Buffer1 ;init vram buffer 1 +InitATLoop: sta PPU_DATA + dey + bne InitATLoop + sta HorizontalScroll ;reset scroll variables + sta VerticalScroll + jmp InitScroll ;initialize scroll registers to zero + +;------------------------------------------------------------------------------------- +;$00 - temp joypad bit + +ReadJoypads: + lda #$01 ;reset and clear strobe of joypad ports + sta JOYPAD_PORT + lsr + tax ;start with joypad 1's port + sta JOYPAD_PORT + jsr ReadPortBits + inx ;increment for joypad 2's port +ReadPortBits: ldy #$08 +PortLoop: pha ;push previous bit onto stack + lda JOYPAD_PORT,x ;read current bit on joypad port + sta $00 ;check d1 and d0 of port output + lsr ;this is necessary on the old + ora $00 ;famicom systems in japan + lsr + pla ;read bits from stack + rol ;rotate bit from carry flag + dey + bne PortLoop ;count down bits left + sta SavedJoypadBits,x ;save controller status here always + pha + and #%00110000 ;check for select or start + and JoypadBitMask,x ;if neither saved state nor current state + beq Save8Bits ;have any of these two set, branch + pla + and #%11001111 ;otherwise store without select + sta SavedJoypadBits,x ;or start bits and leave + rts +Save8Bits: pla + sta JoypadBitMask,x ;save with all bits in another place and leave + rts + +;------------------------------------------------------------------------------------- +;$00 - vram buffer address table low +;$01 - vram buffer address table high + +WriteBufferToScreen: + sta PPU_ADDRESS ;store high byte of vram address + iny + lda ($00),y ;load next byte (second) + sta PPU_ADDRESS ;store low byte of vram address + iny + lda ($00),y ;load next byte (third) + asl ;shift to left and save in stack + pha + lda Mirror_PPU_CTRL_REG1 ;load mirror of $2000, + ora #%00000100 ;set ppu to increment by 32 by default + bcs SetupWrites ;if d7 of third byte was clear, ppu will + and #%11111011 ;only increment by 1 +SetupWrites: jsr WritePPUReg1 ;write to register + pla ;pull from stack and shift to left again + asl + bcc GetLength ;if d6 of third byte was clear, do not repeat byte + ora #%00000010 ;otherwise set d1 and increment Y + iny +GetLength: lsr ;shift back to the right to get proper length + lsr ;note that d1 will now be in carry + tax +OutputToVRAM: bcs RepeatByte ;if carry set, repeat loading the same byte + iny ;otherwise increment Y to load next byte +RepeatByte: lda ($00),y ;load more data from buffer and write to vram + sta PPU_DATA + dex ;done writing? + bne OutputToVRAM + sec + tya + adc $00 ;add end length plus one to the indirect at $00 + sta $00 ;to allow this routine to read another set of updates + lda #$00 + adc $01 + sta $01 + lda #$3f ;sets vram address to $3f00 + sta PPU_ADDRESS + lda #$00 + sta PPU_ADDRESS + sta PPU_ADDRESS ;then reinitializes it for some reason + sta PPU_ADDRESS +UpdateScreen: ldx PPU_STATUS ;reset flip-flop + ldy #$00 ;load first byte from indirect as a pointer + lda ($00),y + bne WriteBufferToScreen ;if byte is zero we have no further updates to make here +InitScroll: sta PPU_SCROLL_REG ;store contents of A into scroll registers + sta PPU_SCROLL_REG ;and end whatever subroutine led us here + rts + +;------------------------------------------------------------------------------------- + +WritePPUReg1: + sta PPU_CTRL_REG1 ;write contents of A to PPU register 1 + sta Mirror_PPU_CTRL_REG1 ;and its mirror + rts + +;------------------------------------------------------------------------------------- +;$00 - used to store status bar nybbles +;$02 - used as temp vram offset +;$03 - used to store length of status bar number + +;status bar name table offset and length data +StatusBarData: + .db $f0, $06 ; top score display on title screen + .db $62, $06 ; player score + .db $62, $06 + .db $6d, $02 ; coin tally + .db $6d, $02 + .db $7a, $03 ; game timer + +StatusBarOffset: + .db $06, $0c, $12, $18, $1e, $24 + +PrintStatusBarNumbers: + sta $00 ;store player-specific offset + jsr OutputNumbers ;use first nybble to print the coin display + lda $00 ;move high nybble to low + lsr ;and print to score display + lsr + lsr + lsr + +OutputNumbers: + clc ;add 1 to low nybble + adc #$01 + and #%00001111 ;mask out high nybble + cmp #$06 + bcs ExitOutputN + pha ;save incremented value to stack for now and + asl ;shift to left and use as offset + tay + ldx VRAM_Buffer1_Offset ;get current buffer pointer + lda #$20 ;put at top of screen by default + cpy #$00 ;are we writing top score on title screen? + bne SetupNums + lda #$22 ;if so, put further down on the screen +SetupNums: sta VRAM_Buffer1,x + lda StatusBarData,y ;write low vram address and length of thing + sta VRAM_Buffer1+1,x ;we're printing to the buffer + lda StatusBarData+1,y + sta VRAM_Buffer1+2,x + sta $03 ;save length byte in counter + stx $02 ;and buffer pointer elsewhere for now + pla ;pull original incremented value from stack + tax + lda StatusBarOffset,x ;load offset to value we want to write + sec + sbc StatusBarData+1,y ;subtract from length byte we read before + tay ;use value as offset to display digits + ldx $02 +DigitPLoop: lda DisplayDigits,y ;write digits to the buffer + sta VRAM_Buffer1+3,x + inx + iny + dec $03 ;do this until all the digits are written + bne DigitPLoop + lda #$00 ;put null terminator at end + sta VRAM_Buffer1+3,x + inx ;increment buffer pointer by 3 + inx + inx + stx VRAM_Buffer1_Offset ;store it in case we want to use it again +ExitOutputN: rts + +;------------------------------------------------------------------------------------- + +DigitsMathRoutine: + lda OperMode ;check mode of operation + cmp #TitleScreenModeValue + beq EraseDMods ;if in title screen mode, branch to lock score + ldx #$05 +AddModLoop: lda DigitModifier,x ;load digit amount to increment + clc + adc DisplayDigits,y ;add to current digit + bmi BorrowOne ;if result is a negative number, branch to subtract + cmp #10 + bcs CarryOne ;if digit greater than $09, branch to add +StoreNewD: sta DisplayDigits,y ;store as new score or game timer digit + dey ;move onto next digits in score or game timer + dex ;and digit amounts to increment + bpl AddModLoop ;loop back if we're not done yet +EraseDMods: lda #$00 ;store zero here + ldx #$06 ;start with the last digit +EraseMLoop: sta DigitModifier-1,x ;initialize the digit amounts to increment + dex + bpl EraseMLoop ;do this until they're all reset, then leave + rts +BorrowOne: dec DigitModifier-1,x ;decrement the previous digit, then put $09 in + lda #$09 ;the game timer digit we're currently on to "borrow + bne StoreNewD ;the one", then do an unconditional branch back +CarryOne: sec ;subtract ten from our digit to make it a + sbc #10 ;proper BCD number, then increment the digit + inc DigitModifier-1,x ;preceding current digit to "carry the one" properly + jmp StoreNewD ;go back to just after we branched here + +;------------------------------------------------------------------------------------- + +UpdateTopScore: + ldx #$05 ;start with mario's score + jsr TopScoreCheck + ldx #$0b ;now do luigi's score + +TopScoreCheck: + ldy #$05 ;start with the lowest digit + sec +GetScoreDiff: lda PlayerScoreDisplay,x ;subtract each player digit from each high score digit + sbc TopScoreDisplay,y ;from lowest to highest, if any top score digit exceeds + dex ;any player digit, borrow will be set until a subsequent + dey ;subtraction clears it (player digit is higher than top) + bpl GetScoreDiff + bcc NoTopSc ;check to see if borrow is still set, if so, no new high score + inx ;increment X and Y once to the start of the score + iny +CopyScore: lda PlayerScoreDisplay,x ;store player's score digits into high score memory area + sta TopScoreDisplay,y + inx + iny + cpy #$06 ;do this until we have stored them all + bcc CopyScore +NoTopSc: rts + +;------------------------------------------------------------------------------------- + +DefaultSprOffsets: + .db $04, $30, $48, $60, $78, $90, $a8, $c0 + .db $d8, $e8, $24, $f8, $fc, $28, $2c + +Sprite0Data: + .db $18, $ff, $23, $58 + +;------------------------------------------------------------------------------------- + +InitializeGame: + ldy #$6f ;clear all memory as in initialization procedure, + jsr InitializeMemory ;but this time, clear only as far as $076f + ldy #$1f +ClrSndLoop: sta SoundMemory,y ;clear out memory used + dey ;by the sound engines + bpl ClrSndLoop + lda #$18 ;set demo timer + sta DemoTimer + jsr LoadAreaPointer + +InitializeArea: + ldy #$4b ;clear all memory again, only as far as $074b + jsr InitializeMemory ;this is only necessary if branching from + ldx #$21 + lda #$00 +ClrTimersLoop: sta Timers,x ;clear out memory between + dex ;$0780 and $07a1 + bpl ClrTimersLoop + lda HalfwayPage + ldy AltEntranceControl ;if AltEntranceControl not set, use halfway page, if any found + beq StartPage + lda EntrancePage ;otherwise use saved entry page number here +StartPage: sta ScreenLeft_PageLoc ;set as value here + sta CurrentPageLoc ;also set as current page + sta BackloadingFlag ;set flag here if halfway page or saved entry page number found + jsr GetScreenPosition ;get pixel coordinates for screen borders + ldy #$20 ;if on odd numbered page, use $2480 as start of rendering + and #%00000001 ;otherwise use $2080, this address used later as name table + beq SetInitNTHigh ;address for rendering of game area + ldy #$24 +SetInitNTHigh: sty CurrentNTAddr_High ;store name table address + ldy #$80 + sty CurrentNTAddr_Low + asl ;store LSB of page number in high nybble + asl ;of block buffer column position + asl + asl + sta BlockBufferColumnPos + dec AreaObjectLength ;set area object lengths for all empty + dec AreaObjectLength+1 + dec AreaObjectLength+2 + lda #$0b ;set value for renderer to update 12 column sets + sta ColumnSets ;12 column sets = 24 metatile columns = 1 1/2 screens + jsr GetAreaDataAddrs ;get enemy and level addresses and load header + lda PrimaryHardMode ;check to see if primary hard mode has been activated + bne SetSecHard ;if so, activate the secondary no matter where we're at + lda WorldNumber ;otherwise check world number + cmp #World5 ;if less than 5, do not activate secondary + bcc CheckHalfway + bne SetSecHard ;if not equal to, then world > 5, thus activate + lda LevelNumber ;otherwise, world 5, so check level number + cmp #Level3 ;if 1 or 2, do not set secondary hard mode flag + bcc CheckHalfway +SetSecHard: inc SecondaryHardMode ;set secondary hard mode flag for areas 5-3 and beyond +CheckHalfway: lda HalfwayPage + beq DoneInitArea + lda #$02 ;if halfway page set, overwrite start position from header + sta PlayerEntranceCtrl +DoneInitArea: lda #Silence ;silence music + sta AreaMusicQueue + lda #$01 ;disable screen output + sta DisableScreenFlag + inc OperMode_Task ;increment one of the modes + rts + +;------------------------------------------------------------------------------------- + +PrimaryGameSetup: + lda #$01 + sta FetchNewGameTimerFlag ;set flag to load game timer from header + sta PlayerSize ;set player's size to small + lda #$02 + sta NumberofLives ;give each player three lives + sta OffScr_NumberofLives + +SecondaryGameSetup: + lda #$00 + sta DisableScreenFlag ;enable screen output + tay +ClearVRLoop: sta VRAM_Buffer1-1,y ;clear buffer at $0300-$03ff + iny + bne ClearVRLoop + sta GameTimerExpiredFlag ;clear game timer exp flag + sta DisableIntermediate ;clear skip lives display flag + sta BackloadingFlag ;clear value here + lda #$ff + sta BalPlatformAlignment ;initialize balance platform assignment flag + lda ScreenLeft_PageLoc ;get left side page location + lsr Mirror_PPU_CTRL_REG1 ;shift LSB of ppu register #1 mirror out + and #$01 ;mask out all but LSB of page location + ror ;rotate LSB of page location into carry then onto mirror + rol Mirror_PPU_CTRL_REG1 ;this is to set the proper PPU name table + jsr GetAreaMusic ;load proper music into queue + lda #$38 ;load sprite shuffle amounts to be used later + sta SprShuffleAmt+2 + lda #$48 + sta SprShuffleAmt+1 + lda #$58 + sta SprShuffleAmt + ldx #$0e ;load default OAM offsets into $06e4-$06f2 +ShufAmtLoop: lda DefaultSprOffsets,x + sta SprDataOffset,x + dex ;do this until they're all set + bpl ShufAmtLoop + ldy #$03 ;set up sprite #0 +ISpr0Loop: lda Sprite0Data,y + sta Sprite_Data,y + dey + bpl ISpr0Loop + jsr DoNothing2 ;these jsrs doesn't do anything useful + jsr DoNothing1 + inc Sprite0HitDetectFlag ;set sprite #0 check flag + inc OperMode_Task ;increment to next task + rts + +;------------------------------------------------------------------------------------- + +;$06 - RAM address low +;$07 - RAM address high + +InitializeMemory: + ldx #$07 ;set initial high byte to $0700-$07ff + lda #$00 ;set initial low byte to start of page (at $00 of page) + sta $06 +InitPageLoop: stx $07 +InitByteLoop: cpx #$01 ;check to see if we're on the stack ($0100-$01ff) + bne InitByte ;if not, go ahead anyway + cpy #$60 ;otherwise, check to see if we're at $0160-$01ff + bcs SkipByte ;if so, skip write +InitByte: sta ($06),y ;otherwise, initialize byte with current low byte in Y +SkipByte: dey + cpy #$ff ;do this until all bytes in page have been erased + bne InitByteLoop + dex ;go onto the next page + bpl InitPageLoop ;do this until all pages of memory have been erased + rts + +;------------------------------------------------------------------------------------- + +MusicSelectData: + .db WaterMusic, GroundMusic, UndergroundMusic, CastleMusic + .db CloudMusic, PipeIntroMusic + +GetAreaMusic: + lda OperMode ;if in title screen mode, leave + beq ExitGetM + lda AltEntranceControl ;check for specific alternate mode of entry + cmp #$02 ;if found, branch without checking starting position + beq ChkAreaType ;from area object data header + ldy #$05 ;select music for pipe intro scene by default + lda PlayerEntranceCtrl ;check value from level header for certain values + cmp #$06 + beq StoreMusic ;load music for pipe intro scene if header + cmp #$07 ;start position either value $06 or $07 + beq StoreMusic +ChkAreaType: ldy AreaType ;load area type as offset for music bit + lda CloudTypeOverride + beq StoreMusic ;check for cloud type override + ldy #$04 ;select music for cloud type level if found +StoreMusic: lda MusicSelectData,y ;otherwise select appropriate music for level type + sta AreaMusicQueue ;store in queue and leave +ExitGetM: rts + +;------------------------------------------------------------------------------------- + +PlayerStarting_X_Pos: + .db $28, $18 + .db $38, $28 + +AltYPosOffset: + .db $08, $00 + +PlayerStarting_Y_Pos: + .db $00, $20, $b0, $50, $00, $00, $b0, $b0 + .db $f0 + +PlayerBGPriorityData: + .db $00, $20, $00, $00, $00, $00, $00, $00 + +GameTimerData: + .db $20 ;dummy byte, used as part of bg priority data + .db $04, $03, $02 + +Entrance_GameTimerSetup: + lda ScreenLeft_PageLoc ;set current page for area objects + sta Player_PageLoc ;as page location for player + lda #$28 ;store value here + sta VerticalForceDown ;for fractional movement downwards if necessary + lda #$01 ;set high byte of player position and + sta PlayerFacingDir ;set facing direction so that player faces right + sta Player_Y_HighPos + lda #$00 ;set player state to on the ground by default + sta Player_State + dec Player_CollisionBits ;initialize player's collision bits + ldy #$00 ;initialize halfway page + sty HalfwayPage + lda AreaType ;check area type + bne ChkStPos ;if water type, set swimming flag, otherwise do not set + iny +ChkStPos: sty SwimmingFlag + ldx PlayerEntranceCtrl ;get starting position loaded from header + ldy AltEntranceControl ;check alternate mode of entry flag for 0 or 1 + beq SetStPos + cpy #$01 + beq SetStPos + ldx AltYPosOffset-2,y ;if not 0 or 1, override $0710 with new offset in X +SetStPos: lda PlayerStarting_X_Pos,y ;load appropriate horizontal position + sta Player_X_Position ;and vertical positions for the player, using + lda PlayerStarting_Y_Pos,x ;AltEntranceControl as offset for horizontal and either $0710 + sta Player_Y_Position ;or value that overwrote $0710 as offset for vertical + lda PlayerBGPriorityData,x + sta Player_SprAttrib ;set player sprite attributes using offset in X + jsr GetPlayerColors ;get appropriate player palette + ldy GameTimerSetting ;get timer control value from header + beq ChkOverR ;if set to zero, branch (do not use dummy byte for this) + lda FetchNewGameTimerFlag ;do we need to set the game timer? if not, use + beq ChkOverR ;old game timer setting + lda GameTimerData,y ;if game timer is set and game timer flag is also set, + sta GameTimerDisplay ;use value of game timer control for first digit of game timer + lda #$01 + sta GameTimerDisplay+2 ;set last digit of game timer to 1 + lsr + sta GameTimerDisplay+1 ;set second digit of game timer + sta FetchNewGameTimerFlag ;clear flag for game timer reset + sta StarInvincibleTimer ;clear star mario timer +ChkOverR: ldy JoypadOverride ;if controller bits not set, branch to skip this part + beq ChkSwimE + lda #$03 ;set player state to climbing + sta Player_State + ldx #$00 ;set offset for first slot, for block object + jsr InitBlock_XY_Pos + lda #$f0 ;set vertical coordinate for block object + sta Block_Y_Position + ldx #$05 ;set offset in X for last enemy object buffer slot + ldy #$00 ;set offset in Y for object coordinates used earlier + jsr Setup_Vine ;do a sub to grow vine +ChkSwimE: ldy AreaType ;if level not water-type, + bne SetPESub ;skip this subroutine + jsr SetupBubble ;otherwise, execute sub to set up air bubbles +SetPESub: lda #$07 ;set to run player entrance subroutine + sta GameEngineSubroutine ;on the next frame of game engine + rts + +;------------------------------------------------------------------------------------- + +;page numbers are in order from -1 to -4 +HalfwayPageNybbles: + .db $56, $40 + .db $65, $70 + .db $66, $40 + .db $66, $40 + .db $66, $40 + .db $66, $60 + .db $65, $70 + .db $00, $00 + +PlayerLoseLife: + inc DisableScreenFlag ;disable screen and sprite 0 check + lda #$00 + sta Sprite0HitDetectFlag + lda #Silence ;silence music + sta EventMusicQueue + dec NumberofLives ;take one life from player + bpl StillInGame ;if player still has lives, branch + lda #$00 + sta OperMode_Task ;initialize mode task, + lda #GameOverModeValue ;switch to game over mode + sta OperMode ;and leave + rts +StillInGame: lda WorldNumber ;multiply world number by 2 and use + asl ;as offset + tax + lda LevelNumber ;if in area -3 or -4, increment + and #$02 ;offset by one byte, otherwise + beq GetHalfway ;leave offset alone + inx +GetHalfway: ldy HalfwayPageNybbles,x ;get halfway page number with offset + lda LevelNumber ;check area number's LSB + lsr + tya ;if in area -2 or -4, use lower nybble + bcs MaskHPNyb + lsr ;move higher nybble to lower if area + lsr ;number is -1 or -3 + lsr + lsr +MaskHPNyb: and #%00001111 ;mask out all but lower nybble + cmp ScreenLeft_PageLoc + beq SetHalfway ;left side of screen must be at the halfway page, + bcc SetHalfway ;otherwise player must start at the + lda #$00 ;beginning of the level +SetHalfway: sta HalfwayPage ;store as halfway page for player + jsr TransposePlayers ;switch players around if 2-player game + jmp ContinueGame ;continue the game + +;------------------------------------------------------------------------------------- + +GameOverMode: + lda OperMode_Task + jsr JumpEngine + + .dw SetupGameOver + .dw ScreenRoutines + .dw RunGameOver + +;------------------------------------------------------------------------------------- + +SetupGameOver: + lda #$00 ;reset screen routine task control for title screen, game, + sta ScreenRoutineTask ;and game over modes + sta Sprite0HitDetectFlag ;disable sprite 0 check + lda #GameOverMusic + sta EventMusicQueue ;put game over music in secondary queue + inc DisableScreenFlag ;disable screen output + inc OperMode_Task ;set secondary mode to 1 + rts + +;------------------------------------------------------------------------------------- + +RunGameOver: + lda #$00 ;reenable screen + sta DisableScreenFlag + lda SavedJoypad1Bits ;check controller for start pressed + and #Start_Button + bne TerminateGame + lda ScreenTimer ;if not pressed, wait for + bne GameIsOn ;screen timer to expire +TerminateGame: + lda #Silence ;silence music + sta EventMusicQueue + jsr TransposePlayers ;check if other player can keep + bcc ContinueGame ;going, and do so if possible + lda WorldNumber ;otherwise put world number of current + sta ContinueWorld ;player into secret continue function variable + lda #$00 + asl ;residual ASL instruction + sta OperMode_Task ;reset all modes to title screen and + sta ScreenTimer ;leave + sta OperMode + rts + +ContinueGame: + jsr LoadAreaPointer ;update level pointer with + lda #$01 ;actual world and area numbers, then + sta PlayerSize ;reset player's size, status, and + inc FetchNewGameTimerFlag ;set game timer flag to reload + lda #$00 ;game timer from header + sta TimerControl ;also set flag for timers to count again + sta PlayerStatus + sta GameEngineSubroutine ;reset task for game core + sta OperMode_Task ;set modes and leave + lda #$01 ;if in game over mode, switch back to + sta OperMode ;game mode, because game is still on +GameIsOn: rts + +TransposePlayers: + sec ;set carry flag by default to end game + lda NumberOfPlayers ;if only a 1 player game, leave + beq ExTrans + lda OffScr_NumberofLives ;does offscreen player have any lives left? + bmi ExTrans ;branch if not + lda CurrentPlayer ;invert bit to update + eor #%00000001 ;which player is on the screen + sta CurrentPlayer + ldx #$06 +TransLoop: lda OnscreenPlayerInfo,x ;transpose the information + pha ;of the onscreen player + lda OffscreenPlayerInfo,x ;with that of the offscreen player + sta OnscreenPlayerInfo,x + pla + sta OffscreenPlayerInfo,x + dex + bpl TransLoop + clc ;clear carry flag to get game going +ExTrans: rts + +;------------------------------------------------------------------------------------- + +DoNothing1: + lda #$ff ;this is residual code, this value is + sta $06c9 ;not used anywhere in the program +DoNothing2: + rts + +;------------------------------------------------------------------------------------- + +AreaParserTaskHandler: + ldy AreaParserTaskNum ;check number of tasks here + bne DoAPTasks ;if already set, go ahead + ldy #$08 + sty AreaParserTaskNum ;otherwise, set eight by default +DoAPTasks: dey + tya + jsr AreaParserTasks + dec AreaParserTaskNum ;if all tasks not complete do not + bne SkipATRender ;render attribute table yet + jsr RenderAttributeTables +SkipATRender: rts + +AreaParserTasks: + jsr JumpEngine + + .dw IncrementColumnPos + .dw RenderAreaGraphics + .dw RenderAreaGraphics + .dw AreaParserCore + .dw IncrementColumnPos + .dw RenderAreaGraphics + .dw RenderAreaGraphics + .dw AreaParserCore + +;------------------------------------------------------------------------------------- + +IncrementColumnPos: + inc CurrentColumnPos ;increment column where we're at + lda CurrentColumnPos + and #%00001111 ;mask out higher nybble + bne NoColWrap + sta CurrentColumnPos ;if no bits left set, wrap back to zero (0-f) + inc CurrentPageLoc ;and increment page number where we're at +NoColWrap: inc BlockBufferColumnPos ;increment column offset where we're at + lda BlockBufferColumnPos + and #%00011111 ;mask out all but 5 LSB (0-1f) + sta BlockBufferColumnPos ;and save + rts + +;------------------------------------------------------------------------------------- +;$00 - used as counter, store for low nybble for background, ceiling byte for terrain +;$01 - used to store floor byte for terrain +;$07 - used to store terrain metatile +;$06-$07 - used to store block buffer address + +BSceneDataOffsets: + .db $00, $30, $60 + +BackSceneryData: + .db $93, $00, $00, $11, $12, $12, $13, $00 ;clouds + .db $00, $51, $52, $53, $00, $00, $00, $00 + .db $00, $00, $01, $02, $02, $03, $00, $00 + .db $00, $00, $00, $00, $91, $92, $93, $00 + .db $00, $00, $00, $51, $52, $53, $41, $42 + .db $43, $00, $00, $00, $00, $00, $91, $92 + + .db $97, $87, $88, $89, $99, $00, $00, $00 ;mountains and bushes + .db $11, $12, $13, $a4, $a5, $a5, $a5, $a6 + .db $97, $98, $99, $01, $02, $03, $00, $a4 + .db $a5, $a6, $00, $11, $12, $12, $12, $13 + .db $00, $00, $00, $00, $01, $02, $02, $03 + .db $00, $a4, $a5, $a5, $a6, $00, $00, $00 + + .db $11, $12, $12, $13, $00, $00, $00, $00 ;trees and fences + .db $00, $00, $00, $9c, $00, $8b, $aa, $aa + .db $aa, $aa, $11, $12, $13, $8b, $00, $9c + .db $9c, $00, $00, $01, $02, $03, $11, $12 + .db $12, $13, $00, $00, $00, $00, $aa, $aa + .db $9c, $aa, $00, $8b, $00, $01, $02, $03 + +BackSceneryMetatiles: + .db $80, $83, $00 ;cloud left + .db $81, $84, $00 ;cloud middle + .db $82, $85, $00 ;cloud right + .db $02, $00, $00 ;bush left + .db $03, $00, $00 ;bush middle + .db $04, $00, $00 ;bush right + .db $00, $05, $06 ;mountain left + .db $07, $06, $0a ;mountain middle + .db $00, $08, $09 ;mountain right + .db $4d, $00, $00 ;fence + .db $0d, $0f, $4e ;tall tree + .db $0e, $4e, $4e ;short tree + +FSceneDataOffsets: + .db $00, $0d, $1a + +ForeSceneryData: + .db $86, $87, $87, $87, $87, $87, $87 ;in water + .db $87, $87, $87, $87, $69, $69 + + .db $00, $00, $00, $00, $00, $45, $47 ;wall + .db $47, $47, $47, $47, $00, $00 + + .db $00, $00, $00, $00, $00, $00, $00 ;over water + .db $00, $00, $00, $00, $86, $87 + +TerrainMetatiles: + .db $69, $54, $52, $62 + +TerrainRenderBits: + .db %00000000, %00000000 ;no ceiling or floor + .db %00000000, %00011000 ;no ceiling, floor 2 + .db %00000001, %00011000 ;ceiling 1, floor 2 + .db %00000111, %00011000 ;ceiling 3, floor 2 + .db %00001111, %00011000 ;ceiling 4, floor 2 + .db %11111111, %00011000 ;ceiling 8, floor 2 + .db %00000001, %00011111 ;ceiling 1, floor 5 + .db %00000111, %00011111 ;ceiling 3, floor 5 + .db %00001111, %00011111 ;ceiling 4, floor 5 + .db %10000001, %00011111 ;ceiling 1, floor 6 + .db %00000001, %00000000 ;ceiling 1, no floor + .db %10001111, %00011111 ;ceiling 4, floor 6 + .db %11110001, %00011111 ;ceiling 1, floor 9 + .db %11111001, %00011000 ;ceiling 1, middle 5, floor 2 + .db %11110001, %00011000 ;ceiling 1, middle 4, floor 2 + .db %11111111, %00011111 ;completely solid top to bottom + +AreaParserCore: + lda BackloadingFlag ;check to see if we are starting right of start + beq RenderSceneryTerrain ;if not, go ahead and render background, foreground and terrain + jsr ProcessAreaData ;otherwise skip ahead and load level data + +RenderSceneryTerrain: + ldx #$0c + lda #$00 +ClrMTBuf: sta MetatileBuffer,x ;clear out metatile buffer + dex + bpl ClrMTBuf + ldy BackgroundScenery ;do we need to render the background scenery? + beq RendFore ;if not, skip to check the foreground + lda CurrentPageLoc ;otherwise check for every third page +ThirdP: cmp #$03 + bmi RendBack ;if less than three we're there + sec + sbc #$03 ;if 3 or more, subtract 3 and + bpl ThirdP ;do an unconditional branch +RendBack: asl ;move results to higher nybble + asl + asl + asl + adc BSceneDataOffsets-1,y ;add to it offset loaded from here + adc CurrentColumnPos ;add to the result our current column position + tax + lda BackSceneryData,x ;load data from sum of offsets + beq RendFore ;if zero, no scenery for that part + pha + and #$0f ;save to stack and clear high nybble + sec + sbc #$01 ;subtract one (because low nybble is $01-$0c) + sta $00 ;save low nybble + asl ;multiply by three (shift to left and add result to old one) + adc $00 ;note that since d7 was nulled, the carry flag is always clear + tax ;save as offset for background scenery metatile data + pla ;get high nybble from stack, move low + lsr + lsr + lsr + lsr + tay ;use as second offset (used to determine height) + lda #$03 ;use previously saved memory location for counter + sta $00 +SceLoop1: lda BackSceneryMetatiles,x ;load metatile data from offset of (lsb - 1) * 3 + sta MetatileBuffer,y ;store into buffer from offset of (msb / 16) + inx + iny + cpy #$0b ;if at this location, leave loop + beq RendFore + dec $00 ;decrement until counter expires, barring exception + bne SceLoop1 +RendFore: ldx ForegroundScenery ;check for foreground data needed or not + beq RendTerr ;if not, skip this part + ldy FSceneDataOffsets-1,x ;load offset from location offset by header value, then + ldx #$00 ;reinit X +SceLoop2: lda ForeSceneryData,y ;load data until counter expires + beq NoFore ;do not store if zero found + sta MetatileBuffer,x +NoFore: iny + inx + cpx #$0d ;store up to end of metatile buffer + bne SceLoop2 +RendTerr: ldy AreaType ;check world type for water level + bne TerMTile ;if not water level, skip this part + lda WorldNumber ;check world number, if not world number eight + cmp #World8 ;then skip this part + bne TerMTile + lda #$62 ;if set as water level and world number eight, + jmp StoreMT ;use castle wall metatile as terrain type +TerMTile: lda TerrainMetatiles,y ;otherwise get appropriate metatile for area type + ldy CloudTypeOverride ;check for cloud type override + beq StoreMT ;if not set, keep value otherwise + lda #$88 ;use cloud block terrain +StoreMT: sta $07 ;store value here + ldx #$00 ;initialize X, use as metatile buffer offset + lda TerrainControl ;use yet another value from the header + asl ;multiply by 2 and use as yet another offset + tay +TerrLoop: lda TerrainRenderBits,y ;get one of the terrain rendering bit data + sta $00 + iny ;increment Y and use as offset next time around + sty $01 + lda CloudTypeOverride ;skip if value here is zero + beq NoCloud2 + cpx #$00 ;otherwise, check if we're doing the ceiling byte + beq NoCloud2 + lda $00 ;if not, mask out all but d3 + and #%00001000 + sta $00 +NoCloud2: ldy #$00 ;start at beginning of bitmasks +TerrBChk: lda Bitmasks,y ;load bitmask, then perform AND on contents of first byte + bit $00 + beq NextTBit ;if not set, skip this part (do not write terrain to buffer) + lda $07 + sta MetatileBuffer,x ;load terrain type metatile number and store into buffer here +NextTBit: inx ;continue until end of buffer + cpx #$0d + beq RendBBuf ;if we're at the end, break out of this loop + lda AreaType ;check world type for underground area + cmp #$02 + bne EndUChk ;if not underground, skip this part + cpx #$0b + bne EndUChk ;if we're at the bottom of the screen, override + lda #$54 ;old terrain type with ground level terrain type + sta $07 +EndUChk: iny ;increment bitmasks offset in Y + cpy #$08 + bne TerrBChk ;if not all bits checked, loop back + ldy $01 + bne TerrLoop ;unconditional branch, use Y to load next byte +RendBBuf: jsr ProcessAreaData ;do the area data loading routine now + lda BlockBufferColumnPos + jsr GetBlockBufferAddr ;get block buffer address from where we're at + ldx #$00 + ldy #$00 ;init index regs and start at beginning of smaller buffer +ChkMTLow: sty $00 + lda MetatileBuffer,x ;load stored metatile number + and #%11000000 ;mask out all but 2 MSB + asl + rol ;make %xx000000 into %000000xx + rol + tay ;use as offset in Y + lda MetatileBuffer,x ;reload original unmasked value here + cmp BlockBuffLowBounds,y ;check for certain values depending on bits set + bcs StrBlock ;if equal or greater, branch + lda #$00 ;if less, init value before storing +StrBlock: ldy $00 ;get offset for block buffer + sta ($06),y ;store value into block buffer + tya + clc ;add 16 (move down one row) to offset + adc #$10 + tay + inx ;increment column value + cpx #$0d + bcc ChkMTLow ;continue until we pass last row, then leave + rts + +;numbers lower than these with the same attribute bits +;will not be stored in the block buffer +BlockBuffLowBounds: + .db $10, $51, $88, $c0 + +;------------------------------------------------------------------------------------- +;$00 - used to store area object identifier +;$07 - used as adder to find proper area object code + +ProcessAreaData: + ldx #$02 ;start at the end of area object buffer +ProcADLoop: stx ObjectOffset + lda #$00 ;reset flag + sta BehindAreaParserFlag + ldy AreaDataOffset ;get offset of area data pointer + lda (AreaData),y ;get first byte of area object + cmp #$fd ;if end-of-area, skip all this crap + beq RdyDecode + lda AreaObjectLength,x ;check area object buffer flag + bpl RdyDecode ;if buffer not negative, branch, otherwise + iny + lda (AreaData),y ;get second byte of area object + asl ;check for page select bit (d7), branch if not set + bcc Chk1Row13 + lda AreaObjectPageSel ;check page select + bne Chk1Row13 + inc AreaObjectPageSel ;if not already set, set it now + inc AreaObjectPageLoc ;and increment page location +Chk1Row13: dey + lda (AreaData),y ;reread first byte of level object + and #$0f ;mask out high nybble + cmp #$0d ;row 13? + bne Chk1Row14 + iny ;if so, reread second byte of level object + lda (AreaData),y + dey ;decrement to get ready to read first byte + and #%01000000 ;check for d6 set (if not, object is page control) + bne CheckRear + lda AreaObjectPageSel ;if page select is set, do not reread + bne CheckRear + iny ;if d6 not set, reread second byte + lda (AreaData),y + and #%00011111 ;mask out all but 5 LSB and store in page control + sta AreaObjectPageLoc + inc AreaObjectPageSel ;increment page select + jmp NextAObj +Chk1Row14: cmp #$0e ;row 14? + bne CheckRear + lda BackloadingFlag ;check flag for saved page number and branch if set + bne RdyDecode ;to render the object (otherwise bg might not look right) +CheckRear: lda AreaObjectPageLoc ;check to see if current page of level object is + cmp CurrentPageLoc ;behind current page of renderer + bcc SetBehind ;if so branch +RdyDecode: jsr DecodeAreaData ;do sub and do not turn on flag + jmp ChkLength +SetBehind: inc BehindAreaParserFlag ;turn on flag if object is behind renderer +NextAObj: jsr IncAreaObjOffset ;increment buffer offset and move on +ChkLength: ldx ObjectOffset ;get buffer offset + lda AreaObjectLength,x ;check object length for anything stored here + bmi ProcLoopb ;if not, branch to handle loopback + dec AreaObjectLength,x ;otherwise decrement length or get rid of it +ProcLoopb: dex ;decrement buffer offset + bpl ProcADLoop ;and loopback unless exceeded buffer + lda BehindAreaParserFlag ;check for flag set if objects were behind renderer + bne ProcessAreaData ;branch if true to load more level data, otherwise + lda BackloadingFlag ;check for flag set if starting right of page $00 + bne ProcessAreaData ;branch if true to load more level data, otherwise leave +EndAParse: rts + +IncAreaObjOffset: + inc AreaDataOffset ;increment offset of level pointer + inc AreaDataOffset + lda #$00 ;reset page select + sta AreaObjectPageSel + rts + +DecodeAreaData: + lda AreaObjectLength,x ;check current buffer flag + bmi Chk1stB + ldy AreaObjOffsetBuffer,x ;if not, get offset from buffer +Chk1stB: ldx #$10 ;load offset of 16 for special row 15 + lda (AreaData),y ;get first byte of level object again + cmp #$fd + beq EndAParse ;if end of level, leave this routine + and #$0f ;otherwise, mask out low nybble + cmp #$0f ;row 15? + beq ChkRow14 ;if so, keep the offset of 16 + ldx #$08 ;otherwise load offset of 8 for special row 12 + cmp #$0c ;row 12? + beq ChkRow14 ;if so, keep the offset value of 8 + ldx #$00 ;otherwise nullify value by default +ChkRow14: stx $07 ;store whatever value we just loaded here + ldx ObjectOffset ;get object offset again + cmp #$0e ;row 14? + bne ChkRow13 + lda #$00 ;if so, load offset with $00 + sta $07 + lda #$2e ;and load A with another value + bne NormObj ;unconditional branch +ChkRow13: cmp #$0d ;row 13? + bne ChkSRows + lda #$22 ;if so, load offset with 34 + sta $07 + iny ;get next byte + lda (AreaData),y + and #%01000000 ;mask out all but d6 (page control obj bit) + beq LeavePar ;if d6 clear, branch to leave (we handled this earlier) + lda (AreaData),y ;otherwise, get byte again + and #%01111111 ;mask out d7 + cmp #$4b ;check for loop command in low nybble + bne Mask2MSB ;(plus d6 set for object other than page control) + inc LoopCommand ;if loop command, set loop command flag +Mask2MSB: and #%00111111 ;mask out d7 and d6 + jmp NormObj ;and jump +ChkSRows: cmp #$0c ;row 12-15? + bcs SpecObj + iny ;if not, get second byte of level object + lda (AreaData),y + and #%01110000 ;mask out all but d6-d4 + bne LrgObj ;if any bits set, branch to handle large object + lda #$16 + sta $07 ;otherwise set offset of 24 for small object + lda (AreaData),y ;reload second byte of level object + and #%00001111 ;mask out higher nybble and jump + jmp NormObj +LrgObj: sta $00 ;store value here (branch for large objects) + cmp #$70 ;check for vertical pipe object + bne NotWPipe + lda (AreaData),y ;if not, reload second byte + and #%00001000 ;mask out all but d3 (usage control bit) + beq NotWPipe ;if d3 clear, branch to get original value + lda #$00 ;otherwise, nullify value for warp pipe + sta $00 +NotWPipe: lda $00 ;get value and jump ahead + jmp MoveAOId +SpecObj: iny ;branch here for rows 12-15 + lda (AreaData),y + and #%01110000 ;get next byte and mask out all but d6-d4 +MoveAOId: lsr ;move d6-d4 to lower nybble + lsr + lsr + lsr +NormObj: sta $00 ;store value here (branch for small objects and rows 13 and 14) + lda AreaObjectLength,x ;is there something stored here already? + bpl RunAObj ;if so, branch to do its particular sub + lda AreaObjectPageLoc ;otherwise check to see if the object we've loaded is on the + cmp CurrentPageLoc ;same page as the renderer, and if so, branch + beq InitRear + ldy AreaDataOffset ;if not, get old offset of level pointer + lda (AreaData),y ;and reload first byte + and #%00001111 + cmp #$0e ;row 14? + bne LeavePar + lda BackloadingFlag ;if so, check backloading flag + bne StrAObj ;if set, branch to render object, else leave +LeavePar: rts +InitRear: lda BackloadingFlag ;check backloading flag to see if it's been initialized + beq BackColC ;branch to column-wise check + lda #$00 ;if not, initialize both backloading and + sta BackloadingFlag ;behind-renderer flags and leave + sta BehindAreaParserFlag + sta ObjectOffset +LoopCmdE: rts +BackColC: ldy AreaDataOffset ;get first byte again + lda (AreaData),y + and #%11110000 ;mask out low nybble and move high to low + lsr + lsr + lsr + lsr + cmp CurrentColumnPos ;is this where we're at? + bne LeavePar ;if not, branch to leave +StrAObj: lda AreaDataOffset ;if so, load area obj offset and store in buffer + sta AreaObjOffsetBuffer,x + jsr IncAreaObjOffset ;do sub to increment to next object data +RunAObj: lda $00 ;get stored value and add offset to it + clc ;then use the jump engine with current contents of A + adc $07 + jsr JumpEngine + +;large objects (rows $00-$0b or 00-11, d6-d4 set) + .dw VerticalPipe ;used by warp pipes + .dw AreaStyleObject + .dw RowOfBricks + .dw RowOfSolidBlocks + .dw RowOfCoins + .dw ColumnOfBricks + .dw ColumnOfSolidBlocks + .dw VerticalPipe ;used by decoration pipes + +;objects for special row $0c or 12 + .dw Hole_Empty + .dw PulleyRopeObject + .dw Bridge_High + .dw Bridge_Middle + .dw Bridge_Low + .dw Hole_Water + .dw QuestionBlockRow_High + .dw QuestionBlockRow_Low + +;objects for special row $0f or 15 + .dw EndlessRope + .dw BalancePlatRope + .dw CastleObject + .dw StaircaseObject + .dw ExitPipe + .dw FlagBalls_Residual + +;small objects (rows $00-$0b or 00-11, d6-d4 all clear) + .dw QuestionBlock ;power-up + .dw QuestionBlock ;coin + .dw QuestionBlock ;hidden, coin + .dw Hidden1UpBlock ;hidden, 1-up + .dw BrickWithItem ;brick, power-up + .dw BrickWithItem ;brick, vine + .dw BrickWithItem ;brick, star + .dw BrickWithCoins ;brick, coins + .dw BrickWithItem ;brick, 1-up + .dw WaterPipe + .dw EmptyBlock + .dw Jumpspring + +;objects for special row $0d or 13 (d6 set) + .dw IntroPipe + .dw FlagpoleObject + .dw AxeObj + .dw ChainObj + .dw CastleBridgeObj + .dw ScrollLockObject_Warp + .dw ScrollLockObject + .dw ScrollLockObject + .dw AreaFrenzy ;flying cheep-cheeps + .dw AreaFrenzy ;bullet bills or swimming cheep-cheeps + .dw AreaFrenzy ;stop frenzy + .dw LoopCmdE + +;object for special row $0e or 14 + .dw AlterAreaAttributes + +;------------------------------------------------------------------------------------- +;(these apply to all area object subroutines in this section unless otherwise stated) +;$00 - used to store offset used to find object code +;$07 - starts with adder from area parser, used to store row offset + +AlterAreaAttributes: + ldy AreaObjOffsetBuffer,x ;load offset for level object data saved in buffer + iny ;load second byte + lda (AreaData),y + pha ;save in stack for now + and #%01000000 + bne Alter2 ;branch if d6 is set + pla + pha ;pull and push offset to copy to A + and #%00001111 ;mask out high nybble and store as + sta TerrainControl ;new terrain height type bits + pla + and #%00110000 ;pull and mask out all but d5 and d4 + lsr ;move bits to lower nybble and store + lsr ;as new background scenery bits + lsr + lsr + sta BackgroundScenery ;then leave + rts +Alter2: pla + and #%00000111 ;mask out all but 3 LSB + cmp #$04 ;if four or greater, set color control bits + bcc SetFore ;and nullify foreground scenery bits + sta BackgroundColorCtrl + lda #$00 +SetFore: sta ForegroundScenery ;otherwise set new foreground scenery bits + rts + +;-------------------------------- + +ScrollLockObject_Warp: + ldx #$04 ;load value of 4 for game text routine as default + lda WorldNumber ;warp zone (4-3-2), then check world number + beq WarpNum + inx ;if world number > 1, increment for next warp zone (5) + ldy AreaType ;check area type + dey + bne WarpNum ;if ground area type, increment for last warp zone + inx ;(8-7-6) and move on +WarpNum: txa + sta WarpZoneControl ;store number here to be used by warp zone routine + jsr WriteGameText ;print text and warp zone numbers + lda #PiranhaPlant + jsr KillEnemies ;load identifier for piranha plants and do sub + +ScrollLockObject: + lda ScrollLock ;invert scroll lock to turn it on + eor #%00000001 + sta ScrollLock + rts + +;-------------------------------- +;$00 - used to store enemy identifier in KillEnemies + +KillEnemies: + sta $00 ;store identifier here + lda #$00 + ldx #$04 ;check for identifier in enemy object buffer +KillELoop: ldy Enemy_ID,x + cpy $00 ;if not found, branch + bne NoKillE + sta Enemy_Flag,x ;if found, deactivate enemy object flag +NoKillE: dex ;do this until all slots are checked + bpl KillELoop + rts + +;-------------------------------- + +FrenzyIDData: + .db FlyCheepCheepFrenzy, BBill_CCheep_Frenzy, Stop_Frenzy + +AreaFrenzy: ldx $00 ;use area object identifier bit as offset + lda FrenzyIDData-8,x ;note that it starts at 8, thus weird address here + ldy #$05 +FreCompLoop: dey ;check regular slots of enemy object buffer + bmi ExitAFrenzy ;if all slots checked and enemy object not found, branch to store + cmp Enemy_ID,y ;check for enemy object in buffer versus frenzy object + bne FreCompLoop + lda #$00 ;if enemy object already present, nullify queue and leave +ExitAFrenzy: sta EnemyFrenzyQueue ;store enemy into frenzy queue + rts + +;-------------------------------- +;$06 - used by MushroomLedge to store length + +AreaStyleObject: + lda AreaStyle ;load level object style and jump to the right sub + jsr JumpEngine + .dw TreeLedge ;also used for cloud type levels + .dw MushroomLedge + .dw BulletBillCannon + +TreeLedge: + jsr GetLrgObjAttrib ;get row and length of green ledge + lda AreaObjectLength,x ;check length counter for expiration + beq EndTreeL + bpl MidTreeL + tya + sta AreaObjectLength,x ;store lower nybble into buffer flag as length of ledge + lda CurrentPageLoc + ora CurrentColumnPos ;are we at the start of the level? + beq MidTreeL + lda #$16 ;render start of tree ledge + jmp NoUnder +MidTreeL: ldx $07 + lda #$17 ;render middle of tree ledge + sta MetatileBuffer,x ;note that this is also used if ledge position is + lda #$4c ;at the start of level for continuous effect + jmp AllUnder ;now render the part underneath +EndTreeL: lda #$18 ;render end of tree ledge + jmp NoUnder + +MushroomLedge: + jsr ChkLrgObjLength ;get shroom dimensions + sty $06 ;store length here for now + bcc EndMushL + lda AreaObjectLength,x ;divide length by 2 and store elsewhere + lsr + sta MushroomLedgeHalfLen,x + lda #$19 ;render start of mushroom + jmp NoUnder +EndMushL: lda #$1b ;if at the end, render end of mushroom + ldy AreaObjectLength,x + beq NoUnder + lda MushroomLedgeHalfLen,x ;get divided length and store where length + sta $06 ;was stored originally + ldx $07 + lda #$1a + sta MetatileBuffer,x ;render middle of mushroom + cpy $06 ;are we smack dab in the center? + bne MushLExit ;if not, branch to leave + inx + lda #$4f + sta MetatileBuffer,x ;render stem top of mushroom underneath the middle + lda #$50 +AllUnder: inx + ldy #$0f ;set $0f to render all way down + jmp RenderUnderPart ;now render the stem of mushroom +NoUnder: ldx $07 ;load row of ledge + ldy #$00 ;set 0 for no bottom on this part + jmp RenderUnderPart + +;-------------------------------- + +;tiles used by pulleys and rope object +PulleyRopeMetatiles: + .db $42, $41, $43 + +PulleyRopeObject: + jsr ChkLrgObjLength ;get length of pulley/rope object + ldy #$00 ;initialize metatile offset + bcs RenderPul ;if starting, render left pulley + iny + lda AreaObjectLength,x ;if not at the end, render rope + bne RenderPul + iny ;otherwise render right pulley +RenderPul: lda PulleyRopeMetatiles,y + sta MetatileBuffer ;render at the top of the screen +MushLExit: rts ;and leave + +;-------------------------------- +;$06 - used to store upper limit of rows for CastleObject + +CastleMetatiles: + .db $00, $45, $45, $45, $00 + .db $00, $48, $47, $46, $00 + .db $45, $49, $49, $49, $45 + .db $47, $47, $4a, $47, $47 + .db $47, $47, $4b, $47, $47 + .db $49, $49, $49, $49, $49 + .db $47, $4a, $47, $4a, $47 + .db $47, $4b, $47, $4b, $47 + .db $47, $47, $47, $47, $47 + .db $4a, $47, $4a, $47, $4a + .db $4b, $47, $4b, $47, $4b + +CastleObject: + jsr GetLrgObjAttrib ;save lower nybble as starting row + sty $07 ;if starting row is above $0a, game will crash!!! + ldy #$04 + jsr ChkLrgObjFixedLength ;load length of castle if not already loaded + txa + pha ;save obj buffer offset to stack + ldy AreaObjectLength,x ;use current length as offset for castle data + ldx $07 ;begin at starting row + lda #$0b + sta $06 ;load upper limit of number of rows to print +CRendLoop: lda CastleMetatiles,y ;load current byte using offset + sta MetatileBuffer,x + inx ;store in buffer and increment buffer offset + lda $06 + beq ChkCFloor ;have we reached upper limit yet? + iny ;if not, increment column-wise + iny ;to byte in next row + iny + iny + iny + dec $06 ;move closer to upper limit +ChkCFloor: cpx #$0b ;have we reached the row just before floor? + bne CRendLoop ;if not, go back and do another row + pla + tax ;get obj buffer offset from before + lda CurrentPageLoc + beq ExitCastle ;if we're at page 0, we do not need to do anything else + lda AreaObjectLength,x ;check length + cmp #$01 ;if length almost about to expire, put brick at floor + beq PlayerStop + ldy $07 ;check starting row for tall castle ($00) + bne NotTall + cmp #$03 ;if found, then check to see if we're at the second column + beq PlayerStop +NotTall: cmp #$02 ;if not tall castle, check to see if we're at the third column + bne ExitCastle ;if we aren't and the castle is tall, don't create flag yet + jsr GetAreaObjXPosition ;otherwise, obtain and save horizontal pixel coordinate + pha + jsr FindEmptyEnemySlot ;find an empty place on the enemy object buffer + pla + sta Enemy_X_Position,x ;then write horizontal coordinate for star flag + lda CurrentPageLoc + sta Enemy_PageLoc,x ;set page location for star flag + lda #$01 + sta Enemy_Y_HighPos,x ;set vertical high byte + sta Enemy_Flag,x ;set flag for buffer + lda #$90 + sta Enemy_Y_Position,x ;set vertical coordinate + lda #StarFlagObject ;set star flag value in buffer itself + sta Enemy_ID,x + rts +PlayerStop: ldy #$52 ;put brick at floor to stop player at end of level + sty MetatileBuffer+10 ;this is only done if we're on the second column +ExitCastle: rts + +;-------------------------------- + +WaterPipe: + jsr GetLrgObjAttrib ;get row and lower nybble + ldy AreaObjectLength,x ;get length (residual code, water pipe is 1 col thick) + ldx $07 ;get row + lda #$6b + sta MetatileBuffer,x ;draw something here and below it + lda #$6c + sta MetatileBuffer+1,x + rts + +;-------------------------------- +;$05 - used to store length of vertical shaft in RenderSidewaysPipe +;$06 - used to store leftover horizontal length in RenderSidewaysPipe +; and vertical length in VerticalPipe and GetPipeHeight + +IntroPipe: + ldy #$03 ;check if length set, if not set, set it + jsr ChkLrgObjFixedLength + ldy #$0a ;set fixed value and render the sideways part + jsr RenderSidewaysPipe + bcs NoBlankP ;if carry flag set, not time to draw vertical pipe part + ldx #$06 ;blank everything above the vertical pipe part +VPipeSectLoop: lda #$00 ;all the way to the top of the screen + sta MetatileBuffer,x ;because otherwise it will look like exit pipe + dex + bpl VPipeSectLoop + lda VerticalPipeData,y ;draw the end of the vertical pipe part + sta MetatileBuffer+7 +NoBlankP: rts + +SidePipeShaftData: + .db $15, $14 ;used to control whether or not vertical pipe shaft + .db $00, $00 ;is drawn, and if so, controls the metatile number +SidePipeTopPart: + .db $15, $1e ;top part of sideways part of pipe + .db $1d, $1c +SidePipeBottomPart: + .db $15, $21 ;bottom part of sideways part of pipe + .db $20, $1f + +ExitPipe: + ldy #$03 ;check if length set, if not set, set it + jsr ChkLrgObjFixedLength + jsr GetLrgObjAttrib ;get vertical length, then plow on through RenderSidewaysPipe + +RenderSidewaysPipe: + dey ;decrement twice to make room for shaft at bottom + dey ;and store here for now as vertical length + sty $05 + ldy AreaObjectLength,x ;get length left over and store here + sty $06 + ldx $05 ;get vertical length plus one, use as buffer offset + inx + lda SidePipeShaftData,y ;check for value $00 based on horizontal offset + cmp #$00 + beq DrawSidePart ;if found, do not draw the vertical pipe shaft + ldx #$00 + ldy $05 ;init buffer offset and get vertical length + jsr RenderUnderPart ;and render vertical shaft using tile number in A + clc ;clear carry flag to be used by IntroPipe +DrawSidePart: ldy $06 ;render side pipe part at the bottom + lda SidePipeTopPart,y + sta MetatileBuffer,x ;note that the pipe parts are stored + lda SidePipeBottomPart,y ;backwards horizontally + sta MetatileBuffer+1,x + rts + +VerticalPipeData: + .db $11, $10 ;used by pipes that lead somewhere + .db $15, $14 + .db $13, $12 ;used by decoration pipes + .db $15, $14 + +VerticalPipe: + jsr GetPipeHeight + lda $00 ;check to see if value was nullified earlier + beq WarpPipe ;(if d3, the usage control bit of second byte, was set) + iny + iny + iny + iny ;add four if usage control bit was not set +WarpPipe: tya ;save value in stack + pha + lda AreaNumber + ora WorldNumber ;if at world 1-1, do not add piranha plant ever + beq DrawPipe + ldy AreaObjectLength,x ;if on second column of pipe, branch + beq DrawPipe ;(because we only need to do this once) + jsr FindEmptyEnemySlot ;check for an empty moving data buffer space + bcs DrawPipe ;if not found, too many enemies, thus skip + jsr GetAreaObjXPosition ;get horizontal pixel coordinate + clc + adc #$08 ;add eight to put the piranha plant in the center + sta Enemy_X_Position,x ;store as enemy's horizontal coordinate + lda CurrentPageLoc ;add carry to current page number + adc #$00 + sta Enemy_PageLoc,x ;store as enemy's page coordinate + lda #$01 + sta Enemy_Y_HighPos,x + sta Enemy_Flag,x ;activate enemy flag + jsr GetAreaObjYPosition ;get piranha plant's vertical coordinate and store here + sta Enemy_Y_Position,x + lda #PiranhaPlant ;write piranha plant's value into buffer + sta Enemy_ID,x + jsr InitPiranhaPlant +DrawPipe: pla ;get value saved earlier and use as Y + tay + ldx $07 ;get buffer offset + lda VerticalPipeData,y ;draw the appropriate pipe with the Y we loaded earlier + sta MetatileBuffer,x ;render the top of the pipe + inx + lda VerticalPipeData+2,y ;render the rest of the pipe + ldy $06 ;subtract one from length and render the part underneath + dey + jmp RenderUnderPart + +GetPipeHeight: + ldy #$01 ;check for length loaded, if not, load + jsr ChkLrgObjFixedLength ;pipe length of 2 (horizontal) + jsr GetLrgObjAttrib + tya ;get saved lower nybble as height + and #$07 ;save only the three lower bits as + sta $06 ;vertical length, then load Y with + ldy AreaObjectLength,x ;length left over + rts + +FindEmptyEnemySlot: + ldx #$00 ;start at first enemy slot +EmptyChkLoop: clc ;clear carry flag by default + lda Enemy_Flag,x ;check enemy buffer for nonzero + beq ExitEmptyChk ;if zero, leave + inx + cpx #$05 ;if nonzero, check next value + bne EmptyChkLoop +ExitEmptyChk: rts ;if all values nonzero, carry flag is set + +;-------------------------------- + +Hole_Water: + jsr ChkLrgObjLength ;get low nybble and save as length + lda #$86 ;render waves + sta MetatileBuffer+10 + ldx #$0b + ldy #$01 ;now render the water underneath + lda #$87 + jmp RenderUnderPart + +;-------------------------------- + +QuestionBlockRow_High: + lda #$03 ;start on the fourth row + .db $2c ;BIT instruction opcode + +QuestionBlockRow_Low: + lda #$07 ;start on the eighth row + pha ;save whatever row to the stack for now + jsr ChkLrgObjLength ;get low nybble and save as length + pla + tax ;render question boxes with coins + lda #$c0 + sta MetatileBuffer,x + rts + +;-------------------------------- + +Bridge_High: + lda #$06 ;start on the seventh row from top of screen + .db $2c ;BIT instruction opcode + +Bridge_Middle: + lda #$07 ;start on the eighth row + .db $2c ;BIT instruction opcode + +Bridge_Low: + lda #$09 ;start on the tenth row + pha ;save whatever row to the stack for now + jsr ChkLrgObjLength ;get low nybble and save as length + pla + tax ;render bridge railing + lda #$0b + sta MetatileBuffer,x + inx + ldy #$00 ;now render the bridge itself + lda #$63 + jmp RenderUnderPart + +;-------------------------------- + +FlagBalls_Residual: + jsr GetLrgObjAttrib ;get low nybble from object byte + ldx #$02 ;render flag balls on third row from top + lda #$6d ;of screen downwards based on low nybble + jmp RenderUnderPart + +;-------------------------------- + +FlagpoleObject: + lda #$24 ;render flagpole ball on top + sta MetatileBuffer + ldx #$01 ;now render the flagpole shaft + ldy #$08 + lda #$25 + jsr RenderUnderPart + lda #$61 ;render solid block at the bottom + sta MetatileBuffer+10 + jsr GetAreaObjXPosition + sec ;get pixel coordinate of where the flagpole is, + sbc #$08 ;subtract eight pixels and use as horizontal + sta Enemy_X_Position+5 ;coordinate for the flag + lda CurrentPageLoc + sbc #$00 ;subtract borrow from page location and use as + sta Enemy_PageLoc+5 ;page location for the flag + lda #$30 + sta Enemy_Y_Position+5 ;set vertical coordinate for flag + lda #$b0 + sta FlagpoleFNum_Y_Pos ;set initial vertical coordinate for flagpole's floatey number + lda #FlagpoleFlagObject + sta Enemy_ID+5 ;set flag identifier, note that identifier and coordinates + inc Enemy_Flag+5 ;use last space in enemy object buffer + rts + +;-------------------------------- + +EndlessRope: + ldx #$00 ;render rope from the top to the bottom of screen + ldy #$0f + jmp DrawRope + +BalancePlatRope: + txa ;save object buffer offset for now + pha + ldx #$01 ;blank out all from second row to the bottom + ldy #$0f ;with blank used for balance platform rope + lda #$44 + jsr RenderUnderPart + pla ;get back object buffer offset + tax + jsr GetLrgObjAttrib ;get vertical length from lower nybble + ldx #$01 +DrawRope: lda #$40 ;render the actual rope + jmp RenderUnderPart + +;-------------------------------- + +CoinMetatileData: + .db $c3, $c2, $c2, $c2 + +RowOfCoins: + ldy AreaType ;get area type + lda CoinMetatileData,y ;load appropriate coin metatile + jmp GetRow + +;-------------------------------- + +C_ObjectRow: + .db $06, $07, $08 + +C_ObjectMetatile: + .db $c5, $0c, $89 + +CastleBridgeObj: + ldy #$0c ;load length of 13 columns + jsr ChkLrgObjFixedLength + jmp ChainObj + +AxeObj: + lda #$08 ;load bowser's palette into sprite portion of palette + sta VRAM_Buffer_AddrCtrl + +ChainObj: + ldy $00 ;get value loaded earlier from decoder + ldx C_ObjectRow-2,y ;get appropriate row and metatile for object + lda C_ObjectMetatile-2,y + jmp ColObj + +EmptyBlock: + jsr GetLrgObjAttrib ;get row location + ldx $07 + lda #$c4 +ColObj: ldy #$00 ;column length of 1 + jmp RenderUnderPart + +;-------------------------------- + +SolidBlockMetatiles: + .db $69, $61, $61, $62 + +BrickMetatiles: + .db $22, $51, $52, $52 + .db $88 ;used only by row of bricks object + +RowOfBricks: + ldy AreaType ;load area type obtained from area offset pointer + lda CloudTypeOverride ;check for cloud type override + beq DrawBricks + ldy #$04 ;if cloud type, override area type +DrawBricks: lda BrickMetatiles,y ;get appropriate metatile + jmp GetRow ;and go render it + +RowOfSolidBlocks: + ldy AreaType ;load area type obtained from area offset pointer + lda SolidBlockMetatiles,y ;get metatile +GetRow: pha ;store metatile here + jsr ChkLrgObjLength ;get row number, load length +DrawRow: ldx $07 + ldy #$00 ;set vertical height of 1 + pla + jmp RenderUnderPart ;render object + +ColumnOfBricks: + ldy AreaType ;load area type obtained from area offset + lda BrickMetatiles,y ;get metatile (no cloud override as for row) + jmp GetRow2 + +ColumnOfSolidBlocks: + ldy AreaType ;load area type obtained from area offset + lda SolidBlockMetatiles,y ;get metatile +GetRow2: pha ;save metatile to stack for now + jsr GetLrgObjAttrib ;get length and row + pla ;restore metatile + ldx $07 ;get starting row + jmp RenderUnderPart ;now render the column + +;-------------------------------- + +BulletBillCannon: + jsr GetLrgObjAttrib ;get row and length of bullet bill cannon + ldx $07 ;start at first row + lda #$64 ;render bullet bill cannon + sta MetatileBuffer,x + inx + dey ;done yet? + bmi SetupCannon + lda #$65 ;if not, render middle part + sta MetatileBuffer,x + inx + dey ;done yet? + bmi SetupCannon + lda #$66 ;if not, render bottom until length expires + jsr RenderUnderPart +SetupCannon: ldx Cannon_Offset ;get offset for data used by cannons and whirlpools + jsr GetAreaObjYPosition ;get proper vertical coordinate for cannon + sta Cannon_Y_Position,x ;and store it here + lda CurrentPageLoc + sta Cannon_PageLoc,x ;store page number for cannon here + jsr GetAreaObjXPosition ;get proper horizontal coordinate for cannon + sta Cannon_X_Position,x ;and store it here + inx + cpx #$06 ;increment and check offset + bcc StrCOffset ;if not yet reached sixth cannon, branch to save offset + ldx #$00 ;otherwise initialize it +StrCOffset: stx Cannon_Offset ;save new offset and leave + rts + +;-------------------------------- + +StaircaseHeightData: + .db $07, $07, $06, $05, $04, $03, $02, $01, $00 + +StaircaseRowData: + .db $03, $03, $04, $05, $06, $07, $08, $09, $0a + +StaircaseObject: + jsr ChkLrgObjLength ;check and load length + bcc NextStair ;if length already loaded, skip init part + lda #$09 ;start past the end for the bottom + sta StaircaseControl ;of the staircase +NextStair: dec StaircaseControl ;move onto next step (or first if starting) + ldy StaircaseControl + ldx StaircaseRowData,y ;get starting row and height to render + lda StaircaseHeightData,y + tay + lda #$61 ;now render solid block staircase + jmp RenderUnderPart + +;-------------------------------- + +Jumpspring: + jsr GetLrgObjAttrib + jsr FindEmptyEnemySlot ;find empty space in enemy object buffer + jsr GetAreaObjXPosition ;get horizontal coordinate for jumpspring + sta Enemy_X_Position,x ;and store + lda CurrentPageLoc ;store page location of jumpspring + sta Enemy_PageLoc,x + jsr GetAreaObjYPosition ;get vertical coordinate for jumpspring + sta Enemy_Y_Position,x ;and store + sta Jumpspring_FixedYPos,x ;store as permanent coordinate here + lda #JumpspringObject + sta Enemy_ID,x ;write jumpspring object to enemy object buffer + ldy #$01 + sty Enemy_Y_HighPos,x ;store vertical high byte + inc Enemy_Flag,x ;set flag for enemy object buffer + ldx $07 + lda #$67 ;draw metatiles in two rows where jumpspring is + sta MetatileBuffer,x + lda #$68 + sta MetatileBuffer+1,x + rts + +;-------------------------------- +;$07 - used to save ID of brick object + +Hidden1UpBlock: + lda Hidden1UpFlag ;if flag not set, do not render object + beq ExitDecBlock + lda #$00 ;if set, init for the next one + sta Hidden1UpFlag + jmp BrickWithItem ;jump to code shared with unbreakable bricks + +QuestionBlock: + jsr GetAreaObjectID ;get value from level decoder routine + jmp DrawQBlk ;go to render it + +BrickWithCoins: + lda #$00 ;initialize multi-coin timer flag + sta BrickCoinTimerFlag + +BrickWithItem: + jsr GetAreaObjectID ;save area object ID + sty $07 + lda #$00 ;load default adder for bricks with lines + ldy AreaType ;check level type for ground level + dey + beq BWithL ;if ground type, do not start with 5 + lda #$05 ;otherwise use adder for bricks without lines +BWithL: clc ;add object ID to adder + adc $07 + tay ;use as offset for metatile +DrawQBlk: lda BrickQBlockMetatiles,y ;get appropriate metatile for brick (question block + pha ;if branched to here from question block routine) + jsr GetLrgObjAttrib ;get row from location byte + jmp DrawRow ;now render the object + +GetAreaObjectID: + lda $00 ;get value saved from area parser routine + sec + sbc #$00 ;possibly residual code + tay ;save to Y +ExitDecBlock: rts + +;-------------------------------- + +HoleMetatiles: + .db $87, $00, $00, $00 + +Hole_Empty: + jsr ChkLrgObjLength ;get lower nybble and save as length + bcc NoWhirlP ;skip this part if length already loaded + lda AreaType ;check for water type level + bne NoWhirlP ;if not water type, skip this part + ldx Whirlpool_Offset ;get offset for data used by cannons and whirlpools + jsr GetAreaObjXPosition ;get proper vertical coordinate of where we're at + sec + sbc #$10 ;subtract 16 pixels + sta Whirlpool_LeftExtent,x ;store as left extent of whirlpool + lda CurrentPageLoc ;get page location of where we're at + sbc #$00 ;subtract borrow + sta Whirlpool_PageLoc,x ;save as page location of whirlpool + iny + iny ;increment length by 2 + tya + asl ;multiply by 16 to get size of whirlpool + asl ;note that whirlpool will always be + asl ;two blocks bigger than actual size of hole + asl ;and extend one block beyond each edge + sta Whirlpool_Length,x ;save size of whirlpool here + inx + cpx #$05 ;increment and check offset + bcc StrWOffset ;if not yet reached fifth whirlpool, branch to save offset + ldx #$00 ;otherwise initialize it +StrWOffset: stx Whirlpool_Offset ;save new offset here +NoWhirlP: ldx AreaType ;get appropriate metatile, then + lda HoleMetatiles,x ;render the hole proper + ldx #$08 + ldy #$0f ;start at ninth row and go to bottom, run RenderUnderPart + +;-------------------------------- + +RenderUnderPart: + sty AreaObjectHeight ;store vertical length to render + ldy MetatileBuffer,x ;check current spot to see if there's something + beq DrawThisRow ;we need to keep, if nothing, go ahead + cpy #$17 + beq WaitOneRow ;if middle part (tree ledge), wait until next row + cpy #$1a + beq WaitOneRow ;if middle part (mushroom ledge), wait until next row + cpy #$c0 + beq DrawThisRow ;if question block w/ coin, overwrite + cpy #$c0 + bcs WaitOneRow ;if any other metatile with palette 3, wait until next row + cpy #$54 + bne DrawThisRow ;if cracked rock terrain, overwrite + cmp #$50 + beq WaitOneRow ;if stem top of mushroom, wait until next row +DrawThisRow: sta MetatileBuffer,x ;render contents of A from routine that called this +WaitOneRow: inx + cpx #$0d ;stop rendering if we're at the bottom of the screen + bcs ExitUPartR + ldy AreaObjectHeight ;decrement, and stop rendering if there is no more length + dey + bpl RenderUnderPart +ExitUPartR: rts + +;-------------------------------- + +ChkLrgObjLength: + jsr GetLrgObjAttrib ;get row location and size (length if branched to from here) + +ChkLrgObjFixedLength: + lda AreaObjectLength,x ;check for set length counter + clc ;clear carry flag for not just starting + bpl LenSet ;if counter not set, load it, otherwise leave alone + tya ;save length into length counter + sta AreaObjectLength,x + sec ;set carry flag if just starting +LenSet: rts + + +GetLrgObjAttrib: + ldy AreaObjOffsetBuffer,x ;get offset saved from area obj decoding routine + lda (AreaData),y ;get first byte of level object + and #%00001111 + sta $07 ;save row location + iny + lda (AreaData),y ;get next byte, save lower nybble (length or height) + and #%00001111 ;as Y, then leave + tay + rts + +;-------------------------------- + +GetAreaObjXPosition: + lda CurrentColumnPos ;multiply current offset where we're at by 16 + asl ;to obtain horizontal pixel coordinate + asl + asl + asl + rts + +;-------------------------------- + +GetAreaObjYPosition: + lda $07 ;multiply value by 16 + asl + asl ;this will give us the proper vertical pixel coordinate + asl + asl + clc + adc #32 ;add 32 pixels for the status bar + rts + +;------------------------------------------------------------------------------------- +;$06-$07 - used to store block buffer address used as indirect + +BlockBufferAddr: + .db Block_Buffer_1, >Block_Buffer_2 + +GetBlockBufferAddr: + pha ;take value of A, save + lsr ;move high nybble to low + lsr + lsr + lsr + tay ;use nybble as pointer to high byte + lda BlockBufferAddr+2,y ;of indirect here + sta $07 + pla + and #%00001111 ;pull from stack, mask out high nybble + clc + adc BlockBufferAddr,y ;add to low byte + sta $06 ;store here and leave + rts + +;------------------------------------------------------------------------------------- + +;unused space + .db $ff, $ff + +;------------------------------------------------------------------------------------- + +AreaDataOfsLoopback: + .db $12, $36, $0e, $0e, $0e, $32, $32, $32, $0a, $26, $40 + +;------------------------------------------------------------------------------------- + +LoadAreaPointer: + jsr FindAreaPointer ;find it and store it here + sta AreaPointer +GetAreaType: and #%01100000 ;mask out all but d6 and d5 + asl + rol + rol + rol ;make %0xx00000 into %000000xx + sta AreaType ;save 2 MSB as area type + rts + +FindAreaPointer: + ldy WorldNumber ;load offset from world variable + lda WorldAddrOffsets,y + clc ;add area number used to find data + adc AreaNumber + tay + lda AreaAddrOffsets,y ;from there we have our area pointer + rts + + +GetAreaDataAddrs: + lda AreaPointer ;use 2 MSB for Y + jsr GetAreaType + tay + lda AreaPointer ;mask out all but 5 LSB + and #%00011111 + sta AreaAddrsLOffset ;save as low offset + lda EnemyAddrHOffsets,y ;load base value with 2 altered MSB, + clc ;then add base value to 5 LSB, result + adc AreaAddrsLOffset ;becomes offset for level data + tay + lda EnemyDataAddrLow,y ;use offset to load pointer + sta EnemyDataLow + lda EnemyDataAddrHigh,y + sta EnemyDataHigh + ldy AreaType ;use area type as offset + lda AreaDataHOffsets,y ;do the same thing but with different base value + clc + adc AreaAddrsLOffset + tay + lda AreaDataAddrLow,y ;use this offset to load another pointer + sta AreaDataLow + lda AreaDataAddrHigh,y + sta AreaDataHigh + ldy #$00 ;load first byte of header + lda (AreaData),y + pha ;save it to the stack for now + and #%00000111 ;save 3 LSB for foreground scenery or bg color control + cmp #$04 + bcc StoreFore + sta BackgroundColorCtrl ;if 4 or greater, save value here as bg color control + lda #$00 +StoreFore: sta ForegroundScenery ;if less, save value here as foreground scenery + pla ;pull byte from stack and push it back + pha + and #%00111000 ;save player entrance control bits + lsr ;shift bits over to LSBs + lsr + lsr + sta PlayerEntranceCtrl ;save value here as player entrance control + pla ;pull byte again but do not push it back + and #%11000000 ;save 2 MSB for game timer setting + clc + rol ;rotate bits over to LSBs + rol + rol + sta GameTimerSetting ;save value here as game timer setting + iny + lda (AreaData),y ;load second byte of header + pha ;save to stack + and #%00001111 ;mask out all but lower nybble + sta TerrainControl + pla ;pull and push byte to copy it to A + pha + and #%00110000 ;save 2 MSB for background scenery type + lsr + lsr ;shift bits to LSBs + lsr + lsr + sta BackgroundScenery ;save as background scenery + pla + and #%11000000 + clc + rol ;rotate bits over to LSBs + rol + rol + cmp #%00000011 ;if set to 3, store here + bne StoreStyle ;and nullify other value + sta CloudTypeOverride ;otherwise store value in other place + lda #$00 +StoreStyle: sta AreaStyle + lda AreaDataLow ;increment area data address by 2 bytes + clc + adc #$02 + sta AreaDataLow + lda AreaDataHigh + adc #$00 + sta AreaDataHigh + rts + +;------------------------------------------------------------------------------------- +;GAME LEVELS DATA + +WorldAddrOffsets: + .db World1Areas-AreaAddrOffsets, World2Areas-AreaAddrOffsets + .db World3Areas-AreaAddrOffsets, World4Areas-AreaAddrOffsets + .db World5Areas-AreaAddrOffsets, World6Areas-AreaAddrOffsets + .db World7Areas-AreaAddrOffsets, World8Areas-AreaAddrOffsets + +AreaAddrOffsets: +World1Areas: .db $25, $29, $c0, $26, $60 +World2Areas: .db $28, $29, $01, $27, $62 +World3Areas: .db $24, $35, $20, $63 +World4Areas: .db $22, $29, $41, $2c, $61 +World5Areas: .db $2a, $31, $26, $62 +World6Areas: .db $2e, $23, $2d, $60 +World7Areas: .db $33, $29, $01, $27, $64 +World8Areas: .db $30, $32, $21, $65 + +;bonus area data offsets, included here for comparison purposes +;underground bonus area - c2 +;cloud area 1 (day) - 2b +;cloud area 2 (night) - 34 +;water area (5-2/6-2) - 00 +;water area (8-4) - 02 +;warp zone area (4-2) - 2f + +EnemyAddrHOffsets: + .db $1f, $06, $1c, $00 + +EnemyDataAddrLow: + .db E_CastleArea1, >E_CastleArea2, >E_CastleArea3, >E_CastleArea4, >E_CastleArea5, >E_CastleArea6 + .db >E_GroundArea1, >E_GroundArea2, >E_GroundArea3, >E_GroundArea4, >E_GroundArea5, >E_GroundArea6 + .db >E_GroundArea7, >E_GroundArea8, >E_GroundArea9, >E_GroundArea10, >E_GroundArea11, >E_GroundArea12 + .db >E_GroundArea13, >E_GroundArea14, >E_GroundArea15, >E_GroundArea16, >E_GroundArea17, >E_GroundArea18 + .db >E_GroundArea19, >E_GroundArea20, >E_GroundArea21, >E_GroundArea22, >E_UndergroundArea1 + .db >E_UndergroundArea2, >E_UndergroundArea3, >E_WaterArea1, >E_WaterArea2, >E_WaterArea3 + +AreaDataHOffsets: + .db $00, $03, $19, $1c + +AreaDataAddrLow: + .db L_WaterArea1, >L_WaterArea2, >L_WaterArea3, >L_GroundArea1, >L_GroundArea2, >L_GroundArea3 + .db >L_GroundArea4, >L_GroundArea5, >L_GroundArea6, >L_GroundArea7, >L_GroundArea8, >L_GroundArea9 + .db >L_GroundArea10, >L_GroundArea11, >L_GroundArea12, >L_GroundArea13, >L_GroundArea14, >L_GroundArea15 + .db >L_GroundArea16, >L_GroundArea17, >L_GroundArea18, >L_GroundArea19, >L_GroundArea20, >L_GroundArea21 + .db >L_GroundArea22, >L_UndergroundArea1, >L_UndergroundArea2, >L_UndergroundArea3, >L_CastleArea1 + .db >L_CastleArea2, >L_CastleArea3, >L_CastleArea4, >L_CastleArea5, >L_CastleArea6 + +;ENEMY OBJECT DATA + +;level 1-4/6-4 +E_CastleArea1: + .db $76, $dd, $bb, $4c, $ea, $1d, $1b, $cc, $56, $5d + .db $16, $9d, $c6, $1d, $36, $9d, $c9, $1d, $04, $db + .db $49, $1d, $84, $1b, $c9, $5d, $88, $95, $0f, $08 + .db $30, $4c, $78, $2d, $a6, $28, $90, $b5 + .db $ff + +;level 4-4 +E_CastleArea2: + .db $0f, $03, $56, $1b, $c9, $1b, $0f, $07, $36, $1b + .db $aa, $1b, $48, $95, $0f, $0a, $2a, $1b, $5b, $0c + .db $78, $2d, $90, $b5 + .db $ff + +;level 2-4/5-4 +E_CastleArea3: + .db $0b, $8c, $4b, $4c, $77, $5f, $eb, $0c, $bd, $db + .db $19, $9d, $75, $1d, $7d, $5b, $d9, $1d, $3d, $dd + .db $99, $1d, $26, $9d, $5a, $2b, $8a, $2c, $ca, $1b + .db $20, $95, $7b, $5c, $db, $4c, $1b, $cc, $3b, $cc + .db $78, $2d, $a6, $28, $90, $b5 + .db $ff + +;level 3-4 +E_CastleArea4: + .db $0b, $8c, $3b, $1d, $8b, $1d, $ab, $0c, $db, $1d + .db $0f, $03, $65, $1d, $6b, $1b, $05, $9d, $0b, $1b + .db $05, $9b, $0b, $1d, $8b, $0c, $1b, $8c, $70, $15 + .db $7b, $0c, $db, $0c, $0f, $08, $78, $2d, $a6, $28 + .db $90, $b5 + .db $ff + +;level 7-4 +E_CastleArea5: + .db $27, $a9, $4b, $0c, $68, $29, $0f, $06, $77, $1b + .db $0f, $0b, $60, $15, $4b, $8c, $78, $2d, $90, $b5 + .db $ff + +;level 8-4 +E_CastleArea6: + .db $0f, $03, $8e, $65, $e1, $bb, $38, $6d, $a8, $3e, $e5, $e7 + .db $0f, $08, $0b, $02, $2b, $02, $5e, $65, $e1, $bb, $0e + .db $db, $0e, $bb, $8e, $db, $0e, $fe, $65, $ec, $0f, $0d + .db $4e, $65, $e1, $0f, $0e, $4e, $02, $e0, $0f, $10, $fe, $e5, $e1 + .db $1b, $85, $7b, $0c, $5b, $95, $78, $2d, $90, $b5 + .db $ff + +;level 3-3 +E_GroundArea1: + .db $a5, $86, $e4, $28, $18, $a8, $45, $83, $69, $03 + .db $c6, $29, $9b, $83, $16, $a4, $88, $24, $e9, $28 + .db $05, $a8, $7b, $28, $24, $8f, $c8, $03, $e8, $03 + .db $46, $a8, $85, $24, $c8, $24 + .db $ff + +;level 8-3 +E_GroundArea2: + .db $eb, $8e, $0f, $03, $fb, $05, $17, $85, $db, $8e + .db $0f, $07, $57, $05, $7b, $05, $9b, $80, $2b, $85 + .db $fb, $05, $0f, $0b, $1b, $05, $9b, $05 + .db $ff + +;level 4-1 +E_GroundArea3: + .db $2e, $c2, $66, $e2, $11, $0f, $07, $02, $11, $0f, $0c + .db $12, $11 + .db $ff + +;level 6-2 +E_GroundArea4: + .db $0e, $c2, $a8, $ab, $00, $bb, $8e, $6b, $82, $de, $00, $a0 + .db $33, $86, $43, $06, $3e, $b4, $a0, $cb, $02, $0f, $07 + .db $7e, $42, $a6, $83, $02, $0f, $0a, $3b, $02, $cb, $37 + .db $0f, $0c, $e3, $0e + .db $ff + +;level 3-1 +E_GroundArea5: + .db $9b, $8e, $ca, $0e, $ee, $42, $44, $5b, $86, $80, $b8 + .db $1b, $80, $50, $ba, $10, $b7, $5b, $00, $17, $85 + .db $4b, $05, $fe, $34, $40, $b7, $86, $c6, $06, $5b, $80 + .db $83, $00, $d0, $38, $5b, $8e, $8a, $0e, $a6, $00 + .db $bb, $0e, $c5, $80, $f3, $00 + .db $ff + +;level 1-1 +E_GroundArea6: + .db $1e, $c2, $00, $6b, $06, $8b, $86, $63, $b7, $0f, $05 + .db $03, $06, $23, $06, $4b, $b7, $bb, $00, $5b, $b7 + .db $fb, $37, $3b, $b7, $0f, $0b, $1b, $37 + .db $ff + +;level 1-3/5-3 +E_GroundArea7: + .db $2b, $d7, $e3, $03, $c2, $86, $e2, $06, $76, $a5 + .db $a3, $8f, $03, $86, $2b, $57, $68, $28, $e9, $28 + .db $e5, $83, $24, $8f, $36, $a8, $5b, $03 + .db $ff + +;level 2-3/7-3 +E_GroundArea8: + .db $0f, $02, $78, $40, $48, $ce, $f8, $c3, $f8, $c3 + .db $0f, $07, $7b, $43, $c6, $d0, $0f, $8a, $c8, $50 + .db $ff + +;level 2-1 +E_GroundArea9: + .db $85, $86, $0b, $80, $1b, $00, $db, $37, $77, $80 + .db $eb, $37, $fe, $2b, $20, $2b, $80, $7b, $38, $ab, $b8 + .db $77, $86, $fe, $42, $20, $49, $86, $8b, $06, $9b, $80 + .db $7b, $8e, $5b, $b7, $9b, $0e, $bb, $0e, $9b, $80 +;end of data terminator here is also used by pipe intro area +E_GroundArea10: + .db $ff + +;level 5-1 +E_GroundArea11: + .db $0b, $80, $60, $38, $10, $b8, $c0, $3b, $db, $8e + .db $40, $b8, $f0, $38, $7b, $8e, $a0, $b8, $c0, $b8 + .db $fb, $00, $a0, $b8, $30, $bb, $ee, $42, $88, $0f, $0b + .db $2b, $0e, $67, $0e + .db $ff + +;cloud level used in levels 2-1 and 5-2 +E_GroundArea12: + .db $0a, $aa, $0e, $28, $2a, $0e, $31, $88 + .db $ff + +;level 4-3 +E_GroundArea13: + .db $c7, $83, $d7, $03, $42, $8f, $7a, $03, $05, $a4 + .db $78, $24, $a6, $25, $e4, $25, $4b, $83, $e3, $03 + .db $05, $a4, $89, $24, $b5, $24, $09, $a4, $65, $24 + .db $c9, $24, $0f, $08, $85, $25 + .db $ff + +;level 6-3 +E_GroundArea14: + .db $cd, $a5, $b5, $a8, $07, $a8, $76, $28, $cc, $25 + .db $65, $a4, $a9, $24, $e5, $24, $19, $a4, $0f, $07 + .db $95, $28, $e6, $24, $19, $a4, $d7, $29, $16, $a9 + .db $58, $29, $97, $29 + .db $ff + +;level 6-1 +E_GroundArea15: + .db $0f, $02, $02, $11, $0f, $07, $02, $11 + .db $ff + +;warp zone area used in level 4-2 +E_GroundArea16: + .db $ff + +;level 8-1 +E_GroundArea17: + .db $2b, $82, $ab, $38, $de, $42, $e2, $1b, $b8, $eb + .db $3b, $db, $80, $8b, $b8, $1b, $82, $fb, $b8, $7b + .db $80, $fb, $3c, $5b, $bc, $7b, $b8, $1b, $8e, $cb + .db $0e, $1b, $8e, $0f, $0d, $2b, $3b, $bb, $b8, $eb, $82 + .db $4b, $b8, $bb, $38, $3b, $b7, $bb, $02, $0f, $13 + .db $1b, $00, $cb, $80, $6b, $bc + .db $ff + +;level 5-2 +E_GroundArea18: + .db $7b, $80, $ae, $00, $80, $8b, $8e, $e8, $05, $f9, $86 + .db $17, $86, $16, $85, $4e, $2b, $80, $ab, $8e, $87, $85 + .db $c3, $05, $8b, $82, $9b, $02, $ab, $02, $bb, $86 + .db $cb, $06, $d3, $03, $3b, $8e, $6b, $0e, $a7, $8e + .db $ff + +;level 8-2 +E_GroundArea19: + .db $29, $8e, $52, $11, $83, $0e, $0f, $03, $9b, $0e + .db $2b, $8e, $5b, $0e, $cb, $8e, $fb, $0e, $fb, $82 + .db $9b, $82, $bb, $02, $fe, $42, $e8, $bb, $8e, $0f, $0a + .db $ab, $0e, $cb, $0e, $f9, $0e, $88, $86, $a6, $06 + .db $db, $02, $b6, $8e + .db $ff + +;level 7-1 +E_GroundArea20: + .db $ab, $ce, $de, $42, $c0, $cb, $ce, $5b, $8e, $1b, $ce + .db $4b, $85, $67, $45, $0f, $07, $2b, $00, $7b, $85 + .db $97, $05, $0f, $0a, $92, $02 + .db $ff + +;cloud level used in levels 3-1 and 6-2 +E_GroundArea21: + .db $0a, $aa, $0e, $24, $4a, $1e, $23, $aa + .db $ff + +;level 3-2 +E_GroundArea22: + .db $1b, $80, $bb, $38, $4b, $bc, $eb, $3b, $0f, $04 + .db $2b, $00, $ab, $38, $eb, $00, $cb, $8e, $fb, $80 + .db $ab, $b8, $6b, $80, $fb, $3c, $9b, $bb, $5b, $bc + .db $fb, $00, $6b, $b8, $fb, $38 + .db $ff + +;level 1-2 +E_UndergroundArea1: + .db $0b, $86, $1a, $06, $db, $06, $de, $c2, $02, $f0, $3b + .db $bb, $80, $eb, $06, $0b, $86, $93, $06, $f0, $39 + .db $0f, $06, $60, $b8, $1b, $86, $a0, $b9, $b7, $27 + .db $bd, $27, $2b, $83, $a1, $26, $a9, $26, $ee, $25, $0b + .db $27, $b4 + .db $ff + +;level 4-2 +E_UndergroundArea2: + .db $0f, $02, $1e, $2f, $60, $e0, $3a, $a5, $a7, $db, $80 + .db $3b, $82, $8b, $02, $fe, $42, $68, $70, $bb, $25, $a7 + .db $2c, $27, $b2, $26, $b9, $26, $9b, $80, $a8, $82 + .db $b5, $27, $bc, $27, $b0, $bb, $3b, $82, $87, $34 + .db $ee, $25, $6b + .db $ff + +;underground bonus rooms area used in many levels +E_UndergroundArea3: + .db $1e, $a5, $0a, $2e, $28, $27, $2e, $33, $c7, $0f, $03, $1e, $40, $07 + .db $2e, $30, $e7, $0f, $05, $1e, $24, $44, $0f, $07, $1e, $22, $6a + .db $2e, $23, $ab, $0f, $09, $1e, $41, $68, $1e, $2a, $8a, $2e, $23, $a2 + .db $2e, $32, $ea + .db $ff + +;water area used in levels 5-2 and 6-2 +E_WaterArea1: + .db $3b, $87, $66, $27, $cc, $27, $ee, $31, $87, $ee, $23, $a7 + .db $3b, $87, $db, $07 + .db $ff + +;level 2-2/7-2 +E_WaterArea2: + .db $0f, $01, $2e, $25, $2b, $2e, $25, $4b, $4e, $25, $cb, $6b, $07 + .db $97, $47, $e9, $87, $47, $c7, $7a, $07, $d6, $c7 + .db $78, $07, $38, $87, $ab, $47, $e3, $07, $9b, $87 + .db $0f, $09, $68, $47, $db, $c7, $3b, $c7 + .db $ff + +;water area used in level 8-4 +E_WaterArea3: + .db $47, $9b, $cb, $07, $fa, $1d, $86, $9b, $3a, $87 + .db $56, $07, $88, $1b, $07, $9d, $2e, $65, $f0 + .db $ff + +;AREA OBJECT DATA + +;level 1-4/6-4 +L_CastleArea1: + .db $9b, $07 + .db $05, $32, $06, $33, $07, $34, $ce, $03, $dc, $51 + .db $ee, $07, $73, $e0, $74, $0a, $7e, $06, $9e, $0a + .db $ce, $06, $e4, $00, $e8, $0a, $fe, $0a, $2e, $89 + .db $4e, $0b, $54, $0a, $14, $8a, $c4, $0a, $34, $8a + .db $7e, $06, $c7, $0a, $01, $e0, $02, $0a, $47, $0a + .db $81, $60, $82, $0a, $c7, $0a, $0e, $87, $7e, $02 + .db $a7, $02, $b3, $02, $d7, $02, $e3, $02, $07, $82 + .db $13, $02, $3e, $06, $7e, $02, $ae, $07, $fe, $0a + .db $0d, $c4, $cd, $43, $ce, $09, $de, $0b, $dd, $42 + .db $fe, $02, $5d, $c7 + .db $fd + +;level 4-4 +L_CastleArea2: + .db $5b, $07 + .db $05, $32, $06, $33, $07, $34, $5e, $0a, $68, $64 + .db $98, $64, $a8, $64, $ce, $06, $fe, $02, $0d, $01 + .db $1e, $0e, $7e, $02, $94, $63, $b4, $63, $d4, $63 + .db $f4, $63, $14, $e3, $2e, $0e, $5e, $02, $64, $35 + .db $88, $72, $be, $0e, $0d, $04, $ae, $02, $ce, $08 + .db $cd, $4b, $fe, $02, $0d, $05, $68, $31, $7e, $0a + .db $96, $31, $a9, $63, $a8, $33, $d5, $30, $ee, $02 + .db $e6, $62, $f4, $61, $04, $b1, $08, $3f, $44, $33 + .db $94, $63, $a4, $31, $e4, $31, $04, $bf, $08, $3f + .db $04, $bf, $08, $3f, $cd, $4b, $03, $e4, $0e, $03 + .db $2e, $01, $7e, $06, $be, $02, $de, $06, $fe, $0a + .db $0d, $c4, $cd, $43, $ce, $09, $de, $0b, $dd, $42 + .db $fe, $02, $5d, $c7 + .db $fd + +;level 2-4/5-4 +L_CastleArea3: + .db $9b, $07 + .db $05, $32, $06, $33, $07, $34, $fe, $00, $27, $b1 + .db $65, $32, $75, $0a, $71, $00, $b7, $31, $08, $e4 + .db $18, $64, $1e, $04, $57, $3b, $bb, $0a, $17, $8a + .db $27, $3a, $73, $0a, $7b, $0a, $d7, $0a, $e7, $3a + .db $3b, $8a, $97, $0a, $fe, $08, $24, $8a, $2e, $00 + .db $3e, $40, $38, $64, $6f, $00, $9f, $00, $be, $43 + .db $c8, $0a, $c9, $63, $ce, $07, $fe, $07, $2e, $81 + .db $66, $42, $6a, $42, $79, $0a, $be, $00, $c8, $64 + .db $f8, $64, $08, $e4, $2e, $07, $7e, $03, $9e, $07 + .db $be, $03, $de, $07, $fe, $0a, $03, $a5, $0d, $44 + .db $cd, $43, $ce, $09, $dd, $42, $de, $0b, $fe, $02 + .db $5d, $c7 + .db $fd + +;level 3-4 +L_CastleArea4: + .db $9b, $07 + .db $05, $32, $06, $33, $07, $34, $fe, $06, $0c, $81 + .db $39, $0a, $5c, $01, $89, $0a, $ac, $01, $d9, $0a + .db $fc, $01, $2e, $83, $a7, $01, $b7, $00, $c7, $01 + .db $de, $0a, $fe, $02, $4e, $83, $5a, $32, $63, $0a + .db $69, $0a, $7e, $02, $ee, $03, $fa, $32, $03, $8a + .db $09, $0a, $1e, $02, $ee, $03, $fa, $32, $03, $8a + .db $09, $0a, $14, $42, $1e, $02, $7e, $0a, $9e, $07 + .db $fe, $0a, $2e, $86, $5e, $0a, $8e, $06, $be, $0a + .db $ee, $07, $3e, $83, $5e, $07, $fe, $0a, $0d, $c4 + .db $41, $52, $51, $52, $cd, $43, $ce, $09, $de, $0b + .db $dd, $42, $fe, $02, $5d, $c7 + .db $fd + +;level 7-4 +L_CastleArea5: + .db $5b, $07 + .db $05, $32, $06, $33, $07, $34, $fe, $0a, $ae, $86 + .db $be, $07, $fe, $02, $0d, $02, $27, $32, $46, $61 + .db $55, $62, $5e, $0e, $1e, $82, $68, $3c, $74, $3a + .db $7d, $4b, $5e, $8e, $7d, $4b, $7e, $82, $84, $62 + .db $94, $61, $a4, $31, $bd, $4b, $ce, $06, $fe, $02 + .db $0d, $06, $34, $31, $3e, $0a, $64, $32, $75, $0a + .db $7b, $61, $a4, $33, $ae, $02, $de, $0e, $3e, $82 + .db $64, $32, $78, $32, $b4, $36, $c8, $36, $dd, $4b + .db $44, $b2, $58, $32, $94, $63, $a4, $3e, $ba, $30 + .db $c9, $61, $ce, $06, $dd, $4b, $ce, $86, $dd, $4b + .db $fe, $02, $2e, $86, $5e, $02, $7e, $06, $fe, $02 + .db $1e, $86, $3e, $02, $5e, $06, $7e, $02, $9e, $06 + .db $fe, $0a, $0d, $c4, $cd, $43, $ce, $09, $de, $0b + .db $dd, $42, $fe, $02, $5d, $c7 + .db $fd + +;level 8-4 +L_CastleArea6: + .db $5b, $06 + .db $05, $32, $06, $33, $07, $34, $5e, $0a, $ae, $02 + .db $0d, $01, $39, $73, $0d, $03, $39, $7b, $4d, $4b + .db $de, $06, $1e, $8a, $ae, $06, $c4, $33, $16, $fe + .db $a5, $77, $fe, $02, $fe, $82, $0d, $07, $39, $73 + .db $a8, $74, $ed, $4b, $49, $fb, $e8, $74, $fe, $0a + .db $2e, $82, $67, $02, $84, $7a, $87, $31, $0d, $0b + .db $fe, $02, $0d, $0c, $39, $73, $5e, $06, $c6, $76 + .db $45, $ff, $be, $0a, $dd, $48, $fe, $06, $3d, $cb + .db $46, $7e, $ad, $4a, $fe, $82, $39, $f3, $a9, $7b + .db $4e, $8a, $9e, $07, $fe, $0a, $0d, $c4, $cd, $43 + .db $ce, $09, $de, $0b, $dd, $42, $fe, $02, $5d, $c7 + .db $fd + +;level 3-3 +L_GroundArea1: + .db $94, $11 + .db $0f, $26, $fe, $10, $28, $94, $65, $15, $eb, $12 + .db $fa, $41, $4a, $96, $54, $40, $a4, $42, $b7, $13 + .db $e9, $19, $f5, $15, $11, $80, $47, $42, $71, $13 + .db $80, $41, $15, $92, $1b, $1f, $24, $40, $55, $12 + .db $64, $40, $95, $12, $a4, $40, $d2, $12, $e1, $40 + .db $13, $c0, $2c, $17, $2f, $12, $49, $13, $83, $40 + .db $9f, $14, $a3, $40, $17, $92, $83, $13, $92, $41 + .db $b9, $14, $c5, $12, $c8, $40, $d4, $40, $4b, $92 + .db $78, $1b, $9c, $94, $9f, $11, $df, $14, $fe, $11 + .db $7d, $c1, $9e, $42, $cf, $20 + .db $fd + +;level 8-3 +L_GroundArea2: + .db $90, $b1 + .db $0f, $26, $29, $91, $7e, $42, $fe, $40, $28, $92 + .db $4e, $42, $2e, $c0, $57, $73, $c3, $25, $c7, $27 + .db $23, $84, $33, $20, $5c, $01, $77, $63, $88, $62 + .db $99, $61, $aa, $60, $bc, $01, $ee, $42, $4e, $c0 + .db $69, $11, $7e, $42, $de, $40, $f8, $62, $0e, $c2 + .db $ae, $40, $d7, $63, $e7, $63, $33, $a7, $37, $27 + .db $43, $04, $cc, $01, $e7, $73, $0c, $81, $3e, $42 + .db $0d, $0a, $5e, $40, $88, $72, $be, $42, $e7, $87 + .db $fe, $40, $39, $e1, $4e, $00, $69, $60, $87, $60 + .db $a5, $60, $c3, $31, $fe, $31, $6d, $c1, $be, $42 + .db $ef, $20 + .db $fd + +;level 4-1 +L_GroundArea3: + .db $52, $21 + .db $0f, $20, $6e, $40, $58, $f2, $93, $01, $97, $00 + .db $0c, $81, $97, $40, $a6, $41, $c7, $40, $0d, $04 + .db $03, $01, $07, $01, $23, $01, $27, $01, $ec, $03 + .db $ac, $f3, $c3, $03, $78, $e2, $94, $43, $47, $f3 + .db $74, $43, $47, $fb, $74, $43, $2c, $f1, $4c, $63 + .db $47, $00, $57, $21, $5c, $01, $7c, $72, $39, $f1 + .db $ec, $02, $4c, $81, $d8, $62, $ec, $01, $0d, $0d + .db $0f, $38, $c7, $07, $ed, $4a, $1d, $c1, $5f, $26 + .db $fd + +;level 6-2 +L_GroundArea4: + .db $54, $21 + .db $0f, $26, $a7, $22, $37, $fb, $73, $20, $83, $07 + .db $87, $02, $93, $20, $c7, $73, $04, $f1, $06, $31 + .db $39, $71, $59, $71, $e7, $73, $37, $a0, $47, $04 + .db $86, $7c, $e5, $71, $e7, $31, $33, $a4, $39, $71 + .db $a9, $71, $d3, $23, $08, $f2, $13, $05, $27, $02 + .db $49, $71, $75, $75, $e8, $72, $67, $f3, $99, $71 + .db $e7, $20, $f4, $72, $f7, $31, $17, $a0, $33, $20 + .db $39, $71, $73, $28, $bc, $05, $39, $f1, $79, $71 + .db $a6, $21, $c3, $06, $d3, $20, $dc, $00, $fc, $00 + .db $07, $a2, $13, $21, $5f, $32, $8c, $00, $98, $7a + .db $c7, $63, $d9, $61, $03, $a2, $07, $22, $74, $72 + .db $77, $31, $e7, $73, $39, $f1, $58, $72, $77, $73 + .db $d8, $72, $7f, $b1, $97, $73, $b6, $64, $c5, $65 + .db $d4, $66, $e3, $67, $f3, $67, $8d, $c1, $cf, $26 + .db $fd + +;level 3-1 +L_GroundArea5: + .db $52, $31 + .db $0f, $20, $6e, $66, $07, $81, $36, $01, $66, $00 + .db $a7, $22, $08, $f2, $67, $7b, $dc, $02, $98, $f2 + .db $d7, $20, $39, $f1, $9f, $33, $dc, $27, $dc, $57 + .db $23, $83, $57, $63, $6c, $51, $87, $63, $99, $61 + .db $a3, $06, $b3, $21, $77, $f3, $f3, $21, $f7, $2a + .db $13, $81, $23, $22, $53, $00, $63, $22, $e9, $0b + .db $0c, $83, $13, $21, $16, $22, $33, $05, $8f, $35 + .db $ec, $01, $63, $a0, $67, $20, $73, $01, $77, $01 + .db $83, $20, $87, $20, $b3, $20, $b7, $20, $c3, $01 + .db $c7, $00, $d3, $20, $d7, $20, $67, $a0, $77, $07 + .db $87, $22, $e8, $62, $f5, $65, $1c, $82, $7f, $38 + .db $8d, $c1, $cf, $26 + .db $fd + +;level 1-1 +L_GroundArea6: + .db $50, $21 + .db $07, $81, $47, $24, $57, $00, $63, $01, $77, $01 + .db $c9, $71, $68, $f2, $e7, $73, $97, $fb, $06, $83 + .db $5c, $01, $d7, $22, $e7, $00, $03, $a7, $6c, $02 + .db $b3, $22, $e3, $01, $e7, $07, $47, $a0, $57, $06 + .db $a7, $01, $d3, $00, $d7, $01, $07, $81, $67, $20 + .db $93, $22, $03, $a3, $1c, $61, $17, $21, $6f, $33 + .db $c7, $63, $d8, $62, $e9, $61, $fa, $60, $4f, $b3 + .db $87, $63, $9c, $01, $b7, $63, $c8, $62, $d9, $61 + .db $ea, $60, $39, $f1, $87, $21, $a7, $01, $b7, $20 + .db $39, $f1, $5f, $38, $6d, $c1, $af, $26 + .db $fd + +;level 1-3/5-3 +L_GroundArea7: + .db $90, $11 + .db $0f, $26, $fe, $10, $2a, $93, $87, $17, $a3, $14 + .db $b2, $42, $0a, $92, $19, $40, $36, $14, $50, $41 + .db $82, $16, $2b, $93, $24, $41, $bb, $14, $b8, $00 + .db $c2, $43, $c3, $13, $1b, $94, $67, $12, $c4, $15 + .db $53, $c1, $d2, $41, $12, $c1, $29, $13, $85, $17 + .db $1b, $92, $1a, $42, $47, $13, $83, $41, $a7, $13 + .db $0e, $91, $a7, $63, $b7, $63, $c5, $65, $d5, $65 + .db $dd, $4a, $e3, $67, $f3, $67, $8d, $c1, $ae, $42 + .db $df, $20 + .db $fd + +;level 2-3/7-3 +L_GroundArea8: + .db $90, $11 + .db $0f, $26, $6e, $10, $8b, $17, $af, $32, $d8, $62 + .db $e8, $62, $fc, $3f, $ad, $c8, $f8, $64, $0c, $be + .db $43, $43, $f8, $64, $0c, $bf, $73, $40, $84, $40 + .db $93, $40, $a4, $40, $b3, $40, $f8, $64, $48, $e4 + .db $5c, $39, $83, $40, $92, $41, $b3, $40, $f8, $64 + .db $48, $e4, $5c, $39, $f8, $64, $13, $c2, $37, $65 + .db $4c, $24, $63, $00, $97, $65, $c3, $42, $0b, $97 + .db $ac, $32, $f8, $64, $0c, $be, $53, $45, $9d, $48 + .db $f8, $64, $2a, $e2, $3c, $47, $56, $43, $ba, $62 + .db $f8, $64, $0c, $b7, $88, $64, $bc, $31, $d4, $45 + .db $fc, $31, $3c, $b1, $78, $64, $8c, $38, $0b, $9c + .db $1a, $33, $18, $61, $28, $61, $39, $60, $5d, $4a + .db $ee, $11, $0f, $b8, $1d, $c1, $3e, $42, $6f, $20 + .db $fd + +;level 2-1 +L_GroundArea9: + .db $52, $31 + .db $0f, $20, $6e, $40, $f7, $20, $07, $84, $17, $20 + .db $4f, $34, $c3, $03, $c7, $02, $d3, $22, $27, $e3 + .db $39, $61, $e7, $73, $5c, $e4, $57, $00, $6c, $73 + .db $47, $a0, $53, $06, $63, $22, $a7, $73, $fc, $73 + .db $13, $a1, $33, $05, $43, $21, $5c, $72, $c3, $23 + .db $cc, $03, $77, $fb, $ac, $02, $39, $f1, $a7, $73 + .db $d3, $04, $e8, $72, $e3, $22, $26, $f4, $bc, $02 + .db $8c, $81, $a8, $62, $17, $87, $43, $24, $a7, $01 + .db $c3, $04, $08, $f2, $97, $21, $a3, $02, $c9, $0b + .db $e1, $69, $f1, $69, $8d, $c1, $cf, $26 + .db $fd + +;pipe intro area +L_GroundArea10: + .db $38, $11 + .db $0f, $26, $ad, $40, $3d, $c7 + .db $fd + +;level 5-1 +L_GroundArea11: + .db $95, $b1 + .db $0f, $26, $0d, $02, $c8, $72, $1c, $81, $38, $72 + .db $0d, $05, $97, $34, $98, $62, $a3, $20, $b3, $06 + .db $c3, $20, $cc, $03, $f9, $91, $2c, $81, $48, $62 + .db $0d, $09, $37, $63, $47, $03, $57, $21, $8c, $02 + .db $c5, $79, $c7, $31, $f9, $11, $39, $f1, $a9, $11 + .db $6f, $b4, $d3, $65, $e3, $65, $7d, $c1, $bf, $26 + .db $fd + +;cloud level used in levels 2-1 and 5-2 +L_GroundArea12: + .db $00, $c1 + .db $4c, $00, $f4, $4f, $0d, $02, $02, $42, $43, $4f + .db $52, $c2, $de, $00, $5a, $c2, $4d, $c7 + .db $fd + +;level 4-3 +L_GroundArea13: + .db $90, $51 + .db $0f, $26, $ee, $10, $0b, $94, $33, $14, $42, $42 + .db $77, $16, $86, $44, $02, $92, $4a, $16, $69, $42 + .db $73, $14, $b0, $00, $c7, $12, $05, $c0, $1c, $17 + .db $1f, $11, $36, $12, $8f, $14, $91, $40, $1b, $94 + .db $35, $12, $34, $42, $60, $42, $61, $12, $87, $12 + .db $96, $40, $a3, $14, $1c, $98, $1f, $11, $47, $12 + .db $9f, $15, $cc, $15, $cf, $11, $05, $c0, $1f, $15 + .db $39, $12, $7c, $16, $7f, $11, $82, $40, $98, $12 + .db $df, $15, $16, $c4, $17, $14, $54, $12, $9b, $16 + .db $28, $94, $ce, $01, $3d, $c1, $5e, $42, $8f, $20 + .db $fd + +;level 6-3 +L_GroundArea14: + .db $97, $11 + .db $0f, $26, $fe, $10, $2b, $92, $57, $12, $8b, $12 + .db $c0, $41, $f7, $13, $5b, $92, $69, $0b, $bb, $12 + .db $b2, $46, $19, $93, $71, $00, $17, $94, $7c, $14 + .db $7f, $11, $93, $41, $bf, $15, $fc, $13, $ff, $11 + .db $2f, $95, $50, $42, $51, $12, $58, $14, $a6, $12 + .db $db, $12, $1b, $93, $46, $43, $7b, $12, $8d, $49 + .db $b7, $14, $1b, $94, $49, $0b, $bb, $12, $fc, $13 + .db $ff, $12, $03, $c1, $2f, $15, $43, $12, $4b, $13 + .db $77, $13, $9d, $4a, $15, $c1, $a1, $41, $c3, $12 + .db $fe, $01, $7d, $c1, $9e, $42, $cf, $20 + .db $fd + +;level 6-1 +L_GroundArea15: + .db $52, $21 + .db $0f, $20, $6e, $44, $0c, $f1, $4c, $01, $aa, $35 + .db $d9, $34, $ee, $20, $08, $b3, $37, $32, $43, $04 + .db $4e, $21, $53, $20, $7c, $01, $97, $21, $b7, $07 + .db $9c, $81, $e7, $42, $5f, $b3, $97, $63, $ac, $02 + .db $c5, $41, $49, $e0, $58, $61, $76, $64, $85, $65 + .db $94, $66, $a4, $22, $a6, $03, $c8, $22, $dc, $02 + .db $68, $f2, $96, $42, $13, $82, $17, $02, $af, $34 + .db $f6, $21, $fc, $06, $26, $80, $2a, $24, $36, $01 + .db $8c, $00, $ff, $35, $4e, $a0, $55, $21, $77, $20 + .db $87, $07, $89, $22, $ae, $21, $4c, $82, $9f, $34 + .db $ec, $01, $03, $e7, $13, $67, $8d, $4a, $ad, $41 + .db $0f, $a6 + .db $fd + +;warp zone area used in level 4-2 +L_GroundArea16: + .db $10, $51 + .db $4c, $00, $c7, $12, $c6, $42, $03, $92, $02, $42 + .db $29, $12, $63, $12, $62, $42, $69, $14, $a5, $12 + .db $a4, $42, $e2, $14, $e1, $44, $f8, $16, $37, $c1 + .db $8f, $38, $02, $bb, $28, $7a, $68, $7a, $a8, $7a + .db $e0, $6a, $f0, $6a, $6d, $c5 + .db $fd + +;level 8-1 +L_GroundArea17: + .db $92, $31 + .db $0f, $20, $6e, $40, $0d, $02, $37, $73, $ec, $00 + .db $0c, $80, $3c, $00, $6c, $00, $9c, $00, $06, $c0 + .db $c7, $73, $06, $83, $28, $72, $96, $40, $e7, $73 + .db $26, $c0, $87, $7b, $d2, $41, $39, $f1, $c8, $f2 + .db $97, $e3, $a3, $23, $e7, $02, $e3, $07, $f3, $22 + .db $37, $e3, $9c, $00, $bc, $00, $ec, $00, $0c, $80 + .db $3c, $00, $86, $21, $a6, $06, $b6, $24, $5c, $80 + .db $7c, $00, $9c, $00, $29, $e1, $dc, $05, $f6, $41 + .db $dc, $80, $e8, $72, $0c, $81, $27, $73, $4c, $01 + .db $66, $74, $0d, $11, $3f, $35, $b6, $41, $2c, $82 + .db $36, $40, $7c, $02, $86, $40, $f9, $61, $39, $e1 + .db $ac, $04, $c6, $41, $0c, $83, $16, $41, $88, $f2 + .db $39, $f1, $7c, $00, $89, $61, $9c, $00, $a7, $63 + .db $bc, $00, $c5, $65, $dc, $00, $e3, $67, $f3, $67 + .db $8d, $c1, $cf, $26 + .db $fd + +;level 5-2 +L_GroundArea18: + .db $55, $b1 + .db $0f, $26, $cf, $33, $07, $b2, $15, $11, $52, $42 + .db $99, $0b, $ac, $02, $d3, $24, $d6, $42, $d7, $25 + .db $23, $84, $cf, $33, $07, $e3, $19, $61, $78, $7a + .db $ef, $33, $2c, $81, $46, $64, $55, $65, $65, $65 + .db $ec, $74, $47, $82, $53, $05, $63, $21, $62, $41 + .db $96, $22, $9a, $41, $cc, $03, $b9, $91, $39, $f1 + .db $63, $26, $67, $27, $d3, $06, $fc, $01, $18, $e2 + .db $d9, $07, $e9, $04, $0c, $86, $37, $22, $93, $24 + .db $87, $84, $ac, $02, $c2, $41, $c3, $23, $d9, $71 + .db $fc, $01, $7f, $b1, $9c, $00, $a7, $63, $b6, $64 + .db $cc, $00, $d4, $66, $e3, $67, $f3, $67, $8d, $c1 + .db $cf, $26 + .db $fd + +;level 8-2 +L_GroundArea19: + .db $50, $b1 + .db $0f, $26, $fc, $00, $1f, $b3, $5c, $00, $65, $65 + .db $74, $66, $83, $67, $93, $67, $dc, $73, $4c, $80 + .db $b3, $20, $c9, $0b, $c3, $08, $d3, $2f, $dc, $00 + .db $2c, $80, $4c, $00, $8c, $00, $d3, $2e, $ed, $4a + .db $fc, $00, $d7, $a1, $ec, $01, $4c, $80, $59, $11 + .db $d8, $11, $da, $10, $37, $a0, $47, $04, $99, $11 + .db $e7, $21, $3a, $90, $67, $20, $76, $10, $77, $60 + .db $87, $07, $d8, $12, $39, $f1, $ac, $00, $e9, $71 + .db $0c, $80, $2c, $00, $4c, $05, $c7, $7b, $39, $f1 + .db $ec, $00, $f9, $11, $0c, $82, $6f, $34, $f8, $11 + .db $fa, $10, $7f, $b2, $ac, $00, $b6, $64, $cc, $01 + .db $e3, $67, $f3, $67, $8d, $c1, $cf, $26 + .db $fd + +;level 7-1 +L_GroundArea20: + .db $52, $b1 + .db $0f, $20, $6e, $45, $39, $91, $b3, $04, $c3, $21 + .db $c8, $11, $ca, $10, $49, $91, $7c, $73, $e8, $12 + .db $88, $91, $8a, $10, $e7, $21, $05, $91, $07, $30 + .db $17, $07, $27, $20, $49, $11, $9c, $01, $c8, $72 + .db $23, $a6, $27, $26, $d3, $03, $d8, $7a, $89, $91 + .db $d8, $72, $39, $f1, $a9, $11, $09, $f1, $63, $24 + .db $67, $24, $d8, $62, $28, $91, $2a, $10, $56, $21 + .db $70, $04, $79, $0b, $8c, $00, $94, $21, $9f, $35 + .db $2f, $b8, $3d, $c1, $7f, $26 + .db $fd + +;cloud level used in levels 3-1 and 6-2 +L_GroundArea21: + .db $06, $c1 + .db $4c, $00, $f4, $4f, $0d, $02, $06, $20, $24, $4f + .db $35, $a0, $36, $20, $53, $46, $d5, $20, $d6, $20 + .db $34, $a1, $73, $49, $74, $20, $94, $20, $b4, $20 + .db $d4, $20, $f4, $20, $2e, $80, $59, $42, $4d, $c7 + .db $fd + +;level 3-2 +L_GroundArea22: + .db $96, $31 + .db $0f, $26, $0d, $03, $1a, $60, $77, $42, $c4, $00 + .db $c8, $62, $b9, $e1, $d3, $06, $d7, $07, $f9, $61 + .db $0c, $81, $4e, $b1, $8e, $b1, $bc, $01, $e4, $50 + .db $e9, $61, $0c, $81, $0d, $0a, $84, $43, $98, $72 + .db $0d, $0c, $0f, $38, $1d, $c1, $5f, $26 + .db $fd + +;level 1-2 +L_UndergroundArea1: + .db $48, $0f + .db $0e, $01, $5e, $02, $a7, $00, $bc, $73, $1a, $e0 + .db $39, $61, $58, $62, $77, $63, $97, $63, $b8, $62 + .db $d6, $07, $f8, $62, $19, $e1, $75, $52, $86, $40 + .db $87, $50, $95, $52, $93, $43, $a5, $21, $c5, $52 + .db $d6, $40, $d7, $20, $e5, $06, $e6, $51, $3e, $8d + .db $5e, $03, $67, $52, $77, $52, $7e, $02, $9e, $03 + .db $a6, $43, $a7, $23, $de, $05, $fe, $02, $1e, $83 + .db $33, $54, $46, $40, $47, $21, $56, $04, $5e, $02 + .db $83, $54, $93, $52, $96, $07, $97, $50, $be, $03 + .db $c7, $23, $fe, $02, $0c, $82, $43, $45, $45, $24 + .db $46, $24, $90, $08, $95, $51, $78, $fa, $d7, $73 + .db $39, $f1, $8c, $01, $a8, $52, $b8, $52, $cc, $01 + .db $5f, $b3, $97, $63, $9e, $00, $0e, $81, $16, $24 + .db $66, $04, $8e, $00, $fe, $01, $08, $d2, $0e, $06 + .db $6f, $47, $9e, $0f, $0e, $82, $2d, $47, $28, $7a + .db $68, $7a, $a8, $7a, $ae, $01, $de, $0f, $6d, $c5 + .db $fd + +;level 4-2 +L_UndergroundArea2: + .db $48, $0f + .db $0e, $01, $5e, $02, $bc, $01, $fc, $01, $2c, $82 + .db $41, $52, $4e, $04, $67, $25, $68, $24, $69, $24 + .db $ba, $42, $c7, $04, $de, $0b, $b2, $87, $fe, $02 + .db $2c, $e1, $2c, $71, $67, $01, $77, $00, $87, $01 + .db $8e, $00, $ee, $01, $f6, $02, $03, $85, $05, $02 + .db $13, $21, $16, $02, $27, $02, $2e, $02, $88, $72 + .db $c7, $20, $d7, $07, $e4, $76, $07, $a0, $17, $06 + .db $48, $7a, $76, $20, $98, $72, $79, $e1, $88, $62 + .db $9c, $01, $b7, $73, $dc, $01, $f8, $62, $fe, $01 + .db $08, $e2, $0e, $00, $6e, $02, $73, $20, $77, $23 + .db $83, $04, $93, $20, $ae, $00, $fe, $0a, $0e, $82 + .db $39, $71, $a8, $72, $e7, $73, $0c, $81, $8f, $32 + .db $ae, $00, $fe, $04, $04, $d1, $17, $04, $26, $49 + .db $27, $29, $df, $33, $fe, $02, $44, $f6, $7c, $01 + .db $8e, $06, $bf, $47, $ee, $0f, $4d, $c7, $0e, $82 + .db $68, $7a, $ae, $01, $de, $0f, $6d, $c5 + .db $fd + +;underground bonus rooms area used in many levels +L_UndergroundArea3: + .db $48, $01 + .db $0e, $01, $00, $5a, $3e, $06, $45, $46, $47, $46 + .db $53, $44, $ae, $01, $df, $4a, $4d, $c7, $0e, $81 + .db $00, $5a, $2e, $04, $37, $28, $3a, $48, $46, $47 + .db $c7, $07, $ce, $0f, $df, $4a, $4d, $c7, $0e, $81 + .db $00, $5a, $33, $53, $43, $51, $46, $40, $47, $50 + .db $53, $04, $55, $40, $56, $50, $62, $43, $64, $40 + .db $65, $50, $71, $41, $73, $51, $83, $51, $94, $40 + .db $95, $50, $a3, $50, $a5, $40, $a6, $50, $b3, $51 + .db $b6, $40, $b7, $50, $c3, $53, $df, $4a, $4d, $c7 + .db $0e, $81, $00, $5a, $2e, $02, $36, $47, $37, $52 + .db $3a, $49, $47, $25, $a7, $52, $d7, $04, $df, $4a + .db $4d, $c7, $0e, $81, $00, $5a, $3e, $02, $44, $51 + .db $53, $44, $54, $44, $55, $24, $a1, $54, $ae, $01 + .db $b4, $21, $df, $4a, $e5, $07, $4d, $c7 + .db $fd + +;water area used in levels 5-2 and 6-2 +L_WaterArea1: + .db $41, $01 + .db $b4, $34, $c8, $52, $f2, $51, $47, $d3, $6c, $03 + .db $65, $49, $9e, $07, $be, $01, $cc, $03, $fe, $07 + .db $0d, $c9, $1e, $01, $6c, $01, $62, $35, $63, $53 + .db $8a, $41, $ac, $01, $b3, $53, $e9, $51, $26, $c3 + .db $27, $33, $63, $43, $64, $33, $ba, $60, $c9, $61 + .db $ce, $0b, $e5, $09, $ee, $0f, $7d, $ca, $7d, $47 + .db $fd + +;level 2-2/7-2 +L_WaterArea2: + .db $41, $01 + .db $b8, $52, $ea, $41, $27, $b2, $b3, $42, $16, $d4 + .db $4a, $42, $a5, $51, $a7, $31, $27, $d3, $08, $e2 + .db $16, $64, $2c, $04, $38, $42, $76, $64, $88, $62 + .db $de, $07, $fe, $01, $0d, $c9, $23, $32, $31, $51 + .db $98, $52, $0d, $c9, $59, $42, $63, $53, $67, $31 + .db $14, $c2, $36, $31, $87, $53, $17, $e3, $29, $61 + .db $30, $62, $3c, $08, $42, $37, $59, $40, $6a, $42 + .db $99, $40, $c9, $61, $d7, $63, $39, $d1, $58, $52 + .db $c3, $67, $d3, $31, $dc, $06, $f7, $42, $fa, $42 + .db $23, $b1, $43, $67, $c3, $34, $c7, $34, $d1, $51 + .db $43, $b3, $47, $33, $9a, $30, $a9, $61, $b8, $62 + .db $be, $0b, $d5, $09, $de, $0f, $0d, $ca, $7d, $47 + .db $fd + +;water area used in level 8-4 +L_WaterArea3: + .db $49, $0f + .db $1e, $01, $39, $73, $5e, $07, $ae, $0b, $1e, $82 + .db $6e, $88, $9e, $02, $0d, $04, $2e, $0b, $45, $09 + .db $4e, $0f, $ed, $47 + .db $fd + +;------------------------------------------------------------------------------------- + +;unused space + .db $ff + +;------------------------------------------------------------------------------------- + +;indirect jump routine called when +;$0770 is set to 1 +GameMode: + lda OperMode_Task + jsr JumpEngine + + .dw InitializeArea + .dw ScreenRoutines + .dw SecondaryGameSetup + .dw GameCoreRoutine + +;------------------------------------------------------------------------------------- + +GameCoreRoutine: + ldx CurrentPlayer ;get which player is on the screen + lda SavedJoypadBits,x ;use appropriate player's controller bits + sta SavedJoypadBits ;as the master controller bits + jsr GameRoutines ;execute one of many possible subs + lda OperMode_Task ;check major task of operating mode + cmp #$03 ;if we are supposed to be here, + bcs GameEngine ;branch to the game engine itself + rts + +GameEngine: + jsr ProcFireball_Bubble ;process fireballs and air bubbles + ldx #$00 +ProcELoop: stx ObjectOffset ;put incremented offset in X as enemy object offset + jsr EnemiesAndLoopsCore ;process enemy objects + jsr FloateyNumbersRoutine ;process floatey numbers + inx + cpx #$06 ;do these two subroutines until the whole buffer is done + bne ProcELoop + jsr GetPlayerOffscreenBits ;get offscreen bits for player object + jsr RelativePlayerPosition ;get relative coordinates for player object + jsr PlayerGfxHandler ;draw the player + jsr BlockObjMT_Updater ;replace block objects with metatiles if necessary + ldx #$01 + stx ObjectOffset ;set offset for second + jsr BlockObjectsCore ;process second block object + dex + stx ObjectOffset ;set offset for first + jsr BlockObjectsCore ;process first block object + jsr MiscObjectsCore ;process misc objects (hammer, jumping coins) + jsr ProcessCannons ;process bullet bill cannons + jsr ProcessWhirlpools ;process whirlpools + jsr FlagpoleRoutine ;process the flagpole + jsr RunGameTimer ;count down the game timer + jsr ColorRotation ;cycle one of the background colors + lda Player_Y_HighPos + cmp #$02 ;if player is below the screen, don't bother with the music + bpl NoChgMus + lda StarInvincibleTimer ;if star mario invincibility timer at zero, + beq ClrPlrPal ;skip this part + cmp #$04 + bne NoChgMus ;if not yet at a certain point, continue + lda IntervalTimerControl ;if interval timer not yet expired, + bne NoChgMus ;branch ahead, don't bother with the music + jsr GetAreaMusic ;to re-attain appropriate level music +NoChgMus: ldy StarInvincibleTimer ;get invincibility timer + lda FrameCounter ;get frame counter + cpy #$08 ;if timer still above certain point, + bcs CycleTwo ;branch to cycle player's palette quickly + lsr ;otherwise, divide by 8 to cycle every eighth frame + lsr +CycleTwo: lsr ;if branched here, divide by 2 to cycle every other frame + jsr CyclePlayerPalette ;do sub to cycle the palette (note: shares fire flower code) + jmp SaveAB ;then skip this sub to finish up the game engine +ClrPlrPal: jsr ResetPalStar ;do sub to clear player's palette bits in attributes +SaveAB: lda A_B_Buttons ;save current A and B button + sta PreviousA_B_Buttons ;into temp variable to be used on next frame + lda #$00 + sta Left_Right_Buttons ;nullify left and right buttons temp variable +UpdScrollVar: lda VRAM_Buffer_AddrCtrl + cmp #$06 ;if vram address controller set to 6 (one of two $0341s) + beq ExitEng ;then branch to leave + lda AreaParserTaskNum ;otherwise check number of tasks + bne RunParser + lda ScrollThirtyTwo ;get horizontal scroll in 0-31 or $00-$20 range + cmp #$20 ;check to see if exceeded $21 + bmi ExitEng ;branch to leave if not + lda ScrollThirtyTwo + sbc #$20 ;otherwise subtract $20 to set appropriately + sta ScrollThirtyTwo ;and store + lda #$00 ;reset vram buffer offset used in conjunction with + sta VRAM_Buffer2_Offset ;level graphics buffer at $0341-$035f +RunParser: jsr AreaParserTaskHandler ;update the name table with more level graphics +ExitEng: rts ;and after all that, we're finally done! + +;------------------------------------------------------------------------------------- + +ScrollHandler: + lda Player_X_Scroll ;load value saved here + clc + adc Platform_X_Scroll ;add value used by left/right platforms + sta Player_X_Scroll ;save as new value here to impose force on scroll + lda ScrollLock ;check scroll lock flag + bne InitScrlAmt ;skip a bunch of code here if set + lda Player_Pos_ForScroll + cmp #$50 ;check player's horizontal screen position + bcc InitScrlAmt ;if less than 80 pixels to the right, branch + lda SideCollisionTimer ;if timer related to player's side collision + bne InitScrlAmt ;not expired, branch + ldy Player_X_Scroll ;get value and decrement by one + dey ;if value originally set to zero or otherwise + bmi InitScrlAmt ;negative for left movement, branch + iny + cpy #$02 ;if value $01, branch and do not decrement + bcc ChkNearMid + dey ;otherwise decrement by one +ChkNearMid: lda Player_Pos_ForScroll + cmp #$70 ;check player's horizontal screen position + bcc ScrollScreen ;if less than 112 pixels to the right, branch + ldy Player_X_Scroll ;otherwise get original value undecremented + +ScrollScreen: + tya + sta ScrollAmount ;save value here + clc + adc ScrollThirtyTwo ;add to value already set here + sta ScrollThirtyTwo ;save as new value here + tya + clc + adc ScreenLeft_X_Pos ;add to left side coordinate + sta ScreenLeft_X_Pos ;save as new left side coordinate + sta HorizontalScroll ;save here also + lda ScreenLeft_PageLoc + adc #$00 ;add carry to page location for left + sta ScreenLeft_PageLoc ;side of the screen + and #$01 ;get LSB of page location + sta $00 ;save as temp variable for PPU register 1 mirror + lda Mirror_PPU_CTRL_REG1 ;get PPU register 1 mirror + and #%11111110 ;save all bits except d0 + ora $00 ;get saved bit here and save in PPU register 1 + sta Mirror_PPU_CTRL_REG1 ;mirror to be used to set name table later + jsr GetScreenPosition ;figure out where the right side is + lda #$08 + sta ScrollIntervalTimer ;set scroll timer (residual, not used elsewhere) + jmp ChkPOffscr ;skip this part +InitScrlAmt: lda #$00 + sta ScrollAmount ;initialize value here +ChkPOffscr: ldx #$00 ;set X for player offset + jsr GetXOffscreenBits ;get horizontal offscreen bits for player + sta $00 ;save them here + ldy #$00 ;load default offset (left side) + asl ;if d7 of offscreen bits are set, + bcs KeepOnscr ;branch with default offset + iny ;otherwise use different offset (right side) + lda $00 + and #%00100000 ;check offscreen bits for d5 set + beq InitPlatScrl ;if not set, branch ahead of this part +KeepOnscr: lda ScreenEdge_X_Pos,y ;get left or right side coordinate based on offset + sec + sbc X_SubtracterData,y ;subtract amount based on offset + sta Player_X_Position ;store as player position to prevent movement further + lda ScreenEdge_PageLoc,y ;get left or right page location based on offset + sbc #$00 ;subtract borrow + sta Player_PageLoc ;save as player's page location + lda Left_Right_Buttons ;check saved controller bits + cmp OffscrJoypadBitsData,y ;against bits based on offset + beq InitPlatScrl ;if not equal, branch + lda #$00 + sta Player_X_Speed ;otherwise nullify horizontal speed of player +InitPlatScrl: lda #$00 ;nullify platform force imposed on scroll + sta Platform_X_Scroll + rts + +X_SubtracterData: + .db $00, $10 + +OffscrJoypadBitsData: + .db $01, $02 + +;------------------------------------------------------------------------------------- + +GetScreenPosition: + lda ScreenLeft_X_Pos ;get coordinate of screen's left boundary + clc + adc #$ff ;add 255 pixels + sta ScreenRight_X_Pos ;store as coordinate of screen's right boundary + lda ScreenLeft_PageLoc ;get page number where left boundary is + adc #$00 ;add carry from before + sta ScreenRight_PageLoc ;store as page number where right boundary is + rts + +;------------------------------------------------------------------------------------- + +GameRoutines: + lda GameEngineSubroutine ;run routine based on number (a few of these routines are + jsr JumpEngine ;merely placeholders as conditions for other routines) + + .dw Entrance_GameTimerSetup + .dw Vine_AutoClimb + .dw SideExitPipeEntry + .dw VerticalPipeEntry + .dw FlagpoleSlide + .dw PlayerEndLevel + .dw PlayerLoseLife + .dw PlayerEntrance + .dw PlayerCtrlRoutine + .dw PlayerChangeSize + .dw PlayerInjuryBlink + .dw PlayerDeath + .dw PlayerFireFlower + +;------------------------------------------------------------------------------------- + +PlayerEntrance: + lda AltEntranceControl ;check for mode of alternate entry + cmp #$02 + beq EntrMode2 ;if found, branch to enter from pipe or with vine + lda #$00 + ldy Player_Y_Position ;if vertical position above a certain + cpy #$30 ;point, nullify controller bits and continue + bcc AutoControlPlayer ;with player movement code, do not return + lda PlayerEntranceCtrl ;check player entry bits from header + cmp #$06 + beq ChkBehPipe ;if set to 6 or 7, execute pipe intro code + cmp #$07 ;otherwise branch to normal entry + bne PlayerRdy +ChkBehPipe: lda Player_SprAttrib ;check for sprite attributes + bne IntroEntr ;branch if found + lda #$01 + jmp AutoControlPlayer ;force player to walk to the right +IntroEntr: jsr EnterSidePipe ;execute sub to move player to the right + dec ChangeAreaTimer ;decrement timer for change of area + bne ExitEntr ;branch to exit if not yet expired + inc DisableIntermediate ;set flag to skip world and lives display + jmp NextArea ;jump to increment to next area and set modes +EntrMode2: lda JoypadOverride ;if controller override bits set here, + bne VineEntr ;branch to enter with vine + lda #$ff ;otherwise, set value here then execute sub + jsr MovePlayerYAxis ;to move player upwards (note $ff = -1) + lda Player_Y_Position ;check to see if player is at a specific coordinate + cmp #$91 ;if player risen to a certain point (this requires pipes + bcc PlayerRdy ;to be at specific height to look/function right) branch + rts ;to the last part, otherwise leave +VineEntr: lda VineHeight + cmp #$60 ;check vine height + bne ExitEntr ;if vine not yet reached maximum height, branch to leave + lda Player_Y_Position ;get player's vertical coordinate + cmp #$99 ;check player's vertical coordinate against preset value + ldy #$00 ;load default values to be written to + lda #$01 ;this value moves player to the right off the vine + bcc OffVine ;if vertical coordinate < preset value, use defaults + lda #$03 + sta Player_State ;otherwise set player state to climbing + iny ;increment value in Y + lda #$08 ;set block in block buffer to cover hole, then + sta Block_Buffer_1+$b4 ;use same value to force player to climb +OffVine: sty DisableCollisionDet ;set collision detection disable flag + jsr AutoControlPlayer ;use contents of A to move player up or right, execute sub + lda Player_X_Position + cmp #$48 ;check player's horizontal position + bcc ExitEntr ;if not far enough to the right, branch to leave +PlayerRdy: lda #$08 ;set routine to be executed by game engine next frame + sta GameEngineSubroutine + lda #$01 ;set to face player to the right + sta PlayerFacingDir + lsr ;init A + sta AltEntranceControl ;init mode of entry + sta DisableCollisionDet ;init collision detection disable flag + sta JoypadOverride ;nullify controller override bits +ExitEntr: rts ;leave! + +;------------------------------------------------------------------------------------- +;$07 - used to hold upper limit of high byte when player falls down hole + +AutoControlPlayer: + sta SavedJoypadBits ;override controller bits with contents of A if executing here + +PlayerCtrlRoutine: + lda GameEngineSubroutine ;check task here + cmp #$0b ;if certain value is set, branch to skip controller bit loading + beq SizeChk + lda AreaType ;are we in a water type area? + bne SaveJoyp ;if not, branch + ldy Player_Y_HighPos + dey ;if not in vertical area between + bne DisJoyp ;status bar and bottom, branch + lda Player_Y_Position + cmp #$d0 ;if nearing the bottom of the screen or + bcc SaveJoyp ;not in the vertical area between status bar or bottom, +DisJoyp: lda #$00 ;disable controller bits + sta SavedJoypadBits +SaveJoyp: lda SavedJoypadBits ;otherwise store A and B buttons in $0a + and #%11000000 + sta A_B_Buttons + lda SavedJoypadBits ;store left and right buttons in $0c + and #%00000011 + sta Left_Right_Buttons + lda SavedJoypadBits ;store up and down buttons in $0b + and #%00001100 + sta Up_Down_Buttons + and #%00000100 ;check for pressing down + beq SizeChk ;if not, branch + lda Player_State ;check player's state + bne SizeChk ;if not on the ground, branch + ldy Left_Right_Buttons ;check left and right + beq SizeChk ;if neither pressed, branch + lda #$00 + sta Left_Right_Buttons ;if pressing down while on the ground, + sta Up_Down_Buttons ;nullify directional bits +SizeChk: jsr PlayerMovementSubs ;run movement subroutines + ldy #$01 ;is player small? + lda PlayerSize + bne ChkMoveDir + ldy #$00 ;check for if crouching + lda CrouchingFlag + beq ChkMoveDir ;if not, branch ahead + ldy #$02 ;if big and crouching, load y with 2 +ChkMoveDir: sty Player_BoundBoxCtrl ;set contents of Y as player's bounding box size control + lda #$01 ;set moving direction to right by default + ldy Player_X_Speed ;check player's horizontal speed + beq PlayerSubs ;if not moving at all horizontally, skip this part + bpl SetMoveDir ;if moving to the right, use default moving direction + asl ;otherwise change to move to the left +SetMoveDir: sta Player_MovingDir ;set moving direction +PlayerSubs: jsr ScrollHandler ;move the screen if necessary + jsr GetPlayerOffscreenBits ;get player's offscreen bits + jsr RelativePlayerPosition ;get coordinates relative to the screen + ldx #$00 ;set offset for player object + jsr BoundingBoxCore ;get player's bounding box coordinates + jsr PlayerBGCollision ;do collision detection and process + lda Player_Y_Position + cmp #$40 ;check to see if player is higher than 64th pixel + bcc PlayerHole ;if so, branch ahead + lda GameEngineSubroutine + cmp #$05 ;if running end-of-level routine, branch ahead + beq PlayerHole + cmp #$07 ;if running player entrance routine, branch ahead + beq PlayerHole + cmp #$04 ;if running routines $00-$03, branch ahead + bcc PlayerHole + lda Player_SprAttrib + and #%11011111 ;otherwise nullify player's + sta Player_SprAttrib ;background priority flag +PlayerHole: lda Player_Y_HighPos ;check player's vertical high byte + cmp #$02 ;for below the screen + bmi ExitCtrl ;branch to leave if not that far down + ldx #$01 + stx ScrollLock ;set scroll lock + ldy #$04 + sty $07 ;set value here + ldx #$00 ;use X as flag, and clear for cloud level + ldy GameTimerExpiredFlag ;check game timer expiration flag + bne HoleDie ;if set, branch + ldy CloudTypeOverride ;check for cloud type override + bne ChkHoleX ;skip to last part if found +HoleDie: inx ;set flag in X for player death + ldy GameEngineSubroutine + cpy #$0b ;check for some other routine running + beq ChkHoleX ;if so, branch ahead + ldy DeathMusicLoaded ;check value here + bne HoleBottom ;if already set, branch to next part + iny + sty EventMusicQueue ;otherwise play death music + sty DeathMusicLoaded ;and set value here +HoleBottom: ldy #$06 + sty $07 ;change value here +ChkHoleX: cmp $07 ;compare vertical high byte with value set here + bmi ExitCtrl ;if less, branch to leave + dex ;otherwise decrement flag in X + bmi CloudExit ;if flag was clear, branch to set modes and other values + ldy EventMusicBuffer ;check to see if music is still playing + bne ExitCtrl ;branch to leave if so + lda #$06 ;otherwise set to run lose life routine + sta GameEngineSubroutine ;on next frame +ExitCtrl: rts ;leave + +CloudExit: + lda #$00 + sta JoypadOverride ;clear controller override bits if any are set + jsr SetEntr ;do sub to set secondary mode + inc AltEntranceControl ;set mode of entry to 3 + rts + +;------------------------------------------------------------------------------------- + +Vine_AutoClimb: + lda Player_Y_HighPos ;check to see whether player reached position + bne AutoClimb ;above the status bar yet and if so, set modes + lda Player_Y_Position + cmp #$e4 + bcc SetEntr +AutoClimb: lda #%00001000 ;set controller bits override to up + sta JoypadOverride + ldy #$03 ;set player state to climbing + sty Player_State + jmp AutoControlPlayer +SetEntr: lda #$02 ;set starting position to override + sta AltEntranceControl + jmp ChgAreaMode ;set modes + +;------------------------------------------------------------------------------------- + +VerticalPipeEntry: + lda #$01 ;set 1 as movement amount + jsr MovePlayerYAxis ;do sub to move player downwards + jsr ScrollHandler ;do sub to scroll screen with saved force if necessary + ldy #$00 ;load default mode of entry + lda WarpZoneControl ;check warp zone control variable/flag + bne ChgAreaPipe ;if set, branch to use mode 0 + iny + lda AreaType ;check for castle level type + cmp #$03 + bne ChgAreaPipe ;if not castle type level, use mode 1 + iny + jmp ChgAreaPipe ;otherwise use mode 2 + +MovePlayerYAxis: + clc + adc Player_Y_Position ;add contents of A to player position + sta Player_Y_Position + rts + +;------------------------------------------------------------------------------------- + +SideExitPipeEntry: + jsr EnterSidePipe ;execute sub to move player to the right + ldy #$02 +ChgAreaPipe: dec ChangeAreaTimer ;decrement timer for change of area + bne ExitCAPipe + sty AltEntranceControl ;when timer expires set mode of alternate entry +ChgAreaMode: inc DisableScreenFlag ;set flag to disable screen output + lda #$00 + sta OperMode_Task ;set secondary mode of operation + sta Sprite0HitDetectFlag ;disable sprite 0 check +ExitCAPipe: rts ;leave + +EnterSidePipe: + lda #$08 ;set player's horizontal speed + sta Player_X_Speed + ldy #$01 ;set controller right button by default + lda Player_X_Position ;mask out higher nybble of player's + and #%00001111 ;horizontal position + bne RightPipe + sta Player_X_Speed ;if lower nybble = 0, set as horizontal speed + tay ;and nullify controller bit override here +RightPipe: tya ;use contents of Y to + jsr AutoControlPlayer ;execute player control routine with ctrl bits nulled + rts + +;------------------------------------------------------------------------------------- + +PlayerChangeSize: + lda TimerControl ;check master timer control + cmp #$f8 ;for specific moment in time + bne EndChgSize ;branch if before or after that point + jmp InitChangeSize ;otherwise run code to get growing/shrinking going +EndChgSize: cmp #$c4 ;check again for another specific moment + bne ExitChgSize ;and branch to leave if before or after that point + jsr DonePlayerTask ;otherwise do sub to init timer control and set routine +ExitChgSize: rts ;and then leave + +;------------------------------------------------------------------------------------- + +PlayerInjuryBlink: + lda TimerControl ;check master timer control + cmp #$f0 ;for specific moment in time + bcs ExitBlink ;branch if before that point + cmp #$c8 ;check again for another specific point + beq DonePlayerTask ;branch if at that point, and not before or after + jmp PlayerCtrlRoutine ;otherwise run player control routine +ExitBlink: bne ExitBoth ;do unconditional branch to leave + +InitChangeSize: + ldy PlayerChangeSizeFlag ;if growing/shrinking flag already set + bne ExitBoth ;then branch to leave + sty PlayerAnimCtrl ;otherwise initialize player's animation frame control + inc PlayerChangeSizeFlag ;set growing/shrinking flag + lda PlayerSize + eor #$01 ;invert player's size + sta PlayerSize +ExitBoth: rts ;leave + +;------------------------------------------------------------------------------------- +;$00 - used in CyclePlayerPalette to store current palette to cycle + +PlayerDeath: + lda TimerControl ;check master timer control + cmp #$f0 ;for specific moment in time + bcs ExitDeath ;branch to leave if before that point + jmp PlayerCtrlRoutine ;otherwise run player control routine + +DonePlayerTask: + lda #$00 + sta TimerControl ;initialize master timer control to continue timers + lda #$08 + sta GameEngineSubroutine ;set player control routine to run next frame + rts ;leave + +PlayerFireFlower: + lda TimerControl ;check master timer control + cmp #$c0 ;for specific moment in time + beq ResetPalFireFlower ;branch if at moment, not before or after + lda FrameCounter ;get frame counter + lsr + lsr ;divide by four to change every four frames + +CyclePlayerPalette: + and #$03 ;mask out all but d1-d0 (previously d3-d2) + sta $00 ;store result here to use as palette bits + lda Player_SprAttrib ;get player attributes + and #%11111100 ;save any other bits but palette bits + ora $00 ;add palette bits + sta Player_SprAttrib ;store as new player attributes + rts ;and leave + +ResetPalFireFlower: + jsr DonePlayerTask ;do sub to init timer control and run player control routine + +ResetPalStar: + lda Player_SprAttrib ;get player attributes + and #%11111100 ;mask out palette bits to force palette 0 + sta Player_SprAttrib ;store as new player attributes + rts ;and leave + +ExitDeath: + rts ;leave from death routine + +;------------------------------------------------------------------------------------- + +FlagpoleSlide: + lda Enemy_ID+5 ;check special use enemy slot + cmp #FlagpoleFlagObject ;for flagpole flag object + bne NoFPObj ;if not found, branch to something residual + lda FlagpoleSoundQueue ;load flagpole sound + sta Square1SoundQueue ;into square 1's sfx queue + lda #$00 + sta FlagpoleSoundQueue ;init flagpole sound queue + ldy Player_Y_Position + cpy #$9e ;check to see if player has slid down + bcs SlidePlayer ;far enough, and if so, branch with no controller bits set + lda #$04 ;otherwise force player to climb down (to slide) +SlidePlayer: jmp AutoControlPlayer ;jump to player control routine +NoFPObj: inc GameEngineSubroutine ;increment to next routine (this may + rts ;be residual code) + +;------------------------------------------------------------------------------------- + +Hidden1UpCoinAmts: + .db $15, $23, $16, $1b, $17, $18, $23, $63 + +PlayerEndLevel: + lda #$01 ;force player to walk to the right + jsr AutoControlPlayer + lda Player_Y_Position ;check player's vertical position + cmp #$ae + bcc ChkStop ;if player is not yet off the flagpole, skip this part + lda ScrollLock ;if scroll lock not set, branch ahead to next part + beq ChkStop ;because we only need to do this part once + lda #EndOfLevelMusic + sta EventMusicQueue ;load win level music in event music queue + lda #$00 + sta ScrollLock ;turn off scroll lock to skip this part later +ChkStop: lda Player_CollisionBits ;get player collision bits + lsr ;check for d0 set + bcs RdyNextA ;if d0 set, skip to next part + lda StarFlagTaskControl ;if star flag task control already set, + bne InCastle ;go ahead with the rest of the code + inc StarFlagTaskControl ;otherwise set task control now (this gets ball rolling!) +InCastle: lda #%00100000 ;set player's background priority bit to + sta Player_SprAttrib ;give illusion of being inside the castle +RdyNextA: lda StarFlagTaskControl + cmp #$05 ;if star flag task control not yet set + bne ExitNA ;beyond last valid task number, branch to leave + inc LevelNumber ;increment level number used for game logic + lda LevelNumber + cmp #$03 ;check to see if we have yet reached level -4 + bne NextArea ;and skip this last part here if not + ldy WorldNumber ;get world number as offset + lda CoinTallyFor1Ups ;check third area coin tally for bonus 1-ups + cmp Hidden1UpCoinAmts,y ;against minimum value, if player has not collected + bcc NextArea ;at least this number of coins, leave flag clear + inc Hidden1UpFlag ;otherwise set hidden 1-up box control flag +NextArea: inc AreaNumber ;increment area number used for address loader + jsr LoadAreaPointer ;get new level pointer + inc FetchNewGameTimerFlag ;set flag to load new game timer + jsr ChgAreaMode ;do sub to set secondary mode, disable screen and sprite 0 + sta HalfwayPage ;reset halfway page to 0 (beginning) + lda #Silence + sta EventMusicQueue ;silence music and leave +ExitNA: rts + +;------------------------------------------------------------------------------------- + +PlayerMovementSubs: + lda #$00 ;set A to init crouch flag by default + ldy PlayerSize ;is player small? + bne SetCrouch ;if so, branch + lda Player_State ;check state of player + bne ProcMove ;if not on the ground, branch + lda Up_Down_Buttons ;load controller bits for up and down + and #%00000100 ;single out bit for down button +SetCrouch: sta CrouchingFlag ;store value in crouch flag +ProcMove: jsr PlayerPhysicsSub ;run sub related to jumping and swimming + lda PlayerChangeSizeFlag ;if growing/shrinking flag set, + bne NoMoveSub ;branch to leave + lda Player_State + cmp #$03 ;get player state + beq MoveSubs ;if climbing, branch ahead, leave timer unset + ldy #$18 + sty ClimbSideTimer ;otherwise reset timer now +MoveSubs: jsr JumpEngine + + .dw OnGroundStateSub + .dw JumpSwimSub + .dw FallingSub + .dw ClimbingSub + +NoMoveSub: rts + +;------------------------------------------------------------------------------------- +;$00 - used by ClimbingSub to store high vertical adder + +OnGroundStateSub: + jsr GetPlayerAnimSpeed ;do a sub to set animation frame timing + lda Left_Right_Buttons + beq GndMove ;if left/right controller bits not set, skip instruction + sta PlayerFacingDir ;otherwise set new facing direction +GndMove: jsr ImposeFriction ;do a sub to impose friction on player's walk/run + jsr MovePlayerHorizontally ;do another sub to move player horizontally + sta Player_X_Scroll ;set returned value as player's movement speed for scroll + rts + +;-------------------------------- + +FallingSub: + lda VerticalForceDown + sta VerticalForce ;dump vertical movement force for falling into main one + jmp LRAir ;movement force, then skip ahead to process left/right movement + +;-------------------------------- + +JumpSwimSub: + ldy Player_Y_Speed ;if player's vertical speed zero + bpl DumpFall ;or moving downwards, branch to falling + lda A_B_Buttons + and #A_Button ;check to see if A button is being pressed + and PreviousA_B_Buttons ;and was pressed in previous frame + bne ProcSwim ;if so, branch elsewhere + lda JumpOrigin_Y_Position ;get vertical position player jumped from + sec + sbc Player_Y_Position ;subtract current from original vertical coordinate + cmp DiffToHaltJump ;compare to value set here to see if player is in mid-jump + bcc ProcSwim ;or just starting to jump, if just starting, skip ahead +DumpFall: lda VerticalForceDown ;otherwise dump falling into main fractional + sta VerticalForce +ProcSwim: lda SwimmingFlag ;if swimming flag not set, + beq LRAir ;branch ahead to last part + jsr GetPlayerAnimSpeed ;do a sub to get animation frame timing + lda Player_Y_Position + cmp #$14 ;check vertical position against preset value + bcs LRWater ;if not yet reached a certain position, branch ahead + lda #$18 + sta VerticalForce ;otherwise set fractional +LRWater: lda Left_Right_Buttons ;check left/right controller bits (check for swimming) + beq LRAir ;if not pressing any, skip + sta PlayerFacingDir ;otherwise set facing direction accordingly +LRAir: lda Left_Right_Buttons ;check left/right controller bits (check for jumping/falling) + beq JSMove ;if not pressing any, skip + jsr ImposeFriction ;otherwise process horizontal movement +JSMove: jsr MovePlayerHorizontally ;do a sub to move player horizontally + sta Player_X_Scroll ;set player's speed here, to be used for scroll later + lda GameEngineSubroutine + cmp #$0b ;check for specific routine selected + bne ExitMov1 ;branch if not set to run + lda #$28 + sta VerticalForce ;otherwise set fractional +ExitMov1: jmp MovePlayerVertically ;jump to move player vertically, then leave + +;-------------------------------- + +ClimbAdderLow: + .db $0e, $04, $fc, $f2 +ClimbAdderHigh: + .db $00, $00, $ff, $ff + +ClimbingSub: + lda Player_YMF_Dummy + clc ;add movement force to dummy variable + adc Player_Y_MoveForce ;save with carry + sta Player_YMF_Dummy + ldy #$00 ;set default adder here + lda Player_Y_Speed ;get player's vertical speed + bpl MoveOnVine ;if not moving upwards, branch + dey ;otherwise set adder to $ff +MoveOnVine: sty $00 ;store adder here + adc Player_Y_Position ;add carry to player's vertical position + sta Player_Y_Position ;and store to move player up or down + lda Player_Y_HighPos + adc $00 ;add carry to player's page location + sta Player_Y_HighPos ;and store + lda Left_Right_Buttons ;compare left/right controller bits + and Player_CollisionBits ;to collision flag + beq InitCSTimer ;if not set, skip to end + ldy ClimbSideTimer ;otherwise check timer + bne ExitCSub ;if timer not expired, branch to leave + ldy #$18 + sty ClimbSideTimer ;otherwise set timer now + ldx #$00 ;set default offset here + ldy PlayerFacingDir ;get facing direction + lsr ;move right button controller bit to carry + bcs ClimbFD ;if controller right pressed, branch ahead + inx + inx ;otherwise increment offset by 2 bytes +ClimbFD: dey ;check to see if facing right + beq CSetFDir ;if so, branch, do not increment + inx ;otherwise increment by 1 byte +CSetFDir: lda Player_X_Position + clc ;add or subtract from player's horizontal position + adc ClimbAdderLow,x ;using value here as adder and X as offset + sta Player_X_Position + lda Player_PageLoc ;add or subtract carry or borrow using value here + adc ClimbAdderHigh,x ;from the player's page location + sta Player_PageLoc + lda Left_Right_Buttons ;get left/right controller bits again + eor #%00000011 ;invert them and store them while player + sta PlayerFacingDir ;is on vine to face player in opposite direction +ExitCSub: rts ;then leave +InitCSTimer: sta ClimbSideTimer ;initialize timer here + rts + +;------------------------------------------------------------------------------------- +;$00 - used to store offset to friction data + +JumpMForceData: + .db $20, $20, $1e, $28, $28, $0d, $04 + +FallMForceData: + .db $70, $70, $60, $90, $90, $0a, $09 + +PlayerYSpdData: + .db $fc, $fc, $fc, $fb, $fb, $fe, $ff + +InitMForceData: + .db $00, $00, $00, $00, $00, $80, $00 + +MaxLeftXSpdData: + .db $d8, $e8, $f0 + +MaxRightXSpdData: + .db $28, $18, $10 + .db $0c ;used for pipe intros + +FrictionData: + .db $e4, $98, $d0 + +Climb_Y_SpeedData: + .db $00, $ff, $01 + +Climb_Y_MForceData: + .db $00, $20, $ff + +PlayerPhysicsSub: + lda Player_State ;check player state + cmp #$03 + bne CheckForJumping ;if not climbing, branch + ldy #$00 + lda Up_Down_Buttons ;get controller bits for up/down + and Player_CollisionBits ;check against player's collision detection bits + beq ProcClimb ;if not pressing up or down, branch + iny + and #%00001000 ;check for pressing up + bne ProcClimb + iny +ProcClimb: ldx Climb_Y_MForceData,y ;load value here + stx Player_Y_MoveForce ;store as vertical movement force + lda #$08 ;load default animation timing + ldx Climb_Y_SpeedData,y ;load some other value here + stx Player_Y_Speed ;store as vertical speed + bmi SetCAnim ;if climbing down, use default animation timing value + lsr ;otherwise divide timer setting by 2 +SetCAnim: sta PlayerAnimTimerSet ;store animation timer setting and leave + rts + +CheckForJumping: + lda JumpspringAnimCtrl ;if jumpspring animating, + bne NoJump ;skip ahead to something else + lda A_B_Buttons ;check for A button press + and #A_Button + beq NoJump ;if not, branch to something else + and PreviousA_B_Buttons ;if button not pressed in previous frame, branch + beq ProcJumping +NoJump: jmp X_Physics ;otherwise, jump to something else + +ProcJumping: + lda Player_State ;check player state + beq InitJS ;if on the ground, branch + lda SwimmingFlag ;if swimming flag not set, jump to do something else + beq NoJump ;to prevent midair jumping, otherwise continue + lda JumpSwimTimer ;if jump/swim timer nonzero, branch + bne InitJS + lda Player_Y_Speed ;check player's vertical speed + bpl InitJS ;if player's vertical speed motionless or down, branch + jmp X_Physics ;if timer at zero and player still rising, do not swim +InitJS: lda #$20 ;set jump/swim timer + sta JumpSwimTimer + ldy #$00 ;initialize vertical force and dummy variable + sty Player_YMF_Dummy + sty Player_Y_MoveForce + lda Player_Y_HighPos ;get vertical high and low bytes of jump origin + sta JumpOrigin_Y_HighPos ;and store them next to each other here + lda Player_Y_Position + sta JumpOrigin_Y_Position + lda #$01 ;set player state to jumping/swimming + sta Player_State + lda Player_XSpeedAbsolute ;check value related to walking/running speed + cmp #$09 + bcc ChkWtr ;branch if below certain values, increment Y + iny ;for each amount equal or exceeded + cmp #$10 + bcc ChkWtr + iny + cmp #$19 + bcc ChkWtr + iny + cmp #$1c + bcc ChkWtr ;note that for jumping, range is 0-4 for Y + iny +ChkWtr: lda #$01 ;set value here (apparently always set to 1) + sta DiffToHaltJump + lda SwimmingFlag ;if swimming flag disabled, branch + beq GetYPhy + ldy #$05 ;otherwise set Y to 5, range is 5-6 + lda Whirlpool_Flag ;if whirlpool flag not set, branch + beq GetYPhy + iny ;otherwise increment to 6 +GetYPhy: lda JumpMForceData,y ;store appropriate jump/swim + sta VerticalForce ;data here + lda FallMForceData,y + sta VerticalForceDown + lda InitMForceData,y + sta Player_Y_MoveForce + lda PlayerYSpdData,y + sta Player_Y_Speed + lda SwimmingFlag ;if swimming flag disabled, branch + beq PJumpSnd + lda #Sfx_EnemyStomp ;load swim/goomba stomp sound into + sta Square1SoundQueue ;square 1's sfx queue + lda Player_Y_Position + cmp #$14 ;check vertical low byte of player position + bcs X_Physics ;if below a certain point, branch + lda #$00 ;otherwise reset player's vertical speed + sta Player_Y_Speed ;and jump to something else to keep player + jmp X_Physics ;from swimming above water level +PJumpSnd: lda #Sfx_BigJump ;load big mario's jump sound by default + ldy PlayerSize ;is mario big? + beq SJumpSnd + lda #Sfx_SmallJump ;if not, load small mario's jump sound +SJumpSnd: sta Square1SoundQueue ;store appropriate jump sound in square 1 sfx queue +X_Physics: ldy #$00 + sty $00 ;init value here + lda Player_State ;if mario is on the ground, branch + beq ProcPRun + lda Player_XSpeedAbsolute ;check something that seems to be related + cmp #$19 ;to mario's speed + bcs GetXPhy ;if =>$19 branch here + bcc ChkRFast ;if not branch elsewhere +ProcPRun: iny ;if mario on the ground, increment Y + lda AreaType ;check area type + beq ChkRFast ;if water type, branch + dey ;decrement Y by default for non-water type area + lda Left_Right_Buttons ;get left/right controller bits + cmp Player_MovingDir ;check against moving direction + bne ChkRFast ;if controller bits <> moving direction, skip this part + lda A_B_Buttons ;check for b button pressed + and #B_Button + bne SetRTmr ;if pressed, skip ahead to set timer + lda RunningTimer ;check for running timer set + bne GetXPhy ;if set, branch +ChkRFast: iny ;if running timer not set or level type is water, + inc $00 ;increment Y again and temp variable in memory + lda RunningSpeed + bne FastXSp ;if running speed set here, branch + lda Player_XSpeedAbsolute + cmp #$21 ;otherwise check player's walking/running speed + bcc GetXPhy ;if less than a certain amount, branch ahead +FastXSp: inc $00 ;if running speed set or speed => $21 increment $00 + jmp GetXPhy ;and jump ahead +SetRTmr: lda #$0a ;if b button pressed, set running timer + sta RunningTimer +GetXPhy: lda MaxLeftXSpdData,y ;get maximum speed to the left + sta MaximumLeftSpeed + lda GameEngineSubroutine ;check for specific routine running + cmp #$07 ;(player entrance) + bne GetXPhy2 ;if not running, skip and use old value of Y + ldy #$03 ;otherwise set Y to 3 +GetXPhy2: lda MaxRightXSpdData,y ;get maximum speed to the right + sta MaximumRightSpeed + ldy $00 ;get other value in memory + lda FrictionData,y ;get value using value in memory as offset + sta FrictionAdderLow + lda #$00 + sta FrictionAdderHigh ;init something here + lda PlayerFacingDir + cmp Player_MovingDir ;check facing direction against moving direction + beq ExitPhy ;if the same, branch to leave + asl FrictionAdderLow ;otherwise shift d7 of friction adder low into carry + rol FrictionAdderHigh ;then rotate carry onto d0 of friction adder high +ExitPhy: rts ;and then leave + +;------------------------------------------------------------------------------------- + +PlayerAnimTmrData: + .db $02, $04, $07 + +GetPlayerAnimSpeed: + ldy #$00 ;initialize offset in Y + lda Player_XSpeedAbsolute ;check player's walking/running speed + cmp #$1c ;against preset amount + bcs SetRunSpd ;if greater than a certain amount, branch ahead + iny ;otherwise increment Y + cmp #$0e ;compare against lower amount + bcs ChkSkid ;if greater than this but not greater than first, skip increment + iny ;otherwise increment Y again +ChkSkid: lda SavedJoypadBits ;get controller bits + and #%01111111 ;mask out A button + beq SetAnimSpd ;if no other buttons pressed, branch ahead of all this + and #$03 ;mask out all others except left and right + cmp Player_MovingDir ;check against moving direction + bne ProcSkid ;if left/right controller bits <> moving direction, branch + lda #$00 ;otherwise set zero value here +SetRunSpd: sta RunningSpeed ;store zero or running speed here + jmp SetAnimSpd +ProcSkid: lda Player_XSpeedAbsolute ;check player's walking/running speed + cmp #$0b ;against one last amount + bcs SetAnimSpd ;if greater than this amount, branch + lda PlayerFacingDir + sta Player_MovingDir ;otherwise use facing direction to set moving direction + lda #$00 + sta Player_X_Speed ;nullify player's horizontal speed + sta Player_X_MoveForce ;and dummy variable for player +SetAnimSpd: lda PlayerAnimTmrData,y ;get animation timer setting using Y as offset + sta PlayerAnimTimerSet + rts + +;------------------------------------------------------------------------------------- + +ImposeFriction: + and Player_CollisionBits ;perform AND between left/right controller bits and collision flag + cmp #$00 ;then compare to zero (this instruction is redundant) + bne JoypFrict ;if any bits set, branch to next part + lda Player_X_Speed + beq SetAbsSpd ;if player has no horizontal speed, branch ahead to last part + bpl RghtFrict ;if player moving to the right, branch to slow + bmi LeftFrict ;otherwise logic dictates player moving left, branch to slow +JoypFrict: lsr ;put right controller bit into carry + bcc RghtFrict ;if left button pressed, carry = 0, thus branch +LeftFrict: lda Player_X_MoveForce ;load value set here + clc + adc FrictionAdderLow ;add to it another value set here + sta Player_X_MoveForce ;store here + lda Player_X_Speed + adc FrictionAdderHigh ;add value plus carry to horizontal speed + sta Player_X_Speed ;set as new horizontal speed + cmp MaximumRightSpeed ;compare against maximum value for right movement + bmi XSpdSign ;if horizontal speed greater negatively, branch + lda MaximumRightSpeed ;otherwise set preset value as horizontal speed + sta Player_X_Speed ;thus slowing the player's left movement down + jmp SetAbsSpd ;skip to the end +RghtFrict: lda Player_X_MoveForce ;load value set here + sec + sbc FrictionAdderLow ;subtract from it another value set here + sta Player_X_MoveForce ;store here + lda Player_X_Speed + sbc FrictionAdderHigh ;subtract value plus borrow from horizontal speed + sta Player_X_Speed ;set as new horizontal speed + cmp MaximumLeftSpeed ;compare against maximum value for left movement + bpl XSpdSign ;if horizontal speed greater positively, branch + lda MaximumLeftSpeed ;otherwise set preset value as horizontal speed + sta Player_X_Speed ;thus slowing the player's right movement down +XSpdSign: cmp #$00 ;if player not moving or moving to the right, + bpl SetAbsSpd ;branch and leave horizontal speed value unmodified + eor #$ff + clc ;otherwise get two's compliment to get absolute + adc #$01 ;unsigned walking/running speed +SetAbsSpd: sta Player_XSpeedAbsolute ;store walking/running speed here and leave + rts + +;------------------------------------------------------------------------------------- +;$00 - used to store downward movement force in FireballObjCore +;$02 - used to store maximum vertical speed in FireballObjCore +;$07 - used to store pseudorandom bit in BubbleCheck + +ProcFireball_Bubble: + lda PlayerStatus ;check player's status + cmp #$02 + bcc ProcAirBubbles ;if not fiery, branch + lda A_B_Buttons + and #B_Button ;check for b button pressed + beq ProcFireballs ;branch if not pressed + and PreviousA_B_Buttons + bne ProcFireballs ;if button pressed in previous frame, branch + lda FireballCounter ;load fireball counter + and #%00000001 ;get LSB and use as offset for buffer + tax + lda Fireball_State,x ;load fireball state + bne ProcFireballs ;if not inactive, branch + ldy Player_Y_HighPos ;if player too high or too low, branch + dey + bne ProcFireballs + lda CrouchingFlag ;if player crouching, branch + bne ProcFireballs + lda Player_State ;if player's state = climbing, branch + cmp #$03 + beq ProcFireballs + lda #Sfx_Fireball ;play fireball sound effect + sta Square1SoundQueue + lda #$02 ;load state + sta Fireball_State,x + ldy PlayerAnimTimerSet ;copy animation frame timer setting + sty FireballThrowingTimer ;into fireball throwing timer + dey + sty PlayerAnimTimer ;decrement and store in player's animation timer + inc FireballCounter ;increment fireball counter + +ProcFireballs: + ldx #$00 + jsr FireballObjCore ;process first fireball object + ldx #$01 + jsr FireballObjCore ;process second fireball object, then do air bubbles + +ProcAirBubbles: + lda AreaType ;if not water type level, skip the rest of this + bne BublExit + ldx #$02 ;otherwise load counter and use as offset +BublLoop: stx ObjectOffset ;store offset + jsr BubbleCheck ;check timers and coordinates, create air bubble + jsr RelativeBubblePosition ;get relative coordinates + jsr GetBubbleOffscreenBits ;get offscreen information + jsr DrawBubble ;draw the air bubble + dex + bpl BublLoop ;do this until all three are handled +BublExit: rts ;then leave + +FireballXSpdData: + .db $40, $c0 + +FireballObjCore: + stx ObjectOffset ;store offset as current object + lda Fireball_State,x ;check for d7 = 1 + asl + bcs FireballExplosion ;if so, branch to get relative coordinates and draw explosion + ldy Fireball_State,x ;if fireball inactive, branch to leave + beq NoFBall + dey ;if fireball state set to 1, skip this part and just run it + beq RunFB + lda Player_X_Position ;get player's horizontal position + adc #$04 ;add four pixels and store as fireball's horizontal position + sta Fireball_X_Position,x + lda Player_PageLoc ;get player's page location + adc #$00 ;add carry and store as fireball's page location + sta Fireball_PageLoc,x + lda Player_Y_Position ;get player's vertical position and store + sta Fireball_Y_Position,x + lda #$01 ;set high byte of vertical position + sta Fireball_Y_HighPos,x + ldy PlayerFacingDir ;get player's facing direction + dey ;decrement to use as offset here + lda FireballXSpdData,y ;set horizontal speed of fireball accordingly + sta Fireball_X_Speed,x + lda #$04 ;set vertical speed of fireball + sta Fireball_Y_Speed,x + lda #$07 + sta Fireball_BoundBoxCtrl,x ;set bounding box size control for fireball + dec Fireball_State,x ;decrement state to 1 to skip this part from now on +RunFB: txa ;add 7 to offset to use + clc ;as fireball offset for next routines + adc #$07 + tax + lda #$50 ;set downward movement force here + sta $00 + lda #$03 ;set maximum speed here + sta $02 + lda #$00 + jsr ImposeGravity ;do sub here to impose gravity on fireball and move vertically + jsr MoveObjectHorizontally ;do another sub to move it horizontally + ldx ObjectOffset ;return fireball offset to X + jsr RelativeFireballPosition ;get relative coordinates + jsr GetFireballOffscreenBits ;get offscreen information + jsr GetFireballBoundBox ;get bounding box coordinates + jsr FireballBGCollision ;do fireball to background collision detection + lda FBall_OffscreenBits ;get fireball offscreen bits + and #%11001100 ;mask out certain bits + bne EraseFB ;if any bits still set, branch to kill fireball + jsr FireballEnemyCollision ;do fireball to enemy collision detection and deal with collisions + jmp DrawFireball ;draw fireball appropriately and leave +EraseFB: lda #$00 ;erase fireball state + sta Fireball_State,x +NoFBall: rts ;leave + +FireballExplosion: + jsr RelativeFireballPosition + jmp DrawExplosion_Fireball + +BubbleCheck: + lda PseudoRandomBitReg+1,x ;get part of LSFR + and #$01 + sta $07 ;store pseudorandom bit here + lda Bubble_Y_Position,x ;get vertical coordinate for air bubble + cmp #$f8 ;if offscreen coordinate not set, + bne MoveBubl ;branch to move air bubble + lda AirBubbleTimer ;if air bubble timer not expired, + bne ExitBubl ;branch to leave, otherwise create new air bubble + +SetupBubble: + ldy #$00 ;load default value here + lda PlayerFacingDir ;get player's facing direction + lsr ;move d0 to carry + bcc PosBubl ;branch to use default value if facing left + ldy #$08 ;otherwise load alternate value here +PosBubl: tya ;use value loaded as adder + adc Player_X_Position ;add to player's horizontal position + sta Bubble_X_Position,x ;save as horizontal position for airbubble + lda Player_PageLoc + adc #$00 ;add carry to player's page location + sta Bubble_PageLoc,x ;save as page location for airbubble + lda Player_Y_Position + clc ;add eight pixels to player's vertical position + adc #$08 + sta Bubble_Y_Position,x ;save as vertical position for air bubble + lda #$01 + sta Bubble_Y_HighPos,x ;set vertical high byte for air bubble + ldy $07 ;get pseudorandom bit, use as offset + lda BubbleTimerData,y ;get data for air bubble timer + sta AirBubbleTimer ;set air bubble timer +MoveBubl: ldy $07 ;get pseudorandom bit again, use as offset + lda Bubble_YMF_Dummy,x + sec ;subtract pseudorandom amount from dummy variable + sbc Bubble_MForceData,y + sta Bubble_YMF_Dummy,x ;save dummy variable + lda Bubble_Y_Position,x + sbc #$00 ;subtract borrow from airbubble's vertical coordinate + cmp #$20 ;if below the status bar, + bcs Y_Bubl ;branch to go ahead and use to move air bubble upwards + lda #$f8 ;otherwise set offscreen coordinate +Y_Bubl: sta Bubble_Y_Position,x ;store as new vertical coordinate for air bubble +ExitBubl: rts ;leave + +Bubble_MForceData: + .db $ff, $50 + +BubbleTimerData: + .db $40, $20 + +;------------------------------------------------------------------------------------- + +RunGameTimer: + lda OperMode ;get primary mode of operation + beq ExGTimer ;branch to leave if in title screen mode + lda GameEngineSubroutine + cmp #$08 ;if routine number less than eight running, + bcc ExGTimer ;branch to leave + cmp #$0b ;if running death routine, + beq ExGTimer ;branch to leave + lda Player_Y_HighPos + cmp #$02 ;if player below the screen, + bcs ExGTimer ;branch to leave regardless of level type + lda GameTimerCtrlTimer ;if game timer control not yet expired, + bne ExGTimer ;branch to leave + lda GameTimerDisplay + ora GameTimerDisplay+1 ;otherwise check game timer digits + ora GameTimerDisplay+2 + beq TimeUpOn ;if game timer digits at 000, branch to time-up code + ldy GameTimerDisplay ;otherwise check first digit + dey ;if first digit not on 1, + bne ResGTCtrl ;branch to reset game timer control + lda GameTimerDisplay+1 ;otherwise check second and third digits + ora GameTimerDisplay+2 + bne ResGTCtrl ;if timer not at 100, branch to reset game timer control + lda #TimeRunningOutMusic + sta EventMusicQueue ;otherwise load time running out music +ResGTCtrl: lda #$18 ;reset game timer control + sta GameTimerCtrlTimer + ldy #$23 ;set offset for last digit + lda #$ff ;set value to decrement game timer digit + sta DigitModifier+5 + jsr DigitsMathRoutine ;do sub to decrement game timer slowly + lda #$a4 ;set status nybbles to update game timer display + jmp PrintStatusBarNumbers ;do sub to update the display +TimeUpOn: sta PlayerStatus ;init player status (note A will always be zero here) + jsr ForceInjury ;do sub to kill the player (note player is small here) + inc GameTimerExpiredFlag ;set game timer expiration flag +ExGTimer: rts ;leave + +;------------------------------------------------------------------------------------- + +WarpZoneObject: + lda ScrollLock ;check for scroll lock flag + beq ExGTimer ;branch if not set to leave + lda Player_Y_Position ;check to see if player's vertical coordinate has + and Player_Y_HighPos ;same bits set as in vertical high byte (why?) + bne ExGTimer ;if so, branch to leave + sta ScrollLock ;otherwise nullify scroll lock flag + inc WarpZoneControl ;increment warp zone flag to make warp pipes for warp zone + jmp EraseEnemyObject ;kill this object + +;------------------------------------------------------------------------------------- +;$00 - used in WhirlpoolActivate to store whirlpool length / 2, page location of center of whirlpool +;and also to store movement force exerted on player +;$01 - used in ProcessWhirlpools to store page location of right extent of whirlpool +;and in WhirlpoolActivate to store center of whirlpool +;$02 - used in ProcessWhirlpools to store right extent of whirlpool and in +;WhirlpoolActivate to store maximum vertical speed + +ProcessWhirlpools: + lda AreaType ;check for water type level + bne ExitWh ;branch to leave if not found + sta Whirlpool_Flag ;otherwise initialize whirlpool flag + lda TimerControl ;if master timer control set, + bne ExitWh ;branch to leave + ldy #$04 ;otherwise start with last whirlpool data +WhLoop: lda Whirlpool_LeftExtent,y ;get left extent of whirlpool + clc + adc Whirlpool_Length,y ;add length of whirlpool + sta $02 ;store result as right extent here + lda Whirlpool_PageLoc,y ;get page location + beq NextWh ;if none or page 0, branch to get next data + adc #$00 ;add carry + sta $01 ;store result as page location of right extent here + lda Player_X_Position ;get player's horizontal position + sec + sbc Whirlpool_LeftExtent,y ;subtract left extent + lda Player_PageLoc ;get player's page location + sbc Whirlpool_PageLoc,y ;subtract borrow + bmi NextWh ;if player too far left, branch to get next data + lda $02 ;otherwise get right extent + sec + sbc Player_X_Position ;subtract player's horizontal coordinate + lda $01 ;get right extent's page location + sbc Player_PageLoc ;subtract borrow + bpl WhirlpoolActivate ;if player within right extent, branch to whirlpool code +NextWh: dey ;move onto next whirlpool data + bpl WhLoop ;do this until all whirlpools are checked +ExitWh: rts ;leave + +WhirlpoolActivate: + lda Whirlpool_Length,y ;get length of whirlpool + lsr ;divide by 2 + sta $00 ;save here + lda Whirlpool_LeftExtent,y ;get left extent of whirlpool + clc + adc $00 ;add length divided by 2 + sta $01 ;save as center of whirlpool + lda Whirlpool_PageLoc,y ;get page location + adc #$00 ;add carry + sta $00 ;save as page location of whirlpool center + lda FrameCounter ;get frame counter + lsr ;shift d0 into carry (to run on every other frame) + bcc WhPull ;if d0 not set, branch to last part of code + lda $01 ;get center + sec + sbc Player_X_Position ;subtract player's horizontal coordinate + lda $00 ;get page location of center + sbc Player_PageLoc ;subtract borrow + bpl LeftWh ;if player to the left of center, branch + lda Player_X_Position ;otherwise slowly pull player left, towards the center + sec + sbc #$01 ;subtract one pixel + sta Player_X_Position ;set player's new horizontal coordinate + lda Player_PageLoc + sbc #$00 ;subtract borrow + jmp SetPWh ;jump to set player's new page location +LeftWh: lda Player_CollisionBits ;get player's collision bits + lsr ;shift d0 into carry + bcc WhPull ;if d0 not set, branch + lda Player_X_Position ;otherwise slowly pull player right, towards the center + clc + adc #$01 ;add one pixel + sta Player_X_Position ;set player's new horizontal coordinate + lda Player_PageLoc + adc #$00 ;add carry +SetPWh: sta Player_PageLoc ;set player's new page location +WhPull: lda #$10 + sta $00 ;set vertical movement force + lda #$01 + sta Whirlpool_Flag ;set whirlpool flag to be used later + sta $02 ;also set maximum vertical speed + lsr + tax ;set X for player offset + jmp ImposeGravity ;jump to put whirlpool effect on player vertically, do not return + +;------------------------------------------------------------------------------------- + +FlagpoleScoreMods: + .db $05, $02, $08, $04, $01 + +FlagpoleScoreDigits: + .db $03, $03, $04, $04, $04 + +FlagpoleRoutine: + ldx #$05 ;set enemy object offset + stx ObjectOffset ;to special use slot + lda Enemy_ID,x + cmp #FlagpoleFlagObject ;if flagpole flag not found, + bne ExitFlagP ;branch to leave + lda GameEngineSubroutine + cmp #$04 ;if flagpole slide routine not running, + bne SkipScore ;branch to near the end of code + lda Player_State + cmp #$03 ;if player state not climbing, + bne SkipScore ;branch to near the end of code + lda Enemy_Y_Position,x ;check flagpole flag's vertical coordinate + cmp #$aa ;if flagpole flag down to a certain point, + bcs GiveFPScr ;branch to end the level + lda Player_Y_Position ;check player's vertical coordinate + cmp #$a2 ;if player down to a certain point, + bcs GiveFPScr ;branch to end the level + lda Enemy_YMF_Dummy,x + adc #$ff ;add movement amount to dummy variable + sta Enemy_YMF_Dummy,x ;save dummy variable + lda Enemy_Y_Position,x ;get flag's vertical coordinate + adc #$01 ;add 1 plus carry to move flag, and + sta Enemy_Y_Position,x ;store vertical coordinate + lda FlagpoleFNum_YMFDummy + sec ;subtract movement amount from dummy variable + sbc #$ff + sta FlagpoleFNum_YMFDummy ;save dummy variable + lda FlagpoleFNum_Y_Pos + sbc #$01 ;subtract one plus borrow to move floatey number, + sta FlagpoleFNum_Y_Pos ;and store vertical coordinate here +SkipScore: jmp FPGfx ;jump to skip ahead and draw flag and floatey number +GiveFPScr: ldy FlagpoleScore ;get score offset from earlier (when player touched flagpole) + lda FlagpoleScoreMods,y ;get amount to award player points + ldx FlagpoleScoreDigits,y ;get digit with which to award points + sta DigitModifier,x ;store in digit modifier + jsr AddToScore ;do sub to award player points depending on height of collision + lda #$05 + sta GameEngineSubroutine ;set to run end-of-level subroutine on next frame +FPGfx: jsr GetEnemyOffscreenBits ;get offscreen information + jsr RelativeEnemyPosition ;get relative coordinates + jsr FlagpoleGfxHandler ;draw flagpole flag and floatey number +ExitFlagP: rts + +;------------------------------------------------------------------------------------- + +Jumpspring_Y_PosData: + .db $08, $10, $08, $00 + +JumpspringHandler: + jsr GetEnemyOffscreenBits ;get offscreen information + lda TimerControl ;check master timer control + bne DrawJSpr ;branch to last section if set + lda JumpspringAnimCtrl ;check jumpspring frame control + beq DrawJSpr ;branch to last section if not set + tay + dey ;subtract one from frame control, + tya ;the only way a poor nmos 6502 can + and #%00000010 ;mask out all but d1, original value still in Y + bne DownJSpr ;if set, branch to move player up + inc Player_Y_Position + inc Player_Y_Position ;move player's vertical position down two pixels + jmp PosJSpr ;skip to next part +DownJSpr: dec Player_Y_Position ;move player's vertical position up two pixels + dec Player_Y_Position +PosJSpr: lda Jumpspring_FixedYPos,x ;get permanent vertical position + clc + adc Jumpspring_Y_PosData,y ;add value using frame control as offset + sta Enemy_Y_Position,x ;store as new vertical position + cpy #$01 ;check frame control offset (second frame is $00) + bcc BounceJS ;if offset not yet at third frame ($01), skip to next part + lda A_B_Buttons + and #A_Button ;check saved controller bits for A button press + beq BounceJS ;skip to next part if A not pressed + and PreviousA_B_Buttons ;check for A button pressed in previous frame + bne BounceJS ;skip to next part if so + lda #$f4 + sta JumpspringForce ;otherwise write new jumpspring force here +BounceJS: cpy #$03 ;check frame control offset again + bne DrawJSpr ;skip to last part if not yet at fifth frame ($03) + lda JumpspringForce + sta Player_Y_Speed ;store jumpspring force as player's new vertical speed + lda #$00 + sta JumpspringAnimCtrl ;initialize jumpspring frame control +DrawJSpr: jsr RelativeEnemyPosition ;get jumpspring's relative coordinates + jsr EnemyGfxHandler ;draw jumpspring + jsr OffscreenBoundsCheck ;check to see if we need to kill it + lda JumpspringAnimCtrl ;if frame control at zero, don't bother + beq ExJSpring ;trying to animate it, just leave + lda JumpspringTimer + bne ExJSpring ;if jumpspring timer not expired yet, leave + lda #$04 + sta JumpspringTimer ;otherwise initialize jumpspring timer + inc JumpspringAnimCtrl ;increment frame control to animate jumpspring +ExJSpring: rts ;leave + +;------------------------------------------------------------------------------------- + +Setup_Vine: + lda #VineObject ;load identifier for vine object + sta Enemy_ID,x ;store in buffer + lda #$01 + sta Enemy_Flag,x ;set flag for enemy object buffer + lda Block_PageLoc,y + sta Enemy_PageLoc,x ;copy page location from previous object + lda Block_X_Position,y + sta Enemy_X_Position,x ;copy horizontal coordinate from previous object + lda Block_Y_Position,y + sta Enemy_Y_Position,x ;copy vertical coordinate from previous object + ldy VineFlagOffset ;load vine flag/offset to next available vine slot + bne NextVO ;if set at all, don't bother to store vertical + sta VineStart_Y_Position ;otherwise store vertical coordinate here +NextVO: txa ;store object offset to next available vine slot + sta VineObjOffset,y ;using vine flag as offset + inc VineFlagOffset ;increment vine flag offset + lda #Sfx_GrowVine + sta Square2SoundQueue ;load vine grow sound + rts + +;------------------------------------------------------------------------------------- +;$06-$07 - used as address to block buffer data +;$02 - used as vertical high nybble of block buffer offset + +VineHeightData: + .db $30, $60 + +VineObjectHandler: + cpx #$05 ;check enemy offset for special use slot + bne ExitVH ;if not in last slot, branch to leave + ldy VineFlagOffset + dey ;decrement vine flag in Y, use as offset + lda VineHeight + cmp VineHeightData,y ;if vine has reached certain height, + beq RunVSubs ;branch ahead to skip this part + lda FrameCounter ;get frame counter + lsr ;shift d1 into carry + lsr + bcc RunVSubs ;if d1 not set (2 frames every 4) skip this part + lda Enemy_Y_Position+5 + sbc #$01 ;subtract vertical position of vine + sta Enemy_Y_Position+5 ;one pixel every frame it's time + inc VineHeight ;increment vine height +RunVSubs: lda VineHeight ;if vine still very small, + cmp #$08 ;branch to leave + bcc ExitVH + jsr RelativeEnemyPosition ;get relative coordinates of vine, + jsr GetEnemyOffscreenBits ;and any offscreen bits + ldy #$00 ;initialize offset used in draw vine sub +VDrawLoop: jsr DrawVine ;draw vine + iny ;increment offset + cpy VineFlagOffset ;if offset in Y and offset here + bne VDrawLoop ;do not yet match, loop back to draw more vine + lda Enemy_OffscreenBits + and #%00001100 ;mask offscreen bits + beq WrCMTile ;if none of the saved offscreen bits set, skip ahead + dey ;otherwise decrement Y to get proper offset again +KillVine: ldx VineObjOffset,y ;get enemy object offset for this vine object + jsr EraseEnemyObject ;kill this vine object + dey ;decrement Y + bpl KillVine ;if any vine objects left, loop back to kill it + sta VineFlagOffset ;initialize vine flag/offset + sta VineHeight ;initialize vine height +WrCMTile: lda VineHeight ;check vine height + cmp #$20 ;if vine small (less than 32 pixels tall) + bcc ExitVH ;then branch ahead to leave + ldx #$06 ;set offset in X to last enemy slot + lda #$01 ;set A to obtain horizontal in $04, but we don't care + ldy #$1b ;set Y to offset to get block at ($04, $10) of coordinates + jsr BlockBufferCollision ;do a sub to get block buffer address set, return contents + ldy $02 + cpy #$d0 ;if vertical high nybble offset beyond extent of + bcs ExitVH ;current block buffer, branch to leave, do not write + lda ($06),y ;otherwise check contents of block buffer at + bne ExitVH ;current offset, if not empty, branch to leave + lda #$26 + sta ($06),y ;otherwise, write climbing metatile to block buffer +ExitVH: ldx ObjectOffset ;get enemy object offset and leave + rts + +;------------------------------------------------------------------------------------- + +CannonBitmasks: + .db %00001111, %00000111 + +ProcessCannons: + lda AreaType ;get area type + beq ExCannon ;if water type area, branch to leave + ldx #$02 +ThreeSChk: stx ObjectOffset ;start at third enemy slot + lda Enemy_Flag,x ;check enemy buffer flag + bne Chk_BB ;if set, branch to check enemy + lda PseudoRandomBitReg+1,x ;otherwise get part of LSFR + ldy SecondaryHardMode ;get secondary hard mode flag, use as offset + and CannonBitmasks,y ;mask out bits of LSFR as decided by flag + cmp #$06 ;check to see if lower nybble is above certain value + bcs Chk_BB ;if so, branch to check enemy + tay ;transfer masked contents of LSFR to Y as pseudorandom offset + lda Cannon_PageLoc,y ;get page location + beq Chk_BB ;if not set or on page 0, branch to check enemy + lda Cannon_Timer,y ;get cannon timer + beq FireCannon ;if expired, branch to fire cannon + sbc #$00 ;otherwise subtract borrow (note carry will always be clear here) + sta Cannon_Timer,y ;to count timer down + jmp Chk_BB ;then jump ahead to check enemy + +FireCannon: + lda TimerControl ;if master timer control set, + bne Chk_BB ;branch to check enemy + lda #$0e ;otherwise we start creating one + sta Cannon_Timer,y ;first, reset cannon timer + lda Cannon_PageLoc,y ;get page location of cannon + sta Enemy_PageLoc,x ;save as page location of bullet bill + lda Cannon_X_Position,y ;get horizontal coordinate of cannon + sta Enemy_X_Position,x ;save as horizontal coordinate of bullet bill + lda Cannon_Y_Position,y ;get vertical coordinate of cannon + sec + sbc #$08 ;subtract eight pixels (because enemies are 24 pixels tall) + sta Enemy_Y_Position,x ;save as vertical coordinate of bullet bill + lda #$01 + sta Enemy_Y_HighPos,x ;set vertical high byte of bullet bill + sta Enemy_Flag,x ;set buffer flag + lsr ;shift right once to init A + sta Enemy_State,x ;then initialize enemy's state + lda #$09 + sta Enemy_BoundBoxCtrl,x ;set bounding box size control for bullet bill + lda #BulletBill_CannonVar + sta Enemy_ID,x ;load identifier for bullet bill (cannon variant) + jmp Next3Slt ;move onto next slot +Chk_BB: lda Enemy_ID,x ;check enemy identifier for bullet bill (cannon variant) + cmp #BulletBill_CannonVar + bne Next3Slt ;if not found, branch to get next slot + jsr OffscreenBoundsCheck ;otherwise, check to see if it went offscreen + lda Enemy_Flag,x ;check enemy buffer flag + beq Next3Slt ;if not set, branch to get next slot + jsr GetEnemyOffscreenBits ;otherwise, get offscreen information + jsr BulletBillHandler ;then do sub to handle bullet bill +Next3Slt: dex ;move onto next slot + bpl ThreeSChk ;do this until first three slots are checked +ExCannon: rts ;then leave + +;-------------------------------- + +BulletBillXSpdData: + .db $18, $e8 + +BulletBillHandler: + lda TimerControl ;if master timer control set, + bne RunBBSubs ;branch to run subroutines except movement sub + lda Enemy_State,x + bne ChkDSte ;if bullet bill's state set, branch to check defeated state + lda Enemy_OffscreenBits ;otherwise load offscreen bits + and #%00001100 ;mask out bits + cmp #%00001100 ;check to see if all bits are set + beq KillBB ;if so, branch to kill this object + ldy #$01 ;set to move right by default + jsr PlayerEnemyDiff ;get horizontal difference between player and bullet bill + bmi SetupBB ;if enemy to the left of player, branch + iny ;otherwise increment to move left +SetupBB: sty Enemy_MovingDir,x ;set bullet bill's moving direction + dey ;decrement to use as offset + lda BulletBillXSpdData,y ;get horizontal speed based on moving direction + sta Enemy_X_Speed,x ;and store it + lda $00 ;get horizontal difference + adc #$28 ;add 40 pixels + cmp #$50 ;if less than a certain amount, player is too close + bcc KillBB ;to cannon either on left or right side, thus branch + lda #$01 + sta Enemy_State,x ;otherwise set bullet bill's state + lda #$0a + sta EnemyFrameTimer,x ;set enemy frame timer + lda #Sfx_Blast + sta Square2SoundQueue ;play fireworks/gunfire sound +ChkDSte: lda Enemy_State,x ;check enemy state for d5 set + and #%00100000 + beq BBFly ;if not set, skip to move horizontally + jsr MoveD_EnemyVertically ;otherwise do sub to move bullet bill vertically +BBFly: jsr MoveEnemyHorizontally ;do sub to move bullet bill horizontally +RunBBSubs: jsr GetEnemyOffscreenBits ;get offscreen information + jsr RelativeEnemyPosition ;get relative coordinates + jsr GetEnemyBoundBox ;get bounding box coordinates + jsr PlayerEnemyCollision ;handle player to enemy collisions + jmp EnemyGfxHandler ;draw the bullet bill and leave +KillBB: jsr EraseEnemyObject ;kill bullet bill and leave + rts + +;------------------------------------------------------------------------------------- + +HammerEnemyOfsData: + .db $04, $04, $04, $05, $05, $05 + .db $06, $06, $06 + +HammerXSpdData: + .db $10, $f0 + +SpawnHammerObj: + lda PseudoRandomBitReg+1 ;get pseudorandom bits from + and #%00000111 ;second part of LSFR + bne SetMOfs ;if any bits are set, branch and use as offset + lda PseudoRandomBitReg+1 + and #%00001000 ;get d3 from same part of LSFR +SetMOfs: tay ;use either d3 or d2-d0 for offset here + lda Misc_State,y ;if any values loaded in + bne NoHammer ;$2a-$32 where offset is then leave with carry clear + ldx HammerEnemyOfsData,y ;get offset of enemy slot to check using Y as offset + lda Enemy_Flag,x ;check enemy buffer flag at offset + bne NoHammer ;if buffer flag set, branch to leave with carry clear + ldx ObjectOffset ;get original enemy object offset + txa + sta HammerEnemyOffset,y ;save here + lda #$90 + sta Misc_State,y ;save hammer's state here + lda #$07 + sta Misc_BoundBoxCtrl,y ;set something else entirely, here + sec ;return with carry set + rts +NoHammer: ldx ObjectOffset ;get original enemy object offset + clc ;return with carry clear + rts + +;-------------------------------- +;$00 - used to set downward force +;$01 - used to set upward force (residual) +;$02 - used to set maximum speed + +ProcHammerObj: + lda TimerControl ;if master timer control set + bne RunHSubs ;skip all of this code and go to last subs at the end + lda Misc_State,x ;otherwise get hammer's state + and #%01111111 ;mask out d7 + ldy HammerEnemyOffset,x ;get enemy object offset that spawned this hammer + cmp #$02 ;check hammer's state + beq SetHSpd ;if currently at 2, branch + bcs SetHPos ;if greater than 2, branch elsewhere + txa + clc ;add 13 bytes to use + adc #$0d ;proper misc object + tax ;return offset to X + lda #$10 + sta $00 ;set downward movement force + lda #$0f + sta $01 ;set upward movement force (not used) + lda #$04 + sta $02 ;set maximum vertical speed + lda #$00 ;set A to impose gravity on hammer + jsr ImposeGravity ;do sub to impose gravity on hammer and move vertically + jsr MoveObjectHorizontally ;do sub to move it horizontally + ldx ObjectOffset ;get original misc object offset + jmp RunAllH ;branch to essential subroutines +SetHSpd: lda #$fe + sta Misc_Y_Speed,x ;set hammer's vertical speed + lda Enemy_State,y ;get enemy object state + and #%11110111 ;mask out d3 + sta Enemy_State,y ;store new state + ldx Enemy_MovingDir,y ;get enemy's moving direction + dex ;decrement to use as offset + lda HammerXSpdData,x ;get proper speed to use based on moving direction + ldx ObjectOffset ;reobtain hammer's buffer offset + sta Misc_X_Speed,x ;set hammer's horizontal speed +SetHPos: dec Misc_State,x ;decrement hammer's state + lda Enemy_X_Position,y ;get enemy's horizontal position + clc + adc #$02 ;set position 2 pixels to the right + sta Misc_X_Position,x ;store as hammer's horizontal position + lda Enemy_PageLoc,y ;get enemy's page location + adc #$00 ;add carry + sta Misc_PageLoc,x ;store as hammer's page location + lda Enemy_Y_Position,y ;get enemy's vertical position + sec + sbc #$0a ;move position 10 pixels upward + sta Misc_Y_Position,x ;store as hammer's vertical position + lda #$01 + sta Misc_Y_HighPos,x ;set hammer's vertical high byte + bne RunHSubs ;unconditional branch to skip first routine +RunAllH: jsr PlayerHammerCollision ;handle collisions +RunHSubs: jsr GetMiscOffscreenBits ;get offscreen information + jsr RelativeMiscPosition ;get relative coordinates + jsr GetMiscBoundBox ;get bounding box coordinates + jsr DrawHammer ;draw the hammer + rts ;and we are done here + +;------------------------------------------------------------------------------------- +;$02 - used to store vertical high nybble offset from block buffer routine +;$06 - used to store low byte of block buffer address + +CoinBlock: + jsr FindEmptyMiscSlot ;set offset for empty or last misc object buffer slot + lda Block_PageLoc,x ;get page location of block object + sta Misc_PageLoc,y ;store as page location of misc object + lda Block_X_Position,x ;get horizontal coordinate of block object + ora #$05 ;add 5 pixels + sta Misc_X_Position,y ;store as horizontal coordinate of misc object + lda Block_Y_Position,x ;get vertical coordinate of block object + sbc #$10 ;subtract 16 pixels + sta Misc_Y_Position,y ;store as vertical coordinate of misc object + jmp JCoinC ;jump to rest of code as applies to this misc object + +SetupJumpCoin: + jsr FindEmptyMiscSlot ;set offset for empty or last misc object buffer slot + lda Block_PageLoc2,x ;get page location saved earlier + sta Misc_PageLoc,y ;and save as page location for misc object + lda $06 ;get low byte of block buffer offset + asl + asl ;multiply by 16 to use lower nybble + asl + asl + ora #$05 ;add five pixels + sta Misc_X_Position,y ;save as horizontal coordinate for misc object + lda $02 ;get vertical high nybble offset from earlier + adc #$20 ;add 32 pixels for the status bar + sta Misc_Y_Position,y ;store as vertical coordinate +JCoinC: lda #$fb + sta Misc_Y_Speed,y ;set vertical speed + lda #$01 + sta Misc_Y_HighPos,y ;set vertical high byte + sta Misc_State,y ;set state for misc object + sta Square2SoundQueue ;load coin grab sound + stx ObjectOffset ;store current control bit as misc object offset + jsr GiveOneCoin ;update coin tally on the screen and coin amount variable + inc CoinTallyFor1Ups ;increment coin tally used to activate 1-up block flag + rts + +FindEmptyMiscSlot: + ldy #$08 ;start at end of misc objects buffer +FMiscLoop: lda Misc_State,y ;get misc object state + beq UseMiscS ;branch if none found to use current offset + dey ;decrement offset + cpy #$05 ;do this for three slots + bne FMiscLoop ;do this until all slots are checked + ldy #$08 ;if no empty slots found, use last slot +UseMiscS: sty JumpCoinMiscOffset ;store offset of misc object buffer here (residual) + rts + +;------------------------------------------------------------------------------------- + +MiscObjectsCore: + ldx #$08 ;set at end of misc object buffer +MiscLoop: stx ObjectOffset ;store misc object offset here + lda Misc_State,x ;check misc object state + beq MiscLoopBack ;branch to check next slot + asl ;otherwise shift d7 into carry + bcc ProcJumpCoin ;if d7 not set, jumping coin, thus skip to rest of code here + jsr ProcHammerObj ;otherwise go to process hammer, + jmp MiscLoopBack ;then check next slot + +;-------------------------------- +;$00 - used to set downward force +;$01 - used to set upward force (residual) +;$02 - used to set maximum speed + +ProcJumpCoin: + ldy Misc_State,x ;check misc object state + dey ;decrement to see if it's set to 1 + beq JCoinRun ;if so, branch to handle jumping coin + inc Misc_State,x ;otherwise increment state to either start off or as timer + lda Misc_X_Position,x ;get horizontal coordinate for misc object + clc ;whether its jumping coin (state 0 only) or floatey number + adc ScrollAmount ;add current scroll speed + sta Misc_X_Position,x ;store as new horizontal coordinate + lda Misc_PageLoc,x ;get page location + adc #$00 ;add carry + sta Misc_PageLoc,x ;store as new page location + lda Misc_State,x + cmp #$30 ;check state of object for preset value + bne RunJCSubs ;if not yet reached, branch to subroutines + lda #$00 + sta Misc_State,x ;otherwise nullify object state + jmp MiscLoopBack ;and move onto next slot +JCoinRun: txa + clc ;add 13 bytes to offset for next subroutine + adc #$0d + tax + lda #$50 ;set downward movement amount + sta $00 + lda #$06 ;set maximum vertical speed + sta $02 + lsr ;divide by 2 and set + sta $01 ;as upward movement amount (apparently residual) + lda #$00 ;set A to impose gravity on jumping coin + jsr ImposeGravity ;do sub to move coin vertically and impose gravity on it + ldx ObjectOffset ;get original misc object offset + lda Misc_Y_Speed,x ;check vertical speed + cmp #$05 + bne RunJCSubs ;if not moving downward fast enough, keep state as-is + inc Misc_State,x ;otherwise increment state to change to floatey number +RunJCSubs: jsr RelativeMiscPosition ;get relative coordinates + jsr GetMiscOffscreenBits ;get offscreen information + jsr GetMiscBoundBox ;get bounding box coordinates (why?) + jsr JCoinGfxHandler ;draw the coin or floatey number + +MiscLoopBack: + dex ;decrement misc object offset + bpl MiscLoop ;loop back until all misc objects handled + rts ;then leave + +;------------------------------------------------------------------------------------- + +CoinTallyOffsets: + .db $17, $1d + +ScoreOffsets: + .db $0b, $11 + +StatusBarNybbles: + .db $02, $13 + +GiveOneCoin: + lda #$01 ;set digit modifier to add 1 coin + sta DigitModifier+5 ;to the current player's coin tally + ldx CurrentPlayer ;get current player on the screen + ldy CoinTallyOffsets,x ;get offset for player's coin tally + jsr DigitsMathRoutine ;update the coin tally + inc CoinTally ;increment onscreen player's coin amount + lda CoinTally + cmp #100 ;does player have 100 coins yet? + bne CoinPoints ;if not, skip all of this + lda #$00 + sta CoinTally ;otherwise, reinitialize coin amount + inc NumberofLives ;give the player an extra life + lda #Sfx_ExtraLife + sta Square2SoundQueue ;play 1-up sound + +CoinPoints: + lda #$02 ;set digit modifier to award + sta DigitModifier+4 ;200 points to the player + +AddToScore: + ldx CurrentPlayer ;get current player + ldy ScoreOffsets,x ;get offset for player's score + jsr DigitsMathRoutine ;update the score internally with value in digit modifier + +GetSBNybbles: + ldy CurrentPlayer ;get current player + lda StatusBarNybbles,y ;get nybbles based on player, use to update score and coins + +UpdateNumber: + jsr PrintStatusBarNumbers ;print status bar numbers based on nybbles, whatever they be + ldy VRAM_Buffer1_Offset + lda VRAM_Buffer1-6,y ;check highest digit of score + bne NoZSup ;if zero, overwrite with space tile for zero suppression + lda #$24 + sta VRAM_Buffer1-6,y +NoZSup: ldx ObjectOffset ;get enemy object buffer offset + rts + +;------------------------------------------------------------------------------------- + +SetupPowerUp: + lda #PowerUpObject ;load power-up identifier into + sta Enemy_ID+5 ;special use slot of enemy object buffer + lda Block_PageLoc,x ;store page location of block object + sta Enemy_PageLoc+5 ;as page location of power-up object + lda Block_X_Position,x ;store horizontal coordinate of block object + sta Enemy_X_Position+5 ;as horizontal coordinate of power-up object + lda #$01 + sta Enemy_Y_HighPos+5 ;set vertical high byte of power-up object + lda Block_Y_Position,x ;get vertical coordinate of block object + sec + sbc #$08 ;subtract 8 pixels + sta Enemy_Y_Position+5 ;and use as vertical coordinate of power-up object +PwrUpJmp: lda #$01 ;this is a residual jump point in enemy object jump table + sta Enemy_State+5 ;set power-up object's state + sta Enemy_Flag+5 ;set buffer flag + lda #$03 + sta Enemy_BoundBoxCtrl+5 ;set bounding box size control for power-up object + lda PowerUpType + cmp #$02 ;check currently loaded power-up type + bcs PutBehind ;if star or 1-up, branch ahead + lda PlayerStatus ;otherwise check player's current status + cmp #$02 + bcc StrType ;if player not fiery, use status as power-up type + lsr ;otherwise shift right to force fire flower type +StrType: sta PowerUpType ;store type here +PutBehind: lda #%00100000 + sta Enemy_SprAttrib+5 ;set background priority bit + lda #Sfx_GrowPowerUp + sta Square2SoundQueue ;load power-up reveal sound and leave + rts + +;------------------------------------------------------------------------------------- + +PowerUpObjHandler: + ldx #$05 ;set object offset for last slot in enemy object buffer + stx ObjectOffset + lda Enemy_State+5 ;check power-up object's state + beq ExitPUp ;if not set, branch to leave + asl ;shift to check if d7 was set in object state + bcc GrowThePowerUp ;if not set, branch ahead to skip this part + lda TimerControl ;if master timer control set, + bne RunPUSubs ;branch ahead to enemy object routines + lda PowerUpType ;check power-up type + beq ShroomM ;if normal mushroom, branch ahead to move it + cmp #$03 + beq ShroomM ;if 1-up mushroom, branch ahead to move it + cmp #$02 + bne RunPUSubs ;if not star, branch elsewhere to skip movement + jsr MoveJumpingEnemy ;otherwise impose gravity on star power-up and make it jump + jsr EnemyJump ;note that green paratroopa shares the same code here + jmp RunPUSubs ;then jump to other power-up subroutines +ShroomM: jsr MoveNormalEnemy ;do sub to make mushrooms move + jsr EnemyToBGCollisionDet ;deal with collisions + jmp RunPUSubs ;run the other subroutines + +GrowThePowerUp: + lda FrameCounter ;get frame counter + and #$03 ;mask out all but 2 LSB + bne ChkPUSte ;if any bits set here, branch + dec Enemy_Y_Position+5 ;otherwise decrement vertical coordinate slowly + lda Enemy_State+5 ;load power-up object state + inc Enemy_State+5 ;increment state for next frame (to make power-up rise) + cmp #$11 ;if power-up object state not yet past 16th pixel, + bcc ChkPUSte ;branch ahead to last part here + lda #$10 + sta Enemy_X_Speed,x ;otherwise set horizontal speed + lda #%10000000 + sta Enemy_State+5 ;and then set d7 in power-up object's state + asl ;shift once to init A + sta Enemy_SprAttrib+5 ;initialize background priority bit set here + rol ;rotate A to set right moving direction + sta Enemy_MovingDir,x ;set moving direction +ChkPUSte: lda Enemy_State+5 ;check power-up object's state + cmp #$06 ;for if power-up has risen enough + bcc ExitPUp ;if not, don't even bother running these routines +RunPUSubs: jsr RelativeEnemyPosition ;get coordinates relative to screen + jsr GetEnemyOffscreenBits ;get offscreen bits + jsr GetEnemyBoundBox ;get bounding box coordinates + jsr DrawPowerUp ;draw the power-up object + jsr PlayerEnemyCollision ;check for collision with player + jsr OffscreenBoundsCheck ;check to see if it went offscreen +ExitPUp: rts ;and we're done + +;------------------------------------------------------------------------------------- +;These apply to all routines in this section unless otherwise noted: +;$00 - used to store metatile from block buffer routine +;$02 - used to store vertical high nybble offset from block buffer routine +;$05 - used to store metatile stored in A at beginning of PlayerHeadCollision +;$06-$07 - used as block buffer address indirect + +BlockYPosAdderData: + .db $04, $12 + +PlayerHeadCollision: + pha ;store metatile number to stack + lda #$11 ;load unbreakable block object state by default + ldx SprDataOffset_Ctrl ;load offset control bit here + ldy PlayerSize ;check player's size + bne DBlockSte ;if small, branch + lda #$12 ;otherwise load breakable block object state +DBlockSte: sta Block_State,x ;store into block object buffer + jsr DestroyBlockMetatile ;store blank metatile in vram buffer to write to name table + ldx SprDataOffset_Ctrl ;load offset control bit + lda $02 ;get vertical high nybble offset used in block buffer routine + sta Block_Orig_YPos,x ;set as vertical coordinate for block object + tay + lda $06 ;get low byte of block buffer address used in same routine + sta Block_BBuf_Low,x ;save as offset here to be used later + lda ($06),y ;get contents of block buffer at old address at $06, $07 + jsr BlockBumpedChk ;do a sub to check which block player bumped head on + sta $00 ;store metatile here + ldy PlayerSize ;check player's size + bne ChkBrick ;if small, use metatile itself as contents of A + tya ;otherwise init A (note: big = 0) +ChkBrick: bcc PutMTileB ;if no match was found in previous sub, skip ahead + ldy #$11 ;otherwise load unbreakable state into block object buffer + sty Block_State,x ;note this applies to both player sizes + lda #$c4 ;load empty block metatile into A for now + ldy $00 ;get metatile from before + cpy #$58 ;is it brick with coins (with line)? + beq StartBTmr ;if so, branch + cpy #$5d ;is it brick with coins (without line)? + bne PutMTileB ;if not, branch ahead to store empty block metatile +StartBTmr: lda BrickCoinTimerFlag ;check brick coin timer flag + bne ContBTmr ;if set, timer expired or counting down, thus branch + lda #$0b + sta BrickCoinTimer ;if not set, set brick coin timer + inc BrickCoinTimerFlag ;and set flag linked to it +ContBTmr: lda BrickCoinTimer ;check brick coin timer + bne PutOldMT ;if not yet expired, branch to use current metatile + ldy #$c4 ;otherwise use empty block metatile +PutOldMT: tya ;put metatile into A +PutMTileB: sta Block_Metatile,x ;store whatever metatile be appropriate here + jsr InitBlock_XY_Pos ;get block object horizontal coordinates saved + ldy $02 ;get vertical high nybble offset + lda #$23 + sta ($06),y ;write blank metatile $23 to block buffer + lda #$10 + sta BlockBounceTimer ;set block bounce timer + pla ;pull original metatile from stack + sta $05 ;and save here + ldy #$00 ;set default offset + lda CrouchingFlag ;is player crouching? + bne SmallBP ;if so, branch to increment offset + lda PlayerSize ;is player big? + beq BigBP ;if so, branch to use default offset +SmallBP: iny ;increment for small or big and crouching +BigBP: lda Player_Y_Position ;get player's vertical coordinate + clc + adc BlockYPosAdderData,y ;add value determined by size + and #$f0 ;mask out low nybble to get 16-pixel correspondence + sta Block_Y_Position,x ;save as vertical coordinate for block object + ldy Block_State,x ;get block object state + cpy #$11 + beq Unbreak ;if set to value loaded for unbreakable, branch + jsr BrickShatter ;execute code for breakable brick + jmp InvOBit ;skip subroutine to do last part of code here +Unbreak: jsr BumpBlock ;execute code for unbreakable brick or question block +InvOBit: lda SprDataOffset_Ctrl ;invert control bit used by block objects + eor #$01 ;and floatey numbers + sta SprDataOffset_Ctrl + rts ;leave! + +;-------------------------------- + +InitBlock_XY_Pos: + lda Player_X_Position ;get player's horizontal coordinate + clc + adc #$08 ;add eight pixels + and #$f0 ;mask out low nybble to give 16-pixel correspondence + sta Block_X_Position,x ;save as horizontal coordinate for block object + lda Player_PageLoc + adc #$00 ;add carry to page location of player + sta Block_PageLoc,x ;save as page location of block object + sta Block_PageLoc2,x ;save elsewhere to be used later + lda Player_Y_HighPos + sta Block_Y_HighPos,x ;save vertical high byte of player into + rts ;vertical high byte of block object and leave + +;-------------------------------- + +BumpBlock: + jsr CheckTopOfBlock ;check to see if there's a coin directly above this block + lda #Sfx_Bump + sta Square1SoundQueue ;play bump sound + lda #$00 + sta Block_X_Speed,x ;initialize horizontal speed for block object + sta Block_Y_MoveForce,x ;init fractional movement force + sta Player_Y_Speed ;init player's vertical speed + lda #$fe + sta Block_Y_Speed,x ;set vertical speed for block object + lda $05 ;get original metatile from stack + jsr BlockBumpedChk ;do a sub to check which block player bumped head on + bcc ExitBlockChk ;if no match was found, branch to leave + tya ;move block number to A + cmp #$09 ;if block number was within 0-8 range, + bcc BlockCode ;branch to use current number + sbc #$05 ;otherwise subtract 5 for second set to get proper number +BlockCode: jsr JumpEngine ;run appropriate subroutine depending on block number + + .dw MushFlowerBlock + .dw CoinBlock + .dw CoinBlock + .dw ExtraLifeMushBlock + .dw MushFlowerBlock + .dw VineBlock + .dw StarBlock + .dw CoinBlock + .dw ExtraLifeMushBlock + +;-------------------------------- + +MushFlowerBlock: + lda #$00 ;load mushroom/fire flower into power-up type + .db $2c ;BIT instruction opcode + +StarBlock: + lda #$02 ;load star into power-up type + .db $2c ;BIT instruction opcode + +ExtraLifeMushBlock: + lda #$03 ;load 1-up mushroom into power-up type + sta $39 ;store correct power-up type + jmp SetupPowerUp + +VineBlock: + ldx #$05 ;load last slot for enemy object buffer + ldy SprDataOffset_Ctrl ;get control bit + jsr Setup_Vine ;set up vine object + +ExitBlockChk: + rts ;leave + +;-------------------------------- + +BrickQBlockMetatiles: + .db $c1, $c0, $5f, $60 ;used by question blocks + + ;these two sets are functionally identical, but look different + .db $55, $56, $57, $58, $59 ;used by ground level types + .db $5a, $5b, $5c, $5d, $5e ;used by other level types + +BlockBumpedChk: + ldy #$0d ;start at end of metatile data +BumpChkLoop: cmp BrickQBlockMetatiles,y ;check to see if current metatile matches + beq MatchBump ;metatile found in block buffer, branch if so + dey ;otherwise move onto next metatile + bpl BumpChkLoop ;do this until all metatiles are checked + clc ;if none match, return with carry clear +MatchBump: rts ;note carry is set if found match + +;-------------------------------- + +BrickShatter: + jsr CheckTopOfBlock ;check to see if there's a coin directly above this block + lda #Sfx_BrickShatter + sta Block_RepFlag,x ;set flag for block object to immediately replace metatile + sta NoiseSoundQueue ;load brick shatter sound + jsr SpawnBrickChunks ;create brick chunk objects + lda #$fe + sta Player_Y_Speed ;set vertical speed for player + lda #$05 + sta DigitModifier+5 ;set digit modifier to give player 50 points + jsr AddToScore ;do sub to update the score + ldx SprDataOffset_Ctrl ;load control bit and leave + rts + +;-------------------------------- + +CheckTopOfBlock: + ldx SprDataOffset_Ctrl ;load control bit + ldy $02 ;get vertical high nybble offset used in block buffer + beq TopEx ;branch to leave if set to zero, because we're at the top + tya ;otherwise set to A + sec + sbc #$10 ;subtract $10 to move up one row in the block buffer + sta $02 ;store as new vertical high nybble offset + tay + lda ($06),y ;get contents of block buffer in same column, one row up + cmp #$c2 ;is it a coin? (not underwater) + bne TopEx ;if not, branch to leave + lda #$00 + sta ($06),y ;otherwise put blank metatile where coin was + jsr RemoveCoin_Axe ;write blank metatile to vram buffer + ldx SprDataOffset_Ctrl ;get control bit + jsr SetupJumpCoin ;create jumping coin object and update coin variables +TopEx: rts ;leave! + +;-------------------------------- + +SpawnBrickChunks: + lda Block_X_Position,x ;set horizontal coordinate of block object + sta Block_Orig_XPos,x ;as original horizontal coordinate here + lda #$f0 + sta Block_X_Speed,x ;set horizontal speed for brick chunk objects + sta Block_X_Speed+2,x + lda #$fa + sta Block_Y_Speed,x ;set vertical speed for one + lda #$fc + sta Block_Y_Speed+2,x ;set lower vertical speed for the other + lda #$00 + sta Block_Y_MoveForce,x ;init fractional movement force for both + sta Block_Y_MoveForce+2,x + lda Block_PageLoc,x + sta Block_PageLoc+2,x ;copy page location + lda Block_X_Position,x + sta Block_X_Position+2,x ;copy horizontal coordinate + lda Block_Y_Position,x + clc ;add 8 pixels to vertical coordinate + adc #$08 ;and save as vertical coordinate for one of them + sta Block_Y_Position+2,x + lda #$fa + sta Block_Y_Speed,x ;set vertical speed...again??? (redundant) + rts + +;------------------------------------------------------------------------------------- + +BlockObjectsCore: + lda Block_State,x ;get state of block object + beq UpdSte ;if not set, branch to leave + and #$0f ;mask out high nybble + pha ;push to stack + tay ;put in Y for now + txa + clc + adc #$09 ;add 9 bytes to offset (note two block objects are created + tax ;when using brick chunks, but only one offset for both) + dey ;decrement Y to check for solid block state + beq BouncingBlockHandler ;branch if found, otherwise continue for brick chunks + jsr ImposeGravityBlock ;do sub to impose gravity on one block object object + jsr MoveObjectHorizontally ;do another sub to move horizontally + txa + clc ;move onto next block object + adc #$02 + tax + jsr ImposeGravityBlock ;do sub to impose gravity on other block object + jsr MoveObjectHorizontally ;do another sub to move horizontally + ldx ObjectOffset ;get block object offset used for both + jsr RelativeBlockPosition ;get relative coordinates + jsr GetBlockOffscreenBits ;get offscreen information + jsr DrawBrickChunks ;draw the brick chunks + pla ;get lower nybble of saved state + ldy Block_Y_HighPos,x ;check vertical high byte of block object + beq UpdSte ;if above the screen, branch to kill it + pha ;otherwise save state back into stack + lda #$f0 + cmp Block_Y_Position+2,x ;check to see if bottom block object went + bcs ChkTop ;to the bottom of the screen, and branch if not + sta Block_Y_Position+2,x ;otherwise set offscreen coordinate +ChkTop: lda Block_Y_Position,x ;get top block object's vertical coordinate + cmp #$f0 ;see if it went to the bottom of the screen + pla ;pull block object state from stack + bcc UpdSte ;if not, branch to save state + bcs KillBlock ;otherwise do unconditional branch to kill it + +BouncingBlockHandler: + jsr ImposeGravityBlock ;do sub to impose gravity on block object + ldx ObjectOffset ;get block object offset + jsr RelativeBlockPosition ;get relative coordinates + jsr GetBlockOffscreenBits ;get offscreen information + jsr DrawBlock ;draw the block + lda Block_Y_Position,x ;get vertical coordinate + and #$0f ;mask out high nybble + cmp #$05 ;check to see if low nybble wrapped around + pla ;pull state from stack + bcs UpdSte ;if still above amount, not time to kill block yet, thus branch + lda #$01 + sta Block_RepFlag,x ;otherwise set flag to replace metatile +KillBlock: lda #$00 ;if branched here, nullify object state +UpdSte: sta Block_State,x ;store contents of A in block object state + rts + +;------------------------------------------------------------------------------------- +;$02 - used to store offset to block buffer +;$06-$07 - used to store block buffer address + +BlockObjMT_Updater: + ldx #$01 ;set offset to start with second block object +UpdateLoop: stx ObjectOffset ;set offset here + lda VRAM_Buffer1 ;if vram buffer already being used here, + bne NextBUpd ;branch to move onto next block object + lda Block_RepFlag,x ;if flag for block object already clear, + beq NextBUpd ;branch to move onto next block object + lda Block_BBuf_Low,x ;get low byte of block buffer + sta $06 ;store into block buffer address + lda #$05 + sta $07 ;set high byte of block buffer address + lda Block_Orig_YPos,x ;get original vertical coordinate of block object + sta $02 ;store here and use as offset to block buffer + tay + lda Block_Metatile,x ;get metatile to be written + sta ($06),y ;write it to the block buffer + jsr ReplaceBlockMetatile ;do sub to replace metatile where block object is + lda #$00 + sta Block_RepFlag,x ;clear block object flag +NextBUpd: dex ;decrement block object offset + bpl UpdateLoop ;do this until both block objects are dealt with + rts ;then leave + +;------------------------------------------------------------------------------------- +;$00 - used to store high nybble of horizontal speed as adder +;$01 - used to store low nybble of horizontal speed +;$02 - used to store adder to page location + +MoveEnemyHorizontally: + inx ;increment offset for enemy offset + jsr MoveObjectHorizontally ;position object horizontally according to + ldx ObjectOffset ;counters, return with saved value in A, + rts ;put enemy offset back in X and leave + +MovePlayerHorizontally: + lda JumpspringAnimCtrl ;if jumpspring currently animating, + bne ExXMove ;branch to leave + tax ;otherwise set zero for offset to use player's stuff + +MoveObjectHorizontally: + lda SprObject_X_Speed,x ;get currently saved value (horizontal + asl ;speed, secondary counter, whatever) + asl ;and move low nybble to high + asl + asl + sta $01 ;store result here + lda SprObject_X_Speed,x ;get saved value again + lsr ;move high nybble to low + lsr + lsr + lsr + cmp #$08 ;if < 8, branch, do not change + bcc SaveXSpd + ora #%11110000 ;otherwise alter high nybble +SaveXSpd: sta $00 ;save result here + ldy #$00 ;load default Y value here + cmp #$00 ;if result positive, leave Y alone + bpl UseAdder + dey ;otherwise decrement Y +UseAdder: sty $02 ;save Y here + lda SprObject_X_MoveForce,x ;get whatever number's here + clc + adc $01 ;add low nybble moved to high + sta SprObject_X_MoveForce,x ;store result here + lda #$00 ;init A + rol ;rotate carry into d0 + pha ;push onto stack + ror ;rotate d0 back onto carry + lda SprObject_X_Position,x + adc $00 ;add carry plus saved value (high nybble moved to low + sta SprObject_X_Position,x ;plus $f0 if necessary) to object's horizontal position + lda SprObject_PageLoc,x + adc $02 ;add carry plus other saved value to the + sta SprObject_PageLoc,x ;object's page location and save + pla + clc ;pull old carry from stack and add + adc $00 ;to high nybble moved to low +ExXMove: rts ;and leave + +;------------------------------------------------------------------------------------- +;$00 - used for downward force +;$01 - used for upward force +;$02 - used for maximum vertical speed + +MovePlayerVertically: + ldx #$00 ;set X for player offset + lda TimerControl + bne NoJSChk ;if master timer control set, branch ahead + lda JumpspringAnimCtrl ;otherwise check to see if jumpspring is animating + bne ExXMove ;branch to leave if so +NoJSChk: lda VerticalForce ;dump vertical force + sta $00 + lda #$04 ;set maximum vertical speed here + jmp ImposeGravitySprObj ;then jump to move player vertically + +;-------------------------------- + +MoveD_EnemyVertically: + ldy #$3d ;set quick movement amount downwards + lda Enemy_State,x ;then check enemy state + cmp #$05 ;if not set to unique state for spiny's egg, go ahead + bne ContVMove ;and use, otherwise set different movement amount, continue on + +MoveFallingPlatform: + ldy #$20 ;set movement amount +ContVMove: jmp SetHiMax ;jump to skip the rest of this + +;-------------------------------- + +MoveRedPTroopaDown: + ldy #$00 ;set Y to move downwards + jmp MoveRedPTroopa ;skip to movement routine + +MoveRedPTroopaUp: + ldy #$01 ;set Y to move upwards + +MoveRedPTroopa: + inx ;increment X for enemy offset + lda #$03 + sta $00 ;set downward movement amount here + lda #$06 + sta $01 ;set upward movement amount here + lda #$02 + sta $02 ;set maximum speed here + tya ;set movement direction in A, and + jmp RedPTroopaGrav ;jump to move this thing + +;-------------------------------- + +MoveDropPlatform: + ldy #$7f ;set movement amount for drop platform + bne SetMdMax ;skip ahead of other value set here + +MoveEnemySlowVert: + ldy #$0f ;set movement amount for bowser/other objects +SetMdMax: lda #$02 ;set maximum speed in A + bne SetXMoveAmt ;unconditional branch + +;-------------------------------- + +MoveJ_EnemyVertically: + ldy #$1c ;set movement amount for podoboo/other objects +SetHiMax: lda #$03 ;set maximum speed in A +SetXMoveAmt: sty $00 ;set movement amount here + inx ;increment X for enemy offset + jsr ImposeGravitySprObj ;do a sub to move enemy object downwards + ldx ObjectOffset ;get enemy object buffer offset and leave + rts + +;-------------------------------- + +MaxSpdBlockData: + .db $06, $08 + +ResidualGravityCode: + ldy #$00 ;this part appears to be residual, + .db $2c ;no code branches or jumps to it... + +ImposeGravityBlock: + ldy #$01 ;set offset for maximum speed + lda #$50 ;set movement amount here + sta $00 + lda MaxSpdBlockData,y ;get maximum speed + +ImposeGravitySprObj: + sta $02 ;set maximum speed here + lda #$00 ;set value to move downwards + jmp ImposeGravity ;jump to the code that actually moves it + +;-------------------------------- + +MovePlatformDown: + lda #$00 ;save value to stack (if branching here, execute next + .db $2c ;part as BIT instruction) + +MovePlatformUp: + lda #$01 ;save value to stack + pha + ldy Enemy_ID,x ;get enemy object identifier + inx ;increment offset for enemy object + lda #$05 ;load default value here + cpy #$29 ;residual comparison, object #29 never executes + bne SetDplSpd ;this code, thus unconditional branch here + lda #$09 ;residual code +SetDplSpd: sta $00 ;save downward movement amount here + lda #$0a ;save upward movement amount here + sta $01 + lda #$03 ;save maximum vertical speed here + sta $02 + pla ;get value from stack + tay ;use as Y, then move onto code shared by red koopa + +RedPTroopaGrav: + jsr ImposeGravity ;do a sub to move object gradually + ldx ObjectOffset ;get enemy object offset and leave + rts + +;------------------------------------------------------------------------------------- +;$00 - used for downward force +;$01 - used for upward force +;$07 - used as adder for vertical position + +ImposeGravity: + pha ;push value to stack + lda SprObject_YMF_Dummy,x + clc ;add value in movement force to contents of dummy variable + adc SprObject_Y_MoveForce,x + sta SprObject_YMF_Dummy,x + ldy #$00 ;set Y to zero by default + lda SprObject_Y_Speed,x ;get current vertical speed + bpl AlterYP ;if currently moving downwards, do not decrement Y + dey ;otherwise decrement Y +AlterYP: sty $07 ;store Y here + adc SprObject_Y_Position,x ;add vertical position to vertical speed plus carry + sta SprObject_Y_Position,x ;store as new vertical position + lda SprObject_Y_HighPos,x + adc $07 ;add carry plus contents of $07 to vertical high byte + sta SprObject_Y_HighPos,x ;store as new vertical high byte + lda SprObject_Y_MoveForce,x + clc + adc $00 ;add downward movement amount to contents of $0433 + sta SprObject_Y_MoveForce,x + lda SprObject_Y_Speed,x ;add carry to vertical speed and store + adc #$00 + sta SprObject_Y_Speed,x + cmp $02 ;compare to maximum speed + bmi ChkUpM ;if less than preset value, skip this part + lda SprObject_Y_MoveForce,x + cmp #$80 ;if less positively than preset maximum, skip this part + bcc ChkUpM + lda $02 + sta SprObject_Y_Speed,x ;keep vertical speed within maximum value + lda #$00 + sta SprObject_Y_MoveForce,x ;clear fractional +ChkUpM: pla ;get value from stack + beq ExVMove ;if set to zero, branch to leave + lda $02 + eor #%11111111 ;otherwise get two's compliment of maximum speed + tay + iny + sty $07 ;store two's compliment here + lda SprObject_Y_MoveForce,x + sec ;subtract upward movement amount from contents + sbc $01 ;of movement force, note that $01 is twice as large as $00, + sta SprObject_Y_MoveForce,x ;thus it effectively undoes add we did earlier + lda SprObject_Y_Speed,x + sbc #$00 ;subtract borrow from vertical speed and store + sta SprObject_Y_Speed,x + cmp $07 ;compare vertical speed to two's compliment + bpl ExVMove ;if less negatively than preset maximum, skip this part + lda SprObject_Y_MoveForce,x + cmp #$80 ;check if fractional part is above certain amount, + bcs ExVMove ;and if so, branch to leave + lda $07 + sta SprObject_Y_Speed,x ;keep vertical speed within maximum value + lda #$ff + sta SprObject_Y_MoveForce,x ;clear fractional +ExVMove: rts ;leave! + +;------------------------------------------------------------------------------------- + +EnemiesAndLoopsCore: + lda Enemy_Flag,x ;check data here for MSB set + pha ;save in stack + asl + bcs ChkBowserF ;if MSB set in enemy flag, branch ahead of jumps + pla ;get from stack + beq ChkAreaTsk ;if data zero, branch + jmp RunEnemyObjectsCore ;otherwise, jump to run enemy subroutines +ChkAreaTsk: lda AreaParserTaskNum ;check number of tasks to perform + and #$07 + cmp #$07 ;if at a specific task, jump and leave + beq ExitELCore + jmp ProcLoopCommand ;otherwise, jump to process loop command/load enemies +ChkBowserF: pla ;get data from stack + and #%00001111 ;mask out high nybble + tay + lda Enemy_Flag,y ;use as pointer and load same place with different offset + bne ExitELCore + sta Enemy_Flag,x ;if second enemy flag not set, also clear first one +ExitELCore: rts + +;-------------------------------- + +;loop command data +LoopCmdWorldNumber: + .db $03, $03, $06, $06, $06, $06, $06, $06, $07, $07, $07 + +LoopCmdPageNumber: + .db $05, $09, $04, $05, $06, $08, $09, $0a, $06, $0b, $10 + +LoopCmdYPosition: + .db $40, $b0, $b0, $80, $40, $40, $80, $40, $f0, $f0, $f0 + +ExecGameLoopback: + lda Player_PageLoc ;send player back four pages + sec + sbc #$04 + sta Player_PageLoc + lda CurrentPageLoc ;send current page back four pages + sec + sbc #$04 + sta CurrentPageLoc + lda ScreenLeft_PageLoc ;subtract four from page location + sec ;of screen's left border + sbc #$04 + sta ScreenLeft_PageLoc + lda ScreenRight_PageLoc ;do the same for the page location + sec ;of screen's right border + sbc #$04 + sta ScreenRight_PageLoc + lda AreaObjectPageLoc ;subtract four from page control + sec ;for area objects + sbc #$04 + sta AreaObjectPageLoc + lda #$00 ;initialize page select for both + sta EnemyObjectPageSel ;area and enemy objects + sta AreaObjectPageSel + sta EnemyDataOffset ;initialize enemy object data offset + sta EnemyObjectPageLoc ;and enemy object page control + lda AreaDataOfsLoopback,y ;adjust area object offset based on + sta AreaDataOffset ;which loop command we encountered + rts + +ProcLoopCommand: + lda LoopCommand ;check if loop command was found + beq ChkEnemyFrenzy + lda CurrentColumnPos ;check to see if we're still on the first page + bne ChkEnemyFrenzy ;if not, do not loop yet + ldy #$0b ;start at the end of each set of loop data +FindLoop: dey + bmi ChkEnemyFrenzy ;if all data is checked and not match, do not loop + lda WorldNumber ;check to see if one of the world numbers + cmp LoopCmdWorldNumber,y ;matches our current world number + bne FindLoop + lda CurrentPageLoc ;check to see if one of the page numbers + cmp LoopCmdPageNumber,y ;matches the page we're currently on + bne FindLoop + lda Player_Y_Position ;check to see if the player is at the correct position + cmp LoopCmdYPosition,y ;if not, branch to check for world 7 + bne WrongChk + lda Player_State ;check to see if the player is + cmp #$00 ;on solid ground (i.e. not jumping or falling) + bne WrongChk ;if not, player fails to pass loop, and loopback + lda WorldNumber ;are we in world 7? (check performed on correct + cmp #World7 ;vertical position and on solid ground) + bne InitMLp ;if not, initialize flags used there, otherwise + inc MultiLoopCorrectCntr ;increment counter for correct progression +IncMLoop: inc MultiLoopPassCntr ;increment master multi-part counter + lda MultiLoopPassCntr ;have we done all three parts? + cmp #$03 + bne InitLCmd ;if not, skip this part + lda MultiLoopCorrectCntr ;if so, have we done them all correctly? + cmp #$03 + beq InitMLp ;if so, branch past unnecessary check here + bne DoLpBack ;unconditional branch if previous branch fails +WrongChk: lda WorldNumber ;are we in world 7? (check performed on + cmp #World7 ;incorrect vertical position or not on solid ground) + beq IncMLoop +DoLpBack: jsr ExecGameLoopback ;if player is not in right place, loop back + jsr KillAllEnemies +InitMLp: lda #$00 ;initialize counters used for multi-part loop commands + sta MultiLoopPassCntr + sta MultiLoopCorrectCntr +InitLCmd: lda #$00 ;initialize loop command flag + sta LoopCommand + +;-------------------------------- + +ChkEnemyFrenzy: + lda EnemyFrenzyQueue ;check for enemy object in frenzy queue + beq ProcessEnemyData ;if not, skip this part + sta Enemy_ID,x ;store as enemy object identifier here + lda #$01 + sta Enemy_Flag,x ;activate enemy object flag + lda #$00 + sta Enemy_State,x ;initialize state and frenzy queue + sta EnemyFrenzyQueue + jmp InitEnemyObject ;and then jump to deal with this enemy + +;-------------------------------- +;$06 - used to hold page location of extended right boundary +;$07 - used to hold high nybble of position of extended right boundary + +ProcessEnemyData: + ldy EnemyDataOffset ;get offset of enemy object data + lda (EnemyData),y ;load first byte + cmp #$ff ;check for EOD terminator + bne CheckEndofBuffer + jmp CheckFrenzyBuffer ;if found, jump to check frenzy buffer, otherwise + +CheckEndofBuffer: + and #%00001111 ;check for special row $0e + cmp #$0e + beq CheckRightBounds ;if found, branch, otherwise + cpx #$05 ;check for end of buffer + bcc CheckRightBounds ;if not at end of buffer, branch + iny + lda (EnemyData),y ;check for specific value here + and #%00111111 ;not sure what this was intended for, exactly + cmp #$2e ;this part is quite possibly residual code + beq CheckRightBounds ;but it has the effect of keeping enemies out of + rts ;the sixth slot + +CheckRightBounds: + lda ScreenRight_X_Pos ;add 48 to pixel coordinate of right boundary + clc + adc #$30 + and #%11110000 ;store high nybble + sta $07 + lda ScreenRight_PageLoc ;add carry to page location of right boundary + adc #$00 + sta $06 ;store page location + carry + ldy EnemyDataOffset + iny + lda (EnemyData),y ;if MSB of enemy object is clear, branch to check for row $0f + asl + bcc CheckPageCtrlRow + lda EnemyObjectPageSel ;if page select already set, do not set again + bne CheckPageCtrlRow + inc EnemyObjectPageSel ;otherwise, if MSB is set, set page select + inc EnemyObjectPageLoc ;and increment page control + +CheckPageCtrlRow: + dey + lda (EnemyData),y ;reread first byte + and #$0f + cmp #$0f ;check for special row $0f + bne PositionEnemyObj ;if not found, branch to position enemy object + lda EnemyObjectPageSel ;if page select set, + bne PositionEnemyObj ;branch without reading second byte + iny + lda (EnemyData),y ;otherwise, get second byte, mask out 2 MSB + and #%00111111 + sta EnemyObjectPageLoc ;store as page control for enemy object data + inc EnemyDataOffset ;increment enemy object data offset 2 bytes + inc EnemyDataOffset + inc EnemyObjectPageSel ;set page select for enemy object data and + jmp ProcLoopCommand ;jump back to process loop commands again + +PositionEnemyObj: + lda EnemyObjectPageLoc ;store page control as page location + sta Enemy_PageLoc,x ;for enemy object + lda (EnemyData),y ;get first byte of enemy object + and #%11110000 + sta Enemy_X_Position,x ;store column position + cmp ScreenRight_X_Pos ;check column position against right boundary + lda Enemy_PageLoc,x ;without subtracting, then subtract borrow + sbc ScreenRight_PageLoc ;from page location + bcs CheckRightExtBounds ;if enemy object beyond or at boundary, branch + lda (EnemyData),y + and #%00001111 ;check for special row $0e + cmp #$0e ;if found, jump elsewhere + beq ParseRow0e + jmp CheckThreeBytes ;if not found, unconditional jump + +CheckRightExtBounds: + lda $07 ;check right boundary + 48 against + cmp Enemy_X_Position,x ;column position without subtracting, + lda $06 ;then subtract borrow from page control temp + sbc Enemy_PageLoc,x ;plus carry + bcc CheckFrenzyBuffer ;if enemy object beyond extended boundary, branch + lda #$01 ;store value in vertical high byte + sta Enemy_Y_HighPos,x + lda (EnemyData),y ;get first byte again + asl ;multiply by four to get the vertical + asl ;coordinate + asl + asl + sta Enemy_Y_Position,x + cmp #$e0 ;do one last check for special row $0e + beq ParseRow0e ;(necessary if branched to $c1cb) + iny + lda (EnemyData),y ;get second byte of object + and #%01000000 ;check to see if hard mode bit is set + beq CheckForEnemyGroup ;if not, branch to check for group enemy objects + lda SecondaryHardMode ;if set, check to see if secondary hard mode flag + beq Inc2B ;is on, and if not, branch to skip this object completely + +CheckForEnemyGroup: + lda (EnemyData),y ;get second byte and mask out 2 MSB + and #%00111111 + cmp #$37 ;check for value below $37 + bcc BuzzyBeetleMutate + cmp #$3f ;if $37 or greater, check for value + bcc DoGroup ;below $3f, branch if below $3f + +BuzzyBeetleMutate: + cmp #Goomba ;if below $37, check for goomba + bne StrID ;value ($3f or more always fails) + ldy PrimaryHardMode ;check if primary hard mode flag is set + beq StrID ;and if so, change goomba to buzzy beetle + lda #BuzzyBeetle +StrID: sta Enemy_ID,x ;store enemy object number into buffer + lda #$01 + sta Enemy_Flag,x ;set flag for enemy in buffer + jsr InitEnemyObject + lda Enemy_Flag,x ;check to see if flag is set + bne Inc2B ;if not, leave, otherwise branch + rts + +CheckFrenzyBuffer: + lda EnemyFrenzyBuffer ;if enemy object stored in frenzy buffer + bne StrFre ;then branch ahead to store in enemy object buffer + lda VineFlagOffset ;otherwise check vine flag offset + cmp #$01 + bne ExEPar ;if other value <> 1, leave + lda #VineObject ;otherwise put vine in enemy identifier +StrFre: sta Enemy_ID,x ;store contents of frenzy buffer into enemy identifier value + +InitEnemyObject: + lda #$00 ;initialize enemy state + sta Enemy_State,x + jsr CheckpointEnemyID ;jump ahead to run jump engine and subroutines +ExEPar: rts ;then leave + +DoGroup: + jmp HandleGroupEnemies ;handle enemy group objects + +ParseRow0e: + iny ;increment Y to load third byte of object + iny + lda (EnemyData),y + lsr ;move 3 MSB to the bottom, effectively + lsr ;making %xxx00000 into %00000xxx + lsr + lsr + lsr + cmp WorldNumber ;is it the same world number as we're on? + bne NotUse ;if not, do not use (this allows multiple uses + dey ;of the same area, like the underground bonus areas) + lda (EnemyData),y ;otherwise, get second byte and use as offset + sta AreaPointer ;to addresses for level and enemy object data + iny + lda (EnemyData),y ;get third byte again, and this time mask out + and #%00011111 ;the 3 MSB from before, save as page number to be + sta EntrancePage ;used upon entry to area, if area is entered +NotUse: jmp Inc3B + +CheckThreeBytes: + ldy EnemyDataOffset ;load current offset for enemy object data + lda (EnemyData),y ;get first byte + and #%00001111 ;check for special row $0e + cmp #$0e + bne Inc2B +Inc3B: inc EnemyDataOffset ;if row = $0e, increment three bytes +Inc2B: inc EnemyDataOffset ;otherwise increment two bytes + inc EnemyDataOffset + lda #$00 ;init page select for enemy objects + sta EnemyObjectPageSel + ldx ObjectOffset ;reload current offset in enemy buffers + rts ;and leave + +CheckpointEnemyID: + lda Enemy_ID,x + cmp #$15 ;check enemy object identifier for $15 or greater + bcs InitEnemyRoutines ;and branch straight to the jump engine if found + tay ;save identifier in Y register for now + lda Enemy_Y_Position,x + adc #$08 ;add eight pixels to what will eventually be the + sta Enemy_Y_Position,x ;enemy object's vertical coordinate ($00-$14 only) + lda #$01 + sta EnemyOffscrBitsMasked,x ;set offscreen masked bit + tya ;get identifier back and use as offset for jump engine + +InitEnemyRoutines: + jsr JumpEngine + +;jump engine table for newly loaded enemy objects + + .dw InitNormalEnemy ;for objects $00-$0f + .dw InitNormalEnemy + .dw InitNormalEnemy + .dw InitRedKoopa + .dw NoInitCode + .dw InitHammerBro + .dw InitGoomba + .dw InitBloober + .dw InitBulletBill + .dw NoInitCode + .dw InitCheepCheep + .dw InitCheepCheep + .dw InitPodoboo + .dw InitPiranhaPlant + .dw InitJumpGPTroopa + .dw InitRedPTroopa + + .dw InitHorizFlySwimEnemy ;for objects $10-$1f + .dw InitLakitu + .dw InitEnemyFrenzy + .dw NoInitCode + .dw InitEnemyFrenzy + .dw InitEnemyFrenzy + .dw InitEnemyFrenzy + .dw InitEnemyFrenzy + .dw EndFrenzy + .dw NoInitCode + .dw NoInitCode + .dw InitShortFirebar + .dw InitShortFirebar + .dw InitShortFirebar + .dw InitShortFirebar + .dw InitLongFirebar + + .dw NoInitCode ;for objects $20-$2f + .dw NoInitCode + .dw NoInitCode + .dw NoInitCode + .dw InitBalPlatform + .dw InitVertPlatform + .dw LargeLiftUp + .dw LargeLiftDown + .dw InitHoriPlatform + .dw InitDropPlatform + .dw InitHoriPlatform + .dw PlatLiftUp + .dw PlatLiftDown + .dw InitBowser + .dw PwrUpJmp ;possibly dummy value + .dw Setup_Vine + + .dw NoInitCode ;for objects $30-$36 + .dw NoInitCode + .dw NoInitCode + .dw NoInitCode + .dw NoInitCode + .dw InitRetainerObj + .dw EndOfEnemyInitCode + +;------------------------------------------------------------------------------------- + +NoInitCode: + rts ;this executed when enemy object has no init code + +;-------------------------------- + +InitGoomba: + jsr InitNormalEnemy ;set appropriate horizontal speed + jmp SmallBBox ;set $09 as bounding box control, set other values + +;-------------------------------- + +InitPodoboo: + lda #$02 ;set enemy position to below + sta Enemy_Y_HighPos,x ;the bottom of the screen + sta Enemy_Y_Position,x + lsr + sta EnemyIntervalTimer,x ;set timer for enemy + lsr + sta Enemy_State,x ;initialize enemy state, then jump to use + jmp SmallBBox ;$09 as bounding box size and set other things + +;-------------------------------- + +InitRetainerObj: + lda #$b8 ;set fixed vertical position for + sta Enemy_Y_Position,x ;princess/mushroom retainer object + rts + +;-------------------------------- + +NormalXSpdData: + .db $f8, $f4 + +InitNormalEnemy: + ldy #$01 ;load offset of 1 by default + lda PrimaryHardMode ;check for primary hard mode flag set + bne GetESpd + dey ;if not set, decrement offset +GetESpd: lda NormalXSpdData,y ;get appropriate horizontal speed +SetESpd: sta Enemy_X_Speed,x ;store as speed for enemy object + jmp TallBBox ;branch to set bounding box control and other data + +;-------------------------------- + +InitRedKoopa: + jsr InitNormalEnemy ;load appropriate horizontal speed + lda #$01 ;set enemy state for red koopa troopa $03 + sta Enemy_State,x + rts + +;-------------------------------- + +HBroWalkingTimerData: + .db $80, $50 + +InitHammerBro: + lda #$00 ;init horizontal speed and timer used by hammer bro + sta HammerThrowingTimer,x ;apparently to time hammer throwing + sta Enemy_X_Speed,x + ldy SecondaryHardMode ;get secondary hard mode flag + lda HBroWalkingTimerData,y + sta EnemyIntervalTimer,x ;set value as delay for hammer bro to walk left + lda #$0b ;set specific value for bounding box size control + jmp SetBBox + +;-------------------------------- + +InitHorizFlySwimEnemy: + lda #$00 ;initialize horizontal speed + jmp SetESpd + +;-------------------------------- + +InitBloober: + lda #$00 ;initialize horizontal speed + sta BlooperMoveSpeed,x +SmallBBox: lda #$09 ;set specific bounding box size control + bne SetBBox ;unconditional branch + +;-------------------------------- + +InitRedPTroopa: + ldy #$30 ;load central position adder for 48 pixels down + lda Enemy_Y_Position,x ;set vertical coordinate into location to + sta RedPTroopaOrigXPos,x ;be used as original vertical coordinate + bpl GetCent ;if vertical coordinate < $80 + ldy #$e0 ;if => $80, load position adder for 32 pixels up +GetCent: tya ;send central position adder to A + adc Enemy_Y_Position,x ;add to current vertical coordinate + sta RedPTroopaCenterYPos,x ;store as central vertical coordinate +TallBBox: lda #$03 ;set specific bounding box size control +SetBBox: sta Enemy_BoundBoxCtrl,x ;set bounding box control here + lda #$02 ;set moving direction for left + sta Enemy_MovingDir,x +InitVStf: lda #$00 ;initialize vertical speed + sta Enemy_Y_Speed,x ;and movement force + sta Enemy_Y_MoveForce,x + rts + +;-------------------------------- + +InitBulletBill: + lda #$02 ;set moving direction for left + sta Enemy_MovingDir,x + lda #$09 ;set bounding box control for $09 + sta Enemy_BoundBoxCtrl,x + rts + +;-------------------------------- + +InitCheepCheep: + jsr SmallBBox ;set vertical bounding box, speed, init others + lda PseudoRandomBitReg,x ;check one portion of LSFR + and #%00010000 ;get d4 from it + sta CheepCheepMoveMFlag,x ;save as movement flag of some sort + lda Enemy_Y_Position,x + sta CheepCheepOrigYPos,x ;save original vertical coordinate here + rts + +;-------------------------------- + +InitLakitu: + lda EnemyFrenzyBuffer ;check to see if an enemy is already in + bne KillLakitu ;the frenzy buffer, and branch to kill lakitu if so + +SetupLakitu: + lda #$00 ;erase counter for lakitu's reappearance + sta LakituReappearTimer + jsr InitHorizFlySwimEnemy ;set $03 as bounding box, set other attributes + jmp TallBBox2 ;set $03 as bounding box again (not necessary) and leave + +KillLakitu: + jmp EraseEnemyObject + +;-------------------------------- +;$01-$03 - used to hold pseudorandom difference adjusters + +PRDiffAdjustData: + .db $26, $2c, $32, $38 + .db $20, $22, $24, $26 + .db $13, $14, $15, $16 + +LakituAndSpinyHandler: + lda FrenzyEnemyTimer ;if timer here not expired, leave + bne ExLSHand + cpx #$05 ;if we are on the special use slot, leave + bcs ExLSHand + lda #$80 ;set timer + sta FrenzyEnemyTimer + ldy #$04 ;start with the last enemy slot +ChkLak: lda Enemy_ID,y ;check all enemy slots to see + cmp #Lakitu ;if lakitu is on one of them + beq CreateSpiny ;if so, branch out of this loop + dey ;otherwise check another slot + bpl ChkLak ;loop until all slots are checked + inc LakituReappearTimer ;increment reappearance timer + lda LakituReappearTimer + cmp #$07 ;check to see if we're up to a certain value yet + bcc ExLSHand ;if not, leave + ldx #$04 ;start with the last enemy slot again +ChkNoEn: lda Enemy_Flag,x ;check enemy buffer flag for non-active enemy slot + beq CreateL ;branch out of loop if found + dex ;otherwise check next slot + bpl ChkNoEn ;branch until all slots are checked + bmi RetEOfs ;if no empty slots were found, branch to leave +CreateL: lda #$00 ;initialize enemy state + sta Enemy_State,x + lda #Lakitu ;create lakitu enemy object + sta Enemy_ID,x + jsr SetupLakitu ;do a sub to set up lakitu + lda #$20 + jsr PutAtRightExtent ;finish setting up lakitu +RetEOfs: ldx ObjectOffset ;get enemy object buffer offset again and leave +ExLSHand: rts + +;-------------------------------- + +CreateSpiny: + lda Player_Y_Position ;if player above a certain point, branch to leave + cmp #$2c + bcc ExLSHand + lda Enemy_State,y ;if lakitu is not in normal state, branch to leave + bne ExLSHand + lda Enemy_PageLoc,y ;store horizontal coordinates (high and low) of lakitu + sta Enemy_PageLoc,x ;into the coordinates of the spiny we're going to create + lda Enemy_X_Position,y + sta Enemy_X_Position,x + lda #$01 ;put spiny within vertical screen unit + sta Enemy_Y_HighPos,x + lda Enemy_Y_Position,y ;put spiny eight pixels above where lakitu is + sec + sbc #$08 + sta Enemy_Y_Position,x + lda PseudoRandomBitReg,x ;get 2 LSB of LSFR and save to Y + and #%00000011 + tay + ldx #$02 +DifLoop: lda PRDiffAdjustData,y ;get three values and save them + sta $01,x ;to $01-$03 + iny + iny ;increment Y four bytes for each value + iny + iny + dex ;decrement X for each one + bpl DifLoop ;loop until all three are written + ldx ObjectOffset ;get enemy object buffer offset + jsr PlayerLakituDiff ;move enemy, change direction, get value - difference + ldy Player_X_Speed ;check player's horizontal speed + cpy #$08 + bcs SetSpSpd ;if moving faster than a certain amount, branch elsewhere + tay ;otherwise save value in A to Y for now + lda PseudoRandomBitReg+1,x + and #%00000011 ;get one of the LSFR parts and save the 2 LSB + beq UsePosv ;branch if neither bits are set + tya + eor #%11111111 ;otherwise get two's compliment of Y + tay + iny +UsePosv: tya ;put value from A in Y back to A (they will be lost anyway) +SetSpSpd: jsr SmallBBox ;set bounding box control, init attributes, lose contents of A + ldy #$02 + sta Enemy_X_Speed,x ;set horizontal speed to zero because previous contents + cmp #$00 ;of A were lost...branch here will never be taken for + bmi SpinyRte ;the same reason + dey +SpinyRte: sty Enemy_MovingDir,x ;set moving direction to the right + lda #$fd + sta Enemy_Y_Speed,x ;set vertical speed to move upwards + lda #$01 + sta Enemy_Flag,x ;enable enemy object by setting flag + lda #$05 + sta Enemy_State,x ;put spiny in egg state and leave +ChpChpEx: rts + +;-------------------------------- + +FirebarSpinSpdData: + .db $28, $38, $28, $38, $28 + +FirebarSpinDirData: + .db $00, $00, $10, $10, $00 + +InitLongFirebar: + jsr DuplicateEnemyObj ;create enemy object for long firebar + +InitShortFirebar: + lda #$00 ;initialize low byte of spin state + sta FirebarSpinState_Low,x + lda Enemy_ID,x ;subtract $1b from enemy identifier + sec ;to get proper offset for firebar data + sbc #$1b + tay + lda FirebarSpinSpdData,y ;get spinning speed of firebar + sta FirebarSpinSpeed,x + lda FirebarSpinDirData,y ;get spinning direction of firebar + sta FirebarSpinDirection,x + lda Enemy_Y_Position,x + clc ;add four pixels to vertical coordinate + adc #$04 + sta Enemy_Y_Position,x + lda Enemy_X_Position,x + clc ;add four pixels to horizontal coordinate + adc #$04 + sta Enemy_X_Position,x + lda Enemy_PageLoc,x + adc #$00 ;add carry to page location + sta Enemy_PageLoc,x + jmp TallBBox2 ;set bounding box control (not used) and leave + +;-------------------------------- +;$00-$01 - used to hold pseudorandom bits + +FlyCCXPositionData: + .db $80, $30, $40, $80 + .db $30, $50, $50, $70 + .db $20, $40, $80, $a0 + .db $70, $40, $90, $68 + +FlyCCXSpeedData: + .db $0e, $05, $06, $0e + .db $1c, $20, $10, $0c + .db $1e, $22, $18, $14 + +FlyCCTimerData: + .db $10, $60, $20, $48 + +InitFlyingCheepCheep: + lda FrenzyEnemyTimer ;if timer here not expired yet, branch to leave + bne ChpChpEx + jsr SmallBBox ;jump to set bounding box size $09 and init other values + lda PseudoRandomBitReg+1,x + and #%00000011 ;set pseudorandom offset here + tay + lda FlyCCTimerData,y ;load timer with pseudorandom offset + sta FrenzyEnemyTimer + ldy #$03 ;load Y with default value + lda SecondaryHardMode + beq MaxCC ;if secondary hard mode flag not set, do not increment Y + iny ;otherwise, increment Y to allow as many as four onscreen +MaxCC: sty $00 ;store whatever pseudorandom bits are in Y + cpx $00 ;compare enemy object buffer offset with Y + bcs ChpChpEx ;if X => Y, branch to leave + lda PseudoRandomBitReg,x + and #%00000011 ;get last two bits of LSFR, first part + sta $00 ;and store in two places + sta $01 + lda #$fb ;set vertical speed for cheep-cheep + sta Enemy_Y_Speed,x + lda #$00 ;load default value + ldy Player_X_Speed ;check player's horizontal speed + beq GSeed ;if player not moving left or right, skip this part + lda #$04 + cpy #$19 ;if moving to the right but not very quickly, + bcc GSeed ;do not change A + asl ;otherwise, multiply A by 2 +GSeed: pha ;save to stack + clc + adc $00 ;add to last two bits of LSFR we saved earlier + sta $00 ;save it there + lda PseudoRandomBitReg+1,x + and #%00000011 ;if neither of the last two bits of second LSFR set, + beq RSeed ;skip this part and save contents of $00 + lda PseudoRandomBitReg+2,x + and #%00001111 ;otherwise overwrite with lower nybble of + sta $00 ;third LSFR part +RSeed: pla ;get value from stack we saved earlier + clc + adc $01 ;add to last two bits of LSFR we saved in other place + tay ;use as pseudorandom offset here + lda FlyCCXSpeedData,y ;get horizontal speed using pseudorandom offset + sta Enemy_X_Speed,x + lda #$01 ;set to move towards the right + sta Enemy_MovingDir,x + lda Player_X_Speed ;if player moving left or right, branch ahead of this part + bne D2XPos1 + ldy $00 ;get first LSFR or third LSFR lower nybble + tya ;and check for d1 set + and #%00000010 + beq D2XPos1 ;if d1 not set, branch + lda Enemy_X_Speed,x + eor #$ff ;if d1 set, change horizontal speed + clc ;into two's compliment, thus moving in the opposite + adc #$01 ;direction + sta Enemy_X_Speed,x + inc Enemy_MovingDir,x ;increment to move towards the left +D2XPos1: tya ;get first LSFR or third LSFR lower nybble again + and #%00000010 + beq D2XPos2 ;check for d1 set again, branch again if not set + lda Player_X_Position ;get player's horizontal position + clc + adc FlyCCXPositionData,y ;if d1 set, add value obtained from pseudorandom offset + sta Enemy_X_Position,x ;and save as enemy's horizontal position + lda Player_PageLoc ;get player's page location + adc #$00 ;add carry and jump past this part + jmp FinCCSt +D2XPos2: lda Player_X_Position ;get player's horizontal position + sec + sbc FlyCCXPositionData,y ;if d1 not set, subtract value obtained from pseudorandom + sta Enemy_X_Position,x ;offset and save as enemy's horizontal position + lda Player_PageLoc ;get player's page location + sbc #$00 ;subtract borrow +FinCCSt: sta Enemy_PageLoc,x ;save as enemy's page location + lda #$01 + sta Enemy_Flag,x ;set enemy's buffer flag + sta Enemy_Y_HighPos,x ;set enemy's high vertical byte + lda #$f8 + sta Enemy_Y_Position,x ;put enemy below the screen, and we are done + rts + +;-------------------------------- + +InitBowser: + jsr DuplicateEnemyObj ;jump to create another bowser object + stx BowserFront_Offset ;save offset of first here + lda #$00 + sta BowserBodyControls ;initialize bowser's body controls + sta BridgeCollapseOffset ;and bridge collapse offset + lda Enemy_X_Position,x + sta BowserOrigXPos ;store original horizontal position here + lda #$df + sta BowserFireBreathTimer ;store something here + sta Enemy_MovingDir,x ;and in moving direction + lda #$20 + sta BowserFeetCounter ;set bowser's feet timer and in enemy timer + sta EnemyFrameTimer,x + lda #$05 + sta BowserHitPoints ;give bowser 5 hit points + lsr + sta BowserMovementSpeed ;set default movement speed here + rts + +;-------------------------------- + +DuplicateEnemyObj: + ldy #$ff ;start at beginning of enemy slots +FSLoop: iny ;increment one slot + lda Enemy_Flag,y ;check enemy buffer flag for empty slot + bne FSLoop ;if set, branch and keep checking + sty DuplicateObj_Offset ;otherwise set offset here + txa ;transfer original enemy buffer offset + ora #%10000000 ;store with d7 set as flag in new enemy + sta Enemy_Flag,y ;slot as well as enemy offset + lda Enemy_PageLoc,x + sta Enemy_PageLoc,y ;copy page location and horizontal coordinates + lda Enemy_X_Position,x ;from original enemy to new enemy + sta Enemy_X_Position,y + lda #$01 + sta Enemy_Flag,x ;set flag as normal for original enemy + sta Enemy_Y_HighPos,y ;set high vertical byte for new enemy + lda Enemy_Y_Position,x + sta Enemy_Y_Position,y ;copy vertical coordinate from original to new +FlmEx: rts ;and then leave + +;-------------------------------- + +FlameYPosData: + .db $90, $80, $70, $90 + +FlameYMFAdderData: + .db $ff, $01 + +InitBowserFlame: + lda FrenzyEnemyTimer ;if timer not expired yet, branch to leave + bne FlmEx + sta Enemy_Y_MoveForce,x ;reset something here + lda NoiseSoundQueue + ora #Sfx_BowserFlame ;load bowser's flame sound into queue + sta NoiseSoundQueue + ldy BowserFront_Offset ;get bowser's buffer offset + lda Enemy_ID,y ;check for bowser + cmp #Bowser + beq SpawnFromMouth ;branch if found + jsr SetFlameTimer ;get timer data based on flame counter + clc + adc #$20 ;add 32 frames by default + ldy SecondaryHardMode + beq SetFrT ;if secondary mode flag not set, use as timer setting + sec + sbc #$10 ;otherwise subtract 16 frames for secondary hard mode +SetFrT: sta FrenzyEnemyTimer ;set timer accordingly + lda PseudoRandomBitReg,x + and #%00000011 ;get 2 LSB from first part of LSFR + sta BowserFlamePRandomOfs,x ;set here + tay ;use as offset + lda FlameYPosData,y ;load vertical position based on pseudorandom offset + +PutAtRightExtent: + sta Enemy_Y_Position,x ;set vertical position + lda ScreenRight_X_Pos + clc + adc #$20 ;place enemy 32 pixels beyond right side of screen + sta Enemy_X_Position,x + lda ScreenRight_PageLoc + adc #$00 ;add carry + sta Enemy_PageLoc,x + jmp FinishFlame ;skip this part to finish setting values + +SpawnFromMouth: + lda Enemy_X_Position,y ;get bowser's horizontal position + sec + sbc #$0e ;subtract 14 pixels + sta Enemy_X_Position,x ;save as flame's horizontal position + lda Enemy_PageLoc,y + sta Enemy_PageLoc,x ;copy page location from bowser to flame + lda Enemy_Y_Position,y + clc ;add 8 pixels to bowser's vertical position + adc #$08 + sta Enemy_Y_Position,x ;save as flame's vertical position + lda PseudoRandomBitReg,x + and #%00000011 ;get 2 LSB from first part of LSFR + sta Enemy_YMF_Dummy,x ;save here + tay ;use as offset + lda FlameYPosData,y ;get value here using bits as offset + ldy #$00 ;load default offset + cmp Enemy_Y_Position,x ;compare value to flame's current vertical position + bcc SetMF ;if less, do not increment offset + iny ;otherwise increment now +SetMF: lda FlameYMFAdderData,y ;get value here and save + sta Enemy_Y_MoveForce,x ;to vertical movement force + lda #$00 + sta EnemyFrenzyBuffer ;clear enemy frenzy buffer + +FinishFlame: + lda #$08 ;set $08 for bounding box control + sta Enemy_BoundBoxCtrl,x + lda #$01 ;set high byte of vertical and + sta Enemy_Y_HighPos,x ;enemy buffer flag + sta Enemy_Flag,x + lsr + sta Enemy_X_MoveForce,x ;initialize horizontal movement force, and + sta Enemy_State,x ;enemy state + rts + +;-------------------------------- + +FireworksXPosData: + .db $00, $30, $60, $60, $00, $20 + +FireworksYPosData: + .db $60, $40, $70, $40, $60, $30 + +InitFireworks: + lda FrenzyEnemyTimer ;if timer not expired yet, branch to leave + bne ExitFWk + lda #$20 ;otherwise reset timer + sta FrenzyEnemyTimer + dec FireworksCounter ;decrement for each explosion + ldy #$06 ;start at last slot +StarFChk: dey + lda Enemy_ID,y ;check for presence of star flag object + cmp #StarFlagObject ;if there isn't a star flag object, + bne StarFChk ;routine goes into infinite loop = crash + lda Enemy_X_Position,y + sec ;get horizontal coordinate of star flag object, then + sbc #$30 ;subtract 48 pixels from it and save to + pha ;the stack + lda Enemy_PageLoc,y + sbc #$00 ;subtract the carry from the page location + sta $00 ;of the star flag object + lda FireworksCounter ;get fireworks counter + clc + adc Enemy_State,y ;add state of star flag object (possibly not necessary) + tay ;use as offset + pla ;get saved horizontal coordinate of star flag - 48 pixels + clc + adc FireworksXPosData,y ;add number based on offset of fireworks counter + sta Enemy_X_Position,x ;store as the fireworks object horizontal coordinate + lda $00 + adc #$00 ;add carry and store as page location for + sta Enemy_PageLoc,x ;the fireworks object + lda FireworksYPosData,y ;get vertical position using same offset + sta Enemy_Y_Position,x ;and store as vertical coordinate for fireworks object + lda #$01 + sta Enemy_Y_HighPos,x ;store in vertical high byte + sta Enemy_Flag,x ;and activate enemy buffer flag + lsr + sta ExplosionGfxCounter,x ;initialize explosion counter + lda #$08 + sta ExplosionTimerCounter,x ;set explosion timing counter +ExitFWk: rts + +;-------------------------------- + +Bitmasks: + .db %00000001, %00000010, %00000100, %00001000, %00010000, %00100000, %01000000, %10000000 + +Enemy17YPosData: + .db $40, $30, $90, $50, $20, $60, $a0, $70 + +SwimCC_IDData: + .db $0a, $0b + +BulletBillCheepCheep: + lda FrenzyEnemyTimer ;if timer not expired yet, branch to leave + bne ExF17 + lda AreaType ;are we in a water-type level? + bne DoBulletBills ;if not, branch elsewhere + cpx #$03 ;are we past third enemy slot? + bcs ExF17 ;if so, branch to leave + ldy #$00 ;load default offset + lda PseudoRandomBitReg,x + cmp #$aa ;check first part of LSFR against preset value + bcc ChkW2 ;if less than preset, do not increment offset + iny ;otherwise increment +ChkW2: lda WorldNumber ;check world number + cmp #World2 + beq Get17ID ;if we're on world 2, do not increment offset + iny ;otherwise increment +Get17ID: tya + and #%00000001 ;mask out all but last bit of offset + tay + lda SwimCC_IDData,y ;load identifier for cheep-cheeps +Set17ID: sta Enemy_ID,x ;store whatever's in A as enemy identifier + lda BitMFilter + cmp #$ff ;if not all bits set, skip init part and compare bits + bne GetRBit + lda #$00 ;initialize vertical position filter + sta BitMFilter +GetRBit: lda PseudoRandomBitReg,x ;get first part of LSFR + and #%00000111 ;mask out all but 3 LSB +ChkRBit: tay ;use as offset + lda Bitmasks,y ;load bitmask + bit BitMFilter ;perform AND on filter without changing it + beq AddFBit + iny ;increment offset + tya + and #%00000111 ;mask out all but 3 LSB thus keeping it 0-7 + jmp ChkRBit ;do another check +AddFBit: ora BitMFilter ;add bit to already set bits in filter + sta BitMFilter ;and store + lda Enemy17YPosData,y ;load vertical position using offset + jsr PutAtRightExtent ;set vertical position and other values + sta Enemy_YMF_Dummy,x ;initialize dummy variable + lda #$20 ;set timer + sta FrenzyEnemyTimer + jmp CheckpointEnemyID ;process our new enemy object + +DoBulletBills: + ldy #$ff ;start at beginning of enemy slots +BB_SLoop: iny ;move onto the next slot + cpy #$05 ;branch to play sound if we've done all slots + bcs FireBulletBill + lda Enemy_Flag,y ;if enemy buffer flag not set, + beq BB_SLoop ;loop back and check another slot + lda Enemy_ID,y + cmp #BulletBill_FrenzyVar ;check enemy identifier for + bne BB_SLoop ;bullet bill object (frenzy variant) +ExF17: rts ;if found, leave + +FireBulletBill: + lda Square2SoundQueue + ora #Sfx_Blast ;play fireworks/gunfire sound + sta Square2SoundQueue + lda #BulletBill_FrenzyVar ;load identifier for bullet bill object + bne Set17ID ;unconditional branch + +;-------------------------------- +;$00 - used to store Y position of group enemies +;$01 - used to store enemy ID +;$02 - used to store page location of right side of screen +;$03 - used to store X position of right side of screen + +HandleGroupEnemies: + ldy #$00 ;load value for green koopa troopa + sec + sbc #$37 ;subtract $37 from second byte read + pha ;save result in stack for now + cmp #$04 ;was byte in $3b-$3e range? + bcs SnglID ;if so, branch + pha ;save another copy to stack + ldy #Goomba ;load value for goomba enemy + lda PrimaryHardMode ;if primary hard mode flag not set, + beq PullID ;branch, otherwise change to value + ldy #BuzzyBeetle ;for buzzy beetle +PullID: pla ;get second copy from stack +SnglID: sty $01 ;save enemy id here + ldy #$b0 ;load default y coordinate + and #$02 ;check to see if d1 was set + beq SetYGp ;if so, move y coordinate up, + ldy #$70 ;otherwise branch and use default +SetYGp: sty $00 ;save y coordinate here + lda ScreenRight_PageLoc ;get page number of right edge of screen + sta $02 ;save here + lda ScreenRight_X_Pos ;get pixel coordinate of right edge + sta $03 ;save here + ldy #$02 ;load two enemies by default + pla ;get first copy from stack + lsr ;check to see if d0 was set + bcc CntGrp ;if not, use default value + iny ;otherwise increment to three enemies +CntGrp: sty NumberofGroupEnemies ;save number of enemies here +GrLoop: ldx #$ff ;start at beginning of enemy buffers +GSltLp: inx ;increment and branch if past + cpx #$05 ;end of buffers + bcs NextED + lda Enemy_Flag,x ;check to see if enemy is already + bne GSltLp ;stored in buffer, and branch if so + lda $01 + sta Enemy_ID,x ;store enemy object identifier + lda $02 + sta Enemy_PageLoc,x ;store page location for enemy object + lda $03 + sta Enemy_X_Position,x ;store x coordinate for enemy object + clc + adc #$18 ;add 24 pixels for next enemy + sta $03 + lda $02 ;add carry to page location for + adc #$00 ;next enemy + sta $02 + lda $00 ;store y coordinate for enemy object + sta Enemy_Y_Position,x + lda #$01 ;activate flag for buffer, and + sta Enemy_Y_HighPos,x ;put enemy within the screen vertically + sta Enemy_Flag,x + jsr CheckpointEnemyID ;process each enemy object separately + dec NumberofGroupEnemies ;do this until we run out of enemy objects + bne GrLoop +NextED: jmp Inc2B ;jump to increment data offset and leave + +;-------------------------------- + +InitPiranhaPlant: + lda #$01 ;set initial speed + sta PiranhaPlant_Y_Speed,x + lsr + sta Enemy_State,x ;initialize enemy state and what would normally + sta PiranhaPlant_MoveFlag,x ;be used as vertical speed, but not in this case + lda Enemy_Y_Position,x + sta PiranhaPlantDownYPos,x ;save original vertical coordinate here + sec + sbc #$18 + sta PiranhaPlantUpYPos,x ;save original vertical coordinate - 24 pixels here + lda #$09 + jmp SetBBox2 ;set specific value for bounding box control + +;-------------------------------- + +InitEnemyFrenzy: + lda Enemy_ID,x ;load enemy identifier + sta EnemyFrenzyBuffer ;save in enemy frenzy buffer + sec + sbc #$12 ;subtract 12 and use as offset for jump engine + jsr JumpEngine + +;frenzy object jump table + .dw LakituAndSpinyHandler + .dw NoFrenzyCode + .dw InitFlyingCheepCheep + .dw InitBowserFlame + .dw InitFireworks + .dw BulletBillCheepCheep + +;-------------------------------- + +NoFrenzyCode: + rts + +;-------------------------------- + +EndFrenzy: + ldy #$05 ;start at last slot +LakituChk: lda Enemy_ID,y ;check enemy identifiers + cmp #Lakitu ;for lakitu + bne NextFSlot + lda #$01 ;if found, set state + sta Enemy_State,y +NextFSlot: dey ;move onto the next slot + bpl LakituChk ;do this until all slots are checked + lda #$00 + sta EnemyFrenzyBuffer ;empty enemy frenzy buffer + sta Enemy_Flag,x ;disable enemy buffer flag for this object + rts + +;-------------------------------- + +InitJumpGPTroopa: + lda #$02 ;set for movement to the left + sta Enemy_MovingDir,x + lda #$f8 ;set horizontal speed + sta Enemy_X_Speed,x +TallBBox2: lda #$03 ;set specific value for bounding box control +SetBBox2: sta Enemy_BoundBoxCtrl,x ;set bounding box control then leave + rts + +;-------------------------------- + +InitBalPlatform: + dec Enemy_Y_Position,x ;raise vertical position by two pixels + dec Enemy_Y_Position,x + ldy SecondaryHardMode ;if secondary hard mode flag not set, + bne AlignP ;branch ahead + ldy #$02 ;otherwise set value here + jsr PosPlatform ;do a sub to add or subtract pixels +AlignP: ldy #$ff ;set default value here for now + lda BalPlatformAlignment ;get current balance platform alignment + sta Enemy_State,x ;set platform alignment to object state here + bpl SetBPA ;if old alignment $ff, put $ff as alignment for negative + txa ;if old contents already $ff, put + tay ;object offset as alignment to make next positive +SetBPA: sty BalPlatformAlignment ;store whatever value's in Y here + lda #$00 + sta Enemy_MovingDir,x ;init moving direction + tay ;init Y + jsr PosPlatform ;do a sub to add 8 pixels, then run shared code here + +;-------------------------------- + +InitDropPlatform: + lda #$ff + sta PlatformCollisionFlag,x ;set some value here + jmp CommonPlatCode ;then jump ahead to execute more code + +;-------------------------------- + +InitHoriPlatform: + lda #$00 + sta XMoveSecondaryCounter,x ;init one of the moving counters + jmp CommonPlatCode ;jump ahead to execute more code + +;-------------------------------- + +InitVertPlatform: + ldy #$40 ;set default value here + lda Enemy_Y_Position,x ;check vertical position + bpl SetYO ;if above a certain point, skip this part + eor #$ff + clc ;otherwise get two's compliment + adc #$01 + ldy #$c0 ;get alternate value to add to vertical position +SetYO: sta YPlatformTopYPos,x ;save as top vertical position + tya + clc ;load value from earlier, add number of pixels + adc Enemy_Y_Position,x ;to vertical position + sta YPlatformCenterYPos,x ;save result as central vertical position + +;-------------------------------- + +CommonPlatCode: + jsr InitVStf ;do a sub to init certain other values +SPBBox: lda #$05 ;set default bounding box size control + ldy AreaType + cpy #$03 ;check for castle-type level + beq CasPBB ;use default value if found + ldy SecondaryHardMode ;otherwise check for secondary hard mode flag + bne CasPBB ;if set, use default value + lda #$06 ;use alternate value if not castle or secondary not set +CasPBB: sta Enemy_BoundBoxCtrl,x ;set bounding box size control here and leave + rts + +;-------------------------------- + +LargeLiftUp: + jsr PlatLiftUp ;execute code for platforms going up + jmp LargeLiftBBox ;overwrite bounding box for large platforms + +LargeLiftDown: + jsr PlatLiftDown ;execute code for platforms going down + +LargeLiftBBox: + jmp SPBBox ;jump to overwrite bounding box size control + +;-------------------------------- + +PlatLiftUp: + lda #$10 ;set movement amount here + sta Enemy_Y_MoveForce,x + lda #$ff ;set moving speed for platforms going up + sta Enemy_Y_Speed,x + jmp CommonSmallLift ;skip ahead to part we should be executing + +;-------------------------------- + +PlatLiftDown: + lda #$f0 ;set movement amount here + sta Enemy_Y_MoveForce,x + lda #$00 ;set moving speed for platforms going down + sta Enemy_Y_Speed,x + +;-------------------------------- + +CommonSmallLift: + ldy #$01 + jsr PosPlatform ;do a sub to add 12 pixels due to preset value + lda #$04 + sta Enemy_BoundBoxCtrl,x ;set bounding box control for small platforms + rts + +;-------------------------------- + +PlatPosDataLow: + .db $08,$0c,$f8 + +PlatPosDataHigh: + .db $00,$00,$ff + +PosPlatform: + lda Enemy_X_Position,x ;get horizontal coordinate + clc + adc PlatPosDataLow,y ;add or subtract pixels depending on offset + sta Enemy_X_Position,x ;store as new horizontal coordinate + lda Enemy_PageLoc,x + adc PlatPosDataHigh,y ;add or subtract page location depending on offset + sta Enemy_PageLoc,x ;store as new page location + rts ;and go back + +;-------------------------------- + +EndOfEnemyInitCode: + rts + +;------------------------------------------------------------------------------------- + +RunEnemyObjectsCore: + ldx ObjectOffset ;get offset for enemy object buffer + lda #$00 ;load value 0 for jump engine by default + ldy Enemy_ID,x + cpy #$15 ;if enemy object < $15, use default value + bcc JmpEO + tya ;otherwise subtract $14 from the value and use + sbc #$14 ;as value for jump engine +JmpEO: jsr JumpEngine + + .dw RunNormalEnemies ;for objects $00-$14 + + .dw RunBowserFlame ;for objects $15-$1f + .dw RunFireworks + .dw NoRunCode + .dw NoRunCode + .dw NoRunCode + .dw NoRunCode + .dw RunFirebarObj + .dw RunFirebarObj + .dw RunFirebarObj + .dw RunFirebarObj + .dw RunFirebarObj + + .dw RunFirebarObj ;for objects $20-$2f + .dw RunFirebarObj + .dw RunFirebarObj + .dw NoRunCode + .dw RunLargePlatform + .dw RunLargePlatform + .dw RunLargePlatform + .dw RunLargePlatform + .dw RunLargePlatform + .dw RunLargePlatform + .dw RunLargePlatform + .dw RunSmallPlatform + .dw RunSmallPlatform + .dw RunBowser + .dw PowerUpObjHandler + .dw VineObjectHandler + + .dw NoRunCode ;for objects $30-$35 + .dw RunStarFlagObj + .dw JumpspringHandler + .dw NoRunCode + .dw WarpZoneObject + .dw RunRetainerObj + +;-------------------------------- + +NoRunCode: + rts + +;-------------------------------- + +RunRetainerObj: + jsr GetEnemyOffscreenBits + jsr RelativeEnemyPosition + jmp EnemyGfxHandler + +;-------------------------------- + +RunNormalEnemies: + lda #$00 ;init sprite attributes + sta Enemy_SprAttrib,x + jsr GetEnemyOffscreenBits + jsr RelativeEnemyPosition + jsr EnemyGfxHandler + jsr GetEnemyBoundBox + jsr EnemyToBGCollisionDet + jsr EnemiesCollision + jsr PlayerEnemyCollision + ldy TimerControl ;if master timer control set, skip to last routine + bne SkipMove + jsr EnemyMovementSubs +SkipMove: jmp OffscreenBoundsCheck + +EnemyMovementSubs: + lda Enemy_ID,x + jsr JumpEngine + + .dw MoveNormalEnemy ;only objects $00-$14 use this table + .dw MoveNormalEnemy + .dw MoveNormalEnemy + .dw MoveNormalEnemy + .dw MoveNormalEnemy + .dw ProcHammerBro + .dw MoveNormalEnemy + .dw MoveBloober + .dw MoveBulletBill + .dw NoMoveCode + .dw MoveSwimmingCheepCheep + .dw MoveSwimmingCheepCheep + .dw MovePodoboo + .dw MovePiranhaPlant + .dw MoveJumpingEnemy + .dw ProcMoveRedPTroopa + .dw MoveFlyGreenPTroopa + .dw MoveLakitu + .dw MoveNormalEnemy + .dw NoMoveCode ;dummy + .dw MoveFlyingCheepCheep + +;-------------------------------- + +NoMoveCode: + rts + +;-------------------------------- + +RunBowserFlame: + jsr ProcBowserFlame + jsr GetEnemyOffscreenBits + jsr RelativeEnemyPosition + jsr GetEnemyBoundBox + jsr PlayerEnemyCollision + jmp OffscreenBoundsCheck + +;-------------------------------- + +RunFirebarObj: + jsr ProcFirebar + jmp OffscreenBoundsCheck + +;-------------------------------- + +RunSmallPlatform: + jsr GetEnemyOffscreenBits + jsr RelativeEnemyPosition + jsr SmallPlatformBoundBox + jsr SmallPlatformCollision + jsr RelativeEnemyPosition + jsr DrawSmallPlatform + jsr MoveSmallPlatform + jmp OffscreenBoundsCheck + +;-------------------------------- + +RunLargePlatform: + jsr GetEnemyOffscreenBits + jsr RelativeEnemyPosition + jsr LargePlatformBoundBox + jsr LargePlatformCollision + lda TimerControl ;if master timer control set, + bne SkipPT ;skip subroutine tree + jsr LargePlatformSubroutines +SkipPT: jsr RelativeEnemyPosition + jsr DrawLargePlatform + jmp OffscreenBoundsCheck + +;-------------------------------- + +LargePlatformSubroutines: + lda Enemy_ID,x ;subtract $24 to get proper offset for jump table + sec + sbc #$24 + jsr JumpEngine + + .dw BalancePlatform ;table used by objects $24-$2a + .dw YMovingPlatform + .dw MoveLargeLiftPlat + .dw MoveLargeLiftPlat + .dw XMovingPlatform + .dw DropPlatform + .dw RightPlatform + +;------------------------------------------------------------------------------------- + +EraseEnemyObject: + lda #$00 ;clear all enemy object variables + sta Enemy_Flag,x + sta Enemy_ID,x + sta Enemy_State,x + sta FloateyNum_Control,x + sta EnemyIntervalTimer,x + sta ShellChainCounter,x + sta Enemy_SprAttrib,x + sta EnemyFrameTimer,x + rts + +;------------------------------------------------------------------------------------- + +MovePodoboo: + lda EnemyIntervalTimer,x ;check enemy timer + bne PdbM ;branch to move enemy if not expired + jsr InitPodoboo ;otherwise set up podoboo again + lda PseudoRandomBitReg+1,x ;get part of LSFR + ora #%10000000 ;set d7 + sta Enemy_Y_MoveForce,x ;store as movement force + and #%00001111 ;mask out high nybble + ora #$06 ;set for at least six intervals + sta EnemyIntervalTimer,x ;store as new enemy timer + lda #$f9 + sta Enemy_Y_Speed,x ;set vertical speed to move podoboo upwards +PdbM: jmp MoveJ_EnemyVertically ;branch to impose gravity on podoboo + +;-------------------------------- +;$00 - used in HammerBroJumpCode as bitmask + +HammerThrowTmrData: + .db $30, $1c + +XSpeedAdderData: + .db $00, $e8, $00, $18 + +RevivedXSpeed: + .db $08, $f8, $0c, $f4 + +ProcHammerBro: + lda Enemy_State,x ;check hammer bro's enemy state for d5 set + and #%00100000 + beq ChkJH ;if not set, go ahead with code + jmp MoveDefeatedEnemy ;otherwise jump to something else +ChkJH: lda HammerBroJumpTimer,x ;check jump timer + beq HammerBroJumpCode ;if expired, branch to jump + dec HammerBroJumpTimer,x ;otherwise decrement jump timer + lda Enemy_OffscreenBits + and #%00001100 ;check offscreen bits + bne MoveHammerBroXDir ;if hammer bro a little offscreen, skip to movement code + lda HammerThrowingTimer,x ;check hammer throwing timer + bne DecHT ;if not expired, skip ahead, do not throw hammer + ldy SecondaryHardMode ;otherwise get secondary hard mode flag + lda HammerThrowTmrData,y ;get timer data using flag as offset + sta HammerThrowingTimer,x ;set as new timer + jsr SpawnHammerObj ;do a sub here to spawn hammer object + bcc DecHT ;if carry clear, hammer not spawned, skip to decrement timer + lda Enemy_State,x + ora #%00001000 ;set d3 in enemy state for hammer throw + sta Enemy_State,x + jmp MoveHammerBroXDir ;jump to move hammer bro +DecHT: dec HammerThrowingTimer,x ;decrement timer + jmp MoveHammerBroXDir ;jump to move hammer bro + +HammerBroJumpLData: + .db $20, $37 + +HammerBroJumpCode: + lda Enemy_State,x ;get hammer bro's enemy state + and #%00000111 ;mask out all but 3 LSB + cmp #$01 ;check for d0 set (for jumping) + beq MoveHammerBroXDir ;if set, branch ahead to moving code + lda #$00 ;load default value here + sta $00 ;save into temp variable for now + ldy #$fa ;set default vertical speed + lda Enemy_Y_Position,x ;check hammer bro's vertical coordinate + bmi SetHJ ;if on the bottom half of the screen, use current speed + ldy #$fd ;otherwise set alternate vertical speed + cmp #$70 ;check to see if hammer bro is above the middle of screen + inc $00 ;increment preset value to $01 + bcc SetHJ ;if above the middle of the screen, use current speed and $01 + dec $00 ;otherwise return value to $00 + lda PseudoRandomBitReg+1,x ;get part of LSFR, mask out all but LSB + and #$01 + bne SetHJ ;if d0 of LSFR set, branch and use current speed and $00 + ldy #$fa ;otherwise reset to default vertical speed +SetHJ: sty Enemy_Y_Speed,x ;set vertical speed for jumping + lda Enemy_State,x ;set d0 in enemy state for jumping + ora #$01 + sta Enemy_State,x + lda $00 ;load preset value here to use as bitmask + and PseudoRandomBitReg+2,x ;and do bit-wise comparison with part of LSFR + tay ;then use as offset + lda SecondaryHardMode ;check secondary hard mode flag + bne HJump + tay ;if secondary hard mode flag clear, set offset to 0 +HJump: lda HammerBroJumpLData,y ;get jump length timer data using offset from before + sta EnemyFrameTimer,x ;save in enemy timer + lda PseudoRandomBitReg+1,x + ora #%11000000 ;get contents of part of LSFR, set d7 and d6, then + sta HammerBroJumpTimer,x ;store in jump timer + +MoveHammerBroXDir: + ldy #$fc ;move hammer bro a little to the left + lda FrameCounter + and #%01000000 ;change hammer bro's direction every 64 frames + bne Shimmy + ldy #$04 ;if d6 set in counter, move him a little to the right +Shimmy: sty Enemy_X_Speed,x ;store horizontal speed + ldy #$01 ;set to face right by default + jsr PlayerEnemyDiff ;get horizontal difference between player and hammer bro + bmi SetShim ;if enemy to the left of player, skip this part + iny ;set to face left + lda EnemyIntervalTimer,x ;check walking timer + bne SetShim ;if not yet expired, skip to set moving direction + lda #$f8 + sta Enemy_X_Speed,x ;otherwise, make the hammer bro walk left towards player +SetShim: sty Enemy_MovingDir,x ;set moving direction + +MoveNormalEnemy: + ldy #$00 ;init Y to leave horizontal movement as-is + lda Enemy_State,x + and #%01000000 ;check enemy state for d6 set, if set skip + bne FallE ;to move enemy vertically, then horizontally if necessary + lda Enemy_State,x + asl ;check enemy state for d7 set + bcs SteadM ;if set, branch to move enemy horizontally + lda Enemy_State,x + and #%00100000 ;check enemy state for d5 set + bne MoveDefeatedEnemy ;if set, branch to move defeated enemy object + lda Enemy_State,x + and #%00000111 ;check d2-d0 of enemy state for any set bits + beq SteadM ;if enemy in normal state, branch to move enemy horizontally + cmp #$05 + beq FallE ;if enemy in state used by spiny's egg, go ahead here + cmp #$03 + bcs ReviveStunned ;if enemy in states $03 or $04, skip ahead to yet another part +FallE: jsr MoveD_EnemyVertically ;do a sub here to move enemy downwards + ldy #$00 + lda Enemy_State,x ;check for enemy state $02 + cmp #$02 + beq MEHor ;if found, branch to move enemy horizontally + and #%01000000 ;check for d6 set + beq SteadM ;if not set, branch to something else + lda Enemy_ID,x + cmp #PowerUpObject ;check for power-up object + beq SteadM + bne SlowM ;if any other object where d6 set, jump to set Y +MEHor: jmp MoveEnemyHorizontally ;jump here to move enemy horizontally for <> $2e and d6 set + +SlowM: ldy #$01 ;if branched here, increment Y to slow horizontal movement +SteadM: lda Enemy_X_Speed,x ;get current horizontal speed + pha ;save to stack + bpl AddHS ;if not moving or moving right, skip, leave Y alone + iny + iny ;otherwise increment Y to next data +AddHS: clc + adc XSpeedAdderData,y ;add value here to slow enemy down if necessary + sta Enemy_X_Speed,x ;save as horizontal speed temporarily + jsr MoveEnemyHorizontally ;then do a sub to move horizontally + pla + sta Enemy_X_Speed,x ;get old horizontal speed from stack and return to + rts ;original memory location, then leave + +ReviveStunned: + lda EnemyIntervalTimer,x ;if enemy timer not expired yet, + bne ChkKillGoomba ;skip ahead to something else + sta Enemy_State,x ;otherwise initialize enemy state to normal + lda FrameCounter + and #$01 ;get d0 of frame counter + tay ;use as Y and increment for movement direction + iny + sty Enemy_MovingDir,x ;store as pseudorandom movement direction + dey ;decrement for use as pointer + lda PrimaryHardMode ;check primary hard mode flag + beq SetRSpd ;if not set, use pointer as-is + iny + iny ;otherwise increment 2 bytes to next data +SetRSpd: lda RevivedXSpeed,y ;load and store new horizontal speed + sta Enemy_X_Speed,x ;and leave + rts + +MoveDefeatedEnemy: + jsr MoveD_EnemyVertically ;execute sub to move defeated enemy downwards + jmp MoveEnemyHorizontally ;now move defeated enemy horizontally + +ChkKillGoomba: + cmp #$0e ;check to see if enemy timer has reached + bne NKGmba ;a certain point, and branch to leave if not + lda Enemy_ID,x + cmp #Goomba ;check for goomba object + bne NKGmba ;branch if not found + jsr EraseEnemyObject ;otherwise, kill this goomba object +NKGmba: rts ;leave! + +;-------------------------------- + +MoveJumpingEnemy: + jsr MoveJ_EnemyVertically ;do a sub to impose gravity on green paratroopa + jmp MoveEnemyHorizontally ;jump to move enemy horizontally + +;-------------------------------- + +ProcMoveRedPTroopa: + lda Enemy_Y_Speed,x + ora Enemy_Y_MoveForce,x ;check for any vertical force or speed + bne MoveRedPTUpOrDown ;branch if any found + sta Enemy_YMF_Dummy,x ;initialize something here + lda Enemy_Y_Position,x ;check current vs. original vertical coordinate + cmp RedPTroopaOrigXPos,x + bcs MoveRedPTUpOrDown ;if current => original, skip ahead to more code + lda FrameCounter ;get frame counter + and #%00000111 ;mask out all but 3 LSB + bne NoIncPT ;if any bits set, branch to leave + inc Enemy_Y_Position,x ;otherwise increment red paratroopa's vertical position +NoIncPT: rts ;leave + +MoveRedPTUpOrDown: + lda Enemy_Y_Position,x ;check current vs. central vertical coordinate + cmp RedPTroopaCenterYPos,x + bcc MovPTDwn ;if current < central, jump to move downwards + jmp MoveRedPTroopaUp ;otherwise jump to move upwards +MovPTDwn: jmp MoveRedPTroopaDown ;move downwards + +;-------------------------------- +;$00 - used to store adder for movement, also used as adder for platform +;$01 - used to store maximum value for secondary counter + +MoveFlyGreenPTroopa: + jsr XMoveCntr_GreenPTroopa ;do sub to increment primary and secondary counters + jsr MoveWithXMCntrs ;do sub to move green paratroopa accordingly, and horizontally + ldy #$01 ;set Y to move green paratroopa down + lda FrameCounter + and #%00000011 ;check frame counter 2 LSB for any bits set + bne NoMGPT ;branch to leave if set to move up/down every fourth frame + lda FrameCounter + and #%01000000 ;check frame counter for d6 set + bne YSway ;branch to move green paratroopa down if set + ldy #$ff ;otherwise set Y to move green paratroopa up +YSway: sty $00 ;store adder here + lda Enemy_Y_Position,x + clc ;add or subtract from vertical position + adc $00 ;to give green paratroopa a wavy flight + sta Enemy_Y_Position,x +NoMGPT: rts ;leave! + +XMoveCntr_GreenPTroopa: + lda #$13 ;load preset maximum value for secondary counter + +XMoveCntr_Platform: + sta $01 ;store value here + lda FrameCounter + and #%00000011 ;branch to leave if not on + bne NoIncXM ;every fourth frame + ldy XMoveSecondaryCounter,x ;get secondary counter + lda XMovePrimaryCounter,x ;get primary counter + lsr + bcs DecSeXM ;if d0 of primary counter set, branch elsewhere + cpy $01 ;compare secondary counter to preset maximum value + beq IncPXM ;if equal, branch ahead of this part + inc XMoveSecondaryCounter,x ;increment secondary counter and leave +NoIncXM: rts +IncPXM: inc XMovePrimaryCounter,x ;increment primary counter and leave + rts +DecSeXM: tya ;put secondary counter in A + beq IncPXM ;if secondary counter at zero, branch back + dec XMoveSecondaryCounter,x ;otherwise decrement secondary counter and leave + rts + +MoveWithXMCntrs: + lda XMoveSecondaryCounter,x ;save secondary counter to stack + pha + ldy #$01 ;set value here by default + lda XMovePrimaryCounter,x + and #%00000010 ;if d1 of primary counter is + bne XMRight ;set, branch ahead of this part here + lda XMoveSecondaryCounter,x + eor #$ff ;otherwise change secondary + clc ;counter to two's compliment + adc #$01 + sta XMoveSecondaryCounter,x + ldy #$02 ;load alternate value here +XMRight: sty Enemy_MovingDir,x ;store as moving direction + jsr MoveEnemyHorizontally + sta $00 ;save value obtained from sub here + pla ;get secondary counter from stack + sta XMoveSecondaryCounter,x ;and return to original place + rts + +;-------------------------------- + +BlooberBitmasks: + .db %00111111, %00000011 + +MoveBloober: + lda Enemy_State,x + and #%00100000 ;check enemy state for d5 set + bne MoveDefeatedBloober ;branch if set to move defeated bloober + ldy SecondaryHardMode ;use secondary hard mode flag as offset + lda PseudoRandomBitReg+1,x ;get LSFR + and BlooberBitmasks,y ;mask out bits in LSFR using bitmask loaded with offset + bne BlooberSwim ;if any bits set, skip ahead to make swim + txa + lsr ;check to see if on second or fourth slot (1 or 3) + bcc FBLeft ;if not, branch to figure out moving direction + ldy Player_MovingDir ;otherwise, load player's moving direction and + bcs SBMDir ;do an unconditional branch to set +FBLeft: ldy #$02 ;set left moving direction by default + jsr PlayerEnemyDiff ;get horizontal difference between player and bloober + bpl SBMDir ;if enemy to the right of player, keep left + dey ;otherwise decrement to set right moving direction +SBMDir: sty Enemy_MovingDir,x ;set moving direction of bloober, then continue on here + +BlooberSwim: + jsr ProcSwimmingB ;execute sub to make bloober swim characteristically + lda Enemy_Y_Position,x ;get vertical coordinate + sec + sbc Enemy_Y_MoveForce,x ;subtract movement force + cmp #$20 ;check to see if position is above edge of status bar + bcc SwimX ;if so, don't do it + sta Enemy_Y_Position,x ;otherwise, set new vertical position, make bloober swim +SwimX: ldy Enemy_MovingDir,x ;check moving direction + dey + bne LeftSwim ;if moving to the left, branch to second part + lda Enemy_X_Position,x + clc ;add movement speed to horizontal coordinate + adc BlooperMoveSpeed,x + sta Enemy_X_Position,x ;store result as new horizontal coordinate + lda Enemy_PageLoc,x + adc #$00 ;add carry to page location + sta Enemy_PageLoc,x ;store as new page location and leave + rts + +LeftSwim: + lda Enemy_X_Position,x + sec ;subtract movement speed from horizontal coordinate + sbc BlooperMoveSpeed,x + sta Enemy_X_Position,x ;store result as new horizontal coordinate + lda Enemy_PageLoc,x + sbc #$00 ;subtract borrow from page location + sta Enemy_PageLoc,x ;store as new page location and leave + rts + +MoveDefeatedBloober: + jmp MoveEnemySlowVert ;jump to move defeated bloober downwards + +ProcSwimmingB: + lda BlooperMoveCounter,x ;get enemy's movement counter + and #%00000010 ;check for d1 set + bne ChkForFloatdown ;branch if set + lda FrameCounter + and #%00000111 ;get 3 LSB of frame counter + pha ;and save it to the stack + lda BlooperMoveCounter,x ;get enemy's movement counter + lsr ;check for d0 set + bcs SlowSwim ;branch if set + pla ;pull 3 LSB of frame counter from the stack + bne BSwimE ;branch to leave, execute code only every eighth frame + lda Enemy_Y_MoveForce,x + clc ;add to movement force to speed up swim + adc #$01 + sta Enemy_Y_MoveForce,x ;set movement force + sta BlooperMoveSpeed,x ;set as movement speed + cmp #$02 + bne BSwimE ;if certain horizontal speed, branch to leave + inc BlooperMoveCounter,x ;otherwise increment movement counter +BSwimE: rts + +SlowSwim: + pla ;pull 3 LSB of frame counter from the stack + bne NoSSw ;branch to leave, execute code only every eighth frame + lda Enemy_Y_MoveForce,x + sec ;subtract from movement force to slow swim + sbc #$01 + sta Enemy_Y_MoveForce,x ;set movement force + sta BlooperMoveSpeed,x ;set as movement speed + bne NoSSw ;if any speed, branch to leave + inc BlooperMoveCounter,x ;otherwise increment movement counter + lda #$02 + sta EnemyIntervalTimer,x ;set enemy's timer +NoSSw: rts ;leave + +ChkForFloatdown: + lda EnemyIntervalTimer,x ;get enemy timer + beq ChkNearPlayer ;branch if expired + +Floatdown: + lda FrameCounter ;get frame counter + lsr ;check for d0 set + bcs NoFD ;branch to leave on every other frame + inc Enemy_Y_Position,x ;otherwise increment vertical coordinate +NoFD: rts ;leave + +ChkNearPlayer: + lda Enemy_Y_Position,x ;get vertical coordinate + adc #$10 ;add sixteen pixels + cmp Player_Y_Position ;compare result with player's vertical coordinate + bcc Floatdown ;if modified vertical less than player's, branch + lda #$00 + sta BlooperMoveCounter,x ;otherwise nullify movement counter + rts + +;-------------------------------- + +MoveBulletBill: + lda Enemy_State,x ;check bullet bill's enemy object state for d5 set + and #%00100000 + beq NotDefB ;if not set, continue with movement code + jmp MoveJ_EnemyVertically ;otherwise jump to move defeated bullet bill downwards +NotDefB: lda #$e8 ;set bullet bill's horizontal speed + sta Enemy_X_Speed,x ;and move it accordingly (note: this bullet bill + jmp MoveEnemyHorizontally ;object occurs in frenzy object $17, not from cannons) + +;-------------------------------- +;$02 - used to hold preset values +;$03 - used to hold enemy state + +SwimCCXMoveData: + .db $40, $80 + .db $04, $04 ;residual data, not used + +MoveSwimmingCheepCheep: + lda Enemy_State,x ;check cheep-cheep's enemy object state + and #%00100000 ;for d5 set + beq CCSwim ;if not set, continue with movement code + jmp MoveEnemySlowVert ;otherwise jump to move defeated cheep-cheep downwards +CCSwim: sta $03 ;save enemy state in $03 + lda Enemy_ID,x ;get enemy identifier + sec + sbc #$0a ;subtract ten for cheep-cheep identifiers + tay ;use as offset + lda SwimCCXMoveData,y ;load value here + sta $02 + lda Enemy_X_MoveForce,x ;load horizontal force + sec + sbc $02 ;subtract preset value from horizontal force + sta Enemy_X_MoveForce,x ;store as new horizontal force + lda Enemy_X_Position,x ;get horizontal coordinate + sbc #$00 ;subtract borrow (thus moving it slowly) + sta Enemy_X_Position,x ;and save as new horizontal coordinate + lda Enemy_PageLoc,x + sbc #$00 ;subtract borrow again, this time from the + sta Enemy_PageLoc,x ;page location, then save + lda #$20 + sta $02 ;save new value here + cpx #$02 ;check enemy object offset + bcc ExSwCC ;if in first or second slot, branch to leave + lda CheepCheepMoveMFlag,x ;check movement flag + cmp #$10 ;if movement speed set to $00, + bcc CCSwimUpwards ;branch to move upwards + lda Enemy_YMF_Dummy,x + clc + adc $02 ;add preset value to dummy variable to get carry + sta Enemy_YMF_Dummy,x ;and save dummy + lda Enemy_Y_Position,x ;get vertical coordinate + adc $03 ;add carry to it plus enemy state to slowly move it downwards + sta Enemy_Y_Position,x ;save as new vertical coordinate + lda Enemy_Y_HighPos,x + adc #$00 ;add carry to page location and + jmp ChkSwimYPos ;jump to end of movement code + +CCSwimUpwards: + lda Enemy_YMF_Dummy,x + sec + sbc $02 ;subtract preset value to dummy variable to get borrow + sta Enemy_YMF_Dummy,x ;and save dummy + lda Enemy_Y_Position,x ;get vertical coordinate + sbc $03 ;subtract borrow to it plus enemy state to slowly move it upwards + sta Enemy_Y_Position,x ;save as new vertical coordinate + lda Enemy_Y_HighPos,x + sbc #$00 ;subtract borrow from page location + +ChkSwimYPos: + sta Enemy_Y_HighPos,x ;save new page location here + ldy #$00 ;load movement speed to upwards by default + lda Enemy_Y_Position,x ;get vertical coordinate + sec + sbc CheepCheepOrigYPos,x ;subtract original coordinate from current + bpl YPDiff ;if result positive, skip to next part + ldy #$10 ;otherwise load movement speed to downwards + eor #$ff + clc ;get two's compliment of result + adc #$01 ;to obtain total difference of original vs. current +YPDiff: cmp #$0f ;if difference between original vs. current vertical + bcc ExSwCC ;coordinates < 15 pixels, leave movement speed alone + tya + sta CheepCheepMoveMFlag,x ;otherwise change movement speed +ExSwCC: rts ;leave + +;-------------------------------- +;$00 - used as counter for firebar parts +;$01 - used for oscillated high byte of spin state or to hold horizontal adder +;$02 - used for oscillated high byte of spin state or to hold vertical adder +;$03 - used for mirror data +;$04 - used to store player's sprite 1 X coordinate +;$05 - used to evaluate mirror data +;$06 - used to store either screen X coordinate or sprite data offset +;$07 - used to store screen Y coordinate +;$ed - used to hold maximum length of firebar +;$ef - used to hold high byte of spinstate + +;horizontal adder is at first byte + high byte of spinstate, +;vertical adder is same + 8 bytes, two's compliment +;if greater than $08 for proper oscillation +FirebarPosLookupTbl: + .db $00, $01, $03, $04, $05, $06, $07, $07, $08 + .db $00, $03, $06, $09, $0b, $0d, $0e, $0f, $10 + .db $00, $04, $09, $0d, $10, $13, $16, $17, $18 + .db $00, $06, $0c, $12, $16, $1a, $1d, $1f, $20 + .db $00, $07, $0f, $16, $1c, $21, $25, $27, $28 + .db $00, $09, $12, $1b, $21, $27, $2c, $2f, $30 + .db $00, $0b, $15, $1f, $27, $2e, $33, $37, $38 + .db $00, $0c, $18, $24, $2d, $35, $3b, $3e, $40 + .db $00, $0e, $1b, $28, $32, $3b, $42, $46, $48 + .db $00, $0f, $1f, $2d, $38, $42, $4a, $4e, $50 + .db $00, $11, $22, $31, $3e, $49, $51, $56, $58 + +FirebarMirrorData: + .db $01, $03, $02, $00 + +FirebarTblOffsets: + .db $00, $09, $12, $1b, $24, $2d + .db $36, $3f, $48, $51, $5a, $63 + +FirebarYPos: + .db $0c, $18 + +ProcFirebar: + jsr GetEnemyOffscreenBits ;get offscreen information + lda Enemy_OffscreenBits ;check for d3 set + and #%00001000 ;if so, branch to leave + bne SkipFBar + lda TimerControl ;if master timer control set, branch + bne SusFbar ;ahead of this part + lda FirebarSpinSpeed,x ;load spinning speed of firebar + jsr FirebarSpin ;modify current spinstate + and #%00011111 ;mask out all but 5 LSB + sta FirebarSpinState_High,x ;and store as new high byte of spinstate +SusFbar: lda FirebarSpinState_High,x ;get high byte of spinstate + ldy Enemy_ID,x ;check enemy identifier + cpy #$1f + bcc SetupGFB ;if < $1f (long firebar), branch + cmp #$08 ;check high byte of spinstate + beq SkpFSte ;if eight, branch to change + cmp #$18 + bne SetupGFB ;if not at twenty-four branch to not change +SkpFSte: clc + adc #$01 ;add one to spinning thing to avoid horizontal state + sta FirebarSpinState_High,x +SetupGFB: sta $ef ;save high byte of spinning thing, modified or otherwise + jsr RelativeEnemyPosition ;get relative coordinates to screen + jsr GetFirebarPosition ;do a sub here (residual, too early to be used now) + ldy Enemy_SprDataOffset,x ;get OAM data offset + lda Enemy_Rel_YPos ;get relative vertical coordinate + sta Sprite_Y_Position,y ;store as Y in OAM data + sta $07 ;also save here + lda Enemy_Rel_XPos ;get relative horizontal coordinate + sta Sprite_X_Position,y ;store as X in OAM data + sta $06 ;also save here + lda #$01 + sta $00 ;set $01 value here (not necessary) + jsr FirebarCollision ;draw fireball part and do collision detection + ldy #$05 ;load value for short firebars by default + lda Enemy_ID,x + cmp #$1f ;are we doing a long firebar? + bcc SetMFbar ;no, branch then + ldy #$0b ;otherwise load value for long firebars +SetMFbar: sty $ed ;store maximum value for length of firebars + lda #$00 + sta $00 ;initialize counter here +DrawFbar: lda $ef ;load high byte of spinstate + jsr GetFirebarPosition ;get fireball position data depending on firebar part + jsr DrawFirebar_Collision ;position it properly, draw it and do collision detection + lda $00 ;check which firebar part + cmp #$04 + bne NextFbar + ldy DuplicateObj_Offset ;if we arrive at fifth firebar part, + lda Enemy_SprDataOffset,y ;get offset from long firebar and load OAM data offset + sta $06 ;using long firebar offset, then store as new one here +NextFbar: inc $00 ;move onto the next firebar part + lda $00 + cmp $ed ;if we end up at the maximum part, go on and leave + bcc DrawFbar ;otherwise go back and do another +SkipFBar: rts + +DrawFirebar_Collision: + lda $03 ;store mirror data elsewhere + sta $05 + ldy $06 ;load OAM data offset for firebar + lda $01 ;load horizontal adder we got from position loader + lsr $05 ;shift LSB of mirror data + bcs AddHA ;if carry was set, skip this part + eor #$ff + adc #$01 ;otherwise get two's compliment of horizontal adder +AddHA: clc ;add horizontal coordinate relative to screen to + adc Enemy_Rel_XPos ;horizontal adder, modified or otherwise + sta Sprite_X_Position,y ;store as X coordinate here + sta $06 ;store here for now, note offset is saved in Y still + cmp Enemy_Rel_XPos ;compare X coordinate of sprite to original X of firebar + bcs SubtR1 ;if sprite coordinate => original coordinate, branch + lda Enemy_Rel_XPos + sec ;otherwise subtract sprite X from the + sbc $06 ;original one and skip this part + jmp ChkFOfs +SubtR1: sec ;subtract original X from the + sbc Enemy_Rel_XPos ;current sprite X +ChkFOfs: cmp #$59 ;if difference of coordinates within a certain range, + bcc VAHandl ;continue by handling vertical adder + lda #$f8 ;otherwise, load offscreen Y coordinate + bne SetVFbr ;and unconditionally branch to move sprite offscreen +VAHandl: lda Enemy_Rel_YPos ;if vertical relative coordinate offscreen, + cmp #$f8 ;skip ahead of this part and write into sprite Y coordinate + beq SetVFbr + lda $02 ;load vertical adder we got from position loader + lsr $05 ;shift LSB of mirror data one more time + bcs AddVA ;if carry was set, skip this part + eor #$ff + adc #$01 ;otherwise get two's compliment of second part +AddVA: clc ;add vertical coordinate relative to screen to + adc Enemy_Rel_YPos ;the second data, modified or otherwise +SetVFbr: sta Sprite_Y_Position,y ;store as Y coordinate here + sta $07 ;also store here for now + +FirebarCollision: + jsr DrawFirebar ;run sub here to draw current tile of firebar + tya ;return OAM data offset and save + pha ;to the stack for now + lda StarInvincibleTimer ;if star mario invincibility timer + ora TimerControl ;or master timer controls set + bne NoColFB ;then skip all of this + sta $05 ;otherwise initialize counter + ldy Player_Y_HighPos + dey ;if player's vertical high byte offscreen, + bne NoColFB ;skip all of this + ldy Player_Y_Position ;get player's vertical position + lda PlayerSize ;get player's size + bne AdjSm ;if player small, branch to alter variables + lda CrouchingFlag + beq BigJp ;if player big and not crouching, jump ahead +AdjSm: inc $05 ;if small or big but crouching, execute this part + inc $05 ;first increment our counter twice (setting $02 as flag) + tya + clc ;then add 24 pixels to the player's + adc #$18 ;vertical coordinate + tay +BigJp: tya ;get vertical coordinate, altered or otherwise, from Y +FBCLoop: sec ;subtract vertical position of firebar + sbc $07 ;from the vertical coordinate of the player + bpl ChkVFBD ;if player lower on the screen than firebar, + eor #$ff ;skip two's compliment part + clc ;otherwise get two's compliment + adc #$01 +ChkVFBD: cmp #$08 ;if difference => 8 pixels, skip ahead of this part + bcs Chk2Ofs + lda $06 ;if firebar on far right on the screen, skip this, + cmp #$f0 ;because, really, what's the point? + bcs Chk2Ofs + lda Sprite_X_Position+4 ;get OAM X coordinate for sprite #1 + clc + adc #$04 ;add four pixels + sta $04 ;store here + sec ;subtract horizontal coordinate of firebar + sbc $06 ;from the X coordinate of player's sprite 1 + bpl ChkFBCl ;if modded X coordinate to the right of firebar + eor #$ff ;skip two's compliment part + clc ;otherwise get two's compliment + adc #$01 +ChkFBCl: cmp #$08 ;if difference < 8 pixels, collision, thus branch + bcc ChgSDir ;to process +Chk2Ofs: lda $05 ;if value of $02 was set earlier for whatever reason, + cmp #$02 ;branch to increment OAM offset and leave, no collision + beq NoColFB + ldy $05 ;otherwise get temp here and use as offset + lda Player_Y_Position + clc + adc FirebarYPos,y ;add value loaded with offset to player's vertical coordinate + inc $05 ;then increment temp and jump back + jmp FBCLoop +ChgSDir: ldx #$01 ;set movement direction by default + lda $04 ;if OAM X coordinate of player's sprite 1 + cmp $06 ;is greater than horizontal coordinate of firebar + bcs SetSDir ;then do not alter movement direction + inx ;otherwise increment it +SetSDir: stx Enemy_MovingDir ;store movement direction here + ldx #$00 + lda $00 ;save value written to $00 to stack + pha + jsr InjurePlayer ;perform sub to hurt or kill player + pla + sta $00 ;get value of $00 from stack +NoColFB: pla ;get OAM data offset + clc ;add four to it and save + adc #$04 + sta $06 + ldx ObjectOffset ;get enemy object buffer offset and leave + rts + +GetFirebarPosition: + pha ;save high byte of spinstate to the stack + and #%00001111 ;mask out low nybble + cmp #$09 + bcc GetHAdder ;if lower than $09, branch ahead + eor #%00001111 ;otherwise get two's compliment to oscillate + clc + adc #$01 +GetHAdder: sta $01 ;store result, modified or not, here + ldy $00 ;load number of firebar ball where we're at + lda FirebarTblOffsets,y ;load offset to firebar position data + clc + adc $01 ;add oscillated high byte of spinstate + tay ;to offset here and use as new offset + lda FirebarPosLookupTbl,y ;get data here and store as horizontal adder + sta $01 + pla ;pull whatever was in A from the stack + pha ;save it again because we still need it + clc + adc #$08 ;add eight this time, to get vertical adder + and #%00001111 ;mask out high nybble + cmp #$09 ;if lower than $09, branch ahead + bcc GetVAdder + eor #%00001111 ;otherwise get two's compliment + clc + adc #$01 +GetVAdder: sta $02 ;store result here + ldy $00 + lda FirebarTblOffsets,y ;load offset to firebar position data again + clc + adc $02 ;this time add value in $02 to offset here and use as offset + tay + lda FirebarPosLookupTbl,y ;get data here and store as vertica adder + sta $02 + pla ;pull out whatever was in A one last time + lsr ;divide by eight or shift three to the right + lsr + lsr + tay ;use as offset + lda FirebarMirrorData,y ;load mirroring data here + sta $03 ;store + rts + +;-------------------------------- + +PRandomSubtracter: + .db $f8, $a0, $70, $bd, $00 + +FlyCCBPriority: + .db $20, $20, $20, $00, $00 + +MoveFlyingCheepCheep: + lda Enemy_State,x ;check cheep-cheep's enemy state + and #%00100000 ;for d5 set + beq FlyCC ;branch to continue code if not set + lda #$00 + sta Enemy_SprAttrib,x ;otherwise clear sprite attributes + jmp MoveJ_EnemyVertically ;and jump to move defeated cheep-cheep downwards +FlyCC: jsr MoveEnemyHorizontally ;move cheep-cheep horizontally based on speed and force + ldy #$0d ;set vertical movement amount + lda #$05 ;set maximum speed + jsr SetXMoveAmt ;branch to impose gravity on flying cheep-cheep + lda Enemy_Y_MoveForce,x + lsr ;get vertical movement force and + lsr ;move high nybble to low + lsr + lsr + tay ;save as offset (note this tends to go into reach of code) + lda Enemy_Y_Position,x ;get vertical position + sec ;subtract pseudorandom value based on offset from position + sbc PRandomSubtracter,y + bpl AddCCF ;if result within top half of screen, skip this part + eor #$ff + clc ;otherwise get two's compliment + adc #$01 +AddCCF: cmp #$08 ;if result or two's compliment greater than eight, + bcs BPGet ;skip to the end without changing movement force + lda Enemy_Y_MoveForce,x + clc + adc #$10 ;otherwise add to it + sta Enemy_Y_MoveForce,x + lsr ;move high nybble to low again + lsr + lsr + lsr + tay +BPGet: lda FlyCCBPriority,y ;load bg priority data and store (this is very likely + sta Enemy_SprAttrib,x ;broken or residual code, value is overwritten before + rts ;drawing it next frame), then leave + +;-------------------------------- +;$00 - used to hold horizontal difference +;$01-$03 - used to hold difference adjusters + +LakituDiffAdj: + .db $15, $30, $40 + +MoveLakitu: + lda Enemy_State,x ;check lakitu's enemy state + and #%00100000 ;for d5 set + beq ChkLS ;if not set, continue with code + jmp MoveD_EnemyVertically ;otherwise jump to move defeated lakitu downwards +ChkLS: lda Enemy_State,x ;if lakitu's enemy state not set at all, + beq Fr12S ;go ahead and continue with code + lda #$00 + sta LakituMoveDirection,x ;otherwise initialize moving direction to move to left + sta EnemyFrenzyBuffer ;initialize frenzy buffer + lda #$10 + bne SetLSpd ;load horizontal speed and do unconditional branch +Fr12S: lda #Spiny + sta EnemyFrenzyBuffer ;set spiny identifier in frenzy buffer + ldy #$02 +LdLDa: lda LakituDiffAdj,y ;load values + sta $0001,y ;store in zero page + dey + bpl LdLDa ;do this until all values are stired + jsr PlayerLakituDiff ;execute sub to set speed and create spinys +SetLSpd: sta LakituMoveSpeed,x ;set movement speed returned from sub + ldy #$01 ;set moving direction to right by default + lda LakituMoveDirection,x + and #$01 ;get LSB of moving direction + bne SetLMov ;if set, branch to the end to use moving direction + lda LakituMoveSpeed,x + eor #$ff ;get two's compliment of moving speed + clc + adc #$01 + sta LakituMoveSpeed,x ;store as new moving speed + iny ;increment moving direction to left +SetLMov: sty Enemy_MovingDir,x ;store moving direction + jmp MoveEnemyHorizontally ;move lakitu horizontally + +PlayerLakituDiff: + ldy #$00 ;set Y for default value + jsr PlayerEnemyDiff ;get horizontal difference between enemy and player + bpl ChkLakDif ;branch if enemy is to the right of the player + iny ;increment Y for left of player + lda $00 + eor #$ff ;get two's compliment of low byte of horizontal difference + clc + adc #$01 ;store two's compliment as horizontal difference + sta $00 +ChkLakDif: lda $00 ;get low byte of horizontal difference + cmp #$3c ;if within a certain distance of player, branch + bcc ChkPSpeed + lda #$3c ;otherwise set maximum distance + sta $00 + lda Enemy_ID,x ;check if lakitu is in our current enemy slot + cmp #Lakitu + bne ChkPSpeed ;if not, branch elsewhere + tya ;compare contents of Y, now in A + cmp LakituMoveDirection,x ;to what is being used as horizontal movement direction + beq ChkPSpeed ;if moving toward the player, branch, do not alter + lda LakituMoveDirection,x ;if moving to the left beyond maximum distance, + beq SetLMovD ;branch and alter without delay + dec LakituMoveSpeed,x ;decrement horizontal speed + lda LakituMoveSpeed,x ;if horizontal speed not yet at zero, branch to leave + bne ExMoveLak +SetLMovD: tya ;set horizontal direction depending on horizontal + sta LakituMoveDirection,x ;difference between enemy and player if necessary +ChkPSpeed: lda $00 + and #%00111100 ;mask out all but four bits in the middle + lsr ;divide masked difference by four + lsr + sta $00 ;store as new value + ldy #$00 ;init offset + lda Player_X_Speed + beq SubDifAdj ;if player not moving horizontally, branch + lda ScrollAmount + beq SubDifAdj ;if scroll speed not set, branch to same place + iny ;otherwise increment offset + lda Player_X_Speed + cmp #$19 ;if player not running, branch + bcc ChkSpinyO + lda ScrollAmount + cmp #$02 ;if scroll speed below a certain amount, branch + bcc ChkSpinyO ;to same place + iny ;otherwise increment once more +ChkSpinyO: lda Enemy_ID,x ;check for spiny object + cmp #Spiny + bne ChkEmySpd ;branch if not found + lda Player_X_Speed ;if player not moving, skip this part + bne SubDifAdj +ChkEmySpd: lda Enemy_Y_Speed,x ;check vertical speed + bne SubDifAdj ;branch if nonzero + ldy #$00 ;otherwise reinit offset +SubDifAdj: lda $0001,y ;get one of three saved values from earlier + ldy $00 ;get saved horizontal difference +SPixelLak: sec ;subtract one for each pixel of horizontal difference + sbc #$01 ;from one of three saved values + dey + bpl SPixelLak ;branch until all pixels are subtracted, to adjust difference +ExMoveLak: rts ;leave!!! + +;------------------------------------------------------------------------------------- +;$04-$05 - used to store name table address in little endian order + +BridgeCollapseData: + .db $1a ;axe + .db $58 ;chain + .db $98, $96, $94, $92, $90, $8e, $8c ;bridge + .db $8a, $88, $86, $84, $82, $80 + +BridgeCollapse: + ldx BowserFront_Offset ;get enemy offset for bowser + lda Enemy_ID,x ;check enemy object identifier for bowser + cmp #Bowser ;if not found, branch ahead, + bne SetM2 ;metatile removal not necessary + stx ObjectOffset ;store as enemy offset here + lda Enemy_State,x ;if bowser in normal state, skip all of this + beq RemoveBridge + and #%01000000 ;if bowser's state has d6 clear, skip to silence music + beq SetM2 + lda Enemy_Y_Position,x ;check bowser's vertical coordinate + cmp #$e0 ;if bowser not yet low enough, skip this part ahead + bcc MoveD_Bowser +SetM2: lda #Silence ;silence music + sta EventMusicQueue + inc OperMode_Task ;move onto next secondary mode in autoctrl mode + jmp KillAllEnemies ;jump to empty all enemy slots and then leave + +MoveD_Bowser: + jsr MoveEnemySlowVert ;do a sub to move bowser downwards + jmp BowserGfxHandler ;jump to draw bowser's front and rear, then leave + +RemoveBridge: + dec BowserFeetCounter ;decrement timer to control bowser's feet + bne NoBFall ;if not expired, skip all of this + lda #$04 + sta BowserFeetCounter ;otherwise, set timer now + lda BowserBodyControls + eor #$01 ;invert bit to control bowser's feet + sta BowserBodyControls + lda #$22 ;put high byte of name table address here for now + sta $05 + ldy BridgeCollapseOffset ;get bridge collapse offset here + lda BridgeCollapseData,y ;load low byte of name table address and store here + sta $04 + ldy VRAM_Buffer1_Offset ;increment vram buffer offset + iny + ldx #$0c ;set offset for tile data for sub to draw blank metatile + jsr RemBridge ;do sub here to remove bowser's bridge metatiles + ldx ObjectOffset ;get enemy offset + jsr MoveVOffset ;set new vram buffer offset + lda #Sfx_Blast ;load the fireworks/gunfire sound into the square 2 sfx + sta Square2SoundQueue ;queue while at the same time loading the brick + lda #Sfx_BrickShatter ;shatter sound into the noise sfx queue thus + sta NoiseSoundQueue ;producing the unique sound of the bridge collapsing + inc BridgeCollapseOffset ;increment bridge collapse offset + lda BridgeCollapseOffset + cmp #$0f ;if bridge collapse offset has not yet reached + bne NoBFall ;the end, go ahead and skip this part + jsr InitVStf ;initialize whatever vertical speed bowser has + lda #%01000000 + sta Enemy_State,x ;set bowser's state to one of defeated states (d6 set) + lda #Sfx_BowserFall + sta Square2SoundQueue ;play bowser defeat sound +NoBFall: jmp BowserGfxHandler ;jump to code that draws bowser + +;-------------------------------- + +PRandomRange: + .db $21, $41, $11, $31 + +RunBowser: + lda Enemy_State,x ;if d5 in enemy state is not set + and #%00100000 ;then branch elsewhere to run bowser + beq BowserControl + lda Enemy_Y_Position,x ;otherwise check vertical position + cmp #$e0 ;if above a certain point, branch to move defeated bowser + bcc MoveD_Bowser ;otherwise proceed to KillAllEnemies + +KillAllEnemies: + ldx #$04 ;start with last enemy slot +KillLoop: jsr EraseEnemyObject ;branch to kill enemy objects + dex ;move onto next enemy slot + bpl KillLoop ;do this until all slots are emptied + sta EnemyFrenzyBuffer ;empty frenzy buffer + ldx ObjectOffset ;get enemy object offset and leave + rts + +BowserControl: + lda #$00 + sta EnemyFrenzyBuffer ;empty frenzy buffer + lda TimerControl ;if master timer control not set, + beq ChkMouth ;skip jump and execute code here + jmp SkipToFB ;otherwise, jump over a bunch of code +ChkMouth: lda BowserBodyControls ;check bowser's mouth + bpl FeetTmr ;if bit clear, go ahead with code here + jmp HammerChk ;otherwise skip a whole section starting here +FeetTmr: dec BowserFeetCounter ;decrement timer to control bowser's feet + bne ResetMDr ;if not expired, skip this part + lda #$20 ;otherwise, reset timer + sta BowserFeetCounter + lda BowserBodyControls ;and invert bit used + eor #%00000001 ;to control bowser's feet + sta BowserBodyControls +ResetMDr: lda FrameCounter ;check frame counter + and #%00001111 ;if not on every sixteenth frame, skip + bne B_FaceP ;ahead to continue code + lda #$02 ;otherwise reset moving/facing direction every + sta Enemy_MovingDir,x ;sixteen frames +B_FaceP: lda EnemyFrameTimer,x ;if timer set here expired, + beq GetPRCmp ;branch to next section + jsr PlayerEnemyDiff ;get horizontal difference between player and bowser, + bpl GetPRCmp ;and branch if bowser to the right of the player + lda #$01 + sta Enemy_MovingDir,x ;set bowser to move and face to the right + lda #$02 + sta BowserMovementSpeed ;set movement speed + lda #$20 + sta EnemyFrameTimer,x ;set timer here + sta BowserFireBreathTimer ;set timer used for bowser's flame + lda Enemy_X_Position,x + cmp #$c8 ;if bowser to the right past a certain point, + bcs HammerChk ;skip ahead to some other section +GetPRCmp: lda FrameCounter ;get frame counter + and #%00000011 + bne HammerChk ;execute this code every fourth frame, otherwise branch + lda Enemy_X_Position,x + cmp BowserOrigXPos ;if bowser not at original horizontal position, + bne GetDToO ;branch to skip this part + lda PseudoRandomBitReg,x + and #%00000011 ;get pseudorandom offset + tay + lda PRandomRange,y ;load value using pseudorandom offset + sta MaxRangeFromOrigin ;and store here +GetDToO: lda Enemy_X_Position,x + clc ;add movement speed to bowser's horizontal + adc BowserMovementSpeed ;coordinate and save as new horizontal position + sta Enemy_X_Position,x + ldy Enemy_MovingDir,x + cpy #$01 ;if bowser moving and facing to the right, skip ahead + beq HammerChk + ldy #$ff ;set default movement speed here (move left) + sec ;get difference of current vs. original + sbc BowserOrigXPos ;horizontal position + bpl CompDToO ;if current position to the right of original, skip ahead + eor #$ff + clc ;get two's compliment + adc #$01 + ldy #$01 ;set alternate movement speed here (move right) +CompDToO: cmp MaxRangeFromOrigin ;compare difference with pseudorandom value + bcc HammerChk ;if difference < pseudorandom value, leave speed alone + sty BowserMovementSpeed ;otherwise change bowser's movement speed +HammerChk: lda EnemyFrameTimer,x ;if timer set here not expired yet, skip ahead to + bne MakeBJump ;some other section of code + jsr MoveEnemySlowVert ;otherwise start by moving bowser downwards + lda WorldNumber ;check world number + cmp #World6 + bcc SetHmrTmr ;if world 1-5, skip this part (not time to throw hammers yet) + lda FrameCounter + and #%00000011 ;check to see if it's time to execute sub + bne SetHmrTmr ;if not, skip sub, otherwise + jsr SpawnHammerObj ;execute sub on every fourth frame to spawn misc object (hammer) +SetHmrTmr: lda Enemy_Y_Position,x ;get current vertical position + cmp #$80 ;if still above a certain point + bcc ChkFireB ;then skip to world number check for flames + lda PseudoRandomBitReg,x + and #%00000011 ;get pseudorandom offset + tay + lda PRandomRange,y ;get value using pseudorandom offset + sta EnemyFrameTimer,x ;set for timer here +SkipToFB: jmp ChkFireB ;jump to execute flames code +MakeBJump: cmp #$01 ;if timer not yet about to expire, + bne ChkFireB ;skip ahead to next part + dec Enemy_Y_Position,x ;otherwise decrement vertical coordinate + jsr InitVStf ;initialize movement amount + lda #$fe + sta Enemy_Y_Speed,x ;set vertical speed to move bowser upwards +ChkFireB: lda WorldNumber ;check world number here + cmp #World8 ;world 8? + beq SpawnFBr ;if so, execute this part here + cmp #World6 ;world 6-7? + bcs BowserGfxHandler ;if so, skip this part here +SpawnFBr: lda BowserFireBreathTimer ;check timer here + bne BowserGfxHandler ;if not expired yet, skip all of this + lda #$20 + sta BowserFireBreathTimer ;set timer here + lda BowserBodyControls + eor #%10000000 ;invert bowser's mouth bit to open + sta BowserBodyControls ;and close bowser's mouth + bmi ChkFireB ;if bowser's mouth open, loop back + jsr SetFlameTimer ;get timing for bowser's flame + ldy SecondaryHardMode + beq SetFBTmr ;if secondary hard mode flag not set, skip this + sec + sbc #$10 ;otherwise subtract from value in A +SetFBTmr: sta BowserFireBreathTimer ;set value as timer here + lda #BowserFlame ;put bowser's flame identifier + sta EnemyFrenzyBuffer ;in enemy frenzy buffer + +;-------------------------------- + +BowserGfxHandler: + jsr ProcessBowserHalf ;do a sub here to process bowser's front + ldy #$10 ;load default value here to position bowser's rear + lda Enemy_MovingDir,x ;check moving direction + lsr + bcc CopyFToR ;if moving left, use default + ldy #$f0 ;otherwise load alternate positioning value here +CopyFToR: tya ;move bowser's rear object position value to A + clc + adc Enemy_X_Position,x ;add to bowser's front object horizontal coordinate + ldy DuplicateObj_Offset ;get bowser's rear object offset + sta Enemy_X_Position,y ;store A as bowser's rear horizontal coordinate + lda Enemy_Y_Position,x + clc ;add eight pixels to bowser's front object + adc #$08 ;vertical coordinate and store as vertical coordinate + sta Enemy_Y_Position,y ;for bowser's rear + lda Enemy_State,x + sta Enemy_State,y ;copy enemy state directly from front to rear + lda Enemy_MovingDir,x + sta Enemy_MovingDir,y ;copy moving direction also + lda ObjectOffset ;save enemy object offset of front to stack + pha + ldx DuplicateObj_Offset ;put enemy object offset of rear as current + stx ObjectOffset + lda #Bowser ;set bowser's enemy identifier + sta Enemy_ID,x ;store in bowser's rear object + jsr ProcessBowserHalf ;do a sub here to process bowser's rear + pla + sta ObjectOffset ;get original enemy object offset + tax + lda #$00 ;nullify bowser's front/rear graphics flag + sta BowserGfxFlag +ExBGfxH: rts ;leave! + +ProcessBowserHalf: + inc BowserGfxFlag ;increment bowser's graphics flag, then run subroutines + jsr RunRetainerObj ;to get offscreen bits, relative position and draw bowser (finally!) + lda Enemy_State,x + bne ExBGfxH ;if either enemy object not in normal state, branch to leave + lda #$0a + sta Enemy_BoundBoxCtrl,x ;set bounding box size control + jsr GetEnemyBoundBox ;get bounding box coordinates + jmp PlayerEnemyCollision ;do player-to-enemy collision detection + +;------------------------------------------------------------------------------------- +;$00 - used to hold movement force and tile number +;$01 - used to hold sprite attribute data + +FlameTimerData: + .db $bf, $40, $bf, $bf, $bf, $40, $40, $bf + +SetFlameTimer: + ldy BowserFlameTimerCtrl ;load counter as offset + inc BowserFlameTimerCtrl ;increment + lda BowserFlameTimerCtrl ;mask out all but 3 LSB + and #%00000111 ;to keep in range of 0-7 + sta BowserFlameTimerCtrl + lda FlameTimerData,y ;load value to be used then leave +ExFl: rts + +ProcBowserFlame: + lda TimerControl ;if master timer control flag set, + bne SetGfxF ;skip all of this + lda #$40 ;load default movement force + ldy SecondaryHardMode + beq SFlmX ;if secondary hard mode flag not set, use default + lda #$60 ;otherwise load alternate movement force to go faster +SFlmX: sta $00 ;store value here + lda Enemy_X_MoveForce,x + sec ;subtract value from movement force + sbc $00 + sta Enemy_X_MoveForce,x ;save new value + lda Enemy_X_Position,x + sbc #$01 ;subtract one from horizontal position to move + sta Enemy_X_Position,x ;to the left + lda Enemy_PageLoc,x + sbc #$00 ;subtract borrow from page location + sta Enemy_PageLoc,x + ldy BowserFlamePRandomOfs,x ;get some value here and use as offset + lda Enemy_Y_Position,x ;load vertical coordinate + cmp FlameYPosData,y ;compare against coordinate data using $0417,x as offset + beq SetGfxF ;if equal, branch and do not modify coordinate + clc + adc Enemy_Y_MoveForce,x ;otherwise add value here to coordinate and store + sta Enemy_Y_Position,x ;as new vertical coordinate +SetGfxF: jsr RelativeEnemyPosition ;get new relative coordinates + lda Enemy_State,x ;if bowser's flame not in normal state, + bne ExFl ;branch to leave + lda #$51 ;otherwise, continue + sta $00 ;write first tile number + ldy #$02 ;load attributes without vertical flip by default + lda FrameCounter + and #%00000010 ;invert vertical flip bit every 2 frames + beq FlmeAt ;if d1 not set, write default value + ldy #$82 ;otherwise write value with vertical flip bit set +FlmeAt: sty $01 ;set bowser's flame sprite attributes here + ldy Enemy_SprDataOffset,x ;get OAM data offset + ldx #$00 + +DrawFlameLoop: + lda Enemy_Rel_YPos ;get Y relative coordinate of current enemy object + sta Sprite_Y_Position,y ;write into Y coordinate of OAM data + lda $00 + sta Sprite_Tilenumber,y ;write current tile number into OAM data + inc $00 ;increment tile number to draw more bowser's flame + lda $01 + sta Sprite_Attributes,y ;write saved attributes into OAM data + lda Enemy_Rel_XPos + sta Sprite_X_Position,y ;write X relative coordinate of current enemy object + clc + adc #$08 + sta Enemy_Rel_XPos ;then add eight to it and store + iny + iny + iny + iny ;increment Y four times to move onto the next OAM + inx ;move onto the next OAM, and branch if three + cpx #$03 ;have not yet been done + bcc DrawFlameLoop + ldx ObjectOffset ;reload original enemy offset + jsr GetEnemyOffscreenBits ;get offscreen information + ldy Enemy_SprDataOffset,x ;get OAM data offset + lda Enemy_OffscreenBits ;get enemy object offscreen bits + lsr ;move d0 to carry and result to stack + pha + bcc M3FOfs ;branch if carry not set + lda #$f8 ;otherwise move sprite offscreen, this part likely + sta Sprite_Y_Position+12,y ;residual since flame is only made of three sprites +M3FOfs: pla ;get bits from stack + lsr ;move d1 to carry and move bits back to stack + pha + bcc M2FOfs ;branch if carry not set again + lda #$f8 ;otherwise move third sprite offscreen + sta Sprite_Y_Position+8,y +M2FOfs: pla ;get bits from stack again + lsr ;move d2 to carry and move bits back to stack again + pha + bcc M1FOfs ;branch if carry not set yet again + lda #$f8 ;otherwise move second sprite offscreen + sta Sprite_Y_Position+4,y +M1FOfs: pla ;get bits from stack one last time + lsr ;move d3 to carry + bcc ExFlmeD ;branch if carry not set one last time + lda #$f8 + sta Sprite_Y_Position,y ;otherwise move first sprite offscreen +ExFlmeD: rts ;leave + +;-------------------------------- + +RunFireworks: + dec ExplosionTimerCounter,x ;decrement explosion timing counter here + bne SetupExpl ;if not expired, skip this part + lda #$08 + sta ExplosionTimerCounter,x ;reset counter + inc ExplosionGfxCounter,x ;increment explosion graphics counter + lda ExplosionGfxCounter,x + cmp #$03 ;check explosion graphics counter + bcs FireworksSoundScore ;if at a certain point, branch to kill this object +SetupExpl: jsr RelativeEnemyPosition ;get relative coordinates of explosion + lda Enemy_Rel_YPos ;copy relative coordinates + sta Fireball_Rel_YPos ;from the enemy object to the fireball object + lda Enemy_Rel_XPos ;first vertical, then horizontal + sta Fireball_Rel_XPos + ldy Enemy_SprDataOffset,x ;get OAM data offset + lda ExplosionGfxCounter,x ;get explosion graphics counter + jsr DrawExplosion_Fireworks ;do a sub to draw the explosion then leave + rts + +FireworksSoundScore: + lda #$00 ;disable enemy buffer flag + sta Enemy_Flag,x + lda #Sfx_Blast ;play fireworks/gunfire sound + sta Square2SoundQueue + lda #$05 ;set part of score modifier for 500 points + sta DigitModifier+4 + jmp EndAreaPoints ;jump to award points accordingly then leave + +;-------------------------------- + +StarFlagYPosAdder: + .db $00, $00, $08, $08 + +StarFlagXPosAdder: + .db $00, $08, $00, $08 + +StarFlagTileData: + .db $54, $55, $56, $57 + +RunStarFlagObj: + lda #$00 ;initialize enemy frenzy buffer + sta EnemyFrenzyBuffer + lda StarFlagTaskControl ;check star flag object task number here + cmp #$05 ;if greater than 5, branch to exit + bcs StarFlagExit + jsr JumpEngine ;otherwise jump to appropriate sub + + .dw StarFlagExit + .dw GameTimerFireworks + .dw AwardGameTimerPoints + .dw RaiseFlagSetoffFWorks + .dw DelayToAreaEnd + +GameTimerFireworks: + ldy #$05 ;set default state for star flag object + lda GameTimerDisplay+2 ;get game timer's last digit + cmp #$01 + beq SetFWC ;if last digit of game timer set to 1, skip ahead + ldy #$03 ;otherwise load new value for state + cmp #$03 + beq SetFWC ;if last digit of game timer set to 3, skip ahead + ldy #$00 ;otherwise load one more potential value for state + cmp #$06 + beq SetFWC ;if last digit of game timer set to 6, skip ahead + lda #$ff ;otherwise set value for no fireworks +SetFWC: sta FireworksCounter ;set fireworks counter here + sty Enemy_State,x ;set whatever state we have in star flag object + +IncrementSFTask1: + inc StarFlagTaskControl ;increment star flag object task number + +StarFlagExit: + rts ;leave + +AwardGameTimerPoints: + lda GameTimerDisplay ;check all game timer digits for any intervals left + ora GameTimerDisplay+1 + ora GameTimerDisplay+2 + beq IncrementSFTask1 ;if no time left on game timer at all, branch to next task + lda FrameCounter + and #%00000100 ;check frame counter for d2 set (skip ahead + beq NoTTick ;for four frames every four frames) branch if not set + lda #Sfx_TimerTick + sta Square2SoundQueue ;load timer tick sound +NoTTick: ldy #$23 ;set offset here to subtract from game timer's last digit + lda #$ff ;set adder here to $ff, or -1, to subtract one + sta DigitModifier+5 ;from the last digit of the game timer + jsr DigitsMathRoutine ;subtract digit + lda #$05 ;set now to add 50 points + sta DigitModifier+5 ;per game timer interval subtracted + +EndAreaPoints: + ldy #$0b ;load offset for mario's score by default + lda CurrentPlayer ;check player on the screen + beq ELPGive ;if mario, do not change + ldy #$11 ;otherwise load offset for luigi's score +ELPGive: jsr DigitsMathRoutine ;award 50 points per game timer interval + lda CurrentPlayer ;get player on the screen (or 500 points per + asl ;fireworks explosion if branched here from there) + asl ;shift to high nybble + asl + asl + ora #%00000100 ;add four to set nybble for game timer + jmp UpdateNumber ;jump to print the new score and game timer + +RaiseFlagSetoffFWorks: + lda Enemy_Y_Position,x ;check star flag's vertical position + cmp #$72 ;against preset value + bcc SetoffF ;if star flag higher vertically, branch to other code + dec Enemy_Y_Position,x ;otherwise, raise star flag by one pixel + jmp DrawStarFlag ;and skip this part here +SetoffF: lda FireworksCounter ;check fireworks counter + beq DrawFlagSetTimer ;if no fireworks left to go off, skip this part + bmi DrawFlagSetTimer ;if no fireworks set to go off, skip this part + lda #Fireworks + sta EnemyFrenzyBuffer ;otherwise set fireworks object in frenzy queue + +DrawStarFlag: + jsr RelativeEnemyPosition ;get relative coordinates of star flag + ldy Enemy_SprDataOffset,x ;get OAM data offset + ldx #$03 ;do four sprites +DSFLoop: lda Enemy_Rel_YPos ;get relative vertical coordinate + clc + adc StarFlagYPosAdder,x ;add Y coordinate adder data + sta Sprite_Y_Position,y ;store as Y coordinate + lda StarFlagTileData,x ;get tile number + sta Sprite_Tilenumber,y ;store as tile number + lda #$22 ;set palette and background priority bits + sta Sprite_Attributes,y ;store as attributes + lda Enemy_Rel_XPos ;get relative horizontal coordinate + clc + adc StarFlagXPosAdder,x ;add X coordinate adder data + sta Sprite_X_Position,y ;store as X coordinate + iny + iny ;increment OAM data offset four bytes + iny ;for next sprite + iny + dex ;move onto next sprite + bpl DSFLoop ;do this until all sprites are done + ldx ObjectOffset ;get enemy object offset and leave + rts + +DrawFlagSetTimer: + jsr DrawStarFlag ;do sub to draw star flag + lda #$06 + sta EnemyIntervalTimer,x ;set interval timer here + +IncrementSFTask2: + inc StarFlagTaskControl ;move onto next task + rts + +DelayToAreaEnd: + jsr DrawStarFlag ;do sub to draw star flag + lda EnemyIntervalTimer,x ;if interval timer set in previous task + bne StarFlagExit2 ;not yet expired, branch to leave + lda EventMusicBuffer ;if event music buffer empty, + beq IncrementSFTask2 ;branch to increment task + +StarFlagExit2: + rts ;otherwise leave + +;-------------------------------- +;$00 - used to store horizontal difference between player and piranha plant + +MovePiranhaPlant: + lda Enemy_State,x ;check enemy state + bne PutinPipe ;if set at all, branch to leave + lda EnemyFrameTimer,x ;check enemy's timer here + bne PutinPipe ;branch to end if not yet expired + lda PiranhaPlant_MoveFlag,x ;check movement flag + bne SetupToMovePPlant ;if moving, skip to part ahead + lda PiranhaPlant_Y_Speed,x ;if currently rising, branch + bmi ReversePlantSpeed ;to move enemy upwards out of pipe + jsr PlayerEnemyDiff ;get horizontal difference between player and + bpl ChkPlayerNearPipe ;piranha plant, and branch if enemy to right of player + lda $00 ;otherwise get saved horizontal difference + eor #$ff + clc ;and change to two's compliment + adc #$01 + sta $00 ;save as new horizontal difference + +ChkPlayerNearPipe: + lda $00 ;get saved horizontal difference + cmp #$21 + bcc PutinPipe ;if player within a certain distance, branch to leave + +ReversePlantSpeed: + lda PiranhaPlant_Y_Speed,x ;get vertical speed + eor #$ff + clc ;change to two's compliment + adc #$01 + sta PiranhaPlant_Y_Speed,x ;save as new vertical speed + inc PiranhaPlant_MoveFlag,x ;increment to set movement flag + +SetupToMovePPlant: + lda PiranhaPlantDownYPos,x ;get original vertical coordinate (lowest point) + ldy PiranhaPlant_Y_Speed,x ;get vertical speed + bpl RiseFallPiranhaPlant ;branch if moving downwards + lda PiranhaPlantUpYPos,x ;otherwise get other vertical coordinate (highest point) + +RiseFallPiranhaPlant: + sta $00 ;save vertical coordinate here + lda FrameCounter ;get frame counter + lsr + bcc PutinPipe ;branch to leave if d0 set (execute code every other frame) + lda TimerControl ;get master timer control + bne PutinPipe ;branch to leave if set (likely not necessary) + lda Enemy_Y_Position,x ;get current vertical coordinate + clc + adc PiranhaPlant_Y_Speed,x ;add vertical speed to move up or down + sta Enemy_Y_Position,x ;save as new vertical coordinate + cmp $00 ;compare against low or high coordinate + bne PutinPipe ;branch to leave if not yet reached + lda #$00 + sta PiranhaPlant_MoveFlag,x ;otherwise clear movement flag + lda #$40 + sta EnemyFrameTimer,x ;set timer to delay piranha plant movement + +PutinPipe: + lda #%00100000 ;set background priority bit in sprite + sta Enemy_SprAttrib,x ;attributes to give illusion of being inside pipe + rts ;then leave + +;------------------------------------------------------------------------------------- +;$07 - spinning speed + +FirebarSpin: + sta $07 ;save spinning speed here + lda FirebarSpinDirection,x ;check spinning direction + bne SpinCounterClockwise ;if moving counter-clockwise, branch to other part + ldy #$18 ;possibly residual ldy + lda FirebarSpinState_Low,x + clc ;add spinning speed to what would normally be + adc $07 ;the horizontal speed + sta FirebarSpinState_Low,x + lda FirebarSpinState_High,x ;add carry to what would normally be the vertical speed + adc #$00 + rts + +SpinCounterClockwise: + ldy #$08 ;possibly residual ldy + lda FirebarSpinState_Low,x + sec ;subtract spinning speed to what would normally be + sbc $07 ;the horizontal speed + sta FirebarSpinState_Low,x + lda FirebarSpinState_High,x ;add carry to what would normally be the vertical speed + sbc #$00 + rts + +;------------------------------------------------------------------------------------- +;$00 - used to hold collision flag, Y movement force + 5 or low byte of name table for rope +;$01 - used to hold high byte of name table for rope +;$02 - used to hold page location of rope + +BalancePlatform: + lda Enemy_Y_HighPos,x ;check high byte of vertical position + cmp #$03 + bne DoBPl + jmp EraseEnemyObject ;if far below screen, kill the object +DoBPl: lda Enemy_State,x ;get object's state (set to $ff or other platform offset) + bpl CheckBalPlatform ;if doing other balance platform, branch to leave + rts + +CheckBalPlatform: + tay ;save offset from state as Y + lda PlatformCollisionFlag,x ;get collision flag of platform + sta $00 ;store here + lda Enemy_MovingDir,x ;get moving direction + beq ChkForFall + jmp PlatformFall ;if set, jump here + +ChkForFall: + lda #$2d ;check if platform is above a certain point + cmp Enemy_Y_Position,x + bcc ChkOtherForFall ;if not, branch elsewhere + cpy $00 ;if collision flag is set to same value as + beq MakePlatformFall ;enemy state, branch to make platforms fall + clc + adc #$02 ;otherwise add 2 pixels to vertical position + sta Enemy_Y_Position,x ;of current platform and branch elsewhere + jmp StopPlatforms ;to make platforms stop + +MakePlatformFall: + jmp InitPlatformFall ;make platforms fall + +ChkOtherForFall: + cmp Enemy_Y_Position,y ;check if other platform is above a certain point + bcc ChkToMoveBalPlat ;if not, branch elsewhere + cpx $00 ;if collision flag is set to same value as + beq MakePlatformFall ;enemy state, branch to make platforms fall + clc + adc #$02 ;otherwise add 2 pixels to vertical position + sta Enemy_Y_Position,y ;of other platform and branch elsewhere + jmp StopPlatforms ;jump to stop movement and do not return + +ChkToMoveBalPlat: + lda Enemy_Y_Position,x ;save vertical position to stack + pha + lda PlatformCollisionFlag,x ;get collision flag + bpl ColFlg ;branch if collision + lda Enemy_Y_MoveForce,x + clc ;add $05 to contents of moveforce, whatever they be + adc #$05 + sta $00 ;store here + lda Enemy_Y_Speed,x + adc #$00 ;add carry to vertical speed + bmi PlatDn ;branch if moving downwards + bne PlatUp ;branch elsewhere if moving upwards + lda $00 + cmp #$0b ;check if there's still a little force left + bcc PlatSt ;if not enough, branch to stop movement + bcs PlatUp ;otherwise keep branch to move upwards +ColFlg: cmp ObjectOffset ;if collision flag matches + beq PlatDn ;current enemy object offset, branch +PlatUp: jsr MovePlatformUp ;do a sub to move upwards + jmp DoOtherPlatform ;jump ahead to remaining code +PlatSt: jsr StopPlatforms ;do a sub to stop movement + jmp DoOtherPlatform ;jump ahead to remaining code +PlatDn: jsr MovePlatformDown ;do a sub to move downwards + +DoOtherPlatform: + ldy Enemy_State,x ;get offset of other platform + pla ;get old vertical coordinate from stack + sec + sbc Enemy_Y_Position,x ;get difference of old vs. new coordinate + clc + adc Enemy_Y_Position,y ;add difference to vertical coordinate of other + sta Enemy_Y_Position,y ;platform to move it in the opposite direction + lda PlatformCollisionFlag,x ;if no collision, skip this part here + bmi DrawEraseRope + tax ;put offset which collision occurred here + jsr PositionPlayerOnVPlat ;and use it to position player accordingly + +DrawEraseRope: + ldy ObjectOffset ;get enemy object offset + lda Enemy_Y_Speed,y ;check to see if current platform is + ora Enemy_Y_MoveForce,y ;moving at all + beq ExitRp ;if not, skip all of this and branch to leave + ldx VRAM_Buffer1_Offset ;get vram buffer offset + cpx #$20 ;if offset beyond a certain point, go ahead + bcs ExitRp ;and skip this, branch to leave + lda Enemy_Y_Speed,y + pha ;save two copies of vertical speed to stack + pha + jsr SetupPlatformRope ;do a sub to figure out where to put new bg tiles + lda $01 ;write name table address to vram buffer + sta VRAM_Buffer1,x ;first the high byte, then the low + lda $00 + sta VRAM_Buffer1+1,x + lda #$02 ;set length for 2 bytes + sta VRAM_Buffer1+2,x + lda Enemy_Y_Speed,y ;if platform moving upwards, branch + bmi EraseR1 ;to do something else + lda #$a2 + sta VRAM_Buffer1+3,x ;otherwise put tile numbers for left + lda #$a3 ;and right sides of rope in vram buffer + sta VRAM_Buffer1+4,x + jmp OtherRope ;jump to skip this part +EraseR1: lda #$24 ;put blank tiles in vram buffer + sta VRAM_Buffer1+3,x ;to erase rope + sta VRAM_Buffer1+4,x + +OtherRope: + lda Enemy_State,y ;get offset of other platform from state + tay ;use as Y here + pla ;pull second copy of vertical speed from stack + eor #$ff ;invert bits to reverse speed + jsr SetupPlatformRope ;do sub again to figure out where to put bg tiles + lda $01 ;write name table address to vram buffer + sta VRAM_Buffer1+5,x ;this time we're doing putting tiles for + lda $00 ;the other platform + sta VRAM_Buffer1+6,x + lda #$02 + sta VRAM_Buffer1+7,x ;set length again for 2 bytes + pla ;pull first copy of vertical speed from stack + bpl EraseR2 ;if moving upwards (note inversion earlier), skip this + lda #$a2 + sta VRAM_Buffer1+8,x ;otherwise put tile numbers for left + lda #$a3 ;and right sides of rope in vram + sta VRAM_Buffer1+9,x ;transfer buffer + jmp EndRp ;jump to skip this part +EraseR2: lda #$24 ;put blank tiles in vram buffer + sta VRAM_Buffer1+8,x ;to erase rope + sta VRAM_Buffer1+9,x +EndRp: lda #$00 ;put null terminator at the end + sta VRAM_Buffer1+10,x + lda VRAM_Buffer1_Offset ;add ten bytes to the vram buffer offset + clc ;and store + adc #10 + sta VRAM_Buffer1_Offset +ExitRp: ldx ObjectOffset ;get enemy object buffer offset and leave + rts + +SetupPlatformRope: + pha ;save second/third copy to stack + lda Enemy_X_Position,y ;get horizontal coordinate + clc + adc #$08 ;add eight pixels + ldx SecondaryHardMode ;if secondary hard mode flag set, + bne GetLRp ;use coordinate as-is + clc + adc #$10 ;otherwise add sixteen more pixels +GetLRp: pha ;save modified horizontal coordinate to stack + lda Enemy_PageLoc,y + adc #$00 ;add carry to page location + sta $02 ;and save here + pla ;pull modified horizontal coordinate + and #%11110000 ;from the stack, mask out low nybble + lsr ;and shift three bits to the right + lsr + lsr + sta $00 ;store result here as part of name table low byte + ldx Enemy_Y_Position,y ;get vertical coordinate + pla ;get second/third copy of vertical speed from stack + bpl GetHRp ;skip this part if moving downwards or not at all + txa + clc + adc #$08 ;add eight to vertical coordinate and + tax ;save as X +GetHRp: txa ;move vertical coordinate to A + ldx VRAM_Buffer1_Offset ;get vram buffer offset + asl + rol ;rotate d7 to d0 and d6 into carry + pha ;save modified vertical coordinate to stack + rol ;rotate carry to d0, thus d7 and d6 are at 2 LSB + and #%00000011 ;mask out all bits but d7 and d6, then set + ora #%00100000 ;d5 to get appropriate high byte of name table + sta $01 ;address, then store + lda $02 ;get saved page location from earlier + and #$01 ;mask out all but LSB + asl + asl ;shift twice to the left and save with the + ora $01 ;rest of the bits of the high byte, to get + sta $01 ;the proper name table and the right place on it + pla ;get modified vertical coordinate from stack + and #%11100000 ;mask out low nybble and LSB of high nybble + clc + adc $00 ;add to horizontal part saved here + sta $00 ;save as name table low byte + lda Enemy_Y_Position,y + cmp #$e8 ;if vertical position not below the + bcc ExPRp ;bottom of the screen, we're done, branch to leave + lda $00 + and #%10111111 ;mask out d6 of low byte of name table address + sta $00 +ExPRp: rts ;leave! + +InitPlatformFall: + tya ;move offset of other platform from Y to X + tax + jsr GetEnemyOffscreenBits ;get offscreen bits + lda #$06 + jsr SetupFloateyNumber ;award 1000 points to player + lda Player_Rel_XPos + sta FloateyNum_X_Pos,x ;put floatey number coordinates where player is + lda Player_Y_Position + sta FloateyNum_Y_Pos,x + lda #$01 ;set moving direction as flag for + sta Enemy_MovingDir,x ;falling platforms + +StopPlatforms: + jsr InitVStf ;initialize vertical speed and low byte + sta Enemy_Y_Speed,y ;for both platforms and leave + sta Enemy_Y_MoveForce,y + rts + +PlatformFall: + tya ;save offset for other platform to stack + pha + jsr MoveFallingPlatform ;make current platform fall + pla + tax ;pull offset from stack and save to X + jsr MoveFallingPlatform ;make other platform fall + ldx ObjectOffset + lda PlatformCollisionFlag,x ;if player not standing on either platform, + bmi ExPF ;skip this part + tax ;transfer collision flag offset as offset to X + jsr PositionPlayerOnVPlat ;and position player appropriately +ExPF: ldx ObjectOffset ;get enemy object buffer offset and leave + rts + +;-------------------------------- + +YMovingPlatform: + lda Enemy_Y_Speed,x ;if platform moving up or down, skip ahead to + ora Enemy_Y_MoveForce,x ;check on other position + bne ChkYCenterPos + sta Enemy_YMF_Dummy,x ;initialize dummy variable + lda Enemy_Y_Position,x + cmp YPlatformTopYPos,x ;if current vertical position => top position, branch + bcs ChkYCenterPos ;ahead of all this + lda FrameCounter + and #%00000111 ;check for every eighth frame + bne SkipIY + inc Enemy_Y_Position,x ;increase vertical position every eighth frame +SkipIY: jmp ChkYPCollision ;skip ahead to last part + +ChkYCenterPos: + lda Enemy_Y_Position,x ;if current vertical position < central position, branch + cmp YPlatformCenterYPos,x ;to slow ascent/move downwards + bcc YMDown + jsr MovePlatformUp ;otherwise start slowing descent/moving upwards + jmp ChkYPCollision +YMDown: jsr MovePlatformDown ;start slowing ascent/moving downwards + +ChkYPCollision: + lda PlatformCollisionFlag,x ;if collision flag not set here, branch + bmi ExYPl ;to leave + jsr PositionPlayerOnVPlat ;otherwise position player appropriately +ExYPl: rts ;leave + +;-------------------------------- +;$00 - used as adder to position player hotizontally + +XMovingPlatform: + lda #$0e ;load preset maximum value for secondary counter + jsr XMoveCntr_Platform ;do a sub to increment counters for movement + jsr MoveWithXMCntrs ;do a sub to move platform accordingly, and return value + lda PlatformCollisionFlag,x ;if no collision with player, + bmi ExXMP ;branch ahead to leave + +PositionPlayerOnHPlat: + lda Player_X_Position + clc ;add saved value from second subroutine to + adc $00 ;current player's position to position + sta Player_X_Position ;player accordingly in horizontal position + lda Player_PageLoc ;get player's page location + ldy $00 ;check to see if saved value here is positive or negative + bmi PPHSubt ;if negative, branch to subtract + adc #$00 ;otherwise add carry to page location + jmp SetPVar ;jump to skip subtraction +PPHSubt: sbc #$00 ;subtract borrow from page location +SetPVar: sta Player_PageLoc ;save result to player's page location + sty Platform_X_Scroll ;put saved value from second sub here to be used later + jsr PositionPlayerOnVPlat ;position player vertically and appropriately +ExXMP: rts ;and we are done here + +;-------------------------------- + +DropPlatform: + lda PlatformCollisionFlag,x ;if no collision between platform and player + bmi ExDPl ;occurred, just leave without moving anything + jsr MoveDropPlatform ;otherwise do a sub to move platform down very quickly + jsr PositionPlayerOnVPlat ;do a sub to position player appropriately +ExDPl: rts ;leave + +;-------------------------------- +;$00 - residual value from sub + +RightPlatform: + jsr MoveEnemyHorizontally ;move platform with current horizontal speed, if any + sta $00 ;store saved value here (residual code) + lda PlatformCollisionFlag,x ;check collision flag, if no collision between player + bmi ExRPl ;and platform, branch ahead, leave speed unaltered + lda #$10 + sta Enemy_X_Speed,x ;otherwise set new speed (gets moving if motionless) + jsr PositionPlayerOnHPlat ;use saved value from earlier sub to position player +ExRPl: rts ;then leave + +;-------------------------------- + +MoveLargeLiftPlat: + jsr MoveLiftPlatforms ;execute common to all large and small lift platforms + jmp ChkYPCollision ;branch to position player correctly + +MoveSmallPlatform: + jsr MoveLiftPlatforms ;execute common to all large and small lift platforms + jmp ChkSmallPlatCollision ;branch to position player correctly + +MoveLiftPlatforms: + lda TimerControl ;if master timer control set, skip all of this + bne ExLiftP ;and branch to leave + lda Enemy_YMF_Dummy,x + clc ;add contents of movement amount to whatever's here + adc Enemy_Y_MoveForce,x + sta Enemy_YMF_Dummy,x + lda Enemy_Y_Position,x ;add whatever vertical speed is set to current + adc Enemy_Y_Speed,x ;vertical position plus carry to move up or down + sta Enemy_Y_Position,x ;and then leave + rts + +ChkSmallPlatCollision: + lda PlatformCollisionFlag,x ;get bounding box counter saved in collision flag + beq ExLiftP ;if none found, leave player position alone + jsr PositionPlayerOnS_Plat ;use to position player correctly +ExLiftP: rts ;then leave + +;------------------------------------------------------------------------------------- +;$00 - page location of extended left boundary +;$01 - extended left boundary position +;$02 - page location of extended right boundary +;$03 - extended right boundary position + +OffscreenBoundsCheck: + lda Enemy_ID,x ;check for cheep-cheep object + cmp #FlyingCheepCheep ;branch to leave if found + beq ExScrnBd + lda ScreenLeft_X_Pos ;get horizontal coordinate for left side of screen + ldy Enemy_ID,x + cpy #HammerBro ;check for hammer bro object + beq LimitB + cpy #PiranhaPlant ;check for piranha plant object + bne ExtendLB ;these two will be erased sooner than others if too far left +LimitB: adc #$38 ;add 56 pixels to coordinate if hammer bro or piranha plant +ExtendLB: sbc #$48 ;subtract 72 pixels regardless of enemy object + sta $01 ;store result here + lda ScreenLeft_PageLoc + sbc #$00 ;subtract borrow from page location of left side + sta $00 ;store result here + lda ScreenRight_X_Pos ;add 72 pixels to the right side horizontal coordinate + adc #$48 + sta $03 ;store result here + lda ScreenRight_PageLoc + adc #$00 ;then add the carry to the page location + sta $02 ;and store result here + lda Enemy_X_Position,x ;compare horizontal coordinate of the enemy object + cmp $01 ;to modified horizontal left edge coordinate to get carry + lda Enemy_PageLoc,x + sbc $00 ;then subtract it from the page coordinate of the enemy object + bmi TooFar ;if enemy object is too far left, branch to erase it + lda Enemy_X_Position,x ;compare horizontal coordinate of the enemy object + cmp $03 ;to modified horizontal right edge coordinate to get carry + lda Enemy_PageLoc,x + sbc $02 ;then subtract it from the page coordinate of the enemy object + bmi ExScrnBd ;if enemy object is on the screen, leave, do not erase enemy + lda Enemy_State,x ;if at this point, enemy is offscreen to the right, so check + cmp #HammerBro ;if in state used by spiny's egg, do not erase + beq ExScrnBd + cpy #PiranhaPlant ;if piranha plant, do not erase + beq ExScrnBd + cpy #FlagpoleFlagObject ;if flagpole flag, do not erase + beq ExScrnBd + cpy #StarFlagObject ;if star flag, do not erase + beq ExScrnBd + cpy #JumpspringObject ;if jumpspring, do not erase + beq ExScrnBd ;erase all others too far to the right +TooFar: jsr EraseEnemyObject ;erase object if necessary +ExScrnBd: rts ;leave + +;------------------------------------------------------------------------------------- + +;some unused space + .db $ff, $ff, $ff + +;------------------------------------------------------------------------------------- +;$01 - enemy buffer offset + +FireballEnemyCollision: + lda Fireball_State,x ;check to see if fireball state is set at all + beq ExitFBallEnemy ;branch to leave if not + asl + bcs ExitFBallEnemy ;branch to leave also if d7 in state is set + lda FrameCounter + lsr ;get LSB of frame counter + bcs ExitFBallEnemy ;branch to leave if set (do routine every other frame) + txa + asl ;multiply fireball offset by four + asl + clc + adc #$1c ;then add $1c or 28 bytes to it + tay ;to use fireball's bounding box coordinates + ldx #$04 + +FireballEnemyCDLoop: + stx $01 ;store enemy object offset here + tya + pha ;push fireball offset to the stack + lda Enemy_State,x + and #%00100000 ;check to see if d5 is set in enemy state + bne NoFToECol ;if so, skip to next enemy slot + lda Enemy_Flag,x ;check to see if buffer flag is set + beq NoFToECol ;if not, skip to next enemy slot + lda Enemy_ID,x ;check enemy identifier + cmp #$24 + bcc GoombaDie ;if < $24, branch to check further + cmp #$2b + bcc NoFToECol ;if in range $24-$2a, skip to next enemy slot +GoombaDie: cmp #Goomba ;check for goomba identifier + bne NotGoomba ;if not found, continue with code + lda Enemy_State,x ;otherwise check for defeated state + cmp #$02 ;if stomped or otherwise defeated, + bcs NoFToECol ;skip to next enemy slot +NotGoomba: lda EnemyOffscrBitsMasked,x ;if any masked offscreen bits set, + bne NoFToECol ;skip to next enemy slot + txa + asl ;otherwise multiply enemy offset by four + asl + clc + adc #$04 ;add 4 bytes to it + tax ;to use enemy's bounding box coordinates + jsr SprObjectCollisionCore ;do fireball-to-enemy collision detection + ldx ObjectOffset ;return fireball's original offset + bcc NoFToECol ;if carry clear, no collision, thus do next enemy slot + lda #%10000000 + sta Fireball_State,x ;set d7 in enemy state + ldx $01 ;get enemy offset + jsr HandleEnemyFBallCol ;jump to handle fireball to enemy collision +NoFToECol: pla ;pull fireball offset from stack + tay ;put it in Y + ldx $01 ;get enemy object offset + dex ;decrement it + bpl FireballEnemyCDLoop ;loop back until collision detection done on all enemies + +ExitFBallEnemy: + ldx ObjectOffset ;get original fireball offset and leave + rts + +BowserIdentities: + .db Goomba, GreenKoopa, BuzzyBeetle, Spiny, Lakitu, Bloober, HammerBro, Bowser + +HandleEnemyFBallCol: + jsr RelativeEnemyPosition ;get relative coordinate of enemy + ldx $01 ;get current enemy object offset + lda Enemy_Flag,x ;check buffer flag for d7 set + bpl ChkBuzzyBeetle ;branch if not set to continue + and #%00001111 ;otherwise mask out high nybble and + tax ;use low nybble as enemy offset + lda Enemy_ID,x + cmp #Bowser ;check enemy identifier for bowser + beq HurtBowser ;branch if found + ldx $01 ;otherwise retrieve current enemy offset + +ChkBuzzyBeetle: + lda Enemy_ID,x + cmp #BuzzyBeetle ;check for buzzy beetle + beq ExHCF ;branch if found to leave (buzzy beetles fireproof) + cmp #Bowser ;check for bowser one more time (necessary if d7 of flag was clear) + bne ChkOtherEnemies ;if not found, branch to check other enemies + +HurtBowser: + dec BowserHitPoints ;decrement bowser's hit points + bne ExHCF ;if bowser still has hit points, branch to leave + jsr InitVStf ;otherwise do sub to init vertical speed and movement force + sta Enemy_X_Speed,x ;initialize horizontal speed + sta EnemyFrenzyBuffer ;init enemy frenzy buffer + lda #$fe + sta Enemy_Y_Speed,x ;set vertical speed to make defeated bowser jump a little + ldy WorldNumber ;use world number as offset + lda BowserIdentities,y ;get enemy identifier to replace bowser with + sta Enemy_ID,x ;set as new enemy identifier + lda #$20 ;set A to use starting value for state + cpy #$03 ;check to see if using offset of 3 or more + bcs SetDBSte ;branch if so + ora #$03 ;otherwise add 3 to enemy state +SetDBSte: sta Enemy_State,x ;set defeated enemy state + lda #Sfx_BowserFall + sta Square2SoundQueue ;load bowser defeat sound + ldx $01 ;get enemy offset + lda #$09 ;award 5000 points to player for defeating bowser + bne EnemySmackScore ;unconditional branch to award points + +ChkOtherEnemies: + cmp #BulletBill_FrenzyVar + beq ExHCF ;branch to leave if bullet bill (frenzy variant) + cmp #Podoboo + beq ExHCF ;branch to leave if podoboo + cmp #$15 + bcs ExHCF ;branch to leave if identifier => $15 + +ShellOrBlockDefeat: + lda Enemy_ID,x ;check for piranha plant + cmp #PiranhaPlant + bne StnE ;branch if not found + lda Enemy_Y_Position,x + adc #$18 ;add 24 pixels to enemy object's vertical position + sta Enemy_Y_Position,x +StnE: jsr ChkToStunEnemies ;do yet another sub + lda Enemy_State,x + and #%00011111 ;mask out 2 MSB of enemy object's state + ora #%00100000 ;set d5 to defeat enemy and save as new state + sta Enemy_State,x + lda #$02 ;award 200 points by default + ldy Enemy_ID,x ;check for hammer bro + cpy #HammerBro + bne GoombaPoints ;branch if not found + lda #$06 ;award 1000 points for hammer bro + +GoombaPoints: + cpy #Goomba ;check for goomba + bne EnemySmackScore ;branch if not found + lda #$01 ;award 100 points for goomba + +EnemySmackScore: + jsr SetupFloateyNumber ;update necessary score variables + lda #Sfx_EnemySmack ;play smack enemy sound + sta Square1SoundQueue +ExHCF: rts ;and now let's leave + +;------------------------------------------------------------------------------------- + +PlayerHammerCollision: + lda FrameCounter ;get frame counter + lsr ;shift d0 into carry + bcc ExPHC ;branch to leave if d0 not set to execute every other frame + lda TimerControl ;if either master timer control + ora Misc_OffscreenBits ;or any offscreen bits for hammer are set, + bne ExPHC ;branch to leave + txa + asl ;multiply misc object offset by four + asl + clc + adc #$24 ;add 36 or $24 bytes to get proper offset + tay ;for misc object bounding box coordinates + jsr PlayerCollisionCore ;do player-to-hammer collision detection + ldx ObjectOffset ;get misc object offset + bcc ClHCol ;if no collision, then branch + lda Misc_Collision_Flag,x ;otherwise read collision flag + bne ExPHC ;if collision flag already set, branch to leave + lda #$01 + sta Misc_Collision_Flag,x ;otherwise set collision flag now + lda Misc_X_Speed,x + eor #$ff ;get two's compliment of + clc ;hammer's horizontal speed + adc #$01 + sta Misc_X_Speed,x ;set to send hammer flying the opposite direction + lda StarInvincibleTimer ;if star mario invincibility timer set, + bne ExPHC ;branch to leave + jmp InjurePlayer ;otherwise jump to hurt player, do not return +ClHCol: lda #$00 ;clear collision flag + sta Misc_Collision_Flag,x +ExPHC: rts + +;------------------------------------------------------------------------------------- + +HandlePowerUpCollision: + jsr EraseEnemyObject ;erase the power-up object + lda #$06 + jsr SetupFloateyNumber ;award 1000 points to player by default + lda #Sfx_PowerUpGrab + sta Square2SoundQueue ;play the power-up sound + lda PowerUpType ;check power-up type + cmp #$02 + bcc Shroom_Flower_PUp ;if mushroom or fire flower, branch + cmp #$03 + beq SetFor1Up ;if 1-up mushroom, branch + lda #$23 ;otherwise set star mario invincibility + sta StarInvincibleTimer ;timer, and load the star mario music + lda #StarPowerMusic ;into the area music queue, then leave + sta AreaMusicQueue + rts + +Shroom_Flower_PUp: + lda PlayerStatus ;if player status = small, branch + beq UpToSuper + cmp #$01 ;if player status not super, leave + bne NoPUp + ldx ObjectOffset ;get enemy offset, not necessary + lda #$02 ;set player status to fiery + sta PlayerStatus + jsr GetPlayerColors ;run sub to change colors of player + ldx ObjectOffset ;get enemy offset again, and again not necessary + lda #$0c ;set value to be used by subroutine tree (fiery) + jmp UpToFiery ;jump to set values accordingly + +SetFor1Up: + lda #$0b ;change 1000 points into 1-up instead + sta FloateyNum_Control,x ;and then leave + rts + +UpToSuper: + lda #$01 ;set player status to super + sta PlayerStatus + lda #$09 ;set value to be used by subroutine tree (super) + +UpToFiery: + ldy #$00 ;set value to be used as new player state + jsr SetPRout ;set values to stop certain things in motion +NoPUp: rts + +;-------------------------------- + +ResidualXSpdData: + .db $18, $e8 + +KickedShellXSpdData: + .db $30, $d0 + +DemotedKoopaXSpdData: + .db $08, $f8 + +PlayerEnemyCollision: + lda FrameCounter ;check counter for d0 set + lsr + bcs NoPUp ;if set, branch to leave + jsr CheckPlayerVertical ;if player object is completely offscreen or + bcs NoPECol ;if down past 224th pixel row, branch to leave + lda EnemyOffscrBitsMasked,x ;if current enemy is offscreen by any amount, + bne NoPECol ;go ahead and branch to leave + lda GameEngineSubroutine + cmp #$08 ;if not set to run player control routine + bne NoPECol ;on next frame, branch to leave + lda Enemy_State,x + and #%00100000 ;if enemy state has d5 set, branch to leave + bne NoPECol + jsr GetEnemyBoundBoxOfs ;get bounding box offset for current enemy object + jsr PlayerCollisionCore ;do collision detection on player vs. enemy + ldx ObjectOffset ;get enemy object buffer offset + bcs CheckForPUpCollision ;if collision, branch past this part here + lda Enemy_CollisionBits,x + and #%11111110 ;otherwise, clear d0 of current enemy object's + sta Enemy_CollisionBits,x ;collision bit +NoPECol: rts + +CheckForPUpCollision: + ldy Enemy_ID,x + cpy #PowerUpObject ;check for power-up object + bne EColl ;if not found, branch to next part + jmp HandlePowerUpCollision ;otherwise, unconditional jump backwards +EColl: lda StarInvincibleTimer ;if star mario invincibility timer expired, + beq HandlePECollisions ;perform task here, otherwise kill enemy like + jmp ShellOrBlockDefeat ;hit with a shell, or from beneath + +KickedShellPtsData: + .db $0a, $06, $04 + +HandlePECollisions: + lda Enemy_CollisionBits,x ;check enemy collision bits for d0 set + and #%00000001 ;or for being offscreen at all + ora EnemyOffscrBitsMasked,x + bne ExPEC ;branch to leave if either is true + lda #$01 + ora Enemy_CollisionBits,x ;otherwise set d0 now + sta Enemy_CollisionBits,x + cpy #Spiny ;branch if spiny + beq ChkForPlayerInjury + cpy #PiranhaPlant ;branch if piranha plant + beq InjurePlayer + cpy #Podoboo ;branch if podoboo + beq InjurePlayer + cpy #BulletBill_CannonVar ;branch if bullet bill + beq ChkForPlayerInjury + cpy #$15 ;branch if object => $15 + bcs InjurePlayer + lda AreaType ;branch if water type level + beq InjurePlayer + lda Enemy_State,x ;branch if d7 of enemy state was set + asl + bcs ChkForPlayerInjury + lda Enemy_State,x ;mask out all but 3 LSB of enemy state + and #%00000111 + cmp #$02 ;branch if enemy is in normal or falling state + bcc ChkForPlayerInjury + lda Enemy_ID,x ;branch to leave if goomba in defeated state + cmp #Goomba + beq ExPEC + lda #Sfx_EnemySmack ;play smack enemy sound + sta Square1SoundQueue + lda Enemy_State,x ;set d7 in enemy state, thus become moving shell + ora #%10000000 + sta Enemy_State,x + jsr EnemyFacePlayer ;set moving direction and get offset + lda KickedShellXSpdData,y ;load and set horizontal speed data with offset + sta Enemy_X_Speed,x + lda #$03 ;add three to whatever the stomp counter contains + clc ;to give points for kicking the shell + adc StompChainCounter + ldy EnemyIntervalTimer,x ;check shell enemy's timer + cpy #$03 ;if above a certain point, branch using the points + bcs KSPts ;data obtained from the stomp counter + 3 + lda KickedShellPtsData,y ;otherwise, set points based on proximity to timer expiration +KSPts: jsr SetupFloateyNumber ;set values for floatey number now +ExPEC: rts ;leave!!! + +ChkForPlayerInjury: + lda Player_Y_Speed ;check player's vertical speed + bmi ChkInj ;perform procedure below if player moving upwards + bne EnemyStomped ;or not at all, and branch elsewhere if moving downwards +ChkInj: lda Enemy_ID,x ;branch if enemy object < $07 + cmp #Bloober + bcc ChkETmrs + lda Player_Y_Position ;add 12 pixels to player's vertical position + clc + adc #$0c + cmp Enemy_Y_Position,x ;compare modified player's position to enemy's position + bcc EnemyStomped ;branch if this player's position above (less than) enemy's +ChkETmrs: lda StompTimer ;check stomp timer + bne EnemyStomped ;branch if set + lda InjuryTimer ;check to see if injured invincibility timer still + bne ExInjColRoutines ;counting down, and branch elsewhere to leave if so + lda Player_Rel_XPos + cmp Enemy_Rel_XPos ;if player's relative position to the left of enemy's + bcc TInjE ;relative position, branch here + jmp ChkEnemyFaceRight ;otherwise do a jump here +TInjE: lda Enemy_MovingDir,x ;if enemy moving towards the left, + cmp #$01 ;branch, otherwise do a jump here + bne InjurePlayer ;to turn the enemy around + jmp LInj + +InjurePlayer: + lda InjuryTimer ;check again to see if injured invincibility timer is + bne ExInjColRoutines ;at zero, and branch to leave if so + +ForceInjury: + ldx PlayerStatus ;check player's status + beq KillPlayer ;branch if small + sta PlayerStatus ;otherwise set player's status to small + lda #$08 + sta InjuryTimer ;set injured invincibility timer + asl + sta Square1SoundQueue ;play pipedown/injury sound + jsr GetPlayerColors ;change player's palette if necessary + lda #$0a ;set subroutine to run on next frame +SetKRout: ldy #$01 ;set new player state +SetPRout: sta GameEngineSubroutine ;load new value to run subroutine on next frame + sty Player_State ;store new player state + ldy #$ff + sty TimerControl ;set master timer control flag to halt timers + iny + sty ScrollAmount ;initialize scroll speed + +ExInjColRoutines: + ldx ObjectOffset ;get enemy offset and leave + rts + +KillPlayer: + stx Player_X_Speed ;halt player's horizontal movement by initializing speed + inx + stx EventMusicQueue ;set event music queue to death music + lda #$fc + sta Player_Y_Speed ;set new vertical speed + lda #$0b ;set subroutine to run on next frame + bne SetKRout ;branch to set player's state and other things + +StompedEnemyPtsData: + .db $02, $06, $05, $06 + +EnemyStomped: + lda Enemy_ID,x ;check for spiny, branch to hurt player + cmp #Spiny ;if found + beq InjurePlayer + lda #Sfx_EnemyStomp ;otherwise play stomp/swim sound + sta Square1SoundQueue + lda Enemy_ID,x + ldy #$00 ;initialize points data offset for stomped enemies + cmp #FlyingCheepCheep ;branch for cheep-cheep + beq EnemyStompedPts + cmp #BulletBill_FrenzyVar ;branch for either bullet bill object + beq EnemyStompedPts + cmp #BulletBill_CannonVar + beq EnemyStompedPts + cmp #Podoboo ;branch for podoboo (this branch is logically impossible + beq EnemyStompedPts ;for cpu to take due to earlier checking of podoboo) + iny ;increment points data offset + cmp #HammerBro ;branch for hammer bro + beq EnemyStompedPts + iny ;increment points data offset + cmp #Lakitu ;branch for lakitu + beq EnemyStompedPts + iny ;increment points data offset + cmp #Bloober ;branch if NOT bloober + bne ChkForDemoteKoopa + +EnemyStompedPts: + lda StompedEnemyPtsData,y ;load points data using offset in Y + jsr SetupFloateyNumber ;run sub to set floatey number controls + lda Enemy_MovingDir,x + pha ;save enemy movement direction to stack + jsr SetStun ;run sub to kill enemy + pla + sta Enemy_MovingDir,x ;return enemy movement direction from stack + lda #%00100000 + sta Enemy_State,x ;set d5 in enemy state + jsr InitVStf ;nullify vertical speed, physics-related thing, + sta Enemy_X_Speed,x ;and horizontal speed + lda #$fd ;set player's vertical speed, to give bounce + sta Player_Y_Speed + rts + +ChkForDemoteKoopa: + cmp #$09 ;branch elsewhere if enemy object < $09 + bcc HandleStompedShellE + and #%00000001 ;demote koopa paratroopas to ordinary troopas + sta Enemy_ID,x + ldy #$00 ;return enemy to normal state + sty Enemy_State,x + lda #$03 ;award 400 points to the player + jsr SetupFloateyNumber + jsr InitVStf ;nullify physics-related thing and vertical speed + jsr EnemyFacePlayer ;turn enemy around if necessary + lda DemotedKoopaXSpdData,y + sta Enemy_X_Speed,x ;set appropriate moving speed based on direction + jmp SBnce ;then move onto something else + +RevivalRateData: + .db $10, $0b + +HandleStompedShellE: + lda #$04 ;set defeated state for enemy + sta Enemy_State,x + inc StompChainCounter ;increment the stomp counter + lda StompChainCounter ;add whatever is in the stomp counter + clc ;to whatever is in the stomp timer + adc StompTimer + jsr SetupFloateyNumber ;award points accordingly + inc StompTimer ;increment stomp timer of some sort + ldy PrimaryHardMode ;check primary hard mode flag + lda RevivalRateData,y ;load timer setting according to flag + sta EnemyIntervalTimer,x ;set as enemy timer to revive stomped enemy +SBnce: lda #$fc ;set player's vertical speed for bounce + sta Player_Y_Speed ;and then leave!!! + rts + +ChkEnemyFaceRight: + lda Enemy_MovingDir,x ;check to see if enemy is moving to the right + cmp #$01 + bne LInj ;if not, branch + jmp InjurePlayer ;otherwise go back to hurt player +LInj: jsr EnemyTurnAround ;turn the enemy around, if necessary + jmp InjurePlayer ;go back to hurt player + + +EnemyFacePlayer: + ldy #$01 ;set to move right by default + jsr PlayerEnemyDiff ;get horizontal difference between player and enemy + bpl SFcRt ;if enemy is to the right of player, do not increment + iny ;otherwise, increment to set to move to the left +SFcRt: sty Enemy_MovingDir,x ;set moving direction here + dey ;then decrement to use as a proper offset + rts + +SetupFloateyNumber: + sta FloateyNum_Control,x ;set number of points control for floatey numbers + lda #$30 + sta FloateyNum_Timer,x ;set timer for floatey numbers + lda Enemy_Y_Position,x + sta FloateyNum_Y_Pos,x ;set vertical coordinate + lda Enemy_Rel_XPos + sta FloateyNum_X_Pos,x ;set horizontal coordinate and leave +ExSFN: rts + +;------------------------------------------------------------------------------------- +;$01 - used to hold enemy offset for second enemy + +SetBitsMask: + .db %10000000, %01000000, %00100000, %00010000, %00001000, %00000100, %00000010 + +ClearBitsMask: + .db %01111111, %10111111, %11011111, %11101111, %11110111, %11111011, %11111101 + +EnemiesCollision: + lda FrameCounter ;check counter for d0 set + lsr + bcc ExSFN ;if d0 not set, leave + lda AreaType + beq ExSFN ;if water area type, leave + lda Enemy_ID,x + cmp #$15 ;if enemy object => $15, branch to leave + bcs ExitECRoutine + cmp #Lakitu ;if lakitu, branch to leave + beq ExitECRoutine + cmp #PiranhaPlant ;if piranha plant, branch to leave + beq ExitECRoutine + lda EnemyOffscrBitsMasked,x ;if masked offscreen bits nonzero, branch to leave + bne ExitECRoutine + jsr GetEnemyBoundBoxOfs ;otherwise, do sub, get appropriate bounding box offset for + dex ;first enemy we're going to compare, then decrement for second + bmi ExitECRoutine ;branch to leave if there are no other enemies +ECLoop: stx $01 ;save enemy object buffer offset for second enemy here + tya ;save first enemy's bounding box offset to stack + pha + lda Enemy_Flag,x ;check enemy object enable flag + beq ReadyNextEnemy ;branch if flag not set + lda Enemy_ID,x + cmp #$15 ;check for enemy object => $15 + bcs ReadyNextEnemy ;branch if true + cmp #Lakitu + beq ReadyNextEnemy ;branch if enemy object is lakitu + cmp #PiranhaPlant + beq ReadyNextEnemy ;branch if enemy object is piranha plant + lda EnemyOffscrBitsMasked,x + bne ReadyNextEnemy ;branch if masked offscreen bits set + txa ;get second enemy object's bounding box offset + asl ;multiply by four, then add four + asl + clc + adc #$04 + tax ;use as new contents of X + jsr SprObjectCollisionCore ;do collision detection using the two enemies here + ldx ObjectOffset ;use first enemy offset for X + ldy $01 ;use second enemy offset for Y + bcc NoEnemyCollision ;if carry clear, no collision, branch ahead of this + lda Enemy_State,x + ora Enemy_State,y ;check both enemy states for d7 set + and #%10000000 + bne YesEC ;branch if at least one of them is set + lda Enemy_CollisionBits,y ;load first enemy's collision-related bits + and SetBitsMask,x ;check to see if bit connected to second enemy is + bne ReadyNextEnemy ;already set, and move onto next enemy slot if set + lda Enemy_CollisionBits,y + ora SetBitsMask,x ;if the bit is not set, set it now + sta Enemy_CollisionBits,y +YesEC: jsr ProcEnemyCollisions ;react according to the nature of collision + jmp ReadyNextEnemy ;move onto next enemy slot + +NoEnemyCollision: + lda Enemy_CollisionBits,y ;load first enemy's collision-related bits + and ClearBitsMask,x ;clear bit connected to second enemy + sta Enemy_CollisionBits,y ;then move onto next enemy slot + +ReadyNextEnemy: + pla ;get first enemy's bounding box offset from the stack + tay ;use as Y again + ldx $01 ;get and decrement second enemy's object buffer offset + dex + bpl ECLoop ;loop until all enemy slots have been checked + +ExitECRoutine: + ldx ObjectOffset ;get enemy object buffer offset + rts ;leave + +ProcEnemyCollisions: + lda Enemy_State,y ;check both enemy states for d5 set + ora Enemy_State,x + and #%00100000 ;if d5 is set in either state, or both, branch + bne ExitProcessEColl ;to leave and do nothing else at this point + lda Enemy_State,x + cmp #$06 ;if second enemy state < $06, branch elsewhere + bcc ProcSecondEnemyColl + lda Enemy_ID,x ;check second enemy identifier for hammer bro + cmp #HammerBro ;if hammer bro found in alt state, branch to leave + beq ExitProcessEColl + lda Enemy_State,y ;check first enemy state for d7 set + asl + bcc ShellCollisions ;branch if d7 is clear + lda #$06 + jsr SetupFloateyNumber ;award 1000 points for killing enemy + jsr ShellOrBlockDefeat ;then kill enemy, then load + ldy $01 ;original offset of second enemy + +ShellCollisions: + tya ;move Y to X + tax + jsr ShellOrBlockDefeat ;kill second enemy + ldx ObjectOffset + lda ShellChainCounter,x ;get chain counter for shell + clc + adc #$04 ;add four to get appropriate point offset + ldx $01 + jsr SetupFloateyNumber ;award appropriate number of points for second enemy + ldx ObjectOffset ;load original offset of first enemy + inc ShellChainCounter,x ;increment chain counter for additional enemies + +ExitProcessEColl: + rts ;leave!!! + +ProcSecondEnemyColl: + lda Enemy_State,y ;if first enemy state < $06, branch elsewhere + cmp #$06 + bcc MoveEOfs + lda Enemy_ID,y ;check first enemy identifier for hammer bro + cmp #HammerBro ;if hammer bro found in alt state, branch to leave + beq ExitProcessEColl + jsr ShellOrBlockDefeat ;otherwise, kill first enemy + ldy $01 + lda ShellChainCounter,y ;get chain counter for shell + clc + adc #$04 ;add four to get appropriate point offset + ldx ObjectOffset + jsr SetupFloateyNumber ;award appropriate number of points for first enemy + ldx $01 ;load original offset of second enemy + inc ShellChainCounter,x ;increment chain counter for additional enemies + rts ;leave!!! + +MoveEOfs: + tya ;move Y ($01) to X + tax + jsr EnemyTurnAround ;do the sub here using value from $01 + ldx ObjectOffset ;then do it again using value from $08 + +EnemyTurnAround: + lda Enemy_ID,x ;check for specific enemies + cmp #PiranhaPlant + beq ExTA ;if piranha plant, leave + cmp #Lakitu + beq ExTA ;if lakitu, leave + cmp #HammerBro + beq ExTA ;if hammer bro, leave + cmp #Spiny + beq RXSpd ;if spiny, turn it around + cmp #GreenParatroopaJump + beq RXSpd ;if green paratroopa, turn it around + cmp #$07 + bcs ExTA ;if any OTHER enemy object => $07, leave +RXSpd: lda Enemy_X_Speed,x ;load horizontal speed + eor #$ff ;get two's compliment for horizontal speed + tay + iny + sty Enemy_X_Speed,x ;store as new horizontal speed + lda Enemy_MovingDir,x + eor #%00000011 ;invert moving direction and store, then leave + sta Enemy_MovingDir,x ;thus effectively turning the enemy around +ExTA: rts ;leave!!! + +;------------------------------------------------------------------------------------- +;$00 - vertical position of platform + +LargePlatformCollision: + lda #$ff ;save value here + sta PlatformCollisionFlag,x + lda TimerControl ;check master timer control + bne ExLPC ;if set, branch to leave + lda Enemy_State,x ;if d7 set in object state, + bmi ExLPC ;branch to leave + lda Enemy_ID,x + cmp #$24 ;check enemy object identifier for + bne ChkForPlayerC_LargeP ;balance platform, branch if not found + lda Enemy_State,x + tax ;set state as enemy offset here + jsr ChkForPlayerC_LargeP ;perform code with state offset, then original offset, in X + +ChkForPlayerC_LargeP: + jsr CheckPlayerVertical ;figure out if player is below a certain point + bcs ExLPC ;or offscreen, branch to leave if true + txa + jsr GetEnemyBoundBoxOfsArg ;get bounding box offset in Y + lda Enemy_Y_Position,x ;store vertical coordinate in + sta $00 ;temp variable for now + txa ;send offset we're on to the stack + pha + jsr PlayerCollisionCore ;do player-to-platform collision detection + pla ;retrieve offset from the stack + tax + bcc ExLPC ;if no collision, branch to leave + jsr ProcLPlatCollisions ;otherwise collision, perform sub +ExLPC: ldx ObjectOffset ;get enemy object buffer offset and leave + rts + +;-------------------------------- +;$00 - counter for bounding boxes + +SmallPlatformCollision: + lda TimerControl ;if master timer control set, + bne ExSPC ;branch to leave + sta PlatformCollisionFlag,x ;otherwise initialize collision flag + jsr CheckPlayerVertical ;do a sub to see if player is below a certain point + bcs ExSPC ;or entirely offscreen, and branch to leave if true + lda #$02 + sta $00 ;load counter here for 2 bounding boxes + +ChkSmallPlatLoop: + ldx ObjectOffset ;get enemy object offset + jsr GetEnemyBoundBoxOfs ;get bounding box offset in Y + and #%00000010 ;if d1 of offscreen lower nybble bits was set + bne ExSPC ;then branch to leave + lda BoundingBox_UL_YPos,y ;check top of platform's bounding box for being + cmp #$20 ;above a specific point + bcc MoveBoundBox ;if so, branch, don't do collision detection + jsr PlayerCollisionCore ;otherwise, perform player-to-platform collision detection + bcs ProcSPlatCollisions ;skip ahead if collision + +MoveBoundBox: + lda BoundingBox_UL_YPos,y ;move bounding box vertical coordinates + clc ;128 pixels downwards + adc #$80 + sta BoundingBox_UL_YPos,y + lda BoundingBox_DR_YPos,y + clc + adc #$80 + sta BoundingBox_DR_YPos,y + dec $00 ;decrement counter we set earlier + bne ChkSmallPlatLoop ;loop back until both bounding boxes are checked +ExSPC: ldx ObjectOffset ;get enemy object buffer offset, then leave + rts + +;-------------------------------- + +ProcSPlatCollisions: + ldx ObjectOffset ;return enemy object buffer offset to X, then continue + +ProcLPlatCollisions: + lda BoundingBox_DR_YPos,y ;get difference by subtracting the top + sec ;of the player's bounding box from the bottom + sbc BoundingBox_UL_YPos ;of the platform's bounding box + cmp #$04 ;if difference too large or negative, + bcs ChkForTopCollision ;branch, do not alter vertical speed of player + lda Player_Y_Speed ;check to see if player's vertical speed is moving down + bpl ChkForTopCollision ;if so, don't mess with it + lda #$01 ;otherwise, set vertical + sta Player_Y_Speed ;speed of player to kill jump + +ChkForTopCollision: + lda BoundingBox_DR_YPos ;get difference by subtracting the top + sec ;of the platform's bounding box from the bottom + sbc BoundingBox_UL_YPos,y ;of the player's bounding box + cmp #$06 + bcs PlatformSideCollisions ;if difference not close enough, skip all of this + lda Player_Y_Speed + bmi PlatformSideCollisions ;if player's vertical speed moving upwards, skip this + lda $00 ;get saved bounding box counter from earlier + ldy Enemy_ID,x + cpy #$2b ;if either of the two small platform objects are found, + beq SetCollisionFlag ;regardless of which one, branch to use bounding box counter + cpy #$2c ;as contents of collision flag + beq SetCollisionFlag + txa ;otherwise use enemy object buffer offset + +SetCollisionFlag: + ldx ObjectOffset ;get enemy object buffer offset + sta PlatformCollisionFlag,x ;save either bounding box counter or enemy offset here + lda #$00 + sta Player_State ;set player state to normal then leave + rts + +PlatformSideCollisions: + lda #$01 ;set value here to indicate possible horizontal + sta $00 ;collision on left side of platform + lda BoundingBox_DR_XPos ;get difference by subtracting platform's left edge + sec ;from player's right edge + sbc BoundingBox_UL_XPos,y + cmp #$08 ;if difference close enough, skip all of this + bcc SideC + inc $00 ;otherwise increment value set here for right side collision + lda BoundingBox_DR_XPos,y ;get difference by subtracting player's left edge + clc ;from platform's right edge + sbc BoundingBox_UL_XPos + cmp #$09 ;if difference not close enough, skip subroutine + bcs NoSideC ;and instead branch to leave (no collision) +SideC: jsr ImpedePlayerMove ;deal with horizontal collision +NoSideC: ldx ObjectOffset ;return with enemy object buffer offset + rts + +;------------------------------------------------------------------------------------- + +PlayerPosSPlatData: + .db $80, $00 + +PositionPlayerOnS_Plat: + tay ;use bounding box counter saved in collision flag + lda Enemy_Y_Position,x ;for offset + clc ;add positioning data using offset to the vertical + adc PlayerPosSPlatData-1,y ;coordinate + .db $2c ;BIT instruction opcode + +PositionPlayerOnVPlat: + lda Enemy_Y_Position,x ;get vertical coordinate + ldy GameEngineSubroutine + cpy #$0b ;if certain routine being executed on this frame, + beq ExPlPos ;skip all of this + ldy Enemy_Y_HighPos,x + cpy #$01 ;if vertical high byte offscreen, skip this + bne ExPlPos + sec ;subtract 32 pixels from vertical coordinate + sbc #$20 ;for the player object's height + sta Player_Y_Position ;save as player's new vertical coordinate + tya + sbc #$00 ;subtract borrow and store as player's + sta Player_Y_HighPos ;new vertical high byte + lda #$00 + sta Player_Y_Speed ;initialize vertical speed and low byte of force + sta Player_Y_MoveForce ;and then leave +ExPlPos: rts + +;------------------------------------------------------------------------------------- + +CheckPlayerVertical: + lda Player_OffscreenBits ;if player object is completely offscreen + cmp #$f0 ;vertically, leave this routine + bcs ExCPV + ldy Player_Y_HighPos ;if player high vertical byte is not + dey ;within the screen, leave this routine + bne ExCPV + lda Player_Y_Position ;if on the screen, check to see how far down + cmp #$d0 ;the player is vertically +ExCPV: rts + +;------------------------------------------------------------------------------------- + +GetEnemyBoundBoxOfs: + lda ObjectOffset ;get enemy object buffer offset + +GetEnemyBoundBoxOfsArg: + asl ;multiply A by four, then add four + asl ;to skip player's bounding box + clc + adc #$04 + tay ;send to Y + lda Enemy_OffscreenBits ;get offscreen bits for enemy object + and #%00001111 ;save low nybble + cmp #%00001111 ;check for all bits set + rts + +;------------------------------------------------------------------------------------- +;$00-$01 - used to hold many values, essentially temp variables +;$04 - holds lower nybble of vertical coordinate from block buffer routine +;$eb - used to hold block buffer adder + +PlayerBGUpperExtent: + .db $20, $10 + +PlayerBGCollision: + lda DisableCollisionDet ;if collision detection disabled flag set, + bne ExPBGCol ;branch to leave + lda GameEngineSubroutine + cmp #$0b ;if running routine #11 or $0b + beq ExPBGCol ;branch to leave + cmp #$04 + bcc ExPBGCol ;if running routines $00-$03 branch to leave + lda #$01 ;load default player state for swimming + ldy SwimmingFlag ;if swimming flag set, + bne SetPSte ;branch ahead to set default state + lda Player_State ;if player in normal state, + beq SetFallS ;branch to set default state for falling + cmp #$03 + bne ChkOnScr ;if in any other state besides climbing, skip to next part +SetFallS: lda #$02 ;load default player state for falling +SetPSte: sta Player_State ;set whatever player state is appropriate +ChkOnScr: lda Player_Y_HighPos + cmp #$01 ;check player's vertical high byte for still on the screen + bne ExPBGCol ;branch to leave if not + lda #$ff + sta Player_CollisionBits ;initialize player's collision flag + lda Player_Y_Position + cmp #$cf ;check player's vertical coordinate + bcc ChkCollSize ;if not too close to the bottom of screen, continue +ExPBGCol: rts ;otherwise leave + +ChkCollSize: + ldy #$02 ;load default offset + lda CrouchingFlag + bne GBBAdr ;if player crouching, skip ahead + lda PlayerSize + bne GBBAdr ;if player small, skip ahead + dey ;otherwise decrement offset for big player not crouching + lda SwimmingFlag + bne GBBAdr ;if swimming flag set, skip ahead + dey ;otherwise decrement offset +GBBAdr: lda BlockBufferAdderData,y ;get value using offset + sta $eb ;store value here + tay ;put value into Y, as offset for block buffer routine + ldx PlayerSize ;get player's size as offset + lda CrouchingFlag + beq HeadChk ;if player not crouching, branch ahead + inx ;otherwise increment size as offset +HeadChk: lda Player_Y_Position ;get player's vertical coordinate + cmp PlayerBGUpperExtent,x ;compare with upper extent value based on offset + bcc DoFootCheck ;if player is too high, skip this part + jsr BlockBufferColli_Head ;do player-to-bg collision detection on top of + beq DoFootCheck ;player, and branch if nothing above player's head + jsr CheckForCoinMTiles ;check to see if player touched coin with their head + bcs AwardTouchedCoin ;if so, branch to some other part of code + ldy Player_Y_Speed ;check player's vertical speed + bpl DoFootCheck ;if player not moving upwards, branch elsewhere + ldy $04 ;check lower nybble of vertical coordinate returned + cpy #$04 ;from collision detection routine + bcc DoFootCheck ;if low nybble < 4, branch + jsr CheckForSolidMTiles ;check to see what player's head bumped on + bcs SolidOrClimb ;if player collided with solid metatile, branch + ldy AreaType ;otherwise check area type + beq NYSpd ;if water level, branch ahead + ldy BlockBounceTimer ;if block bounce timer not expired, + bne NYSpd ;branch ahead, do not process collision + jsr PlayerHeadCollision ;otherwise do a sub to process collision + jmp DoFootCheck ;jump ahead to skip these other parts here + +SolidOrClimb: + cmp #$26 ;if climbing metatile, + beq NYSpd ;branch ahead and do not play sound + lda #Sfx_Bump + sta Square1SoundQueue ;otherwise load bump sound +NYSpd: lda #$01 ;set player's vertical speed to nullify + sta Player_Y_Speed ;jump or swim + +DoFootCheck: + ldy $eb ;get block buffer adder offset + lda Player_Y_Position + cmp #$cf ;check to see how low player is + bcs DoPlayerSideCheck ;if player is too far down on screen, skip all of this + jsr BlockBufferColli_Feet ;do player-to-bg collision detection on bottom left of player + jsr CheckForCoinMTiles ;check to see if player touched coin with their left foot + bcs AwardTouchedCoin ;if so, branch to some other part of code + pha ;save bottom left metatile to stack + jsr BlockBufferColli_Feet ;do player-to-bg collision detection on bottom right of player + sta $00 ;save bottom right metatile here + pla + sta $01 ;pull bottom left metatile and save here + bne ChkFootMTile ;if anything here, skip this part + lda $00 ;otherwise check for anything in bottom right metatile + beq DoPlayerSideCheck ;and skip ahead if not + jsr CheckForCoinMTiles ;check to see if player touched coin with their right foot + bcc ChkFootMTile ;if not, skip unconditional jump and continue code + +AwardTouchedCoin: + jmp HandleCoinMetatile ;follow the code to erase coin and award to player 1 coin + +ChkFootMTile: + jsr CheckForClimbMTiles ;check to see if player landed on climbable metatiles + bcs DoPlayerSideCheck ;if so, branch + ldy Player_Y_Speed ;check player's vertical speed + bmi DoPlayerSideCheck ;if player moving upwards, branch + cmp #$c5 + bne ContChk ;if player did not touch axe, skip ahead + jmp HandleAxeMetatile ;otherwise jump to set modes of operation +ContChk: jsr ChkInvisibleMTiles ;do sub to check for hidden coin or 1-up blocks + beq DoPlayerSideCheck ;if either found, branch + ldy JumpspringAnimCtrl ;if jumpspring animating right now, + bne InitSteP ;branch ahead + ldy $04 ;check lower nybble of vertical coordinate returned + cpy #$05 ;from collision detection routine + bcc LandPlyr ;if lower nybble < 5, branch + lda Player_MovingDir + sta $00 ;use player's moving direction as temp variable + jmp ImpedePlayerMove ;jump to impede player's movement in that direction +LandPlyr: jsr ChkForLandJumpSpring ;do sub to check for jumpspring metatiles and deal with it + lda #$f0 + and Player_Y_Position ;mask out lower nybble of player's vertical position + sta Player_Y_Position ;and store as new vertical position to land player properly + jsr HandlePipeEntry ;do sub to process potential pipe entry + lda #$00 + sta Player_Y_Speed ;initialize vertical speed and fractional + sta Player_Y_MoveForce ;movement force to stop player's vertical movement + sta StompChainCounter ;initialize enemy stomp counter +InitSteP: lda #$00 + sta Player_State ;set player's state to normal + +DoPlayerSideCheck: + ldy $eb ;get block buffer adder offset + iny + iny ;increment offset 2 bytes to use adders for side collisions + lda #$02 ;set value here to be used as counter + sta $00 + +SideCheckLoop: + iny ;move onto the next one + sty $eb ;store it + lda Player_Y_Position + cmp #$20 ;check player's vertical position + bcc BHalf ;if player is in status bar area, branch ahead to skip this part + cmp #$e4 + bcs ExSCH ;branch to leave if player is too far down + jsr BlockBufferColli_Side ;do player-to-bg collision detection on one half of player + beq BHalf ;branch ahead if nothing found + cmp #$1c ;otherwise check for pipe metatiles + beq BHalf ;if collided with sideways pipe (top), branch ahead + cmp #$6b + beq BHalf ;if collided with water pipe (top), branch ahead + jsr CheckForClimbMTiles ;do sub to see if player bumped into anything climbable + bcc CheckSideMTiles ;if not, branch to alternate section of code +BHalf: ldy $eb ;load block adder offset + iny ;increment it + lda Player_Y_Position ;get player's vertical position + cmp #$08 + bcc ExSCH ;if too high, branch to leave + cmp #$d0 + bcs ExSCH ;if too low, branch to leave + jsr BlockBufferColli_Side ;do player-to-bg collision detection on other half of player + bne CheckSideMTiles ;if something found, branch + dec $00 ;otherwise decrement counter + bne SideCheckLoop ;run code until both sides of player are checked +ExSCH: rts ;leave + +CheckSideMTiles: + jsr ChkInvisibleMTiles ;check for hidden or coin 1-up blocks + beq ExCSM ;branch to leave if either found + jsr CheckForClimbMTiles ;check for climbable metatiles + bcc ContSChk ;if not found, skip and continue with code + jmp HandleClimbing ;otherwise jump to handle climbing +ContSChk: jsr CheckForCoinMTiles ;check to see if player touched coin + bcs HandleCoinMetatile ;if so, execute code to erase coin and award to player 1 coin + jsr ChkJumpspringMetatiles ;check for jumpspring metatiles + bcc ChkPBtm ;if not found, branch ahead to continue cude + lda JumpspringAnimCtrl ;otherwise check jumpspring animation control + bne ExCSM ;branch to leave if set + jmp StopPlayerMove ;otherwise jump to impede player's movement +ChkPBtm: ldy Player_State ;get player's state + cpy #$00 ;check for player's state set to normal + bne StopPlayerMove ;if not, branch to impede player's movement + ldy PlayerFacingDir ;get player's facing direction + dey + bne StopPlayerMove ;if facing left, branch to impede movement + cmp #$6c ;otherwise check for pipe metatiles + beq PipeDwnS ;if collided with sideways pipe (bottom), branch + cmp #$1f ;if collided with water pipe (bottom), continue + bne StopPlayerMove ;otherwise branch to impede player's movement +PipeDwnS: lda Player_SprAttrib ;check player's attributes + bne PlyrPipe ;if already set, branch, do not play sound again + ldy #Sfx_PipeDown_Injury + sty Square1SoundQueue ;otherwise load pipedown/injury sound +PlyrPipe: ora #%00100000 + sta Player_SprAttrib ;set background priority bit in player attributes + lda Player_X_Position + and #%00001111 ;get lower nybble of player's horizontal coordinate + beq ChkGERtn ;if at zero, branch ahead to skip this part + ldy #$00 ;set default offset for timer setting data + lda ScreenLeft_PageLoc ;load page location for left side of screen + beq SetCATmr ;if at page zero, use default offset + iny ;otherwise increment offset +SetCATmr: lda AreaChangeTimerData,y ;set timer for change of area as appropriate + sta ChangeAreaTimer +ChkGERtn: lda GameEngineSubroutine ;get number of game engine routine running + cmp #$07 + beq ExCSM ;if running player entrance routine or + cmp #$08 ;player control routine, go ahead and branch to leave + bne ExCSM + lda #$02 + sta GameEngineSubroutine ;otherwise set sideways pipe entry routine to run + rts ;and leave + +;-------------------------------- +;$02 - high nybble of vertical coordinate from block buffer +;$04 - low nybble of horizontal coordinate from block buffer +;$06-$07 - block buffer address + +StopPlayerMove: + jsr ImpedePlayerMove ;stop player's movement +ExCSM: rts ;leave + +AreaChangeTimerData: + .db $a0, $34 + +HandleCoinMetatile: + jsr ErACM ;do sub to erase coin metatile from block buffer + inc CoinTallyFor1Ups ;increment coin tally used for 1-up blocks + jmp GiveOneCoin ;update coin amount and tally on the screen + +HandleAxeMetatile: + lda #$00 + sta OperMode_Task ;reset secondary mode + lda #$02 + sta OperMode ;set primary mode to autoctrl mode + lda #$18 + sta Player_X_Speed ;set horizontal speed and continue to erase axe metatile +ErACM: ldy $02 ;load vertical high nybble offset for block buffer + lda #$00 ;load blank metatile + sta ($06),y ;store to remove old contents from block buffer + jmp RemoveCoin_Axe ;update the screen accordingly + +;-------------------------------- +;$02 - high nybble of vertical coordinate from block buffer +;$04 - low nybble of horizontal coordinate from block buffer +;$06-$07 - block buffer address + +ClimbXPosAdder: + .db $f9, $07 + +ClimbPLocAdder: + .db $ff, $00 + +FlagpoleYPosData: + .db $18, $22, $50, $68, $90 + +HandleClimbing: + ldy $04 ;check low nybble of horizontal coordinate returned from + cpy #$06 ;collision detection routine against certain values, this + bcc ExHC ;makes actual physical part of vine or flagpole thinner + cpy #$0a ;than 16 pixels + bcc ChkForFlagpole +ExHC: rts ;leave if too far left or too far right + +ChkForFlagpole: + cmp #$24 ;check climbing metatiles + beq FlagpoleCollision ;branch if flagpole ball found + cmp #$25 + bne VineCollision ;branch to alternate code if flagpole shaft not found + +FlagpoleCollision: + lda GameEngineSubroutine + cmp #$05 ;check for end-of-level routine running + beq PutPlayerOnVine ;if running, branch to end of climbing code + lda #$01 + sta PlayerFacingDir ;set player's facing direction to right + inc ScrollLock ;set scroll lock flag + lda GameEngineSubroutine + cmp #$04 ;check for flagpole slide routine running + beq RunFR ;if running, branch to end of flagpole code here + lda #BulletBill_CannonVar ;load identifier for bullet bills (cannon variant) + jsr KillEnemies ;get rid of them + lda #Silence + sta EventMusicQueue ;silence music + lsr + sta FlagpoleSoundQueue ;load flagpole sound into flagpole sound queue + ldx #$04 ;start at end of vertical coordinate data + lda Player_Y_Position + sta FlagpoleCollisionYPos ;store player's vertical coordinate here to be used later + +ChkFlagpoleYPosLoop: + cmp FlagpoleYPosData,x ;compare with current vertical coordinate data + bcs MtchF ;if player's => current, branch to use current offset + dex ;otherwise decrement offset to use + bne ChkFlagpoleYPosLoop ;do this until all data is checked (use last one if all checked) +MtchF: stx FlagpoleScore ;store offset here to be used later +RunFR: lda #$04 + sta GameEngineSubroutine ;set value to run flagpole slide routine + jmp PutPlayerOnVine ;jump to end of climbing code + +VineCollision: + cmp #$26 ;check for climbing metatile used on vines + bne PutPlayerOnVine + lda Player_Y_Position ;check player's vertical coordinate + cmp #$20 ;for being in status bar area + bcs PutPlayerOnVine ;branch if not that far up + lda #$01 + sta GameEngineSubroutine ;otherwise set to run autoclimb routine next frame + +PutPlayerOnVine: + lda #$03 ;set player state to climbing + sta Player_State + lda #$00 ;nullify player's horizontal speed + sta Player_X_Speed ;and fractional horizontal movement force + sta Player_X_MoveForce + lda Player_X_Position ;get player's horizontal coordinate + sec + sbc ScreenLeft_X_Pos ;subtract from left side horizontal coordinate + cmp #$10 + bcs SetVXPl ;if 16 or more pixels difference, do not alter facing direction + lda #$02 + sta PlayerFacingDir ;otherwise force player to face left +SetVXPl: ldy PlayerFacingDir ;get current facing direction, use as offset + lda $06 ;get low byte of block buffer address + asl + asl ;move low nybble to high + asl + asl + clc + adc ClimbXPosAdder-1,y ;add pixels depending on facing direction + sta Player_X_Position ;store as player's horizontal coordinate + lda $06 ;get low byte of block buffer address again + bne ExPVne ;if not zero, branch + lda ScreenRight_PageLoc ;load page location of right side of screen + clc + adc ClimbPLocAdder-1,y ;add depending on facing location + sta Player_PageLoc ;store as player's page location +ExPVne: rts ;finally, we're done! + +;-------------------------------- + +ChkInvisibleMTiles: + cmp #$5f ;check for hidden coin block + beq ExCInvT ;branch to leave if found + cmp #$60 ;check for hidden 1-up block +ExCInvT: rts ;leave with zero flag set if either found + +;-------------------------------- +;$00-$01 - used to hold bottom right and bottom left metatiles (in that order) +;$00 - used as flag by ImpedePlayerMove to restrict specific movement + +ChkForLandJumpSpring: + jsr ChkJumpspringMetatiles ;do sub to check if player landed on jumpspring + bcc ExCJSp ;if carry not set, jumpspring not found, therefore leave + lda #$70 + sta VerticalForce ;otherwise set vertical movement force for player + lda #$f9 + sta JumpspringForce ;set default jumpspring force + lda #$03 + sta JumpspringTimer ;set jumpspring timer to be used later + lsr + sta JumpspringAnimCtrl ;set jumpspring animation control to start animating +ExCJSp: rts ;and leave + +ChkJumpspringMetatiles: + cmp #$67 ;check for top jumpspring metatile + beq JSFnd ;branch to set carry if found + cmp #$68 ;check for bottom jumpspring metatile + clc ;clear carry flag + bne NoJSFnd ;branch to use cleared carry if not found +JSFnd: sec ;set carry if found +NoJSFnd: rts ;leave + +HandlePipeEntry: + lda Up_Down_Buttons ;check saved controller bits from earlier + and #%00000100 ;for pressing down + beq ExPipeE ;if not pressing down, branch to leave + lda $00 + cmp #$11 ;check right foot metatile for warp pipe right metatile + bne ExPipeE ;branch to leave if not found + lda $01 + cmp #$10 ;check left foot metatile for warp pipe left metatile + bne ExPipeE ;branch to leave if not found + lda #$30 + sta ChangeAreaTimer ;set timer for change of area + lda #$03 + sta GameEngineSubroutine ;set to run vertical pipe entry routine on next frame + lda #Sfx_PipeDown_Injury + sta Square1SoundQueue ;load pipedown/injury sound + lda #%00100000 + sta Player_SprAttrib ;set background priority bit in player's attributes + lda WarpZoneControl ;check warp zone control + beq ExPipeE ;branch to leave if none found + and #%00000011 ;mask out all but 2 LSB + asl + asl ;multiply by four + tax ;save as offset to warp zone numbers (starts at left pipe) + lda Player_X_Position ;get player's horizontal position + cmp #$60 + bcc GetWNum ;if player at left, not near middle, use offset and skip ahead + inx ;otherwise increment for middle pipe + cmp #$a0 + bcc GetWNum ;if player at middle, but not too far right, use offset and skip + inx ;otherwise increment for last pipe +GetWNum: ldy WarpZoneNumbers,x ;get warp zone numbers + dey ;decrement for use as world number + sty WorldNumber ;store as world number and offset + ldx WorldAddrOffsets,y ;get offset to where this world's area offsets are + lda AreaAddrOffsets,x ;get area offset based on world offset + sta AreaPointer ;store area offset here to be used to change areas + lda #Silence + sta EventMusicQueue ;silence music + lda #$00 + sta EntrancePage ;initialize starting page number + sta AreaNumber ;initialize area number used for area address offset + sta LevelNumber ;initialize level number used for world display + sta AltEntranceControl ;initialize mode of entry + inc Hidden1UpFlag ;set flag for hidden 1-up blocks + inc FetchNewGameTimerFlag ;set flag to load new game timer +ExPipeE: rts ;leave!!! + +ImpedePlayerMove: + lda #$00 ;initialize value here + ldy Player_X_Speed ;get player's horizontal speed + ldx $00 ;check value set earlier for + dex ;left side collision + bne RImpd ;if right side collision, skip this part + inx ;return value to X + cpy #$00 ;if player moving to the left, + bmi ExIPM ;branch to invert bit and leave + lda #$ff ;otherwise load A with value to be used later + jmp NXSpd ;and jump to affect movement +RImpd: ldx #$02 ;return $02 to X + cpy #$01 ;if player moving to the right, + bpl ExIPM ;branch to invert bit and leave + lda #$01 ;otherwise load A with value to be used here +NXSpd: ldy #$10 + sty SideCollisionTimer ;set timer of some sort + ldy #$00 + sty Player_X_Speed ;nullify player's horizontal speed + cmp #$00 ;if value set in A not set to $ff, + bpl PlatF ;branch ahead, do not decrement Y + dey ;otherwise decrement Y now +PlatF: sty $00 ;store Y as high bits of horizontal adder + clc + adc Player_X_Position ;add contents of A to player's horizontal + sta Player_X_Position ;position to move player left or right + lda Player_PageLoc + adc $00 ;add high bits and carry to + sta Player_PageLoc ;page location if necessary +ExIPM: txa ;invert contents of X + eor #$ff + and Player_CollisionBits ;mask out bit that was set here + sta Player_CollisionBits ;store to clear bit + rts + +;-------------------------------- + +SolidMTileUpperExt: + .db $10, $61, $88, $c4 + +CheckForSolidMTiles: + jsr GetMTileAttrib ;find appropriate offset based on metatile's 2 MSB + cmp SolidMTileUpperExt,x ;compare current metatile with solid metatiles + rts + +ClimbMTileUpperExt: + .db $24, $6d, $8a, $c6 + +CheckForClimbMTiles: + jsr GetMTileAttrib ;find appropriate offset based on metatile's 2 MSB + cmp ClimbMTileUpperExt,x ;compare current metatile with climbable metatiles + rts + +CheckForCoinMTiles: + cmp #$c2 ;check for regular coin + beq CoinSd ;branch if found + cmp #$c3 ;check for underwater coin + beq CoinSd ;branch if found + clc ;otherwise clear carry and leave + rts +CoinSd: lda #Sfx_CoinGrab + sta Square2SoundQueue ;load coin grab sound and leave + rts + +GetMTileAttrib: + tay ;save metatile value into Y + and #%11000000 ;mask out all but 2 MSB + asl + rol ;shift and rotate d7-d6 to d1-d0 + rol + tax ;use as offset for metatile data + tya ;get original metatile value back +ExEBG: rts ;leave + +;------------------------------------------------------------------------------------- +;$06-$07 - address from block buffer routine + +EnemyBGCStateData: + .db $01, $01, $02, $02, $02, $05 + +EnemyBGCXSpdData: + .db $10, $f0 + +EnemyToBGCollisionDet: + lda Enemy_State,x ;check enemy state for d6 set + and #%00100000 + bne ExEBG ;if set, branch to leave + jsr SubtEnemyYPos ;otherwise, do a subroutine here + bcc ExEBG ;if enemy vertical coord + 62 < 68, branch to leave + ldy Enemy_ID,x + cpy #Spiny ;if enemy object is not spiny, branch elsewhere + bne DoIDCheckBGColl + lda Enemy_Y_Position,x + cmp #$25 ;if enemy vertical coordinate < 36 branch to leave + bcc ExEBG + +DoIDCheckBGColl: + cpy #GreenParatroopaJump ;check for some other enemy object + bne HBChk ;branch if not found + jmp EnemyJump ;otherwise jump elsewhere +HBChk: cpy #HammerBro ;check for hammer bro + bne CInvu ;branch if not found + jmp HammerBroBGColl ;otherwise jump elsewhere +CInvu: cpy #Spiny ;if enemy object is spiny, branch + beq YesIn + cpy #PowerUpObject ;if special power-up object, branch + beq YesIn + cpy #$07 ;if enemy object =>$07, branch to leave + bcs ExEBGChk +YesIn: jsr ChkUnderEnemy ;if enemy object < $07, or = $12 or $2e, do this sub + bne HandleEToBGCollision ;if block underneath enemy, branch + +NoEToBGCollision: + jmp ChkForRedKoopa ;otherwise skip and do something else + +;-------------------------------- +;$02 - vertical coordinate from block buffer routine + +HandleEToBGCollision: + jsr ChkForNonSolids ;if something is underneath enemy, find out what + beq NoEToBGCollision ;if blank $26, coins, or hidden blocks, jump, enemy falls through + cmp #$23 + bne LandEnemyProperly ;check for blank metatile $23 and branch if not found + ldy $02 ;get vertical coordinate used to find block + lda #$00 ;store default blank metatile in that spot so we won't + sta ($06),y ;trigger this routine accidentally again + lda Enemy_ID,x + cmp #$15 ;if enemy object => $15, branch ahead + bcs ChkToStunEnemies + cmp #Goomba ;if enemy object not goomba, branch ahead of this routine + bne GiveOEPoints + jsr KillEnemyAboveBlock ;if enemy object IS goomba, do this sub + +GiveOEPoints: + lda #$01 ;award 100 points for hitting block beneath enemy + jsr SetupFloateyNumber + +ChkToStunEnemies: + cmp #$09 ;perform many comparisons on enemy object identifier + bcc SetStun + cmp #$11 ;if the enemy object identifier is equal to the values + bcs SetStun ;$09, $0e, $0f or $10, it will be modified, and not + cmp #$0a ;modified if not any of those values, note that piranha plant will + bcc Demote ;always fail this test because A will still have vertical + cmp #PiranhaPlant ;coordinate from previous addition, also these comparisons + bcc SetStun ;are only necessary if branching from $d7a1 +Demote: and #%00000001 ;erase all but LSB, essentially turning enemy object + sta Enemy_ID,x ;into green or red koopa troopa to demote them +SetStun: lda Enemy_State,x ;load enemy state + and #%11110000 ;save high nybble + ora #%00000010 + sta Enemy_State,x ;set d1 of enemy state + dec Enemy_Y_Position,x + dec Enemy_Y_Position,x ;subtract two pixels from enemy's vertical position + lda Enemy_ID,x + cmp #Bloober ;check for bloober object + beq SetWYSpd + lda #$fd ;set default vertical speed + ldy AreaType + bne SetNotW ;if area type not water, set as speed, otherwise +SetWYSpd: lda #$ff ;change the vertical speed +SetNotW: sta Enemy_Y_Speed,x ;set vertical speed now + ldy #$01 + jsr PlayerEnemyDiff ;get horizontal difference between player and enemy object + bpl ChkBBill ;branch if enemy is to the right of player + iny ;increment Y if not +ChkBBill: lda Enemy_ID,x + cmp #BulletBill_CannonVar ;check for bullet bill (cannon variant) + beq NoCDirF + cmp #BulletBill_FrenzyVar ;check for bullet bill (frenzy variant) + beq NoCDirF ;branch if either found, direction does not change + sty Enemy_MovingDir,x ;store as moving direction +NoCDirF: dey ;decrement and use as offset + lda EnemyBGCXSpdData,y ;get proper horizontal speed + sta Enemy_X_Speed,x ;and store, then leave +ExEBGChk: rts + +;-------------------------------- +;$04 - low nybble of vertical coordinate from block buffer routine + +LandEnemyProperly: + lda $04 ;check lower nybble of vertical coordinate saved earlier + sec + sbc #$08 ;subtract eight pixels + cmp #$05 ;used to determine whether enemy landed from falling + bcs ChkForRedKoopa ;branch if lower nybble in range of $0d-$0f before subtract + lda Enemy_State,x + and #%01000000 ;branch if d6 in enemy state is set + bne LandEnemyInitState + lda Enemy_State,x + asl ;branch if d7 in enemy state is not set + bcc ChkLandedEnemyState +SChkA: jmp DoEnemySideCheck ;if lower nybble < $0d, d7 set but d6 not set, jump here + +ChkLandedEnemyState: + lda Enemy_State,x ;if enemy in normal state, branch back to jump here + beq SChkA + cmp #$05 ;if in state used by spiny's egg + beq ProcEnemyDirection ;then branch elsewhere + cmp #$03 ;if already in state used by koopas and buzzy beetles + bcs ExSteChk ;or in higher numbered state, branch to leave + lda Enemy_State,x ;load enemy state again (why?) + cmp #$02 ;if not in $02 state (used by koopas and buzzy beetles) + bne ProcEnemyDirection ;then branch elsewhere + lda #$10 ;load default timer here + ldy Enemy_ID,x ;check enemy identifier for spiny + cpy #Spiny + bne SetForStn ;branch if not found + lda #$00 ;set timer for $00 if spiny +SetForStn: sta EnemyIntervalTimer,x ;set timer here + lda #$03 ;set state here, apparently used to render + sta Enemy_State,x ;upside-down koopas and buzzy beetles + jsr EnemyLanding ;then land it properly +ExSteChk: rts ;then leave + +ProcEnemyDirection: + lda Enemy_ID,x ;check enemy identifier for goomba + cmp #Goomba ;branch if found + beq LandEnemyInitState + cmp #Spiny ;check for spiny + bne InvtD ;branch if not found + lda #$01 + sta Enemy_MovingDir,x ;send enemy moving to the right by default + lda #$08 + sta Enemy_X_Speed,x ;set horizontal speed accordingly + lda FrameCounter + and #%00000111 ;if timed appropriately, spiny will skip over + beq LandEnemyInitState ;trying to face the player +InvtD: ldy #$01 ;load 1 for enemy to face the left (inverted here) + jsr PlayerEnemyDiff ;get horizontal difference between player and enemy + bpl CNwCDir ;if enemy to the right of player, branch + iny ;if to the left, increment by one for enemy to face right (inverted) +CNwCDir: tya + cmp Enemy_MovingDir,x ;compare direction in A with current direction in memory + bne LandEnemyInitState + jsr ChkForBump_HammerBroJ ;if equal, not facing in correct dir, do sub to turn around + +LandEnemyInitState: + jsr EnemyLanding ;land enemy properly + lda Enemy_State,x + and #%10000000 ;if d7 of enemy state is set, branch + bne NMovShellFallBit + lda #$00 ;otherwise initialize enemy state and leave + sta Enemy_State,x ;note this will also turn spiny's egg into spiny + rts + +NMovShellFallBit: + lda Enemy_State,x ;nullify d6 of enemy state, save other bits + and #%10111111 ;and store, then leave + sta Enemy_State,x + rts + +;-------------------------------- + +ChkForRedKoopa: + lda Enemy_ID,x ;check for red koopa troopa $03 + cmp #RedKoopa + bne Chk2MSBSt ;branch if not found + lda Enemy_State,x + beq ChkForBump_HammerBroJ ;if enemy found and in normal state, branch +Chk2MSBSt: lda Enemy_State,x ;save enemy state into Y + tay + asl ;check for d7 set + bcc GetSteFromD ;branch if not set + lda Enemy_State,x + ora #%01000000 ;set d6 + jmp SetD6Ste ;jump ahead of this part +GetSteFromD: lda EnemyBGCStateData,y ;load new enemy state with old as offset +SetD6Ste: sta Enemy_State,x ;set as new state + +;-------------------------------- +;$00 - used to store bitmask (not used but initialized here) +;$eb - used in DoEnemySideCheck as counter and to compare moving directions + +DoEnemySideCheck: + lda Enemy_Y_Position,x ;if enemy within status bar, branch to leave + cmp #$20 ;because there's nothing there that impedes movement + bcc ExESdeC + ldy #$16 ;start by finding block to the left of enemy ($00,$14) + lda #$02 ;set value here in what is also used as + sta $eb ;OAM data offset +SdeCLoop: lda $eb ;check value + cmp Enemy_MovingDir,x ;compare value against moving direction + bne NextSdeC ;branch if different and do not seek block there + lda #$01 ;set flag in A for save horizontal coordinate + jsr BlockBufferChk_Enemy ;find block to left or right of enemy object + beq NextSdeC ;if nothing found, branch + jsr ChkForNonSolids ;check for non-solid blocks + bne ChkForBump_HammerBroJ ;branch if not found +NextSdeC: dec $eb ;move to the next direction + iny + cpy #$18 ;increment Y, loop only if Y < $18, thus we check + bcc SdeCLoop ;enemy ($00, $14) and ($10, $14) pixel coordinates +ExESdeC: rts + +ChkForBump_HammerBroJ: + cpx #$05 ;check if we're on the special use slot + beq NoBump ;and if so, branch ahead and do not play sound + lda Enemy_State,x ;if enemy state d7 not set, branch + asl ;ahead and do not play sound + bcc NoBump + lda #Sfx_Bump ;otherwise, play bump sound + sta Square1SoundQueue ;sound will never be played if branching from ChkForRedKoopa +NoBump: lda Enemy_ID,x ;check for hammer bro + cmp #$05 + bne InvEnemyDir ;branch if not found + lda #$00 + sta $00 ;initialize value here for bitmask + ldy #$fa ;load default vertical speed for jumping + jmp SetHJ ;jump to code that makes hammer bro jump + +InvEnemyDir: + jmp RXSpd ;jump to turn the enemy around + +;-------------------------------- +;$00 - used to hold horizontal difference between player and enemy + +PlayerEnemyDiff: + lda Enemy_X_Position,x ;get distance between enemy object's + sec ;horizontal coordinate and the player's + sbc Player_X_Position ;horizontal coordinate + sta $00 ;and store here + lda Enemy_PageLoc,x + sbc Player_PageLoc ;subtract borrow, then leave + rts + +;-------------------------------- + +EnemyLanding: + jsr InitVStf ;do something here to vertical speed and something else + lda Enemy_Y_Position,x + and #%11110000 ;save high nybble of vertical coordinate, and + ora #%00001000 ;set d3, then store, probably used to set enemy object + sta Enemy_Y_Position,x ;neatly on whatever it's landing on + rts + +SubtEnemyYPos: + lda Enemy_Y_Position,x ;add 62 pixels to enemy object's + clc ;vertical coordinate + adc #$3e + cmp #$44 ;compare against a certain range + rts ;and leave with flags set for conditional branch + +EnemyJump: + jsr SubtEnemyYPos ;do a sub here + bcc DoSide ;if enemy vertical coord + 62 < 68, branch to leave + lda Enemy_Y_Speed,x + clc ;add two to vertical speed + adc #$02 + cmp #$03 ;if green paratroopa not falling, branch ahead + bcc DoSide + jsr ChkUnderEnemy ;otherwise, check to see if green paratroopa is + beq DoSide ;standing on anything, then branch to same place if not + jsr ChkForNonSolids ;check for non-solid blocks + beq DoSide ;branch if found + jsr EnemyLanding ;change vertical coordinate and speed + lda #$fd + sta Enemy_Y_Speed,x ;make the paratroopa jump again +DoSide: jmp DoEnemySideCheck ;check for horizontal blockage, then leave + +;-------------------------------- + +HammerBroBGColl: + jsr ChkUnderEnemy ;check to see if hammer bro is standing on anything + beq NoUnderHammerBro + cmp #$23 ;check for blank metatile $23 and branch if not found + bne UnderHammerBro + +KillEnemyAboveBlock: + jsr ShellOrBlockDefeat ;do this sub to kill enemy + lda #$fc ;alter vertical speed of enemy and leave + sta Enemy_Y_Speed,x + rts + +UnderHammerBro: + lda EnemyFrameTimer,x ;check timer used by hammer bro + bne NoUnderHammerBro ;branch if not expired + lda Enemy_State,x + and #%10001000 ;save d7 and d3 from enemy state, nullify other bits + sta Enemy_State,x ;and store + jsr EnemyLanding ;modify vertical coordinate, speed and something else + jmp DoEnemySideCheck ;then check for horizontal blockage and leave + +NoUnderHammerBro: + lda Enemy_State,x ;if hammer bro is not standing on anything, set d0 + ora #$01 ;in the enemy state to indicate jumping or falling, then leave + sta Enemy_State,x + rts + +ChkUnderEnemy: + lda #$00 ;set flag in A for save vertical coordinate + ldy #$15 ;set Y to check the bottom middle (8,18) of enemy object + jmp BlockBufferChk_Enemy ;hop to it! + +ChkForNonSolids: + cmp #$26 ;blank metatile used for vines? + beq NSFnd + cmp #$c2 ;regular coin? + beq NSFnd + cmp #$c3 ;underwater coin? + beq NSFnd + cmp #$5f ;hidden coin block? + beq NSFnd + cmp #$60 ;hidden 1-up block? +NSFnd: rts + +;------------------------------------------------------------------------------------- + +FireballBGCollision: + lda Fireball_Y_Position,x ;check fireball's vertical coordinate + cmp #$18 + bcc ClearBounceFlag ;if within the status bar area of the screen, branch ahead + jsr BlockBufferChk_FBall ;do fireball to background collision detection on bottom of it + beq ClearBounceFlag ;if nothing underneath fireball, branch + jsr ChkForNonSolids ;check for non-solid metatiles + beq ClearBounceFlag ;branch if any found + lda Fireball_Y_Speed,x ;if fireball's vertical speed set to move upwards, + bmi InitFireballExplode ;branch to set exploding bit in fireball's state + lda FireballBouncingFlag,x ;if bouncing flag already set, + bne InitFireballExplode ;branch to set exploding bit in fireball's state + lda #$fd + sta Fireball_Y_Speed,x ;otherwise set vertical speed to move upwards (give it bounce) + lda #$01 + sta FireballBouncingFlag,x ;set bouncing flag + lda Fireball_Y_Position,x + and #$f8 ;modify vertical coordinate to land it properly + sta Fireball_Y_Position,x ;store as new vertical coordinate + rts ;leave + +ClearBounceFlag: + lda #$00 + sta FireballBouncingFlag,x ;clear bouncing flag by default + rts ;leave + +InitFireballExplode: + lda #$80 + sta Fireball_State,x ;set exploding flag in fireball's state + lda #Sfx_Bump + sta Square1SoundQueue ;load bump sound + rts ;leave + +;------------------------------------------------------------------------------------- +;$00 - used to hold one of bitmasks, or offset +;$01 - used for relative X coordinate, also used to store middle screen page location +;$02 - used for relative Y coordinate, also used to store middle screen coordinate + +;this data added to relative coordinates of sprite objects +;stored in order: left edge, top edge, right edge, bottom edge +BoundBoxCtrlData: + .db $02, $08, $0e, $20 + .db $03, $14, $0d, $20 + .db $02, $14, $0e, $20 + .db $02, $09, $0e, $15 + .db $00, $00, $18, $06 + .db $00, $00, $20, $0d + .db $00, $00, $30, $0d + .db $00, $00, $08, $08 + .db $06, $04, $0a, $08 + .db $03, $0e, $0d, $14 + .db $00, $02, $10, $15 + .db $04, $04, $0c, $1c + +GetFireballBoundBox: + txa ;add seven bytes to offset + clc ;to use in routines as offset for fireball + adc #$07 + tax + ldy #$02 ;set offset for relative coordinates + bne FBallB ;unconditional branch + +GetMiscBoundBox: + txa ;add nine bytes to offset + clc ;to use in routines as offset for misc object + adc #$09 + tax + ldy #$06 ;set offset for relative coordinates +FBallB: jsr BoundingBoxCore ;get bounding box coordinates + jmp CheckRightScreenBBox ;jump to handle any offscreen coordinates + +GetEnemyBoundBox: + ldy #$48 ;store bitmask here for now + sty $00 + ldy #$44 ;store another bitmask here for now and jump + jmp GetMaskedOffScrBits + +SmallPlatformBoundBox: + ldy #$08 ;store bitmask here for now + sty $00 + ldy #$04 ;store another bitmask here for now + +GetMaskedOffScrBits: + lda Enemy_X_Position,x ;get enemy object position relative + sec ;to the left side of the screen + sbc ScreenLeft_X_Pos + sta $01 ;store here + lda Enemy_PageLoc,x ;subtract borrow from current page location + sbc ScreenLeft_PageLoc ;of left side + bmi CMBits ;if enemy object is beyond left edge, branch + ora $01 + beq CMBits ;if precisely at the left edge, branch + ldy $00 ;if to the right of left edge, use value in $00 for A +CMBits: tya ;otherwise use contents of Y + and Enemy_OffscreenBits ;preserve bitwise whatever's in here + sta EnemyOffscrBitsMasked,x ;save masked offscreen bits here + bne MoveBoundBoxOffscreen ;if anything set here, branch + jmp SetupEOffsetFBBox ;otherwise, do something else + +LargePlatformBoundBox: + inx ;increment X to get the proper offset + jsr GetXOffscreenBits ;then jump directly to the sub for horizontal offscreen bits + dex ;decrement to return to original offset + cmp #$fe ;if completely offscreen, branch to put entire bounding + bcs MoveBoundBoxOffscreen ;box offscreen, otherwise start getting coordinates + +SetupEOffsetFBBox: + txa ;add 1 to offset to properly address + clc ;the enemy object memory locations + adc #$01 + tax + ldy #$01 ;load 1 as offset here, same reason + jsr BoundingBoxCore ;do a sub to get the coordinates of the bounding box + jmp CheckRightScreenBBox ;jump to handle offscreen coordinates of bounding box + +MoveBoundBoxOffscreen: + txa ;multiply offset by 4 + asl + asl + tay ;use as offset here + lda #$ff + sta EnemyBoundingBoxCoord,y ;load value into four locations here and leave + sta EnemyBoundingBoxCoord+1,y + sta EnemyBoundingBoxCoord+2,y + sta EnemyBoundingBoxCoord+3,y + rts + +BoundingBoxCore: + stx $00 ;save offset here + lda SprObject_Rel_YPos,y ;store object coordinates relative to screen + sta $02 ;vertically and horizontally, respectively + lda SprObject_Rel_XPos,y + sta $01 + txa ;multiply offset by four and save to stack + asl + asl + pha + tay ;use as offset for Y, X is left alone + lda SprObj_BoundBoxCtrl,x ;load value here to be used as offset for X + asl ;multiply that by four and use as X + asl + tax + lda $01 ;add the first number in the bounding box data to the + clc ;relative horizontal coordinate using enemy object offset + adc BoundBoxCtrlData,x ;and store somewhere using same offset * 4 + sta BoundingBox_UL_Corner,y ;store here + lda $01 + clc + adc BoundBoxCtrlData+2,x ;add the third number in the bounding box data to the + sta BoundingBox_LR_Corner,y ;relative horizontal coordinate and store + inx ;increment both offsets + iny + lda $02 ;add the second number to the relative vertical coordinate + clc ;using incremented offset and store using the other + adc BoundBoxCtrlData,x ;incremented offset + sta BoundingBox_UL_Corner,y + lda $02 + clc + adc BoundBoxCtrlData+2,x ;add the fourth number to the relative vertical coordinate + sta BoundingBox_LR_Corner,y ;and store + pla ;get original offset loaded into $00 * y from stack + tay ;use as Y + ldx $00 ;get original offset and use as X again + rts + +CheckRightScreenBBox: + lda ScreenLeft_X_Pos ;add 128 pixels to left side of screen + clc ;and store as horizontal coordinate of middle + adc #$80 + sta $02 + lda ScreenLeft_PageLoc ;add carry to page location of left side of screen + adc #$00 ;and store as page location of middle + sta $01 + lda SprObject_X_Position,x ;get horizontal coordinate + cmp $02 ;compare against middle horizontal coordinate + lda SprObject_PageLoc,x ;get page location + sbc $01 ;subtract from middle page location + bcc CheckLeftScreenBBox ;if object is on the left side of the screen, branch + lda BoundingBox_DR_XPos,y ;check right-side edge of bounding box for offscreen + bmi NoOfs ;coordinates, branch if still on the screen + lda #$ff ;load offscreen value here to use on one or both horizontal sides + ldx BoundingBox_UL_XPos,y ;check left-side edge of bounding box for offscreen + bmi SORte ;coordinates, and branch if still on the screen + sta BoundingBox_UL_XPos,y ;store offscreen value for left side +SORte: sta BoundingBox_DR_XPos,y ;store offscreen value for right side +NoOfs: ldx ObjectOffset ;get object offset and leave + rts + +CheckLeftScreenBBox: + lda BoundingBox_UL_XPos,y ;check left-side edge of bounding box for offscreen + bpl NoOfs2 ;coordinates, and branch if still on the screen + cmp #$a0 ;check to see if left-side edge is in the middle of the + bcc NoOfs2 ;screen or really offscreen, and branch if still on + lda #$00 + ldx BoundingBox_DR_XPos,y ;check right-side edge of bounding box for offscreen + bpl SOLft ;coordinates, branch if still onscreen + sta BoundingBox_DR_XPos,y ;store offscreen value for right side +SOLft: sta BoundingBox_UL_XPos,y ;store offscreen value for left side +NoOfs2: ldx ObjectOffset ;get object offset and leave + rts + +;------------------------------------------------------------------------------------- +;$06 - second object's offset +;$07 - counter + +PlayerCollisionCore: + ldx #$00 ;initialize X to use player's bounding box for comparison + +SprObjectCollisionCore: + sty $06 ;save contents of Y here + lda #$01 + sta $07 ;save value 1 here as counter, compare horizontal coordinates first + +CollisionCoreLoop: + lda BoundingBox_UL_Corner,y ;compare left/top coordinates + cmp BoundingBox_UL_Corner,x ;of first and second objects' bounding boxes + bcs FirstBoxGreater ;if first left/top => second, branch + cmp BoundingBox_LR_Corner,x ;otherwise compare to right/bottom of second + bcc SecondBoxVerticalChk ;if first left/top < second right/bottom, branch elsewhere + beq CollisionFound ;if somehow equal, collision, thus branch + lda BoundingBox_LR_Corner,y ;if somehow greater, check to see if bottom of + cmp BoundingBox_UL_Corner,y ;first object's bounding box is greater than its top + bcc CollisionFound ;if somehow less, vertical wrap collision, thus branch + cmp BoundingBox_UL_Corner,x ;otherwise compare bottom of first bounding box to the top + bcs CollisionFound ;of second box, and if equal or greater, collision, thus branch + ldy $06 ;otherwise return with carry clear and Y = $0006 + rts ;note horizontal wrapping never occurs + +SecondBoxVerticalChk: + lda BoundingBox_LR_Corner,x ;check to see if the vertical bottom of the box + cmp BoundingBox_UL_Corner,x ;is greater than the vertical top + bcc CollisionFound ;if somehow less, vertical wrap collision, thus branch + lda BoundingBox_LR_Corner,y ;otherwise compare horizontal right or vertical bottom + cmp BoundingBox_UL_Corner,x ;of first box with horizontal left or vertical top of second box + bcs CollisionFound ;if equal or greater, collision, thus branch + ldy $06 ;otherwise return with carry clear and Y = $0006 + rts + +FirstBoxGreater: + cmp BoundingBox_UL_Corner,x ;compare first and second box horizontal left/vertical top again + beq CollisionFound ;if first coordinate = second, collision, thus branch + cmp BoundingBox_LR_Corner,x ;if not, compare with second object right or bottom edge + bcc CollisionFound ;if left/top of first less than or equal to right/bottom of second + beq CollisionFound ;then collision, thus branch + cmp BoundingBox_LR_Corner,y ;otherwise check to see if top of first box is greater than bottom + bcc NoCollisionFound ;if less than or equal, no collision, branch to end + beq NoCollisionFound + lda BoundingBox_LR_Corner,y ;otherwise compare bottom of first to top of second + cmp BoundingBox_UL_Corner,x ;if bottom of first is greater than top of second, vertical wrap + bcs CollisionFound ;collision, and branch, otherwise, proceed onwards here + +NoCollisionFound: + clc ;clear carry, then load value set earlier, then leave + ldy $06 ;like previous ones, if horizontal coordinates do not collide, we do + rts ;not bother checking vertical ones, because what's the point? + +CollisionFound: + inx ;increment offsets on both objects to check + iny ;the vertical coordinates + dec $07 ;decrement counter to reflect this + bpl CollisionCoreLoop ;if counter not expired, branch to loop + sec ;otherwise we already did both sets, therefore collision, so set carry + ldy $06 ;load original value set here earlier, then leave + rts + +;------------------------------------------------------------------------------------- +;$02 - modified y coordinate +;$03 - stores metatile involved in block buffer collisions +;$04 - comes in with offset to block buffer adder data, goes out with low nybble x/y coordinate +;$05 - modified x coordinate +;$06-$07 - block buffer address + +BlockBufferChk_Enemy: + pha ;save contents of A to stack + txa + clc ;add 1 to X to run sub with enemy offset in mind + adc #$01 + tax + pla ;pull A from stack and jump elsewhere + jmp BBChk_E + +ResidualMiscObjectCode: + txa + clc ;supposedly used once to set offset for + adc #$0d ;miscellaneous objects + tax + ldy #$1b ;supposedly used once to set offset for block buffer data + jmp ResJmpM ;probably used in early stages to do misc to bg collision detection + +BlockBufferChk_FBall: + ldy #$1a ;set offset for block buffer adder data + txa + clc + adc #$07 ;add seven bytes to use + tax +ResJmpM: lda #$00 ;set A to return vertical coordinate +BBChk_E: jsr BlockBufferCollision ;do collision detection subroutine for sprite object + ldx ObjectOffset ;get object offset + cmp #$00 ;check to see if object bumped into anything + rts + +BlockBufferAdderData: + .db $00, $07, $0e + +BlockBuffer_X_Adder: + .db $08, $03, $0c, $02, $02, $0d, $0d, $08 + .db $03, $0c, $02, $02, $0d, $0d, $08, $03 + .db $0c, $02, $02, $0d, $0d, $08, $00, $10 + .db $04, $14, $04, $04 + +BlockBuffer_Y_Adder: + .db $04, $20, $20, $08, $18, $08, $18, $02 + .db $20, $20, $08, $18, $08, $18, $12, $20 + .db $20, $18, $18, $18, $18, $18, $14, $14 + .db $06, $06, $08, $10 + +BlockBufferColli_Feet: + iny ;if branched here, increment to next set of adders + +BlockBufferColli_Head: + lda #$00 ;set flag to return vertical coordinate + .db $2c ;BIT instruction opcode + +BlockBufferColli_Side: + lda #$01 ;set flag to return horizontal coordinate + ldx #$00 ;set offset for player object + +BlockBufferCollision: + pha ;save contents of A to stack + sty $04 ;save contents of Y here + lda BlockBuffer_X_Adder,y ;add horizontal coordinate + clc ;of object to value obtained using Y as offset + adc SprObject_X_Position,x + sta $05 ;store here + lda SprObject_PageLoc,x + adc #$00 ;add carry to page location + and #$01 ;get LSB, mask out all other bits + lsr ;move to carry + ora $05 ;get stored value + ror ;rotate carry to MSB of A + lsr ;and effectively move high nybble to + lsr ;lower, LSB which became MSB will be + lsr ;d4 at this point + jsr GetBlockBufferAddr ;get address of block buffer into $06, $07 + ldy $04 ;get old contents of Y + lda SprObject_Y_Position,x ;get vertical coordinate of object + clc + adc BlockBuffer_Y_Adder,y ;add it to value obtained using Y as offset + and #%11110000 ;mask out low nybble + sec + sbc #$20 ;subtract 32 pixels for the status bar + sta $02 ;store result here + tay ;use as offset for block buffer + lda ($06),y ;check current content of block buffer + sta $03 ;and store here + ldy $04 ;get old contents of Y again + pla ;pull A from stack + bne RetXC ;if A = 1, branch + lda SprObject_Y_Position,x ;if A = 0, load vertical coordinate + jmp RetYC ;and jump +RetXC: lda SprObject_X_Position,x ;otherwise load horizontal coordinate +RetYC: and #%00001111 ;and mask out high nybble + sta $04 ;store masked out result here + lda $03 ;get saved content of block buffer + rts ;and leave + +;------------------------------------------------------------------------------------- + +;unused byte + .db $ff + +;------------------------------------------------------------------------------------- +;$00 - offset to vine Y coordinate adder +;$02 - offset to sprite data + +VineYPosAdder: + .db $00, $30 + +DrawVine: + sty $00 ;save offset here + lda Enemy_Rel_YPos ;get relative vertical coordinate + clc + adc VineYPosAdder,y ;add value using offset in Y to get value + ldx VineObjOffset,y ;get offset to vine + ldy Enemy_SprDataOffset,x ;get sprite data offset + sty $02 ;store sprite data offset here + jsr SixSpriteStacker ;stack six sprites on top of each other vertically + lda Enemy_Rel_XPos ;get relative horizontal coordinate + sta Sprite_X_Position,y ;store in first, third and fifth sprites + sta Sprite_X_Position+8,y + sta Sprite_X_Position+16,y + clc + adc #$06 ;add six pixels to second, fourth and sixth sprites + sta Sprite_X_Position+4,y ;to give characteristic staggered vine shape to + sta Sprite_X_Position+12,y ;our vertical stack of sprites + sta Sprite_X_Position+20,y + lda #%00100001 ;set bg priority and palette attribute bits + sta Sprite_Attributes,y ;set in first, third and fifth sprites + sta Sprite_Attributes+8,y + sta Sprite_Attributes+16,y + ora #%01000000 ;additionally, set horizontal flip bit + sta Sprite_Attributes+4,y ;for second, fourth and sixth sprites + sta Sprite_Attributes+12,y + sta Sprite_Attributes+20,y + ldx #$05 ;set tiles for six sprites +VineTL: lda #$e1 ;set tile number for sprite + sta Sprite_Tilenumber,y + iny ;move offset to next sprite data + iny + iny + iny + dex ;move onto next sprite + bpl VineTL ;loop until all sprites are done + ldy $02 ;get original offset + lda $00 ;get offset to vine adding data + bne SkpVTop ;if offset not zero, skip this part + lda #$e0 + sta Sprite_Tilenumber,y ;set other tile number for top of vine +SkpVTop: ldx #$00 ;start with the first sprite again +ChkFTop: lda VineStart_Y_Position ;get original starting vertical coordinate + sec + sbc Sprite_Y_Position,y ;subtract top-most sprite's Y coordinate + cmp #$64 ;if two coordinates are less than 100/$64 pixels + bcc NextVSp ;apart, skip this to leave sprite alone + lda #$f8 + sta Sprite_Y_Position,y ;otherwise move sprite offscreen +NextVSp: iny ;move offset to next OAM data + iny + iny + iny + inx ;move onto next sprite + cpx #$06 ;do this until all sprites are checked + bne ChkFTop + ldy $00 ;return offset set earlier + rts + +SixSpriteStacker: + ldx #$06 ;do six sprites +StkLp: sta Sprite_Data,y ;store X or Y coordinate into OAM data + clc + adc #$08 ;add eight pixels + iny + iny ;move offset four bytes forward + iny + iny + dex ;do another sprite + bne StkLp ;do this until all sprites are done + ldy $02 ;get saved OAM data offset and leave + rts + +;------------------------------------------------------------------------------------- + +FirstSprXPos: + .db $04, $00, $04, $00 + +FirstSprYPos: + .db $00, $04, $00, $04 + +SecondSprXPos: + .db $00, $08, $00, $08 + +SecondSprYPos: + .db $08, $00, $08, $00 + +FirstSprTilenum: + .db $80, $82, $81, $83 + +SecondSprTilenum: + .db $81, $83, $80, $82 + +HammerSprAttrib: + .db $03, $03, $c3, $c3 + +DrawHammer: + ldy Misc_SprDataOffset,x ;get misc object OAM data offset + lda TimerControl + bne ForceHPose ;if master timer control set, skip this part + lda Misc_State,x ;otherwise get hammer's state + and #%01111111 ;mask out d7 + cmp #$01 ;check to see if set to 1 yet + beq GetHPose ;if so, branch +ForceHPose: ldx #$00 ;reset offset here + beq RenderH ;do unconditional branch to rendering part +GetHPose: lda FrameCounter ;get frame counter + lsr ;move d3-d2 to d1-d0 + lsr + and #%00000011 ;mask out all but d1-d0 (changes every four frames) + tax ;use as timing offset +RenderH: lda Misc_Rel_YPos ;get relative vertical coordinate + clc + adc FirstSprYPos,x ;add first sprite vertical adder based on offset + sta Sprite_Y_Position,y ;store as sprite Y coordinate for first sprite + clc + adc SecondSprYPos,x ;add second sprite vertical adder based on offset + sta Sprite_Y_Position+4,y ;store as sprite Y coordinate for second sprite + lda Misc_Rel_XPos ;get relative horizontal coordinate + clc + adc FirstSprXPos,x ;add first sprite horizontal adder based on offset + sta Sprite_X_Position,y ;store as sprite X coordinate for first sprite + clc + adc SecondSprXPos,x ;add second sprite horizontal adder based on offset + sta Sprite_X_Position+4,y ;store as sprite X coordinate for second sprite + lda FirstSprTilenum,x + sta Sprite_Tilenumber,y ;get and store tile number of first sprite + lda SecondSprTilenum,x + sta Sprite_Tilenumber+4,y ;get and store tile number of second sprite + lda HammerSprAttrib,x + sta Sprite_Attributes,y ;get and store attribute bytes for both + sta Sprite_Attributes+4,y ;note in this case they use the same data + ldx ObjectOffset ;get misc object offset + lda Misc_OffscreenBits + and #%11111100 ;check offscreen bits + beq NoHOffscr ;if all bits clear, leave object alone + lda #$00 + sta Misc_State,x ;otherwise nullify misc object state + lda #$f8 + jsr DumpTwoSpr ;do sub to move hammer sprites offscreen +NoHOffscr: rts ;leave + +;------------------------------------------------------------------------------------- +;$00-$01 - used to hold tile numbers ($01 addressed in draw floatey number part) +;$02 - used to hold Y coordinate for floatey number +;$03 - residual byte used for flip (but value set here affects nothing) +;$04 - attribute byte for floatey number +;$05 - used as X coordinate for floatey number + +FlagpoleScoreNumTiles: + .db $f9, $50 + .db $f7, $50 + .db $fa, $fb + .db $f8, $fb + .db $f6, $fb + +FlagpoleGfxHandler: + ldy Enemy_SprDataOffset,x ;get sprite data offset for flagpole flag + lda Enemy_Rel_XPos ;get relative horizontal coordinate + sta Sprite_X_Position,y ;store as X coordinate for first sprite + clc + adc #$08 ;add eight pixels and store + sta Sprite_X_Position+4,y ;as X coordinate for second and third sprites + sta Sprite_X_Position+8,y + clc + adc #$0c ;add twelve more pixels and + sta $05 ;store here to be used later by floatey number + lda Enemy_Y_Position,x ;get vertical coordinate + jsr DumpTwoSpr ;and do sub to dump into first and second sprites + adc #$08 ;add eight pixels + sta Sprite_Y_Position+8,y ;and store into third sprite + lda FlagpoleFNum_Y_Pos ;get vertical coordinate for floatey number + sta $02 ;store it here + lda #$01 + sta $03 ;set value for flip which will not be used, and + sta $04 ;attribute byte for floatey number + sta Sprite_Attributes,y ;set attribute bytes for all three sprites + sta Sprite_Attributes+4,y + sta Sprite_Attributes+8,y + lda #$7e + sta Sprite_Tilenumber,y ;put triangle shaped tile + sta Sprite_Tilenumber+8,y ;into first and third sprites + lda #$7f + sta Sprite_Tilenumber+4,y ;put skull tile into second sprite + lda FlagpoleCollisionYPos ;get vertical coordinate at time of collision + beq ChkFlagOffscreen ;if zero, branch ahead + tya + clc ;add 12 bytes to sprite data offset + adc #$0c + tay ;put back in Y + lda FlagpoleScore ;get offset used to award points for touching flagpole + asl ;multiply by 2 to get proper offset here + tax + lda FlagpoleScoreNumTiles,x ;get appropriate tile data + sta $00 + lda FlagpoleScoreNumTiles+1,x + jsr DrawOneSpriteRow ;use it to render floatey number + +ChkFlagOffscreen: + ldx ObjectOffset ;get object offset for flag + ldy Enemy_SprDataOffset,x ;get OAM data offset + lda Enemy_OffscreenBits ;get offscreen bits + and #%00001110 ;mask out all but d3-d1 + beq ExitDumpSpr ;if none of these bits set, branch to leave + +;------------------------------------------------------------------------------------- + +MoveSixSpritesOffscreen: + lda #$f8 ;set offscreen coordinate if jumping here + +DumpSixSpr: + sta Sprite_Data+20,y ;dump A contents + sta Sprite_Data+16,y ;into third row sprites + +DumpFourSpr: + sta Sprite_Data+12,y ;into second row sprites + +DumpThreeSpr: + sta Sprite_Data+8,y + +DumpTwoSpr: + sta Sprite_Data+4,y ;and into first row sprites + sta Sprite_Data,y + +ExitDumpSpr: + rts + +;------------------------------------------------------------------------------------- + +DrawLargePlatform: + ldy Enemy_SprDataOffset,x ;get OAM data offset + sty $02 ;store here + iny ;add 3 to it for offset + iny ;to X coordinate + iny + lda Enemy_Rel_XPos ;get horizontal relative coordinate + jsr SixSpriteStacker ;store X coordinates using A as base, stack horizontally + ldx ObjectOffset + lda Enemy_Y_Position,x ;get vertical coordinate + jsr DumpFourSpr ;dump into first four sprites as Y coordinate + ldy AreaType + cpy #$03 ;check for castle-type level + beq ShrinkPlatform + ldy SecondaryHardMode ;check for secondary hard mode flag set + beq SetLast2Platform ;branch if not set elsewhere + +ShrinkPlatform: + lda #$f8 ;load offscreen coordinate if flag set or castle-type level + +SetLast2Platform: + ldy Enemy_SprDataOffset,x ;get OAM data offset + sta Sprite_Y_Position+16,y ;store vertical coordinate or offscreen + sta Sprite_Y_Position+20,y ;coordinate into last two sprites as Y coordinate + lda #$5b ;load default tile for platform (girder) + ldx CloudTypeOverride + beq SetPlatformTilenum ;if cloud level override flag not set, use + lda #$75 ;otherwise load other tile for platform (puff) + +SetPlatformTilenum: + ldx ObjectOffset ;get enemy object buffer offset + iny ;increment Y for tile offset + jsr DumpSixSpr ;dump tile number into all six sprites + lda #$02 ;set palette controls + iny ;increment Y for sprite attributes + jsr DumpSixSpr ;dump attributes into all six sprites + inx ;increment X for enemy objects + jsr GetXOffscreenBits ;get offscreen bits again + dex + ldy Enemy_SprDataOffset,x ;get OAM data offset + asl ;rotate d7 into carry, save remaining + pha ;bits to the stack + bcc SChk2 + lda #$f8 ;if d7 was set, move first sprite offscreen + sta Sprite_Y_Position,y +SChk2: pla ;get bits from stack + asl ;rotate d6 into carry + pha ;save to stack + bcc SChk3 + lda #$f8 ;if d6 was set, move second sprite offscreen + sta Sprite_Y_Position+4,y +SChk3: pla ;get bits from stack + asl ;rotate d5 into carry + pha ;save to stack + bcc SChk4 + lda #$f8 ;if d5 was set, move third sprite offscreen + sta Sprite_Y_Position+8,y +SChk4: pla ;get bits from stack + asl ;rotate d4 into carry + pha ;save to stack + bcc SChk5 + lda #$f8 ;if d4 was set, move fourth sprite offscreen + sta Sprite_Y_Position+12,y +SChk5: pla ;get bits from stack + asl ;rotate d3 into carry + pha ;save to stack + bcc SChk6 + lda #$f8 ;if d3 was set, move fifth sprite offscreen + sta Sprite_Y_Position+16,y +SChk6: pla ;get bits from stack + asl ;rotate d2 into carry + bcc SLChk ;save to stack + lda #$f8 + sta Sprite_Y_Position+20,y ;if d2 was set, move sixth sprite offscreen +SLChk: lda Enemy_OffscreenBits ;check d7 of offscreen bits + asl ;and if d7 is not set, skip sub + bcc ExDLPl + jsr MoveSixSpritesOffscreen ;otherwise branch to move all sprites offscreen +ExDLPl: rts + +;------------------------------------------------------------------------------------- + +DrawFloateyNumber_Coin: + lda FrameCounter ;get frame counter + lsr ;divide by 2 + bcs NotRsNum ;branch if d0 not set to raise number every other frame + dec Misc_Y_Position,x ;otherwise, decrement vertical coordinate +NotRsNum: lda Misc_Y_Position,x ;get vertical coordinate + jsr DumpTwoSpr ;dump into both sprites + lda Misc_Rel_XPos ;get relative horizontal coordinate + sta Sprite_X_Position,y ;store as X coordinate for first sprite + clc + adc #$08 ;add eight pixels + sta Sprite_X_Position+4,y ;store as X coordinate for second sprite + lda #$02 + sta Sprite_Attributes,y ;store attribute byte in both sprites + sta Sprite_Attributes+4,y + lda #$f7 + sta Sprite_Tilenumber,y ;put tile numbers into both sprites + lda #$fb ;that resemble "200" + sta Sprite_Tilenumber+4,y + jmp ExJCGfx ;then jump to leave (why not an rts here instead?) + +JumpingCoinTiles: + .db $60, $61, $62, $63 + +JCoinGfxHandler: + ldy Misc_SprDataOffset,x ;get coin/floatey number's OAM data offset + lda Misc_State,x ;get state of misc object + cmp #$02 ;if 2 or greater, + bcs DrawFloateyNumber_Coin ;branch to draw floatey number + lda Misc_Y_Position,x ;store vertical coordinate as + sta Sprite_Y_Position,y ;Y coordinate for first sprite + clc + adc #$08 ;add eight pixels + sta Sprite_Y_Position+4,y ;store as Y coordinate for second sprite + lda Misc_Rel_XPos ;get relative horizontal coordinate + sta Sprite_X_Position,y + sta Sprite_X_Position+4,y ;store as X coordinate for first and second sprites + lda FrameCounter ;get frame counter + lsr ;divide by 2 to alter every other frame + and #%00000011 ;mask out d2-d1 + tax ;use as graphical offset + lda JumpingCoinTiles,x ;load tile number + iny ;increment OAM data offset to write tile numbers + jsr DumpTwoSpr ;do sub to dump tile number into both sprites + dey ;decrement to get old offset + lda #$02 + sta Sprite_Attributes,y ;set attribute byte in first sprite + lda #$82 + sta Sprite_Attributes+4,y ;set attribute byte with vertical flip in second sprite + ldx ObjectOffset ;get misc object offset +ExJCGfx: rts ;leave + +;------------------------------------------------------------------------------------- +;$00-$01 - used to hold tiles for drawing the power-up, $00 also used to hold power-up type +;$02 - used to hold bottom row Y position +;$03 - used to hold flip control (not used here) +;$04 - used to hold sprite attributes +;$05 - used to hold X position +;$07 - counter + +;tiles arranged in top left, right, bottom left, right order +PowerUpGfxTable: + .db $76, $77, $78, $79 ;regular mushroom + .db $d6, $d6, $d9, $d9 ;fire flower + .db $8d, $8d, $e4, $e4 ;star + .db $76, $77, $78, $79 ;1-up mushroom + +PowerUpAttributes: + .db $02, $01, $02, $01 + +DrawPowerUp: + ldy Enemy_SprDataOffset+5 ;get power-up's sprite data offset + lda Enemy_Rel_YPos ;get relative vertical coordinate + clc + adc #$08 ;add eight pixels + sta $02 ;store result here + lda Enemy_Rel_XPos ;get relative horizontal coordinate + sta $05 ;store here + ldx PowerUpType ;get power-up type + lda PowerUpAttributes,x ;get attribute data for power-up type + ora Enemy_SprAttrib+5 ;add background priority bit if set + sta $04 ;store attributes here + txa + pha ;save power-up type to the stack + asl + asl ;multiply by four to get proper offset + tax ;use as X + lda #$01 + sta $07 ;set counter here to draw two rows of sprite object + sta $03 ;init d1 of flip control + +PUpDrawLoop: + lda PowerUpGfxTable,x ;load left tile of power-up object + sta $00 + lda PowerUpGfxTable+1,x ;load right tile + jsr DrawOneSpriteRow ;branch to draw one row of our power-up object + dec $07 ;decrement counter + bpl PUpDrawLoop ;branch until two rows are drawn + ldy Enemy_SprDataOffset+5 ;get sprite data offset again + pla ;pull saved power-up type from the stack + beq PUpOfs ;if regular mushroom, branch, do not change colors or flip + cmp #$03 + beq PUpOfs ;if 1-up mushroom, branch, do not change colors or flip + sta $00 ;store power-up type here now + lda FrameCounter ;get frame counter + lsr ;divide by 2 to change colors every two frames + and #%00000011 ;mask out all but d1 and d0 (previously d2 and d1) + ora Enemy_SprAttrib+5 ;add background priority bit if any set + sta Sprite_Attributes,y ;set as new palette bits for top left and + sta Sprite_Attributes+4,y ;top right sprites for fire flower and star + ldx $00 + dex ;check power-up type for fire flower + beq FlipPUpRightSide ;if found, skip this part + sta Sprite_Attributes+8,y ;otherwise set new palette bits for bottom left + sta Sprite_Attributes+12,y ;and bottom right sprites as well for star only + +FlipPUpRightSide: + lda Sprite_Attributes+4,y + ora #%01000000 ;set horizontal flip bit for top right sprite + sta Sprite_Attributes+4,y + lda Sprite_Attributes+12,y + ora #%01000000 ;set horizontal flip bit for bottom right sprite + sta Sprite_Attributes+12,y ;note these are only done for fire flower and star power-ups +PUpOfs: jmp SprObjectOffscrChk ;jump to check to see if power-up is offscreen at all, then leave + +;------------------------------------------------------------------------------------- +;$00-$01 - used in DrawEnemyObjRow to hold sprite tile numbers +;$02 - used to store Y position +;$03 - used to store moving direction, used to flip enemies horizontally +;$04 - used to store enemy's sprite attributes +;$05 - used to store X position +;$eb - used to hold sprite data offset +;$ec - used to hold either altered enemy state or special value used in gfx handler as condition +;$ed - used to hold enemy state from buffer +;$ef - used to hold enemy code used in gfx handler (may or may not resemble Enemy_ID values) + +;tiles arranged in top left, right, middle left, right, bottom left, right order +EnemyGraphicsTable: + .db $fc, $fc, $aa, $ab, $ac, $ad ;buzzy beetle frame 1 + .db $fc, $fc, $ae, $af, $b0, $b1 ; frame 2 + .db $fc, $a5, $a6, $a7, $a8, $a9 ;koopa troopa frame 1 + .db $fc, $a0, $a1, $a2, $a3, $a4 ; frame 2 + .db $69, $a5, $6a, $a7, $a8, $a9 ;koopa paratroopa frame 1 + .db $6b, $a0, $6c, $a2, $a3, $a4 ; frame 2 + .db $fc, $fc, $96, $97, $98, $99 ;spiny frame 1 + .db $fc, $fc, $9a, $9b, $9c, $9d ; frame 2 + .db $fc, $fc, $8f, $8e, $8e, $8f ;spiny's egg frame 1 + .db $fc, $fc, $95, $94, $94, $95 ; frame 2 + .db $fc, $fc, $dc, $dc, $df, $df ;bloober frame 1 + .db $dc, $dc, $dd, $dd, $de, $de ; frame 2 + .db $fc, $fc, $b2, $b3, $b4, $b5 ;cheep-cheep frame 1 + .db $fc, $fc, $b6, $b3, $b7, $b5 ; frame 2 + .db $fc, $fc, $70, $71, $72, $73 ;goomba + .db $fc, $fc, $6e, $6e, $6f, $6f ;koopa shell frame 1 (upside-down) + .db $fc, $fc, $6d, $6d, $6f, $6f ; frame 2 + .db $fc, $fc, $6f, $6f, $6e, $6e ;koopa shell frame 1 (rightsideup) + .db $fc, $fc, $6f, $6f, $6d, $6d ; frame 2 + .db $fc, $fc, $f4, $f4, $f5, $f5 ;buzzy beetle shell frame 1 (rightsideup) + .db $fc, $fc, $f4, $f4, $f5, $f5 ; frame 2 + .db $fc, $fc, $f5, $f5, $f4, $f4 ;buzzy beetle shell frame 1 (upside-down) + .db $fc, $fc, $f5, $f5, $f4, $f4 ; frame 2 + .db $fc, $fc, $fc, $fc, $ef, $ef ;defeated goomba + .db $b9, $b8, $bb, $ba, $bc, $bc ;lakitu frame 1 + .db $fc, $fc, $bd, $bd, $bc, $bc ; frame 2 + .db $7a, $7b, $da, $db, $d8, $d8 ;princess + .db $cd, $cd, $ce, $ce, $cf, $cf ;mushroom retainer + .db $7d, $7c, $d1, $8c, $d3, $d2 ;hammer bro frame 1 + .db $7d, $7c, $89, $88, $8b, $8a ; frame 2 + .db $d5, $d4, $e3, $e2, $d3, $d2 ; frame 3 + .db $d5, $d4, $e3, $e2, $8b, $8a ; frame 4 + .db $e5, $e5, $e6, $e6, $eb, $eb ;piranha plant frame 1 + .db $ec, $ec, $ed, $ed, $ee, $ee ; frame 2 + .db $fc, $fc, $d0, $d0, $d7, $d7 ;podoboo + .db $bf, $be, $c1, $c0, $c2, $fc ;bowser front frame 1 + .db $c4, $c3, $c6, $c5, $c8, $c7 ;bowser rear frame 1 + .db $bf, $be, $ca, $c9, $c2, $fc ; front frame 2 + .db $c4, $c3, $c6, $c5, $cc, $cb ; rear frame 2 + .db $fc, $fc, $e8, $e7, $ea, $e9 ;bullet bill + .db $f2, $f2, $f3, $f3, $f2, $f2 ;jumpspring frame 1 + .db $f1, $f1, $f1, $f1, $fc, $fc ; frame 2 + .db $f0, $f0, $fc, $fc, $fc, $fc ; frame 3 + +EnemyGfxTableOffsets: + .db $0c, $0c, $00, $0c, $0c, $a8, $54, $3c + .db $ea, $18, $48, $48, $cc, $c0, $18, $18 + .db $18, $90, $24, $ff, $48, $9c, $d2, $d8 + .db $f0, $f6, $fc + +EnemyAttributeData: + .db $01, $02, $03, $02, $01, $01, $03, $03 + .db $03, $01, $01, $02, $02, $21, $01, $02 + .db $01, $01, $02, $ff, $02, $02, $01, $01 + .db $02, $02, $02 + +EnemyAnimTimingBMask: + .db $08, $18 + +JumpspringFrameOffsets: + .db $18, $19, $1a, $19, $18 + +EnemyGfxHandler: + lda Enemy_Y_Position,x ;get enemy object vertical position + sta $02 + lda Enemy_Rel_XPos ;get enemy object horizontal position + sta $05 ;relative to screen + ldy Enemy_SprDataOffset,x + sty $eb ;get sprite data offset + lda #$00 + sta VerticalFlipFlag ;initialize vertical flip flag by default + lda Enemy_MovingDir,x + sta $03 ;get enemy object moving direction + lda Enemy_SprAttrib,x + sta $04 ;get enemy object sprite attributes + lda Enemy_ID,x + cmp #PiranhaPlant ;is enemy object piranha plant? + bne CheckForRetainerObj ;if not, branch + ldy PiranhaPlant_Y_Speed,x + bmi CheckForRetainerObj ;if piranha plant moving upwards, branch + ldy EnemyFrameTimer,x + beq CheckForRetainerObj ;if timer for movement expired, branch + rts ;if all conditions fail, leave + +CheckForRetainerObj: + lda Enemy_State,x ;store enemy state + sta $ed + and #%00011111 ;nullify all but 5 LSB and use as Y + tay + lda Enemy_ID,x ;check for mushroom retainer/princess object + cmp #RetainerObject + bne CheckForBulletBillCV ;if not found, branch + ldy #$00 ;if found, nullify saved state in Y + lda #$01 ;set value that will not be used + sta $03 + lda #$15 ;set value $15 as code for mushroom retainer/princess object + +CheckForBulletBillCV: + cmp #BulletBill_CannonVar ;otherwise check for bullet bill object + bne CheckForJumpspring ;if not found, branch again + dec $02 ;decrement saved vertical position + lda #$03 + ldy EnemyFrameTimer,x ;get timer for enemy object + beq SBBAt ;if expired, do not set priority bit + ora #%00100000 ;otherwise do so +SBBAt: sta $04 ;set new sprite attributes + ldy #$00 ;nullify saved enemy state both in Y and in + sty $ed ;memory location here + lda #$08 ;set specific value to unconditionally branch once + +CheckForJumpspring: + cmp #JumpspringObject ;check for jumpspring object + bne CheckForPodoboo + ldy #$03 ;set enemy state -2 MSB here for jumpspring object + ldx JumpspringAnimCtrl ;get current frame number for jumpspring object + lda JumpspringFrameOffsets,x ;load data using frame number as offset + +CheckForPodoboo: + sta $ef ;store saved enemy object value here + sty $ec ;and Y here (enemy state -2 MSB if not changed) + ldx ObjectOffset ;get enemy object offset + cmp #$0c ;check for podoboo object + bne CheckBowserGfxFlag ;branch if not found + lda Enemy_Y_Speed,x ;if moving upwards, branch + bmi CheckBowserGfxFlag + inc VerticalFlipFlag ;otherwise, set flag for vertical flip + +CheckBowserGfxFlag: + lda BowserGfxFlag ;if not drawing bowser at all, skip to something else + beq CheckForGoomba + ldy #$16 ;if set to 1, draw bowser's front + cmp #$01 + beq SBwsrGfxOfs + iny ;otherwise draw bowser's rear +SBwsrGfxOfs: sty $ef + +CheckForGoomba: + ldy $ef ;check value for goomba object + cpy #Goomba + bne CheckBowserFront ;branch if not found + lda Enemy_State,x + cmp #$02 ;check for defeated state + bcc GmbaAnim ;if not defeated, go ahead and animate + ldx #$04 ;if defeated, write new value here + stx $ec +GmbaAnim: and #%00100000 ;check for d5 set in enemy object state + ora TimerControl ;or timer disable flag set + bne CheckBowserFront ;if either condition true, do not animate goomba + lda FrameCounter + and #%00001000 ;check for every eighth frame + bne CheckBowserFront + lda $03 + eor #%00000011 ;invert bits to flip horizontally every eight frames + sta $03 ;leave alone otherwise + +CheckBowserFront: + lda EnemyAttributeData,y ;load sprite attribute using enemy object + ora $04 ;as offset, and add to bits already loaded + sta $04 + lda EnemyGfxTableOffsets,y ;load value based on enemy object as offset + tax ;save as X + ldy $ec ;get previously saved value + lda BowserGfxFlag + beq CheckForSpiny ;if not drawing bowser object at all, skip all of this + cmp #$01 + bne CheckBowserRear ;if not drawing front part, branch to draw the rear part + lda BowserBodyControls ;check bowser's body control bits + bpl ChkFrontSte ;branch if d7 not set (control's bowser's mouth) + ldx #$de ;otherwise load offset for second frame +ChkFrontSte: lda $ed ;check saved enemy state + and #%00100000 ;if bowser not defeated, do not set flag + beq DrawBowser + +FlipBowserOver: + stx VerticalFlipFlag ;set vertical flip flag to nonzero + +DrawBowser: + jmp DrawEnemyObject ;draw bowser's graphics now + +CheckBowserRear: + lda BowserBodyControls ;check bowser's body control bits + and #$01 + beq ChkRearSte ;branch if d0 not set (control's bowser's feet) + ldx #$e4 ;otherwise load offset for second frame +ChkRearSte: lda $ed ;check saved enemy state + and #%00100000 ;if bowser not defeated, do not set flag + beq DrawBowser + lda $02 ;subtract 16 pixels from + sec ;saved vertical coordinate + sbc #$10 + sta $02 + jmp FlipBowserOver ;jump to set vertical flip flag + +CheckForSpiny: + cpx #$24 ;check if value loaded is for spiny + bne CheckForLakitu ;if not found, branch + cpy #$05 ;if enemy state set to $05, do this, + bne NotEgg ;otherwise branch + ldx #$30 ;set to spiny egg offset + lda #$02 + sta $03 ;set enemy direction to reverse sprites horizontally + lda #$05 + sta $ec ;set enemy state +NotEgg: jmp CheckForHammerBro ;skip a big chunk of this if we found spiny but not in egg + +CheckForLakitu: + cpx #$90 ;check value for lakitu's offset loaded + bne CheckUpsideDownShell ;branch if not loaded + lda $ed + and #%00100000 ;check for d5 set in enemy state + bne NoLAFr ;branch if set + lda FrenzyEnemyTimer + cmp #$10 ;check timer to see if we've reached a certain range + bcs NoLAFr ;branch if not + ldx #$96 ;if d6 not set and timer in range, load alt frame for lakitu +NoLAFr: jmp CheckDefeatedState ;skip this next part if we found lakitu but alt frame not needed + +CheckUpsideDownShell: + lda $ef ;check for enemy object => $04 + cmp #$04 + bcs CheckRightSideUpShell ;branch if true + cpy #$02 + bcc CheckRightSideUpShell ;branch if enemy state < $02 + ldx #$5a ;set for upside-down koopa shell by default + ldy $ef + cpy #BuzzyBeetle ;check for buzzy beetle object + bne CheckRightSideUpShell + ldx #$7e ;set for upside-down buzzy beetle shell if found + inc $02 ;increment vertical position by one pixel + +CheckRightSideUpShell: + lda $ec ;check for value set here + cmp #$04 ;if enemy state < $02, do not change to shell, if + bne CheckForHammerBro ;enemy state => $02 but not = $04, leave shell upside-down + ldx #$72 ;set right-side up buzzy beetle shell by default + inc $02 ;increment saved vertical position by one pixel + ldy $ef + cpy #BuzzyBeetle ;check for buzzy beetle object + beq CheckForDefdGoomba ;branch if found + ldx #$66 ;change to right-side up koopa shell if not found + inc $02 ;and increment saved vertical position again + +CheckForDefdGoomba: + cpy #Goomba ;check for goomba object (necessary if previously + bne CheckForHammerBro ;failed buzzy beetle object test) + ldx #$54 ;load for regular goomba + lda $ed ;note that this only gets performed if enemy state => $02 + and #%00100000 ;check saved enemy state for d5 set + bne CheckForHammerBro ;branch if set + ldx #$8a ;load offset for defeated goomba + dec $02 ;set different value and decrement saved vertical position + +CheckForHammerBro: + ldy ObjectOffset + lda $ef ;check for hammer bro object + cmp #HammerBro + bne CheckForBloober ;branch if not found + lda $ed + beq CheckToAnimateEnemy ;branch if not in normal enemy state + and #%00001000 + beq CheckDefeatedState ;if d3 not set, branch further away + ldx #$b4 ;otherwise load offset for different frame + bne CheckToAnimateEnemy ;unconditional branch + +CheckForBloober: + cpx #$48 ;check for cheep-cheep offset loaded + beq CheckToAnimateEnemy ;branch if found + lda EnemyIntervalTimer,y + cmp #$05 + bcs CheckDefeatedState ;branch if some timer is above a certain point + cpx #$3c ;check for bloober offset loaded + bne CheckToAnimateEnemy ;branch if not found this time + cmp #$01 + beq CheckDefeatedState ;branch if timer is set to certain point + inc $02 ;increment saved vertical coordinate three pixels + inc $02 + inc $02 + jmp CheckAnimationStop ;and do something else + +CheckToAnimateEnemy: + lda $ef ;check for specific enemy objects + cmp #Goomba + beq CheckDefeatedState ;branch if goomba + cmp #$08 + beq CheckDefeatedState ;branch if bullet bill (note both variants use $08 here) + cmp #Podoboo + beq CheckDefeatedState ;branch if podoboo + cmp #$18 ;branch if => $18 + bcs CheckDefeatedState + ldy #$00 + cmp #$15 ;check for mushroom retainer/princess object + bne CheckForSecondFrame ;which uses different code here, branch if not found + iny ;residual instruction + lda WorldNumber ;are we on world 8? + cmp #World8 + bcs CheckDefeatedState ;if so, leave the offset alone (use princess) + ldx #$a2 ;otherwise, set for mushroom retainer object instead + lda #$03 ;set alternate state here + sta $ec + bne CheckDefeatedState ;unconditional branch + +CheckForSecondFrame: + lda FrameCounter ;load frame counter + and EnemyAnimTimingBMask,y ;mask it (partly residual, one byte not ever used) + bne CheckDefeatedState ;branch if timing is off + +CheckAnimationStop: + lda $ed ;check saved enemy state + and #%10100000 ;for d7 or d5, or check for timers stopped + ora TimerControl + bne CheckDefeatedState ;if either condition true, branch + txa + clc + adc #$06 ;add $06 to current enemy offset + tax ;to animate various enemy objects + +CheckDefeatedState: + lda $ed ;check saved enemy state + and #%00100000 ;for d5 set + beq DrawEnemyObject ;branch if not set + lda $ef + cmp #$04 ;check for saved enemy object => $04 + bcc DrawEnemyObject ;branch if less + ldy #$01 + sty VerticalFlipFlag ;set vertical flip flag + dey + sty $ec ;init saved value here + +DrawEnemyObject: + ldy $eb ;load sprite data offset + jsr DrawEnemyObjRow ;draw six tiles of data + jsr DrawEnemyObjRow ;into sprite data + jsr DrawEnemyObjRow + ldx ObjectOffset ;get enemy object offset + ldy Enemy_SprDataOffset,x ;get sprite data offset + lda $ef + cmp #$08 ;get saved enemy object and check + bne CheckForVerticalFlip ;for bullet bill, branch if not found + +SkipToOffScrChk: + jmp SprObjectOffscrChk ;jump if found + +CheckForVerticalFlip: + lda VerticalFlipFlag ;check if vertical flip flag is set here + beq CheckForESymmetry ;branch if not + lda Sprite_Attributes,y ;get attributes of first sprite we dealt with + ora #%10000000 ;set bit for vertical flip + iny + iny ;increment two bytes so that we store the vertical flip + jsr DumpSixSpr ;in attribute bytes of enemy obj sprite data + dey + dey ;now go back to the Y coordinate offset + tya + tax ;give offset to X + lda $ef + cmp #HammerBro ;check saved enemy object for hammer bro + beq FlipEnemyVertically + cmp #Lakitu ;check saved enemy object for lakitu + beq FlipEnemyVertically ;branch for hammer bro or lakitu + cmp #$15 + bcs FlipEnemyVertically ;also branch if enemy object => $15 + txa + clc + adc #$08 ;if not selected objects or => $15, set + tax ;offset in X for next row + +FlipEnemyVertically: + lda Sprite_Tilenumber,x ;load first or second row tiles + pha ;and save tiles to the stack + lda Sprite_Tilenumber+4,x + pha + lda Sprite_Tilenumber+16,y ;exchange third row tiles + sta Sprite_Tilenumber,x ;with first or second row tiles + lda Sprite_Tilenumber+20,y + sta Sprite_Tilenumber+4,x + pla ;pull first or second row tiles from stack + sta Sprite_Tilenumber+20,y ;and save in third row + pla + sta Sprite_Tilenumber+16,y + +CheckForESymmetry: + lda BowserGfxFlag ;are we drawing bowser at all? + bne SkipToOffScrChk ;branch if so + lda $ef + ldx $ec ;get alternate enemy state + cmp #$05 ;check for hammer bro object + bne ContES + jmp SprObjectOffscrChk ;jump if found +ContES: cmp #Bloober ;check for bloober object + beq MirrorEnemyGfx + cmp #PiranhaPlant ;check for piranha plant object + beq MirrorEnemyGfx + cmp #Podoboo ;check for podoboo object + beq MirrorEnemyGfx ;branch if either of three are found + cmp #Spiny ;check for spiny object + bne ESRtnr ;branch closer if not found + cpx #$05 ;check spiny's state + bne CheckToMirrorLakitu ;branch if not an egg, otherwise +ESRtnr: cmp #$15 ;check for princess/mushroom retainer object + bne SpnySC + lda #$42 ;set horizontal flip on bottom right sprite + sta Sprite_Attributes+20,y ;note that palette bits were already set earlier +SpnySC: cpx #$02 ;if alternate enemy state set to 1 or 0, branch + bcc CheckToMirrorLakitu + +MirrorEnemyGfx: + lda BowserGfxFlag ;if enemy object is bowser, skip all of this + bne CheckToMirrorLakitu + lda Sprite_Attributes,y ;load attribute bits of first sprite + and #%10100011 + sta Sprite_Attributes,y ;save vertical flip, priority, and palette bits + sta Sprite_Attributes+8,y ;in left sprite column of enemy object OAM data + sta Sprite_Attributes+16,y + ora #%01000000 ;set horizontal flip + cpx #$05 ;check for state used by spiny's egg + bne EggExc ;if alternate state not set to $05, branch + ora #%10000000 ;otherwise set vertical flip +EggExc: sta Sprite_Attributes+4,y ;set bits of right sprite column + sta Sprite_Attributes+12,y ;of enemy object sprite data + sta Sprite_Attributes+20,y + cpx #$04 ;check alternate enemy state + bne CheckToMirrorLakitu ;branch if not $04 + lda Sprite_Attributes+8,y ;get second row left sprite attributes + ora #%10000000 + sta Sprite_Attributes+8,y ;store bits with vertical flip in + sta Sprite_Attributes+16,y ;second and third row left sprites + ora #%01000000 + sta Sprite_Attributes+12,y ;store with horizontal and vertical flip in + sta Sprite_Attributes+20,y ;second and third row right sprites + +CheckToMirrorLakitu: + lda $ef ;check for lakitu enemy object + cmp #Lakitu + bne CheckToMirrorJSpring ;branch if not found + lda VerticalFlipFlag + bne NVFLak ;branch if vertical flip flag not set + lda Sprite_Attributes+16,y ;save vertical flip and palette bits + and #%10000001 ;in third row left sprite + sta Sprite_Attributes+16,y + lda Sprite_Attributes+20,y ;set horizontal flip and palette bits + ora #%01000001 ;in third row right sprite + sta Sprite_Attributes+20,y + ldx FrenzyEnemyTimer ;check timer + cpx #$10 + bcs SprObjectOffscrChk ;branch if timer has not reached a certain range + sta Sprite_Attributes+12,y ;otherwise set same for second row right sprite + and #%10000001 + sta Sprite_Attributes+8,y ;preserve vertical flip and palette bits for left sprite + bcc SprObjectOffscrChk ;unconditional branch +NVFLak: lda Sprite_Attributes,y ;get first row left sprite attributes + and #%10000001 + sta Sprite_Attributes,y ;save vertical flip and palette bits + lda Sprite_Attributes+4,y ;get first row right sprite attributes + ora #%01000001 ;set horizontal flip and palette bits + sta Sprite_Attributes+4,y ;note that vertical flip is left as-is + +CheckToMirrorJSpring: + lda $ef ;check for jumpspring object (any frame) + cmp #$18 + bcc SprObjectOffscrChk ;branch if not jumpspring object at all + lda #$82 + sta Sprite_Attributes+8,y ;set vertical flip and palette bits of + sta Sprite_Attributes+16,y ;second and third row left sprites + ora #%01000000 + sta Sprite_Attributes+12,y ;set, in addition to those, horizontal flip + sta Sprite_Attributes+20,y ;for second and third row right sprites + +SprObjectOffscrChk: + ldx ObjectOffset ;get enemy buffer offset + lda Enemy_OffscreenBits ;check offscreen information + lsr + lsr ;shift three times to the right + lsr ;which puts d2 into carry + pha ;save to stack + bcc LcChk ;branch if not set + lda #$04 ;set for right column sprites + jsr MoveESprColOffscreen ;and move them offscreen +LcChk: pla ;get from stack + lsr ;move d3 to carry + pha ;save to stack + bcc Row3C ;branch if not set + lda #$00 ;set for left column sprites, + jsr MoveESprColOffscreen ;move them offscreen +Row3C: pla ;get from stack again + lsr ;move d5 to carry this time + lsr + pha ;save to stack again + bcc Row23C ;branch if carry not set + lda #$10 ;set for third row of sprites + jsr MoveESprRowOffscreen ;and move them offscreen +Row23C: pla ;get from stack + lsr ;move d6 into carry + pha ;save to stack + bcc AllRowC + lda #$08 ;set for second and third rows + jsr MoveESprRowOffscreen ;move them offscreen +AllRowC: pla ;get from stack once more + lsr ;move d7 into carry + bcc ExEGHandler + jsr MoveESprRowOffscreen ;move all sprites offscreen (A should be 0 by now) + lda Enemy_ID,x + cmp #Podoboo ;check enemy identifier for podoboo + beq ExEGHandler ;skip this part if found, we do not want to erase podoboo! + lda Enemy_Y_HighPos,x ;check high byte of vertical position + cmp #$02 ;if not yet past the bottom of the screen, branch + bne ExEGHandler + jsr EraseEnemyObject ;what it says + +ExEGHandler: + rts + +DrawEnemyObjRow: + lda EnemyGraphicsTable,x ;load two tiles of enemy graphics + sta $00 + lda EnemyGraphicsTable+1,x + +DrawOneSpriteRow: + sta $01 + jmp DrawSpriteObject ;draw them + +MoveESprRowOffscreen: + clc ;add A to enemy object OAM data offset + adc Enemy_SprDataOffset,x + tay ;use as offset + lda #$f8 + jmp DumpTwoSpr ;move first row of sprites offscreen + +MoveESprColOffscreen: + clc ;add A to enemy object OAM data offset + adc Enemy_SprDataOffset,x + tay ;use as offset + jsr MoveColOffscreen ;move first and second row sprites in column offscreen + sta Sprite_Data+16,y ;move third row sprite in column offscreen + rts + +;------------------------------------------------------------------------------------- +;$00-$01 - tile numbers +;$02 - relative Y position +;$03 - horizontal flip flag (not used here) +;$04 - attributes +;$05 - relative X position + +DefaultBlockObjTiles: + .db $85, $85, $86, $86 ;brick w/ line (these are sprite tiles, not BG!) + +DrawBlock: + lda Block_Rel_YPos ;get relative vertical coordinate of block object + sta $02 ;store here + lda Block_Rel_XPos ;get relative horizontal coordinate of block object + sta $05 ;store here + lda #$03 + sta $04 ;set attribute byte here + lsr + sta $03 ;set horizontal flip bit here (will not be used) + ldy Block_SprDataOffset,x ;get sprite data offset + ldx #$00 ;reset X for use as offset to tile data +DBlkLoop: lda DefaultBlockObjTiles,x ;get left tile number + sta $00 ;set here + lda DefaultBlockObjTiles+1,x ;get right tile number + jsr DrawOneSpriteRow ;do sub to write tile numbers to first row of sprites + cpx #$04 ;check incremented offset + bne DBlkLoop ;and loop back until all four sprites are done + ldx ObjectOffset ;get block object offset + ldy Block_SprDataOffset,x ;get sprite data offset + lda AreaType + cmp #$01 ;check for ground level type area + beq ChkRep ;if found, branch to next part + lda #$86 + sta Sprite_Tilenumber,y ;otherwise remove brick tiles with lines + sta Sprite_Tilenumber+4,y ;and replace then with lineless brick tiles +ChkRep: lda Block_Metatile,x ;check replacement metatile + cmp #$c4 ;if not used block metatile, then + bne BlkOffscr ;branch ahead to use current graphics + lda #$87 ;set A for used block tile + iny ;increment Y to write to tile bytes + jsr DumpFourSpr ;do sub to dump into all four sprites + dey ;return Y to original offset + lda #$03 ;set palette bits + ldx AreaType + dex ;check for ground level type area again + beq SetBFlip ;if found, use current palette bits + lsr ;otherwise set to $01 +SetBFlip: ldx ObjectOffset ;put block object offset back in X + sta Sprite_Attributes,y ;store attribute byte as-is in first sprite + ora #%01000000 + sta Sprite_Attributes+4,y ;set horizontal flip bit for second sprite + ora #%10000000 + sta Sprite_Attributes+12,y ;set both flip bits for fourth sprite + and #%10000011 + sta Sprite_Attributes+8,y ;set vertical flip bit for third sprite +BlkOffscr: lda Block_OffscreenBits ;get offscreen bits for block object + pha ;save to stack + and #%00000100 ;check to see if d2 in offscreen bits are set + beq PullOfsB ;if not set, branch, otherwise move sprites offscreen + lda #$f8 ;move offscreen two OAMs + sta Sprite_Y_Position+4,y ;on the right side + sta Sprite_Y_Position+12,y +PullOfsB: pla ;pull offscreen bits from stack +ChkLeftCo: and #%00001000 ;check to see if d3 in offscreen bits are set + beq ExDBlk ;if not set, branch, otherwise move sprites offscreen + +MoveColOffscreen: + lda #$f8 ;move offscreen two OAMs + sta Sprite_Y_Position,y ;on the left side (or two rows of enemy on either side + sta Sprite_Y_Position+8,y ;if branched here from enemy graphics handler) +ExDBlk: rts + +;------------------------------------------------------------------------------------- +;$00 - used to hold palette bits for attribute byte or relative X position + +DrawBrickChunks: + lda #$02 ;set palette bits here + sta $00 + lda #$75 ;set tile number for ball (something residual, likely) + ldy GameEngineSubroutine + cpy #$05 ;if end-of-level routine running, + beq DChunks ;use palette and tile number assigned + lda #$03 ;otherwise set different palette bits + sta $00 + lda #$84 ;and set tile number for brick chunks +DChunks: ldy Block_SprDataOffset,x ;get OAM data offset + iny ;increment to start with tile bytes in OAM + jsr DumpFourSpr ;do sub to dump tile number into all four sprites + lda FrameCounter ;get frame counter + asl + asl + asl ;move low nybble to high + asl + and #$c0 ;get what was originally d3-d2 of low nybble + ora $00 ;add palette bits + iny ;increment offset for attribute bytes + jsr DumpFourSpr ;do sub to dump attribute data into all four sprites + dey + dey ;decrement offset to Y coordinate + lda Block_Rel_YPos ;get first block object's relative vertical coordinate + jsr DumpTwoSpr ;do sub to dump current Y coordinate into two sprites + lda Block_Rel_XPos ;get first block object's relative horizontal coordinate + sta Sprite_X_Position,y ;save into X coordinate of first sprite + lda Block_Orig_XPos,x ;get original horizontal coordinate + sec + sbc ScreenLeft_X_Pos ;subtract coordinate of left side from original coordinate + sta $00 ;store result as relative horizontal coordinate of original + sec + sbc Block_Rel_XPos ;get difference of relative positions of original - current + adc $00 ;add original relative position to result + adc #$06 ;plus 6 pixels to position second brick chunk correctly + sta Sprite_X_Position+4,y ;save into X coordinate of second sprite + lda Block_Rel_YPos+1 ;get second block object's relative vertical coordinate + sta Sprite_Y_Position+8,y + sta Sprite_Y_Position+12,y ;dump into Y coordinates of third and fourth sprites + lda Block_Rel_XPos+1 ;get second block object's relative horizontal coordinate + sta Sprite_X_Position+8,y ;save into X coordinate of third sprite + lda $00 ;use original relative horizontal position + sec + sbc Block_Rel_XPos+1 ;get difference of relative positions of original - current + adc $00 ;add original relative position to result + adc #$06 ;plus 6 pixels to position fourth brick chunk correctly + sta Sprite_X_Position+12,y ;save into X coordinate of fourth sprite + lda Block_OffscreenBits ;get offscreen bits for block object + jsr ChkLeftCo ;do sub to move left half of sprites offscreen if necessary + lda Block_OffscreenBits ;get offscreen bits again + asl ;shift d7 into carry + bcc ChnkOfs ;if d7 not set, branch to last part + lda #$f8 + jsr DumpTwoSpr ;otherwise move top sprites offscreen +ChnkOfs: lda $00 ;if relative position on left side of screen, + bpl ExBCDr ;go ahead and leave + lda Sprite_X_Position,y ;otherwise compare left-side X coordinate + cmp Sprite_X_Position+4,y ;to right-side X coordinate + bcc ExBCDr ;branch to leave if less + lda #$f8 ;otherwise move right half of sprites offscreen + sta Sprite_Y_Position+4,y + sta Sprite_Y_Position+12,y +ExBCDr: rts ;leave + +;------------------------------------------------------------------------------------- + +DrawFireball: + ldy FBall_SprDataOffset,x ;get fireball's sprite data offset + lda Fireball_Rel_YPos ;get relative vertical coordinate + sta Sprite_Y_Position,y ;store as sprite Y coordinate + lda Fireball_Rel_XPos ;get relative horizontal coordinate + sta Sprite_X_Position,y ;store as sprite X coordinate, then do shared code + +DrawFirebar: + lda FrameCounter ;get frame counter + lsr ;divide by four + lsr + pha ;save result to stack + and #$01 ;mask out all but last bit + eor #$64 ;set either tile $64 or $65 as fireball tile + sta Sprite_Tilenumber,y ;thus tile changes every four frames + pla ;get from stack + lsr ;divide by four again + lsr + lda #$02 ;load value $02 to set palette in attrib byte + bcc FireA ;if last bit shifted out was not set, skip this + ora #%11000000 ;otherwise flip both ways every eight frames +FireA: sta Sprite_Attributes,y ;store attribute byte and leave + rts + +;------------------------------------------------------------------------------------- + +ExplosionTiles: + .db $68, $67, $66 + +DrawExplosion_Fireball: + ldy Alt_SprDataOffset,x ;get OAM data offset of alternate sort for fireball's explosion + lda Fireball_State,x ;load fireball state + inc Fireball_State,x ;increment state for next frame + lsr ;divide by 2 + and #%00000111 ;mask out all but d3-d1 + cmp #$03 ;check to see if time to kill fireball + bcs KillFireBall ;branch if so, otherwise continue to draw explosion + +DrawExplosion_Fireworks: + tax ;use whatever's in A for offset + lda ExplosionTiles,x ;get tile number using offset + iny ;increment Y (contains sprite data offset) + jsr DumpFourSpr ;and dump into tile number part of sprite data + dey ;decrement Y so we have the proper offset again + ldx ObjectOffset ;return enemy object buffer offset to X + lda Fireball_Rel_YPos ;get relative vertical coordinate + sec ;subtract four pixels vertically + sbc #$04 ;for first and third sprites + sta Sprite_Y_Position,y + sta Sprite_Y_Position+8,y + clc ;add eight pixels vertically + adc #$08 ;for second and fourth sprites + sta Sprite_Y_Position+4,y + sta Sprite_Y_Position+12,y + lda Fireball_Rel_XPos ;get relative horizontal coordinate + sec ;subtract four pixels horizontally + sbc #$04 ;for first and second sprites + sta Sprite_X_Position,y + sta Sprite_X_Position+4,y + clc ;add eight pixels horizontally + adc #$08 ;for third and fourth sprites + sta Sprite_X_Position+8,y + sta Sprite_X_Position+12,y + lda #$02 ;set palette attributes for all sprites, but + sta Sprite_Attributes,y ;set no flip at all for first sprite + lda #$82 + sta Sprite_Attributes+4,y ;set vertical flip for second sprite + lda #$42 + sta Sprite_Attributes+8,y ;set horizontal flip for third sprite + lda #$c2 + sta Sprite_Attributes+12,y ;set both flips for fourth sprite + rts ;we are done + +KillFireBall: + lda #$00 ;clear fireball state to kill it + sta Fireball_State,x + rts + +;------------------------------------------------------------------------------------- + +DrawSmallPlatform: + ldy Enemy_SprDataOffset,x ;get OAM data offset + lda #$5b ;load tile number for small platforms + iny ;increment offset for tile numbers + jsr DumpSixSpr ;dump tile number into all six sprites + iny ;increment offset for attributes + lda #$02 ;load palette controls + jsr DumpSixSpr ;dump attributes into all six sprites + dey ;decrement for original offset + dey + lda Enemy_Rel_XPos ;get relative horizontal coordinate + sta Sprite_X_Position,y + sta Sprite_X_Position+12,y ;dump as X coordinate into first and fourth sprites + clc + adc #$08 ;add eight pixels + sta Sprite_X_Position+4,y ;dump into second and fifth sprites + sta Sprite_X_Position+16,y + clc + adc #$08 ;add eight more pixels + sta Sprite_X_Position+8,y ;dump into third and sixth sprites + sta Sprite_X_Position+20,y + lda Enemy_Y_Position,x ;get vertical coordinate + tax + pha ;save to stack + cpx #$20 ;if vertical coordinate below status bar, + bcs TopSP ;do not mess with it + lda #$f8 ;otherwise move first three sprites offscreen +TopSP: jsr DumpThreeSpr ;dump vertical coordinate into Y coordinates + pla ;pull from stack + clc + adc #$80 ;add 128 pixels + tax + cpx #$20 ;if below status bar (taking wrap into account) + bcs BotSP ;then do not change altered coordinate + lda #$f8 ;otherwise move last three sprites offscreen +BotSP: sta Sprite_Y_Position+12,y ;dump vertical coordinate + 128 pixels + sta Sprite_Y_Position+16,y ;into Y coordinates + sta Sprite_Y_Position+20,y + lda Enemy_OffscreenBits ;get offscreen bits + pha ;save to stack + and #%00001000 ;check d3 + beq SOfs + lda #$f8 ;if d3 was set, move first and + sta Sprite_Y_Position,y ;fourth sprites offscreen + sta Sprite_Y_Position+12,y +SOfs: pla ;move out and back into stack + pha + and #%00000100 ;check d2 + beq SOfs2 + lda #$f8 ;if d2 was set, move second and + sta Sprite_Y_Position+4,y ;fifth sprites offscreen + sta Sprite_Y_Position+16,y +SOfs2: pla ;get from stack + and #%00000010 ;check d1 + beq ExSPl + lda #$f8 ;if d1 was set, move third and + sta Sprite_Y_Position+8,y ;sixth sprites offscreen + sta Sprite_Y_Position+20,y +ExSPl: ldx ObjectOffset ;get enemy object offset and leave + rts + +;------------------------------------------------------------------------------------- + +DrawBubble: + ldy Player_Y_HighPos ;if player's vertical high position + dey ;not within screen, skip all of this + bne ExDBub + lda Bubble_OffscreenBits ;check air bubble's offscreen bits + and #%00001000 + bne ExDBub ;if bit set, branch to leave + ldy Bubble_SprDataOffset,x ;get air bubble's OAM data offset + lda Bubble_Rel_XPos ;get relative horizontal coordinate + sta Sprite_X_Position,y ;store as X coordinate here + lda Bubble_Rel_YPos ;get relative vertical coordinate + sta Sprite_Y_Position,y ;store as Y coordinate here + lda #$74 + sta Sprite_Tilenumber,y ;put air bubble tile into OAM data + lda #$02 + sta Sprite_Attributes,y ;set attribute byte +ExDBub: rts ;leave + +;------------------------------------------------------------------------------------- +;$00 - used to store player's vertical offscreen bits + +PlayerGfxTblOffsets: + .db $20, $28, $c8, $18, $00, $40, $50, $58 + .db $80, $88, $b8, $78, $60, $a0, $b0, $b8 + +;tiles arranged in order, 2 tiles per row, top to bottom + +PlayerGraphicsTable: +;big player table + .db $00, $01, $02, $03, $04, $05, $06, $07 ;walking frame 1 + .db $08, $09, $0a, $0b, $0c, $0d, $0e, $0f ; frame 2 + .db $10, $11, $12, $13, $14, $15, $16, $17 ; frame 3 + .db $18, $19, $1a, $1b, $1c, $1d, $1e, $1f ;skidding + .db $20, $21, $22, $23, $24, $25, $26, $27 ;jumping + .db $08, $09, $28, $29, $2a, $2b, $2c, $2d ;swimming frame 1 + .db $08, $09, $0a, $0b, $0c, $30, $2c, $2d ; frame 2 + .db $08, $09, $0a, $0b, $2e, $2f, $2c, $2d ; frame 3 + .db $08, $09, $28, $29, $2a, $2b, $5c, $5d ;climbing frame 1 + .db $08, $09, $0a, $0b, $0c, $0d, $5e, $5f ; frame 2 + .db $fc, $fc, $08, $09, $58, $59, $5a, $5a ;crouching + .db $08, $09, $28, $29, $2a, $2b, $0e, $0f ;fireball throwing + +;small player table + .db $fc, $fc, $fc, $fc, $32, $33, $34, $35 ;walking frame 1 + .db $fc, $fc, $fc, $fc, $36, $37, $38, $39 ; frame 2 + .db $fc, $fc, $fc, $fc, $3a, $37, $3b, $3c ; frame 3 + .db $fc, $fc, $fc, $fc, $3d, $3e, $3f, $40 ;skidding + .db $fc, $fc, $fc, $fc, $32, $41, $42, $43 ;jumping + .db $fc, $fc, $fc, $fc, $32, $33, $44, $45 ;swimming frame 1 + .db $fc, $fc, $fc, $fc, $32, $33, $44, $47 ; frame 2 + .db $fc, $fc, $fc, $fc, $32, $33, $48, $49 ; frame 3 + .db $fc, $fc, $fc, $fc, $32, $33, $90, $91 ;climbing frame 1 + .db $fc, $fc, $fc, $fc, $3a, $37, $92, $93 ; frame 2 + .db $fc, $fc, $fc, $fc, $9e, $9e, $9f, $9f ;killed + +;used by both player sizes + .db $fc, $fc, $fc, $fc, $3a, $37, $4f, $4f ;small player standing + .db $fc, $fc, $00, $01, $4c, $4d, $4e, $4e ;intermediate grow frame + .db $00, $01, $4c, $4d, $4a, $4a, $4b, $4b ;big player standing + +SwimKickTileNum: + .db $31, $46 + +PlayerGfxHandler: + lda InjuryTimer ;if player's injured invincibility timer + beq CntPl ;not set, skip checkpoint and continue code + lda FrameCounter + lsr ;otherwise check frame counter and branch + bcs ExPGH ;to leave on every other frame (when d0 is set) +CntPl: lda GameEngineSubroutine ;if executing specific game engine routine, + cmp #$0b ;branch ahead to some other part + beq PlayerKilled + lda PlayerChangeSizeFlag ;if grow/shrink flag set + bne DoChangeSize ;then branch to some other code + ldy SwimmingFlag ;if swimming flag set, branch to + beq FindPlayerAction ;different part, do not return + lda Player_State + cmp #$00 ;if player status normal, + beq FindPlayerAction ;branch and do not return + jsr FindPlayerAction ;otherwise jump and return + lda FrameCounter + and #%00000100 ;check frame counter for d2 set (8 frames every + bne ExPGH ;eighth frame), and branch if set to leave + tax ;initialize X to zero + ldy Player_SprDataOffset ;get player sprite data offset + lda PlayerFacingDir ;get player's facing direction + lsr + bcs SwimKT ;if player facing to the right, use current offset + iny + iny ;otherwise move to next OAM data + iny + iny +SwimKT: lda PlayerSize ;check player's size + beq BigKTS ;if big, use first tile + lda Sprite_Tilenumber+24,y ;check tile number of seventh/eighth sprite + cmp SwimTileRepOffset ;against tile number in player graphics table + beq ExPGH ;if spr7/spr8 tile number = value, branch to leave + inx ;otherwise increment X for second tile +BigKTS: lda SwimKickTileNum,x ;overwrite tile number in sprite 7/8 + sta Sprite_Tilenumber+24,y ;to animate player's feet when swimming +ExPGH: rts ;then leave + +FindPlayerAction: + jsr ProcessPlayerAction ;find proper offset to graphics table by player's actions + jmp PlayerGfxProcessing ;draw player, then process for fireball throwing + +DoChangeSize: + jsr HandleChangeSize ;find proper offset to graphics table for grow/shrink + jmp PlayerGfxProcessing ;draw player, then process for fireball throwing + +PlayerKilled: + ldy #$0e ;load offset for player killed + lda PlayerGfxTblOffsets,y ;get offset to graphics table + +PlayerGfxProcessing: + sta PlayerGfxOffset ;store offset to graphics table here + lda #$04 + jsr RenderPlayerSub ;draw player based on offset loaded + jsr ChkForPlayerAttrib ;set horizontal flip bits as necessary + lda FireballThrowingTimer + beq PlayerOffscreenChk ;if fireball throw timer not set, skip to the end + ldy #$00 ;set value to initialize by default + lda PlayerAnimTimer ;get animation frame timer + cmp FireballThrowingTimer ;compare to fireball throw timer + sty FireballThrowingTimer ;initialize fireball throw timer + bcs PlayerOffscreenChk ;if animation frame timer => fireball throw timer skip to end + sta FireballThrowingTimer ;otherwise store animation timer into fireball throw timer + ldy #$07 ;load offset for throwing + lda PlayerGfxTblOffsets,y ;get offset to graphics table + sta PlayerGfxOffset ;store it for use later + ldy #$04 ;set to update four sprite rows by default + lda Player_X_Speed + ora Left_Right_Buttons ;check for horizontal speed or left/right button press + beq SUpdR ;if no speed or button press, branch using set value in Y + dey ;otherwise set to update only three sprite rows +SUpdR: tya ;save in A for use + jsr RenderPlayerSub ;in sub, draw player object again + +PlayerOffscreenChk: + lda Player_OffscreenBits ;get player's offscreen bits + lsr + lsr ;move vertical bits to low nybble + lsr + lsr + sta $00 ;store here + ldx #$03 ;check all four rows of player sprites + lda Player_SprDataOffset ;get player's sprite data offset + clc + adc #$18 ;add 24 bytes to start at bottom row + tay ;set as offset here +PROfsLoop: lda #$f8 ;load offscreen Y coordinate just in case + lsr $00 ;shift bit into carry + bcc NPROffscr ;if bit not set, skip, do not move sprites + jsr DumpTwoSpr ;otherwise dump offscreen Y coordinate into sprite data +NPROffscr: tya + sec ;subtract eight bytes to do + sbc #$08 ;next row up + tay + dex ;decrement row counter + bpl PROfsLoop ;do this until all sprite rows are checked + rts ;then we are done! + +;------------------------------------------------------------------------------------- + +IntermediatePlayerData: + .db $58, $01, $00, $60, $ff, $04 + +DrawPlayer_Intermediate: + ldx #$05 ;store data into zero page memory +PIntLoop: lda IntermediatePlayerData,x ;load data to display player as he always + sta $02,x ;appears on world/lives display + dex + bpl PIntLoop ;do this until all data is loaded + ldx #$b8 ;load offset for small standing + ldy #$04 ;load sprite data offset + jsr DrawPlayerLoop ;draw player accordingly + lda Sprite_Attributes+36 ;get empty sprite attributes + ora #%01000000 ;set horizontal flip bit for bottom-right sprite + sta Sprite_Attributes+32 ;store and leave + rts + +;------------------------------------------------------------------------------------- +;$00-$01 - used to hold tile numbers, $00 also used to hold upper extent of animation frames +;$02 - vertical position +;$03 - facing direction, used as horizontal flip control +;$04 - attributes +;$05 - horizontal position +;$07 - number of rows to draw +;these also used in IntermediatePlayerData + +RenderPlayerSub: + sta $07 ;store number of rows of sprites to draw + lda Player_Rel_XPos + sta Player_Pos_ForScroll ;store player's relative horizontal position + sta $05 ;store it here also + lda Player_Rel_YPos + sta $02 ;store player's vertical position + lda PlayerFacingDir + sta $03 ;store player's facing direction + lda Player_SprAttrib + sta $04 ;store player's sprite attributes + ldx PlayerGfxOffset ;load graphics table offset + ldy Player_SprDataOffset ;get player's sprite data offset + +DrawPlayerLoop: + lda PlayerGraphicsTable,x ;load player's left side + sta $00 + lda PlayerGraphicsTable+1,x ;now load right side + jsr DrawOneSpriteRow + dec $07 ;decrement rows of sprites to draw + bne DrawPlayerLoop ;do this until all rows are drawn + rts + +ProcessPlayerAction: + lda Player_State ;get player's state + cmp #$03 + beq ActionClimbing ;if climbing, branch here + cmp #$02 + beq ActionFalling ;if falling, branch here + cmp #$01 + bne ProcOnGroundActs ;if not jumping, branch here + lda SwimmingFlag + bne ActionSwimming ;if swimming flag set, branch elsewhere + ldy #$06 ;load offset for crouching + lda CrouchingFlag ;get crouching flag + bne NonAnimatedActs ;if set, branch to get offset for graphics table + ldy #$00 ;otherwise load offset for jumping + jmp NonAnimatedActs ;go to get offset to graphics table + +ProcOnGroundActs: + ldy #$06 ;load offset for crouching + lda CrouchingFlag ;get crouching flag + bne NonAnimatedActs ;if set, branch to get offset for graphics table + ldy #$02 ;load offset for standing + lda Player_X_Speed ;check player's horizontal speed + ora Left_Right_Buttons ;and left/right controller bits + beq NonAnimatedActs ;if no speed or buttons pressed, use standing offset + lda Player_XSpeedAbsolute ;load walking/running speed + cmp #$09 + bcc ActionWalkRun ;if less than a certain amount, branch, too slow to skid + lda Player_MovingDir ;otherwise check to see if moving direction + and PlayerFacingDir ;and facing direction are the same + bne ActionWalkRun ;if moving direction = facing direction, branch, don't skid + iny ;otherwise increment to skid offset ($03) + +NonAnimatedActs: + jsr GetGfxOffsetAdder ;do a sub here to get offset adder for graphics table + lda #$00 + sta PlayerAnimCtrl ;initialize animation frame control + lda PlayerGfxTblOffsets,y ;load offset to graphics table using size as offset + rts + +ActionFalling: + ldy #$04 ;load offset for walking/running + jsr GetGfxOffsetAdder ;get offset to graphics table + jmp GetCurrentAnimOffset ;execute instructions for falling state + +ActionWalkRun: + ldy #$04 ;load offset for walking/running + jsr GetGfxOffsetAdder ;get offset to graphics table + jmp FourFrameExtent ;execute instructions for normal state + +ActionClimbing: + ldy #$05 ;load offset for climbing + lda Player_Y_Speed ;check player's vertical speed + beq NonAnimatedActs ;if no speed, branch, use offset as-is + jsr GetGfxOffsetAdder ;otherwise get offset for graphics table + jmp ThreeFrameExtent ;then skip ahead to more code + +ActionSwimming: + ldy #$01 ;load offset for swimming + jsr GetGfxOffsetAdder + lda JumpSwimTimer ;check jump/swim timer + ora PlayerAnimCtrl ;and animation frame control + bne FourFrameExtent ;if any one of these set, branch ahead + lda A_B_Buttons + asl ;check for A button pressed + bcs FourFrameExtent ;branch to same place if A button pressed + +GetCurrentAnimOffset: + lda PlayerAnimCtrl ;get animation frame control + jmp GetOffsetFromAnimCtrl ;jump to get proper offset to graphics table + +FourFrameExtent: + lda #$03 ;load upper extent for frame control + jmp AnimationControl ;jump to get offset and animate player object + +ThreeFrameExtent: + lda #$02 ;load upper extent for frame control for climbing + +AnimationControl: + sta $00 ;store upper extent here + jsr GetCurrentAnimOffset ;get proper offset to graphics table + pha ;save offset to stack + lda PlayerAnimTimer ;load animation frame timer + bne ExAnimC ;branch if not expired + lda PlayerAnimTimerSet ;get animation frame timer amount + sta PlayerAnimTimer ;and set timer accordingly + lda PlayerAnimCtrl + clc ;add one to animation frame control + adc #$01 + cmp $00 ;compare to upper extent + bcc SetAnimC ;if frame control + 1 < upper extent, use as next + lda #$00 ;otherwise initialize frame control +SetAnimC: sta PlayerAnimCtrl ;store as new animation frame control +ExAnimC: pla ;get offset to graphics table from stack and leave + rts + +GetGfxOffsetAdder: + lda PlayerSize ;get player's size + beq SzOfs ;if player big, use current offset as-is + tya ;for big player + clc ;otherwise add eight bytes to offset + adc #$08 ;for small player + tay +SzOfs: rts ;go back + +ChangeSizeOffsetAdder: + .db $00, $01, $00, $01, $00, $01, $02, $00, $01, $02 + .db $02, $00, $02, $00, $02, $00, $02, $00, $02, $00 + +HandleChangeSize: + ldy PlayerAnimCtrl ;get animation frame control + lda FrameCounter + and #%00000011 ;get frame counter and execute this code every + bne GorSLog ;fourth frame, otherwise branch ahead + iny ;increment frame control + cpy #$0a ;check for preset upper extent + bcc CSzNext ;if not there yet, skip ahead to use + ldy #$00 ;otherwise initialize both grow/shrink flag + sty PlayerChangeSizeFlag ;and animation frame control +CSzNext: sty PlayerAnimCtrl ;store proper frame control +GorSLog: lda PlayerSize ;get player's size + bne ShrinkPlayer ;if player small, skip ahead to next part + lda ChangeSizeOffsetAdder,y ;get offset adder based on frame control as offset + ldy #$0f ;load offset for player growing + +GetOffsetFromAnimCtrl: + asl ;multiply animation frame control + asl ;by eight to get proper amount + asl ;to add to our offset + adc PlayerGfxTblOffsets,y ;add to offset to graphics table + rts ;and return with result in A + +ShrinkPlayer: + tya ;add ten bytes to frame control as offset + clc + adc #$0a ;this thing apparently uses two of the swimming frames + tax ;to draw the player shrinking + ldy #$09 ;load offset for small player swimming + lda ChangeSizeOffsetAdder,x ;get what would normally be offset adder + bne ShrPlF ;and branch to use offset if nonzero + ldy #$01 ;otherwise load offset for big player swimming +ShrPlF: lda PlayerGfxTblOffsets,y ;get offset to graphics table based on offset loaded + rts ;and leave + +ChkForPlayerAttrib: + ldy Player_SprDataOffset ;get sprite data offset + lda GameEngineSubroutine + cmp #$0b ;if executing specific game engine routine, + beq KilledAtt ;branch to change third and fourth row OAM attributes + lda PlayerGfxOffset ;get graphics table offset + cmp #$50 + beq C_S_IGAtt ;if crouch offset, either standing offset, + cmp #$b8 ;or intermediate growing offset, + beq C_S_IGAtt ;go ahead and execute code to change + cmp #$c0 ;fourth row OAM attributes only + beq C_S_IGAtt + cmp #$c8 + bne ExPlyrAt ;if none of these, branch to leave +KilledAtt: lda Sprite_Attributes+16,y + and #%00111111 ;mask out horizontal and vertical flip bits + sta Sprite_Attributes+16,y ;for third row sprites and save + lda Sprite_Attributes+20,y + and #%00111111 + ora #%01000000 ;set horizontal flip bit for second + sta Sprite_Attributes+20,y ;sprite in the third row +C_S_IGAtt: lda Sprite_Attributes+24,y + and #%00111111 ;mask out horizontal and vertical flip bits + sta Sprite_Attributes+24,y ;for fourth row sprites and save + lda Sprite_Attributes+28,y + and #%00111111 + ora #%01000000 ;set horizontal flip bit for second + sta Sprite_Attributes+28,y ;sprite in the fourth row +ExPlyrAt: rts ;leave + +;------------------------------------------------------------------------------------- +;$00 - used in adding to get proper offset + +RelativePlayerPosition: + ldx #$00 ;set offsets for relative cooordinates + ldy #$00 ;routine to correspond to player object + jmp RelWOfs ;get the coordinates + +RelativeBubblePosition: + ldy #$01 ;set for air bubble offsets + jsr GetProperObjOffset ;modify X to get proper air bubble offset + ldy #$03 + jmp RelWOfs ;get the coordinates + +RelativeFireballPosition: + ldy #$00 ;set for fireball offsets + jsr GetProperObjOffset ;modify X to get proper fireball offset + ldy #$02 +RelWOfs: jsr GetObjRelativePosition ;get the coordinates + ldx ObjectOffset ;return original offset + rts ;leave + +RelativeMiscPosition: + ldy #$02 ;set for misc object offsets + jsr GetProperObjOffset ;modify X to get proper misc object offset + ldy #$06 + jmp RelWOfs ;get the coordinates + +RelativeEnemyPosition: + lda #$01 ;get coordinates of enemy object + ldy #$01 ;relative to the screen + jmp VariableObjOfsRelPos + +RelativeBlockPosition: + lda #$09 ;get coordinates of one block object + ldy #$04 ;relative to the screen + jsr VariableObjOfsRelPos + inx ;adjust offset for other block object if any + inx + lda #$09 + iny ;adjust other and get coordinates for other one + +VariableObjOfsRelPos: + stx $00 ;store value to add to A here + clc + adc $00 ;add A to value stored + tax ;use as enemy offset + jsr GetObjRelativePosition + ldx ObjectOffset ;reload old object offset and leave + rts + +GetObjRelativePosition: + lda SprObject_Y_Position,x ;load vertical coordinate low + sta SprObject_Rel_YPos,y ;store here + lda SprObject_X_Position,x ;load horizontal coordinate + sec ;subtract left edge coordinate + sbc ScreenLeft_X_Pos + sta SprObject_Rel_XPos,y ;store result here + rts + +;------------------------------------------------------------------------------------- +;$00 - used as temp variable to hold offscreen bits + +GetPlayerOffscreenBits: + ldx #$00 ;set offsets for player-specific variables + ldy #$00 ;and get offscreen information about player + jmp GetOffScreenBitsSet + +GetFireballOffscreenBits: + ldy #$00 ;set for fireball offsets + jsr GetProperObjOffset ;modify X to get proper fireball offset + ldy #$02 ;set other offset for fireball's offscreen bits + jmp GetOffScreenBitsSet ;and get offscreen information about fireball + +GetBubbleOffscreenBits: + ldy #$01 ;set for air bubble offsets + jsr GetProperObjOffset ;modify X to get proper air bubble offset + ldy #$03 ;set other offset for airbubble's offscreen bits + jmp GetOffScreenBitsSet ;and get offscreen information about air bubble + +GetMiscOffscreenBits: + ldy #$02 ;set for misc object offsets + jsr GetProperObjOffset ;modify X to get proper misc object offset + ldy #$06 ;set other offset for misc object's offscreen bits + jmp GetOffScreenBitsSet ;and get offscreen information about misc object + +ObjOffsetData: + .db $07, $16, $0d + +GetProperObjOffset: + txa ;move offset to A + clc + adc ObjOffsetData,y ;add amount of bytes to offset depending on setting in Y + tax ;put back in X and leave + rts + +GetEnemyOffscreenBits: + lda #$01 ;set A to add 1 byte in order to get enemy offset + ldy #$01 ;set Y to put offscreen bits in Enemy_OffscreenBits + jmp SetOffscrBitsOffset + +GetBlockOffscreenBits: + lda #$09 ;set A to add 9 bytes in order to get block obj offset + ldy #$04 ;set Y to put offscreen bits in Block_OffscreenBits + +SetOffscrBitsOffset: + stx $00 + clc ;add contents of X to A to get + adc $00 ;appropriate offset, then give back to X + tax + +GetOffScreenBitsSet: + tya ;save offscreen bits offset to stack for now + pha + jsr RunOffscrBitsSubs + asl ;move low nybble to high nybble + asl + asl + asl + ora $00 ;mask together with previously saved low nybble + sta $00 ;store both here + pla ;get offscreen bits offset from stack + tay + lda $00 ;get value here and store elsewhere + sta SprObject_OffscrBits,y + ldx ObjectOffset + rts + +RunOffscrBitsSubs: + jsr GetXOffscreenBits ;do subroutine here + lsr ;move high nybble to low + lsr + lsr + lsr + sta $00 ;store here + jmp GetYOffscreenBits + +;-------------------------------- +;(these apply to these three subsections) +;$04 - used to store proper offset +;$05 - used as adder in DividePDiff +;$06 - used to store preset value used to compare to pixel difference in $07 +;$07 - used to store difference between coordinates of object and screen edges + +XOffscreenBitsData: + .db $7f, $3f, $1f, $0f, $07, $03, $01, $00 + .db $80, $c0, $e0, $f0, $f8, $fc, $fe, $ff + +DefaultXOnscreenOfs: + .db $07, $0f, $07 + +GetXOffscreenBits: + stx $04 ;save position in buffer to here + ldy #$01 ;start with right side of screen +XOfsLoop: lda ScreenEdge_X_Pos,y ;get pixel coordinate of edge + sec ;get difference between pixel coordinate of edge + sbc SprObject_X_Position,x ;and pixel coordinate of object position + sta $07 ;store here + lda ScreenEdge_PageLoc,y ;get page location of edge + sbc SprObject_PageLoc,x ;subtract from page location of object position + ldx DefaultXOnscreenOfs,y ;load offset value here + cmp #$00 + bmi XLdBData ;if beyond right edge or in front of left edge, branch + ldx DefaultXOnscreenOfs+1,y ;if not, load alternate offset value here + cmp #$01 + bpl XLdBData ;if one page or more to the left of either edge, branch + lda #$38 ;if no branching, load value here and store + sta $06 + lda #$08 ;load some other value and execute subroutine + jsr DividePDiff +XLdBData: lda XOffscreenBitsData,x ;get bits here + ldx $04 ;reobtain position in buffer + cmp #$00 ;if bits not zero, branch to leave + bne ExXOfsBS + dey ;otherwise, do left side of screen now + bpl XOfsLoop ;branch if not already done with left side +ExXOfsBS: rts + +;-------------------------------- + +YOffscreenBitsData: + .db $00, $08, $0c, $0e + .db $0f, $07, $03, $01 + .db $00 + +DefaultYOnscreenOfs: + .db $04, $00, $04 + +HighPosUnitData: + .db $ff, $00 + +GetYOffscreenBits: + stx $04 ;save position in buffer to here + ldy #$01 ;start with top of screen +YOfsLoop: lda HighPosUnitData,y ;load coordinate for edge of vertical unit + sec + sbc SprObject_Y_Position,x ;subtract from vertical coordinate of object + sta $07 ;store here + lda #$01 ;subtract one from vertical high byte of object + sbc SprObject_Y_HighPos,x + ldx DefaultYOnscreenOfs,y ;load offset value here + cmp #$00 + bmi YLdBData ;if under top of the screen or beyond bottom, branch + ldx DefaultYOnscreenOfs+1,y ;if not, load alternate offset value here + cmp #$01 + bpl YLdBData ;if one vertical unit or more above the screen, branch + lda #$20 ;if no branching, load value here and store + sta $06 + lda #$04 ;load some other value and execute subroutine + jsr DividePDiff +YLdBData: lda YOffscreenBitsData,x ;get offscreen data bits using offset + ldx $04 ;reobtain position in buffer + cmp #$00 + bne ExYOfsBS ;if bits not zero, branch to leave + dey ;otherwise, do bottom of the screen now + bpl YOfsLoop +ExYOfsBS: rts + +;-------------------------------- + +DividePDiff: + sta $05 ;store current value in A here + lda $07 ;get pixel difference + cmp $06 ;compare to preset value + bcs ExDivPD ;if pixel difference >= preset value, branch + lsr ;divide by eight + lsr + lsr + and #$07 ;mask out all but 3 LSB + cpy #$01 ;right side of the screen or top? + bcs SetOscrO ;if so, branch, use difference / 8 as offset + adc $05 ;if not, add value to difference / 8 +SetOscrO: tax ;use as offset +ExDivPD: rts ;leave + +;------------------------------------------------------------------------------------- +;$00-$01 - tile numbers +;$02 - Y coordinate +;$03 - flip control +;$04 - sprite attributes +;$05 - X coordinate + +DrawSpriteObject: + lda $03 ;get saved flip control bits + lsr + lsr ;move d1 into carry + lda $00 + bcc NoHFlip ;if d1 not set, branch + sta Sprite_Tilenumber+4,y ;store first tile into second sprite + lda $01 ;and second into first sprite + sta Sprite_Tilenumber,y + lda #$40 ;activate horizontal flip OAM attribute + bne SetHFAt ;and unconditionally branch +NoHFlip: sta Sprite_Tilenumber,y ;store first tile into first sprite + lda $01 ;and second into second sprite + sta Sprite_Tilenumber+4,y + lda #$00 ;clear bit for horizontal flip +SetHFAt: ora $04 ;add other OAM attributes if necessary + sta Sprite_Attributes,y ;store sprite attributes + sta Sprite_Attributes+4,y + lda $02 ;now the y coordinates + sta Sprite_Y_Position,y ;note because they are + sta Sprite_Y_Position+4,y ;side by side, they are the same + lda $05 + sta Sprite_X_Position,y ;store x coordinate, then + clc ;add 8 pixels and store another to + adc #$08 ;put them side by side + sta Sprite_X_Position+4,y + lda $02 ;add eight pixels to the next y + clc ;coordinate + adc #$08 + sta $02 + tya ;add eight to the offset in Y to + clc ;move to the next two sprites + adc #$08 + tay + inx ;increment offset to return it to the + inx ;routine that called this subroutine + rts + +;------------------------------------------------------------------------------------- + +;unused space + .db $ff, $ff, $ff, $ff, $ff, $ff + +;------------------------------------------------------------------------------------- + +SoundEngine: + lda OperMode ;are we in title screen mode? + bne SndOn + sta SND_MASTERCTRL_REG ;if so, disable sound and leave + rts +SndOn: lda #$ff + sta JOYPAD_PORT2 ;disable irqs and set frame counter mode??? + lda #$0f + sta SND_MASTERCTRL_REG ;enable first four channels + lda PauseModeFlag ;is sound already in pause mode? + bne InPause + lda PauseSoundQueue ;if not, check pause sfx queue + cmp #$01 + bne RunSoundSubroutines ;if queue is empty, skip pause mode routine +InPause: lda PauseSoundBuffer ;check pause sfx buffer + bne ContPau + lda PauseSoundQueue ;check pause queue + beq SkipSoundSubroutines + sta PauseSoundBuffer ;if queue full, store in buffer and activate + sta PauseModeFlag ;pause mode to interrupt game sounds + lda #$00 ;disable sound and clear sfx buffers + sta SND_MASTERCTRL_REG + sta Square1SoundBuffer + sta Square2SoundBuffer + sta NoiseSoundBuffer + lda #$0f + sta SND_MASTERCTRL_REG ;enable sound again + lda #$2a ;store length of sound in pause counter + sta Squ1_SfxLenCounter +PTone1F: lda #$44 ;play first tone + bne PTRegC ;unconditional branch +ContPau: lda Squ1_SfxLenCounter ;check pause length left + cmp #$24 ;time to play second? + beq PTone2F + cmp #$1e ;time to play first again? + beq PTone1F + cmp #$18 ;time to play second again? + bne DecPauC ;only load regs during times, otherwise skip +PTone2F: lda #$64 ;store reg contents and play the pause sfx +PTRegC: ldx #$84 + ldy #$7f + jsr PlaySqu1Sfx +DecPauC: dec Squ1_SfxLenCounter ;decrement pause sfx counter + bne SkipSoundSubroutines + lda #$00 ;disable sound if in pause mode and + sta SND_MASTERCTRL_REG ;not currently playing the pause sfx + lda PauseSoundBuffer ;if no longer playing pause sfx, check to see + cmp #$02 ;if we need to be playing sound again + bne SkipPIn + lda #$00 ;clear pause mode to allow game sounds again + sta PauseModeFlag +SkipPIn: lda #$00 ;clear pause sfx buffer + sta PauseSoundBuffer + beq SkipSoundSubroutines + +RunSoundSubroutines: + jsr Square1SfxHandler ;play sfx on square channel 1 + jsr Square2SfxHandler ; '' '' '' square channel 2 + jsr NoiseSfxHandler ; '' '' '' noise channel + jsr MusicHandler ;play music on all channels + lda #$00 ;clear the music queues + sta AreaMusicQueue + sta EventMusicQueue + +SkipSoundSubroutines: + lda #$00 ;clear the sound effects queues + sta Square1SoundQueue + sta Square2SoundQueue + sta NoiseSoundQueue + sta PauseSoundQueue + ldy DAC_Counter ;load some sort of counter + lda AreaMusicBuffer + and #%00000011 ;check for specific music + beq NoIncDAC + inc DAC_Counter ;increment and check counter + cpy #$30 + bcc StrWave ;if not there yet, just store it +NoIncDAC: tya + beq StrWave ;if we are at zero, do not decrement + dec DAC_Counter ;decrement counter +StrWave: sty SND_DELTA_REG+1 ;store into DMC load register (??) + rts ;we are done here + +;-------------------------------- + +Dump_Squ1_Regs: + sty SND_SQUARE1_REG+1 ;dump the contents of X and Y into square 1's control regs + stx SND_SQUARE1_REG + rts + +PlaySqu1Sfx: + jsr Dump_Squ1_Regs ;do sub to set ctrl regs for square 1, then set frequency regs + +SetFreq_Squ1: + ldx #$00 ;set frequency reg offset for square 1 sound channel + +Dump_Freq_Regs: + tay + lda FreqRegLookupTbl+1,y ;use previous contents of A for sound reg offset + beq NoTone ;if zero, then do not load + sta SND_REGISTER+2,x ;first byte goes into LSB of frequency divider + lda FreqRegLookupTbl,y ;second byte goes into 3 MSB plus extra bit for + ora #%00001000 ;length counter + sta SND_REGISTER+3,x +NoTone: rts + +Dump_Sq2_Regs: + stx SND_SQUARE2_REG ;dump the contents of X and Y into square 2's control regs + sty SND_SQUARE2_REG+1 + rts + +PlaySqu2Sfx: + jsr Dump_Sq2_Regs ;do sub to set ctrl regs for square 2, then set frequency regs + +SetFreq_Squ2: + ldx #$04 ;set frequency reg offset for square 2 sound channel + bne Dump_Freq_Regs ;unconditional branch + +SetFreq_Tri: + ldx #$08 ;set frequency reg offset for triangle sound channel + bne Dump_Freq_Regs ;unconditional branch + +;-------------------------------- + +SwimStompEnvelopeData: + .db $9f, $9b, $98, $96, $95, $94, $92, $90 + .db $90, $9a, $97, $95, $93, $92 + +PlayFlagpoleSlide: + lda #$40 ;store length of flagpole sound + sta Squ1_SfxLenCounter + lda #$62 ;load part of reg contents for flagpole sound + jsr SetFreq_Squ1 + ldx #$99 ;now load the rest + bne FPS2nd + +PlaySmallJump: + lda #$26 ;branch here for small mario jumping sound + bne JumpRegContents + +PlayBigJump: + lda #$18 ;branch here for big mario jumping sound + +JumpRegContents: + ldx #$82 ;note that small and big jump borrow each others' reg contents + ldy #$a7 ;anyway, this loads the first part of mario's jumping sound + jsr PlaySqu1Sfx + lda #$28 ;store length of sfx for both jumping sounds + sta Squ1_SfxLenCounter ;then continue on here + +ContinueSndJump: + lda Squ1_SfxLenCounter ;jumping sounds seem to be composed of three parts + cmp #$25 ;check for time to play second part yet + bne N2Prt + ldx #$5f ;load second part + ldy #$f6 + bne DmpJpFPS ;unconditional branch +N2Prt: cmp #$20 ;check for third part + bne DecJpFPS + ldx #$48 ;load third part +FPS2nd: ldy #$bc ;the flagpole slide sound shares part of third part +DmpJpFPS: jsr Dump_Squ1_Regs + bne DecJpFPS ;unconditional branch outta here + +PlayFireballThrow: + lda #$05 + ldy #$99 ;load reg contents for fireball throw sound + bne Fthrow ;unconditional branch + +PlayBump: + lda #$0a ;load length of sfx and reg contents for bump sound + ldy #$93 +Fthrow: ldx #$9e ;the fireball sound shares reg contents with the bump sound + sta Squ1_SfxLenCounter + lda #$0c ;load offset for bump sound + jsr PlaySqu1Sfx + +ContinueBumpThrow: + lda Squ1_SfxLenCounter ;check for second part of bump sound + cmp #$06 + bne DecJpFPS + lda #$bb ;load second part directly + sta SND_SQUARE1_REG+1 +DecJpFPS: bne BranchToDecLength1 ;unconditional branch + + +Square1SfxHandler: + ldy Square1SoundQueue ;check for sfx in queue + beq CheckSfx1Buffer + sty Square1SoundBuffer ;if found, put in buffer + bmi PlaySmallJump ;small jump + lsr Square1SoundQueue + bcs PlayBigJump ;big jump + lsr Square1SoundQueue + bcs PlayBump ;bump + lsr Square1SoundQueue + bcs PlaySwimStomp ;swim/stomp + lsr Square1SoundQueue + bcs PlaySmackEnemy ;smack enemy + lsr Square1SoundQueue + bcs PlayPipeDownInj ;pipedown/injury + lsr Square1SoundQueue + bcs PlayFireballThrow ;fireball throw + lsr Square1SoundQueue + bcs PlayFlagpoleSlide ;slide flagpole + +CheckSfx1Buffer: + lda Square1SoundBuffer ;check for sfx in buffer + beq ExS1H ;if not found, exit sub + bmi ContinueSndJump ;small mario jump + lsr + bcs ContinueSndJump ;big mario jump + lsr + bcs ContinueBumpThrow ;bump + lsr + bcs ContinueSwimStomp ;swim/stomp + lsr + bcs ContinueSmackEnemy ;smack enemy + lsr + bcs ContinuePipeDownInj ;pipedown/injury + lsr + bcs ContinueBumpThrow ;fireball throw + lsr + bcs DecrementSfx1Length ;slide flagpole +ExS1H: rts + +PlaySwimStomp: + lda #$0e ;store length of swim/stomp sound + sta Squ1_SfxLenCounter + ldy #$9c ;store reg contents for swim/stomp sound + ldx #$9e + lda #$26 + jsr PlaySqu1Sfx + +ContinueSwimStomp: + ldy Squ1_SfxLenCounter ;look up reg contents in data section based on + lda SwimStompEnvelopeData-1,y ;length of sound left, used to control sound's + sta SND_SQUARE1_REG ;envelope + cpy #$06 + bne BranchToDecLength1 + lda #$9e ;when the length counts down to a certain point, put this + sta SND_SQUARE1_REG+2 ;directly into the LSB of square 1's frequency divider + +BranchToDecLength1: + bne DecrementSfx1Length ;unconditional branch (regardless of how we got here) + +PlaySmackEnemy: + lda #$0e ;store length of smack enemy sound + ldy #$cb + ldx #$9f + sta Squ1_SfxLenCounter + lda #$28 ;store reg contents for smack enemy sound + jsr PlaySqu1Sfx + bne DecrementSfx1Length ;unconditional branch + +ContinueSmackEnemy: + ldy Squ1_SfxLenCounter ;check about halfway through + cpy #$08 + bne SmSpc + lda #$a0 ;if we're at the about-halfway point, make the second tone + sta SND_SQUARE1_REG+2 ;in the smack enemy sound + lda #$9f + bne SmTick +SmSpc: lda #$90 ;this creates spaces in the sound, giving it its distinct noise +SmTick: sta SND_SQUARE1_REG + +DecrementSfx1Length: + dec Squ1_SfxLenCounter ;decrement length of sfx + bne ExSfx1 + +StopSquare1Sfx: + ldx #$00 ;if end of sfx reached, clear buffer + stx $f1 ;and stop making the sfx + ldx #$0e + stx SND_MASTERCTRL_REG + ldx #$0f + stx SND_MASTERCTRL_REG +ExSfx1: rts + +PlayPipeDownInj: + lda #$2f ;load length of pipedown sound + sta Squ1_SfxLenCounter + +ContinuePipeDownInj: + lda Squ1_SfxLenCounter ;some bitwise logic, forces the regs + lsr ;to be written to only during six specific times + bcs NoPDwnL ;during which d3 must be set and d1-0 must be clear + lsr + bcs NoPDwnL + and #%00000010 + beq NoPDwnL + ldy #$91 ;and this is where it actually gets written in + ldx #$9a + lda #$44 + jsr PlaySqu1Sfx +NoPDwnL: jmp DecrementSfx1Length + +;-------------------------------- + +ExtraLifeFreqData: + .db $58, $02, $54, $56, $4e, $44 + +PowerUpGrabFreqData: + .db $4c, $52, $4c, $48, $3e, $36, $3e, $36, $30 + .db $28, $4a, $50, $4a, $64, $3c, $32, $3c, $32 + .db $2c, $24, $3a, $64, $3a, $34, $2c, $22, $2c + +;residual frequency data + .db $22, $1c, $14 + +PUp_VGrow_FreqData: + .db $14, $04, $22, $24, $16, $04, $24, $26 ;used by both + .db $18, $04, $26, $28, $1a, $04, $28, $2a + .db $1c, $04, $2a, $2c, $1e, $04, $2c, $2e ;used by vinegrow + .db $20, $04, $2e, $30, $22, $04, $30, $32 + +PlayCoinGrab: + lda #$35 ;load length of coin grab sound + ldx #$8d ;and part of reg contents + bne CGrab_TTickRegL + +PlayTimerTick: + lda #$06 ;load length of timer tick sound + ldx #$98 ;and part of reg contents + +CGrab_TTickRegL: + sta Squ2_SfxLenCounter + ldy #$7f ;load the rest of reg contents + lda #$42 ;of coin grab and timer tick sound + jsr PlaySqu2Sfx + +ContinueCGrabTTick: + lda Squ2_SfxLenCounter ;check for time to play second tone yet + cmp #$30 ;timer tick sound also executes this, not sure why + bne N2Tone + lda #$54 ;if so, load the tone directly into the reg + sta SND_SQUARE2_REG+2 +N2Tone: bne DecrementSfx2Length + +PlayBlast: + lda #$20 ;load length of fireworks/gunfire sound + sta Squ2_SfxLenCounter + ldy #$94 ;load reg contents of fireworks/gunfire sound + lda #$5e + bne SBlasJ + +ContinueBlast: + lda Squ2_SfxLenCounter ;check for time to play second part + cmp #$18 + bne DecrementSfx2Length + ldy #$93 ;load second part reg contents then + lda #$18 +SBlasJ: bne BlstSJp ;unconditional branch to load rest of reg contents + +PlayPowerUpGrab: + lda #$36 ;load length of power-up grab sound + sta Squ2_SfxLenCounter + +ContinuePowerUpGrab: + lda Squ2_SfxLenCounter ;load frequency reg based on length left over + lsr ;divide by 2 + bcs DecrementSfx2Length ;alter frequency every other frame + tay + lda PowerUpGrabFreqData-1,y ;use length left over / 2 for frequency offset + ldx #$5d ;store reg contents of power-up grab sound + ldy #$7f + +LoadSqu2Regs: + jsr PlaySqu2Sfx + +DecrementSfx2Length: + dec Squ2_SfxLenCounter ;decrement length of sfx + bne ExSfx2 + +EmptySfx2Buffer: + ldx #$00 ;initialize square 2's sound effects buffer + stx Square2SoundBuffer + +StopSquare2Sfx: + ldx #$0d ;stop playing the sfx + stx SND_MASTERCTRL_REG + ldx #$0f + stx SND_MASTERCTRL_REG +ExSfx2: rts + +Square2SfxHandler: + lda Square2SoundBuffer ;special handling for the 1-up sound to keep it + and #Sfx_ExtraLife ;from being interrupted by other sounds on square 2 + bne ContinueExtraLife + ldy Square2SoundQueue ;check for sfx in queue + beq CheckSfx2Buffer + sty Square2SoundBuffer ;if found, put in buffer and check for the following + bmi PlayBowserFall ;bowser fall + lsr Square2SoundQueue + bcs PlayCoinGrab ;coin grab + lsr Square2SoundQueue + bcs PlayGrowPowerUp ;power-up reveal + lsr Square2SoundQueue + bcs PlayGrowVine ;vine grow + lsr Square2SoundQueue + bcs PlayBlast ;fireworks/gunfire + lsr Square2SoundQueue + bcs PlayTimerTick ;timer tick + lsr Square2SoundQueue + bcs PlayPowerUpGrab ;power-up grab + lsr Square2SoundQueue + bcs PlayExtraLife ;1-up + +CheckSfx2Buffer: + lda Square2SoundBuffer ;check for sfx in buffer + beq ExS2H ;if not found, exit sub + bmi ContinueBowserFall ;bowser fall + lsr + bcs Cont_CGrab_TTick ;coin grab + lsr + bcs ContinueGrowItems ;power-up reveal + lsr + bcs ContinueGrowItems ;vine grow + lsr + bcs ContinueBlast ;fireworks/gunfire + lsr + bcs Cont_CGrab_TTick ;timer tick + lsr + bcs ContinuePowerUpGrab ;power-up grab + lsr + bcs ContinueExtraLife ;1-up +ExS2H: rts + +Cont_CGrab_TTick: + jmp ContinueCGrabTTick + +JumpToDecLength2: + jmp DecrementSfx2Length + +PlayBowserFall: + lda #$38 ;load length of bowser defeat sound + sta Squ2_SfxLenCounter + ldy #$c4 ;load contents of reg for bowser defeat sound + lda #$18 +BlstSJp: bne PBFRegs + +ContinueBowserFall: + lda Squ2_SfxLenCounter ;check for almost near the end + cmp #$08 + bne DecrementSfx2Length + ldy #$a4 ;if so, load the rest of reg contents for bowser defeat sound + lda #$5a +PBFRegs: ldx #$9f ;the fireworks/gunfire sound shares part of reg contents here +EL_LRegs: bne LoadSqu2Regs ;this is an unconditional branch outta here + +PlayExtraLife: + lda #$30 ;load length of 1-up sound + sta Squ2_SfxLenCounter + +ContinueExtraLife: + lda Squ2_SfxLenCounter + ldx #$03 ;load new tones only every eight frames +DivLLoop: lsr + bcs JumpToDecLength2 ;if any bits set here, branch to dec the length + dex + bne DivLLoop ;do this until all bits checked, if none set, continue + tay + lda ExtraLifeFreqData-1,y ;load our reg contents + ldx #$82 + ldy #$7f + bne EL_LRegs ;unconditional branch + +PlayGrowPowerUp: + lda #$10 ;load length of power-up reveal sound + bne GrowItemRegs + +PlayGrowVine: + lda #$20 ;load length of vine grow sound + +GrowItemRegs: + sta Squ2_SfxLenCounter + lda #$7f ;load contents of reg for both sounds directly + sta SND_SQUARE2_REG+1 + lda #$00 ;start secondary counter for both sounds + sta Sfx_SecondaryCounter + +ContinueGrowItems: + inc Sfx_SecondaryCounter ;increment secondary counter for both sounds + lda Sfx_SecondaryCounter ;this sound doesn't decrement the usual counter + lsr ;divide by 2 to get the offset + tay + cpy Squ2_SfxLenCounter ;have we reached the end yet? + beq StopGrowItems ;if so, branch to jump, and stop playing sounds + lda #$9d ;load contents of other reg directly + sta SND_SQUARE2_REG + lda PUp_VGrow_FreqData,y ;use secondary counter / 2 as offset for frequency regs + jsr SetFreq_Squ2 + rts + +StopGrowItems: + jmp EmptySfx2Buffer ;branch to stop playing sounds + +;-------------------------------- + +BrickShatterFreqData: + .db $01, $0e, $0e, $0d, $0b, $06, $0c, $0f + .db $0a, $09, $03, $0d, $08, $0d, $06, $0c + +PlayBrickShatter: + lda #$20 ;load length of brick shatter sound + sta Noise_SfxLenCounter + +ContinueBrickShatter: + lda Noise_SfxLenCounter + lsr ;divide by 2 and check for bit set to use offset + bcc DecrementSfx3Length + tay + ldx BrickShatterFreqData,y ;load reg contents of brick shatter sound + lda BrickShatterEnvData,y + +PlayNoiseSfx: + sta SND_NOISE_REG ;play the sfx + stx SND_NOISE_REG+2 + lda #$18 + sta SND_NOISE_REG+3 + +DecrementSfx3Length: + dec Noise_SfxLenCounter ;decrement length of sfx + bne ExSfx3 + lda #$f0 ;if done, stop playing the sfx + sta SND_NOISE_REG + lda #$00 + sta NoiseSoundBuffer +ExSfx3: rts + +NoiseSfxHandler: + ldy NoiseSoundQueue ;check for sfx in queue + beq CheckNoiseBuffer + sty NoiseSoundBuffer ;if found, put in buffer + lsr NoiseSoundQueue + bcs PlayBrickShatter ;brick shatter + lsr NoiseSoundQueue + bcs PlayBowserFlame ;bowser flame + +CheckNoiseBuffer: + lda NoiseSoundBuffer ;check for sfx in buffer + beq ExNH ;if not found, exit sub + lsr + bcs ContinueBrickShatter ;brick shatter + lsr + bcs ContinueBowserFlame ;bowser flame +ExNH: rts + +PlayBowserFlame: + lda #$40 ;load length of bowser flame sound + sta Noise_SfxLenCounter + +ContinueBowserFlame: + lda Noise_SfxLenCounter + lsr + tay + ldx #$0f ;load reg contents of bowser flame sound + lda BowserFlameEnvData-1,y + bne PlayNoiseSfx ;unconditional branch here + +;-------------------------------- + +ContinueMusic: + jmp HandleSquare2Music ;if we have music, start with square 2 channel + +MusicHandler: + lda EventMusicQueue ;check event music queue + bne LoadEventMusic + lda AreaMusicQueue ;check area music queue + bne LoadAreaMusic + lda EventMusicBuffer ;check both buffers + ora AreaMusicBuffer + bne ContinueMusic + rts ;no music, then leave + +LoadEventMusic: + sta EventMusicBuffer ;copy event music queue contents to buffer + cmp #DeathMusic ;is it death music? + bne NoStopSfx ;if not, jump elsewhere + jsr StopSquare1Sfx ;stop sfx in square 1 and 2 + jsr StopSquare2Sfx ;but clear only square 1's sfx buffer +NoStopSfx: ldx AreaMusicBuffer + stx AreaMusicBuffer_Alt ;save current area music buffer to be re-obtained later + ldy #$00 + sty NoteLengthTblAdder ;default value for additional length byte offset + sty AreaMusicBuffer ;clear area music buffer + cmp #TimeRunningOutMusic ;is it time running out music? + bne FindEventMusicHeader + ldx #$08 ;load offset to be added to length byte of header + stx NoteLengthTblAdder + bne FindEventMusicHeader ;unconditional branch + +LoadAreaMusic: + cmp #$04 ;is it underground music? + bne NoStop1 ;no, do not stop square 1 sfx + jsr StopSquare1Sfx +NoStop1: ldy #$10 ;start counter used only by ground level music +GMLoopB: sty GroundMusicHeaderOfs + +HandleAreaMusicLoopB: + ldy #$00 ;clear event music buffer + sty EventMusicBuffer + sta AreaMusicBuffer ;copy area music queue contents to buffer + cmp #$01 ;is it ground level music? + bne FindAreaMusicHeader + inc GroundMusicHeaderOfs ;increment but only if playing ground level music + ldy GroundMusicHeaderOfs ;is it time to loopback ground level music? + cpy #$32 + bne LoadHeader ;branch ahead with alternate offset + ldy #$11 + bne GMLoopB ;unconditional branch + +FindAreaMusicHeader: + ldy #$08 ;load Y for offset of area music + sty MusicOffset_Square2 ;residual instruction here + +FindEventMusicHeader: + iny ;increment Y pointer based on previously loaded queue contents + lsr ;bit shift and increment until we find a set bit for music + bcc FindEventMusicHeader + +LoadHeader: + lda MusicHeaderOffsetData,y ;load offset for header + tay + lda MusicHeaderData,y ;now load the header + sta NoteLenLookupTblOfs + lda MusicHeaderData+1,y + sta MusicDataLow + lda MusicHeaderData+2,y + sta MusicDataHigh + lda MusicHeaderData+3,y + sta MusicOffset_Triangle + lda MusicHeaderData+4,y + sta MusicOffset_Square1 + lda MusicHeaderData+5,y + sta MusicOffset_Noise + sta NoiseDataLoopbackOfs + lda #$01 ;initialize music note counters + sta Squ2_NoteLenCounter + sta Squ1_NoteLenCounter + sta Tri_NoteLenCounter + sta Noise_BeatLenCounter + lda #$00 ;initialize music data offset for square 2 + sta MusicOffset_Square2 + sta AltRegContentFlag ;initialize alternate control reg data used by square 1 + lda #$0b ;disable triangle channel and reenable it + sta SND_MASTERCTRL_REG + lda #$0f + sta SND_MASTERCTRL_REG + +HandleSquare2Music: + dec Squ2_NoteLenCounter ;decrement square 2 note length + bne MiscSqu2MusicTasks ;is it time for more data? if not, branch to end tasks + ldy MusicOffset_Square2 ;increment square 2 music offset and fetch data + inc MusicOffset_Square2 + lda (MusicData),y + beq EndOfMusicData ;if zero, the data is a null terminator + bpl Squ2NoteHandler ;if non-negative, data is a note + bne Squ2LengthHandler ;otherwise it is length data + +EndOfMusicData: + lda EventMusicBuffer ;check secondary buffer for time running out music + cmp #TimeRunningOutMusic + bne NotTRO + lda AreaMusicBuffer_Alt ;load previously saved contents of primary buffer + bne MusicLoopBack ;and start playing the song again if there is one +NotTRO: and #VictoryMusic ;check for victory music (the only secondary that loops) + bne VictoryMLoopBack + lda AreaMusicBuffer ;check primary buffer for any music except pipe intro + and #%01011111 + bne MusicLoopBack ;if any area music except pipe intro, music loops + lda #$00 ;clear primary and secondary buffers and initialize + sta AreaMusicBuffer ;control regs of square and triangle channels + sta EventMusicBuffer + sta SND_TRIANGLE_REG + lda #$90 + sta SND_SQUARE1_REG + sta SND_SQUARE2_REG + rts + +MusicLoopBack: + jmp HandleAreaMusicLoopB + +VictoryMLoopBack: + jmp LoadEventMusic + +Squ2LengthHandler: + jsr ProcessLengthData ;store length of note + sta Squ2_NoteLenBuffer + ldy MusicOffset_Square2 ;fetch another byte (MUST NOT BE LENGTH BYTE!) + inc MusicOffset_Square2 + lda (MusicData),y + +Squ2NoteHandler: + ldx Square2SoundBuffer ;is there a sound playing on this channel? + bne SkipFqL1 + jsr SetFreq_Squ2 ;no, then play the note + beq Rest ;check to see if note is rest + jsr LoadControlRegs ;if not, load control regs for square 2 +Rest: sta Squ2_EnvelopeDataCtrl ;save contents of A + jsr Dump_Sq2_Regs ;dump X and Y into square 2 control regs +SkipFqL1: lda Squ2_NoteLenBuffer ;save length in square 2 note counter + sta Squ2_NoteLenCounter + +MiscSqu2MusicTasks: + lda Square2SoundBuffer ;is there a sound playing on square 2? + bne HandleSquare1Music + lda EventMusicBuffer ;check for death music or d4 set on secondary buffer + and #%10010001 ;note that regs for death music or d4 are loaded by default + bne HandleSquare1Music + ldy Squ2_EnvelopeDataCtrl ;check for contents saved from LoadControlRegs + beq NoDecEnv1 + dec Squ2_EnvelopeDataCtrl ;decrement unless already zero +NoDecEnv1: jsr LoadEnvelopeData ;do a load of envelope data to replace default + sta SND_SQUARE2_REG ;based on offset set by first load unless playing + ldx #$7f ;death music or d4 set on secondary buffer + stx SND_SQUARE2_REG+1 + +HandleSquare1Music: + ldy MusicOffset_Square1 ;is there a nonzero offset here? + beq HandleTriangleMusic ;if not, skip ahead to the triangle channel + dec Squ1_NoteLenCounter ;decrement square 1 note length + bne MiscSqu1MusicTasks ;is it time for more data? + +FetchSqu1MusicData: + ldy MusicOffset_Square1 ;increment square 1 music offset and fetch data + inc MusicOffset_Square1 + lda (MusicData),y + bne Squ1NoteHandler ;if nonzero, then skip this part + lda #$83 + sta SND_SQUARE1_REG ;store some data into control regs for square 1 + lda #$94 ;and fetch another byte of data, used to give + sta SND_SQUARE1_REG+1 ;death music its unique sound + sta AltRegContentFlag + bne FetchSqu1MusicData ;unconditional branch + +Squ1NoteHandler: + jsr AlternateLengthHandler + sta Squ1_NoteLenCounter ;save contents of A in square 1 note counter + ldy Square1SoundBuffer ;is there a sound playing on square 1? + bne HandleTriangleMusic + txa + and #%00111110 ;change saved data to appropriate note format + jsr SetFreq_Squ1 ;play the note + beq SkipCtrlL + jsr LoadControlRegs +SkipCtrlL: sta Squ1_EnvelopeDataCtrl ;save envelope offset + jsr Dump_Squ1_Regs + +MiscSqu1MusicTasks: + lda Square1SoundBuffer ;is there a sound playing on square 1? + bne HandleTriangleMusic + lda EventMusicBuffer ;check for death music or d4 set on secondary buffer + and #%10010001 + bne DeathMAltReg + ldy Squ1_EnvelopeDataCtrl ;check saved envelope offset + beq NoDecEnv2 + dec Squ1_EnvelopeDataCtrl ;decrement unless already zero +NoDecEnv2: jsr LoadEnvelopeData ;do a load of envelope data + sta SND_SQUARE1_REG ;based on offset set by first load +DeathMAltReg: lda AltRegContentFlag ;check for alternate control reg data + bne DoAltLoad + lda #$7f ;load this value if zero, the alternate value +DoAltLoad: sta SND_SQUARE1_REG+1 ;if nonzero, and let's move on + +HandleTriangleMusic: + lda MusicOffset_Triangle + dec Tri_NoteLenCounter ;decrement triangle note length + bne HandleNoiseMusic ;is it time for more data? + ldy MusicOffset_Triangle ;increment square 1 music offset and fetch data + inc MusicOffset_Triangle + lda (MusicData),y + beq LoadTriCtrlReg ;if zero, skip all this and move on to noise + bpl TriNoteHandler ;if non-negative, data is note + jsr ProcessLengthData ;otherwise, it is length data + sta Tri_NoteLenBuffer ;save contents of A + lda #$1f + sta SND_TRIANGLE_REG ;load some default data for triangle control reg + ldy MusicOffset_Triangle ;fetch another byte + inc MusicOffset_Triangle + lda (MusicData),y + beq LoadTriCtrlReg ;check once more for nonzero data + +TriNoteHandler: + jsr SetFreq_Tri + ldx Tri_NoteLenBuffer ;save length in triangle note counter + stx Tri_NoteLenCounter + lda EventMusicBuffer + and #%01101110 ;check for death music or d4 set on secondary buffer + bne NotDOrD4 ;if playing any other secondary, skip primary buffer check + lda AreaMusicBuffer ;check primary buffer for water or castle level music + and #%00001010 + beq HandleNoiseMusic ;if playing any other primary, or death or d4, go on to noise routine +NotDOrD4: txa ;if playing water or castle music or any secondary + cmp #$12 ;besides death music or d4 set, check length of note + bcs LongN + lda EventMusicBuffer ;check for win castle music again if not playing a long note + and #EndOfCastleMusic + beq MediN + lda #$0f ;load value $0f if playing the win castle music and playing a short + bne LoadTriCtrlReg ;note, load value $1f if playing water or castle level music or any +MediN: lda #$1f ;secondary besides death and d4 except win castle or win castle and playing + bne LoadTriCtrlReg ;a short note, and load value $ff if playing a long note on water, castle +LongN: lda #$ff ;or any secondary (including win castle) except death and d4 + +LoadTriCtrlReg: + sta SND_TRIANGLE_REG ;save final contents of A into control reg for triangle + +HandleNoiseMusic: + lda AreaMusicBuffer ;check if playing underground or castle music + and #%11110011 + beq ExitMusicHandler ;if so, skip the noise routine + dec Noise_BeatLenCounter ;decrement noise beat length + bne ExitMusicHandler ;is it time for more data? + +FetchNoiseBeatData: + ldy MusicOffset_Noise ;increment noise beat offset and fetch data + inc MusicOffset_Noise + lda (MusicData),y ;get noise beat data, if nonzero, branch to handle + bne NoiseBeatHandler + lda NoiseDataLoopbackOfs ;if data is zero, reload original noise beat offset + sta MusicOffset_Noise ;and loopback next time around + bne FetchNoiseBeatData ;unconditional branch + +NoiseBeatHandler: + jsr AlternateLengthHandler + sta Noise_BeatLenCounter ;store length in noise beat counter + txa + and #%00111110 ;reload data and erase length bits + beq SilentBeat ;if no beat data, silence + cmp #$30 ;check the beat data and play the appropriate + beq LongBeat ;noise accordingly + cmp #$20 + beq StrongBeat + and #%00010000 + beq SilentBeat + lda #$1c ;short beat data + ldx #$03 + ldy #$18 + bne PlayBeat + +StrongBeat: + lda #$1c ;strong beat data + ldx #$0c + ldy #$18 + bne PlayBeat + +LongBeat: + lda #$1c ;long beat data + ldx #$03 + ldy #$58 + bne PlayBeat + +SilentBeat: + lda #$10 ;silence + +PlayBeat: + sta SND_NOISE_REG ;load beat data into noise regs + stx SND_NOISE_REG+2 + sty SND_NOISE_REG+3 + +ExitMusicHandler: + rts + +AlternateLengthHandler: + tax ;save a copy of original byte into X + ror ;save LSB from original byte into carry + txa ;reload original byte and rotate three times + rol ;turning xx00000x into 00000xxx, with the + rol ;bit in carry as the MSB here + rol + +ProcessLengthData: + and #%00000111 ;clear all but the three LSBs + clc + adc $f0 ;add offset loaded from first header byte + adc NoteLengthTblAdder ;add extra if time running out music + tay + lda MusicLengthLookupTbl,y ;load length + rts + +LoadControlRegs: + lda EventMusicBuffer ;check secondary buffer for win castle music + and #EndOfCastleMusic + beq NotECstlM + lda #$04 ;this value is only used for win castle music + bne AllMus ;unconditional branch +NotECstlM: lda AreaMusicBuffer + and #%01111101 ;check primary buffer for water music + beq WaterMus + lda #$08 ;this is the default value for all other music + bne AllMus +WaterMus: lda #$28 ;this value is used for water music and all other event music +AllMus: ldx #$82 ;load contents of other sound regs for square 2 + ldy #$7f + rts + +LoadEnvelopeData: + lda EventMusicBuffer ;check secondary buffer for win castle music + and #EndOfCastleMusic + beq LoadUsualEnvData + lda EndOfCastleMusicEnvData,y ;load data from offset for win castle music + rts + +LoadUsualEnvData: + lda AreaMusicBuffer ;check primary buffer for water music + and #%01111101 + beq LoadWaterEventMusEnvData + lda AreaMusicEnvData,y ;load default data from offset for all other music + rts + +LoadWaterEventMusEnvData: + lda WaterEventMusEnvData,y ;load data from offset for water music and all other event music + rts + +;-------------------------------- + +;music header offsets + +MusicHeaderData: + .db DeathMusHdr-MHD ;event music + .db GameOverMusHdr-MHD + .db VictoryMusHdr-MHD + .db WinCastleMusHdr-MHD + .db GameOverMusHdr-MHD + .db EndOfLevelMusHdr-MHD + .db TimeRunningOutHdr-MHD + .db SilenceHdr-MHD + + .db GroundLevelPart1Hdr-MHD ;area music + .db WaterMusHdr-MHD + .db UndergroundMusHdr-MHD + .db CastleMusHdr-MHD + .db Star_CloudHdr-MHD + .db GroundLevelLeadInHdr-MHD + .db Star_CloudHdr-MHD + .db SilenceHdr-MHD + + .db GroundLevelLeadInHdr-MHD ;ground level music layout + .db GroundLevelPart1Hdr-MHD, GroundLevelPart1Hdr-MHD + .db GroundLevelPart2AHdr-MHD, GroundLevelPart2BHdr-MHD, GroundLevelPart2AHdr-MHD, GroundLevelPart2CHdr-MHD + .db GroundLevelPart2AHdr-MHD, GroundLevelPart2BHdr-MHD, GroundLevelPart2AHdr-MHD, GroundLevelPart2CHdr-MHD + .db GroundLevelPart3AHdr-MHD, GroundLevelPart3BHdr-MHD, GroundLevelPart3AHdr-MHD, GroundLevelLeadInHdr-MHD + .db GroundLevelPart1Hdr-MHD, GroundLevelPart1Hdr-MHD + .db GroundLevelPart4AHdr-MHD, GroundLevelPart4BHdr-MHD, GroundLevelPart4AHdr-MHD, GroundLevelPart4CHdr-MHD + .db GroundLevelPart4AHdr-MHD, GroundLevelPart4BHdr-MHD, GroundLevelPart4AHdr-MHD, GroundLevelPart4CHdr-MHD + .db GroundLevelPart3AHdr-MHD, GroundLevelPart3BHdr-MHD, GroundLevelPart3AHdr-MHD, GroundLevelLeadInHdr-MHD + .db GroundLevelPart4AHdr-MHD, GroundLevelPart4BHdr-MHD, GroundLevelPart4AHdr-MHD, GroundLevelPart4CHdr-MHD + +;music headers +;header format is as follows: +;1 byte - length byte offset +;2 bytes - music data address +;1 byte - triangle data offset +;1 byte - square 1 data offset +;1 byte - noise data offset (not used by secondary music) + +TimeRunningOutHdr: .db $08, TimeRunOutMusData, $27, $18 +Star_CloudHdr: .db $20, Star_CloudMData, $2e, $1a, $40 +EndOfLevelMusHdr: .db $20, WinLevelMusData, $3d, $21 +ResidualHeaderData: .db $20, $c4, $fc, $3f, $1d +UndergroundMusHdr: .db $18, UndergroundMusData, $00, $00 +SilenceHdr: .db $08, SilenceData, $00 +CastleMusHdr: .db $00, CastleMusData, $93, $62 +VictoryMusHdr: .db $10, VictoryMusData, $24, $14 +GameOverMusHdr: .db $18, GameOverMusData, $1e, $14 +WaterMusHdr: .db $08, WaterMusData, $a0, $70, $68 +WinCastleMusHdr: .db $08, EndOfCastleMusData, $4c, $24 +GroundLevelPart1Hdr: .db $18, GroundM_P1Data, $2d, $1c, $b8 +GroundLevelPart2AHdr: .db $18, GroundM_P2AData, $20, $12, $70 +GroundLevelPart2BHdr: .db $18, GroundM_P2BData, $1b, $10, $44 +GroundLevelPart2CHdr: .db $18, GroundM_P2CData, $11, $0a, $1c +GroundLevelPart3AHdr: .db $18, GroundM_P3AData, $2d, $10, $58 +GroundLevelPart3BHdr: .db $18, GroundM_P3BData, $14, $0d, $3f +GroundLevelLeadInHdr: .db $18, GroundMLdInData, $15, $0d, $21 +GroundLevelPart4AHdr: .db $18, GroundM_P4AData, $18, $10, $7a +GroundLevelPart4BHdr: .db $18, GroundM_P4BData, $19, $0f, $54 +GroundLevelPart4CHdr: .db $18, GroundM_P4CData, $1e, $12, $2b +DeathMusHdr: .db $18, DeathMusData, $1e, $0f, $2d + +;-------------------------------- + +;MUSIC DATA +;square 2/triangle format +;d7 - length byte flag (0-note, 1-length) +;if d7 is set to 0 and d6-d0 is nonzero: +;d6-d0 - note offset in frequency look-up table (must be even) +;if d7 is set to 1: +;d6-d3 - unused +;d2-d0 - length offset in length look-up table +;value of $00 in square 2 data is used as null terminator, affects all sound channels +;value of $00 in triangle data causes routine to skip note + +;square 1 format +;d7-d6, d0 - length offset in length look-up table (bit order is d0,d7,d6) +;d5-d1 - note offset in frequency look-up table +;value of $00 in square 1 data is flag alternate control reg data to be loaded + +;noise format +;d7-d6, d0 - length offset in length look-up table (bit order is d0,d7,d6) +;d5-d4 - beat type (0 - rest, 1 - short, 2 - strong, 3 - long) +;d3-d1 - unused +;value of $00 in noise data is used as null terminator, affects only noise + +;all music data is organized into sections (unless otherwise stated): +;square 2, square 1, triangle, noise + +Star_CloudMData: + .db $84, $2c, $2c, $2c, $82, $04, $2c, $04, $85, $2c, $84, $2c, $2c + .db $2a, $2a, $2a, $82, $04, $2a, $04, $85, $2a, $84, $2a, $2a, $00 + + .db $1f, $1f, $1f, $98, $1f, $1f, $98, $9e, $98, $1f + .db $1d, $1d, $1d, $94, $1d, $1d, $94, $9c, $94, $1d + + .db $86, $18, $85, $26, $30, $84, $04, $26, $30 + .db $86, $14, $85, $22, $2c, $84, $04, $22, $2c + + .db $21, $d0, $c4, $d0, $31, $d0, $c4, $d0, $00 + +GroundM_P1Data: + .db $85, $2c, $22, $1c, $84, $26, $2a, $82, $28, $26, $04 + .db $87, $22, $34, $3a, $82, $40, $04, $36, $84, $3a, $34 + .db $82, $2c, $30, $85, $2a + +SilenceData: + .db $00 + + .db $5d, $55, $4d, $15, $19, $96, $15, $d5, $e3, $eb + .db $2d, $a6, $2b, $27, $9c, $9e, $59 + + .db $85, $22, $1c, $14, $84, $1e, $22, $82, $20, $1e, $04, $87 + .db $1c, $2c, $34, $82, $36, $04, $30, $34, $04, $2c, $04, $26 + .db $2a, $85, $22 + +GroundM_P2AData: + .db $84, $04, $82, $3a, $38, $36, $32, $04, $34 + .db $04, $24, $26, $2c, $04, $26, $2c, $30, $00 + + .db $05, $b4, $b2, $b0, $2b, $ac, $84 + .db $9c, $9e, $a2, $84, $94, $9c, $9e + + .db $85, $14, $22, $84, $2c, $85, $1e + .db $82, $2c, $84, $2c, $1e + +GroundM_P2BData: + .db $84, $04, $82, $3a, $38, $36, $32, $04, $34 + .db $04, $64, $04, $64, $86, $64, $00 + + .db $05, $b4, $b2, $b0, $2b, $ac, $84 + .db $37, $b6, $b6, $45 + + .db $85, $14, $1c, $82, $22, $84, $2c + .db $4e, $82, $4e, $84, $4e, $22 + +GroundM_P2CData: + .db $84, $04, $85, $32, $85, $30, $86, $2c, $04, $00 + + .db $05, $a4, $05, $9e, $05, $9d, $85 + + .db $84, $14, $85, $24, $28, $2c, $82 + .db $22, $84, $22, $14 + + .db $21, $d0, $c4, $d0, $31, $d0, $c4, $d0, $00 + +GroundM_P3AData: + .db $82, $2c, $84, $2c, $2c, $82, $2c, $30 + .db $04, $34, $2c, $04, $26, $86, $22, $00 + + .db $a4, $25, $25, $a4, $29, $a2, $1d, $9c, $95 + +GroundM_P3BData: + .db $82, $2c, $2c, $04, $2c, $04, $2c, $30, $85, $34, $04, $04, $00 + + .db $a4, $25, $25, $a4, $a8, $63, $04 + +;triangle data used by both sections of third part + .db $85, $0e, $1a, $84, $24, $85, $22, $14, $84, $0c + +GroundMLdInData: + .db $82, $34, $84, $34, $34, $82, $2c, $84, $34, $86, $3a, $04, $00 + + .db $a0, $21, $21, $a0, $21, $2b, $05, $a3 + + .db $82, $18, $84, $18, $18, $82, $18, $18, $04, $86, $3a, $22 + +;noise data used by lead-in and third part sections + .db $31, $90, $31, $90, $31, $71, $31, $90, $90, $90, $00 + +GroundM_P4AData: + .db $82, $34, $84, $2c, $85, $22, $84, $24 + .db $82, $26, $36, $04, $36, $86, $26, $00 + + .db $ac, $27, $5d, $1d, $9e, $2d, $ac, $9f + + .db $85, $14, $82, $20, $84, $22, $2c + .db $1e, $1e, $82, $2c, $2c, $1e, $04 + +GroundM_P4BData: + .db $87, $2a, $40, $40, $40, $3a, $36 + .db $82, $34, $2c, $04, $26, $86, $22, $00 + + .db $e3, $f7, $f7, $f7, $f5, $f1, $ac, $27, $9e, $9d + + .db $85, $18, $82, $1e, $84, $22, $2a + .db $22, $22, $82, $2c, $2c, $22, $04 + +DeathMusData: + .db $86, $04 ;death music share data with fourth part c of ground level music + +GroundM_P4CData: + .db $82, $2a, $36, $04, $36, $87, $36, $34, $30, $86, $2c, $04, $00 + + .db $00, $68, $6a, $6c, $45 ;death music only + + .db $a2, $31, $b0, $f1, $ed, $eb, $a2, $1d, $9c, $95 + + .db $86, $04 ;death music only + + .db $85, $22, $82, $22, $87, $22, $26, $2a, $84, $2c, $22, $86, $14 + +;noise data used by fourth part sections + .db $51, $90, $31, $11, $00 + +CastleMusData: + .db $80, $22, $28, $22, $26, $22, $24, $22, $26 + .db $22, $28, $22, $2a, $22, $28, $22, $26 + .db $22, $28, $22, $26, $22, $24, $22, $26 + .db $22, $28, $22, $2a, $22, $28, $22, $26 + .db $20, $26, $20, $24, $20, $26, $20, $28 + .db $20, $26, $20, $28, $20, $26, $20, $24 + .db $20, $26, $20, $24, $20, $26, $20, $28 + .db $20, $26, $20, $28, $20, $26, $20, $24 + .db $28, $30, $28, $32, $28, $30, $28, $2e + .db $28, $30, $28, $2e, $28, $2c, $28, $2e + .db $28, $30, $28, $32, $28, $30, $28, $2e + .db $28, $30, $28, $2e, $28, $2c, $28, $2e, $00 + + .db $04, $70, $6e, $6c, $6e, $70, $72, $70, $6e + .db $70, $6e, $6c, $6e, $70, $72, $70, $6e + .db $6e, $6c, $6e, $70, $6e, $70, $6e, $6c + .db $6e, $6c, $6e, $70, $6e, $70, $6e, $6c + .db $76, $78, $76, $74, $76, $74, $72, $74 + .db $76, $78, $76, $74, $76, $74, $72, $74 + + .db $84, $1a, $83, $18, $20, $84, $1e, $83, $1c, $28 + .db $26, $1c, $1a, $1c + +GameOverMusData: + .db $82, $2c, $04, $04, $22, $04, $04, $84, $1c, $87 + .db $26, $2a, $26, $84, $24, $28, $24, $80, $22, $00 + + .db $9c, $05, $94, $05, $0d, $9f, $1e, $9c, $98, $9d + + .db $82, $22, $04, $04, $1c, $04, $04, $84, $14 + .db $86, $1e, $80, $16, $80, $14 + +TimeRunOutMusData: + .db $81, $1c, $30, $04, $30, $30, $04, $1e, $32, $04, $32, $32 + .db $04, $20, $34, $04, $34, $34, $04, $36, $04, $84, $36, $00 + + .db $46, $a4, $64, $a4, $48, $a6, $66, $a6, $4a, $a8, $68, $a8 + .db $6a, $44, $2b + + .db $81, $2a, $42, $04, $42, $42, $04, $2c, $64, $04, $64, $64 + .db $04, $2e, $46, $04, $46, $46, $04, $22, $04, $84, $22 + +WinLevelMusData: + .db $87, $04, $06, $0c, $14, $1c, $22, $86, $2c, $22 + .db $87, $04, $60, $0e, $14, $1a, $24, $86, $2c, $24 + .db $87, $04, $08, $10, $18, $1e, $28, $86, $30, $30 + .db $80, $64, $00 + + .db $cd, $d5, $dd, $e3, $ed, $f5, $bb, $b5, $cf, $d5 + .db $db, $e5, $ed, $f3, $bd, $b3, $d1, $d9, $df, $e9 + .db $f1, $f7, $bf, $ff, $ff, $ff, $34 + .db $00 ;unused byte + + .db $86, $04, $87, $14, $1c, $22, $86, $34, $84, $2c + .db $04, $04, $04, $87, $14, $1a, $24, $86, $32, $84 + .db $2c, $04, $86, $04, $87, $18, $1e, $28, $86, $36 + .db $87, $30, $30, $30, $80, $2c + +;square 2 and triangle use the same data, square 1 is unused +UndergroundMusData: + .db $82, $14, $2c, $62, $26, $10, $28, $80, $04 + .db $82, $14, $2c, $62, $26, $10, $28, $80, $04 + .db $82, $08, $1e, $5e, $18, $60, $1a, $80, $04 + .db $82, $08, $1e, $5e, $18, $60, $1a, $86, $04 + .db $83, $1a, $18, $16, $84, $14, $1a, $18, $0e, $0c + .db $16, $83, $14, $20, $1e, $1c, $28, $26, $87 + .db $24, $1a, $12, $10, $62, $0e, $80, $04, $04 + .db $00 + +;noise data directly follows square 2 here unlike in other songs +WaterMusData: + .db $82, $18, $1c, $20, $22, $26, $28 + .db $81, $2a, $2a, $2a, $04, $2a, $04, $83, $2a, $82, $22 + .db $86, $34, $32, $34, $81, $04, $22, $26, $2a, $2c, $30 + .db $86, $34, $83, $32, $82, $36, $84, $34, $85, $04, $81, $22 + .db $86, $30, $2e, $30, $81, $04, $22, $26, $2a, $2c, $2e + .db $86, $30, $83, $22, $82, $36, $84, $34, $85, $04, $81, $22 + .db $86, $3a, $3a, $3a, $82, $3a, $81, $40, $82, $04, $81, $3a + .db $86, $36, $36, $36, $82, $36, $81, $3a, $82, $04, $81, $36 + .db $86, $34, $82, $26, $2a, $36 + .db $81, $34, $34, $85, $34, $81, $2a, $86, $2c, $00 + + .db $84, $90, $b0, $84, $50, $50, $b0, $00 + + .db $98, $96, $94, $92, $94, $96, $58, $58, $58, $44 + .db $5c, $44, $9f, $a3, $a1, $a3, $85, $a3, $e0, $a6 + .db $23, $c4, $9f, $9d, $9f, $85, $9f, $d2, $a6, $23 + .db $c4, $b5, $b1, $af, $85, $b1, $af, $ad, $85, $95 + .db $9e, $a2, $aa, $6a, $6a, $6b, $5e, $9d + + .db $84, $04, $04, $82, $22, $86, $22 + .db $82, $14, $22, $2c, $12, $22, $2a, $14, $22, $2c + .db $1c, $22, $2c, $14, $22, $2c, $12, $22, $2a, $14 + .db $22, $2c, $1c, $22, $2c, $18, $22, $2a, $16, $20 + .db $28, $18, $22, $2a, $12, $22, $2a, $18, $22, $2a + .db $12, $22, $2a, $14, $22, $2c, $0c, $22, $2c, $14, $22, $34, $12 + .db $22, $30, $10, $22, $2e, $16, $22, $34, $18, $26 + .db $36, $16, $26, $36, $14, $26, $36, $12, $22, $36 + .db $5c, $22, $34, $0c, $22, $22, $81, $1e, $1e, $85, $1e + .db $81, $12, $86, $14 + +EndOfCastleMusData: + .db $81, $2c, $22, $1c, $2c, $22, $1c, $85, $2c, $04 + .db $81, $2e, $24, $1e, $2e, $24, $1e, $85, $2e, $04 + .db $81, $32, $28, $22, $32, $28, $22, $85, $32 + .db $87, $36, $36, $36, $84, $3a, $00 + + .db $5c, $54, $4c, $5c, $54, $4c + .db $5c, $1c, $1c, $5c, $5c, $5c, $5c + .db $5e, $56, $4e, $5e, $56, $4e + .db $5e, $1e, $1e, $5e, $5e, $5e, $5e + .db $62, $5a, $50, $62, $5a, $50 + .db $62, $22, $22, $62, $e7, $e7, $e7, $2b + + .db $86, $14, $81, $14, $80, $14, $14, $81, $14, $14, $14, $14 + .db $86, $16, $81, $16, $80, $16, $16, $81, $16, $16, $16, $16 + .db $81, $28, $22, $1a, $28, $22, $1a, $28, $80, $28, $28 + .db $81, $28, $87, $2c, $2c, $2c, $84, $30 + +VictoryMusData: + .db $83, $04, $84, $0c, $83, $62, $10, $84, $12 + .db $83, $1c, $22, $1e, $22, $26, $18, $1e, $04, $1c, $00 + + .db $e3, $e1, $e3, $1d, $de, $e0, $23 + .db $ec, $75, $74, $f0, $f4, $f6, $ea, $31, $2d + + .db $83, $12, $14, $04, $18, $1a, $1c, $14 + .db $26, $22, $1e, $1c, $18, $1e, $22, $0c, $14 + +;unused space + .db $ff, $ff, $ff + +FreqRegLookupTbl: + .db $00, $88, $00, $2f, $00, $00 + .db $02, $a6, $02, $80, $02, $5c, $02, $3a + .db $02, $1a, $01, $df, $01, $c4, $01, $ab + .db $01, $93, $01, $7c, $01, $67, $01, $53 + .db $01, $40, $01, $2e, $01, $1d, $01, $0d + .db $00, $fe, $00, $ef, $00, $e2, $00, $d5 + .db $00, $c9, $00, $be, $00, $b3, $00, $a9 + .db $00, $a0, $00, $97, $00, $8e, $00, $86 + .db $00, $77, $00, $7e, $00, $71, $00, $54 + .db $00, $64, $00, $5f, $00, $59, $00, $50 + .db $00, $47, $00, $43, $00, $3b, $00, $35 + .db $00, $2a, $00, $23, $04, $75, $03, $57 + .db $02, $f9, $02, $cf, $01, $fc, $00, $6a + +MusicLengthLookupTbl: + .db $05, $0a, $14, $28, $50, $1e, $3c, $02 + .db $04, $08, $10, $20, $40, $18, $30, $0c + .db $03, $06, $0c, $18, $30, $12, $24, $08 + .db $36, $03, $09, $06, $12, $1b, $24, $0c + .db $24, $02, $06, $04, $0c, $12, $18, $08 + .db $12, $01, $03, $02, $06, $09, $0c, $04 + +EndOfCastleMusicEnvData: + .db $98, $99, $9a, $9b + +AreaMusicEnvData: + .db $90, $94, $94, $95, $95, $96, $97, $98 + +WaterEventMusEnvData: + .db $90, $91, $92, $92, $93, $93, $93, $94 + .db $94, $94, $94, $94, $94, $95, $95, $95 + .db $95, $95, $95, $96, $96, $96, $96, $96 + .db $96, $96, $96, $96, $96, $96, $96, $96 + .db $96, $96, $96, $96, $95, $95, $94, $93 + +BowserFlameEnvData: + .db $15, $16, $16, $17, $17, $18, $19, $19 + .db $1a, $1a, $1c, $1d, $1d, $1e, $1e, $1f + .db $1f, $1f, $1f, $1e, $1d, $1c, $1e, $1f + .db $1f, $1e, $1d, $1c, $1a, $18, $16, $14 + +BrickShatterEnvData: + .db $15, $16, $16, $17, $17, $18, $19, $19 + .db $1a, $1a, $1c, $1d, $1d, $1e, $1e, $1f + +;------------------------------------------------------------------------------------- +;INTERRUPT VECTORS + + .dw NonMaskableInterrupt + .dw Start + .dw $fff0 ;unused diff --git a/other/snow.nes b/other/snow.nes new file mode 100644 index 0000000..54ad8fd Binary files /dev/null and b/other/snow.nes differ diff --git a/other/snow.txt b/other/snow.txt new file mode 100644 index 0000000..6bfa7f6 --- /dev/null +++ b/other/snow.txt @@ -0,0 +1,72 @@ +SNOW intro, by Repulse (Tennessee Carmel-Veilleux) +================================================== + +Introduction +------------ +During the past year, I've worked very hard to learn 6502 assembly and +ultimately, NES programming. What this archive contains is my first +release. It is an Intro called "SNOW". It is the first "real" NES intro in +the Demoparty sense of the word. Let's not forget that other devoted +people have also given time to NES programming: + + -Memblers + -Yoshi + -Matt Conte + -Kevin Horton + -The A/NES crew + -Chris Covell + +and have succeded by bringing technical demonstrations. + +The people +---------- + -Repulse : Code, design + -_Bnu : GFX + -Memblers: Music + -Bananmos: Music Engine/Tracker + -Random : Original music in MOD format + +Distribution License +-------------------- +You can distribute this intro far and wide in both ROM and Cartridge form. +HOWEVER, you cannot sell the data itself, you can only sell the medium. +If you see this rule broken by someone, don't forget to e-mail me :) The +sources will ONLY be available at Yoshi's NES developement site, which is, +BTW, PRIVATE and restricted to the few who can actually make use of it. +Any outside distribution of the source will result in a suit for breach of +contract (to which you are agreeing by downloading this package). In other +words, if you have the source, don't give it to someone else without +asking me first. This is to insure that the NES developement community +doesn't suffer the same lame-fest that the NES emulation scene is +suffering right now. As usual, I cannot be held responsible for physical +or moral harm caused to you or your equipment. + +Technical data about this intro +------------------------------- +Run this intro in the LATEST LoopyNES for the greatest experience :) + +This intro was coded using the following software: + +CA65/LD65: Assembler and Linker from Ulrich von Bassewitz + -> Available at http://www.von-bassewitz.de/uz/cc65/ + +TLayer, NES Screen Arranger from Kent Hansen (Snowbro) + -> Available at (His page is down :( Zophar's Domain + +NT2 from Michael Iwaniec (Bananmos) + -> Available on request + +The demo took approx 30-40 hours to code spanned across 6 months (60% of +the coding was done the week before Coma '99). It contains > 2500 lines of +code. It was coded 100% under Linux. + +Contact +------- +Author: veilleux@atari.org, Repulse on EFnet + +Memblers: 5010.0951@tcon.net + +_Bnu: klass9.v@jokkmokk.se + +Bananmos: bananmos_uv@hotmail.com + diff --git a/other/test001.nes b/other/test001.nes new file mode 100644 index 0000000..b56ac1d Binary files /dev/null and b/other/test001.nes differ diff --git a/other/window2_ntsc.nes b/other/window2_ntsc.nes new file mode 100644 index 0000000..9aafc9a Binary files /dev/null and b/other/window2_ntsc.nes differ diff --git a/other/window2_pal.nes b/other/window2_pal.nes new file mode 100644 index 0000000..f683995 Binary files /dev/null and b/other/window2_pal.nes differ diff --git a/other/window_old_ntsc.nes b/other/window_old_ntsc.nes new file mode 100644 index 0000000..57cb00a Binary files /dev/null and b/other/window_old_ntsc.nes differ diff --git a/other/window_old_pal.nes b/other/window_old_pal.nes new file mode 100644 index 0000000..62bf796 Binary files /dev/null and b/other/window_old_pal.nes differ diff --git a/pal_apu_tests/01.len_ctr.nes b/pal_apu_tests/01.len_ctr.nes new file mode 100644 index 0000000..b0619ce Binary files /dev/null and b/pal_apu_tests/01.len_ctr.nes differ diff --git a/pal_apu_tests/02.len_table.nes b/pal_apu_tests/02.len_table.nes new file mode 100644 index 0000000..60d37ce Binary files /dev/null and b/pal_apu_tests/02.len_table.nes differ diff --git a/pal_apu_tests/03.irq_flag.nes b/pal_apu_tests/03.irq_flag.nes new file mode 100644 index 0000000..81a29da Binary files /dev/null and b/pal_apu_tests/03.irq_flag.nes differ diff --git a/pal_apu_tests/04.clock_jitter.nes b/pal_apu_tests/04.clock_jitter.nes new file mode 100644 index 0000000..b8f0339 Binary files /dev/null and b/pal_apu_tests/04.clock_jitter.nes differ diff --git a/pal_apu_tests/05.len_timing_mode0.nes b/pal_apu_tests/05.len_timing_mode0.nes new file mode 100644 index 0000000..f43e8f5 Binary files /dev/null and b/pal_apu_tests/05.len_timing_mode0.nes differ diff --git a/pal_apu_tests/06.len_timing_mode1.nes b/pal_apu_tests/06.len_timing_mode1.nes new file mode 100644 index 0000000..4c44017 Binary files /dev/null and b/pal_apu_tests/06.len_timing_mode1.nes differ diff --git a/pal_apu_tests/07.irq_flag_timing.nes b/pal_apu_tests/07.irq_flag_timing.nes new file mode 100644 index 0000000..bec3b33 Binary files /dev/null and b/pal_apu_tests/07.irq_flag_timing.nes differ diff --git a/pal_apu_tests/08.irq_timing.nes b/pal_apu_tests/08.irq_timing.nes new file mode 100644 index 0000000..ae4b035 Binary files /dev/null and b/pal_apu_tests/08.irq_timing.nes differ diff --git a/pal_apu_tests/10.len_halt_timing.nes b/pal_apu_tests/10.len_halt_timing.nes new file mode 100644 index 0000000..ae566a4 Binary files /dev/null and b/pal_apu_tests/10.len_halt_timing.nes differ diff --git a/pal_apu_tests/11.len_reload_timing.nes b/pal_apu_tests/11.len_reload_timing.nes new file mode 100644 index 0000000..fbb0531 Binary files /dev/null and b/pal_apu_tests/11.len_reload_timing.nes differ diff --git a/pal_apu_tests/readme.txt b/pal_apu_tests/readme.txt new file mode 100644 index 0000000..611800d --- /dev/null +++ b/pal_apu_tests/readme.txt @@ -0,0 +1,173 @@ +PAL NES APU Tests +----------------- +These tests verify the PAL APU's frame sequencer timing. They have been tested +on a PAL NES and all give a passing result. + +Each .nes file runs several tests and reports the result on screen and by +beeping a number of times. See below for the meaning of failure codes for each +test. It's best to run the tests in order, because later tests depend on things +tested by earlier tests and will give erroneous results if any earlier ones +failed. + +Source code for each test is included, and most tests are clearly divided into +sections. Support code is also included, but it runs on a custom devcart and +assembler so it will require some effort to assemble. Contact me if you'd like +assistance porting them to your setup. + + +Frame sequencer timing +---------------------- +See blargg_apu_2005.07.30 for more information about frame sequencer timing +subtleties. This only lists timing differences. + +Mode 0: 4-step sequence + +Action Envelopes & Length Counter& Interrupt Delay to next + Linear Counter Sweep Units Flag NTSC PAL +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +$4017=$00 - - - 7459 8315 +Step 1 Clock - - 7456 8314 +Step 2 Clock Clock - 7458 8312 +Step 3 Clock - - 7458 8314 +Step 4 Clock Clock Set if enabled 7458 8314 + + +Mode 1: 5-step sequence + +Action Envelopes & Length Counter& Interrupt Delay to next + Linear Counter Sweep Units Flag NTSC PAL +- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +$4017=$80 - - - 1 1 +Step 1 Clock Clock - 7458 8314 +Step 2 Clock - - 7456 8314 +Step 3 Clock Clock - 7458 8312 +Step 4 Clock - - 7458 8314 +Step 5 - - - 7452 8312 + +Note: the IRQ flag is actually effectively set three clocks in a row, starting +one clock earlier than shown. NTSC and PAL times shown for comparison. + + +01.len_ctr +---------- +Tests basic length counter operation + +1) Passed tests +2) Problem with length counter load or $4015 +3) Problem with length table, timing, or $4015 +4) Writing $80 to $4017 should clock length immediately +5) Writing $00 to $4017 shouldn't clock length immediately +6) Clearing enable bit in $4015 should clear length counter +7) When disabled via $4015, length shouldn't allow reloading +8) Halt bit should suspend length clocking + + +02.len_table +------------ +Tests all length table entries. + +1) Passed +2) Failed. Prints four bytes $II $ee $cc $02 that indicate the length load +value written (ll), the value that the emulator uses ($ee), and the correct +value ($cc). + + +03.irq_flag +----------- +Tests basic operation of frame irq flag. + +1) Tests passed +2) Flag shouldn't be set in $4017 mode $40 +3) Flag shouldn't be set in $4017 mode $80 +4) Flag should be set in $4017 mode $00 +5) Reading flag clears it +6) Writing $00 or $80 to $4017 doesn't affect flag +7) Writing $40 or $c0 to $4017 clears flag + + +04.clock_jitter +--------------- +Tests for APU clock jitter. Also tests basic timing of frame irq flag since +it's needed to determine jitter. It's OK if you don't implement jitter, in +which case you'll get error #5, but you can still run later tests without +problem. + +1) Passed tests +2) Frame irq is set too soon +3) Frame irq is set too late +4) Even jitter not handled properly +5) Odd jitter not handled properly + + +05.len_timing_mode0 +------------------- +Tests length counter timing in mode 0. + +1) Passed tests +2) First length is clocked too soon +3) First length is clocked too late +4) Second length is clocked too soon +5) Second length is clocked too late +6) Third length is clocked too soon +7) Third length is clocked too late + + +06.len_timing_mode1 +------------------- +Tests length counter timing in mode 1. + +1) Passed tests +2) First length is clocked too soon +3) First length is clocked too late +4) Second length is clocked too soon +5) Second length is clocked too late +6) Third length is clocked too soon +7) Third length is clocked too late + + +07.irq_flag_timing +------------------ +Frame interrupt flag is set three times in a row 33255 clocks after writing +$4017 with $00. + +1) Success +2) Flag first set too soon +3) Flag first set too late +4) Flag last set too soon +5) Flag last set too late + + +08.irq_timing +------------- +IRQ handler is invoked at minimum 33257 clocks after writing $00 to $4017. + +1) Passed tests +2) Too soon +3) Too late +4) Never occurred + + +10.len_halt_timing +------------------ +Changes to length counter halt occur after clocking length, not before. + +1) Passed tests +2) Length shouldn't be clocked when halted at 16628 +3) Length should be clocked when halted at 16629 +4) Length should be clocked when unhalted at 16628 +5) Length shouldn't be clocked when unhalted at 16629 + + +11.len_reload_timing +-------------------- +Write to length counter reload should be ignored when made during length +counter clocking and the length counter is not zero. + +1) Passed tests +2) Reload just before length clock should work normally +3) Reload just after length clock should work normally +4) Reload during length clock when ctr = 0 should work normally +5) Reload during length clock when ctr > 0 should be ignored + +-- +Shay Green diff --git a/pal_apu_tests/source/01.len_ctr.a b/pal_apu_tests/source/01.len_ctr.a new file mode 100644 index 0000000..8641eba --- /dev/null +++ b/pal_apu_tests/source/01.len_ctr.a @@ -0,0 +1,87 @@ +; Tests basic length counter operation +; to do: test triangle channel's differing halt bit position + + .include "prefix_apu.a" + +test_name: + .db "APU LENGTH COUNTER",0 + +reset: + jsr setup_apu + + lda #2;) Problem with length counter load or $4015 + sta result + lda #$18 ; length = 2 + sta $4003 + jsr should_be_playing + + lda #3;) Problem with length table, timing, or $4015 + sta result + lda #250 ; delay 250 msec + jsr delay_msec + jsr should_be_silent + + lda #4;) Writing $80 to $4017 should clock length immediately + sta result + lda #$00 ; mode 0 + sta $4017 + lda #$18 ; length = 2 + sta $4003 + lda #$80 ; clock length twice + sta $4017 + sta $4017 + jsr should_be_silent + + lda #5;) Writing $00 to $4017 shouldn't clock length immediately + sta result + lda #$00 ; mode 0 + sta $4017 + lda #$18 ; length = 2 + sta $4003 + lda #$00 ; write mode twice + sta $4017 + sta $4017 + jsr should_be_playing + + lda #6;) Clearing enable bit in $4015 should clear length counter + sta result + lda #$18 ; length = 2 + sta $4003 + lda #$00 + sta $4015 + lda #$01 + sta $4015 + jsr should_be_silent + + lda #7;) When disabled via $4015, length shouldn't allow reloading + sta result + lda #$00 + sta $4015 + lda #$18 ; attempt to reload + sta $4003 + lda #$01 + sta $4015 + jsr should_be_silent + + lda #8;) Halt bit should suspend length clocking + sta result + lda #$30 ; halt length + sta $4000 + lda #$18 ; length = 2 + sta $4003 + lda #$80 ; attempt to clock length twice + sta $4017 + sta $4017 + jsr should_be_playing + + jmp tests_passed + +should_be_playing: + lda $4015 + and #$01 + jmp error_if_eq + +should_be_silent: + lda $4015 + and #$01 + jmp error_if_ne diff --git a/pal_apu_tests/source/02.len_table.a b/pal_apu_tests/source/02.len_table.a new file mode 100644 index 0000000..222b953 --- /dev/null +++ b/pal_apu_tests/source/02.len_table.a @@ -0,0 +1,49 @@ +; Tests all length table entries. +; +; 1) Passed +; 2) Failed. Prints four bytes $II $ee $cc $02 that indicate the length +; load value written (ll), the value that the emulator uses ($ee), and the +; correct value ($cc). + + .include "prefix_apu.a" + +; Zero-page +entry = 10 + +test_name: + .db "APU LENGTH TABLE",0 + +reset: + jsr setup_apu + + lda #31 + sta entry +loop: lda #$00 ; sync apu + sta $4017 + lda entry + asl a + asl a + asl a + sta $4003 ; load length + lda #$01 ; check resulting length + jsr count_length + ldx entry + cmp table,x + bne error + dec entry + bpl loop + + jmp tests_passed + +error: + inx + inx + txa + sta result + jmp report_final_result + +table: + .byte 10, 254, 20, 2, 40, 4, 80, 6 + .byte 160, 8, 60, 10, 14, 12, 26, 14 + .byte 12, 16, 24, 18, 48, 20, 96, 22 + .byte 192, 24, 72, 26, 16, 28, 32, 30 diff --git a/pal_apu_tests/source/03.irq_flag.a b/pal_apu_tests/source/03.irq_flag.a new file mode 100644 index 0000000..1346ca3 --- /dev/null +++ b/pal_apu_tests/source/03.irq_flag.a @@ -0,0 +1,80 @@ +; Tests basic operation of frame irq flag. + + .include "prefix_apu.a" + +test_name: + .db "APU FRAME IRQ FLAG",0 + +reset: + jsr setup_apu + + lda #2;) Flag shouldn't be set in $4017 mode $40 + sta result + lda #$40 + sta $4017 + lda #20 + jsr delay_msec + jsr should_be_clear + + lda #3;) Flag shouldn't be set in $4017 mode $80 + sta result + lda #$80 + sta $4017 + lda #20 + jsr delay_msec + jsr should_be_clear + + lda #4;) Flag should be set in $4017 mode $00 + sta result + lda #$00 + sta $4017 + lda #20 + jsr delay_msec + jsr should_be_set + + lda #5;) Reading flag clears it + sta result + lda #$00 + sta $4017 + lda #20 + jsr delay_msec + lda $4015 + jsr should_be_clear + + lda #6;) Writing $00 or $80 to $4017 doesn't affect flag + sta result + lda #$00 + sta $4017 + lda #20 + jsr delay_msec + lda #$00 + sta $4017 + lda #$80 + sta $4017 + jsr should_be_set + + lda #7;) Writing $40 or $c0 to $4017 clears flag + sta result + lda #$00 + sta $4017 + lda #20 + jsr delay_msec + lda #$40 + sta $4017 + lda #$00 + sta $4017 + jsr should_be_clear + + jmp tests_passed + +; Report error if flag isn't clear +should_be_clear: + lda $4015 + and #$40 + jmp error_if_ne + +; Report error if flag isn't set +should_be_set: + lda $4015 + and #$40 + jmp error_if_eq diff --git a/pal_apu_tests/source/04.clock_jitter.a b/pal_apu_tests/source/04.clock_jitter.a new file mode 100644 index 0000000..86528d4 --- /dev/null +++ b/pal_apu_tests/source/04.clock_jitter.a @@ -0,0 +1,81 @@ +; Tests jitter where write to $4017 is delayed by one +; clock if it's on an odd clock. + .include "prefix_apu.a" + +jitter = 1 + +; remove indented delays for NTSC timing + +test_name: + .db "APU CLOCK JITTER",0 + +reset: + jsr setup_apu + + lda #2;) Frame irq is set too soon + sta result + lda #$40 ; clear frame irq flag + sta $4017 + lda #$00 ; begin mode 0, frame irq enabled + sta $4017 + ldy #48 ; 29826 delay + lda #123 + jsr delay_ya1 + ldy #26 ; 3424 delay + lda #25 + jsr delay_ya1 + lda $4015 ; read at 33253 + and #$40 + jsr error_if_ne + + lda #3;) Frame irq is set too late + sta result + lda #$40 ; clear frame irq flag + sta $4017 + lda #$00 ; begin mode 0, frame irq enabled + sta $4017 + ldy #48 ; 29828 delay + lda #123 + jsr delay_ya3 + ldy #26 ; 3424 delay + lda #25 + jsr delay_ya1 + lda $4015 ; read at 33255 + and #$40 + jsr error_if_eq + + lda #4;) Even jitter not handled properly + sta result + jsr get_jitter + sta 0 should be ignored + sta result + jsr sync_apu + lda #$38 ; length = 6 + sta reload + lda #$40 ; begin mode 0 + sta $4017 + ldy #205 ; 16623 delay + lda #15 + jsr delay_ya1 + lda #$18 ; try to reload counter with 2 + sta reload ; write at 16629 + lda #mask + jsr count_length + cmp #5 ; should have ignored reload + jsr error_if_ne + + jmp tests_passed diff --git a/pal_apu_tests/source/console.a b/pal_apu_tests/source/console.a new file mode 100644 index 0000000..38cdc5b --- /dev/null +++ b/pal_apu_tests/source/console.a @@ -0,0 +1,108 @@ +; Simple text console using NES PPU. + +console_pos = $7f2 +console_pos_h = $7f3 + +; Print char A to console +; Preserved: A, X, Y +print_char: + jsr wait_vbl ; wait for safe access +print_char_no_wait: + pha + lda console_pos_h + sta $2006 + inc console_pos + lda console_pos + sta $2006 + lda #0 ; restore scroll + sta $2005 + sta $2005 + pla + sta $2007 + rts + .code + +; Go to next line +; Preserved: A, X, Y +console_newline: + pha + lda console_pos + and #$e0 + clc + adc #$21 + sta console_pos + lda console_pos_h + adc #0 + sta console_pos_h + pla + rts + .code + +; Initialize console +init_console: + lda #$81 + sta console_pos + lda #$20 + sta console_pos_h + + jsr wait_vbl ; init ppu + lda #0 + sta $2000 + sta $2001 + + lda #$3f ; load palette + jsr set_vpage + lda #15 ; bg + ldx #48 ; fg + ldy #8 +pal: sta $2007 + stx $2007 + stx $2007 + stx $2007 + dey + bne pal + + lda #$02 ; load tiles + jsr set_vpage + lda #chr_data.lsb + sta <$f0 + lda #chr_data.msb + sta <$f1 + ldy #0 + lda #59 ; 59 chars in data + sta <$f2 +chr_loop: + ldx #8 + lda #0 +: sta $2007 + dex + bne - + + ldx #8 +: lda ($f0),y + iny + sta $2007 + dex + bne - + + tya + bne + + inc <$f1 +: dec <$f2 + bne chr_loop + + lda #32 + jsr fill_nametable + + jsr wait_vbl ; enable ppu + lda #0 + sta $2005 + sta $2005 + lda #$0a + sta $2001 + rts + .code + +chr_data: + .incbin "chr.bin" + diff --git a/pal_apu_tests/source/debug.a b/pal_apu_tests/source/debug.a new file mode 100644 index 0000000..b53f49b --- /dev/null +++ b/pal_apu_tests/source/debug.a @@ -0,0 +1,156 @@ +; Simple debugging utilities for reporting events and state. +; Text output is sent to debug_char and debug_char_nowait. + +; Beep A times. Made to require minimal features from APU. +debug_beeps: +beep_loop: + pha + + lda #1 ; set up square 1 + sta $4015 + sta $4003 + sta $4001 + sta $4002 + + lda #$0f ; fade volume +: pha + eor #$30 + sta $4000 + lda #8 + jsr delay_msec + pla + clc + adc #-1 + bpl - + + lda #0 + sta $4015 ; silence square for a bit + lda #120 + jsr delay_msec + + pla + clc + adc #-1 + bne beep_loop + rts + .code + +; Print indicated register to console as $hh +; Preserved: A, X, Y, P +print_a: + php + jsr debug_byte + plp + rts + .code + +print_x: + php + pha + txa + jsr debug_byte + pla + plp + rts + .code + +print_y: + php + pha + tya + jsr debug_byte + pla + plp + rts + .code + +print_s: + php + pha + txa + pha + tsx + txa + jsr debug_byte + pla + tax + pla + plp + rts + .code + +print_p: + php + pha + php + pla + jsr debug_byte + pla + plp + rts + .code + +print_ay: + php + pha + lda #36 + jsr debug_char + pla + pha + jsr hex_byte + tya + jsr hex_byte + lda #32 ; ' ' + jsr debug_char_no_wait + pla + plp + rts + .code + +print_newline: + jmp debug_newline + .code + +; Print address YA to console as $hhhh +; Preserved: A, X, Y +debug_addr: + pha + lda #36 ; '$' + jsr debug_char + tya + jsr hex_byte + jmp debug_byte_impl + .code + +; Print byte A to console as $hh +; Preserved: A, X, Y +debug_byte: + pha + lda #36 ; '$' + jsr debug_char +debug_byte_impl: + pla + pha + jsr hex_byte + lda #32 ; ' ' + jsr debug_char_no_wait + pla + rts + +hex_byte: + pha + lsr a + lsr a + lsr a + lsr a + jsr nybble + pla + and #$0f +nybble: + cmp #10 + bcc not_letter + adc #6 ; relies on carry being set +not_letter: + adc #$30 + jmp debug_char_no_wait + .code diff --git a/pal_apu_tests/source/delays.a b/pal_apu_tests/source/delays.a new file mode 100644 index 0000000..0a5f2cb --- /dev/null +++ b/pal_apu_tests/source/delays.a @@ -0,0 +1,122 @@ +; Delays specified in milliseconds, tenths of a millisecond, +; tens of milliseconds, and CPU clocks. + +; TODO: delay loops that take only a single count + +; Delay for almost A milliseconds (A * 0.999009524 msec) +; Preserved: X, Y +delay_msec: + pha ; 3 + lda #253 ; 2 + sec ; 2 +delay_msec_: + nop ; 2 + adc #-2 ; 2 + bne delay_msec_ ; 3 + ; -1 + pla ; 4 + clc ; 2 + adc #-1 ; 2 + bne delay_msec ; 3 + rts + .code + +; Delay for almost 'A / 10' milliseconds (A * 0.099453968 msec) +; Preserved: X, Y +delay_01msec: + pha ; 3 + lda #18 ; 2 + sec ; 2 +delay_01msec_: + nop ; 2 + nop ; 2 + adc #-2 ; 2 + bne delay_01msec_ ; 3 + ; -1 + pla ; 4 + clc ; 2 + adc #-1 ; 2 + bne delay_01msec ; 3 + rts + .code + +; Delay for almost A*10 milliseconds +; Preserved: X, Y +delay_10msec: + pha + lda #10 + jsr delay_msec + pla + clc + adc #-1 + bne delay_10msec + rts + .code + +; Delay n clocks +; Preserved: P, A, X, Y +delay_32: nop +delay_30: nop +delay_28: nop +delay_26: nop +delay_24: nop +delay_22: nop +delay_20: nop +delay_18: nop +delay_16: nop +delay_14: nop +delay_12: rts + +delay_33: nop +delay_31: nop +delay_29: nop +delay_27: nop +delay_25: nop +delay_23: nop +delay_21: nop +delay_19: nop +delay_17: beq + ; 5 +: bne + +: rts ; 6 + .code + +; Delay (5 * A + 6) * Y + 7 + n clocks. Use delay_ya +; command-line tool to generate proper code to call this. +delay_ya11: + .db $a2 ; 1 ldx #imm +delay_ya10: + .db $a2 ; 1 ldx #imm +delay_ya9: + .db $a2 ; 1 ldx #imm +delay_ya8: + .db $a2 ; 1 ldx #imm +delay_ya7: + .db $a2 ; 1 ldx #imm +delay_ya6: + .db $a2 ; 1 ldx #imm +delay_ya5: + .db $a2 ; 1 ldx #imm +delay_ya4: + .db $a2 ; 1 ldx #imm +delay_ya3: + .db $a2 ; 1 ldx #imm +delay_ya2: + .db $a2 ; 1 ldx #imm +delay_ya1: + .db $a6 ; 1 ldx zp +delay_ya0: + nop ; 2 +delay_ya: + ; 2 lda # + ; 2 ldy # + ; 6 jsr + tax ; *2 +delay_yax: + dex ; **2 + bne delay_yax ; **3 + ; *-1 + dey ; *2 + bne delay_ya ; *3 + ; -1 + rts ; 6 + .code diff --git a/pal_apu_tests/source/prefix_apu.a b/pal_apu_tests/source/prefix_apu.a new file mode 100644 index 0000000..4994b9d --- /dev/null +++ b/pal_apu_tests/source/prefix_apu.a @@ -0,0 +1,52 @@ + .include "validation.a" + .include "sync_apu.a" + +; Set up APU with square 1 enabled and unhalted +setup_apu: + sei + lda #0 + sta result + lda #$40 ; mode 0, interrupt disabled + sta $4017 + lda #$01 ; enable square 1 + sta $4015 + lda #$10 ; unhalt length + sta $4000 + lda #$7f ; sweep off + sta $4001 + lda #$ff ; period + sta $4002 + rts + .code + +; Count number of length clocks required until $4015 AND A becomes 0 +; then return result in A. +count_length: + ldy #0 + bit $4015 + beq count_length_end + ldx #$c0 +: stx $4017 + iny + beq count_length_overflow + bit $4015 + bne - +count_length_end: + tya + rts +count_length_overflow: + lda #$ff + rts + .code + +; Synchronize with DMC +sync_dmc: + lda #$00 + sta $4010 + lda #1 + sta $4013 + lda #$10 + sta $4015 +: bit $4015 + bne - + rts diff --git a/pal_apu_tests/source/runtime_rom.a b/pal_apu_tests/source/runtime_rom.a new file mode 100644 index 0000000..9aa1ca8 --- /dev/null +++ b/pal_apu_tests/source/runtime_rom.a @@ -0,0 +1,13 @@ +; Build as standalone NES ROM using console for output + + .include "runtime_rom_common.a" + +patch_reset_then_wait: +exit: jmp exit + + .default reset = main + + .org $fffa + .dw nmi + .dw reset + .dw irq diff --git a/pal_apu_tests/source/runtime_rom_common.a b/pal_apu_tests/source/runtime_rom_common.a new file mode 100644 index 0000000..321d462 --- /dev/null +++ b/pal_apu_tests/source/runtime_rom_common.a @@ -0,0 +1,45 @@ + + .include "delays.a" + .include "console.a" + .include "debug.a" + .include "ppu_util.a" + +console_ready = $7f1 + +debug_char: + pha + lda #$a5 + cmp console_ready + beq + + sta console_ready + txa + pha + tya + pha + jsr init_console + pla + tay + pla + tax +: pla + jmp print_char + +debug_newline: + jsr console_newline + jmp console_newline + +debug_char_no_wait: + jmp print_char_no_wait + +init_runtime: +clear_console_ready: + lda #0 + sta console_ready + rts + +forever: + sei ; disable interrupts + lda #0 + sta $2000 + jsr clear_console_ready + jmp exit diff --git a/pal_apu_tests/source/sync_apu.a b/pal_apu_tests/source/sync_apu.a new file mode 100644 index 0000000..d66f3cd --- /dev/null +++ b/pal_apu_tests/source/sync_apu.a @@ -0,0 +1,19 @@ +; Synchronize APU divide-by-two so that an sta $4017 will +; start the frame counter without an extra clock delay. +; Takes 16 msec to execute. +sync_apu: + sei + lda #$40 ; clear irq flag + sta $4017 + lda #$00 ; mode 0, frame irq enabled + sta $4017 + ldy #62 ; 33251 delay + lda #106 + jsr delay_ya2 + lda $4015 + and #$40 + bne + ; delay extra clock if odd jitter +: lda #$40 ; clear irq flag + sta $4017 + rts + .code diff --git a/pal_apu_tests/source/validation.a b/pal_apu_tests/source/validation.a new file mode 100644 index 0000000..e87fa22 --- /dev/null +++ b/pal_apu_tests/source/validation.a @@ -0,0 +1,128 @@ +; Utilities for test ROMs + +result = $f8 + +default_test_name: + .db 0 + .default test_name = default_test_name + +; Report error if branch with given condition would be taken +error_if_ne: + bne report_final_result_ + rts +error_if_eq: + beq report_final_result_ + rts +error_if_mi: + bmi report_final_result_ + rts +error_if_pl: + bpl report_final_result_ + rts +error_if_cc: + bcc report_final_result_ + rts +error_if_cs: + bcs report_final_result_ + rts +error_if_vc: + bvc report_final_result_ + rts +error_if_vs: + bvs report_final_result_ + rts + +; Report passing result +tests_passed: + lda #1 + sta result +; Report final result code and wait forever. +; To have a custom string printed as the test name, +; define the label test_name to a null-terminated string: +; test_name: .db "TEST NAME",0 +report_final_result: +report_final_result_: + sei ; disable interrupts + lda #0 + sta $2000 + jsr init_runtime + + lda test_name + beq no_name + jsr debug_char + ldx #1 +: lda test_name,x + beq no_name + jsr debug_char_no_wait + inx + jmp - + +no_name: + jsr debug_newline + + lda result + bne + + ldx #internal_error_str.msb + ldy #internal_error_str.lsb + jsr print_str + jmp forever +: cmp #1 + beq test_passed + + ldx #failed_str.msb + ldy #failed_str.lsb + jsr print_str + + lda result + cmp #10 + bcc + + clc + adc #-10 + pha + lda #'1' + jsr debug_char_no_wait + pla +: clc + adc #'0' + jsr debug_char_no_wait +report_beeps: + lda result + jsr debug_beeps + jmp forever + +test_passed: + ldx #passed_str.msb + ldy #passed_str.lsb + jsr print_str + jmp report_beeps + +passed_str: + .db "PASSED",0 +failed_str: + .db "FAILED: #",0 +internal_error_str: + .db "INTERNAL ERROR",0 + .code + +; Print null-terminated string at X*$100+Y +print_str: + sty <0 + stx <1 + jsr wait_vbl + ldy #0 + lda (0),y +: iny + jsr debug_char_no_wait + lda (0),y + bne - + rts + .code + + .default nmi = default_nmi + .default irq = default_irq + .code +default_irq: + bit $4015 +default_nmi: + rti + diff --git a/ppu_open_bus/ppu_open_bus.nes b/ppu_open_bus/ppu_open_bus.nes new file mode 100644 index 0000000..a02a15d Binary files /dev/null and b/ppu_open_bus/ppu_open_bus.nes differ diff --git a/ppu_open_bus/readme.txt b/ppu_open_bus/readme.txt new file mode 100644 index 0000000..66d9ff2 --- /dev/null +++ b/ppu_open_bus/readme.txt @@ -0,0 +1,90 @@ +NES PPU Open-Bus Test +--------------------- +Tests behavior when reading from open-bus PPU bits/registers, those bits +that aren't otherwise defined. Unlike other open-bus addresses, the PPU +ones are separate. Takes about 5 seconds to run. + +The PPU effectively has a "decay register", an 8-bit register. Each bit +can be refreshed with a 0 or 1. If a bit isn't refreshed with a 1 for +about 600 milliseconds, it will decay to 0 (some decay sooner, depending +on the NES and temperature). + +Writing to any PPU register sets the decay register to the value +written. Reading from a PPU register is more complex. The following +shows the effect of a read from each register: + + Addr Open-bus bits + 7654 3210 + - - - - - - - - - - - - - - - - + $2000 DDDD DDDD + $2001 DDDD DDDD + $2002 ---D DDDD + $2003 DDDD DDDD + $2004 ---- ---- + $2005 DDDD DDDD + $2006 DDDD DDDD + $2007 ---- ---- non-palette + DD-- ---- palette + +A D means that this bit reads back as whatever is in the decay register +at that bit, and doesn't refresh the decay register at that bit. A - +means that this bit reads back as defined by the PPU, and refreshes the +decay register at the corresponding bit. + + +Flashes, clicks, other glitches +------------------------------- +Some tests might need to turn the screen off and on, or cause slight +audio clicks. This does not indicate failure, and should be ignored. +Only the test result reported at the end is important, unless stated +otherwise. + + +Text output +----------- +Tests generally print information on screen. They also output the same +text as a zero-terminted string beginning at $6004, allowing examination +of output in an NSF player, or a NES emulator without a working PPU. The +tests also work properly if the PPU doesn't set the VBL flag properly or +doesn't implement it at all. + +The final result is displayed and also written to $6000. Before the test +starts, $80 is written there so you can tell when it's done. If a test +needs the NES to be reset, it writes $81 there (emulator should wait a +couple of frames after seeing $81). In addition, $DE $B0 $G1 is written +to $6001-$6003 to allow an emulator to detect when a test is being run, +as opposed to some other NES program. In NSF builds, the final result is +also reported via a series of beeps (see below). + +See the source code for more information about a particular test and why +it might be failing. Each test has comments and correct output at the +top. + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to avoid the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +In addition to the other text output methods described above, NSF builds +report essential information bytes audibly, including the final result. +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason as listed in the +source code by the corresponding set_code line. Examples: + +Tones Binary Decimal Meaning +- - - - - - - - - - - - - - - - - - - - +low 0 0 passed +low high 01 1 failed +low high low 010 2 error 2 + +-- +Shay Green diff --git a/ppu_open_bus/source/common/ascii.chr b/ppu_open_bus/source/common/ascii.chr new file mode 100644 index 0000000..2c5b26b Binary files /dev/null and b/ppu_open_bus/source/common/ascii.chr differ diff --git a/ppu_open_bus/source/common/build_rom.s b/ppu_open_bus/source/common/build_rom.s new file mode 100644 index 0000000..dcb7df5 --- /dev/null +++ b/ppu_open_bus/source/common/build_rom.s @@ -0,0 +1,92 @@ +; Builds program as iNES ROM + +; Default is 16K PRG and 8K CHR ROM, NROM (0) + +.if 0 ; Options to set before .include "shell.inc": +CHR_RAM=1 ; Use CHR-RAM instead of CHR-ROM +CART_WRAM=1 ; Use mapper that supports 8K WRAM in cart +CUSTOM_MAPPER=n ; Specify mapper number +.endif + +.ifndef CUSTOM_MAPPER + .ifdef CART_WRAM + CUSTOM_MAPPER = 2 ; UNROM + .else + CUSTOM_MAPPER = 0 ; NROM + .endif +.endif + +;;;; iNES header +.ifndef CUSTOM_HEADER + .segment "HEADER" + .byte $4E,$45,$53,26 ; "NES" EOF + + .ifdef CHR_RAM + .byte 2,0 ; 32K PRG, CHR RAM + .else + .byte 2,1 ; 32K PRG, 8K CHR + .endif + + .byte CUSTOM_MAPPER*$10+$01 ; vertical mirroring +.endif + +.ifndef CUSTOM_VECTORS + .segment "VECTORS" + .word -1,-1,-1, nmi, reset, irq +.endif + +;;;; CHR-RAM/ROM +.ifdef CHR_RAM + .define CHARS "CHARS_PRG" + .segment CHARS + ascii_chr: + + .segment "CHARS_PRG_ASCII" + .align $200 + .incbin "ascii.chr" + ascii_chr_end: +.else + .define CHARS "CHARS" + .segment "CHARS_ASCII" + .align $200 + .incbin "ascii.chr" + .res $1800 +.endif + +.segment CHARS + .res $10,0 + +;;;; Shell +.ifndef NEED_CONSOLE + NEED_CONSOLE=1 +.endif + +; Move code to $C000 +.segment "DMC" + .res $4000 + +.include "shell.s" + +std_reset: + lda #0 + sta PPUCTRL + sta PPUMASK + jmp run_shell + +init_runtime: + .ifdef CHR_RAM + load_ascii_chr + .endif + rts + +post_exit: + jsr set_final_result + jmp forever + +; This helps devcart recover after running test. +; It is never executed by test ROM. +.segment "LOADER" + .incbin "devcart.bin" + +.code +.align 256 diff --git a/ppu_open_bus/source/common/console.s b/ppu_open_bus/source/common/console.s new file mode 100644 index 0000000..f2d6c19 --- /dev/null +++ b/ppu_open_bus/source/common/console.s @@ -0,0 +1,224 @@ +; Scrolling text console with line wrapping, 30x29 characters. +; Buffers lines for speed. Will work even if PPU doesn't +; support scrolling (until text reaches bottom). Keeps border +; along bottom in case TV cuts it off. +; +; Defers most initialization until first newline, at which +; point it clears nametable and makes palette non-black. +; +; ** ASCII font must already be in CHR, and mirroring +; must be vertical or single-screen. + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs +console_margin = 1 + +console_buf_size = 32 +console_width = console_buf_size - (console_margin*2) + +zp_byte console_pos +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_buf_size + + +; Initializes console +console_init: + ; Flag that console hasn't been initialized + setb console_scroll,-1 + jmp console_clear_line_ + + +; Hides console by blacking palette and disabling PPU. +; Preserved: A, X, Y +console_hide: + pha + jsr console_wait_vbl_ + setb PPUMASK,0 + lda #$0F + jsr console_load_palette_ + pla + rts + + +console_wait_vbl_: + lda console_scroll + cmp #-1 + jne wait_vbl_optional + + ; Deferred initialization of PPU until first use of console + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + setb console_scroll,16 + + jsr console_hide + txa + pha + + ; Fill nametable with spaces + setb PPUADDR,$20 + setb PPUADDR,$00 + ldx #240 + lda #' ' +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + + ; Clear attributes + lda #0 + ldx #$40 +: sta PPUDATA + dex + bne :- + + pla + tax + jmp console_show + + +; Shows console display +; Preserved: X, Y +console_show: + pha + + jsr console_wait_vbl_ + setb PPUMASK,PPUMASK_BG0 + + lda #$30 ; white + jsr console_load_palette_ + + jmp console_apply_scroll_ + + +console_load_palette_: + pha + setb PPUADDR,$3F + setb PPUADDR,$00 + setb PPUDATA,$0F ; black + pla + sta PPUDATA + sta PPUDATA + sta PPUDATA + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + stx console_temp + + ; Newline if buf full and next char isn't space + ldx console_pos + bpl :+ + cmp #' ' + beq @ignore_space + ldx console_temp + jsr console_newline + stx console_temp + ldx console_pos +: + ; Write to buffer + sta console_buf+console_margin,x + dex + stx console_pos + +@ignore_space: + ldx console_temp + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl_ + jsr console_flush_ + jsr console_clear_line_ + + ; Scroll up 8 pixels and clear one line AHEAD + lda console_scroll + jsr console_add_8_to_scroll_ + sta console_scroll + jsr console_add_8_to_scroll_ + jsr console_flush_a + jmp console_apply_scroll_ + + +; A = (A + 8) % 240 +console_add_8_to_scroll_: + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_clear_line_: + stx console_temp + + ; Start new clear line + lda #' ' + ldx #console_buf_size-1 +: sta console_buf,x + dex + bpl :- + ldx #console_width-1 + stx console_pos + + ldx console_temp + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl_ + jsr console_flush_ +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + lda console_scroll + jsr console_add_8_to_scroll_ + jsr console_add_8_to_scroll_ + sta PPUSCROLL + + pla + rts + +console_flush_: + lda console_scroll +console_flush_a: + ; Address line in nametable + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + sta PPUADDR + + ; Copy line + stx console_temp + ldx #console_buf_size-1 +: lda console_buf,x + sta PPUDATA + dex + bpl :- + ldx console_temp + + rts + diff --git a/ppu_open_bus/source/common/crc.s b/ppu_open_bus/source/common/crc.s new file mode 100644 index 0000000..de96c2a --- /dev/null +++ b/ppu_open_bus/source/common/crc.s @@ -0,0 +1,118 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + jmp reset_crc + + +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on + + +; EQ if checksum matches CRC +; Out: A=0 and EQ if match, A>0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ; Compare with complemented checksum + ldy #3 +: lda (ptr),y + sec + adc checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/ppu_open_bus/source/common/delay.s b/ppu_open_bus/source/common/delay.s new file mode 100644 index 0000000..2ff059a --- /dev/null +++ b/ppu_open_bus/source/common/delay.s @@ -0,0 +1,190 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_byte delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/ppu_open_bus/source/common/devcart.bin b/ppu_open_bus/source/common/devcart.bin new file mode 100644 index 0000000..e8958c6 Binary files /dev/null and b/ppu_open_bus/source/common/devcart.bin differ diff --git a/ppu_open_bus/source/common/macros.inc b/ppu_open_bus/source/common/macros.inc new file mode 100644 index 0000000..52982a6 --- /dev/null +++ b/ppu_open_bus/source/common/macros.inc @@ -0,0 +1,169 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name. +; If size is omitted, reserves one byte. +.macro zp_res name,size + .ifblank size + zp_res name,1 + .else + seg_data "ZEROPAGE",{name: .res size} + .endif +.endmacro + +.macro bss_res name,size + .ifblank size + bss_res name,1 + .else + seg_data "BSS",{name: .res size} + .endif +.endmacro + +.macro nv_res name,size + .ifblank size + nv_res name,1 + .else + seg_data "NVRAM",{name: .res size} + .endif +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "RODATA",{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro + +; Copies byte from in to out +; Preserved: X, Y +.macro mov out, in + lda in + sta out +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments word at Addr and sets Z flag appropriately +; Preserved: A, X, Y +.macro incw Addr + .local @incw_skip ; doesn't work, so HOW THE HELL DO YOU MAKE A LOCAL LABEL IN A MACRO THAT DOESN"T DISTURB INVOKING CODE< HUH?????? POS + inc Addr + bne @incw_skip + inc Addr+1 +@incw_skip: +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro incxy7 + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro diff --git a/ppu_open_bus/source/common/neshw.inc b/ppu_open_bus/source/common/neshw.inc new file mode 100644 index 0000000..bdd4136 --- /dev/null +++ b/ppu_open_bus/source/common/neshw.inc @@ -0,0 +1,37 @@ +; NES I/O locations and masks + +; Clocks per second +.ifndef CLOCK_RATE + CLOCK_RATE = 1789773 ; NTSC +; CLOCK_RATE = 1662607 ; PAL +.endif + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 diff --git a/ppu_open_bus/source/common/ppu.s b/ppu_open_bus/source/common/ppu.s new file mode 100644 index 0000000..21cad12 --- /dev/null +++ b/ppu_open_bus/source/common/ppu.s @@ -0,0 +1,142 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts diff --git a/ppu_open_bus/source/common/print.s b/ppu_open_bus/source/common/print.s new file mode 100644 index 0000000..d2d7b81 --- /dev/null +++ b/ppu_open_bus/source/common/print.s @@ -0,0 +1,225 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr @nibble + pla + + pha + and #$0F + jsr @nibble + pla + rts + +@nibble: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: Y +print_dec: + ; Hundreds + cmp #10 + blt @ones + cmp #100 + blt @tens + ldx #'0'-1 +: inx + sbc #100 + bge :- + adc #100 + jsr @digit + + ; Tens +@tens: sec + ldx #'0'-1 +: inx + sbc #10 + bge :- + adc #10 + jsr @digit + + ; Ones +@ones: ora #'0' + jmp print_char + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/ppu_open_bus/source/common/shell.inc b/ppu_open_bus/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/ppu_open_bus/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/ppu_open_bus/source/common/shell.s b/ppu_open_bus/source/common/shell.s new file mode 100644 index 0000000..fcd49a9 --- /dev/null +++ b/ppu_open_bus/source/common/shell.s @@ -0,0 +1,331 @@ +; Common routines and runtime + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +;**** Special globals **** + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +.segment "NVRAM" + ; Beginning of variables not cleared at startup + nvram_begin: + +;**** Code segment setup **** + +.segment "RODATA" + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +; Move code to $E200 ($200 bytes for text output) +.segment "DMC" + .res $2200 + +; Devcart corrupts byte at $E000 when powering off +.segment "CODE" + nop + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "print.s" +.include "delay.s" +.include "crc.s" +.include "testing.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts + +.endif + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sei + cld + ldx #$FF + txs + pha + + setb SNDCHN,0 + .ifndef BUILD_NSF + setb PPUCTRL,0 + .endif + + pla + pha + jsr report_result + ;jsr clear_nvram ; TODO: was this needed for anything? + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + +;**** Other routines **** + +; Reports internal error and exits program +internal_error: + print_str newline,"Internal error" + lda #255 + jmp exit + + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +; Clears $0-($100+S) and nv_ram_end-$7FF +clear_ram: + lda #0 + + ; Main pages + tax +: sta 0,x + sta $300,x + sta $400,x + sta $500,x + sta $600,x + sta $700,x + inx + bne :- + + ; Stack except that above stack pointer + tsx + inx +: dex + sta $100,x + bne :- + + ; BSS except nvram + ldx #<__NVRAM_SIZE__ +: sta __NVRAM_LOAD__,x + inx + bne :- + + rts + + +; Clears nvram +clear_nvram: + ldx #<__NVRAM_SIZE__ + beq @empty + lda #0 +: dex + sta __NVRAM_LOAD__,x + bne :- +@empty: + rts + + +; Prints filename and newline, if available, otherwise nothing. +; Preserved: A, X, Y +print_filename: + .ifdef FILENAME_KNOWN + pha + jsr print_newline + setw addr,filename + jsr print_str_addr + jsr print_newline + pla + .endif + rts + +.pushseg +.segment "RODATA" + ; Filename terminated with zero byte. + filename: + .ifdef FILENAME_KNOWN + .incbin "ram:nes_temp" + .endif + .byte 0 +.popseg + + +;**** ROM-specific **** +.ifndef BUILD_NSF + +.include "ppu.s" + +avoid_silent_nsf: +play_byte: + rts + +; Loads ASCII font into CHR RAM +.macro load_ascii_chr + bit PPUSTATUS + setb PPUADDR,$00 + setb PPUADDR,$00 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page +.endmacro + +; Disables interrupts and loops forever +.ifndef CUSTOM_FOREVER +forever: + sei + lda #0 + sta PPUCTRL +: beq :- + .res $10,$EA ; room for code to run loader +.endif + + +; Default NMI +.ifndef CUSTOM_NMI + zp_byte nmi_count + + nmi: + inc nmi_count + rti + + ; Waits for NMI. Must be using NMI handler that increments + ; nmi_count, with NMI enabled. + ; Preserved: X, Y + wait_nmi: + lda nmi_count + : cmp nmi_count + beq :- + rts +.endif + + +; Default IRQ +.ifndef CUSTOM_IRQ + irq: + bit SNDCHN ; clear APU IRQ flag + rti +.endif + +.endif diff --git a/ppu_open_bus/source/common/testing.s b/ppu_open_bus/source/common/testing.s new file mode 100644 index 0000000..ba41f03 --- /dev/null +++ b/ppu_open_bus/source/common/testing.s @@ -0,0 +1,106 @@ +; Utilities for writing test ROMs + +; In NVRAM so these can be used before initializing runtime, +; then runtime initialized without clearing them +nv_res test_code ; code of current test +nv_res test_name,2 ; address of name of current test, or 0 of none + + +; Sets current test code and optional name. Also resets +; checksum. +; Preserved: A, X, Y +.macro set_test code,name + pha + lda #code + jsr set_test_ + .ifblank name + setb test_name+1,0 + .else + .local Addr + setw test_name,Addr + seg_data "RODATA",{Addr: .byte name,0} + .endif + pla +.endmacro + +set_test_: + sta test_code + jmp reset_crc + + +; Initializes testing module +init_testing: + jmp init_crc + + +; Reports that all tests passed +tests_passed: + jsr print_filename + print_str newline,"Passed" + lda #0 + jmp exit + + +; Reports "Done" if set_test has never been used, +; "Passed" if set_test 0 was last used, or +; failure if set_test n was last used. +tests_done: + ldx test_code + jeq tests_passed + inx + bne test_failed + jsr print_filename + print_str newline,"Done" + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + ldx test_code + + ; Treat $FF as 1, in case it wasn't ever set + inx + bne :+ + inx + stx test_code +: + ; If code >= 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/ppu_open_bus/source/common/text_out.s b/ppu_open_bus/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/ppu_open_bus/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +Most update a running CRC-32 checksum which can be checked with +check_crc, allowing ALL the output to be checked very easily. If the +checksum doesn't match, it is printed, so you can run the code on a NES +and paste the correct checksum into your code. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported due to their complexity. I +load the code at $E000 since my devcart requires it, and I don't want +the normal ROM to differ in any way from what I've tested. This also +allows easy self-modifying code. + +Some macros are used to make common operations more convenient. The left +is equivalent to the right: + + Macro Equivalent + ------------------------------------- + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + set addr,byte lda #byte + sta addr + + zp_byte name .zeropage + name: .res 1 + .code + + zp_res name,n .zeropage + name: .res n + .code + + bss_res name,n .bss + name: .res n + .code + + for_loop routine,begin,end,step + calls routine with A set to successive values + + loop_n_times routine,count + calls routine with A from 0 to count-1 + +-- +Shay Green diff --git a/ppu_vbl_nmi/ppu_vbl_nmi.nes b/ppu_vbl_nmi/ppu_vbl_nmi.nes new file mode 100644 index 0000000..bb3743c Binary files /dev/null and b/ppu_vbl_nmi/ppu_vbl_nmi.nes differ diff --git a/ppu_vbl_nmi/readme.txt b/ppu_vbl_nmi/readme.txt new file mode 100644 index 0000000..8b2f22d --- /dev/null +++ b/ppu_vbl_nmi/readme.txt @@ -0,0 +1,255 @@ +NES PPU Tests +------------- +These tests verify the behavior and timing of the NTSC PPU's VBL flag, +NMI enable, and NMI interrupt. Timing is tested to an accuracy of one +PPU clock. Note that often the NES starts up with a different value in +the clock divider, causing PPU timing to be slightly different and fail +some of the tests. These test the timings that have been most fully +documented and emulated. + + +01-vbl_basics +------------- +Tests basic VBL operation and VBL period. + +2) VBL period is way off +3) Reading VBL flag should clear it +4) Writing $2002 shouldn't affect VBL flag +5) $2002 should be mirrored at $200A +6) $2002 should be mirrored every 8 bytes up to $2FFA +7) VBL period is too short with BG off +8) VBL period is too long with BG off + + +02-vbl_set_time +--------------- +Verifies time VBL flag is set. + +Reads $2002 twice and prints VBL flags from +them. Test is run one PPU clock later each time, +around the time the flag is set. + +00 - V +01 - V +02 - V +03 - V ; after some resets this is - - +04 - - ; flag setting is suppressed +05 V - +06 V - +07 V - +08 V - + + +03-vbl_clear_time +----------------- +Tests time VBL flag is cleared. + +Reads $2002 and prints VBL flag. +Test is run one PPU clock later each line, +around the time the flag is cleared. + +00 V +01 V +02 V +03 V +04 V +05 V +06 - +07 - +08 - + + +04-nmi_control +-------------- +Tests immediate NMI behavior when enabling while VBL flag is already set + +2) Shouldn't occur when disabled +3) Should occur when enabled and VBL begins +4) $2000 should be mirrored every 8 bytes +5) Should occur immediately if enabled while VBL flag is set +6) Shouldn't occur if enabled while VBL flag is clear +7) Shouldn't occur again if writing $80 when already enabled +8) Shouldn't occur again if writing $80 when already enabled 2 +9) Should occur again if enabling after disabled +10) Should occur again if enabling after disabled 2 +11) Immediate occurence should be after NEXT instruction + + +05-nmi_timing +------------- +Tests NMI timing. + +Prints which instruction NMI occurred +after. Test is run one PPU clock later +each line. + +00 4 +01 4 +02 4 +03 3 +04 3 +05 3 +06 3 +07 3 +08 3 +09 2 + + +06-suppression +-------------- +Tests behavior when $2002 is read near time +VBL flag is set. + +Reads $2002 one PPU clock later each time. +Prints whether VBL flag read back as set, and +whether NMI occurred. + +00 - N +01 - N +02 - N +03 - N ; normal behavior +04 - - ; flag never set, no NMI +05 V - ; flag read back as set, but no NMI +06 V - +07 V N ; normal behavior +08 V N +09 V N + + +07-nmi_on_timing +---------------- +Tests NMI occurrence when enabled near time +VBL flag is cleared. + +Enables NMI one PPU clock later on each line. +Prints whether NMI occurred. + +00 N +01 N +02 N +03 N +04 N +05 - +06 - +07 - +08 - + + +08-nmi_off_timing +----------------- +Tests NMI occurrence when disabled near time +VBL flag is set. + +Disables NMI one PPU clock later on each line. +Prints whether NMI occurred. + +03 - +04 - +05 - +06 - +07 N +08 N +09 N +0A N +0B N +0C N + + +09-even_odd_frames +------------------ +Tests clock skipped on every other PPU frame when BG rendering +is enabled. + +Tries pattern of BG enabled/disabled during a sequence of +5 frames, then finds how many clocks were skipped. Prints +number skipped clocks to help find problems. + +Correct output: 00 01 01 02 + + +10-even_odd_timing +------------------ +Tests timing of skipped clock every other frame +when BG is enabled. + +Output: 08 08 09 07 + +2) Clock is skipped too soon, relative to enabling BG +3) Clock is skipped too late, relative to enabling BG +4) Clock is skipped too soon, relative to disabling BG +5) Clock is skipped too late, relative to disabling BG + +Multi-tests +----------- +The NES/NSF builds in the main directory consist of multiple sub-tests. +When run, they list the subtests as they are run. The final result code +refers to the first sub-test that failed. For more information about any +failed subtests, run them individually from rom_singles/ and +nsf_singles/. + + +Flashes, clicks, other glitches +------------------------------- +If a test prints "passed", it passed, even if there were some flashes or +odd sounds. Only a test which prints "done" at the end requires that you +watch/listen while it runs in order to determine whether it passed. Such +tests involve things which the CPU cannot directly test. + + +Alternate output +---------------- +Tests generally print information on screen, but also report the final +result audibly, and output text to memory, in case the PPU doesn't work +or there isn't one, as in an NSF or a NES emulator early in development. + +After the tests are done, the final result is reported as a series of +beeps (see below). For NSF builds, any important diagnostic bytes are +also reported as beeps, before the final result. + + +Output at $6000 +--------------- +All text output is written starting at $6004, with a zero-byte +terminator at the end. As more text is written, the terminator is moved +forward, so an emulator can print the current text at any time. + +The test status is written to $6000. $80 means the test is running, $81 +means the test needs the reset button pressed, but delayed by at least +100 msec from now. $00-$7F means the test has completed and given that +result code. + +To allow an emulator to know when one of these tests is running and the +data at $6000+ is valid, as opposed to some other NES program, $DE $B0 +$G1 is written to $6001-$6003. + + +Audible output +-------------- +A byte is reported as a series of tones. The code is in binary, with a +low tone for 0 and a high tone for 1, and with leading zeroes skipped. +The first tone is always a zero. A final code of 0 means passed, 1 means +failure, and 2 or higher indicates a specific reason. See the source +code of the test for more information about the meaning of a test code. +They are found after the set_test macro. For example, the cause of test +code 3 would be found in a line containing set_test 3. Examples: + + Tones Binary Decimal Meaning + - - - - - - - - - - - - - - - - - - - - + low 0 0 passed + low high 01 1 failed + low high low 010 2 error 2 + + +NSF versions +------------ +Many NSF-based tests require that the NSF player either not interrupt +the init routine with the play routine, or if it does, not interrupt the +play routine again if it hasn't returned yet. This is because many tests +need to run for a while without returning. + +NSF versions also make periodic clicks to prevent the NSF player from +thinking the track is silent and thus ending the track before it's done +testing. + +-- +Shay Green diff --git a/ppu_vbl_nmi/rom_singles/01-vbl_basics.nes b/ppu_vbl_nmi/rom_singles/01-vbl_basics.nes new file mode 100644 index 0000000..baa0591 Binary files /dev/null and b/ppu_vbl_nmi/rom_singles/01-vbl_basics.nes differ diff --git a/ppu_vbl_nmi/rom_singles/02-vbl_set_time.nes b/ppu_vbl_nmi/rom_singles/02-vbl_set_time.nes new file mode 100644 index 0000000..83e7683 Binary files /dev/null and b/ppu_vbl_nmi/rom_singles/02-vbl_set_time.nes differ diff --git a/ppu_vbl_nmi/rom_singles/03-vbl_clear_time.nes b/ppu_vbl_nmi/rom_singles/03-vbl_clear_time.nes new file mode 100644 index 0000000..045f85f Binary files /dev/null and b/ppu_vbl_nmi/rom_singles/03-vbl_clear_time.nes differ diff --git a/ppu_vbl_nmi/rom_singles/04-nmi_control.nes b/ppu_vbl_nmi/rom_singles/04-nmi_control.nes new file mode 100644 index 0000000..ac0ed51 Binary files /dev/null and b/ppu_vbl_nmi/rom_singles/04-nmi_control.nes differ diff --git a/ppu_vbl_nmi/rom_singles/05-nmi_timing.nes b/ppu_vbl_nmi/rom_singles/05-nmi_timing.nes new file mode 100644 index 0000000..1cc9cd1 Binary files /dev/null and b/ppu_vbl_nmi/rom_singles/05-nmi_timing.nes differ diff --git a/ppu_vbl_nmi/rom_singles/06-suppression.nes b/ppu_vbl_nmi/rom_singles/06-suppression.nes new file mode 100644 index 0000000..ac16afa Binary files /dev/null and b/ppu_vbl_nmi/rom_singles/06-suppression.nes differ diff --git a/ppu_vbl_nmi/rom_singles/07-nmi_on_timing.nes b/ppu_vbl_nmi/rom_singles/07-nmi_on_timing.nes new file mode 100644 index 0000000..5eaacc4 Binary files /dev/null and b/ppu_vbl_nmi/rom_singles/07-nmi_on_timing.nes differ diff --git a/ppu_vbl_nmi/rom_singles/08-nmi_off_timing.nes b/ppu_vbl_nmi/rom_singles/08-nmi_off_timing.nes new file mode 100644 index 0000000..9bb8c2d Binary files /dev/null and b/ppu_vbl_nmi/rom_singles/08-nmi_off_timing.nes differ diff --git a/ppu_vbl_nmi/rom_singles/09-even_odd_frames.nes b/ppu_vbl_nmi/rom_singles/09-even_odd_frames.nes new file mode 100644 index 0000000..50c8e9f Binary files /dev/null and b/ppu_vbl_nmi/rom_singles/09-even_odd_frames.nes differ diff --git a/ppu_vbl_nmi/rom_singles/10-even_odd_timing.nes b/ppu_vbl_nmi/rom_singles/10-even_odd_timing.nes new file mode 100644 index 0000000..7d9ec44 Binary files /dev/null and b/ppu_vbl_nmi/rom_singles/10-even_odd_timing.nes differ diff --git a/ppu_vbl_nmi/source/01-vbl_basics.s b/ppu_vbl_nmi/source/01-vbl_basics.s new file mode 100644 index 0000000..00174af --- /dev/null +++ b/ppu_vbl_nmi/source/01-vbl_basics.s @@ -0,0 +1,58 @@ +; Tests basic VBL operation and VBL period. + +.include "shell.inc" + +main: set_test 2,"VBL period is way off" + jsr wait_vbl + delay 30111 + lda $2002 + jpl test_failed + + set_test 3,"Reading VBL flag should clear it" + lda $2002 + jmi test_failed + + set_test 4,"Writing $2002 shouldn't affect VBL flag" + jsr wait_vbl + delay 30111 + sta $2002 + lda $2002 + jpl test_failed + + set_test 5,"$2002 should be mirrored at $200A" + jsr wait_vbl + delay 30111 + lda $200A + jpl test_failed + lda $200A + jmi test_failed + + set_test 6,"$2002 should be mirrored every 8 bytes up to $2FFA" + jsr wait_vbl + delay 30111 + lda $2FFA + jpl test_failed + lda $2FFA + jmi test_failed + + delay_msec 1000 + + lda #0 ; BG off + sta $2001 + + ; Delay 60 frames after VBL, then read VBL flag + jsr wait_vbl + delay 29780*60+60/3 + ldx $2002 + delay 5 + lda $2002 + + set_test 7,"VBL period is too short with BG off" + cpx #0 + jmi test_failed + + set_test 8,"VBL period is too long with BG off" + cmp #0 + jpl test_failed + + jmp tests_passed diff --git a/ppu_vbl_nmi/source/02-vbl_set_time.s b/ppu_vbl_nmi/source/02-vbl_set_time.s new file mode 100644 index 0000000..c3c21a1 Binary files /dev/null and b/ppu_vbl_nmi/source/02-vbl_set_time.s differ diff --git a/ppu_vbl_nmi/source/03-vbl_clear_time.s b/ppu_vbl_nmi/source/03-vbl_clear_time.s new file mode 100644 index 0000000..aa0f1c4 --- /dev/null +++ b/ppu_vbl_nmi/source/03-vbl_clear_time.s @@ -0,0 +1,33 @@ +; Tests time VBL flag is cleared. +; +; Reads $2002 and prints VBL flag. +; Test is run one PPU clock later each line, +; around the time the flag is cleared. +; +; 00 V +; 01 V +; 02 V +; 03 V +; 04 V +; 05 V +; 06 - +; 07 - +; 08 - + +.include "shell.inc" +.include "sync_vbl.s" + +main: jsr console_hide + loop_n_times test,9 + check_crc $E8ADB5BB + jmp tests_passed + +test: jsr print_a + jsr disable_rendering + jsr sync_vbl_delay + delay 29763 + delay 2273 + lda $2002 + print_cc bmi,'V','-' + jsr print_newline + rts diff --git a/ppu_vbl_nmi/source/04-nmi_control.s b/ppu_vbl_nmi/source/04-nmi_control.s new file mode 100644 index 0000000..5b07cc0 --- /dev/null +++ b/ppu_vbl_nmi/source/04-nmi_control.s @@ -0,0 +1,111 @@ +; Tests immediate NMI behavior when enabling while VBL flag is already set + +CUSTOM_NMI=1 +.include "shell.inc" + +zp_byte nmi_count + +nmi: inc nmi_count + rti + +; Waits until NMI is about to occur +begin: lda #0 + sta $2000 + jsr wait_vbl + delay 29600 + lda #0 + sta nmi_count + rts + +; Enables NMI, waits, then reads NMI count +end: lda #$80 + sta $2000 + delay 200 + lda nmi_count + rts + +main: set_test 2,"Shouldn't occur when disabled" + jsr begin + delay 200 + lda nmi_count + jne test_failed + + set_test 3,"Should occur when enabled and VBL begins" + jsr begin + jsr end + jeq test_failed + + set_test 4,"$2000 should be mirrored every 8 bytes" + jsr begin + lda #$80 + sta $2FF8 + delay 200 + lda nmi_count + jeq test_failed + + set_test 5,"Should occur immediately if enabled while VBL flag is set" + jsr begin + delay 200 ; VBL flag set during this time + jsr end ; NMI is enabled here, and should occur immediately + cmp #1 + jne test_failed + + set_test 6,"Shouldn't occur if enabled while VBL flag is clear" + jsr begin + delay 200 + lda $2002 ; clear VBL flag + jsr end + jne test_failed + + set_test 7,"Shouldn't occur again if writing $80 when already enabled" + jsr begin + lda #$80 + sta $2000 + delay 200 ; NMI occurs here + jsr end ; writes $80 again, shouldn't occur again + cmp #1 ; only 1 NMI should have occurred + jne test_failed + + set_test 8,"Shouldn't occur again if writing $80 when already enabled 2" + jsr begin + delay 200 ; VBL flag set during this time + lda #$80 ; enable NMI, which should result in immediate NMI + sta $2000 + jsr end ; writes $80 again, shouldn't occur again + cmp #1 ; only 1 NMI should have occurred + jne test_failed + + set_test 9,"Should occur again if enabling after disabled" + jsr begin + lda #$80 + sta $2000 + delay 200 ; NMI occurs here + lda #$00 ; disable NMI + sta $2000 + jsr end ; NMI is enabled again, and should occur immediately + cmp #2 ; 2 NMIs should have occurred + jne test_failed + + set_test 10,"Should occur again if enabling after disabled 2" + jsr begin + delay 200 ; VBL flag set during this time + lda #$80 ; enable NMI, which should result in immediate NMI + sta $2000 + lda #$00 ; disable NMI + sta $2000 + jsr end ; NMI is enabled again, and should occur immediately + cmp #2 ; 2 NMIs should have occurred + jne test_failed + + set_test 11,"Immediate occurence should be after NEXT instruction" + jsr begin + delay 200 ; VBL flag set during this time + ldx #0 + lda #$80 ; enable NMI, which should result in immediate NMI + sta $2000 ; after NEXT instruction + stx nmi_count ; clear nmi_count + ; NMI should occur here + lda nmi_count + jeq test_failed + + jmp tests_passed diff --git a/ppu_vbl_nmi/source/05-nmi_timing.s b/ppu_vbl_nmi/source/05-nmi_timing.s new file mode 100644 index 0000000..eb115ff --- /dev/null +++ b/ppu_vbl_nmi/source/05-nmi_timing.s @@ -0,0 +1,55 @@ +; Tests NMI timing. +; +; Prints which instruction NMI occurred +; after. Test is run one PPU clock later +; each line. +; +; 00 4 +; 01 4 +; 02 4 +; 03 3 +; 04 3 +; 05 3 +; 06 3 +; 07 3 +; 08 3 +; 09 2 + +CUSTOM_NMI=1 +.include "shell.inc" +.include "sync_vbl.s" + +zp_byte nmi_data + +nmi: stx nmi_data + rti + +main: jsr console_hide + loop_n_times test,10 + check_crc $A6CCB10A + jmp tests_passed + +test: jsr print_a + jsr disable_rendering + jsr sync_vbl_delay + delay 29749+29781 + lda #$FF + sta nmi_data + ldx #0 + lda #$80 + sta $2000 +landing: + ; NMI occurs after one of these + ; instructions and prints X + ldx #1 + ldx #2 + ldx #3 + ldx #4 + ldx #5 + + lda #0 + sta $2000 + lda nmi_data + jsr print_dec + jsr print_newline + rts diff --git a/ppu_vbl_nmi/source/06-suppression.s b/ppu_vbl_nmi/source/06-suppression.s new file mode 100644 index 0000000..ba1c288 --- /dev/null +++ b/ppu_vbl_nmi/source/06-suppression.s @@ -0,0 +1,63 @@ +; Tests behavior when $2002 is read near time +; VBL flag is set. +; +; Reads $2002 one PPU clock later each time. +; Prints whether VBL flag read back as set, and +; whether NMI occurred. +; +; 00 - N +; 01 - N +; 02 - N +; 03 - N ; normal behavior +; 04 - - ; flag never set, no NMI +; 05 V - ; flag read back as set, but no NMI +; 06 V - +; 07 V N ; normal behavior +; 08 V N +; 09 V N + +CUSTOM_NMI=1 +.include "shell.inc" +.include "sync_vbl.s" + +zp_byte nmi_count + +nmi: inc nmi_count + rti + +main: jsr console_hide + loop_n_times test,10 + check_crc $A6816580 + jmp tests_passed + +test: jsr print_a + jsr disable_rendering + jsr sync_vbl_delay + delay 29748 + + lda #0 + sta 0 and NE if different +; Preserved: X, Y +.macro is_crc crc + jsr_with_addr is_crc_,{.dword crc} +.endmacro + +is_crc_: + tya + pha + + ldy #3 +: lda (ptr),y + cmp checksum,y + bne @wrong + dey + bpl :- + pla + tay + lda #0 + rts + +@wrong: + pla + tay + lda #1 + rts diff --git a/ppu_vbl_nmi/source/common/delay.s b/ppu_vbl_nmi/source/common/delay.s new file mode 100644 index 0000000..e8d7278 --- /dev/null +++ b/ppu_vbl_nmi/source/common/delay.s @@ -0,0 +1,202 @@ +; Delays in CPU clocks, milliseconds, etc. All routines are re-entrant +; (no global data). No routines touch X or Y during execution. +; Code generated by macros is relocatable; it contains no JMPs to itself. + +zp_res delay_temp_ ; only written to + +; Delays n clocks, from 2 to 16777215 +; Preserved: A, X, Y, flags +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + delay_ (n) +.endmacro + + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: A, X, Y, flags +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + +; Delays approximately n milliseconds (1/1000 second), +; without caring whether it's NTSC or PAL. +; n can range from 0 to 1100. +; Preserved: A, X, Y, flags +.macro delay_msec_approx n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*1726190+500)/1000 +.endmacro + + +.align 64 + +; Delays A clocks + overhead +; Preserved: X, Y +; Time: A+25 clocks (including JSR) +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 clocks + overhead +; Preserved: X, Y +; Time: A*256+16 clocks (including JSR) +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_11_clocks_: +: pha + lda #256-19-22 + jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536 clocks + overhead +; Preserved: X, Y +; Time: A*65536+16 clocks (including JSR) +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_11_clocks_: +: pha + lda #256-19-22-13 + jsr delay_a_25_clocks + lda #255 + jsr delay_256a_11_clocks_ + pla + clc + adc #-1 + bne :- + rts + +max_short_delay = 41 + + ; delay_short_ macro jumps into these + .res (max_short_delay-12)/2,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_short_ n + .if n < 0 .or n = 1 .or n > max_short_delay + .error "Internal delay error" + .endif + .if n = 0 + ; nothing + .elseif n = 2 + nop + .elseif n = 3 + sta 65536+17 + lda #^(n - 15) + jsr delay_65536a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (((n - 15) & $FFFF) + 2) + .elseif n > 255+27 + lda #>(n - 15) + jsr delay_256a_11_clocks_ + ; +2 ensures remaining clocks is never 1 + delay_nosave_ (<(n - 15) + 2) + .elseif n >= 27 + lda #<(n - 27) + jsr delay_a_25_clocks + .else + delay_short_ n + .endif +.endmacro + +.macro delay_ n + .if n > max_short_delay + php + pha + delay_nosave_ (n - 14) + pla + plp + .else + delay_short_ n + .endif +.endmacro + diff --git a/ppu_vbl_nmi/source/common/macros.inc b/ppu_vbl_nmi/source/common/macros.inc new file mode 100644 index 0000000..dc968ad --- /dev/null +++ b/ppu_vbl_nmi/source/common/macros.inc @@ -0,0 +1,321 @@ +BLARGG_MACROS_INCLUDED = 1 + +; Allows extra error checking with modified version +; of ca65. Otherwise acts like a constant of 0. +ADDR = 0 + +; Switches to Segment and places Line there. +; Line can be an .align directive, .res, .byte, etc. +; Examples: +; seg_data BSS, .align 256 +; seg_data RODATA, {message: .byte "Test",0} +.macro seg_data Segment, Line + .pushseg + .segment .string(Segment) + Line + .popseg +.endmacro + +; Reserves Size bytes in Segment for Name. +; If Size is omitted, reserves one byte. +.macro seg_res Segment, Name, Size + .ifblank Size + seg_data Segment, Name: .res 1 + .else + seg_data Segment, Name: .res Size + .endif +.endmacro + +; Shortcuts for zeropage, bss, and stack +.define zp_res seg_res ZEROPAGE, +.define nv_res seg_res NVRAM, +.define bss_res seg_res BSS, +.define sp_res seg_res STACK, +.define zp_byte zp_res + +; Copies byte from Src to Addr. If Src begins with #, +; it sets Addr to the immediate value. +; Out: A = byte copied +; Preserved: X, Y +.macro mov Addr, Src + lda Src + sta Addr +.endmacro + +; Copies word from Src to Addr. If Src begins with #, +; it sets Addr the immediate value. +; Out: A = high byte of word +; Preserved: X, Y +.macro movw Addr, Src + .if .match( .left( 1, {Src} ), # ) + lda #<(.right( .tcount( {Src} )-1, {Src} )) + sta Addr + lda #>(.right( .tcount( {Src} )-1, {Src} )) + sta 1+(Addr) + .else + lda Src + sta Addr + lda 1+(Src) + sta 1+(Addr) + .endif +.endmacro + +; Increments 16-bit value at Addr. +; Out: EQ/NE based on resulting 16-bit value +; Preserved: A, X, Y +.macro incw Addr + .local @Skip + inc Addr + bne @Skip + inc 1+(Addr) +@Skip: +.endmacro + +; Adds Src to word at Addr. +; Out: A = high byte of result, carry set appropriately +; Preserved: X, Y +.macro addw Addr, Src + .if .match( .left( 1, {Src} ), # ) + addw_ Addr,(.right( .tcount( {Src} )-1, {Src} )) + .else + lda Addr + clc + adc Src + sta Addr + + lda 1+(Addr) + adc 1+(Src) + sta 1+(Addr) + .endif +.endmacro +.macro addw_ Addr, Imm + lda Addr + clc + adc #> 8) <> 0 + lda 1+(Addr) + adc #>Imm + sta 1+(Addr) + ;.else + ; .local @Skip + ; bcc @Skip + ; inc 1+(Addr) + ;@Skip: + ;.endif +.endmacro + +; Splits list of words into tables of low and high bytes +; Example: split_words foo, {$1234, $5678} +; expands to: +; foo_l: $34, $78 +; foo_h: $12, $56 +; foo_count = 2 +.macro split_words Label, Words + .ident (.concat (.string(Label), "_l")): .lobytes Words + .ident (.concat (.string(Label), "_h")): .hibytes Words + .ident (.concat (.string(Label), "_count")) = * - .ident (.concat (.string(Label), "_h")) +.endmacro + +.macro SELECT Bool, True, False, Extra + .ifndef Bool + False Extra + .elseif Bool <> 0 + True Extra + .else + False Extra + .endif +.endmacro + +.macro DEFAULT Name, Value + .ifndef Name + Name = Value + .endif +.endmacro + +.ifp02 + ; 6502 doesn't define these alternate names + .define blt bcc + .define bge bcs +.endif +.define jlt jcc +.define jge jcs + +; Jxx Target = Bxx Target, except it can go farther than +; 128 bytes. Implemented via branch around a JMP. + +; Don't use ca65's longbranch, because they fail for @labels +;.macpack longbranch + +.macro jeq Target + bne *+5 + jmp Target +.endmacro + +.macro jne Target + beq *+5 + jmp Target +.endmacro + +.macro jmi Target + bpl *+5 + jmp Target +.endmacro + +.macro jpl Target + bmi *+5 + jmp Target +.endmacro + +.macro jcs Target + bcc *+5 + jmp Target +.endmacro + +.macro jcc Target + bcs *+5 + jmp Target +.endmacro + +.macro jvs Target + bvc *+5 + jmp Target +.endmacro + +.macro jvc Target + bvs *+5 + jmp Target +.endmacro + + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data RODATA,{Addr: data} +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + .local @for_loop + lda #start +@for_loop: + pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne @for_loop +.endmacro + +; Calls routine n times. The value of A in the routine +; counts from 0 to n-1. +.macro loop_n_times routine,n + for_loop routine,0,n-1,+1 +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + .local @for_loop_skip + .local @for_loop + ldy #>(start) + lda #<(start) +@for_loop: + tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc @for_loop_skip + iny +.else + bcs @for_loop_skip + dey +.endif +@for_loop_skip: + cmp #<((end)+(step)) + bne @for_loop + cpy #>((end)+(step)) + bne @for_loop +.endmacro + +; Stores byte at addr +; Preserved: X, Y +.macro setb addr, byte + lda #byte + sta addr +.endmacro + +; Stores word at addr +; Preserved: X, Y +.macro setw addr, word + lda #<(word) + sta addr + lda #>(word) + sta addr+1 +.endmacro + +; Loads XY with 16-bit immediate or value at address +.macro ldxy Arg + .if .match( .left( 1, {Arg} ), # ) + ldy #<(.right( .tcount( {Arg} )-1, {Arg} )) + ldx #>(.right( .tcount( {Arg} )-1, {Arg} )) + .else + ldy (Arg) + ldx (Arg)+1 + .endif +.endmacro + +; Increments XY as 16-bit register, in CONSTANT time. +; Z flag set based on entire result. +; Preserved: A +; Time: 7 clocks +.macro inxy + iny ; 2 + beq *+4 ; 3 + ; -1 + bne *+3 ; 3 + ; -1 + inx ; 2 +.endmacro + +; Negates A and adds it to operand +.macro subaf Operand + eor #$FF + sec + adc Operand +.endmacro + +; Initializes CPU registers to reasonable values +; Preserved: A, Y +.macro init_cpu_regs + sei + cld ; unnecessary on NES, but might help on clone + ldx #$FF + txs + .ifndef BUILD_NSF + inx + stx PPUCTRL + .endif +.endmacro diff --git a/ppu_vbl_nmi/source/common/neshw.inc b/ppu_vbl_nmi/source/common/neshw.inc new file mode 100644 index 0000000..d636a97 --- /dev/null +++ b/ppu_vbl_nmi/source/common/neshw.inc @@ -0,0 +1,56 @@ +; NES I/O locations and masks + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 + +.ifndef REGION_FREE + .ifndef PAL_ONLY + .ifndef NTSC_ONLY + NTSC_ONLY = 1 + .endif + .endif +.else + .ifdef NTSC_ONLY + .error "NTSC_ONLY and REGION_FREE defined" + .endif + .ifdef PAL_ONLY + .error "PAL_ONLY and REGION_FREE defined" + .endif +.endif + +.ifdef NTSC_ONLY + CLOCK_RATE = 1789773 + PPU_FRAMELEN = 29781 +.endif + +.ifdef PAL_ONLY + CLOCK_RATE = 1662607 + PPU_FRAMELEN = 33248 +.endif diff --git a/ppu_vbl_nmi/source/common/ppu.s b/ppu_vbl_nmi/source/common/ppu.s new file mode 100644 index 0000000..4827b3e --- /dev/null +++ b/ppu_vbl_nmi/source/common/ppu.s @@ -0,0 +1,203 @@ +; PPU utilities + +bss_res ppu_not_present + +; Sets PPUADDR to w +; Preserved: X, Y +.macro set_ppuaddr w + bit PPUSTATUS + setb PPUADDR,>w + setb PPUADDR, 1789773 + .error "Currently only supports NTSC" + .endif + delay ((n)*341)/3 +.endmacro + + +; Waits for VBL then disables PPU rendering. +; Preserved: A, X, Y +disable_rendering: + pha + jsr wait_vbl_optional + setb PPUMASK,0 + pla + rts + + +; Fills first nametable with $00 +; Preserved: Y +clear_nametable: + ldx #$20 + bne clear_nametable_ + +clear_nametable2: + ldx #$24 +clear_nametable_: + lda #0 + jsr fill_screen_ + + ; Clear pattern table + ldx #64 +: sta PPUDATA + dex + bne :- + rts + + +; Fills screen with tile A +; Preserved: A, Y +fill_screen: + ldx #$20 + bne fill_screen_ + +; Same as fill_screen, but fills other nametable +fill_screen2: + ldx #$24 +fill_screen_: + stx PPUADDR + ldx #$00 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + rts + + +; Fills palette with $0F +; Preserved: Y +clear_palette: + set_ppuaddr $3F00 + ldx #$20 + lda #$0F +: sta PPUDATA + dex + bne :- + + +; Fills OAM with $FF +; Preserved: Y +clear_oam: + lda #$FF + +; Fills OAM with A +; Preserved: A, Y +fill_oam: + ldx #0 + stx SPRADDR +: sta SPRDATA + dex + bne :- + rts + + +; Initializes wait_vbl_optional. Must be called before +; using it. +.align 32 +init_wait_vbl: + ; Wait for VBL flag to be set, or ~60000 + ; clocks (2 frames) to pass + ldy #24 + ldx #1 + bit PPUSTATUS +: bit PPUSTATUS + bmi @set + dex + bne :- + dey + bpl :- +@set: + ; Be sure flag didn't stay set (in case + ; PPUSTATUS always has high bit set) + tya + ora PPUSTATUS + sta ppu_not_present + rts + + +; Same as wait_vbl, but returns immediately if PPU +; isn't working or doesn't support VBL flag +; Preserved: A, X, Y +.align 16 +wait_vbl_optional: + bit ppu_not_present + bmi :++ + ; FALL THROUGH + +; Clears VBL flag then waits for it to be set. +; Preserved: A, X, Y +wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- +: rts + + +.macro check_ppu_region_ Len + ; Delays since VBL began + jsr wait_vbl_optional ; 10 average + delay Len - 18 - 200 + lda PPUSTATUS ; 4 + bmi @ok ; 2 + delay 200 + ; Next VBL should roughly begin here if it's the + ; one we are detecting + delay 200 + lda PPUSTATUS ; 2 + bpl @ok +.endmacro + +check_ppu_region: + +.ifndef REGION_FREE +.ifdef PAL_ONLY + check_ppu_region_ 29781 + print_str {newline,"Note: This test is meant for PAL NES only.",newline,newline} +.endif + +.ifdef NTSC_ONLY + check_ppu_region_ 33248 + print_str {newline,"Note: This test is meant for NTSC NES only.",newline,newline} +.endif +.endif +@ok: rts + + +; Loads ASCII font into CHR RAM and fills rest with $FF +.macro load_chr_ram + bit PPUSTATUS + setb PPUADDR,0 + setb PPUADDR,0 + + ; Copy ascii_chr to 0 + setb addr,ascii_chr + ldy #0 +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>ascii_chr_end + bne @page + + ; Fill rest + lda #$FF +: sta PPUDATA + iny + bne :- + inx + cpx #$20 + bne :- +.endmacro diff --git a/ppu_vbl_nmi/source/common/print.s b/ppu_vbl_nmi/source/common/print.s new file mode 100644 index 0000000..976fd9c --- /dev/null +++ b/ppu_vbl_nmi/source/common/print.s @@ -0,0 +1,278 @@ +; Prints values in various ways to output, +; including numbers and strings. + +newline = 10 + +zp_byte print_temp_ + +; Prints indicated register to console as two hex +; chars and space +; Preserved: A, X, Y, flags +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: A, X, Y +print_hex: + jsr update_crc + + pha + lsr a + lsr a + lsr a + lsr a + jsr print_nibble_ + pla + + pha + and #$0F + jsr print_nibble_ + pla + rts + +print_nibble_: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints low 4 bits of A as single hex character +; Preserved: A, X, Y +print_nibble: + pha + and #$0F + jsr update_crc + jsr print_nibble_ + pla + rts + + +; Prints character and updates checksum UNLESS +; it's a newline. +; Preserved: A, X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: pha + jsr print_char_ + pla + rts + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str,str2 + jsr print_str_ + .byte str + .ifnblank str2 + .byte str2 + .endif + .byte 0 +.endmacro + + +print_str_: + sta print_temp_ + + pla + sta addr + pla + sta addr+1 + + jsr inc_addr + jsr print_str_addr + + lda print_temp_ + jmp (addr) + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + bne :+ + inc addr+1 +: rts + + +; Prints A as 1-3 digit decimal. +; In: A = MSB +; Preserved: A, X, Y +print_dec: + sta print_temp_ + pha + txa + pha + tya + pha + ldy print_temp_ + lda #0 + sta print_temp_ + tya + jmp :+ + + +; Prints 16-bit AY as 1-5 digit decimal. +; Preserved: A, X, Y +print_ay_dec: + jsr update_crc + sta print_temp_ + pha + txa + pha + tya + pha +: jsr update_crc + + ; Strip leading zeroes + ldx #6 +: dex + cmp @lsb-1,x + lda print_temp_ + sbc @msb-1,x + tya + bcc :- + bcs @non_zero + + ; Print remaining digits + +@more: ; Commit subtraction + iny + sta print_temp_ + pla + + ; Subtract +@digit: sbc @lsb,x + pha + lda print_temp_ + sbc @msb,x + bcs @more + + ; Print digit and undo subtraction + tya + jsr print_char_ + pla + clc + adc @lsb,x +@non_zero: + sec + ldy #'0' + dex + bne @digit + + ora #'0' + jsr print_char_ + + pla + tay + pla + tax + pla + rts + +@lsb: .byte 0,<10,<100,<1000,<10000 +@msb: .byte 0,>10,>100,>1000,>10000 + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + ; Avoids labels since they're not local + ; to macros in ca65. + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/ppu_vbl_nmi/source/common/shell.inc b/ppu_vbl_nmi/source/common/shell.inc new file mode 100644 index 0000000..0f2557c --- /dev/null +++ b/ppu_vbl_nmi/source/common/shell.inc @@ -0,0 +1,32 @@ +; Included at beginning of program + +.ifdef CUSTOM_PREFIX + .include "custom_prefix.s" +.endif + +; Sub-test in a multi-test ROM +.ifdef BUILD_MULTI + .include "build_multi.s" +.else + +; NSF music file +.ifdef BUILD_NSF + .include "build_nsf.s" +.endif + +; Devcart +.ifdef BUILD_DEVCART + .include "build_devcart.s" +.endif + +; NES internal RAM +.ifdef BUILD_NOCART + .include "build_nocart.s" +.endif + +; NES ROM (default) +.ifndef SHELL_INCLUDED + .include "build_rom.s" +.endif + +.endif ; .ifdef BUILD_MULTI diff --git a/ppu_vbl_nmi/source/common/shell.s b/ppu_vbl_nmi/source/common/shell.s new file mode 100644 index 0000000..c5c152f --- /dev/null +++ b/ppu_vbl_nmi/source/common/shell.s @@ -0,0 +1,182 @@ +; Shell that sets up testing framework and calls main + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INCLUDED + .error "shell.s included twice" + .end +.endif +SHELL_INCLUDED = 1 + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E +ptr = addr + +; Move code from $C000 to $E200, to accommodate my devcarts +.ifndef LARGER_ROM_HACK +.segment "CODE" + .res $2200 +.endif + +; Put shell code after user code, so user code is in more +; consistent environment +.segment "CODE2" + + ; Any user code which runs off end might end up here, + ; so catch that mistake. + nop ; in case there was three-byte opcode before this + nop + jmp internal_error + +;**** Common routines **** + +.include "macros.inc" +.include "neshw.inc" +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +;**** Shell core **** + +.ifndef CUSTOM_RESET + reset: + sei + jmp std_reset +.endif + + +; Sets up hardware then runs main +run_shell: + init_cpu_regs + jsr init_shell + set_test $FF + jmp run_main + + +; Initializes shell without affecting current set_test values +init_shell: + jsr clear_ram + jsr init_wait_vbl ; waits for VBL once here, + jsr wait_vbl_optional ; so only need to wait once more + jsr init_text_out + jsr init_testing + jsr init_runtime + jsr console_init + rts + + +; Runs main in consistent PPU/APU environment, then exits +; with code 0 +run_main: + jsr pre_main + jsr main + lda #0 + jmp exit + + +; Sets up environment for main to run in +pre_main: + +.ifndef BUILD_NSF + jsr disable_rendering + setb PPUCTRL,0 + jsr clear_palette + jsr clear_nametable + jsr clear_nametable2 + jsr clear_oam +.endif + + ; Clear APU registers + lda #0 + sta $4015 + ldx #$13 +: sta $4000,x + dex + bpl :- + + ; CPU registers + lda #$34 + pha + lda #0 + tax + tay + jsr wait_vbl_optional + plp + sta SNDMODE + rts + + +.ifndef CUSTOM_EXIT + exit: +.endif + +; Reports result and ends program +std_exit: + sta temp + init_cpu_regs + setb SNDCHN,0 + lda temp + + jsr report_result + pha + jsr check_ppu_region + pla + jmp post_exit + + +; Reports final result code in A +report_result: + jsr :+ + jmp play_byte + +: jsr print_newline + jsr console_show + + ; 0: "" + cmp #1 + bge :+ + rts +: + ; 1: "Failed" + bne :+ + print_str {"Failed",newline} + rts + + ; n: "Failed #n" +: print_str "Failed #" + jsr print_dec + jsr print_newline + rts + + +;**** Other routines **** + +.include "shell_misc.s" + +.ifdef NEED_CONSOLE + .include "console.s" +.else + ; Stubs so code doesn't have to care whether + ; console exists + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.endif + +.ifndef CUSTOM_PRINT + .include "text_out.s" + + print_char_: + jsr write_text_out + jmp console_print + + stop_capture: + rts +.endif diff --git a/ppu_vbl_nmi/source/common/shell_misc.s b/ppu_vbl_nmi/source/common/shell_misc.s new file mode 100644 index 0000000..cf95e5a --- /dev/null +++ b/ppu_vbl_nmi/source/common/shell_misc.s @@ -0,0 +1,226 @@ +; Reports internal error and exits program +internal_error: +assert_failed: + pla + tay + pla + init_cpu_regs + print_str newline,"internal error, PC=" + jsr print_hex + jsr print_y + lda #255 + jmp exit + +.import __NVRAM_LOAD__, __NVRAM_SIZE__ + +.macro fill_ram_ Begin, End + ; Simpler to count from negative size up to 0, + ; and adjust address downward to compensate + ; for initial low byte in Y index + .local Neg_size + Neg_size = (Begin) - (End) + ldxy #(Begin) - 4 + delay (n)/3-1 + .endif + delay 29781*4 + .elseif (n) .MOD 3 = 2 + delay (n)/3 + delay 29781*2 + .else + delay (n)/3 + .endif +.endmacro diff --git a/ppu_vbl_nmi/source/common/testing.s b/ppu_vbl_nmi/source/common/testing.s new file mode 100644 index 0000000..a40d9b7 --- /dev/null +++ b/ppu_vbl_nmi/source/common/testing.s @@ -0,0 +1,105 @@ +; Utilities for writing test ROMs + +; In NVRAM so these can be used before initializing runtime, +; then runtime initialized without clearing them +nv_res test_code ; code of current test +nv_res test_name,2 ; address of name of current test, or 0 of none + + +; Sets current test code and optional name. Also resets +; checksum. +; Preserved: A, X, Y +.macro set_test code,name + pha + lda #code + jsr set_test_ + .ifblank name + setb test_name+1,0 + .else + .local Addr + setw test_name,Addr + seg_data RODATA,{Addr: .byte name,0} + .endif + pla +.endmacro + +set_test_: + sta test_code + jmp reset_crc + + +; Initializes testing module +init_testing = init_crc + + +; Reports that all tests passed +tests_passed: + jsr print_filename + print_str newline,"Passed" + lda #0 + jmp exit + + +; Reports "Done" if set_test has never been used, +; "Passed" if set_test 0 was last used, or +; failure if set_test n was last used. +tests_done: + ldx test_code + jeq tests_passed + inx + bne test_failed + jsr print_filename + print_str newline,"Done" + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + ldx test_code + + ; Treat $FF as 1, in case it wasn't ever set + inx + bne :+ + inx + stx test_code +: + ; If code >= 2, print name + cpx #2-1 ; -1 due to inx above + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: + jsr print_filename + + ; End program + lda test_code + jmp exit + + +; If checksum doesn't match expected, reports failed test. +; Clears checksum afterwards. +; Preserved: A, X, Y +.macro check_crc expected + jsr_with_addr check_crc_,{.dword expected} +.endmacro + +check_crc_: + pha + jsr is_crc_ + bne :+ + jsr reset_crc + pla + rts + +: jsr print_newline + jsr print_crc + jmp test_failed diff --git a/ppu_vbl_nmi/source/common/text_out.s b/ppu_vbl_nmi/source/common/text_out.s new file mode 100644 index 0000000..3a4137f --- /dev/null +++ b/ppu_vbl_nmi/source/common/text_out.s @@ -0,0 +1,61 @@ +; Text output as expanding zero-terminated string at text_out_base + +; The final exit result byte is written here +final_result = $6000 + +; Text output is written here as an expanding +; zero-terminated string +text_out_base = $6004 + +bss_res text_out_temp +zp_res text_out_addr,2 + +init_text_out: + ldx #0 + + ; Put valid data first + setb text_out_base,0 + + lda #$80 + jsr set_final_result + + ; Now fill in signature that tells emulator there's + ; useful data there + setb text_out_base-3,$DE + setb text_out_base-2,$B0 + setb text_out_base-1,$61 + + ldx #>text_out_base + stx text_out_addr+1 + setb text_out_addr,". + +Several routines are available to print values and text to the console. +The first time a line of text is completed, the console initializes the +PPU. Most print routines update a running CRC-32 checksum which can be +checked with check_crc. This allows ALL the output to be checked very +easily. If the checksum doesn't match, it is printed, so during +development the code can be run on a NES to get the correct checksum, +which is then typed into the test code. The checksum is also useful when +comparing different outputs; rather than having to examine all the +output, one need only compare checksums. + +The default is to build an iNES ROM. Defining BUILD_NSF will build as an +NSF. The other build types aren't supported by the included code, due to +their complexity. + + +Macros +------ +Some macros are used to make common operations more convenient, defined +in common/macros.inc. The left is equivalent to the right: + + Macro Equivalent + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + setb addr,byte lda #byte + sta addr + + setw addr,word lda #word + sta addr+1 + + ldxy #value ldx #>value + ldy # diff --git a/read_joy3/count_errors.nes b/read_joy3/count_errors.nes new file mode 100644 index 0000000..cd48591 Binary files /dev/null and b/read_joy3/count_errors.nes differ diff --git a/read_joy3/count_errors_fast.nes b/read_joy3/count_errors_fast.nes new file mode 100644 index 0000000..2838ea5 Binary files /dev/null and b/read_joy3/count_errors_fast.nes differ diff --git a/read_joy3/source/common/ascii.chr b/read_joy3/source/common/ascii.chr new file mode 100644 index 0000000..0687d4e Binary files /dev/null and b/read_joy3/source/common/ascii.chr differ diff --git a/read_joy3/source/common/console.s b/read_joy3/source/common/console.s new file mode 100644 index 0000000..9d8f559 --- /dev/null +++ b/read_joy3/source/common/console.s @@ -0,0 +1,196 @@ +; Scrolling text console with line wrapping, 30x30 characters. +; Buffers lines for speed. Will work even if PPU doesn't +; support scrolling (until text reaches bottom). +; ** ASCII font must already be in CHR, and mirroring +; must be vertical or single-screen. + +; Number of characters of margin on left and right, to avoid +; text getting cut off by common TVs +console_margin = 1 + +console_buf_size = 32 +console_width = console_buf_size - (console_margin*2) + +zp_byte console_pos +zp_byte console_scroll +zp_byte console_temp +bss_res console_buf,console_buf_size + + +; Waits for beginning of VBL +; Preserved: A, X, Y +console_wait_vbl: + bit PPUSTATUS +: bit PPUSTATUS + bpl :- + rts + + +; Initializes console +console_init: + jsr console_hide + lda #0 + sta PPUCTRL + + ; Load palette + lda #$3F + sta PPUADDR + lda #0 + sta PPUADDR + lda #$0F ; black background + sta PPUDATA + lda #$30 ; white text + sta PPUDATA + sta PPUDATA + sta PPUDATA + + ; Fill nametable with spaces + lda #$20 + sta PPUADDR + ldx #0 + stx PPUADDR + ldx #240 +: sta PPUDATA + sta PPUDATA + sta PPUDATA + sta PPUDATA + dex + bne :- + + ; Clear attributes + lda #0 + ldx #$40 +: sta PPUDATA + dex + bne :- + + ; In case PPU doesn't support scrolling, start a + ; couple of lines down + lda #8 + sta console_scroll + jsr console_scroll_up_ + jmp console_show + + +; Shows console display +; Preserved: X, Y +console_show: + pha + jsr console_wait_vbl + lda #PPUMASK_BG0 + sta PPUMASK + jmp console_apply_scroll_ + + +; Hides console display and makes screen black +; Preserved: X, Y +console_hide: + jsr console_wait_vbl + lda #0 + sta PPUMASK + rts + + +; Prints char A to console. Will not appear until +; a newline or flush occurs. +; Preserved: A, X, Y +console_print: + cmp #10 + beq console_newline + + ; Write to buffer + stx console_temp + ldx console_pos + sta console_buf+console_margin,x + ldx console_temp + + ; Update pos and print newline if buf full + dec console_pos + bmi console_newline ; reached end of line + + rts + + +; Displays current line and starts new one +; Preserved: A, X, Y +console_newline: + pha + jsr console_wait_vbl + jsr console_flush_ + jsr console_scroll_up_ + jsr console_flush_ + jmp console_apply_scroll_ + + +console_get_scroll_: + ; A = (console_scroll+8)%240 + lda console_scroll + cmp #240-8 + bcc :+ + adc #16-1;+1 for set carry +: adc #8 + rts + + +console_scroll_up_: + ; Scroll up 8 pixels + jsr console_get_scroll_ + sta console_scroll + + stx console_temp + + ; Start new clear line + lda #' ' + ldx #console_buf_size-1 +: sta console_buf,x + dex + bpl :- + ldx #console_width-1 + stx console_pos + + ldx console_temp + rts + + +; Displays current line's contents without scrolling. +; Preserved: A, X, Y +console_flush: + pha + jsr console_wait_vbl + jsr console_flush_ +console_apply_scroll_: + lda #0 + sta PPUADDR + sta PPUADDR + + sta PPUSCROLL + jsr console_get_scroll_ + sta PPUSCROLL + + pla + rts + +console_flush_: + ; Address line in nametable + lda console_scroll + sta console_temp + lda #$08 + asl console_temp + rol a + asl console_temp + rol a + sta PPUADDR + lda console_temp + sta PPUADDR + + ; Copy line + stx console_temp + ldx #console_buf_size-1 +: lda console_buf,x + sta PPUDATA + dex + bpl :- + ldx console_temp + + rts + diff --git a/read_joy3/source/common/crc.s b/read_joy3/source/common/crc.s new file mode 100644 index 0000000..d4c431a --- /dev/null +++ b/read_joy3/source/common/crc.s @@ -0,0 +1,85 @@ +; CRC-32 checksum calculation + +zp_res checksum,4 +zp_byte checksum_temp +zp_byte checksum_off_ + +; Turns CRC updating on/off. Allows nesting. +; Preserved: A, X, Y +crc_off: + dec checksum_off_ + rts + +crc_on: inc checksum_off_ + beq :+ + jpl internal_error ; catch unbalanced crc calls +: rts + + +; Initializes checksum module. Might initialize tables +; in the future. +init_crc: + ; FALL THROUGH +; Clears checksum and turns it on +; Preserved: X, Y +reset_crc: + lda #0 + sta checksum_off_ + lda #$FF + sta checksum + sta checksum + 1 + sta checksum + 2 + sta checksum + 3 + rts + + +; Updates checksum with byte in A (unless disabled via crc_off) +; Preserved: A, X, Y +; Time: 357 clocks average +update_crc: + bit checksum_off_ + bmi update_crc_off +update_crc_: + pha + stx checksum_temp + eor checksum + ldx #8 +@bit: lsr checksum+3 + ror checksum+2 + ror checksum+1 + ror a + bcc :+ + sta checksum + lda checksum+3 + eor #$ED + sta checksum+3 + lda checksum+2 + eor #$B8 + sta checksum+2 + lda checksum+1 + eor #$83 + sta checksum+1 + lda checksum + eor #$20 +: dex + bne @bit + sta checksum + ldx checksum_temp + pla +update_crc_off: + rts + + +; Prints checksum as 8-character hex value +print_crc: + jsr crc_off + + ; Print complement + ldx #3 +: lda checksum,x + eor #$FF + jsr print_hex + dex + bpl :- + + jmp crc_on diff --git a/read_joy3/source/common/delay.s b/read_joy3/source/common/delay.s new file mode 100644 index 0000000..ffccc5b --- /dev/null +++ b/read_joy3/source/common/delay.s @@ -0,0 +1,144 @@ +; Delays in clocks and milliseconds. All routines re-entrant +; (no global data). + +; Delays n milliseconds (1/1000 second) +; n can range from 0 to 1100. +; Preserved: X, Y +.macro delay_msec n + .if (n) < 0 .or (n) > 1100 + .error "time out of range" + .endif + delay ((n)*CLOCK_RATE+500)/1000 +.endmacro + + +; Delays n microseconds (1/1000000 second). +; n can range from 0 to 100000. +; Preserved: X, Y +.macro delay_usec n + .if (n) < 0 .or (n) > 100000 + .error "time out of range" + .endif + delay ((n)*((CLOCK_RATE+50)/100)+5000)/10000 +.endmacro + + +; Delays n clocks, from 2 to 16777215 +; Preserved: X, Y +.macro delay n + .if (n) < 0 .or (n) = 1 .or (n) > 16777215 + .error "Delay out of range" + .endif + .if (n) < 14 .and (n) <> 12 + delay_inline (n) + .elseif (n) < 27 + delay_unrolled (n) + .elseif <(n) = 0 + delay_256 (n) + .else + lda #<((n)-27) + jsr delay_a_25_clocks + delay_256 ((n)-27) + .endif +.endmacro + + +; Delays A+25 clocks (including JSR) +; Preserved: X, Y +.align 64 +: sbc #7 ; carry set by CMP +delay_a_25_clocks: + cmp #7 + bcs :- ; do multiples of 7 + lsr a ; bit 0 + bcs :+ +: ; A=clocks/2, either 0,1,2,3 + beq @zero ; 0: 5 + lsr a + beq :+ ; 1: 7 + bcc :+ ; 2: 9 +@zero: bne :+ ; 3: 11 +: rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256+16 clocks (including JSR) +; Preserved: X, Y +delay_256a_16_clocks: + cmp #0 + bne :+ + rts +delay_256a_clocks_: + pha + lda #256-19-22-16 + bne @first ; always branches +: pha + lda #256-19-22 +@first: jsr delay_a_25_clocks + pla + clc + adc #-1 + bne :- + rts + + +; Delays A*65536+16 clocks (including JSR) +; Preserved: X, Y +delay_65536a_16_clocks: + cmp #0 + bne :+ + rts +delay_65536a_clocks_: + pha + lda #256-19-22-16 + bne @first +: pha + lda #256-19-22 +@first: jsr delay_a_25_clocks + lda #255 + jsr delay_256a_clocks_ + pla + clc + adc #-1 + bne :- + rts + +.macro delay_inline n + .if n = 7 .or n >= 9 + pha + pla + delay_inline (n-7) + .elseif n >= 3 .and n & 1 + lda <0 + delay_inline (n-3) + .elseif n >= 2 + nop + delay_inline (n-2) + .elseif n > 0 + .error "delay_short internal error" + .endif +.endmacro + +.macro delay_unrolled n + .if n & 1 + lda <0 + jsr delay_unrolled_-((n-15)/2) + .else + jsr delay_unrolled_-((n-12)/2) + .endif +.endmacro + + .res 7,$EA ; NOP +delay_unrolled_: + rts + +.macro delay_256 n + .if >n + lda #>n + jsr delay_256a_clocks_ + .endif + .if ^n + lda #^n + jsr delay_65536a_clocks_ + .endif +.endmacro + diff --git a/read_joy3/source/common/macros.inc b/read_joy3/source/common/macros.inc new file mode 100644 index 0000000..2a5008c --- /dev/null +++ b/read_joy3/source/common/macros.inc @@ -0,0 +1,96 @@ +; jxx equivalents to bxx +.macpack longbranch + +; blt, bge equivalents to bcc, bcs +.define blt bcc +.define bge bcs +.define jge jcs +.define jlt jcc + +; Puts data in another segment +.macro seg_data seg,data + .pushseg + .segment seg + data + .popseg +.endmacro + +; Reserves size bytes in zeropage/bss for name +.macro zp_res name,size + seg_data "ZEROPAGE",{name: .res size} +.endmacro + +.macro bss_res name,size + seg_data "BSS",{name: .res size} +.endmacro + +; Reserves one byte in zeropage for name (very common) +.macro zp_byte name + seg_data "ZEROPAGE",{name: .res 1} +.endmacro + +; Passes constant data to routine in addr +; Preserved: A, X, Y +.macro jsr_with_addr routine,data + .local Addr + pha + lda #Addr + sta addr+1 + pla + jsr routine + seg_data "STRINGS",{Addr: data} +.endmacro + +; If name isn't yet defined, defines it with value +.macro SET_DEFAULT name,value + .ifndef name + name=value + .endif +.endmacro + +; Calls routine multiple times, with A having the +; value 'start' the first time, 'start+step' the +; second time, up to 'end' for the last time. +.macro for_loop routine,start,end,step + lda #start +: pha + jsr routine + pla + clc + adc #step + cmp #<((end)+(step)) + bne :- +.endmacro + +; Same as for_loop, except uses 16-bit value in YX. +; -256 <= step <= 255 +.macro for_loop16 routine,start,end,step +.if (step) < -256 || (step) > 255 + .error "Step must be within -256 to 255" +.endif + ldy #>(start) + lda #<(start) +: tax + pha + tya + pha + jsr routine + pla + tay + pla + clc + adc #step +.if (step) > 0 + bcc :+ + iny +.else + bcs :+ + dey +.endif +: cmp #<((end)+(step)) + bne :-- + cpy #>((end)+(step)) + bne :-- +.endmacro diff --git a/read_joy3/source/common/nes.inc b/read_joy3/source/common/nes.inc new file mode 100644 index 0000000..aff4389 --- /dev/null +++ b/read_joy3/source/common/nes.inc @@ -0,0 +1,35 @@ +; NES I/O locations and masks + +; Clocks per second +CLOCK_RATE = 1789773 +;CLOCK_RATE = 1662607 + +.ifndef BUILD_NSF + +; PPU +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +SPRADDR = $2003 +SPRDATA = $2004 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 +SPRDMA = $4014 + +PPUCTRL_NMI = $80 +PPUMASK_BG0 = $0A +PPUCTRL_8X8 = $00 +PPUCTRL_8X16 = $20 +PPUMASK_SPR = $14 +PPUMASK_BG0CLIP = $08 + +.endif + +; APU +SNDCHN = $4015 +JOY1 = $4016 +JOY2 = $4017 +SNDMODE = $4017 + +SNDMODE_NOIRQ = $40 diff --git a/read_joy3/source/common/print.s b/read_joy3/source/common/print.s new file mode 100644 index 0000000..126186b --- /dev/null +++ b/read_joy3/source/common/print.s @@ -0,0 +1,191 @@ +; Prints values in various ways to output, including numbers and strings. + +newline = 10 + +; Prints indicated register to console as two hex chars and space +; Preserved: A, X, Y, P +print_a: + php + pha +print_reg_: + jsr print_hex + lda #' ' + jsr print_char_ + pla + plp + rts + +print_x: + php + pha + txa + jmp print_reg_ + +print_y: + php + pha + tya + jmp print_reg_ + +print_p: + php + pha + php + pla + jmp print_reg_ + +print_s: + php + pha + txa + tsx + inx + inx + inx + inx + jsr print_x + tax + pla + plp + rts + + +; Prints A as two hex characters, NO space after +; Preserved: X, Y +print_hex: + jsr update_crc + + ; Print high nibble + pha + lsr a + lsr a + lsr a + lsr a + jsr @nibble + pla + + ; Print low nibble + and #$0F +@nibble: + cmp #10 + blt @digit + adc #6;+1 since carry is set +@digit: adc #'0' + jmp print_char_ + + +; Prints character and updates checksum UNLESS it's a newline. +; Preserved: X, Y +print_char: + cmp #newline + beq :+ + jsr update_crc +: jmp print_char_ + + +; Prints space. Does NOT update checksum. +; Preserved: A, X, Y +print_space: + pha + lda #' ' + jsr print_char_ + pla + rts + + +; Advances to next line. Does NOT update checksum. +; Preserved: A, X, Y +print_newline: + pha + lda #newline + jsr print_char_ + pla + rts + + +; Prints string +; Preserved: A, X, Y +.macro print_str str + jsr_with_addr print_str_addr,{.byte str,0} +.endmacro + + +; Prints string at addr and leaves addr pointing to +; byte AFTER zero terminator. +; Preserved: A, X, Y +print_str_addr: + pha + tya + pha + + ldy #0 + beq :+ ; always taken +@loop: jsr print_char + jsr inc_addr +: lda (addr),y + bne @loop + + pla + tay + pla + ; FALL THROUGH + +; Increments 16-bit value in addr. +; Preserved: A, X, Y +inc_addr: + inc addr + beq :+ + rts +: inc addr+1 + rts + + +; Prints A as 1-3 digit decimal value, NO space after. +; Preserved: Y +print_dec: + ; Hundreds + cmp #100 + blt @tens + ldx #'0' +: sbc #100 + inx + cmp #100 + bge :- + jsr @digit + + ; Tens +@tens: cmp #10 + blt @ones + ldx #'0' +: sbc #10 + inx + cmp #10 + bge :- + jsr @digit + + ; Ones +@ones: ora #'0' + jmp print_char + + ; Print a single digit +@digit: pha + txa + jsr print_char + pla + rts + + +; Prints one of two characters based on condition. +; SEC; print_cc bcs,'C','-' prints 'C'. +; Preserved: A, X, Y, flags +.macro print_cc cond,yes,no + php + pha + cond *+6 + lda #no + bne *+4 + lda #yes + jsr print_char + pla + plp +.endmacro diff --git a/read_joy3/source/common/serial.s b/read_joy3/source/common/serial.s new file mode 100644 index 0000000..59fbebd --- /dev/null +++ b/read_joy3/source/common/serial.s @@ -0,0 +1,43 @@ +; Serial output at 57600 bits/sec on controller port 2 +; +; Uses stack and register A only, and doesn't mind page crossing +; (uses subroutines instead of loops). + +; Initializes serial. If this isn't done, first byte sent to +; PC might be corrupt. +; Preserved: X, Y +serial_init: + sec + lda #$FF + bne serial_write_ ; always branches + + +; Writes byte A to serial +; Preserved: X, Y +serial_write: + clc +serial_write_: + jsr @bit ; start + nop ; TODO: why the extra delay? + jsr @first ; bit 0 + jsr @bit ; bit 1 + jsr @bit ; bit 2 + jsr @bit ; bit 3 + jsr @bit ; bit 4 + jsr @bit ; bit 5 + jsr @bit ; bit 6 + jsr @bit ; bit 7 + sec ; 2 stop bit +@first: nop ; 4 + nop +@bit: ; 6 jsr + pha ; 3 + rol a ; 2 + and #1 ; 2 + sta JOY1 ; 4 + pla ; 4 + ror a ; 2 +.if CLOCK_RATE = 1789773 + nop ; 2 +.endif + rts ; 6 diff --git a/read_joy3/source/common/shell.inc b/read_joy3/source/common/shell.inc new file mode 100644 index 0000000..61fe108 --- /dev/null +++ b/read_joy3/source/common/shell.inc @@ -0,0 +1,434 @@ +; Program shell with text console. Included before user code. + +; Detect inclusion loops (otherwise ca65 goes crazy) +.ifdef SHELL_INC + .error "File included twice" + .end +.endif +SHELL_INC = 1 + + +; ******************************************* Prefix + +.segment "CODE2" + +; Temporary variables that ANY routine might modify, so +; only use them between routine calls. +temp = <$A +temp2 = <$B +temp3 = <$C +addr = <$E + +; RAM that isn't cleared by init routine +nv_ram = $7F0 + +; Macros and constants +.include "macros.inc" +.include "nes.inc" + +; Interrupt handlers are wrapped with these +.define BEGIN_NMI nmi: +.define END_NMI + +.define BEGIN_IRQ irq: +.define END_IRQ + +; Set undefined flags to 0, allowing simpler .if statements +SET_DEFAULT BUILD_NSF,0 +SET_DEFAULT BUILD_MULTI,0 +SET_DEFAULT BUILD_DEVCART,0 + +; Number of clocks devcart takes to jump to user reset +SET_DEFAULT DEVCART_DELAY,0 + + +; ******************************************* Libraries + +.include "delay.s" +.include "print.s" +.include "crc.s" +.include "testing.s" + +.if !BUILD_MULTI + .include "serial.s" +.endif + +; Sets up environment, calls main, then exits with code 0 +run_main: + ; Initialize libraries + jsr init_crc + + ; Establish consistent environment before + ; running main + jsr wait_vbl + lda #PPUMASK_BG0 + sta PPUMASK + delay 2370+24 + lda #$34 + pha + lda #0 + sta SNDMODE + tax + tay + clc + clv + plp + + jsr main + + ; Default to silent exit if main returns + lda #0 + ; FALL THROUGH + +; Exits program and prints result code if non-zero +exit: + ; Reset stack + ldx #$FF + txs + + ; Disable interrupts + sei + pha + jsr nmi_off + pla + + jmp exit_ + +; Reports internal error and exits program +internal_error: + print_str "Internal error" + lda #1 + jmp exit + + +.if BUILD_NSF || BUILD_MULTI || BUILD_DEVCART + console_init: + console_show: + console_hide: + console_print: + console_flush: + rts +.else + .include "console.s" +.endif + + +; ******************************************* Single Test + +.if !BUILD_MULTI + +print_char_: + jsr console_print + jmp serial_write + +; Reset handler +.ifndef CUSTOM_RESET + reset = std_reset +.endif + +.macro init_nes + sei + cld + ldx #$FF + txs + + .if !BUILD_NSF + ; Init PPU + lda #0 + sta PPUCTRL + sta PPUMASK + .endif + + ; Clear RAM + lda #0 + ldx #7 ; last page + ldy #@ascii +@page: + stx addr+1 +: lda (addr),y + sta PPUDATA + iny + bne :- + inx + cpx #>@ascii_end + bne @page + +.pushseg +.rodata +@ascii: + .incbin "ascii.chr" +@ascii_end: +.popseg +.endif + + jsr console_init + jsr serial_init + jmp run_main + + +; Exit handler +exit_: + ; 0: "" + cmp #1 + jlt exit2 + + ; 1: "Failed" + bne :+ + print_str {newline,"Failed"} + jmp exit2 + + ; n: "Error n" +: pha + print_str {newline,"Error "} + jsr print_dec + pla + +exit2: +.if !BUILD_DEVCART + ; Be sure output is visible + pha + print_str {newline,newline,newline} + jsr console_show + pla + + ; Report audibly as well + jsr beep_bits +.else + ; Tell host to stop capturing serial + lda #$1A + jsr serial_write + delay_msec 400 +.endif + + ; Clear nv_ram + lda #0 + ldx #filename + sta addr+1 + jsr print_str_addr + jsr print_newline + rts + +.pushseg +.segment "STRINGS" +; Filename terminated with zero byte, or just zero byte +; if filename isn't available. +filename: + .incbin "ram:rom.nes" + .byte 0 +.popseg +.else +print_filename: + rts + +filename: + .byte 0 +.endif + + +; User code goes in main code segment +.segment "CODE" + nop diff --git a/read_joy3/source/common/sync_dmc.s b/read_joy3/source/common/sync_dmc.s new file mode 100644 index 0000000..1b65873 --- /dev/null +++ b/read_joy3/source/common/sync_dmc.s @@ -0,0 +1,49 @@ +; Synchronizes precisely with DMC timer. Leaves DMC at +; maximum rate, length 0 (1 byte). To avoid stopping +; other sound channels when writing to SNDCHN, it +; writes $1F rather than $10. +; Preserved: A, X, Y +; Time: ~680-3800 clocks +.align 64 +sync_dmc: + pha + + ; Setup + lda #0 + sta $4013 + lda #$0F + sta $4010 + sta SNDCHN + + ; Start twice (first will clear immediately) + lda #$1F + sta SNDCHN + nop + sta SNDCHN + + ; Coarse synchronize + lda #$10 +: bit SNDCHN + bne :- + + ; DO NOT write to memory. It affects timing. + + bit <0 ; 3 fine-tune: 2=OK 3=OK 4=fail + nop ; 2 + lda #57 ; 400 delay for first iter + bne :+ ; 3 + + ; Fine synchronize. 433 clocks per iter. +@sync: lda #59 ; 414 delay +: sec + sbc #1 + bne :- + ; 4 DMC wait-states + lda #$1F ; 2 + sta SNDCHN ; 4 + lda #$10 ; 2 + bit SNDCHN ; 4 + bne @sync ; 3 + + pla + rts diff --git a/read_joy3/source/common/testing.s b/read_joy3/source/common/testing.s new file mode 100644 index 0000000..8bb0741 --- /dev/null +++ b/read_joy3/source/common/testing.s @@ -0,0 +1,162 @@ +; Utilities for writing test ROMs + +zp_res test_code,1 ; code of current test +zp_res test_name,2 ; address of name of current test, or 0 of none + + +; Reports that all tests passed +tests_passed: +.if !BUILD_MULTI + jsr print_filename + print_str "Passed" +.endif + lda #0 + jmp exit + + +; Reports that the current test failed. Prints code and +; name last set with set_test, or just "Failed" if none +; have been set yet. +test_failed: + lda test_code + + ; Treat 0 as 1, in case it wasn't ever set + bne :+ + lda #1 + sta test_code +: + ; If code >= 2, print name + cmp #2 + blt :+ + lda test_name+1 + beq :+ + jsr print_newline + sta addr+1 + lda test_name + sta addr + jsr print_str_addr + jsr print_newline +: +.if !BUILD_MULTI + jsr print_filename +.endif + ; End program + lda test_code + jmp exit + + +; Sets current test code and optional name. Also resets +; checksum. +.macro set_test code,name + pha + lda #code + jsr set_test_ + .local Addr + lda #Addr + sta iter + ldx #iter + ldx #". + +Several routines are available to print values and text to the console. +Most update a running CRC-32 checksum which can be checked with +check_crc, allowing ALL the output to be checked very easily. If the +checksum doesn't match, it is printed, so you can run the code on a NES +and paste the correct checksum into your code. + +The default is to build an iNES ROM, with other build types that I +haven't documented (devcart, sub-test of a multi-test ROM, NSF music +file). My nes.cfg file puts the code at $E000 since my devcart requires +it, and I don't want the normal ROM to differ in any way from what I've +tested. + +Library routines are organized by function into several files, each with +short documentation. Each routine may also optionally list registers +which are preserved, rather than those which are modified (trashed) as +is more commonly done. This is because it's best for the caller to +assume that ALL registers are NOT preserved unless noted. + +Some macros are used to make common operations more convenient. The left +is equivalent to the right: + + Macro Equivalent + ------------------------------------- + blt bcc + + bge bcs + + jne label beq skip + jmp label + skip: + etc. + + zp_byte name .zeropage + name: .res 1 + .code + + zp_res name,n .zeropage + name: .res n + .code + + bss_res name,n .bss + name: .res n + .code + + for_loop routine,begin,end,step + calls a routine with A set to successive values diff --git a/read_joy3/source/test_buttons.s b/read_joy3/source/test_buttons.s new file mode 100644 index 0000000..7e4c89e --- /dev/null +++ b/read_joy3/source/test_buttons.s @@ -0,0 +1,62 @@ +; Tests normal read_joy operation for each button. +; Runs DMC while testing, to be sure it doesn't +; affect anything. + +.include "shell.inc" +.include "read_joy.inc" + +zp_byte button + +dmc_rate = 15 ; 0 to 15 + +main: ; Start DMC + lda #$40+dmc_rate + sta $4010 + lda #$FF + sta $4012 + sta $4013 + lda #0 + sta $4015 + lda #$10 + sta $4015 + + print_str {"Press indicated buttons",newline,newline} + + ; First string + lda #names + sta addr+1 + + lda #$01 + sta button +@loop: jsr print_str_addr + jsr print_newline + jsr print_newline + + ; Wait for button press +: jsr read_joy + beq :- + + ; Be sure nothing but correct button + cmp button + jne test_failed + + ; Wait for button to be released +: jsr read_joy + bne :- + delay_msec 50 ; debounce + + asl button + bcc @loop + + jmp tests_passed + +names: .byte "A",0 + .byte "B",0 + .byte "Select",0 + .byte "Start",0 + .byte "Up",0 + .byte "Down",0 + .byte "Left",0 + .byte "Right",0 diff --git a/read_joy3/source/thorough_test.s b/read_joy3/source/thorough_test.s new file mode 100644 index 0000000..2cfa7e5 --- /dev/null +++ b/read_joy3/source/thorough_test.s @@ -0,0 +1,67 @@ +; Thoroughly tests routine with DMC DMA occurring +; at every possible timing. Ensures that no more +; than one corruption can occur out of the four +; the routine makes. + +iter = 1000 + +.include "shell.inc" +.include "sync_dmc.s" +.include "read_joy.inc" + +log_size = 4 +zp_res log,log_size + +main: for_loop16 test,0,iter,+1 + jmp tests_passed + +test: jsr sync_dmc + + ; Start DMC + lda #$4F + sta $4010 + lda #$10 + sta $4015 + + ; Delay + txa + jsr delay_a_25_clocks + tya + jsr delay_256a_16_clocks + + ; DMC DMA occurs during this code + jsr read_joy + pha + pha + + ; Recover second controller read + ; from below stack pointer + tsx + dex + txs + pla + + ; Ensure that no more than one reading + ; was corrupted by DMC DMA. + ldx #0 + cmp #0 + beq :+ + inx +: lda mapdata + sta mapptrhi + lda #$00 ;Set initial speed + sta scrollsx + sta scrollsy + sta controldelay + +mainloop: jsr waitvblank + jsr erasemagicchar + jsr scrollaction + jsr drawmagicchar + jsr setgamescreen + jsr setsprites + + lda ntscdelay ;Handle NTSC delay + sec + sbc ntscflag + bcs ml_nontscdelay + lda #$05 +ml_nontscdelay: sta ntscdelay + bcc ml_skip + + jsr readcontroller + jsr steering + jsr scrolllogic +ml_skip: jsr setpanel +mainloop2: jmp mainloop + +readcontroller: lda #$01 + sta $4016 + lda #$00 + sta $4016 + sta control + ldx #$08 +readcloop: lda $4016 + ror + lda control + ror + sta control + dex + bne readcloop + rts + +steering: inc controldelay + lda controldelay + and #$03 + beq steering2 + rts +steering2: lda control + and #CTRL_UP + beq steering3 + lda scrollsy + cmp #-8 + beq steering3 + dec scrollsy +steering3: lda control + and #CTRL_DOWN + beq steering4 + lda scrollsy + cmp #8 + beq steering4 + inc scrollsy +steering4: lda control + and #CTRL_LEFT + beq steering5 + lda scrollsx + cmp #-8 + beq steering5 + dec scrollsx +steering5: lda control + and #CTRL_RIGHT + beq steering6 + lda scrollsx + cmp #8 + beq steering6 + inc scrollsx +steering6: lda control + and #CTRL_A+CTRL_B + beq steering7 + lda #$00 + sta scrollsx + sta scrollsy +steering7: rts + +resetscroll: lda #$00 + sta scrollx + sta scrolly + sta scrollsx + sta scrollsy + sta rawscrollx + sta rawscrolly + sta blockx + sta blocky + sta screenshift + ldx #$07 ;Calculate 7 * MAPSIZEX + lda #$00 ;to help scrolling + sta btmrowlo + sta btmrowhi +rscr_loop1: lda btmrowlo + clc + adc mapsizex + sta btmrowlo + lda btmrowhi + adc mapsizex+1 + sta btmrowhi + dex + bne rscr_loop1 + ldx #$3f +rscr_loop2: lda #$00 ;Clear color buffer + sta COLORBUF,x + dex + bpl rscr_loop2 + lda #$ff ;Reset magic char + sta mcharlo + sta mcharhi + lda #SPLITPOINT-1 ;Set sprite0 for screen + sta SPRBUF ;split + lda #$ff + sta SPRBUF+1 + lda #$00 + sta SPRBUF+2 + lda #$f8 + sta SPRBUF+3 + rts + +erasemagicchar: lda mcharhi + bmi emc_noneed + sta $2006 + lda mcharlo + sta $2006 + lda mcharunder + sta $2007 +emc_noneed: lda #$ff + sta mcharhi + sta mcharlo + rts + +drawmagicchar: lda rawscrolly + clc + adc #(SPLITPOINT/8)+2 + cmp #SCRSIZEY + bcc dmc_posok + sbc #SCRSIZEY +dmc_posok: asl + tax + lda scract_rowtbl+1,x + sta mcharhi + lda scrollx + and #$07 + cmp #$01 + lda rawscrollx + adc #SCRSIZEX-2 + and #SCRSIZEX-1 + ora scract_rowtbl,x + sta mcharlo + lda mcharhi + sta $2006 + lda mcharlo + sta $2006 + lda $2007 ;First read is rubbish + lda $2007 + sta mcharunder + lda mcharhi + sta $2006 + lda mcharlo + sta $2006 + lda #$ff + sta $2007 + rts + +;ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» +;ºSCROLLLOGIC º +;º º +;ºUpdates scrolling position, block & map pointers and draws the new graphics º +;ºto the horizontal and vertical buffers for the SCROLLACTION routine. º +;º º +;ºParameters: - º +;ºReturns: - º +;ºDestroys: A,X,Y º +;ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ + +scrolllogic: lda #$00 + sta screenshift + lda scrollx + lsr + lsr + lsr + sta temp1 ;Temp1 = old raw x scrolling + lda scrolly + lsr + lsr + lsr + sta temp2 ;Temp2 = old raw y scrolling + lda scrollx + clc + adc scrollsx + sta scrollx + lsr + lsr + lsr + sta rawscrollx ;New raw x scrolling + lda scrollsy + clc + adc scrolly + cmp #$f0 + bcc scrlog_notover + ldx scrollsy + bmi scrlog_overneg + sec + sbc #$f0 + jmp scrlog_notover +scrlog_overneg: sec + sbc #$10 +scrlog_notover: sta scrolly + lsr + lsr + lsr + sta rawscrolly ;new raw y scrolling + lda temp1 ;Any shifting in X-dir? + cmp rawscrollx + beq scrlog_xshiftok + lda scrollsx + bmi scrlog_xshiftneg + lda #SCROLL_RIGHT + sta screenshift + inc blockx + lda blockx + cmp #$04 + bne scrlog_xshiftok + lda #$00 + sta blockx + inc mapptrlo + bne scrlog_xshiftok + inc mapptrhi + jmp scrlog_xshiftok +scrlog_xshiftneg: + lda #SCROLL_LEFT + sta screenshift + dec blockx + bpl scrlog_xshiftok + lda #$03 + sta blockx + dec mapptrlo + lda mapptrlo + cmp #$ff + bne scrlog_xshiftok + dec mapptrhi +scrlog_xshiftok: + lda temp2 ;Any shifting in Y-dir? + cmp rawscrolly + beq scrlog_yshiftok + lda scrollsy + bmi scrlog_yshiftneg + lda #SCROLL_DOWN + ora screenshift + sta screenshift + inc blocky + lda blocky + cmp #$04 + bne scrlog_yshiftok + lda #$00 + sta blocky + lda mapptrlo + clc + adc mapsizex + sta mapptrlo + lda mapptrhi + adc mapsizex+1 + sta mapptrhi + jmp scrlog_yshiftok +scrlog_yshiftneg: + lda #SCROLL_UP + ora screenshift + sta screenshift + dec blocky + bpl scrlog_yshiftok + lda #$03 + sta blocky + lda mapptrlo + sec + sbc mapsizex + sta mapptrlo + lda mapptrhi + sbc mapsizex+1 + sta mapptrhi +scrlog_yshiftok:lda screenshift + asl + tax + lda scrlog_jumptbl,x + sta temp1 + lda scrlog_jumptbl+1,x + sta temp2 + jmp (temp1) + +scrlog_jumptbl: dc.w scrlog_shno ;0 + dc.w scrlog_shleft ;1 + dc.w scrlog_shright ;2 + dc.w scrlog_shno ;3 + dc.w scrlog_shup ;4 + dc.w scrlog_shupleft ;5 + dc.w scrlog_shupright ;6 + dc.w scrlog_shno ;7 + dc.w scrlog_shdown ;8 + dc.w scrlog_shdownleft ;9 + dc.w scrlog_shdownright ;10 + +scrlog_shno: rts +scrlog_shleft: jmp scrlog_doleft +scrlog_shright: jmp scrlog_doright +scrlog_shup: jmp scrlog_doup +scrlog_shdown: jmp scrlog_dodown +scrlog_shupleft:jsr scrlog_doleft + jmp scrlog_doup +scrlog_shupright:jsr scrlog_doright + jmp scrlog_doup +scrlog_shdownleft:jsr scrlog_doleft + jmp scrlog_dodown +scrlog_shdownright:jsr scrlog_doright + jmp scrlog_dodown + +scrlog_doleft: lda mapptrlo ;Calc. map pointer + sta srclo + lda mapptrhi + sta srchi + lda #SCRSIZEY + sta temp1 ;Chars to do + lda rawscrollx ;Position onscreen where drawing + sta temp6 ;happens (to help coloring) + ldx rawscrolly ;Pointer within screen + lda blocky ;Calc. starting blockindex + asl + asl + adc blockx +scrlog_doleftnb:sta temp2 ;Index within block + ldy #$00 ;Get block from map + lda (srclo),y + tay + lda blocktbllo,y + sta destlo + lda blocktblhi,y + sta desthi + lda blockdata,y ;Color + sta temp5 + ldy temp2 +scrlog_doleftloop: + lda (destlo),y + sta HORIZBUF,x + jsr scrlog_xcolor + inx + cpx #SCRSIZEY + bcc scrlog_doleftno1 + ldx #$00 +scrlog_doleftno1: + dec temp1 + beq scrlog_doleftrdy + tya + clc + adc #$04 + tay + cmp #$10 + bcc scrlog_doleftloop + lda srclo + clc + adc mapsizex + sta srclo + lda srchi + adc mapsizex+1 + sta srchi + lda blockx + jmp scrlog_doleftnb +scrlog_doleftrdy:rts + +scrlog_doright: lda mapptrlo + clc + adc #$07 + sta srclo + lda mapptrhi + adc #$00 + sta srchi + lda rawscrollx + clc + adc #SCRSIZEX-1 + and #SCRSIZEX-1 + sta temp6 + lda #SCRSIZEY + sta temp1 ;Chars to do + lda blockx + clc + adc #$03 + cmp #$04 + bcc scrlog_dorightno2 + and #$03 + pha + inc srclo + bne scrlog_dorightno3 + inc srchi +scrlog_dorightno3:pla +scrlog_dorightno2:sta temp3 + ldx rawscrolly ;Pointer within screen + lda blocky + asl + asl + clc + adc temp3 +scrlog_dorightnb:sta temp2 ;Pointer within block + ldy #$00 ;Get block from map + lda (srclo),y + tay + lda blocktbllo,y + sta destlo + lda blocktblhi,y + sta desthi + lda blockdata,y ;Color + sta temp5 + ldy temp2 +scrlog_dorightloop: + lda (destlo),y + sta HORIZBUF,x + jsr scrlog_xcolor + inx + cpx #SCRSIZEY + bcc scrlog_dorightno1 + ldx #$00 +scrlog_dorightno1: + dec temp1 + beq scrlog_dorightrdy + tya + clc + adc #$04 + tay + cmp #$10 + bcc scrlog_dorightloop + lda srclo + clc + adc mapsizex + sta srclo + lda srchi + adc mapsizex+1 + sta srchi + lda temp3 + jmp scrlog_dorightnb +scrlog_dorightrdy:rts + +scrlog_doup: lda mapptrlo + sta srclo + lda mapptrhi + sta srchi + lda #SCRSIZEX + sta temp1 ;Chars to do + lda rawscrolly + sta temp6 + ldx rawscrollx ;Pointer within screen + lda blocky + asl + asl + sta temp3 + adc blockx +scrlog_doupnb: sta temp2 ;Pointer within block + ldy #$00 ;Get block from map + lda (srclo),y + tay + lda blocktbllo,y + sta destlo + lda blocktblhi,y + sta desthi + lda blockdata,y ;Color + sta temp5 + ldy temp2 +scrlog_douploop:lda (destlo),y + sta VERTBUF,x + jsr scrlog_ycolor + inx + txa + and #SCRSIZEX-1 + tax + dec temp1 + beq scrlog_douprdy + iny + tya + and #$03 + bne scrlog_douploop + inc srclo + bne scrlog_doupno2 + inc srchi +scrlog_doupno2: lda temp3 + jmp scrlog_doupnb +scrlog_douprdy: rts + +scrlog_dodown: lda mapptrlo + clc + adc btmrowlo + sta srclo + lda mapptrhi + adc btmrowhi + sta srchi + lda #SCRSIZEX + sta temp1 ;Chars to do + lda rawscrolly + clc + adc #SCRSIZEY-1 + cmp #SCRSIZEY + bcc scrlog_dodownok1 + sbc #SCRSIZEY +scrlog_dodownok1: + sta temp6 + ldx rawscrollx ;Pointer within screen + lda blocky + clc + adc #$01 + cmp #$04 + bcc scrlog_dodownno3 + and #$03 + pha + lda srclo + clc + adc mapsizex + sta srclo + lda srchi + adc mapsizex+1 + sta srchi + pla +scrlog_dodownno3: + asl + asl + sta temp3 + adc blockx +scrlog_dodownnb:sta temp2 ;Pointer within block + ldy #$00 ;Get block from map + lda (srclo),y + tay + lda blocktbllo,y + sta destlo + lda blocktblhi,y + sta desthi + lda blockdata,y ;Color + sta temp5 + ldy temp2 +scrlog_dodownloop:lda (destlo),y + sta VERTBUF,x + jsr scrlog_ycolor + inx + txa + and #SCRSIZEX-1 + tax + dec temp1 + beq scrlog_dodownrdy + iny + tya + and #$03 + bne scrlog_dodownloop + inc srclo + bne scrlog_dodownno2 + inc srchi +scrlog_dodownno2:lda temp3 + jmp scrlog_dodownnb +scrlog_dodownrdy:rts + +;ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» +;ºSCRLOG_XCOLOR º +;ºSCRLOG_YCOLOR º +;º º +;ºSubroutines to color the blocks (not so simple :-)) º +;º º +;ºParameters: Temp5 = Block color byte º +;º Temp6 = Position (X for xcolor, Y for ycolor) º +;º X = Position in the other axis º +;º Y = Position within the block º +;ºReturns: - º +;ºDestroys: A º +;ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ + +scrlog_xcolor: txa + and #$01 + beq scrlog_xcolorok + rts +scrlog_xcolorok:tya + and #$02 ;Right part of block? + bne scrlog_xcolor0 + lda temp5 + jmp scrlog_xcolor1 +scrlog_xcolor0: lda temp5 + lsr + lsr +scrlog_xcolor1: cpy #$08 ;Lower part of block? + bcc scrlog_xcolor2 + lsr + lsr + lsr + lsr +scrlog_xcolor2: and #$03 + sta temp7 ;Color now in temp7 + txa + asl + and #$f8 + sta temp8 ;Position, where to draw + lda temp6 + lsr + lsr + clc + adc temp8 + sta temp8 ;Byteposition is ready in temp8 + sty temp9 ;Store Y + txa ;Check Y-fineposition + and #$02 + bne scrlog_xcolor5 ;Lower part +scrlog_xcolor3: lda temp6 ;Check X-fineposition + and #$02 + bne scrlog_xcolor4 ;Right part + ldy temp8 + lda COLORBUF,y + and #%11111100 + ora temp7 + sta COLORBUF,y + ldy temp9 ;Get Y back + rts +scrlog_xcolor4: ldy temp7 + lda scrlog_mul4tbl,y + sta temp7 + ldy temp8 + lda COLORBUF,y + and #%11110011 + ora temp7 + sta COLORBUF,y + ldy temp9 + rts +scrlog_xcolor5: lda temp6 + and #$02 + bne scrlog_xcolor6 + ldy temp7 + lda scrlog_mul16tbl,y + sta temp7 + ldy temp8 + lda COLORBUF,y + and #%11001111 + ora temp7 + sta COLORBUF,y + ldy temp9 + rts +scrlog_xcolor6: ldy temp7 + lda scrlog_mul64tbl,y + sta temp7 + ldy temp8 + lda COLORBUF,y + and #%00111111 + ora temp7 + sta COLORBUF,y + ldy temp9 + rts + +scrlog_ycolor: txa + and #$01 + beq scrlog_ycolorok + rts +scrlog_ycolorok:tya + and #$02 ;Right part of block? + bne scrlog_ycolor0 + lda temp5 + jmp scrlog_ycolor1 +scrlog_ycolor0: lda temp5 + lsr + lsr +scrlog_ycolor1: cpy #$08 ;Lower part of block? + bcc scrlog_ycolor2 + lsr + lsr + lsr + lsr +scrlog_ycolor2: and #$03 + sta temp7 ;Color now in temp7 + lda temp6 + asl + and #$f8 + sta temp8 ;Position, where to draw + txa + lsr + lsr + clc + adc temp8 + sta temp8 ;Byteposition is ready in temp8 + sty temp9 ;Store Y + lda temp6 ;Check Y-fineposition + and #$02 + bne scrlog_ycolor5 ;Lower part +scrlog_ycolor3: txa ;Check X-fineposition + and #$02 + bne scrlog_ycolor4 ;Right part + ldy temp8 + lda COLORBUF,y + and #%11111100 + ora temp7 + sta COLORBUF,y + ldy temp9 ;Get Y back + rts +scrlog_ycolor4: ldy temp7 + lda scrlog_mul4tbl,y + sta temp7 + ldy temp8 + lda COLORBUF,y + and #%11110011 + ora temp7 + sta COLORBUF,y + ldy temp9 + rts +scrlog_ycolor5: txa + and #$02 + bne scrlog_ycolor6 + ldy temp7 + lda scrlog_mul16tbl,y + sta temp7 + ldy temp8 + lda COLORBUF,y + and #%11001111 + ora temp7 + sta COLORBUF,y + ldy temp9 + rts +scrlog_ycolor6: ldy temp7 + lda scrlog_mul64tbl,y + sta temp7 + ldy temp8 + lda COLORBUF,y + and #%00111111 + ora temp7 + sta COLORBUF,y + ldy temp9 + rts + +scrlog_mul4tbl: dc.b 0,4,8,12 +scrlog_mul16tbl:dc.b 0,16,32,48 +scrlog_mul64tbl:dc.b 0,64,128,192 + +;ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ» +;ºSCROLLACTION º +;º º +;ºBlits the horizontal & vertical buffers to PPU memory and also updates º +;ºattribute table. To be called during vblank. Also, RESETSCROLL or SCROLLLOGICº +;ºmust be called before calling this. º +;º º +;ºParameters: - º +;ºReturns: - º +;ºDestroys: A,X,Y º +;ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍͼ + +scrollaction: lda $2002 + lda screenshift + asl + tax + lda scract_jumptbl,x + sta temp1 + lda scract_jumptbl+1,x + sta temp2 + jmp (temp1) + +scract_jumptbl: dc.w scract_done ;0 + dc.w scract_doleft ;1 + dc.w scract_doright ;2 + dc.w scract_done ;3 + dc.w scract_doup ;4 + dc.w scract_doupleft ;5 + dc.w scract_doupright ;6 + dc.w scract_done ;7 + dc.w scract_dodown ;8 + dc.w scract_dodownleft ;9 + dc.w scract_dodownright ;10 + +scract_doupleft:jsr scract_doleft + jmp scract_doup + +scract_doupright:jsr scract_doright + jmp scract_doup + +scract_dodownleft:jsr scract_doleft + jmp scract_dodown + +scract_dodownright:jsr scract_doright + jmp scract_dodown + +scract_doleft: lda #$20 + sta $2006 + lda rawscrollx + and #$1f + sta $2006 + jsr scract_horizshift + lda rawscrollx + lsr + lsr + tax +scract_doleftattr: +N SET 0 + REPEAT 8 + lda #$23 + sta $2006 + txa + ora #$c0+N + sta $2006 + lda COLORBUF+N,x + sta $2007 +N SET N+8 + REPEND +scract_done: rts + +scract_doright: lda #$20 + sta $2006 + lda rawscrollx + clc + adc #SCRSIZEX-1 + and #$1f + sta $2006 + jsr scract_horizshift + lda rawscrollx + clc + adc #$1f + and #$1f + lsr + lsr + tax + jmp scract_doleftattr + +scract_doup: lda rawscrolly + asl + tax + lda scract_rowtbl+1,x + sta $2006 + lda scract_rowtbl,x + sta $2006 + jsr scract_vertshift + lda #$23 + sta $2006 + lda rawscrolly + asl + and #$f8 + tax + ora #$c0 + sta $2006 +scract_doupattr: +N SET 0 + REPEAT 8 + lda COLORBUF+N,x + sta $2007 +N SET N+1 + REPEND + rts + +scract_dodown: lda rawscrolly + clc + adc #SCRSIZEY-1 + cmp #SCRSIZEY + bcc scract_dodownok1 + sbc #SCRSIZEY +scract_dodownok1: + sta temp1 + asl + tax + lda scract_rowtbl+1,x + sta $2006 + lda scract_rowtbl,x + sta $2006 + jsr scract_vertshift + lda #$23 + sta $2006 + lda temp1 + asl + and #$f8 + tax + ora #$c0 + sta $2006 + jmp scract_doupattr + +scract_horizshift: + lda #PPU0VALUE+4 ;Vertical increment + sta $2000 +N SET 0 + REPEAT SCRSIZEY + lda HORIZBUF+N + sta $2007 +N SET N+1 + REPEND + lda #PPU0VALUE ;Normal increment + sta $2000 + rts + +scract_vertshift: +N SET 0 + REPEAT SCRSIZEX + lda VERTBUF+N + sta $2007 +N SET N+1 + REPEND + rts + +scract_rowtbl: +N SET $2000 + REPEAT SCRSIZEY + dc.w N +N SET N+32 + REPEND + +setgamescreen: lda #$00 + sta $2006 + sta $2006 + lda scrollx + sec + sbc #$08 + sta $2005 + lda scrolly + clc + adc #$10 + cmp #SCRSIZEY*8 + bcc setgame_ok + sbc #SCRSIZEY*8 +setgame_ok: sta $2005 + lda #$1c ;Turn on onescreen mirror + jsr write8000 + lda #$18 ;BG & sprites on, clipping + sta $2001 + lda #$00 ;Assume no shifting on next fr. + sta screenshift + rts + +setpanel: bit $2002 ;Wait if sprite hit still on + bvs setpanel + ldx nmicount +setpanel_wait: cpx nmicount ;Check if vblank occurs before + bne setpanel_toolong ;spritehit (something went + bit $2002 ;wrong) + bvc setpanel_wait + lda #$00 ;Blank screen + sta $2001 + lda #$1e ;Turn off onescreen mirror + jsr write8000 + lda #$00 + ldy #$04 ;Set scrolling & display pos. + sta $2005 + sta $2005 + sty $2006 + sta $2006 + lda #$0a + sta $2001 ;Just BG on, no sprites, no clip +setpanel_toolong: + rts + +setsprites: lda #>SPRBUF ;Start sprite DMA + sta $4014 + rts + +clearsprites: ldx #$00 +clrspr_loop: lda #$f0 + sta SPRBUF,x + lda #$00 + sta SPRBUF+1,x + lda #$00 + sta SPRBUF+2,x + lda #$f8 + sta SPRBUF+3,x + inx + inx + inx + inx + bne clrspr_loop + rts + +setnametable: lda #$1e ;Turn off onescreen mirror + jsr write8000 + lda #PPU0VALUE ;Normal increment + sta $2000 + lda #$20 ;Address $2000 + sta $2006 + lda #$00 + sta $2006 + ldx #$00 + ldy #$00 +setntbl_loop1: lda #$00 ;Wipe both nametables + sta $2007 + inx + bne setntbl_loop1 + iny + cpy #$08 + bcc setntbl_loop1 + ldx #$00 + lda #$24 ;Address $2400 + sta $2006 + lda #$00 + sta $2006 + lda ntscflag + bne setntbl_loop3 + +setntbl_loop2: lda paneltext,x ;Now write text to the + and #$3f ;second nametable + sta $2007 + inx + cpx #6*32 + bcc setntbl_loop2 + rts +setntbl_loop3: lda paneltext2,x ;Now write text to the + and #$3f ;second nametable + sta $2007 + inx + cpx #6*32 + bcc setntbl_loop3 + rts + +loadpalette: lda #$3f + sta $2006 + lda #$00 + sta $2006 + ldx #$00 +loadpalette2: lda palette,x + sta $2007 + inx + cpx #$20 + bne loadpalette2 + rts + +loadchars: lda #$00 + sta $2006 + sta $2006 + lda #chardata + sta srchi + ldy #$00 + ldx #$10 +loadchars2: lda (srclo),y + sta $2007 + iny + bne loadchars2 + inc srchi + dex + bne loadchars2 + rts + +detectntsc: lda #$01 + sta ntscflag ;Assume NTSC + sta ntscdelay + jsr waitvblank + lda #$00 + sta temp1 + sta temp2 + lda nmicount +dntsc_loop: cmp nmicount + bne dntsc_over + inc temp1 + bne dntsc_loop + inc temp2 + bne dntsc_loop +dntsc_over: asl temp1 + lda temp2 + rol + cmp #$12 + bcc dntsc_nopal + dec ntscflag +dntsc_nopal: rts + + +setupppu: lda #PPU0VALUE ;Blank screen, leave NMI's on + sta $2000 + lda #$00 + sta $2001 + rts + +setupmapper: lda #$1e +write8000: pha ;Write to MMC 1 mapper + lda #$80 ;First reset + sta $8000 ;Then 5 bits of data + pla + sta $8000 + lsr + sta $8000 + lsr + sta $8000 + lsr + sta $8000 + lsr + sta $8000 + rts + +waitvblank: lda nmicount +waitvblank2: cmp nmicount + beq waitvblank2 + lda #$00 ;Blank screen, with clipping + sta $2001 + lda $2002 + lda #$1e ;Turn off onescreen mirror + jmp write8000 + +nmi: inc nmicount +irq: rti + +paneltext: dc.b " " + dc.b $1d,"MULTIDIRECTIONAL SCROLLING TEST" + dc.b $1e,"RUNNING IN PAL, 50HZ REFRESH " + dc.b $1f + ds.b 30,$3c + dc.b $3e + dc.b " " + dc.b " " + dc.b " " + +paneltext2: dc.b " " + dc.b $1d,"MULTIDIRECTIONAL SCROLLING TEST" + dc.b $1e,"RUNNING IN NTSC, 60HZ REFRESH " + dc.b $1f + ds.b 30,$3c + dc.b $3e + dc.b " " + dc.b " " + dc.b " " + + +blocktbllo: +N SET blockdata+512 + REPEAT 128 + dc.b N +N SET N+16 + REPEND + +blocktblhi: +N SET blockdata+512 + REPEAT 128 + dc.b N/256 +N SET N+16 + REPEND + +palette: incbin scrtest.pal + incbin scrtest.pal + +blockdata: incbin scrtest.blk + +map: incbin scrtest.map +mapsizex = map+0 +mapsizey = map+2 +mapdata = map+4 + +chardata: incbin scrtest.chr + + org $fffa + + dc.w nmi + dc.w start + dc.w irq + diff --git a/scrolltest/scrollhd.bin b/scrolltest/scrollhd.bin new file mode 100644 index 0000000..12b581d Binary files /dev/null and b/scrolltest/scrollhd.bin differ diff --git a/scrolltest/scrollhd.s b/scrolltest/scrollhd.s new file mode 100644 index 0000000..4000c48 --- /dev/null +++ b/scrolltest/scrollhd.s @@ -0,0 +1,11 @@ + .processor 6502 + .org $0000 + + ;NES + dc.b $4e,$45,$53,$1a + + dc.b $01 ;1 16kb PRG rom banks + dc.b $00 ;0 CHR rom banks + dc.b $10 ;Mapper 1 (MMC1) + dc.b $00 + ds.b 8,0 diff --git a/scrolltest/scrtest.blk b/scrolltest/scrtest.blk new file mode 100644 index 0000000..bb4cf41 Binary files /dev/null and b/scrolltest/scrtest.blk differ diff --git a/scrolltest/scrtest.chr b/scrolltest/scrtest.chr new file mode 100644 index 0000000..042038e Binary files /dev/null and b/scrolltest/scrtest.chr differ diff --git a/scrolltest/scrtest.map b/scrolltest/scrtest.map new file mode 100644 index 0000000..827f485 Binary files /dev/null and b/scrolltest/scrtest.map differ diff --git a/scrolltest/scrtest.pal b/scrolltest/scrtest.pal new file mode 100644 index 0000000..9fa0c0c Binary files /dev/null and b/scrolltest/scrtest.pal differ diff --git a/soundtest/README.TXT b/soundtest/README.TXT new file mode 100644 index 0000000..9b93ec2 --- /dev/null +++ b/soundtest/README.TXT @@ -0,0 +1 @@ +Check "sndtest.asm" for program info! diff --git a/soundtest/SNDTEST.ASM b/soundtest/SNDTEST.ASM new file mode 100644 index 0000000..83c3496 --- /dev/null +++ b/soundtest/SNDTEST.ASM @@ -0,0 +1,515 @@ +; +; NES sound testing program +; Written by SnowBro +; +; Yep, I know there's a program similar to this already around, but I +; wanted to write one on my own. Partly to try and figure out more about +; how the NES sound registers work, and partly to get some experience +; with 6502 programming, which I'm pretty rusty at. I tried to keep the +; code small, but was a bit lazy here and there. Same goes for the lacking +; comments in some areas. The font was disrespectfully ripped from +; Castlevania 3. +; +; This code hasn't been tested on a real NES, so I don't know if it will +; work properly (sigh). If anyone has the chance to try it, please report +; back to me. It has been tested with most reliable NES emulators and +; should work fine on them. +; +; The program is simple: it lets you set individual bits of the NES sound +; registers for each channel and then test the output. +; The joypad buttons do the following: +; +; Select: Select channel +; Up/Down: Select register of current channel +; Right/Left: Select bit of current register +; A: Toggle current bit +; Start: Play channel +; +; The code was assembled with X816, a great S/NES assembler by minus. +; Modify the code as you wish. +; +; Check out my webpage at http://home.sol.no/~kenhanse/nes/ for some +; of the other stuff I've written. +; +; Kent Hansen 01/01/99 +; + +.mem 8 +.index 8 +.org $C000 + +;------------------------------[ Define stuff ]------------------------------- + +RIGHT_BUTTON EQU %00000001 +LEFT_BUTTON EQU %00000010 +DOWN_BUTTON EQU %00000100 +UP_BUTTON EQU %00001000 +START_BUTTON EQU %00010000 +SELECT_BUTTON EQU %00100000 +B_BUTTON EQU %01000000 +A_BUTTON EQU %10000000 + +; Zero page addresses for where to store data + +SND_REGS EQU $00 ; data for 4 channels, 4 bytes each +CUR_CHN EQU $10 ; current channel (0...3) +CUR_REG EQU $11 ; current register of channel (0...3) +CUR_BIT EQU $12 ; current bit of register +JOY_STAT EQU $FC ; byte containing status of joypad +OLD_STAT EQU $FD ; joypad status from previous refresh +ADDR_LO EQU $FE +ADDR_HI EQU $FF + +;---------------------------[ Create font table ]----------------------------- + +.asctable +cleartable +20h = 00h +"A".."Z" = 01h +"0".."9" = 20h +":" = 2Ah +.end + +;---------------------------------[ Data ]------------------------------------ + +chn_text .asc "CHANNEL: 0" + .db 0FFh + +palette .db 0Fh,0Fh,0Fh,20h + .db 0Fh,0Fh,0Fh,20h + .db 0Fh,0Fh,0Fh,20h + .db 0Fh,0Fh,0Fh,20h + + .db 0Fh,0Fh,0Fh,20h + .db 0Fh,0Fh,0Fh,20h + .db 0Fh,0Fh,0Fh,20h + .db 0Fh,0Fh,0Fh,20h + +;---------------------------------[ Code ]------------------------------------ + +reset: +cld ; clear decimal mode +sei ; disable interrupts +lda #$00 +sta $2000 ; disable various stuff +sta $2001 ; ditto +ldx #$FF +txs ; set stack pointer + +jsr clear_ram +jsr wait_vblank +jsr set_palette +jsr clear_nametable +jsr setup_screen ; writes some text info to the name table + +lda #%00011110 +sta $2001 ; enable bg & sprites + +lda #%10000000 +sta $2000 ; enable NMI + +main_loop: +lda #$00 +sta $2005 ; hscroll = 0 +sta $2005 ; vscroll = 0 +jsr wait_vblank +jsr print_regs ; print the regs in binary format +jsr set_cursors ; show the ChannelCursor and BitCursor + +; the next piece of code handles button presses + +lda #RIGHT_BUTTON +and JOY_STAT +beq + +and OLD_STAT ; If this AND results in a non-zero result, it means +bne + ; the button was pressed last refresh too, so do nothing. +jsr b0 ; Do appropriate action for this button ++ ; Repeat for the other 7 buttons... +lda #LEFT_BUTTON +and JOY_STAT +beq + +and OLD_STAT +bne + +jsr b1 ++ +lda #DOWN_BUTTON +and JOY_STAT +beq + +and OLD_STAT +bne + +jsr b2 ++ +lda #UP_BUTTON +and JOY_STAT +beq + +and OLD_STAT +bne + +jsr b3 ++ +lda #START_BUTTON +and JOY_STAT +beq + +and OLD_STAT +bne + +jsr b4 ++ +lda #SELECT_BUTTON +and JOY_STAT +beq + +and OLD_STAT +bne + +jsr b5 ++ +lda #A_BUTTON +and JOY_STAT +beq + +and OLD_STAT +bne + +jsr b6 ++ +lda #B_BUTTON +and JOY_STAT +beq + +and OLD_STAT +bne + +jsr b7 ++ + +- ; This code is NESticle-specific, as NESticle +lda $2002 ; doesn't seem to reset the 7th bit of $2002 when +bmi - ; it's read, only when the VBlank is actually over. + ; The program is buggy in NESticle if I remove this + ; loop. +jmp main_loop + +;----------------------------------------------------------------------------- + +b0: +dec CUR_BIT +lda CUR_BIT +and #$07 +sta CUR_BIT +rts + +b1: +inc CUR_BIT +lda CUR_BIT +and #$07 +sta CUR_BIT +rts + +b2: +inc CUR_REG +lda CUR_REG +and #$03 +sta CUR_REG +rts + +b3: +dec CUR_REG +lda CUR_REG +and #$03 +sta CUR_REG +rts + +b4: +lda #$01 +ldx CUR_CHN +beq + +- +asl +dex +bne - ++ +sta $4015 ; enable sound channel + +ldy #$10 +ldx #$00 +- +lda SND_REGS,x +sta $4000,x +inx +dey +bne - +rts + +b5: +inc CUR_CHN +lda CUR_CHN +and #$03 +sta CUR_CHN + +lda #$20 +sta $2006 +lda #$69 +sta $2006 +lda CUR_CHN +ora #$20 +sta $2007 +rts + +b6: +lda #$01 +ldx CUR_BIT +beq + +clc +- +asl +dex +bne - ++ +pha +lda CUR_CHN +asl +asl +clc +adc CUR_REG +tax +pla +eor SND_REGS,x +sta SND_REGS,x +rts + +b7: +rts + +;----------------------------------------------------------------------------- + +print_regs: +lda #$20 +sta ADDR_HI +lda #$C8 +sta ADDR_LO + +lda CUR_CHN +asl +asl +tax + +print_one_reg: +lda ADDR_HI +sta $2006 +lda ADDR_LO +sta $2006 +lda SND_REGS,x +ldy #$08 +- +asl +pha +lda #$00 +rol +ora #$20 +sta $2007 +pla +dey +bne - +lda ADDR_LO +clc +adc #$40 +sta ADDR_LO +bcc + +inc ADDR_HI ++ +inx +txa +and #$03 +bne print_one_reg +rts + +set_cursors: +lda #$00 +sta $2003 +lda CUR_REG +asl +asl +asl +asl +clc +adc #$30 +sta $2004 +lda #$30 +sta $2004 +lda #$00 +sta $2004 +lda #$38 +sta $2004 + +lda CUR_REG +asl +asl +asl +asl +clc +adc #$38 +sta $2004 +lda #$31 +sta $2004 +lda #$00 +sta $2004 +lda CUR_BIT +eor #$07 +asl +asl +asl +clc +adc #$40 +sta $2004 +rts + +setup_screen: +lda #$20 +sta $2006 +lda #$60 +sta $2006 +ldx #$00 +- +lda chn_text,x +cmp #$FF +beq end_text +sta $2007 +inx +bne - +end_text: + +lda #$20 +sta ADDR_HI +lda #$C0 +sta ADDR_LO +ldy #$20 +- +lda ADDR_HI +sta $2006 +lda ADDR_LO +sta $2006 +lda #$12 ; "R" +sta $2007 +lda #$05 ; "E" +sta $2007 +lda #$07 ; "G" +sta $2007 +lda #$00 ; " " +sta $2007 +sty $2007 +lda #$2A ; ":" +sta $2007 +lda ADDR_LO +clc +adc #$40 +sta ADDR_LO +bcc + +inc ADDR_HI ++ +iny +cpy #$24 +bne - +rts + +wait_vblank: +lda $2002 +bpl wait_vblank +rts + +; Clear RAM at $0000-$07FF +; ======================== + +clear_ram: +lda #$07 ; high byte of last RAM page +sta ADDR_HI +lda #$00 ; low byte of last RAM page +sta ADDR_LO +clear_it: +ldy ADDR_HI ; load high byte of address +cpy #$01 ; if it equals 1... +beq + ; ... skip (don't mess with stack at $0100-$01FF) +ldy #$00 +- +sta (ADDR_LO),y +iny +bne - ++ +dec ADDR_HI ; decrement high byte of address +bpl clear_it +rts + +; Read joypad +; =========== +; Returns: JOY_STAT = status of all buttons + +read_joypad: +ldy #$01 +sty $4016 ; reset strobe +dey +sty $4016 ; clear strobe +sty JOY_STAT ; JOY_STAT = 0 (clear all button bits) +ldy #$08 ; do all 8 buttons +read_button: +lda $4016 ; load button status +and #$01 ; only keep lowest bit +lsr a ; transfer to carry flag +rol JOY_STAT +dey +bne read_button +rts + +; Clear nametable +; =============== +; A : Select nametable (range 0...3) + +clear_nametable: +lda #$20 +sta $2006 ; write high byte of name table address +lda #$00 +sta $2006 ; write low byte of name table address + +ldx #$04 +ldy #$00 +lda #$00 +- +sta $2007 +dey +bne - +dex +bne - +rts + +; Set palette +; =========== + +set_palette: +lda #$3F +sta $2006 +lda #$00 +sta $2006 +ldx #$00 +ldy #$20 +- +lda palette,x +sta $2007 +inx +dey +bne - +rts + +nmi: +pha ; push A on stack +txa +pha ; push X on stack +tya +pha ; push Y on stack + +lda JOY_STAT +sta OLD_STAT +jsr read_joypad + +pla +tay ; restore Y +pla +tax ; restore X +pla ; restore A +rti + +.pad $FFFA + +.dw nmi,reset,reset + +.end diff --git a/soundtest/SNDTEST.CHR b/soundtest/SNDTEST.CHR new file mode 100644 index 0000000..6d2fd20 Binary files /dev/null and b/soundtest/SNDTEST.CHR differ diff --git a/soundtest/SNDTEST.NES b/soundtest/SNDTEST.NES new file mode 100644 index 0000000..38acf20 Binary files /dev/null and b/soundtest/SNDTEST.NES differ diff --git a/sprdma_and_dmc_dma/sprdma_and_dmc_dma.nes b/sprdma_and_dmc_dma/sprdma_and_dmc_dma.nes new file mode 100644 index 0000000..8c013e3 Binary files /dev/null and b/sprdma_and_dmc_dma/sprdma_and_dmc_dma.nes differ diff --git a/sprdma_and_dmc_dma/sprdma_and_dmc_dma_512.nes b/sprdma_and_dmc_dma/sprdma_and_dmc_dma_512.nes new file mode 100644 index 0000000..6668048 Binary files /dev/null and b/sprdma_and_dmc_dma/sprdma_and_dmc_dma_512.nes differ diff --git a/sprite_hit_tests_2005.10.05/01.basics.nes b/sprite_hit_tests_2005.10.05/01.basics.nes new file mode 100644 index 0000000..c51b7a4 Binary files /dev/null and b/sprite_hit_tests_2005.10.05/01.basics.nes differ diff --git a/sprite_hit_tests_2005.10.05/02.alignment.nes b/sprite_hit_tests_2005.10.05/02.alignment.nes new file mode 100644 index 0000000..6039e19 Binary files /dev/null and b/sprite_hit_tests_2005.10.05/02.alignment.nes differ diff --git a/sprite_hit_tests_2005.10.05/03.corners.nes b/sprite_hit_tests_2005.10.05/03.corners.nes new file mode 100644 index 0000000..9be8516 Binary files /dev/null and b/sprite_hit_tests_2005.10.05/03.corners.nes differ diff --git a/sprite_hit_tests_2005.10.05/04.flip.nes b/sprite_hit_tests_2005.10.05/04.flip.nes new file mode 100644 index 0000000..d584ce4 Binary files /dev/null and b/sprite_hit_tests_2005.10.05/04.flip.nes differ diff --git a/sprite_hit_tests_2005.10.05/05.left_clip.nes b/sprite_hit_tests_2005.10.05/05.left_clip.nes new file mode 100644 index 0000000..67dd7e7 Binary files /dev/null and b/sprite_hit_tests_2005.10.05/05.left_clip.nes differ diff --git a/sprite_hit_tests_2005.10.05/06.right_edge.nes b/sprite_hit_tests_2005.10.05/06.right_edge.nes new file mode 100644 index 0000000..c6f5bfe Binary files /dev/null and b/sprite_hit_tests_2005.10.05/06.right_edge.nes differ diff --git a/sprite_hit_tests_2005.10.05/07.screen_bottom.nes b/sprite_hit_tests_2005.10.05/07.screen_bottom.nes new file mode 100644 index 0000000..49d28e1 Binary files /dev/null and b/sprite_hit_tests_2005.10.05/07.screen_bottom.nes differ diff --git a/sprite_hit_tests_2005.10.05/08.double_height.nes b/sprite_hit_tests_2005.10.05/08.double_height.nes new file mode 100644 index 0000000..cdb3e1b Binary files /dev/null and b/sprite_hit_tests_2005.10.05/08.double_height.nes differ diff --git a/sprite_hit_tests_2005.10.05/09.timing_basics.nes b/sprite_hit_tests_2005.10.05/09.timing_basics.nes new file mode 100644 index 0000000..578f720 Binary files /dev/null and b/sprite_hit_tests_2005.10.05/09.timing_basics.nes differ diff --git a/sprite_hit_tests_2005.10.05/10.timing_order.nes b/sprite_hit_tests_2005.10.05/10.timing_order.nes new file mode 100644 index 0000000..cf166b7 Binary files /dev/null and b/sprite_hit_tests_2005.10.05/10.timing_order.nes differ diff --git a/sprite_hit_tests_2005.10.05/11.edge_timing.nes b/sprite_hit_tests_2005.10.05/11.edge_timing.nes new file mode 100644 index 0000000..1ace1ed Binary files /dev/null and b/sprite_hit_tests_2005.10.05/11.edge_timing.nes differ diff --git a/sprite_hit_tests_2005.10.05/readme.txt b/sprite_hit_tests_2005.10.05/readme.txt new file mode 100644 index 0000000..8ffe65e --- /dev/null +++ b/sprite_hit_tests_2005.10.05/readme.txt @@ -0,0 +1,162 @@ +NTSC NES PPU Sprite 0 Test ROMs +------------------------------- +These ROMs test much of sprite 0 hit behavior on a NTSC NES PPU. They +have been tested on an actual NES and all give a passing result. I wrote +them to verify that my NES emulator's sprite 0 hit emulation was working +properly. + +Each test ROM runs several tests and reports the result on screen and by +beeping a number of times. See below for the meaning of failure codes +for each test. It's best to run the tests in order, because some earlier +ROMs test things that later ones assume will work properly. + +The main source code for each test is included, and most tests are +clearly divided into sections. All the asm source is included, but it +runs on a custom devcart and assembler so it will require some effort to +assemble. Contact me if you'd assistance porting them to your setup. + + +01.basics +--------- +Tests basic sprite 0 hit behavior (nothing timing related). + +2) Sprite hit isn't working at all +3) Should hit even when completely behind background +4) Should miss when background rendering is off +5) Should miss when sprite rendering is off +6) Should miss when all rendering is off +7) All-transparent sprite should miss +8) Only low two palette index bits are relevant +9) Any non-zero palette index should hit with any other +10) Should miss when background is all transparent +11) Should always miss other sprites + + +02.alignment +------------ +Tests alignment of sprite hit with background. Places a solid background +tile in the middle of the screen and places the sprite on all four edges +both overlapping and non-overlapping. + +2) Basic sprite-background alignment is way off +3) Sprite should miss left side of bg tile +4) Sprite should hit left side of bg tile +5) Sprite should miss right side of bg tile +6) Sprite should hit right side of bg tile +7) Sprite should miss top of bg tile +8) Sprite should hit top of bg tile +9) Sprite should miss bottom of bg tile +10) Sprite should hit bottom of bg tile + + +03.corners +---------- +Tests sprite 0 hit using a sprite with a single pixel set, for each of +the four corners. + +2) Lower-right pixel should hit +3) Lower-left pixel should hit +4) Upper-right pixel should hit +5) Upper-left pixel should hit + + +04.flip +------- +Tests sprite 0 hit for single pixel sprite and background. + +2) Horizontal flipping doesn't work +3) Vertical flipping doesn't work +4) Horizontal + Vertical flipping doesn't work + + +05.left_clip +------------ +Tests sprite 0 hit with regard to clipping of left 8 pixels of screen. + +2) Should miss when entirely in left-edge clipping +3) Left-edge clipping occurs when $2001 is not $1e +4) Left-edge clipping is off when $2001 = $1e +5) Left-edge clipping blocks all hits only when X = 0 +6) Should miss; sprite pixel covered by left-edge clip +7) Should hit; sprite pixel outside left-edge clip +8) Should hit; sprite pixel outside left-edge clip + + +06.right_edge +------------- +Tests sprite 0 hit with regard to column 255 (ignored) and off right +edge of screen. + +2) Should always miss when X = 255 +3) Should hit; sprite has pixels < 255 +4) Should miss; sprite pixel is at 255 +5) Should hit; sprite pixel is at 254 +6) Should also hit; sprite pixel is at 254 + + +07.screen_bottom +---------------- +Tests sprite 0 hit with regard to bottom of screen. + +2) Should always miss when Y >= 239 +3) Can hit when Y < 239 +4) Should always miss when Y = 255 +5) Should hit; sprite pixel is at 238 +6) Should miss; sprite pixel is at 239 +7) Should hit; sprite pixel is at 238 + + +08.double_height +---------------- +Tests basic sprite 0 hit double-height operation. + +2) Lower sprite tile should miss bottom of bg tile +3) Lower sprite tile should hit bottom of bg tile +3) Lower sprite tile should miss top of bg tile +4) Lower sprite tile should hit top of bg tile + + +09.timing_basics +---------------- +Tests sprite 0 hit timing to within 12 or so PPU clocks. Tests flag +timing for upper-left corner, upper-right corner, lower-right corner, +and time flag is cleared (at end of VBL). Depends on proper PPU frame +length (less than 29781 CPU clocks). + +2) Upper-left corner too soon +3) Upper-left corner too late +4) Upper-right corner too soon +5) Upper-right corner too late +6) Lower-left corner too soon +7) Lower-left corner too late +8) Cleared at end of VBL too soon +9) Cleared at end of VBL too late + + +10.timing_order +--------------- +Tests sprite 0 hit timing for which pixel it first reports hit on. Each +test hits at the same location on screen, though different relative to +the position of the sprite. + +2) Upper-left corner too soon +3) Upper-left corner too late +4) Upper-right corner too soon +5) Upper-right corner too late +6) Lower-left corner too soon +7) Lower-left corner too late +8) Lower-right corner too soon +9) Lower-right corner too late + + +11.edge_timing +-------------- +Tests sprite 0 hit timing for which pixel it first reports hit on when +some pixels are under clip, or at or beyond right edge. + +2) Hit time shouldn't be based on pixels under left clip +3) Hit time shouldn't be based on pixels at X=255 +4) Hit time shouldn't be based on pixels off right edge + +-- +Shay Green (swap to e-mail) diff --git a/sprite_hit_tests_2005.10.05/source/01.basics.asm b/sprite_hit_tests_2005.10.05/source/01.basics.asm new file mode 100644 index 0000000..ae45964 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/01.basics.asm @@ -0,0 +1,90 @@ +; Tests basic sprite 0 hit behavior (nothing timing related). + + .include "prefix_sprite_hit.a" + +test_name: + .db "SPRITE HIT BASICS",0 + .code + +reset: + jsr begin_sprite_hit_tests + + lda #solid_tile + jsr fill_nametable + + ; Put sprite in middle of screen + lda #0 + sta sprite_attr + ldx #128 + ldy #120 + jsr set_sprite_xy + + lda #solid_tile + sta sprite_tile + lda #2;) Sprite hit isn't working at all + ldx #$18 + jsr sprite_should_hit + + lda #$20 + sta sprite_attr + lda #3;) Should hit even when completely behind background + ldx #$18 + jsr sprite_should_hit + + lda #4;) Should miss when background rendering is off + ldx #$10 + jsr sprite_should_miss + + lda #5;) Should miss when sprite rendering is off + ldx #$08 + jsr sprite_should_miss + + lda #6;) Should miss when all rendering is off + ldx #$00 + jsr sprite_should_miss + + lda #blank_tile + sta sprite_tile + + lda #7;) All-transparent sprite should miss + ldx #$18 + jsr sprite_should_miss + + lda #$ff + sta sprite_attr + lda #8;) Only low two palette index bits are relevant + ldx #$18 + jsr sprite_should_miss + + lda #1 + jsr fill_nametable + lda #2 + sta sprite_tile + lda #0 + sta sprite_attr + + lda #9;) Any non-zero palette index should hit with any other + ldx #$18 + jsr sprite_should_hit + + lda #0 + jsr fill_nametable + + lda #10;) Should miss when background is all transparent + ldx #$18 + jsr sprite_should_miss + + lda #120 + sta sprites + 4 + lda #3 + sta sprites + 5 + lda #0 + sta sprites + 6 + lda #128 + sta sprites + 7 + lda #11;) Should always miss other sprites + ldx #$18 + jsr sprite_should_miss + + jmp tests_passed + diff --git a/sprite_hit_tests_2005.10.05/source/02.alignment.asm b/sprite_hit_tests_2005.10.05/source/02.alignment.asm new file mode 100644 index 0000000..65cb5b4 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/02.alignment.asm @@ -0,0 +1,98 @@ +; Tests alignment of sprite hit with background. +; Places a solid background tile in the middle of the screen and +; places the sprite on all four edges both overlapping and +; non-overlapping. + + .include "prefix_sprite_hit.a" + +test_name: + .db "SPRITE HIT ALIGNMENT",0 + .code + +reset: + jsr begin_sprite_hit_tests + + ; Single solid tile in middle of screen + lda #$21 + ldx #$f0 + jsr set_vaddr + lda #solid_tile + sta $2007 + + lda #0 + sta sprite_attr + lda #solid_tile + sta sprite_tile + + ldx #128 + ldy #119 + jsr set_sprite_xy + lda #2;) Basic sprite-background alignment is way off + ldx #$18 + jsr sprite_should_hit + + ; Left + + ldx #120 + ldy #119 + jsr set_sprite_xy + lda #3;) Sprite should miss left side of bg tile + ldx #$18 + jsr sprite_should_miss + + ldx #121 + ldy #119 + jsr set_sprite_xy + lda #4;) Sprite should hit left side of bg tile + ldx #$18 + jsr sprite_should_hit + + ; Right + + ldx #136 + ldy #119 + jsr set_sprite_xy + lda #5;) Sprite should miss right side of bg tile + ldx #$18 + jsr sprite_should_miss + + ldx #135 + ldy #119 + jsr set_sprite_xy + lda #6;) Sprite should hit right side of bg tile + ldx #$18 + jsr sprite_should_hit + + ; Above + + ldx #128 + ldy #111 + jsr set_sprite_xy + lda #7;) Sprite should miss top of bg tile + ldx #$18 + jsr sprite_should_miss + + ldx #128 + ldy #112 + jsr set_sprite_xy + lda #8;) Sprite should hit top of bg tile + ldx #$18 + jsr sprite_should_hit + + ; Below + + ldx #128 + ldy #127 + jsr set_sprite_xy + lda #9;) Sprite should miss bottom of bg tile + ldx #$18 + jsr sprite_should_miss + + ldx #128 + ldy #126 + jsr set_sprite_xy + lda #10;) Sprite should hit bottom of bg tile + ldx #$18 + jsr sprite_should_hit + + jmp tests_passed diff --git a/sprite_hit_tests_2005.10.05/source/03.corners.asm b/sprite_hit_tests_2005.10.05/source/03.corners.asm new file mode 100644 index 0000000..d7a03f1 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/03.corners.asm @@ -0,0 +1,61 @@ +; Tests sprite 0 hit using a sprite with a single pixel set, +; for each of the four corners. + + .include "prefix_sprite_hit.a" + +test_name: + .db "SPRITE HIT CORNERS",0 + .code + +set_params: + sta sprite_tile + eor #$03 + pha + jsr set_sprite_xy + lda #$21 + ldx #$f0 + jsr set_vaddr + pla + sta $2007 + rts + .code + +reset: + jsr begin_sprite_hit_tests + + lda #0 + sta sprite_attr + + lda #lower_right_tile + ldx #121 + ldy #112 + jsr set_params + lda #2;) Lower-right pixel should hit + ldx #$18 + jsr sprite_should_hit + + lda #lower_left_tile + ldx #135 + ldy #112 + jsr set_params + lda #3;) Lower-left pixel should hit + ldx #$18 + jsr sprite_should_hit + + lda #upper_right_tile + ldx #121 + ldy #126 + jsr set_params + lda #4;) Upper-right pixel should hit + ldx #$18 + jsr sprite_should_hit + + lda #upper_left_tile + ldx #135 + ldy #126 + jsr set_params + lda #5;) Upper-left pixel should hit + ldx #$18 + jsr sprite_should_hit + + jmp tests_passed diff --git a/sprite_hit_tests_2005.10.05/source/04.flip.asm b/sprite_hit_tests_2005.10.05/source/04.flip.asm new file mode 100644 index 0000000..1a42fb6 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/04.flip.asm @@ -0,0 +1,52 @@ +; Tests sprite 0 hit for single pixel sprite and background. + + .include "prefix_sprite_hit.a" + +test_name: + .db "SPRITE HIT FLIPPING",0 + .code + +test_flip: + jsr set_sprite_xy + rts + .code + +reset: + jsr begin_sprite_hit_tests + + ; Single solid tile in middle of screen + lda #$21 + ldx #$f0 + jsr set_vaddr + lda #solid_tile + sta $2007 + + ldx #121 + ldy #112 + jsr set_sprite_xy + + lda #$40 + sta sprite_attr + lda #lower_left_tile + sta sprite_tile + lda #2;) Horizontal flipping doesn't work + ldx #$18 + jsr sprite_should_hit + + lda #$80 + sta sprite_attr + lda #upper_right_tile + sta sprite_tile + lda #3;) Vertical flipping doesn't work + ldx #$18 + jsr sprite_should_hit + + lda #$c0 + sta sprite_attr + lda #upper_left_tile + sta sprite_tile + lda #4;) Horizontal + Vertical flipping doesn't work + ldx #$18 + jsr sprite_should_hit + + jmp tests_passed diff --git a/sprite_hit_tests_2005.10.05/source/05.left_clip.asm b/sprite_hit_tests_2005.10.05/source/05.left_clip.asm new file mode 100644 index 0000000..b871159 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/05.left_clip.asm @@ -0,0 +1,72 @@ +; Tests sprite 0 hit with regard to clipping of left 8 pixels of screen. + + .include "prefix_sprite_hit.a" + +test_name: + .db "SPRITE HIT LEFT CLIPPING",0 + .code + +reset: + jsr begin_sprite_hit_tests + + lda #solid_tile + jsr fill_nametable + lda #0 + sta sprite_attr + ldy #120 + sta sprite_y + + ; Basic + + lda #solid_tile + sta sprite_tile + + lda #0 + sta sprite_x + lda #2;) Should miss when entirely in left-edge clipping + ldx #$18 + jsr sprite_should_miss + + lda #3;) Left-edge clipping occurs when $2001 is not $1e + ldx #$1a + jsr sprite_should_miss + lda #3 + ldx #$1c + jsr sprite_should_miss + + lda #4;) Left-edge clipping is off when $2001 = $1e + ldx #$1e + jsr sprite_should_hit + + lda #1 + sta sprite_x + lda #5;) Left-edge clipping blocks all hits only when X = 0 + ldx #$18 + jsr sprite_should_hit + + ; Detailed + + lda #upper_left_tile + sta sprite_tile + lda #7 + sta sprite_x + lda #6;) Should miss; sprite pixel covered by left-edge clip + ldx #$18 + jsr sprite_should_miss + + lda #8 + sta sprite_x + lda #7;) Should hit; sprite pixel outside left-edge clip + ldx #$18 + jsr sprite_should_hit + + lda #upper_right_tile + sta sprite_tile + lda #1 + sta sprite_x + lda #8;) Should hit; sprite pixel outside left-edge clip + ldx #$18 + jsr sprite_should_hit + + jmp tests_passed + diff --git a/sprite_hit_tests_2005.10.05/source/06.right_edge.asm b/sprite_hit_tests_2005.10.05/source/06.right_edge.asm new file mode 100644 index 0000000..dff7267 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/06.right_edge.asm @@ -0,0 +1,60 @@ +; Tests sprite 0 hit with regard to column 255 (ignored) and off +; right edge of screen. + + .include "prefix_sprite_hit.a" + +test_name: + .db "SPRITE HIT RIGHT EDGE",0 + .code + +reset: + jsr begin_sprite_hit_tests + lda #solid_tile + jsr fill_nametable + lda #0 + sta sprite_attr + lda #120 + sta sprite_y + + ; Basic + + lda #solid_tile + sta sprite_tile + lda #255 + sta sprite_x + lda #2;) Should always miss when X = 255 + ldx #$1e + jsr sprite_should_miss + + lda #254 + sta sprite_x + lda #3;) Should hit; sprite has pixels < 255 + ldx #$1e + jsr sprite_should_hit + + ; Detailed + + lda #upper_right_tile + sta sprite_tile + lda #248 + sta sprite_x + lda #4;) Should miss; sprite pixel is at 255 + ldx #$1e + jsr sprite_should_miss + + lda #247 + sta sprite_x + lda #5;) Should hit; sprite pixel is at 254 + ldx #$1e + jsr sprite_should_hit + + lda #upper_left_tile + sta sprite_tile + lda #254 + sta sprite_x + lda #6;) Should also hit; sprite pixel is at 254 + ldx #$1e + jsr sprite_should_hit + + jmp tests_passed + diff --git a/sprite_hit_tests_2005.10.05/source/07.screen_bottom.asm b/sprite_hit_tests_2005.10.05/source/07.screen_bottom.asm new file mode 100644 index 0000000..db2c166 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/07.screen_bottom.asm @@ -0,0 +1,65 @@ +; Tests sprite 0 hit with regard to bottom of screen. + + .include "prefix_sprite_hit.a" + +test_name: + .db "SPRITE HIT SCREEN BOTTOM",0 + .code + +reset: + jsr begin_sprite_hit_tests + lda #solid_tile + jsr fill_nametable + lda #0 + sta sprite_attr + lda #128 + sta sprite_x + + ; Basic + + lda #solid_tile + sta sprite_tile + lda #239 + sta sprite_y + lda #2;) Should always miss when Y >= 239 + ldx #$1e + jsr sprite_should_miss + + lda #238 + sta sprite_y + lda #3;) Can hit when Y < 239 + ldx #$1e + jsr sprite_should_hit + + lda #255 + sta sprite_y + lda #4;) Should always miss when Y = 255 + ldx #$1e + jsr sprite_should_miss + + ; Detailed + + lda #lower_right_tile + sta sprite_tile + lda #231 + sta sprite_y + lda #5;) Should hit; sprite pixel is at 238 + ldx #$1e + jsr sprite_should_hit + + lda #232 + sta sprite_y + lda #6;) Should miss; sprite pixel is at 239 + ldx #$1e + jsr sprite_should_miss + + lda #upper_left_tile + sta sprite_tile + lda #238 + sta sprite_y + lda #7;) Should hit; sprite pixel is at 238 + ldx #$1e + jsr sprite_should_hit + + jmp tests_passed + diff --git a/sprite_hit_tests_2005.10.05/source/08.double_height.asm b/sprite_hit_tests_2005.10.05/source/08.double_height.asm new file mode 100644 index 0000000..4b79fb2 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/08.double_height.asm @@ -0,0 +1,55 @@ +; Tests basic sprite 0 hit double-height operation. + + .include "prefix_sprite_hit.a" + +test_name: + .db "SPRITE HIT DOUBLE HEIGHT",0 + .code + +reset: + jsr begin_sprite_hit_tests + + lda #$20 ; double-height sprites + sta $2000 + + ; Single solid tile in middle of screen + lda #$21 + ldx #$f0 + jsr set_vaddr + lda #solid_tile + sta $2007 + + lda #0 + sta sprite_attr + lda #0 ; tiles 0 and 1 + sta sprite_tile + + ldx #128 + ldy #119 + jsr set_sprite_xy + lda #2;) Lower sprite tile should miss bottom of bg tile + ldx #$18 + jsr sprite_should_miss + + ldx #128 + ldy #118 + jsr set_sprite_xy + lda #3;) Lower sprite tile should hit bottom of bg tile + ldx #$18 + jsr sprite_should_hit + + ldx #128 + ldy #103 + jsr set_sprite_xy + lda #3;) Lower sprite tile should miss top of bg tile + ldx #$18 + jsr sprite_should_miss + + ldx #128 + ldy #104 + jsr set_sprite_xy + lda #4;) Lower sprite tile should hit top of bg tile + ldx #$18 + jsr sprite_should_hit + + jmp tests_passed diff --git a/sprite_hit_tests_2005.10.05/source/09.timing_basics.asm b/sprite_hit_tests_2005.10.05/source/09.timing_basics.asm new file mode 100644 index 0000000..843c719 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/09.timing_basics.asm @@ -0,0 +1,83 @@ +; Tests sprite 0 hit timing to within 12 or so PPU clocks. +; Tests flag timing for upper-left corner, upper-right corner, +; lower-right corner, and time flag is cleared (at end of VBL). +; Depends on proper PPU frame length (less than 29781 CPU clocks). + + .include "prefix_sprite_hit.a" + +test_name: + .db "SPRITE HIT TIMING",0 + .code + +reset: + jsr begin_sprite_hit_tests + + lda #solid_tile + jsr fill_nametable + + lda #0 + sta sprite_attr + lda #solid_tile + sta sprite_tile + + ldx #0 + ldy #0 + jsr set_sprite_xy + lda #2;) Upper-left corner + ldx #$1e + jsr begin_sprite_hit_timing + ldy #3 ; 1943 delay + lda #127 + jsr delay_ya3 + ldx $2002 + ldy $2002 + jsr check_sprite_hit_timing + + ldx #254 + ldy #0 + jsr set_sprite_xy + lda #4;) Upper-right corner + ldx #$1e + jsr begin_sprite_hit_timing + ldy #5 ; 2027 delay + lda #79 + jsr delay_ya5 + ldx $2002 + ldy $2002 + jsr check_sprite_hit_timing + + ldx #0 + ldy #238 + jsr set_sprite_xy + lda #6;) Lower-left corner + ldx #$1e + jsr begin_sprite_hit_timing + ldy #111 ; 28995 delay + lda #51 + jsr delay_ya7 + ldx $2002 + ldy $2002 + jsr check_sprite_hit_timing + + ldx #0 + ldy #0 + jsr set_sprite_xy + lda #8;) Cleared at end of VBL + ldx #$1e + jsr begin_sprite_hit_timing + ldy #60 ; 29780 delay + lda #98 + jsr delay_ya3 + ldy #3 ; 1715 delay + lda #112 + jsr delay_ya0 + lda $2002 + ldy $2002 + eor #$40 ; invert readings + tax + tya + eor #$40 + tay + jsr check_sprite_hit_timing + + jmp tests_passed diff --git a/sprite_hit_tests_2005.10.05/source/10.timing_order.asm b/sprite_hit_tests_2005.10.05/source/10.timing_order.asm new file mode 100644 index 0000000..0ae5912 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/10.timing_order.asm @@ -0,0 +1,61 @@ +; Tests sprite 0 hit timing for which pixel it first reports hit on. +; Each test hits at the same location on screen, though different +; relative to the position of the sprite. + + .include "prefix_sprite_hit.a" + +test_name: + .db "SPRITE HIT ORDER",0 + .code + +test_hit_time: + jsr set_sprite_xy + ldx #$18 + jsr begin_sprite_hit_timing + ldy #88 ; 15511 delay + lda #34 + jsr delay_ya6 + ldx $2002 ; timing really tight here + ldy $2002 + jsr check_sprite_hit_timing + rts + .code + +reset: + jsr begin_sprite_hit_tests + + ; Solid tile in middle of screen + lda #blank_tile + jsr fill_nametable + lda #$21 + ldx #$f0 + jsr set_vaddr + lda #solid_tile + sta $2007 + + lda #0 + sta sprite_attr + lda #solid_tile + sta sprite_tile + + lda #2;) Upper-left corner + ldx #128 + ldy #119 + jsr test_hit_time + + lda #4;) Upper-right corner + ldx #123 + ldy #119 + jsr test_hit_time + + lda #6;) Lower-left corner + ldx #128 + ldy #114 + jsr test_hit_time + + lda #8;) Lower-right corner + ldx #123 + ldy #114 + jsr test_hit_time + + jmp tests_passed diff --git a/sprite_hit_tests_2005.10.05/source/11.edge_timing.asm b/sprite_hit_tests_2005.10.05/source/11.edge_timing.asm new file mode 100644 index 0000000..5d07ba2 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/11.edge_timing.asm @@ -0,0 +1,62 @@ +; Tests sprite 0 hit timing for which pixel it first reports hit on +; when some pixels are under clip, or at or beyond right edge. + + .include "prefix_sprite_hit.a" + +test_name: + .db "SPRITE HIT EDGE TIMING",0 + .code + +reset: + jsr begin_sprite_hit_tests + + lda #solid_tile + jsr fill_nametable + + lda #120 + sta sprite_y + lda #two_corners_tile + sta sprite_tile + lda #0 + sta sprite_attr + + lda #7 + sta sprite_x + lda #2;) Hit time shouldn't be based on pixels under left clip + ldx #$18 + jsr begin_sprite_hit_timing + ldy #27 ; 16380 delay + lda #120 + jsr delay_ya1 + lda $2002 + and #$40 + jsr error_if_ne + + lda #$80 + sta sprite_attr + + lda #248 + sta sprite_x + lda #3;) Hit time shouldn't be based on pixels at X=255 + ldx #$18 + jsr begin_sprite_hit_timing + ldy #41 ; 16458 delay + lda #79 + jsr delay_ya0 + lda $2002 + and #$40 + jsr error_if_ne + + lda #249 + sta sprite_x + lda #4;) Hit time shouldn't be based on pixels off right edge + ldx #$18 + jsr begin_sprite_hit_timing + ldy #41 ; 16458 delay + lda #79 + jsr delay_ya0 + lda $2002 + and #$40 + jsr error_if_ne + + jmp tests_passed diff --git a/sprite_hit_tests_2005.10.05/source/prefix_sprite_hit.a b/sprite_hit_tests_2005.10.05/source/prefix_sprite_hit.a new file mode 100644 index 0000000..eca5a61 --- /dev/null +++ b/sprite_hit_tests_2005.10.05/source/prefix_sprite_hit.a @@ -0,0 +1,178 @@ + .include "prefix_ppu.a" + +sprites = $200 +sprite_y = $200 +sprite_tile = $201 +sprite_attr = $202 +sprite_x = $203 + +; Clear sprite table +clear_sprite_table: + lda #$f8 + ldx #0 +: sta sprites,x + inx + bne - + rts + .code + +; Use DMA to copy sprite table to PPU +; Preserved: A, X, Y +dma_sprite_table: + pha + lda #0 + sta $2003 + lda #$02 + sta $4014 + pla + rts + .code + +; Set sprite 0 X and Y in table +; Preserved: A, X, Y +set_sprite_xy: + stx sprite_x + sty sprite_y + rts + .code + +; Set sprite 0 X and Y in PPU +; Preserved: A, X, Y +set_sprite_xy_ppu: + pha + lda #0 + sta $2003 + sty $2004 + lda #3 + sta $2003 + stx $2004 + pla + rts + .code + +tiles: + ; 0-3 filled, color 0, 1/2, 2/1, 3 (not sure which of the 1/2) +blank_tile = 0 + .db $00,$00,$00,$00,$00,$00,$00,$00 ; 0 + .db $00,$00,$00,$00,$00,$00,$00,$00 + + .db $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + .db $00,$00,$00,$00,$00,$00,$00,$00 + + .db $00,$00,$00,$00,$00,$00,$00,$00 + .db $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + +solid_tile = 3 + .db $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + .db $ff,$ff,$ff,$ff,$ff,$ff,$ff,$ff + + ; 4-7 upper-left, upper-right, lower-left, lower-right +upper_left_tile = 4 + .db $80,$00,$00,$00,$00,$00,$00,$00 + .db $80,$00,$00,$00,$00,$00,$00,$00 + +upper_right_tile = 5 + .db $01,$00,$00,$00,$00,$00,$00,$00 + .db $01,$00,$00,$00,$00,$00,$00,$00 + +lower_left_tile = 6 + .db $00,$00,$00,$00,$00,$00,$00,$80 + .db $00,$00,$00,$00,$00,$00,$00,$80 + +lower_right_tile = 7 + .db $00,$00,$00,$00,$00,$00,$00,$01 + .db $00,$00,$00,$00,$00,$00,$00,$01 + +two_corners_tile = 8 + .db $80,$00,$00,$00,$00,$00,$00,$01 + .db $80,$00,$00,$00,$00,$00,$00,$01 + + .code + +begin_sprite_hit_tests: + jsr begin_ppu_test + jsr clear_palette + jsr clear_sprite_table + lda #9 + jsr load_tiles + lda #blank_tile + jsr fill_nametable + rts + .code + +test_for_hit: + sta diff --git a/sprite_overflow_tests/source/1.Basics.a b/sprite_overflow_tests/source/1.Basics.a new file mode 100644 index 0000000..bc2e757 --- /dev/null +++ b/sprite_overflow_tests/source/1.Basics.a @@ -0,0 +1,67 @@ +; Tests basic operation of sprite overflow flag (bit 5 of $2002). + + .include "prefix.a" + +test_name: + .db "SPRITE OVERFLOW BASICS",0 + .code + +reset: + jsr begin_sprite_overflow_tests + + ; move first 9 sprites to Y = 128 + jsr clear_sprite_table + lda #128 + ldx #0 + ldy #9 + jsr move_sprites + + ; read $2002 at various times + jsr wait_vbl + lda #$18 + sta $2001 + jsr dma_sprite_table + ldy #208 + jsr delay_y_scanlines + + lda #2;) Should be set when 9 sprites are on a scanline + sta result + lda $2002 ; raw scanline 21 + 128 + 64 + and #$20 + jsr error_if_eq + + lda #3;) Reading $2002 shouldn't clear flag + sta result + lda $2002 + and #$20 + jsr error_if_eq + ldy #59 + jsr delay_y_scanlines + + lda #4;) Shouldn't be cleared at the beginning of VBL + sta result + lda $2002 ; raw scanline 10 of next frame + and #$20 + jsr error_if_eq + ldy #20 + jsr delay_y_scanlines + + lda #5;) Should be cleared at the end of VBL + sta result + lda $2002 ; raw scanline 30 of next frame + and #$20 + jsr error_if_ne + + lda #6;) Shouldn't be set when all rendering is off + ldx #$00 + jsr sprites_should_not_overflow + + lda #7;) Should work normally when $2001 = $08 (bg rendering only) + ldx #$08 + jsr sprites_should_overflow + + lda #8;) Should work normally when $2001 = $10 (sprite rendering only) + ldx #$10 + jsr sprites_should_overflow + + jmp tests_passed diff --git a/sprite_overflow_tests/source/2.Details.a b/sprite_overflow_tests/source/2.Details.a new file mode 100644 index 0000000..2e35547 --- /dev/null +++ b/sprite_overflow_tests/source/2.Details.a @@ -0,0 +1,108 @@ +; Tests details of sprite overflow flag + + .include "prefix.a" + +test_name: + .db "SPRITE OVERFLOW DETAILS",0 + .code + +reset: + jsr begin_sprite_overflow_tests + + ; Move first 9 sprites to Y = 128, X = 0 + jsr clear_sprite_table + lda #128 + ldx #0 + ldy #9 + jsr move_sprites + lda #0 + ldx #3 + ldy #9 + jsr move_sprites + lda #2;) Should be set even when sprites are under left clip (X = 0) + ldx #$18 + jsr sprites_should_overflow + + lda #3;) Disabling rendering shouldn't clear flag + ldx #$18 + jsr test_for_overflow + lda #0 + sta $2001 + lda $2002 ; raw scanline 10 of next frame + and #$20 + jsr error_if_eq + lda #4;) Should be cleared at the end of VBL even when rendering is off + ldy #20 + jsr delay_y_scanlines + lda $2002 ; raw scanline 30 of next frame + and #$20 + jsr error_if_ne + + jsr clear_sprite_table + lda #239 + ldx #0 + ldy #9 + jsr move_sprites + lda #5;) Should be set when sprite Y coordinates are 239 + ldx #$18 + jsr sprites_should_overflow + + jsr clear_sprite_table + lda #240 + ldx #0 + ldy #64 + jsr move_sprites + lda #6;) Shouldn't be set when sprite Y coordinates are 240 (off screen) + ldx #$18 + jsr sprites_should_not_overflow + + jsr clear_sprite_table + lda #255 + ldx #0 + ldy #64 + jsr move_sprites + lda #7;) Shouldn't be set when sprite Y coordinates are 255 (off screen) + ldx #$18 + jsr sprites_should_not_overflow + + jsr clear_sprite_table + ldx #0 + ldy #11 + lda #128 + jsr move_sprites + lda #240 + sta sprites + 0 + sta sprites + 8 + lda #8;) Should be set regardless of which sprites are involved + ldx #$18 + jsr sprites_should_overflow + + ldy #0 + lda #0 +: sta sprites,y + clc + adc #1 + iny + iny + iny + iny + bne - + lda #9;) Shouldn't be set when all scanlines have 7 or fewer sprites + ldx #$18 + jsr sprites_should_not_overflow + + jsr clear_sprite_table + ldx #8 + ldy #7 + lda #128 + jsr move_sprites + lda #113 + sta sprites + 0 + sta sprites + 4 + lda #10;) Double-height sprites aren't handled properly + ldy #$20 + sty $2000 + ldx #$18 + jsr sprites_should_overflow + + jmp tests_passed diff --git a/sprite_overflow_tests/source/3.Timing.a b/sprite_overflow_tests/source/3.Timing.a new file mode 100644 index 0000000..836b2c3 --- /dev/null +++ b/sprite_overflow_tests/source/3.Timing.a @@ -0,0 +1,142 @@ +; Tests sprite overflow flag timing + + .include "prefix.a" + +test_name: + .db "SPRITE OVERFLOW TIMING",0 + .code + +begin_timing: + sta result + jsr sync_ppu_20 + lda #$18 + sta $2001 + jsr dma_sprite_table + pha ; 14 delay for later expansion + pla + pha + pla + rts + +check_sprite_overflow_timing: + txa + and #$20 + jsr error_if_ne + inc result + tya + and #$20 + jsr error_if_eq + rts + +reset: + jsr begin_sprite_overflow_tests + + lda #128 + ldx #0 + ldy #9 + jsr move_sprites + lda #2;) Cleared too late/3)too early at end of VBL + jsr begin_timing + ldy #61 ; 31497 delay + lda #102 + jsr delay_ya4 + ldy $2002 + ldx $2002 + jsr check_sprite_overflow_timing + + jsr clear_sprite_table + lda #0 + ldx #0 + ldy #9 + jsr move_sprites + lda #0 + ldx #3 + ldy #9 + jsr move_sprites + lda #4;) Set too early/5)too late for first scanline + jsr begin_timing + ldy #2 ; 1874 delay (1873 and 1875 work reliably, + lda #184 ; 1872 and 1876 fail part of the time) + jsr delay_ya5 + ldx $2002 + ldy $2002 + jsr check_sprite_overflow_timing + + lda #255 + ldx #3 + ldy #9 + jsr move_sprites + lda #6;) Sprite horizontal positions should have no effect on timing + jsr begin_timing + ldy #2 ; 1874 delay (1873 and 1875 work reliably, + lda #184 ; 1872 and 1876 fail part of the time) + jsr delay_ya5 + lda $2002 + ldx $2002 + and #$20 + jsr error_if_ne + txa + and #$20 + jsr error_if_eq + + jsr clear_sprite_table + lda #0 + ldx #220 + ldy #9 + jsr move_sprites + lda #7;) Set too early/ 8)late for last sprites on first scanline + jsr begin_timing + ldy #8 ; 1911 delay (1909 fails often and 1912 fails occasionally) + lda #46 + jsr delay_ya6 + ldx $2002 + ldy $2002 + jsr check_sprite_overflow_timing + + jsr clear_sprite_table + lda #239 + ldx #0 + ldy #9 + jsr move_sprites + lda #9;) Set too early/10)too late for last scanline + jsr begin_timing + ldy #26 ; 29040 delay (29038 and 29042 fail sometimes) + lda #222 + jsr delay_ya7 + ldx $2002 + ldy $2002 + jsr check_sprite_overflow_timing + + jsr clear_sprite_table + lda #0 + ldx #0 + ldy #8 + jsr move_sprites + lda #0 + sta sprites + 255 + lda #11;) Set too early/12)too late when 9th sprite # is way after 8th + jsr begin_timing + ldy #8 ; 1911 delay (1909 fails often and 1912 fails occasionally) + lda #46 + jsr delay_ya6 + ldx $2002 + ldy $2002 + jsr check_sprite_overflow_timing + + jsr clear_sprite_table + lda #0 + ldx #4 + ldy #8 + jsr move_sprites + lda #1 + sta sprites + 0 + lda #13;) Overflow on second scanline occurs too early/14)too late + jsr begin_timing + ldy #3 ; 1987 delay (1986 & 1988 work too) + lda #130 + jsr delay_ya2 + ldx $2002 + ldy $2002 + jsr check_sprite_overflow_timing + + jmp tests_passed diff --git a/sprite_overflow_tests/source/4.Obscure.a b/sprite_overflow_tests/source/4.Obscure.a new file mode 100644 index 0000000..9a77eb7 --- /dev/null +++ b/sprite_overflow_tests/source/4.Obscure.a @@ -0,0 +1,144 @@ +; Tests the pathological behavior when 8 sprites are on a scanline +; and the one just after the 8th is not on the scanline. After that, +; the PPU interprets different bytes of each following sprite as +; its Y coordinate. 1 2 3 4 5 6 7 8 9 10 11 12 13 14: If 1-8 are +; on the same scanline, 9 isn't, then the second byte of 10, the +; third byte of 11, fourth byte of 12, first byte of 13, second byte +; of 14, etc. are treated as those sprites' Y coordinates for the +; purpose of setting the overflow flag. This search continues until +; all sprites have been scanned or one of the (erroneously interpreted) +; Y coordinates places the sprite within the scanline. + + .include "prefix.a" + +test_name: + .db "SPRITE OVERFLOW OBSCURE",0 + .code + +reset: + jsr begin_sprite_overflow_tests + + jsr clear_sprite_table + lda #2;) Checks that second byte of sprite #10 is treated as its Y + ldx #128 + stx sprites + 0 + stx sprites + 4 + stx sprites + 8 + stx sprites + 12 + stx sprites + 16 + stx sprites + 20 + stx sprites + 24 + stx sprites + 28 + ; leave sprites + 32 off screen + stx sprites + 37 + ldx #$18 + jsr sprites_should_overflow + + jsr clear_sprite_table + lda #3;) Checks that third byte of sprite #11 is treated as its Y + ldx #128 + stx sprites + 0 + stx sprites + 4 + stx sprites + 8 + stx sprites + 12 + stx sprites + 16 + stx sprites + 20 + stx sprites + 24 + stx sprites + 28 + ; leave sprites + 32 off screen + ; leave sprites + 37 off screen + stx sprites + 42 + ldx #$18 + jsr sprites_should_overflow + + jsr clear_sprite_table + lda #4;) Checks that fourth byte of sprite #12 is treated as its Y + ldx #128 + stx sprites + 0 + stx sprites + 4 + stx sprites + 8 + stx sprites + 12 + stx sprites + 16 + stx sprites + 20 + stx sprites + 24 + stx sprites + 28 + ; leave sprites + 32 off screen + ; leave sprites + 37 off screen + ; leave sprites + 42 off screen + stx sprites + 47 + ldx #$18 + jsr sprites_should_overflow + + jsr clear_sprite_table + lda #5;) Checks that first byte of sprite #13 is treated as its Y + ldx #128 + stx sprites + 0 + stx sprites + 4 + stx sprites + 8 + stx sprites + 12 + stx sprites + 16 + stx sprites + 20 + stx sprites + 24 + stx sprites + 28 + ; leave sprites + 32 off screen + ; leave sprites + 37 off screen + ; leave sprites + 42 off screen + ; leave sprites + 47 off screen + stx sprites + 48 + ldx #$18 + jsr sprites_should_overflow + + jsr clear_sprite_table + lda #6;) Checks that second byte of sprite #14 is treated as its Y + ldx #128 + stx sprites + 0 + stx sprites + 4 + stx sprites + 8 + stx sprites + 12 + stx sprites + 16 + stx sprites + 20 + stx sprites + 24 + stx sprites + 28 + ; leave sprites + 32 off screen + ; leave sprites + 37 off screen + ; leave sprites + 42 off screen + ; leave sprites + 47 off screen + ; leave sprites + 48 off screen + stx sprites + 53 + ldx #$18 + jsr sprites_should_overflow + + jsr clear_sprite_table + lda #7;) Checks that search stops at the last (64th) sprite + ldx #128 + stx sprites + 1 ; if search erroneously wraps, + stx sprites + 2 ; it will grab one of these + stx sprites + 3 + stx sprites + 8 + stx sprites + 12 + stx sprites + 16 + stx sprites + 20 + stx sprites + 24 + stx sprites + 28 + stx sprites + 32 + stx sprites + 36 + ldx #$18 + jsr sprites_should_not_overflow + + jsr clear_sprite_table + lda #8;) Same as test #2 but using a different range of sprites + ldx #128 + stx sprites + 4 + stx sprites + 8 + stx sprites + 12 + stx sprites + 16 + stx sprites + 20 + stx sprites + 24 + stx sprites + 28 + stx sprites + 32 + ; leave sprites + 36 off screen + stx sprites + 41 + ldx #$18 + jsr sprites_should_overflow + + jmp tests_passed diff --git a/sprite_overflow_tests/source/5.Emulator.a b/sprite_overflow_tests/source/5.Emulator.a new file mode 100644 index 0000000..dbc6881 --- /dev/null +++ b/sprite_overflow_tests/source/5.Emulator.a @@ -0,0 +1,126 @@ +; Tests things that an optimized emulator is likely get wrong + + .include "prefix.a" + +test_name: + .db "SPRITE OVERFLOW EMULATION",0 + .code + +reset: + jsr begin_sprite_overflow_tests + + jsr clear_sprite_table + lda #128 + ldx #0 + ldy #9 + jsr move_sprites + lda #2;) Didn't calculate overflow when no $2002 read for frame + ldx #$18 + jsr begin_overflow_test + ldy #16 + jsr delay_y_scanlines + ldy #240 + jsr delay_y_scanlines + ldy #10 + jsr delay_y_scanlines + lda $2002 + and #$20 + jsr error_if_eq + + lda #3;) Disabling rendering didn't recalculate flag time + sta result + jsr clear_sprite_table + lda #127 ; 9 sprites at 127, 9 at 230 + ldx #0 + ldy #9 + jsr move_sprites + lda #230 + ldy #9 + jsr move_sprites + jsr wait_vbl + lda #$18 ; enable rendering + sta $2001 + jsr dma_sprite_table ; 4.5 scanlines + ldy #21 + jsr delay_y_scanlines + ldy #121 + jsr delay_y_scanlines + lda $2002 ; have emulator think it'll occur next + lda #$00 ; disable rendering + sta $2001 + ldy #12 + jsr delay_y_scanlines + lda #$18 ; enable rendering + sta $2001 + ldy #92 + jsr delay_y_scanlines + lda $2002 + pha + ldy #4 + jsr delay_y_scanlines + ldx $2002 + pla + and #$20 + jsr error_if_ne + txa + and #$20 + jsr error_if_eq + + lda #4;) Changing sprite RAM didn't recalculate flag time + sta result + lda #248 + ldx #0 + ldy #2 + jsr move_sprites + jsr wait_vbl + lda #$18 ; enable rendering + sta $2001 + ldy #142 + jsr delay_y_scanlines + lda $2002 ; have emulator think it'll occur next + lda #$00 ; disable rendering + sta $2001 + jsr dma_sprite_table + lda #$18 ; enable rendering + sta $2001 + ldy #104 + jsr delay_y_scanlines + lda $2002 + pha + ldy #4 + jsr delay_y_scanlines + ldx $2002 + pla + and #$20 + jsr error_if_ne + txa + and #$20 + jsr error_if_eq + + jsr clear_sprite_table + lda #100 + ldx #0 + ldy #7 + jsr move_sprites + lda #115 + ldy #2 + jsr move_sprites + lda #200 + ldy #9 + jsr move_sprites + lda #5;) Changing sprite height didn't recalculate time + ldx #$18 + jsr begin_overflow_test + ldy #66 + jsr delay_y_scanlines + lda $2002 ; have emulator think it'll occur at 200 + lda #$20 ; change sprite height so it'll occur at 115 + sta $2000 + ldy #100 + jsr delay_y_scanlines + lda $2002 + and #$20 + jsr error_if_eq + + jmp tests_passed + diff --git a/sprite_overflow_tests/source/console.a b/sprite_overflow_tests/source/console.a new file mode 100644 index 0000000..946b07f --- /dev/null +++ b/sprite_overflow_tests/source/console.a @@ -0,0 +1,107 @@ + +console_pos = $7f2 +console_pos_h = $7f3 + +; Print char A to console +; Preserved: A, X, Y +print_char: + jsr wait_vbl ; wait for safe access +print_char_no_wait: + pha + lda console_pos_h + sta $2006 + inc console_pos + lda console_pos + sta $2006 + lda #0 ; restore scroll + sta $2005 + sta $2005 + pla + sta $2007 + rts + .code + +; Go to next line +; Preserved: A, X, Y +console_newline: + pha + lda console_pos + and #$e0 + clc + adc #$21 + sta console_pos + lda console_pos_h + adc #0 + sta console_pos_h + pla + rts + .code + +; Initialize console +init_console: + lda #$81 + sta console_pos + lda #$20 + sta console_pos_h + + jsr wait_vbl ; init ppu + lda #0 + sta $2000 + sta $2001 + + lda #$3f ; load palette + jsr set_vpage + lda #15 ; bg + ldx #48 ; fg + ldy #8 +pal: sta $2007 + stx $2007 + stx $2007 + stx $2007 + dey + bne pal + + lda #$02 ; load tiles + jsr set_vpage + lda #chr_data.lsb + sta <$f0 + lda #chr_data.msb + sta <$f1 + ldy #0 + lda #59 ; 59 chars in data + sta <$f2 +chr_loop: + ldx #8 + lda #0 +: sta $2007 + dex + bne - + + ldx #8 +: lda ($f0),y + iny + sta $2007 + dex + bne - + + tya + bne + + inc <$f1 +: dec <$f2 + bne chr_loop + + lda #32 + jsr fill_nametable + + jsr wait_vbl ; enable ppu + lda #0 + sta $2005 + sta $2005 + lda #$0a + sta $2001 + rts + .code + +chr_data: + .incbin "chr.bin" + diff --git a/sprite_overflow_tests/source/debug.a b/sprite_overflow_tests/source/debug.a new file mode 100644 index 0000000..fff98dc --- /dev/null +++ b/sprite_overflow_tests/source/debug.a @@ -0,0 +1,154 @@ + +; Beep A times. Made to require minimal features from APU. +debug_beeps: +beep_loop: + pha + + lda #1 ; set up square 1 + sta $4015 + sta $4003 + sta $4001 + sta $4002 + + lda #$0f ; fade volume +: pha + eor #$30 + sta $4000 + lda #8 + jsr delay_msec + pla + clc + adc #-1 + bpl - + + lda #0 + sta $4015 ; silence square for a bit + lda #120 + jsr delay_msec + + pla + clc + adc #-1 + bne beep_loop + rts + .code + +; Print indicated register to console as $hh +; Preserved: A, X, Y, P +print_a: + php + jsr debug_byte + plp + rts + .code + +print_x: + php + pha + txa + jsr debug_byte + pla + plp + rts + .code + +print_y: + php + pha + tya + jsr debug_byte + pla + plp + rts + .code + +print_s: + php + pha + txa + pha + tsx + txa + jsr debug_byte + pla + tax + pla + plp + rts + .code + +print_p: + php + pha + php + pla + jsr debug_byte + pla + plp + rts + .code + +print_ay: + php + pha + lda #36 + jsr debug_char + pla + pha + jsr hex_byte + tya + jsr hex_byte + lda #32 ; ' ' + jsr debug_char_no_wait + pla + plp + rts + .code + +print_newline: + jmp debug_newline + .code + +; Print address YA to console as $hhhh +; Preserved: A, X, Y +debug_addr: + pha + lda #36 ; '$' + jsr debug_char + tya + jsr hex_byte + jmp debug_byte_impl + .code + +; Print byte A to console as $hh +; Preserved: A, X, Y +debug_byte: + pha + lda #36 ; '$' + jsr debug_char +debug_byte_impl: + pla + pha + jsr hex_byte + lda #32 ; ' ' + jsr debug_char_no_wait + pla + rts + +hex_byte: + pha + lsr a + lsr a + lsr a + lsr a + jsr nybble + pla + and #$0f +nybble: + cmp #10 + bcc not_letter + adc #6 ; relies on carry being set +not_letter: + adc #$30 + jmp debug_char_no_wait + .code diff --git a/sprite_overflow_tests/source/delays.a b/sprite_overflow_tests/source/delays.a new file mode 100644 index 0000000..72d62c8 --- /dev/null +++ b/sprite_overflow_tests/source/delays.a @@ -0,0 +1,118 @@ +; to do: delay loops that take only a single count + +; Delay for almost A milliseconds (A * 0.999009524 msec) +; Preserved: X, Y +delay_msec: + pha ; 3 + lda #253 ; 2 + sec ; 2 +delay_msec_: + nop ; 2 + adc #-2 ; 2 + bne delay_msec_ ; 3 + ; -1 + pla ; 4 + clc ; 2 + adc #-1 ; 2 + bne delay_msec ; 3 + rts + .code + +; Delay for almost 'A / 10' milliseconds (A * 0.099453968 msec) +; Preserved: X, Y +delay_01msec: + pha ; 3 + lda #18 ; 2 + sec ; 2 +delay_01msec_: + nop ; 2 + nop ; 2 + adc #-2 ; 2 + bne delay_01msec_ ; 3 + ; -1 + pla ; 4 + clc ; 2 + adc #-1 ; 2 + bne delay_01msec ; 3 + rts + .code + +; Delay for almost A*10 milliseconds +; Preserved: X, Y +delay_10msec: + pha + lda #10 + jsr delay_msec + pla + clc + adc #-1 + bne delay_10msec + rts + .code + +; Delay n clocks +; Preserved: P, A, X, Y +delay_32: nop +delay_30: nop +delay_28: nop +delay_26: nop +delay_24: nop +delay_22: nop +delay_20: nop +delay_18: nop +delay_16: nop +delay_14: nop +delay_12: rts + +delay_33: nop +delay_31: nop +delay_29: nop +delay_27: nop +delay_25: nop +delay_23: nop +delay_21: nop +delay_19: nop +delay_17: beq + ; 5 +: bne + +: rts ; 6 + .code + +; Delay (5 * A + 6) * Y + 7 + n clocks +delay_ya11: + .db $a2 ; 1 ldx #imm +delay_ya10: + .db $a2 ; 1 ldx #imm +delay_ya9: + .db $a2 ; 1 ldx #imm +delay_ya8: + .db $a2 ; 1 ldx #imm +delay_ya7: + .db $a2 ; 1 ldx #imm +delay_ya6: + .db $a2 ; 1 ldx #imm +delay_ya5: + .db $a2 ; 1 ldx #imm +delay_ya4: + .db $a2 ; 1 ldx #imm +delay_ya3: + .db $a2 ; 1 ldx #imm +delay_ya2: + .db $a2 ; 1 ldx #imm +delay_ya1: + .db $a6 ; 1 ldx zp +delay_ya0: + nop ; 2 +delay_ya: + ; 2 lda # + ; 2 ldy # + ; 6 jsr + tax ; *2 +delay_yax: + dex ; **2 + bne delay_yax ; **3 + ; *-1 + dey ; *2 + bne delay_ya ; *3 + ; -1 + rts ; 6 + .code diff --git a/sprite_overflow_tests/source/ppu_sync.a b/sprite_overflow_tests/source/ppu_sync.a new file mode 100644 index 0000000..3c29d05 --- /dev/null +++ b/sprite_overflow_tests/source/ppu_sync.a @@ -0,0 +1,124 @@ + +; Same as sync_ppu_20 plus next frame is odd (clock subtracted) or even +; (no clock subtracted). +sync_ppu_odd_20: + lda #$80 + bne sync_ppu_frame_ +sync_ppu_even_20: + lda #$00 +sync_ppu_frame_: + pha + ; Synchronize with PPU + jsr sync_ppu_20 ; synchronize with PPU + + ; Run for two frames with BG enabled. One of the two frames will + ; be one PPU clock shorter. Note whether the first frame was the + ; shorter one. + + lda #$08 ; 6 enable bg + sta $2001 + ldy #41 ; 29785 delay + lda #144 + jsr delay_ya2 + nop ; 2 delay + pla ; 4 + eor $2002 ; 4 find whether frame was odd or even + pha ; 3 + ; run another enabled frame so that clock will + ; have been subtracted on one of the two frames + ldy #43 ; 29730+ delay + lda #137 + jsr delay_ya1 + lda #$00 ; 6 disable bg + sta $2001 + + ; If the first frame was shorter, wait three frames to switch + ; the even/odd synchronization without changing the CPU clock's + ; synchronization with the PPU clock. + + pla ; 4 + pha ; 3 + bpl + ; 3 + ; -1 + ldy #75 ; 89343 delay + lda #237 + jsr delay_ya1 +: pla ; 4 + rts ; 6 + .code + +; After return, 30 clocks until VBL flag will read +; as set, then 29781, 29780, 29781, 29781, 29780, etc. +; Turns off PPU rendering, NMI, IRQ, and DMC. +sync_ppu_align2_30: + pha + txa + pha + tya + pha + lda #0 ; disable dmc and irq + sta $4015 + sei + jsr wait_vbl + lda #0 ; disable bg and nmi + sta $2000 + sta $2001 + + bit $2002 +: bit $2002 ; 1 + bpl - ; 2 + + ldy #141 ; 29774 delay + lda #41 + jsr delay_ya6 + +: ldy #86 ; 29774 delay + lda #68 + jsr delay_ya1 + + bit $2002 ; 1 + bpl - ; 2 + + ldy #28 ; 29726 delay + lda #211 + jsr delay_ya1 + + pla ; 16 + tay + pla + tax + pla + + rts ; 6 + .code + +sync_ppu_align1_30: + jsr sync_ppu_align2_30 + ldy #86 ; 29775 delay + lda #68 + jsr delay_ya2 + rts + .code + +sync_ppu_align1_31: + jsr sync_ppu_align2_30 + ldy #86 ; 29774 delay + lda #68 + jsr delay_ya1 + rts + .code + +sync_ppu_align0_30: + jsr sync_ppu_align1_30 + ldy #86 ; 29774 delay + lda #68 + jsr delay_ya1 + rts + .code + +sync_ppu_20: + jsr sync_ppu_align2_30 + nop ; 4 + nop + rts ; 6 + .code diff --git a/sprite_overflow_tests/source/ppu_util.a b/sprite_overflow_tests/source/ppu_util.a new file mode 100644 index 0000000..f004227 --- /dev/null +++ b/sprite_overflow_tests/source/ppu_util.a @@ -0,0 +1,123 @@ + +; Clear VBL flag then wait for it to be set +; Preserved: A, X, Y +wait_vbl: + bit $2002 +: bit $2002 + bpl - + rts + .code + +; Set VRAM address to A * $100 +; Preserved: X, Y +set_vpage: + bit $2002 + sta $2006 + lda #0 + sta $2006 + rts + .code + +; Set VRAM address to A * $100 + X +; Preserved: A, X, Y +set_vaddr: + bit $2002 + sta $2006 + stx $2006 + rts + .code + +; Set X and Y scroll +; Preserved: A, X, Y +set_vscroll: + bit $2002 + stx $2005 + sty $2005 + rts + .code + +; Turn off NMI and disable BG and sprites +; Preserved: A, X, Y +disable_ppu: + pha + lda #0 + sta $2000 + sta $2001 + bit $2002 + sta $2006 + sta $2006 + pla + rts + .code + +; Set sprite memory to $ff +; Preserved: Y +clear_sprites: + lda #$ff + ldx #0 +: sta $2004 + dex + bne - + rts + .code + +; Clear/fill nametable with 0/A and clear attributes to 0 +clear_nametable: + lda #0 +fill_nametable: + pha + lda #$20 + jsr set_vpage + pla + ldx #240 +: sta $2007 + sta $2007 + sta $2007 + sta $2007 + dex + bne - + lda #0 + ldx #64 +: sta $2007 + dex + bne - + rts + .code + +; Clear/fill VRAM with 0/A +clear_vram: + lda #0 +fill_vram: + ldx #0 + ldy #$24 + bne fill_vram_ + +; Clear/fill CHR with 0/A +fill_chr1: + ldx #$10 + ldy #$10 + bne fill_vram_ + +fill_chr0: + ldx #0 + ldy #$10 + bne fill_vram_ + +clear_chr: + lda #0 +fill_chr: + ldx #0 + ldy #$20 +; Fill VRAM Y*$100 bytes of VRAM with A, starting at X*$100 +fill_vram_: + bit $2002 + stx $2006 + ldx #0 + stx $2006 +: sta $2007 + dex + bne - + dey + bne - + rts + .code diff --git a/sprite_overflow_tests/source/prefix.a b/sprite_overflow_tests/source/prefix.a new file mode 100644 index 0000000..fe6d74e --- /dev/null +++ b/sprite_overflow_tests/source/prefix.a @@ -0,0 +1,145 @@ + .include "prefix_ppu.a" + +sprites = $200 + +; Clear sprite table with $F8 +clear_sprite_table: + lda #$F8 + ldx #0 +: sta sprites,x + inx + bne - + rts + .code + +; Set every 4th sprite byte starting at X +; to A, modifying Y sprites in all. +move_sprites: +: sta sprites,x + inx + inx + inx + inx + dey + bne - + rts + +; Use DMA to copy sprite table to PPU +; Preserved: A, X, Y +dma_sprite_table: + pha + lda #0 + sta $2003 + lda #$02 + sta $4014 + pla + rts + .code + +begin_sprite_overflow_tests: + sei + lda #0 + sta $2000 + sta $2001 + jsr wait_vbl + jsr wait_vbl + + ; black palette + lda #$3F + jsr set_vpage + ldy #$40 + lda #$0F +: sta $2007 + dey + bne - + + jsr clear_sprite_table + rts + .code + +begin_overflow_test: + sta $@ + +$(objdir)/%.o: $(srcdir)/%.s + $(AS65) $(CFLAGS65) $< -o $@ + +$(objdir)/%.o: $(objdir)/%.s + $(AS65) $(CFLAGS65) $< -o $@ + +map.txt spritecans.prg: nes.ini $(objlistntsc) + $(LD65) -C $^ -m map.txt -o spritecans.prg + +spritecans.chr: $(imgdir)/main.png + tools/pilbmp2nes.py $< $@ + +$(objdir)/ntscPeriods.s: tools/mktables.py + $< period $@ + +%.nes: %.prg %.chr + cat $^ > $@ diff --git a/spritecans-2011/nes.ini b/spritecans-2011/nes.ini new file mode 100644 index 0000000..5fe8bed --- /dev/null +++ b/spritecans-2011/nes.ini @@ -0,0 +1,31 @@ +# +# Linker script for Thwaite (lite version) +# Copyright 2010 Damian Yerrick +# +# Copying and distribution of this file, with or without +# modification, are permitted in any medium without royalty +# provided the copyright notice and this notice are preserved. +# This file is offered as-is, without any warranty. +# +MEMORY { + ZP: start = $10, size = $f0, type = rw; + # use first $10 zeropage locations as locals + HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00; + RAM: start = $0300, size = $0500, type = rw; + ROM7: start = $C000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF; +} + +SEGMENTS { + INESHDR: load = HEADER, type = ro, align = $10; + ZEROPAGE: load = ZP, type = zp; + BSS: load = RAM, type = bss, define = yes, align = $100; + DMC: load = ROM7, type = ro, align = 64, optional = yes; + CODE: load = ROM7, type = ro, align = $100; + RODATA: load = ROM7, type = ro, align = $100; + VECTORS: load = ROM7, type = ro, start = $FFFA; +} + +FILES { + %O: format = bin; +} + diff --git a/spritecans-2011/obj/nes/index.txt b/spritecans-2011/obj/nes/index.txt new file mode 100644 index 0000000..109fef6 --- /dev/null +++ b/spritecans-2011/obj/nes/index.txt @@ -0,0 +1 @@ +Files produced by build tools go here, but caulk goes where? diff --git a/spritecans-2011/spritecans.nes b/spritecans-2011/spritecans.nes new file mode 100644 index 0000000..bba9e82 Binary files /dev/null and b/spritecans-2011/spritecans.nes differ diff --git a/spritecans-2011/src/music.s b/spritecans-2011/src/music.s new file mode 100644 index 0000000..a0c4e33 --- /dev/null +++ b/spritecans-2011/src/music.s @@ -0,0 +1,381 @@ +; music.s +; part of sound engine for LJ65 + +;;; Copyright (C) 2009-2011 Damian Yerrick +; +; This program is free software; you can redistribute it and/or +; modify it under the terms of the GNU General Public License +; as published by the Free Software Foundation; either version 3 +; of the License, or (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program; if not, write to +; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +; Boston, MA 02111-1307, USA. +; +; Visit http://www.pineight.com/ for more information. + +.importzp psg_sfx_state +.import soundBSS +.import start_sound +.importzp tvSystem +.export music_playing +.export init_music, stop_music, update_music, update_music_ch +.include "src/musicseq.h" + +musicPatternPos = psg_sfx_state + 2 +conductorPos = psg_sfx_state + 16 +noteEnvVol = soundBSS + 0 +notePitch = soundBSS + 1 +noteRowsLeft = soundBSS + 2 +; 3 is in sound.s +musicPattern = soundBSS + 16 +patternTranspose = soundBSS + 17 +noteInstrument = soundBSS + 18 +; 19 is in sound.s +tempoCounterLo = soundBSS + 48 +tempoCounterHi = soundBSS + 49 +music_tempoLo = soundBSS + 50 +music_tempoHi = soundBSS + 51 +conductorSegno = soundBSS + 52 + +conductorWaitRows = soundBSS + 62 +music_playing = soundBSS + 63 + +FRAMES_PER_MINUTE_PAL = 3000 +FRAMES_PER_MINUTE_NTSC = 3606 + + +.segment "RODATA" + +fpmLo: + .byt FRAMES_PER_MINUTE_NTSC, >FRAMES_PER_MINUTE_PAL + +silentPattern: + .byt 26*8+7, 255 + +durations: + .byt 1, 2, 3, 4, 6, 8, 12, 16 + +.segment "CODE" +.proc init_music + asl a + tax + lda songTable,x + sta conductorPos + sta conductorSegno + lda songTable+1,x + sta conductorPos+1 + sta conductorSegno+1 + ldx #12 + stx music_playing + channelLoop: + lda #$FF + sta musicPattern,x + lda #silentPattern + sta musicPatternPos+1,x + lda #0 + sta patternTranspose,x + sta noteInstrument,x + sta noteEnvVol,x + sta noteRowsLeft,x + dex + dex + dex + dex + bpl channelLoop + lda #0 + sta conductorWaitRows + lda #$FF + sta tempoCounterLo + sta tempoCounterHi + lda #<300 + sta music_tempoLo + lda #>300 + sta music_tempoHi + rts +.endproc + +.proc stop_music + lda #0 + sta music_playing + rts +.endproc + +.proc update_music + lda music_playing + beq music_not_playing + lda music_tempoLo + clc + adc tempoCounterLo + sta tempoCounterLo + lda music_tempoHi + adc tempoCounterHi + sta tempoCounterHi + bcs new_tick +music_not_playing: + rts +new_tick: + + ldy tvSystem + beq is_ntsc_1 + ldy #1 +is_ntsc_1: + ; Subtract tempo + lda tempoCounterLo + sbc fpmLo,y + sta tempoCounterLo + lda tempoCounterHi + sbc fpmHi,y + sta tempoCounterHi + + ;jmp skipConductor + + lda conductorWaitRows + beq doConductor + dec conductorWaitRows + jmp skipConductor + +doConductor: + + ldy #0 + lda (conductorPos),y + inc conductorPos + bne :+ + inc conductorPos+1 + : + sta 0 + cmp #CON_SETTEMPO + bcc @notTempoChange + and #%00000011 + sta music_tempoHi + + lda (conductorPos),y + inc conductorPos + bne :+ + inc conductorPos+1 + : + sta music_tempoLo + jmp doConductor + @notTempoChange: + cmp #CON_WAITROWS + bcc conductorPlayPattern + beq conductorDoWaitRows + + cmp #CON_FINE + bne @notFine + lda #0 + sta music_playing + sta music_tempoHi + sta music_tempoLo + rts + @notFine: + + cmp #CON_SEGNO + bne @notSegno + lda conductorPos + sta conductorSegno + lda conductorPos+1 + sta conductorSegno+1 + jmp doConductor + @notSegno: + + cmp #CON_DALSEGNO + bne @notDalSegno + lda conductorSegno + sta conductorPos + lda conductorSegno+1 + sta conductorPos+1 + jmp doConductor + @notDalSegno: + + jmp skipConductor + +conductorPlayPattern: + and #$03 + asl a + asl a + tax + lda #0 + sta noteRowsLeft,x + lda (conductorPos),y + sta musicPattern,x + iny + lda (conductorPos),y + sta patternTranspose,x + iny + lda (conductorPos),y + sta noteInstrument,x + tya + sec + adc conductorPos + sta conductorPos + bcc :+ + inc conductorPos+1 + : + jsr startPattern + jmp doConductor + + ; this should be last so it can fall into skipConductor +conductorDoWaitRows: + + lda (conductorPos),y + inc conductorPos + bne :+ + inc conductorPos+1 + : + sta conductorWaitRows + +skipConductor: + + ldx #12 + channelLoop: + lda noteRowsLeft,x + bne skipNote + lda (musicPatternPos,x) + cmp #255 + bne notStartPatternOver + jsr startPattern + lda (musicPatternPos,x) + notStartPatternOver: + + inc musicPatternPos,x + bne patternNotNewPage + inc musicPatternPos+1,x + patternNotNewPage: + + ; set the note's duration + pha + and #$07 + tay + lda durations,y + sta noteRowsLeft,x + pla + lsr a + lsr a + lsr a + cmp #25 + bcc isTransposedNote + beq notKeyOff + lda #0 + sta noteEnvVol,x + notKeyOff: + jmp skipNote + + isTransposedNote: + cpx #12 + beq isDrumNote + adc patternTranspose,x + sta notePitch,x + lda noteInstrument,x + asl a + asl a + tay + lda instrumentTable,y + asl a + asl a + asl a + asl a + ora #$0C + sta noteEnvVol,x + + skipNote: + dec noteRowsLeft,x + dex + dex + dex + dex + bpl channelLoop + + rts + +isDrumNote: + stx 5 + tax + lda drumSFX,x + jsr start_sound + ldx 5 + jmp skipNote + +startPattern: + lda musicPattern,x + asl a + bcc @notSilentPattern + lda #silentPattern + sta musicPatternPos+1,x + rts + @notSilentPattern: + tay + lda musicPatternTable,y + sta musicPatternPos,x + lda musicPatternTable+1,y + sta musicPatternPos+1,x + rts +.endproc + +.proc update_music_ch + ch_number = 0 + out_volume = 2 + out_pitch = 3 + + lda music_playing + beq silenced + lda noteEnvVol,x + lsr a + lsr a + lsr a + lsr a + bne notSilenced +silenced: + lda #0 + sta 2 + rts +notSilenced: + sta 2 + lda noteInstrument,x + asl a + asl a + tay + lda 2 + eor instrumentTable,y + and #$0F + eor instrumentTable,y + sta 2 + lda noteEnvVol,x + sec + sbc instrumentTable+1,y + bcc silenced + sta noteEnvVol,x + lda notePitch,x + sta 3 + + ; bit 7 of attribute 2: cut note when half a row remains + lda instrumentTable+2,y + bpl notCutNote + lda noteRowsLeft,x + bne notCutNote + + clc + lda tempoCounterLo + adc #<(FRAMES_PER_MINUTE_NTSC/2) + lda tempoCounterHi + adc #>(FRAMES_PER_MINUTE_NTSC/2) + bcc notCutNote + lda #0 + sta noteEnvVol,x + +notCutNote: + rts +.endproc + diff --git a/spritecans-2011/src/musicseq.h b/spritecans-2011/src/musicseq.h new file mode 100644 index 0000000..76809ea --- /dev/null +++ b/spritecans-2011/src/musicseq.h @@ -0,0 +1,104 @@ +; Copyright 2010 Damian Yerrick +; +; Copying and distribution of this file, with or without +; modification, are permitted in any medium without royalty +; provided the copyright notice and this notice are preserved. +; This file is offered as-is, without any warranty. + +.global musicPatternTable, drumSFX, instrumentTable, songTable + +N_C = 0*8 +N_CS = 1*8 +N_D = 2*8 +N_DS = 3*8 +N_E = 4*8 +N_F = 5*8 +N_FS = 6*8 +N_G = 7*8 +N_GS = 8*8 +N_A = 9*8 +N_AS = 10*8 +N_B = 11*8 +N_DB = N_CS +N_EB = N_DS +N_GB = N_FS +N_AB = N_GS +N_BB = N_AS +N_CH = N_C + 12*8 +N_CSH = N_CS + 12*8 +N_DBH = N_DB + 12*8 +N_DH = N_D + 12*8 +N_DSH = N_DS + 12*8 +N_EBH = N_EB + 12*8 +N_EH = N_E + 12*8 +N_FH = N_F + 12*8 +N_FSH = N_FS + 12*8 +N_GBH = N_GB + 12*8 +N_GH = N_G + 12*8 +N_GSH = N_GS + 12*8 +N_ABH = N_AB + 12*8 +N_AH = N_A + 12*8 +N_ASH = N_AS + 12*8 +N_BBH = N_BB + 12*8 +N_BH = N_B + 12*8 +N_CHH = N_CH + 12*8 +N_TIE = 25*8 +REST = 26*8 + +D_8 = 1 +D_D8 = 2 +D_4 = 3 +D_D4 = 4 +D_2 = 5 +D_D2 = 6 +D_1 = 7 + +CON_PLAYPAT = $00 ; next: pattern, transpose (if not drums), instrument (if not drums) +CON_LOOPPAT = $10 ; as CON_PLAYPAT +CON_WAITROWS = $20 ; next: number of rows to wait minus 1 +CON_FINE = $21 ; stop music now +CON_SEGNO = $22 ; set loop point +CON_DALSEGNO = $23 ; jump to loop point. if no point was set, jump to start of song. +CON_SETTEMPO = $30 ; low bits: bits 8-9 of tempo in rows/min; next: bits 0-7 of tempo + +; Conductor macros +.macro playPatSq1 patid, transpose, instrument + .byt CON_PLAYPAT|0, patid, transpose, instrument +.endmacro +.macro playPatSq2 patid, transpose, instrument + .byt CON_PLAYPAT|1, patid, transpose, instrument +.endmacro +.macro playPatTri patid, transpose, instrument + .byt CON_PLAYPAT|2, patid, transpose, instrument +.endmacro +.macro playPatNoise patid, transpose, instrument + .byt CON_PLAYPAT|3, patid, transpose, instrument +.endmacro +.macro stopPatSq1 + .byt CON_PLAYPAT|0, 255, 0, 0 +.endmacro +.macro stopPatSq2 + .byt CON_PLAYPAT|1, 255, 0, 0 +.endmacro +.macro stopPatTri + .byt CON_PLAYPAT|2, 255, 0, 0 +.endmacro +.macro stopPatNoise + .byt CON_PLAYPAT|3, 255, 0, 0 +.endmacro +.macro waitRows n + .byt CON_WAITROWS, n-1 +.endmacro +.macro fine + .byt CON_FINE +.endmacro +.macro segno + .byt CON_SEGNO +.endmacro +.macro dalSegno + .byt CON_DALSEGNO +.endmacro +.macro setTempo rowsPerMin + .byt CON_SETTEMPO|>rowsPerMin, obeydata + sta 1 + jsr copynametable + +; initialize sprite cans + ldx #NUM_CANS - 1 +setupCanPos: + lda #0 + sta canXLo,x + sta canYLo,x + + ; can't have 128 as the velocity because the bounce code fails if + ; x == -x. The value 128 is in 0-63, so use 0-127 for positions + ; and 128-255 for velocities. + lda randnos+128,x + sta canDXLo,x + lda randnos+192,x + sta canDYLo,x + txa + sta canTheta,x + lda randnos+0,x + cmp #224 + bcc @canNotPastBottom + eor #%10000000 +@canNotPastBottom: + sta canYHi,x + lda randnos+64,x + cmp #248 + bcc @canNotPastRight + eor #%10000000 +@canNotPastRight: + sta canXHi,x + txa + lsr a + lsr a + lsr a + sec + sbc #4 + sta canDTheta,x + dex + bpl setupCanPos + jsr getTVSystem + sta tvSystem + + lda #NUM_CANS-1 + sta last_active_can + jsr sortCans + lda #0 + sta last_active_can + lda #NTSC_INCREASE_TIME + ldy tvSystem + beq @initialNotPAL + lda #PAL_INCREASE_TIME +@initialNotPAL: + sta till_increase + + jsr init_sound + lda #0 ; start "Celestial Soda Pop" + jsr init_music + lda #VBLANK_NMI + sta PPUCTRL + +mainloop: + jsr update_sound + + ; In the early warmup of the intro, increase the number of + ; sprites until all 64 are on screen. + dec till_increase + bne no_increase_yet + lda last_active_can + cmp #NUM_CANS - 1 + bcs no_increase_yet + sec + rol last_active_can + lda #NTSC_INCREASE_TIME + ldy tvSystem + beq @increaseNotPAL + lda #PAL_INCREASE_TIME +@increaseNotPAL: + sta till_increase +no_increase_yet: + + ldy firstsproffset + tya + clc + adc #60 + sta firstsproffset + ldx last_active_can +moveCanLoop: + + ; move can horizontally + lda canDXLo,x + bpl :+ + dec canXHi,x +: + clc + adc canXLo,x + sta canXLo,x + bcc :+ + inc canXHi,x +: + ; does it need to bounce off the left/right wall? + lda canXHi,x + cmp #248 + bcc :+ + lda #0 + sbc canDXLo,x + sta canDXLo,x +: + + ; move can vertically + lda canDYLo,x + bpl :+ + dec canYHi,x +: + clc + adc canYLo,x + sta canYLo,x + bcc :+ + inc canYHi,x +: + lda canYHi,x + + ; does it need to bounce off the bottom/top wall? + cmp #224 + bcc :+ + lda #0 + sbc canDYLo,x + sta canDYLo,x +: + + ; rotate the can + lda canTheta,x + clc + adc canDTheta,x + sta canTheta,x + and #%11110000 + lsr a + lsr a + lsr a + ora #%10000000 + + ; now actually draw the can + sta OAM+1,y + lda #%00100001 ; behind bg, basecolor=$3F14 + sta OAM+2,y + + lda canXHi,x + cmp #252 + bcc :+ + lda #0 +: + sta OAM+3,y + lda canYHi,x + cmp #248 + bcc :+ + lda #0 +: + sta OAM,y + + tya + clc + adc #68 + tay + dex + bpl moveCanLoop + + ; clear out sprite space used for inactive cans + + lda last_active_can + eor #$3F + beq allCansActive + tax +eraseInactiveCanLoop: + lda #$FF + sta OAM,y + tya + clc + adc #68 + tay + dex + bne eraseInactiveCanLoop + lda last_active_can + beq noSortNeeded +allCansActive: + jsr sortCans +noSortNeeded: + + jsr wait4vbl + ; If the time since power on is correct, + ; erase the boot message. + lda nmis ;erase + bne @noerasemsg + lda nmis+1 + cmp #2 + bne @noerasemsg + + lda #$21 + sta PPUADDR + lda #$c0 + sta PPUADDR + ldx #32 + lda #0 +@eraseloop: + .repeat 4 + sta PPUDATA + .endrepeat + dex + bne @eraseloop +@noerasemsg: + + ; Copy sprite display list to the PPU + lda #0 + sta PPUSCROLL + sta PPUSCROLL + sta OAMADDR + lda #>OAM + sta OAM_DMA + + lda #VBLANK_NMI|OBJ_8X16 + sta PPUCTRL + lda #BG_ON|OBJ_ON + sta PPUMASK + + jmp mainloop +.endproc + + + +;; +; Does one pass of bubble sort on the sprites by their X coordinates. +.proc sortCans + ldx #0 +sortloop: + lda canXHi,x ; compare the sprites' X coordinates + cmp canXHi+1,x + bcs noSwap + jsr swapCans +noSwap: + inx + cpx last_active_can + bcc sortloop + rts +.endproc + + +;; +; Swaps a pair of sprites x and x+1. +.proc swapCans + ldy canXHi,x + lda canXHi+1,x + sta canXHi,x + tya + sta canXHi+1,x + + ldy canXLo,x + lda canXLo+1,x + sta canXLo,x + tya + sta canXLo+1,x + + ldy canDXLo,x + lda canDXLo+1,x + sta canDXLo,x + tya + sta canDXLo+1,x + + ldy canYHi,x + lda canYHi+1,x + sta canYHi,x + tya + sta canYHi+1,x + + ldy canYLo,x + lda canYLo+1,x + sta canYLo,x + tya + sta canYLo+1,x + + ldy canDYLo,x + lda canDYLo+1,x + sta canDYLo,x + tya + sta canDYLo+1,x + + ldy canTheta,x + lda canTheta+1,x + sta canTheta,x + tya + sta canTheta+1,x + + ldy canDTheta,x + lda canDTheta+1,x + sta canDTheta,x + tya + sta canDTheta+1,x + rts +.endproc + + +;; +; Copies a name table from address in 0 to CIRAM $2000. +.proc copynametable +src = 0 + + ldy #VBLANK_NMI + sty PPUCTRL + lda #$20 + sta PPUADDR + ldy #$00 + sty PPUADDR + ldx #4 +copyloop: + .repeat 2 + lda (src),y + sta PPUDATA + iny + .endrepeat + bne copyloop + inc src+1 + dex + bne copyloop + + rts +.endproc + +;; +; Waits for vertical blanking. +.proc wait4vbl + lda nmis +notyet: + cmp nmis + beq notyet + rts +.endproc + +;; +; Increments retrace count for wait4vbl and other logic. +.proc nmipoint + inc nmis + bne nohi + inc nmis+1 +nohi: + rti +.endproc + +;; +; IRQ handler that does nothing because doesn't use +; mapper IRQs, APU frame IRQs, or DPCM IRQs. +.proc irqpoint + rti +.endproc + +; +; DATA TABLES +; + +.segment "RODATA" + +obeydata: .incbin "src/sprite.nam" ; made with 8name II +randnos: ; per http://en.wikipedia.org/wiki/Rijndael_S-box + .byt 99,124,119,123,242,107,111,197, 48, 1,103, 43,254,215,171,118 + .byt 202,130,201,125,250, 89, 71,240,173,212,162,175,156,164,114,192 + .byt 183,253,147, 38, 54, 63,247,204, 52,165,229,241,113,216, 49, 21 + .byt 4,199, 35,195, 24,150, 5,154, 7, 18,128,226,235, 39,178,117 + .byt 9,131, 44, 26, 27,110, 90,160, 82, 59,214,179, 41,227, 47,132 + .byt 83,209, 0,237, 32,252,177, 91,106,203,190, 57, 74, 76, 88,207 + .byt 208,239,170,251, 67, 77, 51,133, 69,249, 2,127, 80, 60,159,168 + .byt 81,163, 64,143,146,157, 56,245,188,182,218, 33, 16,255,243,210 + .byt 205, 12, 19,236, 95,151, 68, 23,196,167,126, 61,100, 93, 25,115 + .byt 96,129, 79,220, 34, 42,144,136, 70,238,184, 20,222, 94, 11,219 + .byt 224, 50, 58, 10, 73, 6, 36, 92,194,211,172, 98,145,149,228,121 + .byt 231,200, 55,109,141,213, 78,169,108, 86,244,234,101,122,174, 8 + .byt 186,120, 37, 46, 28,166,180,198,232,221,116, 31, 75,189,139,138 + .byt 112, 62,181,102, 72, 3,246, 14, 97, 53, 87,185,134,193, 29,158 + .byt 225,248,152, 17,105,217,142,148,155, 30,135,233,206, 85, 40,223 + .byt 140,161,137, 13,191,230, 66,104, 65,153, 45, 15,176, 84,187, 22 + + +; palette +titlepal: + .byt $0f,$00,$10,$30,$0f,$12,$1a,$30,$0f,$1a,$2c,$30,$0f,$12,$14,$30 + .byt $0f,$00,$10,$30,$0f,$12,$1a,$30,$0f,$1a,$2c,$30,$0f,$12,$14,$30 + +.segment "VECTORS" + .addr nmipoint, resetpoint, irqpoint diff --git a/spritecans-2011/tilesets/main.png b/spritecans-2011/tilesets/main.png new file mode 100644 index 0000000..2db7e4a Binary files /dev/null and b/spritecans-2011/tilesets/main.png differ diff --git a/spritecans-2011/tools/8name.py b/spritecans-2011/tools/8name.py new file mode 100644 index 0000000..1a2f258 --- /dev/null +++ b/spritecans-2011/tools/8name.py @@ -0,0 +1,559 @@ +#!/usr/bin/env python +from __future__ import with_statement, division +from Tkinter import Tk, Frame, Button, Canvas, Label, NW +from array import array +from PIL import Image as PILImage, ImageTk, ImageDraw + +def tileFromPlanar(s, opaqueBase=0): + nPlanes = len(s) // 8 + im = PILImage.new('P', (8, 8)) + pixels = im.load() + if pixels is None: + print "Ouch!", repr(im) + for y in range(8): + planedata = [ord(c) for c in s[y::8]] + for x in range(8): + c = 0 + for i in range(nPlanes): + if planedata[i] & (0x80 >> x): + c |= 1 << i + pixels[x, y] = c + opaqueBase if c > 0 else c + return im + +def renderAttrPicker(tileImg, palette, value): + im = PILImage.new('P', (128, 32)) + im.putpalette(palette) + for i in range(4): + x = i * 32 + t = 0 if i == value else 3 + im.paste(i * 4 + 1, (x + 16, t, x + 32 - t, 16)) + im.paste(i * 4 + 2, (x + t, 16, x + 16, 32 - t)) + im.paste(i * 4 + 3, (x + 16, 16, x + 32 - t, 32 - t)) + im.paste(tileImg.resize((16, 16)), (value * 32, 0)) + #im.show() + return im + +def renderChrFile(tiledata, palette, opaqueBase=0): + tiles = [tiledata[i:i + 16] for i in range(0, len(tiledata), 16)] + rows = [tiles[i:i + 16] for i in range(0, len(tiles), 16)] + h = len(rows) * 8 + w = 128 + + im = PILImage.new('P', (w, h)) + y = 0 + #palette = [0, 0, 0, 153, 102, 0, 102, 204, 0, 255, 204, 0] + im.putpalette(palette) + for row in rows: + x = 0 + for tiledata in row: + tile = tileFromPlanar(tiledata, opaqueBase) + im.paste(tile, (x, y)) + x += 8 + y += 8 + return im + +class NamFile: + defaultNESPalette = \ + "\x0f\x00\x10\x30\x0f\x12\x1a\x30\x0f\x1a\x2c\x30\x0f\x12\x14\x30" + nesclut = [ + (0x80,0x80,0x80), (0x00,0x00,0xBB), (0x37,0x00,0xBF), (0x84,0x00,0xA6), + (0xBB,0x00,0x6A), (0xB7,0x00,0x1E), (0xB3,0x00,0x00), (0x91,0x26,0x00), + (0x7B,0x2B,0x00), (0x00,0x3E,0x00), (0x00,0x48,0x0D), (0x00,0x3C,0x22), + (0x00,0x2F,0x66), (0x00,0x00,0x00), (0x05,0x05,0x05), (0x05,0x05,0x05), + + (0xC8,0xC8,0xC8), (0x00,0x59,0xFF), (0x44,0x3C,0xFF), (0xB7,0x33,0xCC), + (0xFF,0x33,0xAA), (0xFF,0x37,0x5E), (0xFF,0x37,0x1A), (0xD5,0x4B,0x00), + (0xC4,0x62,0x00), (0x3C,0x7B,0x00), (0x1E,0x84,0x15), (0x00,0x95,0x66), + (0x00,0x84,0xC4), (0x11,0x11,0x11), (0x09,0x09,0x09), (0x09,0x09,0x09), + + (0xFF,0xFF,0xFF), (0x00,0x95,0xFF), (0x6F,0x84,0xFF), (0xD5,0x6F,0xFF), + (0xFF,0x77,0xCC), (0xFF,0x6F,0x99), (0xFF,0x7B,0x59), (0xFF,0x91,0x5F), + (0xFF,0xA2,0x33), (0xA6,0xBF,0x00), (0x51,0xD9,0x6A), (0x4D,0xD5,0xAE), + (0x00,0xD9,0xFF), (0x66,0x66,0x66), (0x0D,0x0D,0x0D), (0x0D,0x0D,0x0D), + + (0xFF,0xFF,0xFF), (0x84,0xBF,0xFF), (0xBB,0xBB,0xFF), (0xD0,0xBB,0xFF), + (0xFF,0xBF,0xEA), (0xFF,0xBF,0xCC), (0xFF,0xC4,0xB7), (0xFF,0xCC,0xAE), + (0xFF,0xD9,0xA2), (0xCC,0xE1,0x99), (0xAE,0xEE,0xB7), (0xAA,0xF7,0xEE), + (0xB3,0xEE,0xFF), (0xDD,0xDD,0xDD), (0x11,0x11,0x11), (0x11,0x11,0x11) + ] + + def __init__(self, chrfilename, palfilename='', namfilename=None): + self.loadchr(chrfilename) + self.loadpal(palfilename) + self.loadnam(namfilename) + + def loadchr(self, chrfilename): + with open(chrfilename, 'rb') as infp: + chrdata = infp.read(4096) + self.chrdata = chrdata + if len(self.chrdata) != 4096: + raise ValueError("not enough data for pattern table") + + def loadpal(self, palfilename): + self.palfilename = palfilename + try: + with open(palfilename, 'rb') as infp: + pal = infp.read(16) + if len(paldata) != 16: + raise ValueError("not enough data for palette") + except IOError, e: + import errno + if e.errno in (errno.ENOENT, errno.EINVAL): + pal = self.defaultNESPalette + else: + raise + self.clut = [] + for c in pal: + self.clut.extend(self.nesclut[ord(c) & 0x3F]) + + def loadnam(self, namfilename): + print "namfilename is", namfilename + if namfilename is not None: + try: + with open(namfilename, 'rb') as infp: + namdata = infp.read(1152) + if ((len(namdata) != 1024 + and namdata.startswith('\x04\x00')) + or namfilename.lower().endswith('.pkb')): + print "unpacking" + namdata = UnPackBits(namdata[2:]).flush().tostring() + if len(namdata) != 1024: + raise ValueError("not enough data for nametable") + self.namdata = array('B', namdata) + except IOError, e: + import errno + if e.errno == errno.ENOENT: + namfilename = None + else: + raise + if namfilename is None: + self.namdata = array('B', [0 for i in range(1024)]) + self.namfilename = namfilename + self.setUnsaved(False) + + def setUnsaved(self, isSaved): + import datetime + self.unsaved = (datetime.datetime.now() + if isSaved + else False) + + def savenam(self, namfilename=None): + if namfilename is None: + namfilename = self.namfilename + s = self.namdata.tostring() + if namfilename.lower().endswith('.pkb'): + s = "\x04\x00" + PackBits(s).flush().tostring() + with open(namfilename, 'wb') as outfp: + outfp.write(s) + self.namfilename = namfilename + self.setUnsaved(False) + + def getTile(self, x, y): + if x < 0 or x >= 32 or y < 0 or y >= 30: + return None + nameIdx = y * 32 + x + tileNo = self.namdata[nameIdx] + attrIdx = (y >> 2) * 8 + (x >> 2) + 960 + attrShift = ((y & 0x02) << 1) | (x & 0x02) + attrNo = (self.namdata[attrIdx] >> attrShift) & 0x03 + return (tileNo, attrNo) + + def setTile(self, x, y, tileNo, attrNo=None): + if x < 0 or x >= 32 or y < 0 or y >= 30: + return + nameIdx = y * 32 + x + self.namdata[nameIdx] = tileNo + if attrNo is not None: + attrIdx = (y >> 2) * 8 + (x >> 2) + 960 + attrShift = ((y & 0x02) << 1) | (x & 0x02) + attrByte = (attrNo & 0x03) << attrShift + attrByte |= self.namdata[attrIdx] & ~(0x03 << attrShift) + self.namdata[attrIdx] = attrByte + + def getTileData(self, tileNo): + return self.chrdata[tileNo * 16:tileNo * 16 + 16] + + def renderTile(self, tileNo, attrNo): + return tileFromPlanar(self.getTileData(tileNo), attrNo * 4) + +def build_menubar(parent, mbardata): + from Tkinter import Menu + menubar = Menu(parent) + parent.config(menu=menubar) + menus = [] + for (label, items) in mbardata: + menu = Menu(menubar) + menus.append(menu) + menubar.add_cascade(label=label, menu=menu) + for item in items: + if item == '-': + menu.add_separator() + else: + label = item[0] + underline = label.find('&') + if underline >= 0: + label = label[:underline] + label[underline+1:] + else: + underline = None + accelerator = item[2] if len(item) > 2 else None + menu.add_command(label=label, command=item[1], + accelerator=accelerator, + underline=underline) + return (menubar, menus) + +class TilePicker(Frame): + def __init__(self, parent, doc, **kw): + apply(Frame.__init__, (self, parent), kw) + self.doc = doc + self.tilePicker = None + self.attrPicker = None + self.status = None + self.curTile = 0 + self.setAttribute(0) + self.tilePicker = Label(self, image=self.tilePickerPI, + width=128, borderwidth=0) + self.tilePicker.grid(row=0,column=0) + self.tilePicker.bind("", self.tilePickerCallback) + self.attrPicker = Label(self, image=self.attrPickerPI, + borderwidth=0) + self.attrPicker.grid(row=1,column=0) + self.attrPicker.bind("", self.attrPickerCallback) + self.status = Label(self) + self.status.grid(row=2,column=0) + self.setStatus() + + def setAttribute(self, value): + self.curAttribute = value & 0x03 + self.updateWidgets() + + def updateWidgets(self): + self.tilePickerImage = renderChrFile(self.doc.chrdata, + self.doc.clut, + self.curAttribute * 4) + self.tilePickerPI = ImageTk.PhotoImage(self.tilePickerImage) + if self.tilePicker is not None: + self.tilePicker.configure(image=self.tilePickerPI) + + previewTile = self.doc.renderTile(self.curTile, self.curAttribute) + self.attrPickerImage = renderAttrPicker(previewTile, + self.doc.clut, + self.curAttribute) + self.attrPickerPI = ImageTk.PhotoImage(self.attrPickerImage) + if self.attrPicker is not None: + self.attrPicker.configure(image=self.attrPickerPI) + + self.setStatus() + + def setTile(self, tile): + self.curTile = tile + self.setAttribute(self.curAttribute) + + def setStatus(self): + if self.status is None: + return + label = "tile $%02x attr %d" % (self.curTile, self.curAttribute) + self.status.configure(text=label) + + def tilePickerCallback(self, event): + if event.x >= 0 and event.x < 128 and event.y >= 0 and event.y < 128: + tileX = event.x // 8 + tileY = event.y // 8 + newTileNo = tileY * 16 + tileX + #print "mouse was clicked on tile", newTileNo + self.setTile(newTileNo) + return + print "mouse was clicked at (%d, %d)" % (event.x, event.y) + + def attrPickerCallback(self, event): + if event.x >= 0 and event.x < 128: + attr = event.x // 32 + #print "mouse was clicked on attribute", attr + self.setAttribute(attr) + return + print "mouse was clicked at (%d, %d)" % (event.x, event.y) + +class NamDisplay(Canvas): + def __init__(self, parent, doc, **kw): + kw['width'] = 512 + kw['height'] = 480 + kw['relief']='raised' + kw['highlightthickness'] = 0 + apply(Canvas.__init__, (self, parent), kw) + self.doc = doc + self.tile = [] + im = PILImage.new('RGB', (32, 32)) + for y in range(15): + row = [] + for x in range(16): + tile = ImageTk.PhotoImage(im) + if True or ((x ^ y) & 1) == 0: + self.create_image(x * 32, y * 32, image=tile, anchor=NW) + row.append(tile) + self.tile.append(row) + self.updating = False + self.updScreen() + + def updScreen(self): + self.updating = True + for y in range(0, 30, 2): + for x in range(0, 32, 2): + self.updTile(x, y) + self.updating = False + self.update_idletasks() + + def updTile(self, x, y): + if x < 0 or x >= 32 or y < 0 or y >= 30: + return + y = y & ~1 + x = x & ~1 + im = PILImage.new('RGB', (32, 32)) + dst = self.tile[y >> 1][x >> 1] + for y1 in range(2): + for x1 in range(2): + (tileNo, attrNo) = self.doc.getTile(x + x1, y + y1) + tile = self.doc.renderTile(tileNo, attrNo).resize((16, 16)) + tile.putpalette(self.doc.clut) + im.paste(tile, (x1 * 16, y1 * 16)) + dst.paste(im) + if not self.updating: + self.update_idletasks() + +class PackBits(): + def __init__(self, toWrite=''): + self.bytes = array('b') + self.closed = False + self.mode = 'wb' + self.name = '' + self.newlines = None + if toWrite: + self.write(toWrite) + + def close(self): + self.bytes = None + self.closed = True + + def write(self, s): + """Add a string to the buffer.""" + if not self.closed: + self.bytes.fromstring(s) + + def tell(self): + return len(self.bytes) + + def truncate(self, length): + if not self.closed: + del self[length:] + + def writelines(self, seq): + """Add a sequence of strings to the buffer.""" + self.write(''.join(seq)) + + def flush(self): + """Compress the data to a file.""" + i = 0 + base = 0 + out = array('b') + while base < len(self.bytes): + + # measure the run starting at t + i = 1 + imax = min(128, len(self.bytes) - base) + basebyte = self.bytes[base] + while (i < imax + and basebyte == self.bytes[base + i]): + i += 1 + # if the run is either length 3 or to the end of the file, + # write it + if i > 2 or base + i == len(self.bytes): + out.append(1 - i) + out.append(self.bytes[base]) + base += i + continue + + # measure the nonrun starting at t + i = 1 + imax = min(128, len(self.bytes) - base) + while (i < imax + and (base + i + 2 >= len(self.bytes) + or self.bytes[base + i] != self.bytes[base + i + 1] + or self.bytes[base + i] != self.bytes[base + i + 2])): + i += 1 + out.append(i - 1) + out.extend(self.bytes[base:base + i]) + base += i + return out + + @staticmethod + def test(): + pb = PackBits('stopping stoppping stopppppi') + data = pb.flush() + print repr(data) + +class UnPackBits(PackBits): + def flush(self): + out = array('b') + base = 0 + while base < len(self.bytes): + c = self.bytes[base] + if c > 0 and c <= 127: + b = self.bytes[base + 1] + out.extend(self.bytes[base + 1:base + c + 2]) + base += 2 + c + elif c >= -127: + b = self.bytes[base + 1] + out.fromlist([b] * (1 - c)) + base += 2 + return out + + @staticmethod + def test(): + start = 'stopping stoppping stopppppi' + packed = PackBits(start).flush().tostring() + print repr(packed) + unpacked = UnPackBits(packed).flush().tostring() + print repr(unpacked) + print "pass" if start == unpacked else "fail" + + +class App: + filetypes = [ + ('NES nametable', '*.nam'), + ('NES compressed nametable', '*.pkb'), + ('PNG image', '*.png'), + ('GIF image', '*.gif'), + ('Windows bitmap', '*.bmp') + ] + + def __init__(self, w, doc): + import sys + self.window = w + self.doc = doc + mbardata = [ + ("File", [ + ("&New Nametable", lambda: self.file_new_nam(), "Ctrl+N"), + ("&Open Nametable...", lambda: self.file_open_nam(), "Ctrl+O"), + ("Open &Pattern Table...", lambda: self.file_open_chr(), "Ctrl+L"), + '-', + ("&Save", lambda: self.file_save_nam(), "Ctrl+S"), + ("Save &As...", lambda: self.file_save_nam_as(), "Ctrl+A"), + '-', + ("E&xit", lambda: self.file_quit(), "Ctrl+Q") + ]), + ("Help", [ + ("&About...", lambda: self.about()) + ]) + ] + (menubar, menus) = build_menubar(w, mbardata) + w.bind("", lambda e: self.file_new_nam()) + w.bind("", lambda e: self.file_new_nam()) + w.bind("", lambda e: self.file_open_nam()) + w.bind("", lambda e: self.file_open_nam()) + w.bind("", lambda e: self.file_open_chr()) + w.bind("", lambda e: self.file_open_chr()) + w.bind("", lambda e: self.file_save_nam()) + w.bind("", lambda e: self.file_save_nam()) + w.bind("", lambda e: self.file_quit()) + w.bind("", lambda e: self.file_quit()) + + self.tilePicker = TilePicker(w, doc) + self.tilePicker.grid(row=0,column=0, sticky=NW) + self.namDisplay = NamDisplay(w, doc, borderwidth=0) + self.namDisplay.grid(row=0,column=1) + self.namDisplay.bind("", self.namPickupCallback) + self.namDisplay.bind("", self.namPickupCallback) + self.namDisplay.bind("", self.namWriteCallback) + self.namDisplay.bind("", self.namWriteCallback) + w.wm_resizable(0,0) + self.updWindowTitle() + + def namPickupCallback(self, event): + if event.x >= 0 and event.x < 512 and event.y >= 0 and event.y < 512: + x = event.x // 16 + y = event.y // 16 + (tile, attribute) = self.doc.getTile(x, y) + self.tilePicker.curTile = tile + self.tilePicker.setAttribute(attribute) + return + + def namWriteCallback(self, event): + if event.x >= 0 and event.x < 512 and event.y >= 0 and event.y < 512: + x = event.x // 16 + y = event.y // 16 + t = self.tilePicker.curTile + a = self.tilePicker.curAttribute + self.doc.setTile(x, y, t, a) + if not self.doc.unsaved: + self.doc.setUnsaved(True) + self.updWindowTitle() + self.namDisplay.updTile(x, y) + return + + def updWindowTitle(self): + nfn = self.doc.namfilename + if nfn is None: + nfn = 'untitled' + if self.doc.unsaved: + nfn = '*' + nfn + appname = '8name II' + title = ' - '.join((nfn, appname)) + self.window.title(title) + + def file_new_nam(self): + print "File > New Nametable" + + def file_open_nam(self): + from tkFileDialog import askopenfilename + filename = askopenfilename(parent=root, + filetypes=self.filetypes, + initialfile=self.doc.namfilename, + title="Open Nametable") + print "file open nam: filename is", filename + if not isinstance(filename, basestring): + return + self.doc.loadnam(filename) + self.namDisplay.updScreen() + self.updWindowTitle() + + def file_open_chr(self): + from tkFileDialog import askopenfilename + filename = askopenfilename(parent=root, + filetypes=[('Pattern Table', '*.chr')], + initialfile=self.doc.namfilename, + title="Open Pattern Table") + if not isinstance(filename, str): + return + self.doc.loadchr(filename) + self.tilePicker.updateWidgets() + self.namDisplay.updScreen() + + def file_save_nam(self): + if self.doc.namfilename is None: + return self.file_save_nam_as() + self.doc.savenam() + self.updWindowTitle() + + def file_save_nam_as(self): + from tkFileDialog import asksaveasfilename + filename = asksaveasfilename(parent=root, + filetypes=self.filetypes, + title="Save Nametable As") + ext = filename[-4:].lower() + if ext in ('.png', '.gif', '.bmp'): + print "Would save image to", filename + else: + self.doc.savenam(filename) + self.updWindowTitle() + + def file_quit(self): + self.window.destroy() + +root = Tk() +app = App(root, NamFile('../spritecans.chr')) +root.mainloop() +print "remain:" +print "1. implement image saving" +print "2. implement and test loading" +print "3. implement and test compressed pkb support" +print "4. Implement stub methods for File menu items" +print "5. Warn on closing document where doc.unsaved is not False" +print "6. Write palette editor" diff --git a/spritecans-2011/tools/mktables.py b/spritecans-2011/tools/mktables.py new file mode 100644 index 0000000..a499597 --- /dev/null +++ b/spritecans-2011/tools/mktables.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python +# +# Lookup table generator for Concentration Room +# Copyright 2010 Damian Yerrick +# +# Copying and distribution of this file, with or without +# modification, are permitted in any medium without royalty +# provided the copyright notice and this notice are preserved. +# This file is offered as-is, without any warranty. +# +from __future__ import with_statement, division +import sys + +ntscOctaveBase = 39375000.0/(22 * 16 * 55) +palOctaveBase = 266017125.0/(10 * 16 * 16 * 55) +maxNote = 80 + +def makePeriodTable(filename, pal=False): + semitone = 2.0**(1./12) + octaveBase = palOctaveBase if pal else ntscOctaveBase + relFreqs = [(1 << (i // 12)) * semitone**(i % 12) + for i in xrange(maxNote)] + periods = [int(round(octaveBase / freq)) - 1 for freq in relFreqs] + systemName = "PAL" if pal else "NTSC" + with open(filename, 'wt') as outfp: + outfp.write("""; %s period table generated by mktables.py +.export periodTableLo, periodTableHi +.segment "RODATA" +periodTableLo:\n""" + % systemName) + for i in range(0, maxNote, 12): + outfp.write(' .byt ' + + ','.join('$%02x' % (i % 256) + for i in periods[i:i + 12]) + + '\n') + outfp.write('periodTableHi:\n') + for i in range(0, maxNote, 12): + outfp.write(' .byt ' + + ','.join('$%02x' % (i >> 8) + for i in periods[i:i + 12]) + + '\n') + +def makePALPeriodTable(filename): + return makePeriodTable(filename, pal=True) + +tableNames = { + 'period': makePeriodTable, + 'palperiod': makePALPeriodTable +} + +def main(argv): + if len(argv) >= 2 and argv[1] in ('/?', '-?', '-h', '--help'): + print "usage: %s TABLENAME FILENAME" % argv[0] + print "known tables:", ' '.join(sorted(tableNames)) + elif len(argv) < 3: + print "mktables: too few arguments; try %s --help" % argv[0] + elif argv[1] in tableNames: + tableNames[argv[1]](argv[2]) + else: + print "mktables: no such table %s; try %s --help" % (argv[1], argv[0]) + +if __name__=='__main__': + main(sys.argv) + diff --git a/spritecans-2011/tools/pilbmp2nes.py b/spritecans-2011/tools/pilbmp2nes.py new file mode 100644 index 0000000..797bc9a --- /dev/null +++ b/spritecans-2011/tools/pilbmp2nes.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# +# Bitmap to NES CHR converter using Python Imaging Library +# Copyright 2010 Damian Yerrick +# +# Copying and distribution of this file, with or without +# modification, are permitted in any medium without royalty +# provided the copyright notice and this notice are preserved. +# This file is offered as-is, without any warranty. +# +from __future__ import with_statement +from PIL import Image +from time import sleep + +def formatTilePlanar(tile, nPlanes): + import array + if (tile.size != (8, 8)): + return None + pixels = iter(tile.getdata()) + outplanes = [array.array('B') + for i in range(nPlanes)] + for y in range(8): + slivers = [0 for i in range(nPlanes)] + for x in range(8): + px = pixels.next() + for i in range(nPlanes): + slivers[i] = slivers[i] << 1 + if px & 0x01: + slivers[i] = slivers[i] | 1 + px >>= 1 + for i in range(nPlanes): + outplanes[i].append(slivers[i]) + out = "".join(plane.tostring() for plane in outplanes) + return out + +def parse_argv(argv): + from optparse import OptionParser + parser = OptionParser(usage="usage: %prog [options] [-i] INFILE [-o] OUTFILE") + parser.add_option("-i", "--image", dest="infilename", + help="read image from INFILE", metavar="INFILE") + parser.add_option("-o", "--output", dest="outfilename", + help="write CHR data to OUTFILE", metavar="OUTFILE") + parser.add_option("-W", "--tile-width", dest="tileWidth", + help="set width of metatiles", metavar="HEIGHT", + type="int", default=8) + parser.add_option("-H", "--tile-height", dest="tileHeight", + help="set height of metatiles", metavar="HEIGHT", + type="int", default=8) + (options, args) = parser.parse_args(argv[1:]) + + tileWidth = int(options.tileWidth) + if tileWidth <= 0: + raise ValueError("tile width '%d' must be positive" % tileWidth) + + tileHeight = int(options.tileHeight) + if tileHeight <= 0: + raise ValueError("tile height '%d' must be positive" % tileHeight) + + # Fill unfilled roles with positional arguments + argsreader = iter(args) + try: + infilename = options.infilename + if infilename is None: + infilename = argsreader.next() + outfilename = options.infilename + if outfilename is None: + outfilename = argsreader.next() + except StopIteration: + raise ValueError("not enough filenames") + try: + argsreader.next() + raise ValueError("too many filenames") + except StopIteration: + pass + + return (infilename, outfilename, tileWidth, tileHeight) + +argvTestingMode = True + +def main(argv=None): + if argv is None: + import sys + import os + argv = sys.argv + print argv + if (argvTestingMode and len(argv) < 2 + and sys.stdin.isatty() and sys.stdout.isatty()): + argv.extend(raw_input('args:').split()) + try: + (infilename, outfilename, tileWidth, tileHeight) = parse_argv(argv) + except StandardError, e: + import sys + sys.stderr.write("%s: %s\n" % (argv[0], str(e))) + sys.exit(1) + + print "loading", infilename + im = Image.open(infilename) + im.load() + (w, h) = im.size + + out = [] + for mt_y in range(0, h, tileHeight): + for mt_x in range(0, w, tileWidth): + metatile = im.crop((mt_x, mt_y, + mt_x + tileWidth, mt_y + tileHeight)) + for tile_y in range(0, tileHeight, 8): + for tile_x in range(0, tileWidth, 8): + tile = metatile.crop((tile_x, tile_y, + tile_x + 8, tile_y + 8)) + data = formatTilePlanar(tile, 2) + out.append(data) + with open(outfilename, 'wb') as outfp: + outfp.writelines(out) + +if __name__=='__main__': + main() diff --git a/spritecans-2011/zip.in b/spritecans-2011/zip.in new file mode 100644 index 0000000..f2f5698 --- /dev/null +++ b/spritecans-2011/zip.in @@ -0,0 +1,18 @@ +README.txt +obj/nes/index.txt +src/music.s +src/musicseq.h +src/musicseq.s +src/nes.h +src/paldetect.s +src/sound.s +src/sprite.nam +src/sprite.s +tilesets/main.png +tools/8name.py +tools/mktables.py +tools/pilbmp2nes.py +makefile +nes.ini +spritecans.nes +zip.in diff --git a/stars_se/Header_32k b/stars_se/Header_32k new file mode 100644 index 0000000..57c2980 Binary files /dev/null and b/stars_se/Header_32k differ diff --git a/stars_se/Make_StarsSE b/stars_se/Make_StarsSE new file mode 100644 index 0000000..f6696a4 --- /dev/null +++ b/stars_se/Make_StarsSE @@ -0,0 +1,2 @@ +DAsm StarsSE.asm -f3 +Join Header_32k MUSIC.bin StarsSE.SPR a.out StarsSE.CHR AS StarsSE.NES diff --git a/stars_se/ReadmeStarsSE.TXT b/stars_se/ReadmeStarsSE.TXT new file mode 100644 index 0000000..c8edc42 --- /dev/null +++ b/stars_se/ReadmeStarsSE.TXT @@ -0,0 +1,21 @@ +This is an NES demo that I made to show off some of the NES' features. +It doesn't do too much that resembles an actual game, but it should give +you an idea of what the NES can do technically. Besides, I'm proud of +what I did, since I'm a beginner at NES (and 6502) programming. + +This demo, called "Stars SE" is a tribute to the NES and NES games of +the past. It's also a little tip of the hat to the C-64-style intros. +I have included the source code to this version of Stars, but the music +is not included, as I did not write it. + +I've tried to code this demo by the book, without using any bad tricks +(since I don't really know any tricks anyway). A _good_ NES emulator, +that is, one that behaves closest to a real NES, should have no problem +running this thing. + +Please E-Mail me if you like this demo. Enjoy! + +Chris Covell +ccovell@direct.ca +http://mypage.direct.ca/c/ccovell/ +Powered by Amiga! diff --git a/stars_se/StarsSE.CHR b/stars_se/StarsSE.CHR new file mode 100644 index 0000000..2d9eae6 Binary files /dev/null and b/stars_se/StarsSE.CHR differ diff --git a/stars_se/StarsSE.NES b/stars_se/StarsSE.NES new file mode 100644 index 0000000..df55428 Binary files /dev/null and b/stars_se/StarsSE.NES differ diff --git a/stars_se/StarsSE.SPR b/stars_se/StarsSE.SPR new file mode 100644 index 0000000..6f00447 Binary files /dev/null and b/stars_se/StarsSE.SPR differ diff --git a/stars_se/StarsSE.asm b/stars_se/StarsSE.asm new file mode 100644 index 0000000..a4eb74a --- /dev/null +++ b/stars_se/StarsSE.asm @@ -0,0 +1,533 @@ +;Stars Demo *SE* +;------------------- +;Binary created using DAsm 2.12 running on an Amiga. + + PROCESSOR 6502 + +SPRITEMAP EQU #$0600 ;Region in Memory to copy sprites. +SPRITEHARD EQU #$C000 ;Format: Y-pos,Tile#,Attr,X-Pos +SPRITEDIR EQU #$C100 ;Format: DY,D2Y,D2X,DX + +NMIPASS EQU #$0001 ;NMI has passed flag. +NMIODD EQU #$0000 ;Odd NMIs + +XSCROLL EQU #$0002 ;Scroll values. +YSCROLL EQU #$0003 +TEXTLO EQU #$04 ;Zero-Page pointers for the text. +TEXTHI EQU #$05 +SCRLO EQU #$0006 ;Place to put the text. +SCRHI EQU #$0007 +NMICOUNT EQU #$0008 +SPRITEHIT EQU #$0009 +FREEZEROL EQU #$0A +FREEZEROH EQU #$0B ;In other words, free, always empty zero-page space. +XSCROLLSPEED EQU #$000C ;Duh. +YSCROLLSPEED EQU #$000D +CHECKFORSPR EQU #$000E ;Flag to check for sprites +RAINBOWPOS EQU #$000F ;Position in rainbow-cycle. +TEXTHARDLO EQU #$10 +TEXTHARDHI EQU #$11 ;Hard Copy of text + +;-----------------Music-Playing Code---------------------------- + +;MUSIC IS NOT INCLUDED IN THE SOURCE BECAUSE IT IS NOT MINE TO GIVE OUT!! + +;int_en EQU #$0100 +;sng_ctr EQU #$0101 +;pv_btn EQU #$0102 +; +;INIT_ADD EQU #$8000 +;PLAY_ADD EQU #$800E +;START_SONG EQU #$00 ;Although I played around with the order. +;MAX_SONG EQU #$0C +;--------------------------- + ORG $C200 ;16Kb PRG-ROM, 8Kb CHR-ROM + +Reset_Routine SUBROUTINE + cld ;Clear decimal flag + sei ;Disable interrupts +.WaitV lda $2002 + bpl .WaitV ;Wait for vertical blanking interval + ldx #$00 + stx $2000 + stx $2001 ;Screen display off, amongst other things + dex + txs ;Top of stack at $1FF + +;Clear (most of) the NES' WRAM. This routine is ripped from "Duck Hunt" - I should probably clear all +;$800 bytes. + ldy #$06 ;To clear 7 x $100 bytes, from $000 to $6FF? + sty $01 ;Store count value in $01 + ldy #$00 + sty $00 + lda #$00 + +.Clear sta ($00),y ;Clear $100 bytes + dey + bne .Clear + + dec $01 ;Decrement "banks" left counter + bpl .Clear ;Do next if >= 0 + + + lda #$20 + sta $2006 + lda #$00 + sta $2006 + + ldx #$00 + ldy #$10 +.ClearPPU sta $2007 ;Clear the PPU space. REALLY IMPORTANT for a real NES! + dex + bne .ClearPPU + dey + bne .ClearPPU + +;------------------------------------------------------ +;********* Set up Name & Attributes ****************** + + lda #<.NESDeck + sta FREEZEROL + lda #>.NESDeck + sta FREEZEROH + + lda #$20 ;Load up entire name & attribute table for screen 0. + sta $2006 + lda #$00 + sta $2006 + ldx #$00 + ldy #$04 + +.LoadDeck + txa + pha + + ldx #$00 + lda (FREEZEROL),X ;Load up NES image + sta $2007 + + pla + tax + + inc FREEZEROL + bne .NoDeck1 + inc FREEZEROH + +.NoDeck1 + dex + bne .LoadDeck + dey + bne .LoadDeck + +;-------------------------------------------------- + +;---------- Set up other bits ---------------- + + lda #$26 ;Bars on screen 1. + sta $2006 + lda #$C0 + sta $2006 + + jsr DrawBits + + lda #$27 + sta $2006 + lda #$60 + sta $2006 + + jsr DrawBits + +;---------------------------------------------- + +;********* Initialize Palette to colour table ******** + + ldx #$3F + stx $2006 + ldx #$00 + stx $2006 + + ldx #$00 + ldy #$20 ;Save BG & Sprite palettes. +.InitPal lda .Palette,X + sta $2007 + inx + dey + bne .InitPal +;------------------------------------------------------ + +;------------------------------- + + ldx #$00 +.CopySpr lda SPRITEHARD,X + sta SPRITEMAP,X ;Copy sprite map. + inx + bne .CopySpr + +;------------------------------- + + lda #$00 + sta NMIPASS ;Flag for NMI Passed + sta NMIODD ;Even/OddVBs + sta NMICOUNT + sta SPRITEHIT + sta CHECKFORSPR + sta RAINBOWPOS ;Position in rainbow + + lda #<.text ;Start of text pointer + sta TEXTLO + sta TEXTHARDLO + lda #>.text + sta TEXTHI + sta TEXTHARDHI + + lda #$23 ;Where to place text on-screen + sta SCRHI + lda #$30 + sta SCRLO + + lda #$79 + sta XSCROLL ;X-Y scroll values + lda #$00 + sta YSCROLL + + lda #$02 + sta XSCROLLSPEED + lda #$01 + sta YSCROLLSPEED ;X-Y scrollspeeds + +;------- MUSIC CODE ---------------- +; lda #START_SONG ;Start Song +; sta sng_ctr +; jsr INIT_ADD ;init tune +; LDA #$01 +; sta int_en +;------------------------------- + + lda #$00 + sta $2006 + sta $2006 + +;Enable vblank interrupts, etc. + lda #%10001000 + sta $2000 + lda #%00011000 ;Screen on, sprites on, show leftmost 8 pixels, colour + sta $2001 +; cli ;Enable interrupts(?) NO!!!!!!!!!!!!!! + +.Loop + + lda NMIPASS + beq .Loop + + lda #$00 + sta NMIPASS + + lda NMIODD + bne .CheckSpr1 + + + ldx #$00 +.SprMov lda SPRITEMAP,X ;Grab sprite Y-Position + clc + adc SPRITEDIR,X ;Add DY + bcc .NextMov1 + + pha ;If it goes off-screen... + inx + lda SPRITEDIR,X ;Get D2Y... + inx + inx + clc + adc SPRITEMAP,X ;..Add to X-Position + sta SPRITEMAP,X + dex + dex + dex + pla + +.NextMov1 sta SPRITEMAP,X + inx + inx + inx + lda SPRITEMAP,X ;Grab sprite X-position + clc + adc SPRITEDIR,X ;Add DX + bcc .NextMov2 + + pha ;If it goes off-screen... + dex + lda SPRITEDIR,X ;Get D2X + dex + dex + clc + adc SPRITEMAP,X ;Add to Y-Position + sta SPRITEMAP,X + inx + inx + inx + pla + +.NextMov2 sta SPRITEMAP,X + inx + bne .SprMov + +.CheckSpr1 + lda $2002 + and #$40 + bne .CheckSpr1 + +.CheckSpr2 + lda $2002 + and #$40 + beq .CheckSpr2 + + lda XSCROLL + sta $2005 + lda $2002 + +;------- MUSIC CODE --------------- +; jsr r_btn +; and #$10 +; beq .Loop +; +; inc sng_ctr +; LDA #MAX_SONG ;Max Song. +; cmp sng_ctr +; bne .no_scr +; lda #$0 +; sta sng_ctr +; +;.no_scr lda #$0 +; sta int_en +; lda sng_ctr +; jsr INIT_ADD +; lda #$01 +; sta int_en ;check button, if pressed inc song # and re-init +;------------------------------------------------------------------------- + + jmp .Loop + +.Palette dc.b #$0D,#$16,#$27,#$38,#$0D,#$00,#$10,#$30,#$0D,#$16,#$28,#$39,#$0D,#$2B,#$21,#$24 + dc.b #$0D,#$00,#$10,#$30,#$0D,#$16,#$26,#$16,#$0D,#$07,#$16,#$28,#$0D,#$00,#$00,#$00 + +.NESDeck + INCLUDE starsnam.asm + + +.text + INCLUDE scrolltext.bin + dc.b #$00 + dc.b "Hello, all you hackers!" + +IncText SUBROUTINE + inc TEXTLO + bne .nocarry + inc TEXTHI +.nocarry rts + +IncScr SUBROUTINE + inc SCRLO + lda SCRLO + and #$1F + bne .nocarry2 + lda SCRLO + sec + sbc #$20 + sta SCRLO + lda SCRHI + clc + sta SCRHI +.nocarry2 rts + +DrawBits SUBROUTINE + ldy #$20 + lda #$04 +.Bits1A sta $2007 + dey + bne .Bits1A + ldy #$20 + lda #$05 +.Bits1B sta $2007 + dey + bne .Bits1B + rts + +;************* MUSIC CODE ******************* +;r_btn SUBROUTINE +; ldy #$08 ;read keypad +; ldx #$01 +; stx $4016 +; dex +; stx $4016 +; +;.r_bit lda $4016 +; ROR +; txa +; ROL +; tax +; dey +; bne .r_bit +; +; cmp pv_btn +; beq .no_chg +; sta pv_btn +; rts +; +;.no_chg lda #$0 +; rts +;*********************************************** + +IncRainbow SUBROUTINE + inc RAINBOWPOS + lda RAINBOWPOS + cmp #$06 + bne .RainEnd + lda #$00 + sta RAINBOWPOS +.RainEnd + rts + +SaveRainbow SUBROUTINE + ldx RAINBOWPOS + lda .Rainbow,X + sta $2007 + jsr IncRainbow + rts + +.Rainbow dc.b #$16,#$28,#$39,#$2B,#$21,#$24 + +NMI_Routine SUBROUTINE + php + pha + txa + pha + tya + pha + + lda XSCROLL + clc + adc XSCROLLSPEED + sta XSCROLL + + lda YSCROLL + clc + adc YSCROLLSPEED + sta YSCROLL + + lda #$00 + sta $2005 + sta $2005 + + + inc NMICOUNT + + lda NMICOUNT + cmp #$04 + bne .NotThere + + lda #$00 + sta NMICOUNT + + lda SCRHI + sta $2006 + lda SCRLO + sta $2006 + + ldx #$00 + lda (TEXTLO),X + + sta $2007 + + lda SCRHI + eor #$04 + sta $2006 + lda SCRLO + sta $2006 + + lda (TEXTLO),X + sta $2007 + cmp #$00 + bne .NoTextWrap + + lda TEXTHARDLO ;Start of text pointer + sta TEXTLO + lda TEXTHARDHI + sta TEXTHI + +.NoTextWrap jsr IncText + jsr IncScr + +;--------------------------------------------- +; increase rainbow colours here. + lda #$3F + sta $2006 + lda #$09 + sta $2006 + + jsr SaveRainbow + jsr SaveRainbow + jsr SaveRainbow + lda #$0D + sta $2007 + jsr SaveRainbow + jsr SaveRainbow + jsr SaveRainbow + jsr IncRainbow + jsr IncRainbow + jsr IncRainbow + jsr IncRainbow + jsr IncRainbow +;----------------------------------- + +.NotThere + + lda #$01 + eor NMIODD ;Update Even/Odd NMIs + sta NMIODD + + lda #$01 + sta NMIPASS + + lda #$00 + sta $2006 + sta $2006 + + lda #$00 + sta $2005 + sta $2005 + + lda #$01 + sta CHECKFORSPR + + lda #>SPRITEMAP ;Point to SPRITEMAP + sta $4014 ;Xfer sprites over + +;----- MUSIC CODE ----------------------------- +; lda int_en +; beq .no_ints +; jsr PLAY_ADD ;play tune +; +;.no_ints +;------------------------------------- + + + pla + tay + pla + tax + pla + plp + rti + +IRQ_Routine ;Dummy label + rti + +;That's all the code. Now we just need to set the vector table approriately. + + ORG $FFFA,0 + dc.w NMI_Routine + dc.w Reset_Routine + dc.w IRQ_Routine ;Not used, just points to RTI + + +;The end. diff --git a/stars_se/scrolltext.bin b/stars_se/scrolltext.bin new file mode 100644 index 0000000..ec55fd7 --- /dev/null +++ b/stars_se/scrolltext.bin @@ -0,0 +1,239 @@ +; scroll.txt + + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $50, $73, $73, $73, $73, $74 + DC.B $2e, $2e, $2e, $2e, $2e, $2e, $2e, $2e, $2e, $2e, $2e, $2e, $2e, $2e, $20, $20 + DC.B $48, $65, $79, $2c, $20, $6c, $69, $73, $74, $65, $6e, $21, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $48, $65, $61, $72, $20, $74, $68 + DC.B $61, $74, $3f, $20, $20, $49, $74, $27, $73, $20, $79, $6f, $75, $72, $20, $4e + DC.B $45, $53, $20, $63, $61, $6c, $6c, $69, $6e, $67, $20, $79, $6f, $75, $2e, $20 + DC.B $20, $20, $43, $27, $6d, $6f, $6e, $2c, $20, $74, $61, $6b, $65, $20, $69, $74 + DC.B $20, $6f, $75, $74, $20, $6f, $66, $20, $73, $74, $6f, $72, $61, $67, $65, $20 + DC.B $61, $6e, $64, $20, $74, $75, $72, $6e, $20, $69, $74, $20, $6f, $6e, $2e, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $49, $74, $27, $73, $20, $62, $65, $65, $6e + DC.B $20, $79, $65, $61, $72, $73, $3b, $20, $77, $68, $79, $20, $68, $61, $76, $65 + DC.B $20, $79, $6f, $75, $20, $67, $69, $76, $65, $6e, $20, $75, $70, $20, $6f, $6e + DC.B $20, $79, $6f, $75, $72, $20, $66, $61, $69, $74, $68, $66, $75, $6c, $20, $67 + DC.B $72, $65, $79, $20, $62, $6f, $78, $3f, $20, $20, $20, $20, $20, $4c, $6f, $61 + DC.B $64, $20, $69, $6e, $20, $73, $6f, $6d, $65, $20, $4d, $61, $72, $69, $6f, $2e + DC.B $20, $20, $53, $6f, $6d, $65, $20, $43, $6f, $6e, $74, $72, $61, $2e, $20, $20 + DC.B $53, $6f, $6d, $65, $20, $4d, $65, $67, $61, $20, $4d, $61, $6e, $21, $21, $21 + DC.B $20, $20, $44, $6f, $20, $69, $74, $21, $20, $20, $20, $20, $44, $4f, $20, $49 + DC.B $54, $21, $21, $21, $20, $20, $59, $6f, $75, $72, $20, $4e, $45, $53, $20, $69 + DC.B $73, $20, $73, $74, $69, $6c, $6c, $20, $61, $6c, $69, $76, $65, $20, $69, $6e + DC.B $20, $27, $39, $39, $21, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $57 + DC.B $65, $6c, $63, $6f, $6d, $65, $20, $74, $6f, $20, $74, $68, $65, $20, $73, $65 + DC.B $63, $6f, $6e, $64, $20, $72, $65, $6c, $65, $61, $73, $65, $20, $6f, $66, $20 + DC.B $61, $20, $43, $2d, $36, $34, $2d, $73, $74, $79, $6c, $65, $20, $69, $6e, $74 + DC.B $72, $6f, $20, $63, $61, $6c, $6c, $65, $64, $20, $22, $53, $74, $61, $72, $73 + DC.B $20, $2a, $53, $45, $2a, $22, $20, $6f, $6e, $20, $74, $68, $65, $20, $4e, $45 + DC.B $53, $2e, $20, $20, $57, $68, $61, $74, $20, $64, $6f, $65, $73, $20, $74, $68 + DC.B $65, $20, $53, $45, $20, $73, $74, $61, $6e, $64, $20, $66, $6f, $72, $3f, $20 + DC.B $20, $57, $65, $6c, $6c, $2c, $20, $79, $6f, $75, $20, $63, $6f, $75, $6c, $64 + DC.B $20, $73, $61, $79, $20, $22, $53, $70, $65, $63, $69, $61, $6c, $20, $45, $64 + DC.B $69, $74, $69, $6f, $6e, $22, $20, $6f, $72, $20, $22, $53, $65, $63, $6f, $6e + DC.B $64, $20, $45, $64, $69, $74, $69, $6f, $6e, $22, $2c, $20, $62, $75, $74, $20 + DC.B $49, $27, $6c, $6c, $20, $73, $61, $79, $20, $22, $53, $6f, $75, $6e, $64, $20 + DC.B $45, $6e, $68, $61, $6e, $63, $65, $64, $2e, $22, $20, $20, $54, $68, $61, $74 + DC.B $27, $73, $20, $62, $65, $63, $61, $75, $73, $65, $20, $74, $68, $65, $72, $65 + DC.B $27, $73, $20, $61, $20, $6e, $65, $77, $20, $73, $6f, $6e, $67, $20, $6f, $6e + DC.B $20, $74, $68, $69, $73, $20, $76, $65, $72, $73, $69, $6f, $6e, $20, $6f, $66 + DC.B $20, $53, $74, $61, $72, $73, $2e, $20, $20, $49, $74, $20, $6e, $6f, $20, $6c + DC.B $6f, $6e, $67, $65, $72, $20, $68, $61, $73, $20, $6d, $75, $73, $69, $63, $20 + DC.B $6a, $75, $73, $74, $20, $72, $69, $70, $70, $65, $64, $20, $6f, $75, $74, $20 + DC.B $6f, $66, $20, $61, $20, $67, $61, $6d, $65, $3b, $20, $69, $74, $27, $73, $20 + DC.B $61, $6e, $20, $61, $63, $74, $75, $61, $6c, $20, $2a, $63, $6f, $6d, $70, $6f + DC.B $73, $69, $74, $69, $6f, $6e, $2a, $21, $21, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $54, $68, $69, $73, $20, $64, $65, $6d, $6f, $20, $64, $6f, $65 + DC.B $73, $6e, $27, $74, $20, $64, $6f, $20, $6d, $75, $63, $68, $2c, $20, $6e, $6f + DC.B $20, $33, $2d, $44, $20, $76, $65, $63, $74, $6f, $72, $73, $20, $6f, $72, $20 + DC.B $73, $63, $61, $6c, $69, $6e, $67, $20, $6f, $72, $20, $46, $4d, $56, $2c, $20 + DC.B $6f, $72, $20, $6e, $6f, $74, $68, $69, $6e, $27, $2e, $20, $20, $42, $75, $74 + DC.B $20, $69, $74, $20, $64, $6f, $65, $73, $20, $68, $65, $61, $72, $6b, $65, $6e + DC.B $20, $62, $61, $63, $6b, $20, $74, $6f, $20, $74, $68, $6f, $73, $65, $20, $68 + DC.B $65, $61, $64, $79, $20, $38, $2d, $62, $69, $74, $20, $64, $61, $79, $73, $20 + DC.B $6f, $66, $20, $31, $30, $20, $74, $6f, $20, $31, $35, $20, $79, $65, $61, $72 + DC.B $73, $20, $61, $67, $6f, $2c, $20, $77, $68, $65, $6e, $20, $77, $65, $20, $77 + DC.B $65, $72, $65, $20, $61, $6c, $6c, $20, $6a, $75, $73, $74, $20, $6b, $69, $64 + DC.B $73, $20, $74, $72, $79, $69, $6e, $67, $20, $74, $6f, $20, $68, $69, $74, $20 + DC.B $74, $68, $65, $20, $6d, $69, $6e, $75, $73, $20, $77, $6f, $72, $6c, $64, $20 + DC.B $6f, $72, $20, $73, $61, $76, $65, $20, $74, $68, $65, $20, $70, $72, $69, $6e + DC.B $63, $65, $73, $73, $2e, $20, $20, $49, $20, $64, $69, $64, $6e, $27, $74, $20 + DC.B $65, $76, $65, $6e, $20, $6b, $6e, $6f, $77, $20, $61, $6e, $79, $74, $68, $69 + DC.B $6e, $67, $20, $61, $62, $6f, $75, $74, $20, $73, $6f, $66, $74, $77, $61, $72 + DC.B $65, $20, $6f, $72, $20, $70, $72, $6f, $67, $72, $61, $6d, $6d, $69, $6e, $67 + DC.B $20, $62, $61, $63, $6b, $20, $74, $68, $65, $6e, $2e, $20, $20, $41, $6c, $6c + DC.B $20, $49, $20, $63, $6f, $75, $6c, $64, $20, $64, $6f, $20, $77, $61, $73, $20 + DC.B $70, $75, $6e, $63, $68, $20, $73, $6f, $6d, $65, $20, $42, $41, $53, $49, $43 + DC.B $20, $70, $72, $6f, $67, $72, $61, $6d, $73, $20, $69, $6e, $74, $6f, $20, $6d + DC.B $79, $20, $54, $52, $53, $2d, $38, $30, $2e, $20, $20, $3a, $2d, $50, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $49, $27, $76, $65, $20, $63, $6f, $6d, $65, $20 + DC.B $71, $75, $69, $74, $65, $20, $61, $20, $77, $61, $79, $20, $66, $72, $6f, $6d + DC.B $20, $74, $68, $61, $74, $20, $70, $6f, $69, $6e, $74, $2c, $20, $61, $6e, $64 + DC.B $20, $49, $27, $6d, $20, $67, $6c, $61, $64, $20, $69, $74, $20, $61, $6c, $6c + DC.B $20, $68, $61, $70, $70, $65, $6e, $65, $64, $20, $74, $68, $65, $20, $77, $61 + DC.B $79, $20, $69, $74, $20, $64, $69, $64, $2e, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $54, $68, $69, $73, $20, $64, $65, $6d, $6f + DC.B $20, $77, $61, $73, $20, $63, $72, $65, $61, $74, $65, $64, $20, $61, $6e, $64 + DC.B $20, $70, $72, $6f, $67, $72, $61, $6d, $6d, $65, $64, $20, $62, $79, $20, $6d + DC.B $65, $2c, $20, $43, $68, $72, $69, $73, $20, $43, $6f, $76, $65, $6c, $6c, $2c + DC.B $20, $61, $74, $20, $74, $68, $65, $20, $65, $6e, $64, $20, $6f, $66, $20, $4a + DC.B $75, $6e, $65, $20, $31, $39, $39, $39, $20, $41, $2e, $44, $2e, $20, $20, $49 + DC.B $20, $75, $73, $65, $64, $20, $6d, $79, $20, $43, $3d, $20, $41, $6d, $69, $67 + DC.B $61, $20, $31, $32, $30, $30, $20, $66, $6f, $72, $20, $70, $72, $69, $6d, $61 + DC.B $72, $79, $20, $70, $72, $6f, $67, $72, $61, $6d, $6d, $69, $6e, $67, $2c, $20 + DC.B $61, $73, $73, $65, $6d, $62, $6c, $69, $6e, $67, $2c, $20, $61, $6e, $64, $20 + DC.B $74, $65, $73, $74, $69, $6e, $67, $3b, $20, $61, $6e, $64, $20, $49, $20, $75 + DC.B $73, $65, $64, $20, $6d, $79, $20, $4d, $61, $63, $20, $61, $6e, $64, $20, $50 + DC.B $43, $20, $66, $6f, $72, $20, $73, $65, $63, $6f, $6e, $64, $61, $72, $79, $20 + DC.B $67, $72, $61, $70, $68, $69, $63, $73, $20, $70, $72, $6f, $67, $72, $61, $6d + DC.B $6d, $69, $6e, $67, $20, $61, $6e, $64, $20, $74, $65, $73, $74, $69, $6e, $67 + DC.B $2e, $20, $20, $54, $68, $69, $73, $20, $64, $65, $6d, $6f, $20, $68, $61, $73 + DC.B $20, $62, $65, $65, $6e, $20, $74, $65, $73, $74, $65, $64, $20, $6f, $6e, $20 + DC.B $61, $20, $72, $65, $61, $6c, $20, $4e, $45, $53, $20, $75, $73, $69, $6e, $67 + DC.B $20, $61, $6e, $20, $45, $45, $50, $52, $4f, $4d, $2d, $70, $72, $6f, $67, $72 + DC.B $61, $6d, $6d, $65, $72, $20, $63, $6f, $6e, $6e, $65, $63, $74, $65, $64, $20 + DC.B $74, $6f, $20, $6d, $79, $20, $50, $43, $2c, $20, $73, $6f, $20, $72, $65, $73 + DC.B $74, $20, $61, $73, $73, $75, $72, $65, $64, $20, $74, $68, $61, $74, $20, $69 + DC.B $74, $20, $77, $6f, $72, $6b, $73, $20, $6f, $6e, $20, $74, $68, $65, $20, $72 + DC.B $65, $61, $6c, $20, $74, $68, $69, $6e, $67, $20, $28, $6c, $6f, $6f, $6b, $73 + DC.B $20, $6e, $69, $63, $65, $20, $74, $6f, $6f, $21, $29, $20, $20, $20, $57, $68 + DC.B $79, $20, $64, $69, $64, $20, $49, $20, $64, $6f, $20, $74, $68, $69, $73, $3f + DC.B $20, $20, $49, $20, $64, $6f, $6e, $27, $74, $20, $6b, $6e, $6f, $77, $21, $20 + DC.B $20, $49, $66, $20, $73, $6f, $6d, $65, $62, $6f, $64, $79, $20, $61, $73, $6b + DC.B $73, $20, $6d, $65, $20, $77, $68, $79, $20, $74, $68, $65, $20, $48, $65, $6c + DC.B $6c, $20, $49, $20, $62, $6f, $74, $68, $65, $72, $20, $77, $69, $74, $68, $20 + DC.B $74, $68, $65, $20, $63, $72, $75, $73, $74, $79, $20, $6f, $6c, $64, $20, $38 + DC.B $2d, $62, $69, $74, $20, $6d, $61, $63, $68, $69, $6e, $65, $73, $2c, $20, $49 + DC.B $20, $6a, $75, $73, $74, $20, $74, $65, $6c, $6c, $20, $74, $68, $65, $6d, $3a + DC.B $20, $20, $6d, $65, $6d, $6f, $72, $69, $65, $73, $2e, $20, $20, $49, $74, $27 + DC.B $73, $20, $6c, $69, $6b, $65, $20, $74, $68, $65, $20, $54, $56, $20, $73, $68 + DC.B $6f, $77, $73, $20, $6f, $72, $20, $6d, $75, $73, $69, $63, $20, $74, $68, $61 + DC.B $74, $20, $79, $6f, $75, $20, $67, $72, $65, $77, $20, $75, $70, $20, $77, $69 + DC.B $74, $68, $2e, $20, $20, $54, $68, $65, $79, $27, $72, $65, $20, $6a, $75, $73 + DC.B $74, $20, $74, $68, $65, $72, $65, $2c, $20, $61, $6e, $64, $20, $77, $68, $65 + DC.B $6e, $20, $79, $6f, $75, $20, $77, $61, $74, $63, $68, $20, $74, $68, $65, $6d + DC.B $20, $6f, $72, $20, $6c, $69, $73, $74, $65, $6e, $20, $74, $6f, $20, $74, $68 + DC.B $65, $6d, $2c, $20, $74, $68, $65, $79, $20, $62, $72, $69, $6e, $67, $20, $62 + DC.B $61, $63, $6b, $20, $61, $20, $66, $6c, $6f, $6f, $64, $20, $6f, $66, $20, $6d + DC.B $65, $6d, $6f, $72, $69, $65, $73, $20, $6f, $66, $20, $74, $68, $6f, $73, $65 + DC.B $20, $74, $69, $6d, $65, $73, $2e, $20, $20, $57, $68, $65, $6e, $20, $49, $20 + DC.B $70, $6c, $61, $79, $20, $61, $20, $67, $61, $6d, $65, $20, $74, $68, $61, $74 + DC.B $20, $49, $20, $75, $73, $65, $64, $20, $74, $6f, $20, $70, $6c, $61, $79, $20 + DC.B $69, $6e, $20, $6d, $79, $20, $63, $68, $69, $6c, $64, $68, $6f, $6f, $64, $2c + DC.B $20, $49, $20, $67, $61, $69, $6e, $20, $61, $20, $73, $65, $6e, $73, $65, $20 + DC.B $6f, $66, $20, $63, $6f, $6d, $70, $6c, $65, $74, $69, $6f, $6e, $20, $6b, $6e + DC.B $6f, $77, $69, $6e, $67, $20, $68, $6f, $77, $20, $61, $6e, $64, $20, $77, $68 + DC.B $79, $20, $74, $68, $65, $20, $70, $72, $6f, $67, $72, $61, $6d, $6d, $65, $72 + DC.B $73, $20, $6d, $61, $64, $65, $20, $74, $68, $65, $20, $67, $61, $6d, $65, $20 + DC.B $74, $68, $65, $20, $77, $61, $79, $20, $74, $68, $65, $79, $20, $64, $69, $64 + DC.B $2e, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $43, $72, $65, $64, $69, $74, $73, $3a, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $43, $6f, $64, $65, $20, $26, $20, $47, $66, $78, $20, $62, $79, $20 + DC.B $20, $20, $43, $68, $72, $69, $73, $20, $43, $2e, $20, $28, $43, $4d, $43, $29 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $4d + DC.B $75, $73, $69, $63, $20, $62, $79, $20, $20, $20, $22, $4d, $65, $6d, $62, $6c + DC.B $65, $72, $73, $22, $20, $28, $35, $30, $31, $30, $2e, $30, $39, $35, $31, $40 + DC.B $74, $63, $6f, $6e, $2e, $6e, $65, $74, $29, $2c, $20, $61, $6e, $64, $20, $49 + DC.B $20, $74, $68, $61, $6e, $6b, $20, $68, $69, $6d, $20, $65, $74, $65, $72, $6e + DC.B $61, $6c, $6c, $79, $20, $66, $6f, $72, $20, $74, $68, $65, $20, $67, $72, $65 + DC.B $61, $74, $20, $6d, $75, $73, $69, $63, $61, $6c, $20, $72, $65, $6e, $64, $69 + DC.B $74, $69, $6f, $6e, $20, $6f, $66, $20, $4d, $61, $72, $74, $69, $6e, $20, $47 + DC.B $61, $6c, $77, $61, $79, $27, $73, $20, $22, $43, $6f, $6d, $69, $63, $20, $42 + DC.B $61, $6b, $65, $72, $79, $22, $20, $73, $6f, $6e, $67, $2e, $20, $20, $49, $74 + DC.B $20, $73, $6f, $75, $6e, $64, $73, $20, $66, $61, $6e, $74, $61, $73, $74, $69 + DC.B $63, $21, $20, $20, $4c, $69, $73, $74, $65, $6e, $20, $74, $6f, $20, $74, $68 + DC.B $6f, $73, $65, $20, $70, $75, $6c, $73, $65, $77, $69, $64, $74, $68, $20, $73 + DC.B $77, $65, $65, $70, $73, $2e, $2e, $2e, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $49, $66, $20 + DC.B $79, $6f, $75, $20, $70, $72, $65, $66, $65, $72, $20, $74, $68, $65, $20, $76 + DC.B $65, $72, $73, $69, $6f, $6e, $20, $77, $69, $74, $68, $20, $74, $68, $65, $20 + DC.B $53, $69, $6c, $69, $75, $73, $20, $6d, $75, $73, $69, $63, $20, $69, $6e, $20 + DC.B $69, $74, $2c, $20, $49, $27, $6d, $20, $73, $75, $72, $65, $20, $79, $6f, $75 + DC.B $20, $63, $61, $6e, $20, $73, $74, $69, $6c, $6c, $20, $66, $69, $6e, $64, $20 + DC.B $69, $74, $20, $61, $72, $6f, $75, $6e, $64, $20, $74, $68, $65, $20, $4e, $65 + DC.B $74, $2e, $2e, $2e, $2e, $20, $6f, $72, $20, $65, $2d, $6d, $61, $69, $6c, $20 + DC.B $6d, $65, $2e, $2e, $2e, $2e, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $49, $66, $20, $79, $6f, $75 + DC.B $20, $6c, $69, $6b, $65, $20, $74, $68, $69, $73, $20, $64, $65, $6d, $6f, $20 + DC.B $61, $74, $20, $61, $6c, $6c, $2c, $20, $6d, $61, $6b, $65, $20, $73, $75, $72 + DC.B $65, $20, $79, $6f, $75, $20, $76, $69, $73, $69, $74, $20, $6d, $79, $20, $68 + DC.B $6f, $6d, $65, $70, $61, $67, $65, $20, $6f, $6e, $20, $74, $68, $65, $20, $57 + DC.B $65, $62, $2e, $20, $20, $49, $27, $6d, $20, $75, $73, $75, $61, $6c, $6c, $79 + DC.B $20, $61, $74, $20, $68, $74, $74, $70, $3a, $2f, $2f, $6d, $79, $70, $61, $67 + DC.B $65, $2e, $64, $69, $72, $65, $63, $74, $2e, $63, $61, $2f, $63, $2f, $63, $63 + DC.B $6f, $76, $65, $6c, $6c, $20, $62, $75, $74, $20, $69, $66, $20, $74, $68, $61 + DC.B $74, $20, $64, $6f, $65, $73, $6e, $27, $74, $20, $77, $6f, $72, $6b, $2c, $20 + DC.B $74, $72, $79, $20, $65, $2d, $6d, $61, $69, $6c, $69, $6e, $67, $20, $6d, $65 + DC.B $20, $28, $63, $63, $6f, $76, $65, $6c, $6c, $40, $64, $69, $72, $65, $63, $74 + DC.B $2e, $63, $61, $29, $2e, $20, $20, $59, $6f, $75, $27, $72, $65, $20, $73, $75 + DC.B $72, $65, $20, $74, $6f, $20, $66, $69, $6e, $64, $20, $6c, $6f, $74, $73, $20 + DC.B $6f, $66, $20, $63, $6f, $6f, $6c, $20, $6c, $69, $74, $74, $6c, $65, $20, $74 + DC.B $68, $69, $6e, $67, $73, $2e, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $4f, $6b, $2c, $20, $73, $69, $6e, $63, $65, $20, $49 + DC.B $20, $73, $74, $69, $6c, $6c, $20, $68, $61, $76, $65, $20, $70, $6c, $65, $6e + DC.B $74, $79, $20, $6f, $66, $20, $73, $70, $61, $63, $65, $2c, $20, $6c, $65, $74 + DC.B $20, $6d, $65, $20, $67, $6f, $20, $6f, $6e, $20, $77, $69, $74, $68, $20, $74 + DC.B $68, $65, $20, $73, $74, $61, $6e, $64, $61, $72, $64, $20, $67, $72, $65, $65 + DC.B $74, $73, $3a, $20, $48, $69, $2c, $20, $74, $6f, $20, $6d, $79, $20, $62, $65 + DC.B $73, $74, $20, $70, $61, $6c, $20, $53, $69, $6d, $6f, $6e, $2c, $20, $61, $6e + DC.B $64, $20, $74, $6f, $20, $48, $75, $6e, $74, $65, $72, $2c, $20, $4a, $2e, $43 + DC.B $2e, $2c, $20, $4a, $61, $69, $6d, $65, $2c, $20, $4a, $61, $6e, $65, $74, $2c + DC.B $20, $52, $69, $63, $68, $2c, $20, $54, $65, $64, $2c, $20, $46, $61, $72, $73 + DC.B $68, $61, $64, $2c, $20, $44, $65, $6e, $69, $73, $65, $2c, $20, $61, $6e, $64 + DC.B $20, $6c, $69, $74, $74, $6c, $65, $20, $45, $74, $68, $61, $6e, $2e, $20, $20 + DC.B $48, $69, $2c, $20, $74, $6f, $20, $6d, $79, $20, $66, $61, $6d, $69, $6c, $79 + DC.B $2c, $20, $61, $6c, $6c, $20, $74, $68, $65, $20, $70, $65, $6f, $70, $6c, $65 + DC.B $20, $6f, $6e, $20, $74, $68, $65, $20, $49, $6e, $74, $65, $72, $6e, $65, $74 + DC.B $20, $77, $68, $6f, $6d, $20, $49, $27, $76, $65, $20, $6d, $65, $74, $2c, $20 + DC.B $61, $6c, $6c, $20, $74, $68, $65, $20, $70, $65, $6f, $70, $6c, $65, $20, $6f + DC.B $6e, $20, $74, $68, $65, $20, $4e, $45, $53, $20, $61, $6e, $64, $20, $4e, $45 + DC.B $53, $44, $65, $76, $20, $6c, $69, $73, $74, $73, $2e, $20, $20, $20, $20, $20 + DC.B $20, $20, $49, $20, $77, $6f, $75, $6c, $64, $20, $6c, $69, $6b, $65, $20, $74 + DC.B $6f, $20, $73, $61, $79, $20, $74, $68, $61, $6e, $6b, $73, $20, $74, $6f, $20 + DC.B $6d, $79, $20, $66, $72, $69, $65, $6e, $64, $73, $20, $66, $6f, $72, $20, $6e + DC.B $6f, $74, $20, $64, $65, $73, $65, $72, $74, $69, $6e, $67, $20, $6d, $65, $2c + DC.B $20, $6e, $6f, $20, $74, $68, $61, $6e, $6b, $73, $20, $74, $6f, $20, $4e, $69 + DC.B $6e, $74, $65, $6e, $64, $6f, $20, $66, $6f, $72, $20, $64, $65, $73, $65, $72 + DC.B $74, $69, $6e, $67, $20, $74, $68, $65, $20, $4e, $45, $53, $2e, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $53, $50, $45, $43, $49, $41, $4c, $20, $74, $68, $61, $6e, $6b, $73 + DC.B $20, $61, $6e, $64, $20, $67, $72, $65, $65, $74, $69, $6e, $67, $73, $20, $67 + DC.B $6f, $20, $74, $6f, $3a, $20, $4d, $61, $72, $6b, $20, $4b, $2e, $2c, $20, $4c + DC.B $6f, $6f, $70, $79, $2c, $20, $42, $61, $6e, $61, $6e, $6d, $6f, $73, $2c, $20 + DC.B $4b, $65, $76, $69, $6e, $20, $48, $2e, $2c, $20, $4d, $61, $74, $74, $20, $43 + DC.B $2e, $2c, $20, $4d, $65, $6d, $62, $6c, $65, $72, $73, $2c, $20, $4d, $61, $72 + DC.B $61, $74, $20, $46, $2e, $2c, $20, $61, $6e, $64, $20, $74, $68, $65, $20, $72 + DC.B $65, $73, $74, $2e, $20, $20, $44, $6f, $6e, $27, $74, $20, $66, $65, $65, $6c + DC.B $20, $62, $61, $64, $20, $69, $66, $20, $79, $6f, $75, $27, $72, $65, $20, $6e + DC.B $6f, $74, $20, $69, $6e, $20, $74, $68, $69, $73, $20, $6c, $69, $73, $74, $2e + DC.B $20, $20, $4d, $61, $69, $6c, $20, $6d, $65, $20, $61, $6e, $64, $20, $49, $27 + DC.B $6c, $6c, $20, $73, $61, $79, $20, $68, $69, $20, $74, $6f, $20, $79, $6f, $75 + DC.B $20, $74, $6f, $6f, $2e, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $28, $4d, $61, $6e, $2c, $20, $64, $6f, $6e, $27 + DC.B $74, $20, $73, $63, $72, $6f, $6c, $6c, $65, $72, $73, $20, $61, $6c, $77, $61 + DC.B $79, $73, $20, $6d, $61, $6b, $65, $20, $69, $74, $20, $73, $65, $65, $6d, $20 + DC.B $74, $68, $61, $74, $20, $6f, $6e, $65, $20, $68, $61, $73, $20, $6d, $6f, $72 + DC.B $65, $20, $66, $72, $69, $65, $6e, $64, $73, $20, $74, $68, $61, $6e, $20, $6f + DC.B $6e, $65, $20, $61, $63, $74, $75, $61, $6c, $6c, $79, $20, $68, $61, $73, $3f + DC.B $29, $20, $20, $20, $3a, $2d, $29, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $54, $68 + DC.B $69, $73, $20, $74, $65, $78, $74, $20, $77, $69, $6c, $6c, $20, $77, $72, $61 + DC.B $70, $20, $73, $6f, $6f, $6e, $2e, $20, $20, $50, $72, $65, $73, $73, $20, $22 + DC.B $53, $74, $61, $72, $74, $22, $20, $74, $6f, $20, $72, $65, $73, $74, $61, $72 + DC.B $74, $20, $74, $68, $65, $20, $73, $6f, $6e, $67, $2e, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20 + DC.B $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $20, $4f + DC.B $68, $2c, $20, $61, $6e, $64, $20, $6f, $6e, $65, $20, $6d, $6f, $72, $65, $20 + DC.B $74, $68, $69, $6e, $67, $3a, $20, $20, $20, $20, $20, $20, $20, $20, $20, $54 + DC.B $68, $65, $20, $4e, $45, $53, $20, $49, $53, $20, $61, $20, $73, $74, $61, $72 + DC.B $20, $69, $6e, $20, $31, $39, $39, $39, $21, $21 diff --git a/stars_se/starsnam.asm b/stars_se/starsnam.asm new file mode 100644 index 0000000..72bbde0 --- /dev/null +++ b/stars_se/starsnam.asm @@ -0,0 +1,66 @@ +; stars.nam + + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04 + DC.B $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04 + DC.B $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05 + DC.B $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $f0, $f1, $f2, $f3, $00, $00, $00, $06, $07, $08, $09 + DC.B $0a, $0b, $0c, $0d, $0e, $0f, $00, $ec, $00, $f0, $f1, $f2, $f3, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $f4, $f5, $f6, $f7, $00, $00, $00, $16, $17, $18, $19 + DC.B $1a, $1b, $1c, $1d, $1e, $1f, $00, $00, $00, $f4, $f5, $f6, $f7, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $f8, $f9, $fa, $fb, $00, $00, $00, $11, $12, $13, $14 + DC.B $15, $00, $fc, $fd, $fe, $ff, $00, $00, $00, $f8, $f9, $fa, $fb, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $ec, $00, $00, $00, $00, $00, $00, $00, $00, $00, $80, $81, $82, $83 + DC.B $84, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $d3, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $85, $86, $87, $88, $88, $88 + DC.B $88, $89, $8a, $8b, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $8c, $8d, $8e, $88, $88, $88, $88, $88 + DC.B $88, $88, $88, $8f, $90, $91, $00, $00, $00, $00, $00, $d3, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $92, $93, $94, $95, $88, $88, $88, $88 + DC.B $88, $88, $88, $96, $97, $98, $99, $9a, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $9b, $9c, $9d, $9e, $9f, $88, $a0, $88 + DC.B $88, $a1, $a2, $a3, $a4, $a5, $a6, $a7, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $ed, $d3, $00, $a8, $a9, $aa, $ab, $ac, $9e, $ad, $ae + DC.B $af, $b0, $b1, $b2, $b3, $b4, $b5, $b6, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $b7, $b8, $b9, $ba, $bb, $ab, $bc, $bd + DC.B $be, $bf, $c0, $c1, $c2, $c3, $c4, $c5, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $c6, $c7, $c8, $c9, $ca, $cb, $cc + DC.B $cd, $ce, $c2, $cf, $d0, $d1, $d2, $d3, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $d4, $d5, $c8, $d6, $d7 + DC.B $d8, $d9, $da, $db, $dc, $dd, $00, $00, $00, $00, $00, $00, $00, $ed, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $de, $df, $e0, $e1 + DC.B $e2, $e3, $e4, $e5, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $ec, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $e6, $e7 + DC.B $e8, $e9, $ea, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $eb, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04 + DC.B $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04 + DC.B $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05 + DC.B $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04 + DC.B $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04, $04 + DC.B $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05 + DC.B $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05, $05 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $55, $55, $55, $be, $be, $76, $55, $55 + DC.B $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55, $55 + DC.B $55, $55, $15, $55, $55, $55, $55, $55, $05, $05, $05, $05, $05, $05, $05, $05 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 diff --git a/status.txt b/status.txt new file mode 100644 index 0000000..ccda32e --- /dev/null +++ b/status.txt @@ -0,0 +1,178 @@ +^ NESICIDE ^ Nestopia v1.40 ^ Nintendulator 0.975 ^ NESICIDE Notes ^ ROM Path ^ Other Info ^ +| ???? | ???? | ???? | Not sure yet. | apu_mixer\dmc.nes | | +| ???? | ???? | ???? | Not sure yet. | apu_mixer\noise.nes | | +| ???? | ???? | ???? | Not sure yet. | apu_mixer\square.nes | | +| ???? | ???? | ???? | Not sure yet. | apu_mixer\triangle.nes | | +| PASS | PASS | | | apu_reset\4015_cleared.nes | | +| PASS | PASS | | | apu_reset\4017_timing.nes | Delay: 11 clocks | +| PASS | FAIL | | | apu_reset\4017_written.nes | | +| PASS | PASS | | | apu_reset\irq_flag_cleared.nes | | +| PASS | PASS | | | apu_reset\len_ctrs_enabled.nes | | +| PASS | PASS | | | apu_reset\works_immediately.nes | | +| PASS | PASS | | | apu_test\apu_test.nes | | +| PASS | PASS | | | apu_test\rom_singles\1-len_ctr.nes | | +| PASS | PASS | | | apu_test\rom_singles\2-len_table.nes | | +| PASS | PASS | | | apu_test\rom_singles\3-irq_flag.nes | | +| PASS | PASS | | | apu_test\rom_singles\4-jitter.nes | | +| PASS | PASS | | | apu_test\rom_singles\5-len_timing.nes | | +| PASS | PASS | | | apu_test\rom_singles\6-irq_flag_timing.nes | | +| PASS | PASS | | | apu_test\rom_singles\7-dmc_basics.nes | | +| PASS | PASS | | | apu_test\rom_singles\8-dmc_rates.nes | | +| PASS | PASS | | | blargg_apu_2005.07.30\01.len_ctr.nes | | +| PASS | PASS | | | blargg_apu_2005.07.30\02.len_table.nes | | +| PASS | PASS | | | blargg_apu_2005.07.30\03.irq_flag.nes | | +| PASS | PASS | | | blargg_apu_2005.07.30\04.clock_jitter.nes | | +| PASS | PASS | | | blargg_apu_2005.07.30\05.len_timing_mode0.nes | | +| PASS | PASS | | | blargg_apu_2005.07.30\06.len_timing_mode1.nes | | +| PASS | PASS | | | blargg_apu_2005.07.30\07.irq_flag_timing.nes | | +| PASS | PASS | | | blargg_apu_2005.07.30\08.irq_timing.nes | | +| PASS | PASS | | | blargg_apu_2005.07.30\09.reset_timing.nes | | +| PASS | PASS | | | blargg_apu_2005.07.30\10.len_halt_timing.nes | | +| PASS | PASS | | | blargg_apu_2005.07.30\11.len_reload_timing.nes | | +| FAIL | FAIL | | Errors: 2 | blargg_nes_cpu_test5\cpu.nes | | +| PASS | PASS | | | blargg_nes_cpu_test5\official.nes | | +| PASS | PASS | | | blargg_ppu_tests_2005.09.15b\palette_ram.nes | | +| PASS | FAIL | | | blargg_ppu_tests_2005.09.15b\power_up_palette.nes | | +| PASS | PASS | | | blargg_ppu_tests_2005.09.15b\sprite_ram.nes | | +| PASS | PASS | | | blargg_ppu_tests_2005.09.15b\vbl_clear_time.nes | | +| PASS | PASS | | | blargg_ppu_tests_2005.09.15b\vram_access.nes | | +| PASS | PASS | | | branch_timing_tests\1.Branch_Basics.nes | | +| PASS | PASS | | | branch_timing_tests\2.Backward_Branch.nes | | +| PASS | PASS | | | branch_timing_tests\3.Forward_Branch.nes | | +| PASS | PASS | | | cpu_dummy_reads\cpu_dummy_reads.nes | | +| FAIL | FAIL | | Fails test 2 | cpu_interrupts_v2\cpu_interrupts.nes | | +| PASS | PASS | | | cpu_interrupts_v2\rom_singles\1-cli_latency.nes | | +| FAIL | PASS | | Wrong clocking | cpu_interrupts_v2\rom_singles\2-nmi_and_brk.nes | | +| FAIL | PASS | | Wrong clocking | cpu_interrupts_v2\rom_singles\3-nmi_and_irq.nes | | +| FAIL | FAIL | | Wrong clocking | cpu_interrupts_v2\rom_singles\4-irq_and_dma.nes | | +| FAIL | FAIL | | Wrong clocking | cpu_interrupts_v2\rom_singles\5-branch_delays_irq.nes | | +| PASS | PASS | | | cpu_reset\ram_after_reset.nes | | +| PASS | PASS | | | cpu_reset\registers.nes | | +| PASS | PASS | | | cpu_timing_test6\cpu_timing_test.nes | Passes official and NOP, fails undocumented (temporarily due to CPU restructuring) | +| PASS | PASS | | | dmc_dma_during_read4\dma_2007_read.nes | CRC: 5E3DF9C4 | +| PASS | PASS | | | dmc_dma_during_read4\dma_2007_write.nes | | +| FAIL | PASS | | | dmc_dma_during_read4\dma_4016_read.nes | Too many reads stealing joypad bits. | +| FAIL | FAIL | | | dmc_dma_during_read4\double_2007_read.nes | | +| PASS | PASS | | | dmc_dma_during_read4\read_write_2007.nes | | +| ???? | ???? | ???? | Not sure yet. | dmc_tests\buffer_retained.nes | | +| ???? | ???? | ???? | Not sure yet. | dmc_tests\latency.nes | | +| ???? | ???? | ???? | Not sure yet. | dmc_tests\status.nes | | +| ???? | ???? | ???? | Not sure yet. | dmc_tests\status_irq.nes | | +| PASS | PASS | | | exram\mmc5exram.nes | | +| PASS | PASS | | | full_palette\flowing_palette.nes | | +| PASS | PASS | | | full_palette\full_palette.nes | | +| PASS | PASS | | | full_palette\full_palette_smooth.nes | | +| PASS | PASS | | | instr_misc\instr_misc.nes | | +| PASS | PASS | | | instr_misc\rom_singles\01-abs_x_wrap.nes | | +| PASS | PASS | | | instr_misc\rom_singles\02-branch_wrap.nes | | +| PASS | PASS | | | instr_misc\rom_singles\03-dummy_reads.nes | | +| PASS | PASS | | | instr_misc\rom_singles\04-dummy_reads_apu.nes | | +| PASS | PASS | | | instr_test\rom_singles\01-implied.nes | | +| FAIL | FAIL | | ARR, ATX, AXS fail | instr_test-v3\rom_singles\02-immediate.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\03-zero_page.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\04-zp_xy.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\05-absolute.nes | | +| FAIL | FAIL | | SYA, SXA fail | instr_test-v3\rom_singles\06-abs_xy.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\07-ind_x.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\08-ind_y.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\09-branches.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\10-stack.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\11-jmp_jsr.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\12-rts.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\13-rti.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\14-brk.nes | | +| PASS | PASS | | | instr_test-v3\rom_singles\15-special.nes | | +| FAIL | FAIL | | Fails on 02-immediate.nes run. | instr_test-v3\all_instrs.nes | | +| PASS | PASS | | | instr_test-v3\official_only.nes | | +| PASS | PASS | | | instr_timing\instr_timing.nes | Fails undocumented (temporarily, due to CPU restructuring) | +| PASS | PASS | | | instr_timing\rom_singles\1-instr_timing.nes | Fails undocumented (temporarily, due to CPU restructuring) | +| PASS | PASS | | | instr_timing\rom_singles\2-branch_timing.nes | | +| PASS | PASS | | | mmc3_irq_tests\1.Clocking.nes | | +| PASS | PASS | | | mmc3_irq_tests\2.Details.nes | | +| PASS | PASS | | | mmc3_irq_tests\3.A12_clocking.nes | | +| PASS | PASS | | | mmc3_irq_tests\4.Scanline_timing.nes | Note: latest version of this test fails (see mmc3_test below). | +| FAIL | FAIL | | Code $03: IRQ shouldn't occur when reloading after counter normally reaches 0 | mmc3_irq_tests\5.MMC3_rev_A.nes | | +| FAIL | PASS | | Code $02: Should reload and set IRQ every clock when reload is 0 | mmc3_irq_tests\6.MMC3_rev_B.nes | | +| PASS | PASS | | | mmc3_test\1-clocking.nes | | +| PASS | PASS | | | mmc3_test\2-details.nes | | +| PASS | PASS | | | mmc3_test\3-A12_clocking.nes | | +| FAIL | PASS | | Scanline 0 IRQ should occur later when $2000=$08 | mmc3_test\4-scanline_timing.nes | | +| FAIL | PASS | | Code $03: IRQ shouldn't occur when reloading after counter normally reaches 0 | mmc3_irq_tests\5-MMC3.nes | | +| FAIL | FAIL | | Code $02: Should reload and set IRQ every clock when reload is 0 | mmc3_irq_tests\6.MMC6.nes | | +| PASS | PASS | | | nmi_sync\demo_ntsc.nes | | +| PASS | PASS | | | nmi_sync\demo_pal.nes | | +| PASS | PASS | | | oam_read\oam_read.nes | | +| PASS | PASS | | | oam_stress\oam_stress.nes | | +| FAIL | PASS | | | other\blargg_litewall-2.nes | Strange sawtooth artifacting. | +| PASS | PASS | | | other\litewall5.nes | | +| PASS | PASS | | | other\midscanline.nes | | +| PASS | PASS | | | other\nestest.nes | | +| PASS | PASS | | | other\PCM.demo.wgraphics.nes | | +| PASS | PASS | | | other\RasterChromaLuma.NES | | +| PASS | PASS | | | other\RasterDemo.NES | | +| PASS | PASS | | | other\RasterTest1.NES | | +| PASS | PASS | | | other\RasterTest2.NES | | +| PASS | PASS | | | other\RasterTest3.NES | | +| PASS | PASS | | | other\RasterTest3a.NES | | +| PASS | PASS | | | other\RasterTest3b.NES | | +| PASS | PASS | | | other\RasterTest3c.NES | | +| PASS | PASS | | | other\RasterTest3d.NES | | +| PASS | PASS | | | other\RasterTest3e.NES | | +| FAIL | ???? | | Incorrect OAM readback. | other\read2004.nes | Tests OAM readback. | +| PASS | PASS | | | other\Retrocoders - Years behind.NES | | +| PASS | PASS | | | other\S0.NES | Tests Sprite-0 hit visually. | +| PASS | PASS | | | pal_apu_tests\01.len_ctr.nes | | +| PASS | PASS | | | pal_apu_tests\02.len_table.nes | | +| PASS | PASS | | | pal_apu_tests\03.irq_flag.nes | | +| PASS | PASS | | | pal_apu_tests\04.clock_jitter.nes | | +| PASS | PASS | | | pal_apu_tests\05.len_timing_mode0.nes | | +| PASS | PASS | | | pal_apu_tests\06.len_timing_mode1.nes | | +| PASS | PASS | | | pal_apu_tests\07.irq_flag_timing.nes | | +| PASS | PASS | | Code $02: Too soon | pal_apu_tests\08.irq_timing.nes | | +| PASS | PASS | | | pal_apu_tests\10.len_halt_timing.nes | | +| PASS | PASS | | | pal_apu_tests\11.len_reload_timing.nes | | +| PASS | FAIL | | | ppu_open_bus\ppu_open_bus.nes | | +| PASS | PASS | | | ppu_vbl_nmi\rom_singles\01-vbl_basics.nes | | +| PASS | PASS | | | ppu_vbl_nmi\rom_singles\02-vbl_set_time.nes | | +| PASS | PASS | | | ppu_vbl_nmi\rom_singles\03-vbl_clear_time.nes | | +| PASS | PASS | | | ppu_vbl_nmi\rom_singles\04-nmi_control.nes | | +| PASS | PASS | | | ppu_vbl_nmi\rom_singles\05-nmi_timing.nes | | +| PASS | PASS | | | ppu_vbl_nmi\rom_singles\06-suppression.nes | | +| PASS | FAIL | | | ppu_vbl_nmi\rom_singles\07-nmi_on_timing.nes | | +| PASS | PASS | | | ppu_vbl_nmi\rom_singles\08-nmi_off_timing.nes | | +| PASS | PASS | | | ppu_vbl_nmi\rom_singles\09-even_odd_frames.nes | | +| FAIL | PASS | | Failed #3 | ppu_vbl_nmi\rom_singles\10-even_odd_timing.nes | | +| FAIL | FAIL | | Fails on 10-even-odd-timing.nes | ppu_vbl_nmi\ppu_vbl_nmi.nes | | +| FAIL | FAIL | | Conflicts: 72/1000 | read_joy3\count_errors.nes | | +| FAIL | FAIL | | Errors: 7/1000 | read_joy3\count_errors_fast.nes | | +| PASS | PASS | | | read_joy3\test_buttons.nes | | +| PASS | PASS | | | read_joy3\thorough_test.nes | | +| PASS | PASS | | | scanline\scanline.nes | | +| PASS | PASS | | | scrolltest\scroll.nes | Strange characters instead of graphics but equivalent results on other emus. | +| PASS | FAIL | | | sprdma_and_dmc_dma\sprdma_and_dmc_dma.nes | | +| PASS | FAIL | | | sprdma_and_dmc_dma\sprdma_and_dmc_dma_512.nes | | +| PASS | PASS | | | sprite_hit_tests_2005.10.05\01.basics.nes | | +| PASS | PASS | | | sprite_hit_tests_2005.10.05\02.alignment.nes | | +| PASS | PASS | | | sprite_hit_tests_2005.10.05\03.corners.nes | | +| PASS | PASS | | | sprite_hit_tests_2005.10.05\04.flip.nes | | +| PASS | PASS | | | sprite_hit_tests_2005.10.05\05.left_clip.nes | | +| PASS | PASS | | | sprite_hit_tests_2005.10.05\06.right_edge.nes | | +| PASS | PASS | | | sprite_hit_tests_2005.10.05\07.screen_bottom.nes | | +| PASS | PASS | | | sprite_hit_tests_2005.10.05\08.double_height.nes | | +| PASS | PASS | | | sprite_hit_tests_2005.10.05\09.timing_basics.nes | | +| PASS | PASS | | | sprite_hit_tests_2005.10.05\10.timing_order.nes | | +| PASS | PASS | | | sprite_hit_tests_2005.10.05\11.edge_timing.nes | | +| PASS | PASS | | | sprite_overflow_tests\1.Basics.nes | | +| PASS | PASS | | | sprite_overflow_tests\2.Details.nes | | +| PASS | PASS | | | sprite_overflow_tests\3.Timing.nes | | +| PASS | PASS | | | sprite_overflow_tests\4.Obscure.nes | | +| PASS | PASS | | | sprite_overflow_tests\5.Emulator.nes | | +| PASS | PASS | | | stomper\smwstomp.nes | | +| FAIL | FAIL | | 12/14 PPU, 0/0 APU, 48/48 CPU, 0/0 I/O | stress\NEStress.nes | | +| PASS | PASS | | | vbl_nmi_timing\1.frame_basics.nes | | +| PASS | PASS | | | vbl_nmi_timing\2.vbl_timing.nes | | +| PASS | PASS | | | vbl_nmi_timing\3.even_odd_frames.nes | | +| PASS | PASS | | | vbl_nmi_timing\4.vbl_clear_timing.nes | | +| PASS | PASS | | | vbl_nmi_timing\5.nmi_suppression.nes | | +| PASS | PASS | | | vbl_nmi_timing\6.nmi_disable.nes | | +| PASS | PASS | | | vbl_nmi_timing\7.nmi_timing.nes | | diff --git a/stomper/smwnsf.dat b/stomper/smwnsf.dat new file mode 100644 index 0000000..81d500c --- /dev/null +++ b/stomper/smwnsf.dat @@ -0,0 +1,1024 @@ +.db $00,$81,$04,$81,$10,$81,$16,$81,$1C,$81,$24,$81,$40,$81,$46,$81 +.db $4A,$81,$58,$81,$5C,$81,$76,$81,$80,$81,$8A,$81,$94,$81,$9C,$81 +.db $A6,$81,$AC,$81,$C2,$81,$CD,$81,$D9,$81,$E3,$81,$ED,$81,$F7,$81 +.db $FF,$81,$03,$82,$1F,$82,$2B,$82,$3B,$82,$41,$82,$45,$82,$49,$82 +.db $4F,$82,$6F,$82,$75,$82,$7D,$82,$8D,$82,$91,$82,$00,$81,$00,$81 +.db $00,$81,$00,$81,$00,$81,$93,$82,$00,$81,$00,$81,$9D,$82,$B5,$82 +.db $BF,$82,$C9,$82,$D1,$82,$FD,$82,$1B,$83,$00,$81,$27,$83,$37,$83 +.db $3F,$83,$43,$83,$00,$81,$00,$81,$00,$81,$00,$81,$00,$81,$00,$81 +.db $4F,$83,$5F,$83,$79,$83,$81,$83,$97,$83,$A1,$83,$B3,$83,$C7,$83 +.db $0B,$84,$0F,$84,$13,$84,$25,$84,$2B,$84,$31,$84,$37,$84,$00,$81 +.db $3D,$84,$4E,$84,$5E,$84,$62,$84,$6E,$84,$72,$84,$86,$84,$B0,$84 +.db $B4,$84,$C0,$84,$00,$81,$CC,$84,$D0,$84,$DE,$84,$EE,$84,$08,$85 +.db $20,$85,$28,$85,$3E,$85,$54,$85,$76,$85,$00,$81,$00,$81,$00,$81 +.db $00,$81,$00,$81,$00,$81,$00,$81,$98,$85,$00,$81,$00,$81,$9A,$85 +.db $00,$81,$00,$81,$00,$81,$00,$81,$A0,$85,$A8,$85,$00,$81,$00,$81 +.db $00,$81,$00,$81,$00,$81,$B3,$85,$00,$81,$00,$81,$00,$81,$00,$81 +.db $00,$00,$FF,$00,$05,$0F,$02,$0C,$04,$04,$08,$02,$08,$01,$FF,$00 +.db $01,$06,$FE,$04,$FF,$FE,$01,$04,$FE,$02,$FF,$FE,$08,$00,$08,$40 +.db $FE,$80,$FF,$FE,$01,$14,$01,$0A,$01,$05,$01,$03,$0A,$00,$01,$01 +.db $01,$02,$01,$01,$01,$00,$01,$FE,$01,$FE,$01,$00,$01,$00,$FF,$F0 +.db $06,$0F,$FE,$00,$FF,$00,$FE,$80,$FF,$FE,$01,$01,$01,$03,$02,$05 +.db $03,$04,$06,$03,$FE,$02,$FF,$FE,$FE,$40,$FF,$FE,$49,$10,$42,$08 +.db $04,$04,$C8,$02,$01,$02,$02,$04,$01,$02,$02,$00,$01,$FE,$02,$FC +.db $01,$FE,$02,$00,$FF,$F0,$06,$0F,$02,$00,$03,$0F,$FE,$00,$FF,$00 +.db $01,$03,$01,$06,$01,$03,$FE,$01,$FF,$FE,$01,$02,$01,$04,$01,$02 +.db $FE,$01,$FF,$FE,$01,$00,$01,$40,$FE,$80,$FF,$FE,$02,$00,$02,$01 +.db $02,$02,$02,$01,$FF,$F8,$0A,$0F,$FE,$00,$FF,$FE,$01,$06,$11,$04 +.db $02,$01,$01,$03,$11,$02,$02,$01,$01,$02,$11,$01,$02,$01,$FE,$01 +.db $FF,$FE,$00,$01,$40,$01,$80,$01,$40,$10,$00,$FF,$F6,$12,$0F,$FE +.db $00,$01,$80,$01,$40,$FE,$00,$FF,$FE,$01,$02,$01,$04,$01,$08,$FE +.db $04,$FF,$FE,$01,$01,$01,$02,$01,$04,$FE,$02,$FF,$FE,$03,$01,$03 +.db $02,$03,$01,$03,$00,$FF,$F8,$01,$80,$01,$40,$FE,$00,$FF,$00,$FE +.db $0F,$FF,$FE,$01,$08,$01,$06,$11,$04,$01,$00,$01,$04,$01,$03,$11 +.db $02,$01,$00,$01,$02,$12,$01,$01,$00,$13,$01,$FE,$00,$FF,$FE,$01 +.db $00,$01,$40,$01,$80,$01,$40,$10,$00,$FF,$F6,$01,$08,$01,$06,$01 +.db $04,$01,$03,$01,$02,$01,$01,$0E,$00,$FF,$F2,$28,$0F,$FE,$00,$FF +.db $FE,$FE,$04,$FF,$FE,$FE,$02,$FF,$FE,$01,$80,$FE,$00,$FF,$FE,$01 +.db $0F,$01,$0E,$02,$0D,$02,$0C,$02,$0B,$03,$0A,$03,$09,$03,$08,$03 +.db $07,$04,$06,$0E,$05,$04,$04,$04,$03,$DE,$02,$05,$01,$FF,$00,$01 +.db $00,$FE,$0F,$FF,$FE,$0A,$00,$03,$01,$05,$00,$FF,$FC,$01,$04,$0F +.db $07,$0F,$06,$0F,$04,$0F,$03,$0F,$02,$0F,$01,$FF,$00,$FE,$80,$FF +.db $FE,$FF,$01,$01,$F6,$0A,$00,$03,$03,$03,$00,$FF,$FC,$02,$0B,$03 +.db $0A,$03,$09,$03,$08,$03,$07,$04,$06,$0E,$05,$04,$04,$04,$03,$DE +.db $02,$05,$01,$FF,$00,$01,$08,$01,$04,$01,$02,$FE,$01,$FF,$FE,$01 +.db $C0,$01,$80,$01,$40,$03,$00,$FF,$F8,$01,$04,$01,$08,$FE,$04,$FF +.db $FE,$01,$01,$01,$02,$01,$04,$01,$08,$01,$04,$01,$02,$02,$00,$01 +.db $01,$01,$01,$01,$02,$01,$04,$01,$02,$01,$01,$02,$00,$01,$01,$01 +.db $01,$01,$01,$01,$02,$01,$01,$01,$01,$02,$00,$FF,$00,$01,$01,$01 +.db $01,$01,$02,$01,$04,$01,$02,$01,$01,$02,$00,$01,$01,$01,$01,$01 +.db $01,$01,$02,$01,$01,$01,$01,$02,$00,$FF,$00,$01,$C0,$01,$80,$01 +.db $40,$01,$00,$01,$40,$FF,$F6,$01,$0E,$04,$0F,$01,$0C,$04,$0E,$01 +.db $0B,$03,$0C,$FE,$0A,$FF,$FE,$01,$80,$01,$40,$01,$C0,$FF,$F6,$FE +.db $80,$FF,$00,$01,$00,$01,$FE,$01,$FE,$01,$FD,$01,$FC,$FF,$F6,$02 +.db $04,$02,$02,$01,$04,$02,$02,$02,$01,$01,$02,$03,$01,$FF,$00,$01 +.db $0F,$02,$08,$02,$04,$01,$08,$02,$04,$02,$02,$01,$04,$02,$02,$02 +.db $01,$01,$02,$03,$01,$FE,$00,$FF,$FE,$03,$00,$03,$40,$04,$80,$FF +.db $FA,$01,$00,$01,$14,$01,$04,$01,$18,$01,$08,$01,$1C,$01,$10,$01 +.db $24,$01,$20,$01,$34,$FF,$EC,$01,$00,$01,$80,$01,$40,$01,$C0,$FF +.db $F6,$01,$0F,$05,$00,$05,$0E,$05,$00,$05,$0A,$05,$00,$05,$04,$FE +.db $00,$FF,$F6,$05,$0F,$05,$0C,$05,$0A,$05,$08,$0A,$04,$0A,$02,$0A +.db $01,$0A,$00,$A0,$01,$FF,$00,$05,$01,$05,$01,$05,$02,$05,$03,$05 +.db $04,$05,$05,$05,$06,$05,$07,$05,$08,$05,$09,$05,$0A,$05,$0B,$05 +.db $0C,$05,$0D,$05,$0E,$05,$0F,$05,$0F,$05,$0E,$05,$0D,$05,$0C,$05 +.db $0B,$05,$0A,$05,$09,$05,$08,$05,$07,$05,$06,$05,$05,$05,$04,$05 +.db $03,$05,$02,$05,$01,$05,$01,$A0,$01,$FF,$00,$FE,$0F,$FF,$00,$01 +.db $00,$FF,$00,$04,$F6,$04,$EC,$1E,$E2,$04,$D8,$04,$E2,$04,$EC,$04 +.db $F6,$04,$00,$FF,$F0,$01,$05,$FE,$03,$FF,$FE,$01,$80,$FE,$40,$FF +.db $FE,$03,$00,$0F,$01,$FF,$FC,$03,$01,$03,$02,$FF,$FC,$08,$23,$07 +.db $19,$06,$0F,$05,$0F,$04,$0F,$03,$0F,$02,$0F,$01,$FF,$FE,$23,$07 +.db $19,$06,$0F,$05,$0F,$04,$0F,$03,$0F,$02,$0F,$01,$FF,$FE,$FE,$0F +.db $FF,$00,$01,$0F,$02,$08,$04,$04,$08,$02,$08,$01,$FF,$00,$FE,$80 +.db $FF,$00,$01,$00,$01,$FE,$01,$FE,$01,$FE,$01,$FE,$01,$FD,$01,$FD +.db $01,$FD,$01,$FC,$FF,$F6,$02,$08,$02,$07,$02,$06,$02,$05,$02,$07 +.db $02,$06,$02,$05,$02,$04,$03,$06,$03,$05,$03,$04,$03,$03,$03,$05 +.db $03,$04,$03,$03,$03,$02,$03,$04,$03,$03,$03,$02,$03,$01,$FF,$14 +.db $FE,$80,$FF,$00,$01,$00,$01,$FE,$01,$FE,$01,$FD,$01,$FC,$FF,$F6 +.db $05,$0F,$02,$0C,$04,$04,$08,$02,$08,$01,$FF,$00,$FE,$80,$FF,$00 +.db $04,$10,$04,$0F,$04,$0E,$04,$0C,$04,$0B,$04,$0A,$FF,$00,$02,$10 +.db $02,$0F,$01,$0E,$02,$0D,$02,$08,$01,$07,$03,$06,$FF,$FA,$02,$40 +.db $02,$20,$02,$10,$02,$08,$02,$04,$02,$02,$02,$FE,$02,$FC,$02,$F8 +.db $02,$F0,$02,$E0,$02,$C0,$FF,$00,$04,$10,$04,$0F,$04,$0E,$04,$0D +.db $04,$0C,$04,$0B,$04,$0A,$04,$09,$04,$08,$04,$07,$04,$06,$FF,$F8 +.db $05,$C0,$04,$00,$03,$00,$FF,$FD,$03,$08,$03,$07,$03,$05,$03,$03 +.db $03,$02,$02,$00,$03,$00,$02,$00,$02,$00,$02,$00,$FF,$00,$40,$14 +.db $01,$14,$01,$14,$01,$14,$01,$16,$01,$18,$01,$1A,$01,$1B,$01,$1C +.db $01,$1D,$FF,$FC,$01,$50,$01,$46,$01,$3C,$01,$32,$01,$28,$01,$1E +.db $01,$14,$01,$0A,$01,$F6,$01,$EC,$01,$E2,$01,$D8,$01,$CE,$01,$C4 +.db $01,$BA,$01,$B0,$FF,$FE,$01,$B0,$01,$BA,$01,$C4,$01,$CE,$01,$D8 +.db $01,$E2,$01,$EC,$01,$F6,$01,$0A,$01,$14,$01,$1E,$01,$28,$01,$32 +.db $01,$3C,$01,$46,$01,$50,$FF,$FE,$FF,$00,$02,$01,$02,$00,$FF,$F8 +.db $04,$03,$DE,$02,$05,$01,$FF,$00,$00,$01,$40,$01,$80,$01,$40,$10 +.db $00,$FF,$F6,$00,$01,$40,$01,$80,$01,$40,$10,$00,$FF,$F6,$A5,$8F +.db $30,$02,$D0,$0A,$A5,$8E,$C5,$90,$F0,$0B,$A5,$8E,$85,$90,$20,$2F +.db $8E,$A9,$00,$85,$8F,$60,$A9,$0F,$8D,$15,$40,$A9,$00,$8D,$00,$40 +.db $8D,$04,$40,$8D,$08,$40,$8D,$0C,$40,$8D,$10,$40,$A9,$7F,$8D,$01 +.db $40,$8D,$05,$40,$A9,$00,$A0,$D5,$99,$00,$07,$88,$D0,$FA,$A9,$FF +.db $A2,$04,$CA,$9D,$4D,$07,$9D,$B1,$07,$D0,$F7,$A2,$04,$CA,$9D,$65 +.db $07,$9D,$C9,$07,$D0,$F7,$A2,$02,$CA,$9D,$5B,$07,$9D,$BF,$07,$D0 +.db $F7,$A2,$08,$CA,$9D,$01,$07,$D0,$FA,$60,$AD,$09,$07,$F0,$13,$A9 +.db $00,$8D,$15,$40,$A9,$07,$8D,$15,$40,$20,$85,$8C,$20,$E8,$86,$4C +.db $71,$86,$A9,$0F,$8D,$15,$40,$20,$85,$8C,$20,$E8,$86,$A9,$00,$8D +.db $0A,$07,$8D,$0B,$07,$8D,$0C,$07,$20,$A1,$87,$20,$25,$8B,$EE,$0A +.db $07,$EE,$0B,$07,$EE,$0C,$07,$EE,$0C,$07,$AE,$0A,$07,$E0,$04,$D0 +.db $E7,$A9,$10,$8D,$0A,$07,$A9,$64,$8D,$0B,$07,$8D,$0C,$07,$20,$A1 +.db $87,$20,$25,$8B,$EE,$0A,$07,$EE,$0B,$07,$EE,$0C,$07,$EE,$0C,$07 +.db $AE,$0A,$07,$E0,$14,$D0,$E7,$60,$A8,$C0,$01,$D0,$05,$A9,$00,$8D +.db $15,$40,$C0,$F0,$F0,$05,$C0,$F1,$F0,$36,$60,$A2,$00,$A0,$00,$20 +.db $D6,$8D,$A5,$FE,$29,$F0,$09,$30,$8D,$00,$40,$A2,$01,$A0,$02,$20 +.db $D6,$8D,$A5,$FE,$29,$F0,$09,$30,$8D,$04,$40,$A9,$00,$8D,$08,$40 +.db $A9,$30,$8D,$0C,$40,$A9,$FF,$8D,$09,$07,$A9,$0F,$8D,$15,$40,$60 +.db $A9,$00,$8D,$09,$07,$4C,$47,$86,$AD,$00,$07,$F0,$19,$A0,$00,$B9 +.db $01,$07,$30,$0D,$AA,$98,$48,$20,$07,$87,$68,$A8,$A9,$FF,$99,$01 +.db $07,$C8,$C0,$08,$D0,$E9,$60,$8A,$0A,$AA,$BD,$41,$8E,$85,$FE,$BD +.db $42,$8E,$85,$FF,$A0,$00,$B1,$FE,$8D,$0A,$07,$AA,$E0,$FF,$F0,$BF +.db $AD,$0A,$07,$30,$0A,$8D,$0B,$07,$0A,$8D,$0C,$07,$4C,$41,$87,$29 +.db $7F,$18,$69,$64,$8D,$0B,$07,$8A,$29,$7F,$0A,$18,$69,$64,$8D,$0C +.db $07,$AE,$0C,$07,$C8,$B1,$FE,$9D,$24,$07,$C8,$B1,$FE,$9D,$25,$07 +.db $C8,$98,$48,$A0,$00,$AD,$0A,$07,$10,$02,$A0,$64,$AD,$0A,$07,$29 +.db $0F,$A8,$A9,$00,$9D,$31,$07,$9D,$32,$07,$AE,$0B,$07,$A9,$00,$9D +.db $49,$07,$A9,$FF,$9D,$4D,$07,$C0,$04,$10,$21,$A9,$00,$9D,$61,$07 +.db $A9,$FF,$9D,$65,$07,$C0,$03,$10,$13,$A9,$00,$9D,$0D,$07,$C0,$02 +.db $10,$0A,$A9,$00,$9D,$59,$07,$A9,$FF,$9D,$5B,$07,$68,$A8,$4C,$16 +.db $87,$AE,$0C,$07,$BD,$24,$07,$1D,$25,$07,$F0,$33,$BD,$31,$07,$D0 +.db $2B,$BD,$32,$07,$D0,$0F,$20,$E0,$87,$AE,$0C,$07,$AC,$0B,$07,$B9 +.db $2D,$07,$9D,$32,$07,$DE,$32,$07,$AC,$0A,$07,$C0,$04,$30,$05,$A0 +.db $64,$4C,$D6,$87,$A0,$00,$B9,$2C,$07,$9D,$31,$07,$DE,$31,$07,$60 +.db $AE,$0C,$07,$BD,$24,$07,$85,$FE,$BD,$25,$07,$85,$FF,$A0,$00,$B1 +.db $FE,$10,$3F,$AA,$E0,$F0,$30,$03,$4C,$7F,$88,$29,$7F,$F0,$06,$AE +.db $0B,$07,$9D,$2D,$07,$20,$20,$8E,$4C,$E0,$87,$20,$20,$8E,$60,$AE +.db $0A,$06,$F0,$0B,$A2,$FF,$E8,$DD,$20,$88,$D0,$FA,$BD,$29,$88,$60 +.db $03,$00,$06,$09,$0C,$12,$18,$24,$30,$02,$03,$04,$06,$08,$0C,$10 +.db $18,$20,$D0,$07,$20,$20,$8E,$4C,$0A,$8A,$60,$48,$AD,$0A,$07,$29 +.db $0F,$AA,$68,$E0,$03,$F0,$1F,$E0,$04,$D0,$00,$AE,$0B,$07,$18,$7D +.db $0D,$07,$0A,$A8,$AE,$0C,$07,$B9,$C0,$8E,$9D,$39,$07,$B9,$C1,$8E +.db $9D,$3A,$07,$4C,$78,$88,$AA,$29,$10,$F0,$06,$8A,$29,$0F,$09,$80 +.db $AA,$8A,$AE,$0C,$07,$9D,$39,$07,$20,$65,$8A,$20,$20,$8E,$60,$29 +.db $0F,$0A,$A8,$20,$20,$8E,$98,$AA,$BD,$95,$88,$85,$FE,$BD,$96,$88 +.db $85,$FF,$6C,$FE,$00,$B5,$88,$CC,$88,$E6,$88,$12,$89,$3A,$89,$57 +.db $89,$7A,$89,$E0,$87,$A1,$89,$BA,$89,$D3,$89,$27,$8A,$0B,$88,$FB +.db $87,$E0,$87,$EC,$89,$AE,$0C,$07,$18,$BD,$24,$07,$69,$02,$9D,$1C +.db $07,$BD,$25,$07,$69,$00,$9D,$1D,$07,$4C,$3D,$89,$AE,$0C,$07,$BD +.db $1C,$07,$9D,$24,$07,$BD,$1D,$07,$9D,$25,$07,$A9,$00,$9D,$1C,$07 +.db $9D,$1D,$07,$4C,$E0,$87,$AE,$0C,$07,$BD,$24,$07,$85,$FE,$BD,$25 +.db $07,$85,$FF,$AE,$0B,$07,$A0,$00,$B1,$FE,$9D,$10,$07,$20,$20,$8E +.db $AE,$0C,$07,$BD,$24,$07,$9D,$14,$07,$BD,$25,$07,$9D,$15,$07,$4C +.db $E0,$87,$AE,$0B,$07,$DE,$10,$07,$F0,$12,$AE,$0C,$07,$BD,$14,$07 +.db $9D,$24,$07,$BD,$15,$07,$9D,$25,$07,$4C,$E0,$87,$AE,$0C,$07,$A9 +.db $00,$9D,$14,$07,$9D,$15,$07,$4C,$E0,$87,$AE,$0C,$07,$BD,$24,$07 +.db $85,$FE,$BD,$25,$07,$85,$FF,$A0,$00,$B1,$FE,$9D,$24,$07,$C8,$B1 +.db $FE,$9D,$25,$07,$4C,$E0,$87,$AE,$0C,$07,$BD,$24,$07,$85,$FE,$BD +.db $25,$07,$85,$FF,$A0,$00,$B1,$FE,$AE,$0A,$07,$E0,$04,$30,$02,$A0 +.db $64,$99,$2C,$07,$20,$20,$8E,$4C,$E0,$87,$AD,$0A,$07,$29,$0F,$AA +.db $E0,$03,$10,$17,$AE,$0C,$07,$BD,$24,$07,$85,$FE,$BD,$25,$07,$85 +.db $FF,$A0,$00,$B1,$FE,$AE,$0B,$07,$9D,$0D,$07,$20,$20,$8E,$4C,$E0 +.db $87,$AD,$0A,$07,$29,$0F,$AA,$E0,$04,$30,$06,$20,$20,$8E,$4C,$E0 +.db $87,$20,$0D,$8B,$9D,$49,$07,$4C,$E0,$87,$AD,$0A,$07,$29,$0F,$AA +.db $E0,$02,$30,$06,$20,$20,$8E,$4C,$E0,$87,$20,$0D,$8B,$9D,$59,$07 +.db $4C,$E0,$87,$AD,$0A,$07,$29,$0F,$AA,$E0,$04,$30,$06,$20,$20,$8E +.db $4C,$E0,$87,$20,$0D,$8B,$9D,$61,$07,$4C,$E0,$87,$AE,$0C,$07,$A9 +.db $00,$9D,$24,$07,$9D,$25,$07,$AD,$0A,$07,$29,$0F,$0A,$AA,$18,$69 +.db $64,$A8,$A9,$FF,$9D,$42,$07,$99,$42,$07,$AC,$0B,$07,$AD,$0A,$07 +.db $29,$0F,$AA,$A9,$FF,$E0,$04,$10,$0D,$99,$4D,$07,$99,$65,$07,$E0 +.db $02,$10,$03,$99,$5B,$07,$60,$AE,$0C,$07,$BD,$24,$07,$85,$FE,$BD +.db $25,$07,$85,$FF,$20,$20,$8E,$AE,$0B,$07,$BD,$10,$07,$D0,$07,$A0 +.db $00,$B1,$FE,$9D,$10,$07,$DE,$10,$07,$D0,$17,$AE,$0C,$07,$BD,$24 +.db $07,$18,$69,$02,$9D,$24,$07,$BD,$25,$07,$69,$00,$9D,$25,$07,$4C +.db $E0,$87,$4C,$3A,$89,$AE,$0C,$07,$BD,$42,$07,$09,$80,$9D,$42,$07 +.db $20,$7A,$8A,$20,$AB,$8A,$20,$DC,$8A,$60,$AD,$0A,$07,$29,$0F,$AA +.db $E0,$04,$30,$01,$60,$AE,$0B,$07,$AC,$0C,$07,$BD,$49,$07,$0A,$AA +.db $BD,$00,$80,$99,$51,$07,$85,$FE,$BD,$01,$80,$99,$52,$07,$85,$FF +.db $AE,$0B,$07,$A0,$00,$B1,$FE,$9D,$4D,$07,$60,$AD,$0A,$07,$29,$0F +.db $AA,$E0,$02,$30,$01,$60,$AE,$0B,$07,$AC,$0C,$07,$BD,$59,$07,$0A +.db $AA,$BD,$00,$80,$99,$5D,$07,$85,$FE,$BD,$01,$80,$99,$5E,$07,$85 +.db $FF,$AE,$0B,$07,$A0,$00,$B1,$FE,$9D,$5B,$07,$60,$AD,$0A,$07,$29 +.db $0F,$AA,$E0,$04,$30,$01,$60,$AE,$0B,$07,$AC,$0C,$07,$BD,$61,$07 +.db $0A,$AA,$BD,$00,$80,$99,$69,$07,$85,$FE,$BD,$01,$80,$99,$6A,$07 +.db $85,$FF,$AE,$0B,$07,$A0,$00,$B1,$FE,$9D,$65,$07,$60,$AE,$0C,$07 +.db $BD,$24,$07,$85,$FE,$BD,$25,$07,$85,$FF,$20,$20,$8E,$A0,$00,$B1 +.db $FE,$AE,$0B,$07,$60,$20,$2F,$8B,$20,$A2,$8B,$20,$15,$8C,$60,$AD +.db $0A,$07,$29,$0F,$AA,$E0,$04,$10,$68,$AE,$0B,$07,$BD,$4D,$07,$A8 +.db $C0,$FF,$F0,$5D,$AE,$0B,$07,$BD,$4D,$07,$D0,$52,$AE,$0C,$07,$A9 +.db $02,$18,$7D,$51,$07,$9D,$51,$07,$85,$FE,$A9,$00,$7D,$52,$07,$9D +.db $52,$07,$85,$FF,$AE,$0B,$07,$A0,$00,$B1,$FE,$9D,$4D,$07,$A8,$C0 +.db $FF,$D0,$C6,$AE,$0C,$07,$A0,$01,$B1,$FE,$29,$FE,$10,$13,$18,$7D +.db $51,$07,$9D,$51,$07,$85,$FE,$B0,$03,$DE,$52,$07,$BD,$52,$07,$85 +.db $FF,$AE,$0B,$07,$A0,$00,$B1,$FE,$9D,$4D,$07,$4C,$39,$8B,$DE,$4D +.db $07,$60,$AD,$0A,$07,$29,$0F,$AA,$E0,$02,$10,$68,$AE,$0B,$07,$BD +.db $5B,$07,$A8,$C0,$FF,$F0,$5D,$AE,$0B,$07,$BD,$5B,$07,$D0,$52,$AE +.db $0C,$07,$A9,$02,$18,$7D,$5D,$07,$9D,$5D,$07,$85,$FE,$A9,$00,$7D +.db $5E,$07,$9D,$5E,$07,$85,$FF,$AE,$0B,$07,$A0,$00,$B1,$FE,$9D,$5B +.db $07,$A8,$C0,$FF,$D0,$C6,$AE,$0C,$07,$A0,$01,$B1,$FE,$29,$FE,$10 +.db $13,$18,$7D,$5D,$07,$9D,$5D,$07,$85,$FE,$B0,$03,$DE,$5E,$07,$BD +.db $5E,$07,$85,$FF,$AE,$0B,$07,$A0,$00,$B1,$FE,$9D,$5B,$07,$4C,$AC +.db $8B,$DE,$5B,$07,$60,$AD,$0A,$07,$29,$0F,$AA,$E0,$04,$10,$65,$AE +.db $0B,$07,$BD,$65,$07,$A8,$C0,$FF,$F0,$5A,$BD,$65,$07,$D0,$52,$AE +.db $0C,$07,$A9,$02,$18,$7D,$69,$07,$9D,$69,$07,$85,$FE,$A9,$00,$7D +.db $6A,$07,$9D,$6A,$07,$85,$FF,$AE,$0B,$07,$A0,$00,$B1,$FE,$9D,$65 +.db $07,$A8,$C0,$FF,$D0,$C9,$AE,$0C,$07,$A0,$01,$B1,$FE,$29,$FE,$10 +.db $13,$18,$7D,$69,$07,$9D,$69,$07,$85,$FE,$B0,$03,$DE,$6A,$07,$BD +.db $6A,$07,$85,$FF,$AE,$0B,$07,$A0,$00,$B1,$FE,$9D,$65,$07,$4C,$1F +.db $8C,$DE,$65,$07,$60,$20,$92,$8C,$20,$E4,$8C,$20,$36,$8D,$20,$85 +.db $8D,$60,$A2,$64,$A0,$64,$AD,$88,$07,$0D,$89,$07,$D0,$04,$A2,$00 +.db $A0,$00,$20,$B1,$8D,$A5,$FE,$48,$20,$D6,$8D,$68,$05,$FE,$09,$30 +.db $8D,$00,$40,$20,$FB,$8D,$A9,$00,$85,$FF,$A5,$FE,$10,$02,$C6,$FF +.db $B9,$39,$07,$18,$65,$FE,$99,$41,$07,$8D,$02,$40,$B9,$42,$07,$85 +.db $FE,$B9,$3A,$07,$65,$FF,$AA,$E4,$FE,$F0,$08,$99,$42,$07,$09,$F8 +.db $8D,$03,$40,$60,$A2,$65,$A0,$66,$AD,$8A,$07,$0D,$8B,$07,$D0,$04 +.db $A2,$01,$A0,$02,$20,$B1,$8D,$A5,$FE,$48,$20,$D6,$8D,$68,$05,$FE +.db $09,$30,$8D,$04,$40,$20,$FB,$8D,$A9,$00,$85,$FF,$A5,$FE,$10,$02 +.db $C6,$FF,$B9,$39,$07,$18,$65,$FE,$99,$41,$07,$8D,$06,$40,$B9,$42 +.db $07,$85,$FE,$B9,$3A,$07,$65,$FF,$AA,$E4,$FE,$F0,$08,$99,$42,$07 +.db $09,$F8,$8D,$07,$40,$60,$A2,$66,$A0,$68,$AD,$8C,$07,$0D,$8D,$07 +.db $D0,$04,$A2,$02,$A0,$04,$20,$B1,$8D,$A5,$FE,$F0,$02,$A9,$FF,$09 +.db $80,$8D,$08,$40,$20,$FB,$8D,$A9,$00,$85,$FF,$A5,$FE,$10,$02,$C6 +.db $FF,$B9,$39,$07,$18,$65,$FE,$99,$41,$07,$8D,$0A,$40,$B9,$42,$07 +.db $85,$FE,$B9,$3A,$07,$65,$FF,$AA,$E4,$FE,$F0,$08,$99,$42,$07,$09 +.db $F8,$8D,$0B,$40,$60,$A2,$67,$A0,$6A,$AD,$8E,$07,$0D,$8F,$07,$D0 +.db $04,$A2,$03,$A0,$06,$20,$B1,$8D,$A5,$FE,$09,$30,$8D,$0C,$40,$20 +.db $FB,$8D,$B9,$39,$07,$18,$65,$FE,$8D,$0E,$40,$A9,$F8,$8D,$0F,$40 +.db $60,$98,$48,$BD,$4D,$07,$A8,$C0,$FF,$D0,$05,$A9,$00,$4C,$D1,$8D +.db $68,$48,$A8,$B9,$51,$07,$85,$FE,$B9,$52,$07,$85,$FF,$A0,$01,$B1 +.db $FE,$85,$FE,$68,$A8,$60,$98,$48,$BD,$5B,$07,$A8,$C0,$FF,$D0,$05 +.db $A9,$00,$4C,$F6,$8D,$68,$48,$A8,$B9,$5D,$07,$85,$FE,$B9,$5E,$07 +.db $85,$FF,$A0,$01,$B1,$FE,$85,$FE,$68,$A8,$60,$98,$48,$BD,$65,$07 +.db $A8,$C0,$FF,$D0,$05,$A9,$00,$4C,$1B,$8E,$68,$48,$A8,$B9,$69,$07 +.db $85,$FE,$B9,$6A,$07,$85,$FF,$A0,$01,$B1,$FE,$85,$FE,$68,$A8,$60 +.db $AE,$0C,$07,$FE,$24,$07,$BD,$24,$07,$D0,$03,$FE,$25,$07,$60,$AA +.db $A0,$FF,$C0,$07,$F0,$0A,$C8,$B9,$01,$07,$10,$F6,$8A,$99,$01,$07 +.db $60,$A1,$8E,$BE,$8F,$D8,$8F,$E9,$8F,$FC,$8F,$0E,$90,$2A,$90,$46 +.db $90,$59,$90,$7D,$90,$9F,$90,$B5,$90,$CB,$90,$01,$91,$18,$91,$2C +.db $91,$3D,$91,$4F,$91,$61,$91,$73,$91,$85,$91,$97,$91,$A9,$91,$BB +.db $91,$CD,$91,$E8,$91,$FA,$91,$13,$92,$31,$92,$41,$92,$A1,$8E,$A1 +.db $8E,$68,$95,$A5,$95,$0E,$96,$C0,$96,$D5,$97,$C1,$98,$89,$99,$F8 +.db $9A,$F9,$9B,$50,$9F,$1F,$A0,$4A,$A1,$33,$A5,$93,$A7,$A1,$8E,$C5 +.db $A8,$00,$BF,$8E,$01,$BF,$8E,$02,$BF,$8E,$03,$BF,$8E,$04,$BF,$8E +.db $80,$BF,$8E,$81,$BF,$8E,$82,$BF,$8E,$83,$BF,$8E,$84,$BF,$8E,$FF +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$F2,$07,$80,$07,$14,$07,$AE,$06,$4E,$06,$F4,$05,$9E,$05 +.db $4D,$05,$01,$05,$B9,$04,$75,$04,$35,$04,$F9,$03,$C0,$03,$8A,$03 +.db $57,$03,$27,$03,$FA,$02,$CF,$02,$A7,$02,$81,$02,$5D,$02,$3B,$02 +.db $1B,$02,$FC,$01,$E0,$01,$C5,$01,$AC,$01,$94,$01,$7D,$01,$68,$01 +.db $53,$01,$40,$01,$2E,$01,$1D,$01,$0D,$01,$FE,$00,$F0,$00,$E2,$00 +.db $D6,$00,$CA,$00,$BE,$00,$B4,$00,$AA,$00,$A0,$00,$97,$00,$8F,$00 +.db $87,$00,$7F,$00,$78,$00,$71,$00,$6B,$00,$65,$00,$5F,$00,$5A,$00 +.db $55,$00,$50,$00,$4C,$00,$47,$00,$43,$00,$40,$00,$3C,$00,$39,$00 +.db $35,$00,$32,$00,$30,$00,$2D,$00,$2A,$00,$28,$00,$26,$00,$24,$00 +.db $22,$00,$20,$00,$1E,$00,$1C,$00,$1B,$00,$19,$00,$18,$00,$16,$00 +.db $15,$00,$14,$00,$13,$00,$12,$00,$11,$00,$10,$00,$0F,$00,$0E,$00 +.db $0D,$00,$0C,$00,$0B,$00,$0A,$00,$09,$00,$08,$00,$07,$00,$06,$00 +.db $05,$00,$04,$00,$03,$00,$02,$00,$F5,$01,$F6,$00,$F9,$38,$FA,$39 +.db $F8,$40,$82,$28,$00,$39,$00,$38,$00,$39,$FF,$F6,$00,$F9,$38,$FA +.db $39,$F8,$40,$84,$00,$82,$28,$00,$39,$00,$38,$00,$39,$FF,$80,$98 +.db $8F,$81,$AB,$8F,$FF,$F5,$01,$F6,$18,$F9,$38,$FA,$39,$F8,$40,$83 +.db $28,$00,$24,$00,$28,$00,$24,$FF,$81,$C5,$8F,$FF,$F5,$01,$F6,$00 +.db $F9,$38,$FA,$63,$F8,$11,$88,$2D,$FF,$80,$DC,$8F,$FF,$F5,$01,$F6 +.db $00,$F9,$38,$FA,$63,$F8,$11,$84,$30,$88,$3E,$FF,$82,$ED,$8F,$FF +.db $F5,$01,$F6,$09,$F9,$38,$FA,$39,$F8,$41,$82,$45,$47,$FF,$80,$00 +.db $90,$FF,$F5,$01,$F6,$07,$F9,$38,$FA,$39,$F8,$41,$83,$47,$47,$F9 +.db $38,$FA,$39,$F8,$40,$47,$47,$47,$47,$FF,$80,$12,$90,$FF,$F5,$01 +.db $F6,$0C,$F9,$38,$FA,$39,$F8,$40,$83,$34,$00,$3C,$00,$40,$00,$3C +.db $00,$3E,$00,$8C,$43,$FF,$80,$2E,$90,$FF,$F5,$01,$F6,$07,$F9,$38 +.db $FA,$39,$F8,$40,$83,$40,$86,$45,$FF,$80,$4A,$90,$FF,$F5,$01,$F6 +.db $00,$F9,$5B,$FA,$39,$F8,$11,$82,$37,$00,$37,$00,$84,$37,$82,$39 +.db $00,$39,$00,$84,$39,$82,$3B,$00,$3B,$00,$84,$3B,$FF,$82,$5D,$90 +.db $FF,$F5,$01,$F6,$18,$F9,$38,$FA,$63,$F8,$11,$83,$37,$00,$37,$00 +.db $37,$00,$39,$00,$39,$00,$39,$00,$3B,$00,$3B,$00,$3B,$00,$FF,$82 +.db $81,$90,$FF,$F5,$01,$F6,$00,$F9,$5B,$FA,$39,$F8,$11,$83,$2B,$00 +.db $2D,$00,$2F,$00,$FF,$82,$A3,$90,$FF,$F5,$01,$F6,$07,$F9,$00,$FA +.db $64,$F8,$11,$85,$1F,$00,$1F,$00,$8B,$1F,$FF,$80,$B9,$90,$83,$B9 +.db $90,$FF,$F5,$01,$F6,$07,$F9,$00,$FA,$63,$F8,$11,$8E,$2B,$F9,$00 +.db $FA,$64,$F8,$11,$24,$FF,$F6,$07,$F9,$34,$FA,$39,$F8,$41,$83,$15 +.db $8A,$00,$83,$15,$FF,$F6,$07,$F9,$5B,$FA,$39,$F8,$11,$8E,$2B,$24 +.db $FF,$80,$D2,$90,$81,$E6,$90,$83,$F5,$90,$FF,$F5,$01,$F6,$07,$F9 +.db $00,$FA,$64,$F8,$11,$AC,$2B,$FF,$80,$0B,$91,$83,$0B,$91,$FF,$F5 +.db $01,$F6,$00,$F9,$34,$FA,$39,$F8,$41,$9C,$15,$FF,$80,$1F,$91,$FF +.db $F5,$03,$F6,$01,$F9,$34,$FA,$39,$F8,$46,$98,$2D,$FF,$83,$30,$91 +.db $FF,$F5,$01,$F6,$09,$F9,$38,$FA,$39,$F8,$41,$84,$30,$31,$FF,$80 +.db $41,$91,$FF,$F5,$01,$F6,$09,$F9,$38,$FA,$39,$F8,$41,$84,$32,$33 +.db $FF,$80,$53,$91,$FF,$F5,$01,$F6,$09,$F9,$38,$FA,$39,$F8,$41,$84 +.db $34,$35,$FF,$80,$65,$91,$FF,$F5,$01,$F6,$09,$F9,$38,$FA,$39,$F8 +.db $41,$84,$35,$36,$FF,$80,$77,$91,$FF,$F5,$01,$F6,$09,$F9,$38,$FA +.db $39,$F8,$41,$84,$37,$38,$FF,$80,$89,$91,$FF,$F5,$01,$F6,$09,$F9 +.db $38,$FA,$39,$F8,$41,$84,$39,$3A,$FF,$80,$9B,$91,$FF,$F5,$01,$F6 +.db $09,$F9,$38,$FA,$39,$F8,$41,$84,$3B,$3C,$FF,$80,$AD,$91,$FF,$F5 +.db $01,$F6,$09,$F9,$38,$FA,$39,$F8,$41,$84,$3C,$3D,$FF,$80,$BF,$91 +.db $FF,$F5,$01,$F6,$09,$F9,$12,$FA,$00,$F8,$13,$88,$00,$84,$3A,$F9 +.db $38,$FA,$39,$F8,$41,$90,$45,$FF,$80,$D1,$91,$FF,$F5,$01,$F6,$0D +.db $F9,$60,$FA,$5E,$F8,$61,$8C,$18,$0C,$FF,$81,$EC,$91,$FF,$F5,$03 +.db $F6,$18,$F9,$50,$FA,$51,$F8,$52,$8A,$18,$F9,$12,$FA,$00,$F8,$11 +.db $8F,$24,$FF,$83,$FE,$91,$FF,$F5,$01,$F6,$12,$F9,$38,$FA,$39,$F8 +.db $40,$83,$47,$45,$43,$41,$40,$3E,$3C,$3B,$39,$37,$35,$34,$32,$30 +.db $FF,$80,$17,$92,$FF,$F5,$03,$F6,$01,$F9,$38,$FA,$39,$F8,$41,$45 +.db $FF,$83,$35,$92,$FF,$F5,$01,$F6,$07,$F9,$12,$FA,$00,$F8,$11,$8C +.db $2B,$2B,$2B,$1F,$2B,$2B,$2B,$1F,$B0,$2C,$A4,$2B,$8C,$1F,$F6,$13 +.db $F9,$38,$FA,$39,$F8,$40,$8C,$28,$28,$28,$1F,$28,$28,$28,$1F,$28 +.db $28,$28,$86,$28,$29,$98,$28,$8C,$26,$1F,$26,$26,$26,$1F,$26,$26 +.db $26,$1F,$26,$26,$26,$86,$26,$28,$98,$26,$8C,$24,$1F,$28,$28,$28 +.db $1F,$28,$28,$28,$1F,$28,$28,$28,$86,$26,$28,$98,$29,$8C,$2D,$1F +.db $2B,$2B,$2B,$1F,$29,$29,$29,$23,$B0,$24,$8C,$00,$00,$F6,$07,$F9 +.db $12,$FA,$00,$F8,$11,$2B,$29,$98,$28,$2B,$A4,$30,$8C,$2F,$98,$2C +.db $2D,$A4,$32,$8C,$30,$F9,$07,$FA,$05,$F8,$02,$98,$2F,$30,$32,$35 +.db $B0,$34,$8C,$00,$00,$F9,$12,$FA,$00,$F8,$11,$2B,$29,$98,$28,$2B +.db $A4,$30,$8C,$2F,$98,$2C,$2D,$A4,$32,$8C,$30,$F9,$07,$FA,$05,$F8 +.db $02,$98,$2F,$8C,$2E,$2F,$A4,$35,$8C,$2F,$B0,$30,$8C,$00,$00,$00 +.db $2B,$2D,$2D,$2D,$2F,$98,$30,$35,$F9,$12,$FA,$00,$F8,$11,$8C,$34 +.db $86,$35,$34,$8C,$32,$86,$34,$32,$A4,$30,$F9,$07,$FA,$05,$F8,$02 +.db $8C,$2B,$2D,$2D,$2D,$2F,$98,$30,$35,$F9,$12,$FA,$00,$F8,$11,$8C +.db $34,$86,$35,$34,$8C,$32,$86,$34,$32,$A4,$30,$F9,$07,$FA,$05,$F8 +.db $02,$8C,$2B,$2D,$2D,$2D,$2F,$98,$30,$35,$F9,$12,$FA,$00,$F8,$11 +.db $8C,$37,$36,$37,$32,$32,$31,$32,$2B,$B0,$2C,$2B,$8C,$00,$00,$98 +.db $18,$F4,$5E,$92,$FF,$F6,$13,$F9,$17,$FA,$16,$F8,$14,$8C,$2B,$2B +.db $2B,$1F,$2B,$2B,$2B,$1F,$B0,$2C,$A4,$2B,$8C,$00,$F9,$12,$FA,$00 +.db $F8,$11,$8C,$28,$28,$28,$1F,$28,$28,$28,$1F,$28,$28,$28,$86,$28 +.db $29,$98,$28,$8C,$26,$1F,$26,$26,$26,$1F,$26,$26,$26,$1F,$26,$26 +.db $26,$86,$26,$28,$98,$26,$8C,$24,$1F,$28,$28,$28,$1F,$28,$28,$28 +.db $1F,$28,$28,$28,$86,$26,$28,$98,$29,$8C,$2D,$1F,$2B,$2B,$2B,$1F +.db $29,$29,$29,$23,$B0,$24,$8C,$00,$00,$00,$00,$F9,$17,$FA,$16,$F8 +.db $15,$24,$28,$2B,$28,$24,$28,$2B,$28,$29,$2D,$30,$2D,$29,$2D,$30 +.db $2D,$2B,$2F,$26,$2F,$2B,$2F,$26,$2F,$24,$28,$2B,$28,$24,$28,$2B +.db $28,$24,$28,$2B,$28,$30,$28,$2B,$28,$29,$2D,$30,$2D,$35,$2D,$30 +.db $2D,$2B,$2F,$26,$2F,$2B,$2F,$26,$2F,$28,$2B,$26,$2B,$B0,$24,$F9 +.db $38,$FA,$39,$F8,$41,$83,$30,$34,$37,$3B,$3C,$40,$43,$47,$8C,$00 +.db $00,$00,$00,$00,$00,$F9,$17,$FA,$16,$F8,$14,$34,$86,$35,$34,$8C +.db $32,$86,$34,$32,$B0,$30,$F9,$38,$FA,$39,$F8,$41,$83,$30,$34,$37 +.db $3B,$3C,$40,$43,$47,$8C,$00,$00,$00,$00,$00,$00,$F9,$17,$FA,$16 +.db $F8,$14,$34,$86,$35,$34,$8C,$32,$86,$34,$32,$B0,$30,$F9,$38,$FA +.db $39,$F8,$41,$83,$30,$34,$37,$3B,$3C,$40,$43,$47,$8C,$00,$00,$00 +.db $00,$00,$00,$F9,$17,$FA,$16,$F8,$14,$37,$36,$37,$32,$32,$31,$32 +.db $2B,$B0,$2C,$F6,$13,$F9,$38,$FA,$39,$F8,$40,$8C,$2B,$2A,$30,$34 +.db $37,$00,$F9,$12,$FA,$00,$F8,$11,$98,$1F,$F4,$7C,$93,$FF,$F6,$07 +.db $F9,$07,$FA,$05,$F8,$02,$8C,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$98,$24,$8C,$00,$00,$98,$24,$8C,$00 +.db $00,$98,$24,$8C,$00,$00,$98,$1F,$8C,$00,$00,$98,$1F,$8C,$00,$00 +.db $98,$1F,$8C,$00,$00,$98,$1F,$8C,$00,$00,$98,$24,$8C,$00,$00,$98 +.db $24,$8C,$00,$00,$98,$28,$8C,$00,$00,$98,$29,$8C,$00,$00,$98,$2A +.db $8C,$00,$00,$98,$2B,$8C,$00,$00,$98,$23,$8C,$00,$00,$98,$24,$1F +.db $24,$8C,$00,$00,$B0,$24,$28,$29,$2A,$2B,$26,$28,$24,$24,$28,$29 +.db $2A,$2B,$23,$24,$8C,$00,$00,$00,$00,$98,$29,$8C,$00,$29,$98,$29 +.db $8C,$00,$00,$98,$28,$8C,$00,$00,$98,$24,$8C,$00,$00,$98,$29,$8C +.db $00,$29,$98,$29,$8C,$00,$00,$98,$28,$8C,$00,$00,$98,$24,$8C,$00 +.db $00,$98,$29,$8C,$00,$29,$98,$29,$8C,$00,$00,$98,$2B,$8C,$00,$00 +.db $98,$26,$8C,$00,$00,$98,$1F,$8C,$00,$00,$98,$1D,$8C,$00,$00,$00 +.db $00,$98,$1F,$F4,$B7,$94,$FF,$FF,$00,$45,$92,$01,$65,$93,$02,$9E +.db $94,$03,$67,$95,$04,$67,$95,$FF,$F5,$01,$F6,$11,$F9,$5B,$FA,$39 +.db $F8,$11,$D0,$26,$A8,$26,$23,$23,$94,$26,$28,$D0,$32,$FF,$F6,$11 +.db $F9,$5B,$FA,$39,$F8,$11,$85,$00,$D0,$28,$A8,$28,$26,$26,$94,$28 +.db $2D,$D0,$32,$FF,$FF,$00,$78,$95,$01,$8E,$95,$02,$A4,$95,$03,$A4 +.db $95,$04,$A4,$95,$FF,$F5,$01,$F6,$0C,$F9,$38,$FA,$39,$F8,$41,$81 +.db $43,$00,$47,$87,$00,$00,$00,$81,$00,$F6,$0C,$F9,$00,$FA,$00,$F8 +.db $53,$87,$33,$34,$33,$34,$30,$2B,$2D,$30,$2C,$2D,$28,$24,$26,$24 +.db $26,$28,$24,$00,$00,$00,$F9,$17,$FA,$16,$F8,$14,$9C,$1D,$FF,$F6 +.db $00,$F9,$12,$FA,$00,$F8,$11,$87,$00,$00,$00,$00,$8E,$18,$24,$16 +.db $22,$15,$21,$9C,$13,$87,$18,$00,$00,$00,$9C,$1D,$FF,$FF,$00,$B5 +.db $95,$01,$EF,$95,$02,$0D,$96,$03,$0D,$96,$04,$0D,$96,$FF,$F5,$01 +.db $F6,$05,$F9,$12,$FA,$00,$F8,$11,$98,$30,$30,$8C,$2D,$98,$30,$8C +.db $2D,$30,$2D,$2B,$B0,$30,$8C,$2D,$37,$39,$37,$39,$92,$37,$86,$2B +.db $35,$34,$8C,$32,$B0,$30,$8C,$00,$00,$00,$00,$FF,$F6,$05,$F9,$12 +.db $FA,$00,$F8,$11,$98,$24,$24,$23,$23,$22,$22,$21,$21,$8C,$24,$00 +.db $00,$00,$00,$1F,$21,$23,$98,$24,$1F,$B0,$18,$FF,$F6,$05,$F9,$12 +.db $FA,$00,$F8,$11,$98,$24,$24,$23,$23,$21,$21,$1F,$1F,$8C,$24,$00 +.db $00,$00,$00,$1F,$21,$23,$98,$24,$1F,$B0,$24,$FF,$F6,$00,$F9,$00 +.db $FA,$00,$F8,$53,$8C,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$86,$26,$26,$8C,$29,$86,$24,$24,$24,$24,$24,$24,$24 +.db $24,$26,$26,$26,$26,$26,$26,$26,$26,$29,$29,$8C,$29,$00,$FF,$FF +.db $00,$1E,$96,$01,$4C,$96,$02,$6C,$96,$03,$8C,$96,$04,$BF,$96,$FF +.db $F5,$01,$F6,$18,$F9,$5B,$FA,$39,$F8,$11,$8C,$00,$86,$00,$24,$2F +.db $2D,$8C,$2F,$2D,$00,$98,$2D,$8C,$00,$86,$00,$24,$30,$2D,$8C,$30 +.db $2F,$00,$98,$2F,$8C,$00,$86,$00,$24,$2F,$2D,$8C,$2F,$2D,$00,$98 +.db $2D,$8C,$30,$2D,$29,$98,$28,$86,$00,$98,$26,$86,$00,$8C,$00,$86 +.db $00,$24,$2F,$2D,$8C,$2F,$2D,$00,$98,$2D,$8C,$00,$86,$00,$24,$30 +.db $2D,$8C,$30,$2F,$00,$98,$2F,$8C,$00,$86,$00,$24,$2F,$2D,$8C,$2F +.db $2D,$00,$98,$2D,$92,$26,$86,$28,$8C,$29,$2B,$F6,$0C,$F9,$07,$FA +.db $05,$F8,$03,$86,$2B,$2D,$98,$2B,$8C,$00,$F4,$D0,$96,$FF,$F6,$00 +.db $F9,$12,$FA,$00,$F8,$11,$98,$18,$24,$15,$21,$1A,$26,$13,$1F,$18 +.db $24,$15,$21,$8C,$20,$00,$00,$00,$B0,$1F,$98,$18,$24,$15,$21,$1A +.db $26,$13,$1F,$18,$24,$8C,$15,$00,$98,$21,$8C,$00,$00,$00,$00,$00 +.db $00,$00,$00,$F4,$4E,$97,$FF,$F6,$0C,$F9,$07,$FA,$05,$F8,$03,$8C +.db $00,$00,$86,$2F,$92,$2F,$B0,$2D,$8C,$00,$00,$86,$30,$92,$30,$B0 +.db $2F,$8C,$00,$00,$86,$2F,$92,$2F,$B0,$2D,$28,$26,$8C,$00,$00,$86 +.db $2F,$92,$2F,$B0,$2D,$8C,$00,$00,$86,$30,$92,$30,$B0,$2F,$8C,$00 +.db $00,$86,$2F,$92,$2F,$B0,$2D,$8C,$00,$00,$00,$00,$00,$00,$00,$00 +.db $F4,$87,$97,$FF,$FF,$00,$D0,$96,$01,$4E,$97,$02,$D4,$97,$03,$D4 +.db $97,$04,$D4,$97,$FF,$F5,$01,$F6,$00,$F9,$12,$FA,$00,$F8,$11,$A4 +.db $18,$8C,$1F,$98,$24,$1F,$8C,$18,$18,$98,$18,$8C,$00,$00,$00,$00 +.db $A4,$1A,$8C,$21,$98,$26,$21,$8C,$1A,$1A,$98,$1A,$8C,$00,$00,$00 +.db $00,$A4,$18,$8C,$1F,$98,$24,$1F,$8C,$24,$23,$22,$21,$B0,$20,$F4 +.db $E5,$97,$FF,$F6,$18,$F9,$38,$FA,$39,$F8,$40,$92,$24,$86,$24,$8C +.db $24,$1F,$98,$21,$1F,$8C,$24,$24,$24,$F9,$0E,$FA,$0F,$F8,$03,$2B +.db $98,$2D,$2B,$F9,$38,$FA,$39,$F8,$40,$92,$26,$86,$26,$8C,$26,$21 +.db $98,$23,$21,$8C,$26,$26,$26,$F9,$0E,$FA,$0F,$F8,$03,$2D,$98,$2F +.db $2D,$F9,$38,$FA,$39,$F8,$40,$92,$24,$86,$24,$8C,$24,$1F,$98,$21 +.db $1F,$F6,$00,$F9,$12,$FA,$00,$F8,$11,$8C,$22,$21,$20,$1F,$B0,$1E +.db $F4,$23,$98,$FF,$F6,$00,$F9,$12,$FA,$00,$F8,$11,$A4,$24,$8C,$2B +.db $98,$30,$2B,$8C,$24,$24,$98,$24,$8C,$00,$00,$00,$00,$A4,$26,$8C +.db $2D,$98,$32,$2D,$8C,$26,$26,$98,$26,$8C,$00,$00,$00,$00,$A4,$24 +.db $8C,$2B,$98,$30,$2B,$8C,$27,$26,$25,$23,$B0,$22,$F4,$84,$98,$FF +.db $FF,$00,$E5,$97,$01,$23,$98,$02,$84,$98,$03,$C0,$98,$04,$C0,$98 +.db $FF,$F5,$01,$F6,$18,$F9,$07,$FA,$05,$F8,$02,$90,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$2C,$C0,$2B,$90,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$2B,$C0,$2C,$90,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$2C,$F4,$EC,$98,$FF,$F6,$01 +.db $F9,$34,$FA,$39,$F8,$41,$88,$15,$15,$15,$15,$90,$00,$15,$88,$15 +.db $15,$90,$00,$00,$00,$88,$15,$15,$15,$15,$90,$00,$15,$88,$15,$15 +.db $90,$00,$00,$00,$F4,$0E,$99,$FF,$F6,$04,$F9,$07,$FA,$05,$F8,$02 +.db $90,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $2C,$C0,$2B,$90,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$2B +.db $C0,$2C,$90,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$2C,$F4 +.db $51,$99,$FF,$F6,$01,$F9,$00,$FA,$00,$F8,$53,$88,$24,$24,$24,$24 +.db $29,$24,$24,$24,$F4,$73,$99,$FF,$FF,$00,$D1,$98,$01,$0E,$99,$02 +.db $38,$99,$03,$73,$99,$04,$88,$99,$FF,$F5,$01,$F6,$05,$F9,$12,$FA +.db $00,$F8,$11,$94,$15,$00,$9E,$00,$8A,$15,$94,$15,$00,$00,$00,$18 +.db $00,$9E,$00,$8A,$18,$94,$18,$00,$00,$00,$94,$15,$00,$9E,$00,$8A +.db $15,$94,$15,$00,$00,$00,$18,$00,$9E,$00,$8A,$18,$94,$18,$00,$00 +.db $00,$15,$00,$9E,$00,$8A,$15,$94,$15,$00,$00,$00,$18,$00,$9E,$00 +.db $8A,$18,$94,$18,$00,$00,$00,$1B,$00,$9E,$00,$8A,$1B,$94,$1B,$00 +.db $00,$00,$14,$00,$9E,$00,$8A,$14,$94,$14,$00,$00,$00,$F4,$BA,$99 +.db $FF,$F6,$1D,$F9,$5B,$FA,$39,$F8,$11,$94,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$85,$35,$32,$2D,$A8,$35 +.db $85,$00,$D0,$32,$94,$00,$85,$37,$34,$30,$A8,$37,$85,$00,$D0,$34 +.db $94,$00,$85,$35,$32,$2D,$A8,$35,$85,$00,$D0,$32,$94,$00,$85,$37 +.db $34,$30,$A8,$37,$85,$00,$D0,$34,$94,$00,$85,$33,$30,$2C,$BC,$33 +.db $85,$00,$94,$00,$00,$00,$00,$85,$32,$2F,$2B,$BC,$32,$85,$00,$94 +.db $00,$00,$00,$00,$F4,$1A,$9A,$FF,$F6,$00,$F9,$00,$FA,$00,$F8,$53 +.db $94,$2F,$8A,$24,$24,$9E,$29,$8A,$24,$2F,$29,$24,$24,$A8,$2F,$94 +.db $2F,$8A,$24,$24,$9E,$29,$8A,$24,$2F,$29,$2F,$2F,$A8,$29,$94,$2F +.db $8A,$24,$24,$9E,$29,$8A,$24,$2F,$29,$24,$24,$A8,$2F,$94,$2F,$8A +.db $24,$24,$9E,$29,$8A,$24,$2F,$29,$2F,$2F,$A8,$29,$94,$2F,$8A,$24 +.db $24,$9E,$29,$8A,$24,$2F,$29,$24,$24,$A8,$2F,$94,$2F,$8A,$24,$24 +.db $9E,$29,$8A,$24,$2F,$29,$2F,$2F,$A8,$29,$94,$2F,$8A,$24,$24,$9E +.db $29,$8A,$24,$2F,$29,$24,$24,$94,$2F,$8A,$24,$24,$94,$2F,$8A,$24 +.db $24,$9E,$29,$8A,$24,$85,$29,$29,$8A,$2F,$2F,$85,$29,$29,$8A,$2F +.db $29,$94,$29,$F4,$8E,$9A,$FF,$FF,$00,$99,$99,$01,$01,$9A,$02,$99 +.db $99,$03,$68,$9A,$04,$F7,$9A,$FF,$F5,$01,$F6,$03,$F9,$12,$FA,$00 +.db $F8,$11,$88,$2D,$2F,$90,$2D,$88,$2D,$2F,$90,$2D,$2D,$A0,$39,$90 +.db $39,$38,$2F,$2F,$C0,$2F,$90,$00,$88,$2D,$2F,$90,$2D,$88,$2D,$2F +.db $90,$2D,$2D,$A0,$39,$90,$39,$38,$2F,$2F,$C0,$2F,$90,$00,$32,$30 +.db $30,$32,$30,$30,$A0,$32,$90,$31,$2F,$2F,$31,$2F,$2F,$A0,$31,$90 +.db $32,$30,$30,$32,$30,$30,$88,$30,$90,$39,$C0,$38,$88,$00,$90,$00 +.db $00,$00,$00,$F4,$08,$9B,$FF,$F6,$03,$F9,$17,$FA,$16,$F8,$14,$90 +.db $00,$00,$00,$00,$21,$B0,$29,$90,$00,$00,$00,$00,$26,$B0,$28,$90 +.db $00,$00,$00,$00,$21,$B0,$29,$90,$00,$00,$00,$00,$26,$B0,$28,$90 +.db $26,$00,$00,$26,$00,$00,$26,$00,$25,$00,$00,$25,$00,$00,$25,$00 +.db $26,$00,$00,$26,$00,$00,$26,$00,$C0,$25,$90,$00,$00,$00,$00,$F4 +.db $67,$9B,$FF,$F6,$03,$F9,$5B,$FA,$39,$F8,$11,$A0,$2D,$34,$C0,$39 +.db $A0,$2C,$34,$90,$00,$00,$00,$00,$A0,$2D,$34,$C0,$39,$A0,$2C,$34 +.db $90,$00,$00,$00,$00,$26,$00,$00,$26,$00,$00,$26,$00,$25,$00,$00 +.db $25,$00,$00,$25,$00,$26,$00,$00,$26,$00,$00,$26,$00,$C0,$25,$90 +.db $00,$00,$00,$00,$F4,$B3,$9B,$FF,$FF,$00,$08,$9B,$01,$67,$9B,$02 +.db $B3,$9B,$03,$F8,$9B,$04,$F8,$9B,$FF,$F5,$01,$F6,$05,$F9,$12,$FA +.db $00,$F8,$11,$98,$2D,$92,$29,$86,$24,$23,$92,$24,$98,$26,$F6,$11 +.db $F9,$00,$FA,$00,$F8,$33,$92,$1C,$86,$1C,$8C,$1C,$86,$1C,$1C,$92 +.db $1D,$86,$1D,$8C,$1D,$86,$1D,$1D,$92,$1C,$86,$1C,$8C,$1C,$86,$1C +.db $1C,$8C,$1D,$A4,$1D,$F6,$05,$92,$28,$86,$28,$8C,$28,$86,$28,$28 +.db $92,$29,$86,$29,$8C,$29,$86,$29,$29,$92,$28,$86,$28,$8C,$28,$86 +.db $28,$28,$92,$26,$86,$26,$8C,$26,$86,$26,$26,$92,$2B,$86,$2B,$8C +.db $2B,$86,$2B,$2B,$92,$2D,$86,$2D,$8C,$2D,$86,$2D,$2D,$92,$28,$86 +.db $28,$8C,$26,$86,$26,$26,$8C,$24,$00,$00,$00,$92,$28,$86,$28,$8C +.db $28,$86,$28,$28,$92,$29,$86,$29,$8C,$29,$86,$29,$29,$92,$28,$86 +.db $28,$8C,$28,$86,$28,$28,$92,$26,$86,$26,$8C,$26,$86,$26,$26,$92 +.db $2B,$86,$2B,$8C,$2B,$86,$2B,$2B,$92,$2D,$86,$2D,$8C,$2D,$86,$2D +.db $2D,$92,$28,$86,$28,$8C,$26,$86,$26,$26,$8C,$24,$00,$00,$00,$92 +.db $28,$86,$29,$8C,$2B,$86,$2D,$2D,$92,$2B,$86,$28,$8C,$24,$86,$26 +.db $26,$92,$2D,$86,$2F,$8C,$24,$86,$25,$25,$92,$26,$86,$24,$8C,$2F +.db $86,$2B,$2B,$92,$29,$86,$28,$8C,$29,$86,$2D,$2D,$92,$26,$86,$24 +.db $8C,$2F,$86,$2B,$2B,$92,$24,$86,$28,$8C,$26,$86,$24,$24,$92,$2F +.db $86,$2B,$8C,$24,$00,$92,$28,$86,$28,$8C,$28,$86,$28,$28,$92,$29 +.db $86,$29,$8C,$29,$86,$29,$29,$92,$28,$86,$28,$8C,$28,$86,$28,$28 +.db $92,$29,$86,$29,$8C,$29,$86,$29,$29,$8C,$00,$00,$00,$00,$00,$00 +.db $86,$2D,$2D,$2F,$2F,$92,$30,$86,$24,$8C,$2B,$86,$2B,$2B,$8C,$24 +.db $00,$00,$00,$F9,$00,$FA,$00,$F8,$53,$24,$21,$24,$26,$92,$28,$86 +.db $28,$27,$92,$26,$8C,$24,$21,$86,$24,$24,$26,$B0,$28,$86,$00,$8C +.db $24,$21,$24,$26,$86,$28,$29,$2B,$98,$2D,$86,$00,$8C,$21,$24,$86 +.db $29,$28,$26,$B0,$24,$86,$00,$F4,$1E,$9C,$FF,$F6,$05,$F9,$17,$FA +.db $16,$F8,$14,$98,$28,$92,$24,$86,$1F,$1E,$92,$1F,$98,$21,$F9,$17 +.db $FA,$16,$F8,$14,$98,$18,$15,$1A,$13,$18,$15,$8C,$1A,$A4,$19,$8C +.db $18,$00,$1C,$00,$1D,$00,$1E,$00,$1D,$00,$1C,$00,$1A,$13,$15,$17 +.db $18,$00,$1C,$00,$1D,$00,$1E,$00,$1F,$13,$17,$1A,$18,$13,$98,$18 +.db $8C,$18,$00,$1C,$00,$1D,$00,$1E,$00,$1D,$00,$1C,$00,$1A,$13,$15 +.db $17,$18,$00,$1C,$00,$1D,$00,$1E,$00,$1F,$13,$17,$1A,$18,$13,$98 +.db $18,$8C,$10,$11,$13,$15,$13,$10,$0C,$0E,$15,$17,$18,$19,$1A,$18 +.db $17,$13,$11,$10,$11,$15,$1A,$18,$17,$13,$18,$1C,$1A,$18,$17,$13 +.db $18,$00,$18,$00,$18,$00,$17,$00,$17,$00,$15,$00,$15,$00,$14,$00 +.db $14,$00,$13,$00,$00,$00,$00,$00,$15,$17,$98,$18,$13,$A4,$18,$8C +.db $00,$10,$11,$13,$15,$13,$10,$0C,$0E,$15,$17,$18,$19,$1A,$18,$17 +.db $13,$11,$10,$11,$15,$1A,$18,$17,$13,$18,$1C,$1A,$18,$17,$13,$18 +.db $00,$F4,$9E,$9D,$FF,$F6,$11,$F9,$17,$FA,$16,$F8,$15,$8C,$00,$00 +.db $00,$00,$00,$00,$00,$00,$8C,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$28,$00,$24,$86,$00,$1F,$21,$92,$24 +.db $24,$86,$21,$8C,$1F,$24,$24,$2B,$98,$28,$92,$26,$86,$1F,$8C,$28 +.db $00,$24,$86,$00,$1F,$21,$92,$24,$24,$86,$21,$8C,$1F,$24,$86,$29 +.db $28,$8C,$26,$98,$24,$92,$00,$86,$1F,$8C,$28,$00,$24,$86,$00,$1F +.db $21,$92,$24,$24,$86,$21,$8C,$1F,$24,$24,$2B,$98,$28,$92,$26,$86 +.db $1F,$8C,$28,$00,$24,$86,$00,$1F,$21,$92,$24,$24,$86,$21,$8C,$1F +.db $24,$86,$29,$28,$8C,$26,$98,$24,$8C,$00,$00,$28,$00,$92,$24,$86 +.db $1F,$98,$28,$24,$86,$27,$24,$8C,$1F,$27,$B0,$26,$8C,$00,$28,$00 +.db $92,$24,$86,$1F,$98,$28,$24,$86,$27,$24,$8C,$1F,$B0,$2B,$8C,$00 +.db $00,$28,$00,$24,$86,$00,$1F,$21,$92,$24,$24,$86,$26,$28,$24,$8C +.db $1F,$98,$21,$A4,$24,$8C,$1F,$2B,$2D,$2B,$2D,$92,$2B,$86,$1F,$29 +.db $28,$26,$B0,$24,$86,$00,$8C,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$F4,$66,$9E,$FF,$FF +.db $00,$09,$9C,$01,$8B,$9D,$02,$55,$9E,$03,$4F,$9F,$04,$4F,$9F,$FF +.db $F5,$01,$F6,$05,$F9,$5B,$FA,$39,$F8,$11,$98,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$98,$00,$27,$A4,$24 +.db $8C,$1F,$21,$C8,$24,$8C,$00,$98,$00,$1F,$8C,$24,$A4,$2B,$B0,$27 +.db $98,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$27,$A4,$24,$8C,$1F,$21,$C8,$24,$8C,$00,$98,$00,$1F,$8C +.db $24,$A4,$2B,$B0,$27,$98,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$27,$A4,$24,$8C,$1F,$98,$27,$C8,$24 +.db $8C,$26,$23,$1F,$98,$27,$E0,$26,$A4,$00,$98,$00,$27,$A4,$24,$8C +.db $1F,$98,$27,$C8,$24,$8C,$26,$23,$1F,$98,$27,$E0,$26,$A4,$00,$F4 +.db $7B,$9F,$FF,$F6,$05,$F9,$38,$FA,$39,$F8,$40,$86,$21,$00,$24,$00 +.db $22,$00,$26,$00,$21,$00,$24,$00,$1F,$00,$20,$00,$F4,$F3,$9F,$FF +.db $F6,$01,$F9,$00,$FA,$00,$F8,$53,$8C,$2F,$F4,$10,$A0,$FF,$FF,$00 +.db $60,$9F,$01,$1E,$A0,$02,$F3,$9F,$03,$10,$A0,$04,$1E,$A0,$FF,$F5 +.db $01,$F6,$1A,$F9,$00,$FA,$00,$F8,$33,$F0,$D1,$A0,$F0,$D1,$A0,$F0 +.db $D1,$A0,$F0,$D1,$A0,$F0,$D1,$A0,$F0,$D1,$A0,$F0,$D1,$A0,$F0,$D1 +.db $A0,$F0,$DC,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F0,$DC,$A0 +.db $F0,$DC,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F6,$18,$F0,$D1,$A0,$F0,$D1 +.db $A0,$F0,$D1,$A0,$F0,$D1,$A0,$F0,$D1,$A0,$F0,$D1,$A0,$F0,$D1,$A0 +.db $F0,$D1,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F0 +.db $DC,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F6,$16,$F0,$D1,$A0 +.db $F0,$D1,$A0,$F0,$D1,$A0,$F0,$D1,$A0,$F0,$D1,$A0,$F0,$D1,$A0,$F0 +.db $D1,$A0,$F0,$D1,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F0,$DC +.db $A0,$F0,$DC,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F0,$DC,$A0,$F4,$2F,$A0 +.db $FF,$8A,$2D,$2F,$2B,$2D,$2D,$2F,$2B,$2D,$F1,$FF,$8A,$2D,$2F,$2B +.db $2F,$2D,$2F,$2B,$2F,$F1,$FF,$F6,$0E,$F0,$03,$A1,$F0,$2C,$A1,$F6 +.db $0C,$F0,$03,$A1,$F0,$2C,$A1,$F6,$0A,$F0,$03,$A1,$F0,$2C,$A1,$F4 +.db $E7,$A0,$FF,$F9,$00,$FA,$62,$F8,$02,$94,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$F1,$FF,$F9,$12,$FA,$00 +.db $F8,$11,$D0,$18,$A8,$15,$10,$11,$D0,$15,$94,$00,$00,$A8,$10,$15 +.db $1C,$D0,$18,$17,$94,$00,$00,$F1,$FF,$FF,$00,$2F,$A0,$01,$E7,$A0 +.db $02,$E7,$A0,$03,$49,$A1,$04,$49,$A1,$FF,$F5,$01,$F6,$03,$F9,$00 +.db $FA,$00,$F8,$33,$F0,$1E,$A2,$F0,$1E,$A2,$F0,$1E,$A2,$F0,$1E,$A2 +.db $86,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$F9,$12,$FA +.db $00,$F8,$11,$98,$26,$B0,$28,$98,$00,$26,$B0,$28,$98,$00,$00,$26 +.db $28,$29,$27,$E0,$28,$98,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$F9,$12,$FA,$00,$F8,$11,$98,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$B0,$30,$98,$2D,$28,$27,$C8 +.db $2D,$98,$00,$28,$2D,$34,$B0,$30,$2F,$98,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$B0,$30,$98,$2D,$28,$27 +.db $C8,$2D,$98,$00,$28,$2D,$34,$B0,$30,$2F,$98,$00,$2D,$34,$39,$B0 +.db $35,$34,$98,$00,$28,$32,$34,$B0,$32,$30,$98,$00,$30,$2F,$2D,$B0 +.db $2F,$2A,$98,$00,$2F,$2D,$2F,$B0,$2D,$2C,$F4,$B2,$A1,$FF,$86,$2D +.db $2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2D,$2F +.db $2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$30 +.db $30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$30,$2F +.db $2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$2F,$F1 +.db $FF,$F6,$03,$F9,$12,$FA,$00,$F8,$11,$98,$00,$00,$00,$0E,$E0,$10 +.db $98,$00,$00,$00,$0E,$E0,$10,$B0,$1D,$98,$1A,$15,$17,$C8,$1A,$98 +.db $00,$15,$1A,$21,$B0,$1D,$1C,$98,$00,$00,$00,$0E,$E0,$10,$98,$00 +.db $00,$00,$0E,$E0,$10,$B0,$1D,$98,$1A,$15,$17,$C8,$1A,$98,$00,$15 +.db $1A,$21,$B0,$1D,$1C,$98,$00,$00,$00,$0E,$10,$F9,$34,$FA,$39,$F8 +.db $41,$15,$15,$F9,$17,$FA,$16,$F8,$14,$0E,$10,$F9,$34,$FA,$39,$F8 +.db $41,$15,$15,$F9,$12,$FA,$00,$F8,$11,$00,$0E,$10,$11,$0F,$C8,$10 +.db $F6,$0F,$F9,$07,$FA,$05,$F8,$02,$E0,$40,$98,$00,$F9,$12,$FA,$00 +.db $F8,$11,$8C,$3E,$3B,$37,$35,$F6,$0D,$86,$3E,$3B,$37,$35,$F6,$0B +.db $3E,$3B,$37,$35,$F6,$09,$3E,$3B,$37,$35,$F6,$07,$3E,$3B,$37,$35 +.db $F6,$05,$3E,$3B,$37,$35,$F6,$03,$3E,$3B,$37,$35,$F6,$0F,$F0,$9C +.db $A3,$F6,$15,$F0,$9C,$A3,$F6,$14,$F0,$9C,$A3,$F6,$16,$F0,$9C,$A3 +.db $F6,$0F,$F0,$9C,$A3,$F6,$15,$F0,$9C,$A3,$F6,$14,$F0,$9C,$A3,$F6 +.db $16,$F0,$9C,$A3,$F6,$0F,$F0,$9C,$A3,$F6,$15,$F0,$9C,$A3,$F6,$14 +.db $F0,$9C,$A3,$F6,$16,$F0,$9C,$A3,$F6,$0F,$F0,$9C,$A3,$F6,$15,$F0 +.db $9C,$A3,$F6,$14,$F0,$9C,$A3,$F6,$16,$F0,$9C,$A3,$F6,$0F,$F0,$9C +.db $A3,$F6,$15,$F0,$9C,$A3,$F6,$14,$F0,$9C,$A3,$F6,$16,$F0,$9C,$A3 +.db $F6,$17,$F0,$9C,$A3,$F6,$1A,$F0,$9C,$A3,$F6,$16,$F0,$9C,$A3,$F6 +.db $1B,$F0,$9C,$A3,$F6,$17,$F0,$9C,$A3,$F6,$16,$F0,$9C,$A3,$F6,$11 +.db $F0,$9C,$A3,$F6,$16,$F0,$9C,$A3,$F4,$20,$A3,$FF,$F9,$00,$FA,$00 +.db $F8,$33,$86,$28,$2D,$28,$2D,$30,$2D,$30,$2D,$2F,$2B,$21,$28,$24 +.db $1C,$23,$20,$F1,$FF,$F6,$1B,$F9,$07,$FA,$05,$F8,$02,$98,$00,$00 +.db $00,$0E,$B0,$10,$98,$00,$00,$00,$00,$00,$0E,$B0,$10,$98,$00,$00 +.db $00,$00,$00,$0E,$B0,$10,$98,$00,$00,$00,$00,$00,$0E,$B0,$10,$98 +.db $00,$00,$00,$00,$00,$0E,$B0,$10,$98,$00,$00,$00,$00,$00,$0E,$B0 +.db $10,$98,$00,$00,$00,$00,$00,$0E,$B0,$10,$98,$00,$00,$00,$00,$00 +.db $0E,$B0,$10,$98,$00,$00,$00,$00,$00,$00,$00,$0E,$10,$00,$00,$0E +.db $10,$00,$0E,$10,$11,$0E,$E0,$10,$98,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$F6,$0F,$F9,$38,$FA,$39,$F8,$40,$B0,$15,$15 +.db $1B,$1B,$1A,$1A,$1C,$1C,$98,$15,$15,$15,$15,$1B,$1B,$1B,$1B,$1A +.db $1A,$1A,$1A,$1C,$1C,$1C,$1C,$F6,$0F,$F0,$C3,$A4,$F6,$15,$F0,$C3 +.db $A4,$F6,$14,$F0,$C3,$A4,$F6,$16,$F0,$C3,$A4,$F6,$0F,$F0,$C3,$A4 +.db $F6,$15,$F0,$C3,$A4,$F6,$14,$F0,$C3,$A4,$F6,$16,$F0,$C3,$A4,$F6 +.db $0F,$F0,$C3,$A4,$F6,$15,$F0,$C3,$A4,$F6,$14,$F0,$C3,$A4,$F6,$16 +.db $F0,$C3,$A4,$F6,$17,$F0,$C3,$A4,$F6,$1A,$F0,$C3,$A4,$F6,$16,$F0 +.db $C3,$A4,$F6,$1B,$F0,$C3,$A4,$F6,$17,$F0,$C3,$A4,$F6,$16,$F0,$C3 +.db $A4,$F6,$11,$F0,$C3,$A4,$F6,$16,$F0,$C3,$A4,$F6,$0F,$F0,$C3,$A4 +.db $F6,$15,$F0,$C3,$A4,$F6,$14,$F0,$C3,$A4,$F6,$16,$F0,$C3,$A4,$F4 +.db $47,$A4,$FF,$86,$15,$00,$15,$00,$15,$00,$15,$00,$15,$00,$15,$00 +.db $15,$00,$15,$00,$F1,$FF,$F6,$01,$F9,$00,$FA,$00,$F8,$53,$98,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$2F,$2F,$00,$00,$2F,$2F,$00,$2F,$2F,$2F,$2F,$E0 +.db $2F,$FF,$FF,$00,$5A,$A1,$01,$61,$A2,$02,$B5,$A3,$03,$D6,$A4,$04 +.db $32,$A5,$FF,$F5,$01,$F6,$05,$F9,$07,$FA,$05,$F8,$02,$AA,$2D,$9C +.db $29,$8E,$24,$9C,$23,$8E,$24,$AA,$26,$8E,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$F6,$11,$F9,$07,$FA,$05,$F8,$02,$AA,$28,$9C,$24,$8E,$1F +.db $9C,$21,$8E,$24,$AA,$24,$9C,$1F,$8E,$24,$9C,$24,$8E,$2B,$AA,$28 +.db $26,$28,$9C,$24,$8E,$1F,$9C,$21,$8E,$24,$AA,$24,$9C,$1F,$8E,$24 +.db $29,$28,$26,$AA,$24,$8E,$00,$00,$00,$AA,$28,$9C,$24,$8E,$1F,$9C +.db $21,$8E,$24,$AA,$24,$9C,$1F,$8E,$24,$9C,$24,$8E,$2B,$AA,$28,$26 +.db $28,$9C,$24,$8E,$1F,$9C,$21,$8E,$24,$AA,$24,$9C,$1F,$8E,$24,$29 +.db $28,$26,$AA,$24,$8E,$00,$00,$00,$AA,$28,$9C,$24,$8E,$1F,$AA,$28 +.db $24,$27,$9C,$24,$8E,$27,$AA,$26,$8E,$00,$00,$00,$AA,$28,$9C,$24 +.db $8E,$1F,$AA,$28,$24,$27,$9C,$24,$8E,$1F,$AA,$2B,$8E,$00,$00,$00 +.db $AA,$28,$9C,$24,$8E,$1F,$9C,$21,$8E,$24,$AA,$24,$9C,$1F,$8E,$24 +.db $29,$28,$26,$AA,$24,$8E,$00,$00,$00,$F4,$59,$A5,$FF,$F6,$05,$F9 +.db $07,$FA,$05,$F8,$02,$AA,$28,$9C,$24,$8E,$1F,$9C,$1E,$8E,$1F,$AA +.db $21,$F9,$17,$FA,$16,$F8,$15,$8E,$00,$24,$24,$00,$21,$21,$00,$26 +.db $26,$1F,$21,$23,$00,$24,$24,$00,$21,$21,$00,$26,$26,$1F,$21,$23 +.db $00,$24,$24,$00,$28,$28,$00,$29,$29,$00,$2A,$2A,$00,$29,$29,$00 +.db $28,$28,$00,$26,$26,$00,$00,$00,$00,$24,$24,$00,$28,$28,$00,$29 +.db $29,$00,$2A,$2A,$00,$29,$29,$00,$28,$28,$00,$00,$00,$00,$00,$00 +.db $F6,$1D,$F9,$5B,$FA,$39,$F8,$11,$AA,$28,$9C,$24,$8E,$1F,$9C,$21 +.db $8E,$24,$AA,$24,$9C,$1F,$8E,$24,$9C,$24,$8E,$2B,$AA,$28,$26,$28 +.db $9C,$24,$8E,$1F,$9C,$21,$8E,$24,$AA,$24,$9C,$1F,$8E,$24,$29,$28 +.db $26,$AA,$24,$8E,$00,$00,$00,$F6,$05,$F9,$17,$FA,$16,$F8,$15,$00 +.db $29,$29,$00,$29,$29,$00,$28,$28,$00,$28,$28,$00,$27,$27,$00,$27 +.db $27,$00,$26,$26,$00,$00,$00,$00,$29,$29,$00,$29,$29,$00,$28,$28 +.db $00,$28,$28,$00,$27,$27,$00,$27,$27,$00,$26,$26,$00,$00,$00,$00 +.db $24,$24,$00,$28,$28,$00,$29,$29,$00,$2A,$2A,$00,$29,$29,$00,$28 +.db $28,$00,$00,$00,$00,$00,$00,$F4,$31,$A6,$FF,$F6,$05,$F9,$5B,$FA +.db $39,$F8,$11,$8E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $8E,$24,$00,$00,$21,$00,$00,$26,$00,$00,$1F,$21,$23,$24,$00,$00 +.db $21,$00,$00,$26,$00,$00,$1F,$21,$23,$AA,$24,$28,$29,$2A,$29,$28 +.db $26,$8E,$1F,$21,$23,$AA,$24,$28,$29,$2A,$2B,$1F,$8E,$24,$2B,$28 +.db $AA,$24,$24,$28,$29,$2A,$29,$28,$26,$8E,$1F,$21,$23,$AA,$24,$28 +.db $29,$2A,$2B,$1F,$8E,$24,$2B,$28,$AA,$24,$29,$29,$28,$28,$27,$27 +.db $26,$8E,$1F,$21,$23,$AA,$29,$29,$28,$28,$27,$27,$26,$8E,$1F,$21 +.db $23,$AA,$24,$28,$29,$2A,$2B,$1F,$8E,$24,$2B,$28,$AA,$24,$F4,$20 +.db $A7,$FF,$FF,$00,$43,$A5,$01,$1D,$A6,$02,$0B,$A7,$03,$92,$A7,$04 +.db $92,$A7,$FF,$F5,$02,$F6,$0C,$F9,$5B,$FA,$39,$F8,$11,$A4,$24,$8C +.db $24,$86,$28,$8C,$28,$86,$26,$8C,$24,$23,$21,$98,$21,$86,$21,$23 +.db $A4,$1F,$86,$21,$23,$A4,$24,$8C,$24,$86,$28,$8C,$28,$86,$26,$8C +.db $24,$23,$21,$98,$21,$86,$21,$23,$B0,$1F,$98,$28,$28,$86,$2B,$8C +.db $2B,$86,$29,$8C,$28,$26,$B0,$24,$29,$A4,$24,$8C,$24,$86,$28,$8C +.db $28,$86,$26,$8C,$24,$23,$21,$98,$21,$86,$21,$23,$A4,$1F,$86,$21 +.db $23,$A4,$24,$8C,$24,$86,$28,$8C,$28,$86,$26,$8C,$24,$23,$21,$98 +.db $21,$86,$21,$23,$B0,$1F,$98,$28,$28,$86,$2B,$8C,$2B,$86,$29,$8C +.db $28,$26,$B0,$24,$A4,$23,$86,$23,$23,$B0,$24,$8C,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$F4,$A3,$A7,$FF,$F6,$00,$F9,$17 +.db $FA,$16,$F8,$14,$8C,$00,$86,$24,$00,$21,$8C,$1F,$86,$00,$89,$20 +.db $83,$21,$86,$22,$98,$23,$86,$00,$8C,$00,$86,$26,$00,$28,$8C,$29 +.db $86,$00,$23,$24,$26,$8C,$29,$86,$25,$8C,$26,$F4,$3C,$A8,$FF,$F6 +.db $0C,$F9,$38,$FA,$39,$F8,$41,$8C,$18,$18,$1F,$1C,$1C,$23,$1F,$1C +.db $1D,$24,$29,$26,$2B,$1F,$26,$23,$F4,$6F,$A8,$FF,$F6,$01,$F9,$00 +.db $FA,$00,$F8,$53,$8C,$2F,$86,$24,$24,$8C,$2F,$86,$24,$24,$8C,$2F +.db $86,$24,$24,$8C,$2F,$86,$24,$24,$8C,$2F,$86,$24,$24,$8C,$2F,$86 +.db $24,$24,$83,$2F,$24,$86,$2F,$2F,$83,$24,$24,$86,$2F,$24,$8C,$24 +.db $F4,$8C,$A8,$FF,$FF,$00,$A3,$A7,$01,$3C,$A8,$02,$6F,$A8,$03,$8C +.db $A8,$04,$C4,$A8,$FF,$60,$A9,$02,$AC,$38,$06,$F0,$0F,$C0,$D0,$90 +.db $06,$C0,$F6,$B0,$07,$A9,$01,$25,$06,$F0,$01,$60,$A5,$13,$C9,$E0 +.db $B0,$2D,$A0,$04,$A6,$1E,$AD,$1F,$06,$C9,$01,$90,$0E,$A5,$14,$D0 +.db $04,$A5,$15,$F0,$06,$BD,$AF,$AB,$4C,$0E,$A9,$BD,$9E,$AB,$85,$34 +.db $30,$06,$8C,$24,$06,$20,$4A,$A1,$AC,$24,$06,$C0,$04,$F0,$01,$60 +.db $A5,$34,$29,$0F,$D0,$03,$4C,$E4,$AA,$C9,$01,$D0,$03,$4C,$0A,$AB +.db $C9,$02,$D0,$03,$4C,$3E,$AB,$C9,$03,$D0,$03,$4C,$50,$AB,$C9,$04 +.db $D0,$03,$4C,$7E,$AB,$AC,$27,$06,$B9,$2E,$AB,$4C,$10,$AB,$AE,$1F +.db $06,$F0,$03,$4C,$1D,$AA,$48,$AD,$24,$06,$0A,$A8,$B9,$0F,$AC,$85 +.db $34,$B9,$10,$AC,$85,$35,$68,$48,$0A,$A8,$B1,$34,$85,$32,$C8,$B1 +.db $34,$85,$33,$A9,$00,$85,$2E,$68,$A8,$A5,$13,$38,$F9,$D9,$AB,$A6 +.db $1E,$18,$7D,$C0,$AB,$AE,$24,$06,$D0,$03,$38,$E9,$03,$85,$2B,$A5 +.db $19,$45,$1A,$29,$40,$D0,$21,$A5,$12,$18,$79,$EB,$AB,$85,$28,$A5 +.db $2E,$18,$69,$04,$A8,$A6,$3C,$B1,$32,$C9,$FF,$F0,$5C,$29,$3F,$9D +.db $01,$02,$48,$A9,$40,$4C,$D2,$A9,$A5,$12,$18,$79,$F4,$AB,$85,$28 +.db $A4,$2E,$A6,$3C,$B1,$32,$C9,$FF,$F0,$3F,$29,$3F,$9D,$01,$02,$48 +.db $A9,$00,$85,$38,$AD,$C7,$03,$0A,$A8,$B9,$28,$CF,$85,$34,$B9,$29 +.db $CF,$85,$35,$68,$A8,$B1,$34,$05,$38,$0D,$E0,$06,$9D,$02,$02,$A4 +.db $2E,$A5,$28,$18,$79,$D1,$AB,$9D,$03,$02,$A5,$2B,$18,$79,$D5,$AB +.db $9D,$00,$02,$8A,$18,$69,$04,$85,$3C,$E6,$2E,$A5,$2E,$C9,$04,$B0 +.db $0B,$A5,$19,$45,$1A,$29,$40,$D0,$A7,$4C,$9F,$A9,$60,$48,$AD,$24 +.db $06,$0A,$A8,$B9,$0F,$AC,$85,$34,$B9,$10,$AC,$85,$35,$68,$48,$0A +.db $A8,$B1,$34,$85,$32,$C8,$B1,$34,$85,$33,$A9,$00,$85,$2E,$68,$A8 +.db $A5,$13,$38,$F9,$E2,$AB,$A6,$1E,$18,$7D,$C0,$AB,$AE,$24,$06,$D0 +.db $03,$38,$E9,$03,$85,$2B,$A5,$19,$45,$1A,$29,$40,$D0,$21,$A5,$12 +.db $18,$79,$FD,$AB,$85,$28,$A5,$2E,$18,$69,$04,$A8,$A6,$3C,$B1,$32 +.db $C9,$FF,$F0,$5C,$29,$3F,$9D,$01,$02,$48,$A9,$40,$4C,$99,$AA,$A5 +.db $12,$18,$79,$06,$AC,$85,$28,$A4,$2E,$A6,$3C,$B1,$32,$C9,$FF,$F0 +.db $3F,$29,$3F,$9D,$01,$02,$48,$A9,$00,$85,$38,$AD,$C7,$03,$0A,$A8 +.db $B9,$28,$CF,$85,$34,$B9,$29,$CF,$85,$35,$68,$A8,$B1,$34,$05,$38 +.db $0D,$E0,$06,$9D,$02,$02,$A4,$2E,$A5,$28,$18,$79,$D1,$AB,$9D,$03 +.db $02,$A5,$2B,$18,$79,$D5,$AB,$9D,$00,$02,$8A,$18,$69,$04,$85,$3C +.db $E6,$2E,$A5,$2E,$C9,$04,$B0,$0B,$A5,$19,$45,$1A,$29,$40,$D0,$A7 +.db $4C,$66,$AA,$60,$AC,$27,$06,$B9,$FE,$AA,$10,$0F,$A9,$00,$8D,$27 +.db $06,$A9,$03,$8D,$24,$06,$20,$4A,$A1,$A9,$08,$4C,$4E,$A9,$07,$07 +.db $07,$07,$07,$07,$07,$08,$08,$08,$08,$80,$AC,$27,$06,$B9,$23,$AB +.db $10,$0E,$A9,$00,$8D,$27,$06,$A5,$14,$D0,$03,$8D,$28,$06,$A9,$00 +.db $4C,$4E,$A9,$08,$08,$07,$07,$01,$01,$02,$02,$00,$00,$80,$08,$08 +.db $08,$07,$07,$07,$01,$01,$01,$02,$02,$02,$00,$00,$00,$80,$A9,$00 +.db $8D,$27,$06,$A9,$03,$8D,$24,$06,$20,$4A,$A1,$A9,$00,$4C,$4E,$A9 +.db $AC,$27,$06,$A5,$19,$29,$04,$F0,$03,$4C,$0A,$AB,$B9,$74,$AB,$10 +.db $10,$A9,$00,$8D,$27,$06,$A5,$15,$D0,$05,$A9,$01,$8D,$28,$06,$A9 +.db $03,$4C,$4E,$A9,$04,$04,$04,$05,$05,$05,$03,$03,$03,$80,$A9,$00 +.db $8D,$27,$06,$A0,$02,$AD,$2B,$06,$29,$01,$D0,$02,$A0,$07,$A5,$15 +.db $F0,$08,$A5,$19,$29,$04,$D0,$02,$A0,$06,$98,$4C,$4E,$A9,$80,$05 +.db $01,$01,$03,$04,$80,$80,$80,$01,$03,$03,$01,$82,$82,$01,$80,$80 +.db $05,$01,$01,$03,$04,$01,$80,$80,$01,$03,$03,$01,$01,$01,$01,$80 +.db $00,$00,$00,$00,$00,$00,$00,$06,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$08,$00,$08,$00,$00,$08,$08,$11,$19,$11,$1E,$1E,$1E,$1E +.db $19,$17,$1B,$23,$1B,$29,$29,$29,$29,$23,$21,$EE,$EE,$EE,$EF,$EF +.db $EF,$EF,$EE,$EE,$02,$02,$02,$01,$01,$01,$01,$02,$02,$EA,$EA,$EA +.db $ED,$ED,$ED,$ED,$EA,$EA,$06,$06,$06,$03,$03,$03,$03,$06,$06,$19 +.db $AC,$19,$AC,$19,$AC,$19,$AC,$19,$AC,$2B,$AC,$33,$AC,$3B,$AC,$43 +.db $AC,$4B,$AC,$53,$AC,$5B,$AC,$63,$AC,$6B,$AC,$01,$02,$03,$04,$02 +.db $01,$04,$03,$05,$06,$07,$08,$06,$05,$08,$07,$0B,$0C,$FF,$FF,$0C +.db $0B,$FF,$FF,$0D,$0E,$0F,$10,$0E,$0D,$10,$0F,$11,$12,$13,$14,$12 +.db $11,$14,$13,$15,$16,$17,$18,$16,$15,$18,$17,$19,$1A,$1B,$1C,$1A +.db $19,$1C,$1B,$FF,$1D,$09,$0A,$1D,$FF,$0A,$09,$20,$FF,$1E,$1F,$FF +.db $20,$1F,$1E,$A5,$E1,$C9,$09,$B0,$24,$60,$AD,$7D,$06,$F0,$0D,$A5 +.db $19,$29,$04,$D0,$06,$A9,$00,$85,$1D,$85,$17,$60,$20,$F5,$AC,$20 +.db $DA,$AD,$20,$53,$AD,$20,$7A,$AD,$20,$83,$B0,$A5,$E1,$0A,$A8,$B9 +.db $AC,$AC,$85,$32,$B9,$AD,$AC,$85,$33,$6C,$32,$00,$16,$AE,$16,$AE +.db $16,$AE,$43,$B6,$43,$B5,$EB,$B6,$A5,$B8,$A5,$B8,$D3,$B8,$EB,$B6 +.db $31,$B7,$49,$B7,$5B,$B7,$7C,$B7,$94,$B7,$A6,$B7,$BE,$B7,$D6,$B7 +.db $E8,$B7,$AD,$26,$06,$F0,$15,$EE,$26,$06,$AD,$26,$06,$29,$7F,$C9 +.db $20,$90,$08,$A9,$00,$8D,$26,$06,$20,$4A,$A1,$60,$A9,$02,$85,$DF +.db $A9,$00,$85,$E0,$60,$AD,$F6,$04,$C9,$07,$90,$09,$A9,$00,$8D,$6E +.db $03,$8D,$6F,$03,$60,$AD,$16,$06,$C9,$3B,$B0,$04,$EE,$16,$06,$60 +.db $A9,$00,$8D,$16,$06,$AD,$6E,$03,$38,$E9,$01,$8D,$6E,$03,$AD,$6F +.db $03,$E9,$00,$8D,$6F,$03,$D0,$2A,$AD,$6E,$03,$D0,$25,$8D,$24,$06 +.db $A9,$01,$8D,$98,$03,$AD,$1F,$06,$F0,$03,$20,$52,$B4,$A9,$00,$85 +.db $E0,$85,$E1,$8D,$DC,$06,$8D,$DD,$06,$A9,$04,$85,$DF,$20,$4A,$A1 +.db $68,$68,$60,$A5,$E1,$C9,$05,$B0,$20,$AD,$1D,$06,$F0,$08,$A5,$E1 +.db $D0,$17,$A2,$01,$D0,$11,$A5,$E1,$C9,$03,$F0,$0D,$A2,$00,$AD,$24 +.db $06,$C9,$03,$90,$02,$A2,$04,$86,$E1,$60,$AD,$1F,$06,$F0,$2A,$AD +.db $FD,$05,$F0,$25,$A9,$00,$8D,$FD,$05,$A9,$40,$85,$15,$A5,$19,$09 +.db $04,$85,$19,$A9,$0C,$85,$8F,$A9,$03,$85,$1D,$A9,$00,$85,$E1,$20 +.db $52,$B4,$A9,$04,$8D,$F1,$05,$85,$50,$AD,$36,$06,$CD,$37,$06,$F0 +.db $06,$8D,$37,$06,$20,$4A,$A1,$AC,$38,$06,$F0,$1D,$A5,$06,$29,$03 +.db $D0,$17,$C0,$01,$D0,$04,$A2,$26,$86,$8E,$C0,$F8,$90,$08,$20,$B0 +.db $A4,$A9,$FF,$8D,$38,$06,$EE,$38,$06,$60,$AD,$0C,$03,$29,$20,$F0 +.db $30,$A9,$07,$85,$DF,$AC,$24,$06,$C0,$04,$D0,$02,$A0,$03,$AE,$25 +.db $06,$F0,$1E,$E0,$02,$B0,$04,$C0,$00,$D0,$11,$8C,$25,$06,$A9,$01 +.db $8E,$24,$06,$E0,$03,$D0,$02,$A9,$81,$8D,$26,$06,$BD,$12,$AE,$85 +.db $8F,$60,$00,$09,$09,$0E,$AD,$24,$06,$C9,$03,$90,$03,$20,$C3,$B5 +.db $A5,$1E,$0A,$A8,$B9,$31,$AE,$85,$32,$B9,$32,$AE,$85,$33,$6C,$32 +.db $00,$59,$AE,$AC,$AE,$CD,$AE,$AC,$AE,$FA,$AE,$31,$AF,$59,$AE,$82 +.db $AE,$59,$AE,$FA,$AE,$FA,$AE,$69,$B0,$69,$B0,$CD,$AE,$FA,$AE,$FA +.db $AE,$FA,$AE,$4D,$AF,$69,$B0,$59,$AF,$A5,$15,$D0,$1A,$A5,$19,$29 +.db $04,$D0,$13,$AD,$0A,$03,$D0,$02,$85,$1D,$20,$2C,$B5,$20,$16,$B3 +.db $20,$A6,$B3,$20,$68,$AF,$60,$A5,$19,$29,$04,$D0,$F9,$A9,$0A,$85 +.db $1D,$60,$A5,$15,$D0,$1B,$A5,$19,$29,$04,$D0,$14,$AD,$0A,$03,$29 +.db $04,$D0,$04,$A9,$00,$85,$1D,$20,$2C,$B5,$20,$A6,$B3,$20,$68,$AF +.db $60,$A5,$19,$29,$04,$D0,$F9,$A9,$0A,$85,$1D,$60,$20,$E6,$AE,$A5 +.db $14,$D0,$02,$85,$1D,$20,$16,$B3,$20,$D0,$B4,$20,$A6,$B3,$20,$68 +.db $AF,$AD,$0A,$03,$29,$04,$F0,$04,$A9,$07,$85,$1D,$60,$20,$E6,$AE +.db $A5,$14,$D0,$02,$85,$1D,$20,$16,$B3,$20,$D0,$B4,$20,$A6,$B3,$20 +.db $BA,$B4,$20,$68,$AF,$60,$A5,$15,$C9,$07,$90,$0D,$A2,$0A,$AD,$1D +.db $06,$F0,$02,$A2,$0B,$86,$1D,$68,$68,$60,$A5,$15,$D0,$0A,$A5,$19 +.db $29,$04,$D0,$04,$A9,$01,$85,$1D,$20,$1C,$AF,$20,$8D,$AF,$20,$6C +.db $B3,$A5,$E1,$F0,$03,$20,$A6,$B3,$20,$29,$B4,$60,$A5,$19,$29,$04 +.db $F0,$0E,$AD,$0A,$03,$29,$80,$F0,$07,$A5,$15,$18,$69,$01,$85,$15 +.db $60,$A5,$15,$D0,$0A,$A5,$19,$29,$04,$D0,$04,$A9,$01,$85,$1D,$20 +.db $B8,$AF,$20,$6C,$B3,$A5,$E1,$F0,$03,$20,$A6,$B3,$60,$A2,$00,$A0 +.db $10,$20,$69,$B8,$A9,$04,$85,$1D,$60,$20,$A6,$B3,$A2,$00,$A0,$08 +.db $20,$69,$B8,$A9,$00,$85,$1D,$60,$AD,$24,$06,$C9,$02,$D0,$1D,$AD +.db $2F,$06,$2D,$30,$06,$D0,$15,$AD,$0A,$03,$29,$04,$D0,$0E,$AD,$0C +.db $03,$29,$40,$F0,$07,$A9,$13,$85,$1D,$20,$D4,$AF,$60,$AD,$24,$06 +.db $C9,$02,$D0,$23,$AD,$2F,$06,$2D,$30,$06,$D0,$1B,$AD,$0A,$03,$29 +.db $04,$D0,$14,$AD,$0C,$03,$29,$40,$F0,$0D,$A0,$11,$AD,$1D,$06,$F0 +.db $01,$C8,$84,$1D,$20,$D4,$AF,$60,$AD,$24,$06,$C9,$02,$F0,$01,$60 +.db $AE,$31,$06,$BD,$2F,$06,$D0,$F7,$A9,$40,$E0,$00,$D0,$01,$8A,$85 +.db $32,$4C,$E3,$AF,$A5,$19,$29,$C0,$85,$32,$AE,$31,$06,$BD,$2F,$06 +.db $F0,$01,$60,$FE,$2F,$06,$A4,$A3,$A5,$32,$99,$64,$05,$8A,$18,$69 +.db $06,$99,$00,$05,$A5,$32,$29,$40,$D0,$0F,$A5,$0F,$18,$69,$00,$99 +.db $14,$05,$A5,$0E,$69,$00,$4C,$15,$B0,$A5,$0F,$38,$E9,$08,$99,$14 +.db $05,$A5,$0E,$E9,$00,$99,$28,$05,$A5,$10,$99,$50,$05,$A9,$EC,$30 +.db $1B,$18,$65,$11,$99,$3C,$05,$B0,$04,$C9,$F0,$90,$26,$18,$69,$10 +.db $99,$3C,$05,$B9,$50,$05,$18,$69,$01,$4C,$50,$B0,$18,$65,$11,$99 +.db $3C,$05,$B0,$0F,$38,$E9,$10,$99,$3C,$05,$B9,$50,$05,$38,$E9,$01 +.db $99,$50,$05,$A9,$00,$99,$8C,$05,$99,$78,$05,$99,$69,$06,$E6,$A3 +.db $AD,$31,$06,$49,$01,$8D,$31,$06,$60,$A5,$19,$29,$04,$D0,$0A,$A2 +.db $0B,$A5,$15,$D0,$02,$A2,$01,$86,$1D,$20,$43,$B3,$20,$A6,$B3,$20 +.db $8D,$AF,$60,$AD,$1F,$06,$F0,$15,$C9,$02,$B0,$03,$4C,$5A,$B1,$C9 +.db $06,$B0,$03,$4C,$9E,$B0,$C9,$07,$B0,$07,$4C,$88,$B1,$60,$20,$A5 +.db $B1,$AD,$0C,$03,$29,$40,$F0,$3C,$A9,$00,$8D,$13,$06,$8D,$14,$06 +.db $A0,$0C,$AD,$0A,$03,$29,$04,$F0,$02,$A0,$0E,$AD,$0A,$03,$29,$03 +.db $F0,$02,$A0,$0D,$84,$E1,$AD,$1F,$06,$C9,$07,$90,$0D,$38,$E9,$05 +.db $8D,$1F,$06,$20,$4A,$A1,$A9,$00,$85,$17,$AD,$1F,$06,$C9,$04,$D0 +.db $04,$20,$BD,$B1,$60,$A4,$A3,$99,$00,$05,$A5,$19,$29,$40,$D0,$0F +.db $A5,$0F,$18,$69,$10,$99,$14,$05,$A5,$0E,$69,$00,$4C,$0B,$B1,$A5 +.db $0F,$38,$E9,$20,$99,$14,$05,$A5,$0E,$E9,$00,$99,$28,$05,$A5,$10 +.db $99,$50,$05,$A9,$E4,$30,$1B,$18,$65,$11,$99,$3C,$05,$B0,$04,$C9 +.db $F0,$90,$26,$18,$69,$10,$99,$3C,$05,$B9,$50,$05,$18,$69,$01,$4C +.db $46,$B1,$18,$65,$11,$99,$3C,$05,$B0,$0F,$38,$E9,$10,$99,$3C,$05 +.db $B9,$50,$05,$38,$E9,$01,$99,$50,$05,$A9,$00,$99,$64,$05,$99,$78 +.db $05,$AD,$32,$06,$99,$8C,$05,$E6,$A3,$60,$A5,$E1,$C9,$09,$B0,$27 +.db $AD,$0C,$03,$29,$40,$F0,$20,$A9,$19,$85,$8F,$A0,$09,$AD,$0A,$03 +.db $29,$04,$F0,$02,$A0,$0B,$AD,$0A,$03,$29,$03,$F0,$08,$A0,$09,$A5 +.db $15,$D0,$02,$A0,$0A,$84,$E1,$60,$A0,$0F,$AD,$0A,$03,$29,$04,$F0 +.db $02,$A0,$11,$AD,$0A,$03,$29,$03,$F0,$08,$A0,$0F,$A5,$15,$D0,$02 +.db $A0,$10,$84,$E1,$60,$A2,$1E,$A0,$3B,$20,$87,$B8,$A9,$12,$85,$E1 +.db $AD,$1F,$06,$18,$69,$05,$8D,$1F,$06,$20,$4A,$A1,$60,$A4,$A3,$A9 +.db $08,$99,$00,$05,$A9,$09,$99,$01,$05,$A9,$0A,$99,$02,$05,$A5,$19 +.db $99,$64,$05,$99,$65,$05,$99,$66,$05,$29,$40,$D0,$0F,$A5,$0F,$18 +.db $69,$08,$99,$14,$05,$A5,$0E,$69,$00,$4C,$F8,$B1,$A5,$0F,$38,$E9 +.db $10,$99,$14,$05,$A5,$0E,$E9,$00,$99,$28,$05,$A5,$10,$99,$50,$05 +.db $A9,$E4,$30,$1B,$18,$65,$11,$99,$3C,$05,$B0,$04,$C9,$F0,$90,$26 +.db $18,$69,$10,$99,$3C,$05,$B9,$50,$05,$18,$69,$01,$4C,$33,$B2,$18 +.db $65,$11,$99,$3C,$05,$B0,$0F,$38,$E9,$10,$99,$3C,$05,$B9,$50,$05 +.db $38,$E9,$01,$99,$50,$05,$A9,$00,$99,$8C,$05,$99,$69,$06,$C8,$A5 +.db $19,$29,$40,$D0,$0F,$A5,$0F,$18,$69,$08,$99,$14,$05,$A5,$0E,$69 +.db $00,$4C,$60,$B2,$A5,$0F,$38,$E9,$10,$99,$14,$05,$A5,$0E,$E9,$00 +.db $99,$28,$05,$A5,$10,$99,$50,$05,$A9,$E4,$30,$1B,$18,$65,$11,$99 +.db $3C,$05,$B0,$04,$C9,$F0,$90,$26,$18,$69,$10,$99,$3C,$05,$B9,$50 +.db $05,$18,$69,$01,$4C,$9B,$B2,$18,$65,$11,$99,$3C,$05,$B0,$0F,$38 +.db $E9,$10,$99,$3C,$05,$B9,$50,$05,$38,$E9,$01,$99,$50,$05,$A9,$00 +.db $99,$8C,$05,$99,$69,$06,$C8,$A5,$19,$29,$40,$D0,$0F,$A5,$0F,$18 +.db $69,$08,$99,$14,$05,$A5,$0E,$69,$00,$4C,$C8,$B2,$A5,$0F,$38,$E9 +.db $10,$99,$14,$05,$A5,$0E,$E9,$00,$99,$28,$05,$A5,$10,$99,$50,$05 +.db $A9,$E4,$30,$1B,$18,$65,$11,$99,$3C,$05,$B0,$04,$C9,$F0,$90,$26 +.db $18,$69,$10,$99,$3C,$05,$B9,$50,$05,$18,$69,$01,$4C,$03,$B3,$18 +.db $65,$11,$99,$3C,$05,$B0,$0F,$38,$E9,$10,$99,$3C,$05,$B9,$50,$05 +.db $38,$E9,$01,$99,$50,$05,$A9,$00,$99,$8C,$05,$99,$69,$06,$C8,$84 +.db $A3,$A9,$1B,$85,$8F,$60,$AD,$0A,$03,$29,$01,$F0,$07,$A5,$19,$29 +.db $BE,$4C,$2F,$B3,$AD,$0A,$03,$29,$02,$F0,$17,$A5,$19,$09,$41,$85 +.db $19,$A5,$14,$C9,$10,$B0,$07,$A5,$14,$18,$69,$04,$85,$14,$A9,$01 +.db $85,$1D,$60,$AD,$0A,$03,$29,$01,$F0,$07,$A5,$19,$29,$BE,$4C,$5C +.db $B3,$AD,$0A,$03,$29,$02,$F0,$13,$A5,$19,$09,$41,$85,$19,$A5,$14 +.db $C9,$10,$B0,$07,$A5,$14,$18,$69,$04,$85,$14,$60,$A5,$19,$85,$26 +.db $AD,$0A,$03,$29,$01,$F0,$07,$A5,$19,$29,$BE,$4C,$8D,$B3,$AD,$0A +.db $03,$29,$02,$D0,$04,$A9,$01,$D0,$13,$A5,$19,$09,$41,$85,$19,$45 +.db $26,$29,$40,$F0,$05,$A9,$00,$85,$14,$60,$A9,$03,$18,$65,$14,$C9 +.db $20,$B0,$02,$85,$14,$60,$AD,$1D,$06,$F0,$42,$AD,$36,$06,$F0,$19 +.db $A9,$20,$85,$15,$AD,$0A,$03,$29,$04,$F0,$07,$A5,$19,$29,$FB,$85 +.db $19,$60,$A5,$19,$09,$04,$85,$19,$60,$AD,$0C,$03,$29,$80,$F0,$1C +.db $AD,$0A,$03,$29,$08,$F0,$03,$20,$52,$B4,$A9,$30,$85,$15,$A5,$19 +.db $09,$04,$85,$19,$A9,$04,$85,$8F,$A9,$0C,$85,$1D,$60,$AD,$0C,$03 +.db $29,$80,$F0,$F8,$A5,$15,$D0,$F4,$AD,$36,$06,$D0,$07,$AD,$0A,$03 +.db $29,$08,$D0,$38,$A0,$48,$AD,$0A,$03,$29,$40,$F0,$02,$A0,$58,$AD +.db $0A,$03,$29,$04,$F0,$02,$A0,$28,$84,$15,$A5,$19,$09,$04,$85,$19 +.db $A9,$04,$85,$1D,$A9,$03,$85,$8F,$60,$AD,$1F,$06,$F0,$23,$AD,$0C +.db $03,$29,$80,$F0,$1C,$AD,$0A,$03,$29,$08,$F0,$15,$20,$52,$B4,$A9 +.db $50,$85,$15,$A5,$19,$09,$04,$85,$19,$A9,$05,$85,$1D,$A9,$01,$85 +.db $8F,$60,$AD,$1F,$06,$F0,$62,$A5,$19,$8D,$22,$06,$AD,$1F,$06,$8D +.db $21,$06,$A9,$00,$8D,$1F,$06,$8D,$20,$06,$20,$4A,$A1,$A9,$04,$8D +.db $F1,$05,$A5,$11,$38,$E9,$20,$8D,$F4,$05,$A5,$10,$E9,$00,$8D,$F5 +.db $05,$AD,$22,$06,$29,$40,$D0,$11,$A5,$19,$09,$01,$85,$19,$A5,$0F +.db $38,$E9,$08,$8D,$F2,$05,$4C,$A7,$B4,$A5,$19,$29,$FE,$85,$19,$A5 +.db $0F,$38,$E9,$18,$8D,$F2,$05,$A5,$0E,$E9,$00,$8D,$F3,$05,$A9,$00 +.db $8D,$F6,$05,$A9,$30,$85,$14,$E6,$A3,$60,$A5,$17,$C9,$10,$90,$0F +.db $AD,$0C,$03,$29,$80,$F0,$08,$A9,$60,$85,$15,$A9,$09,$85,$1D,$60 +.db $AD,$1D,$06,$D0,$4D,$AD,$0A,$03,$29,$03,$F0,$47,$AD,$0A,$03,$29 +.db $40,$D0,$1C,$8D,$14,$03,$A5,$14,$C9,$10,$B0,$07,$A5,$14,$18,$69 +.db $04,$85,$14,$60,$AD,$14,$03,$C9,$30,$B0,$04,$EE,$14,$03,$60,$A5 +.db $19,$29,$01,$C5,$4F,$F0,$0A,$85,$4F,$A9,$10,$85,$14,$A9,$00,$85 +.db $1D,$A9,$02,$85,$1D,$A5,$14,$C9,$40,$B0,$07,$A5,$14,$18,$69,$04 +.db $85,$14,$60,$A5,$14,$F0,$FB,$A9,$01,$85,$1D,$60,$AD,$0A,$03,$29 +.db $04,$F0,$04,$A9,$07,$85,$1D,$AD,$0A,$03,$29,$08,$F0,$04,$A9,$08 +.db $85,$1D,$60,$20,$C3,$B5,$A5,$1E,$0A,$A8,$B9,$57,$B5,$85,$32,$B9 +.db $58,$B5,$85,$33,$6C,$32,$00,$59,$AE,$AC,$AE,$CD,$AE,$AC,$AE,$FA +.db $AE,$FA,$AE,$59,$AE,$59,$AE,$59,$AE,$77,$B5,$FA,$AE,$69,$B0,$69 +.db $B0,$CD,$AE,$FA,$AE,$A3,$B5,$EE,$1C,$06,$AD,$1C,$06,$C9,$50,$90 +.db $0A,$A9,$00,$8D,$1C,$06,$A9,$0F,$85,$1D,$60,$A5,$15,$D0,$0A,$A5 +.db $19,$29,$04,$D0,$04,$A9,$01,$85,$1D,$20,$43,$B3,$20,$DD,$B5,$20 +.db $30,$B6,$60,$AD,$1F,$06,$F0,$06,$20,$43,$B3,$4C,$B1,$B5,$20,$E2 +.db $B5,$20,$DD,$B5,$A5,$19,$29,$04,$D0,$08,$A5,$15,$D0,$04,$A9,$01 +.db $85,$1D,$60,$AD,$1F,$06,$D0,$14,$AD,$36,$06,$D0,$0F,$AD,$0C,$03 +.db $29,$40,$F0,$08,$A9,$08,$85,$E1,$A9,$01,$85,$8F,$60,$A9,$30,$85 +.db $14,$60,$AD,$0A,$03,$29,$40,$D0,$05,$A9,$0A,$85,$1D,$60,$A2,$00 +.db $A5,$19,$29,$40,$F0,$01,$E8,$8A,$85,$32,$49,$01,$85,$34,$E6,$32 +.db $E6,$34,$A5,$19,$29,$04,$D0,$12,$AD,$0C,$03,$25,$32,$F0,$0B,$E6 +.db $15,$A9,$50,$C5,$15,$B0,$18,$85,$15,$60,$AD,$0C,$03,$25,$34,$F0 +.db $0E,$A5,$19,$09,$04,$85,$19,$A9,$50,$C5,$15,$B0,$02,$85,$15,$60 +.db $AD,$0A,$03,$29,$40,$F0,$0B,$AD,$0A,$03,$29,$80,$F0,$04,$A9,$40 +.db $85,$15,$60,$AD,$0A,$03,$29,$0F,$D0,$0A,$A9,$00,$85,$15,$85,$14 +.db $A9,$0D,$85,$1D,$20,$86,$B6,$20,$5B,$B6,$60,$AD,$0C,$03,$29,$80 +.db $F0,$23,$AD,$0A,$03,$29,$08,$D0,$1C,$A9,$50,$85,$15,$A5,$19,$09 +.db $04,$85,$19,$A9,$04,$85,$1D,$A9,$03,$85,$8F,$A9,$00,$85,$E1,$8D +.db $DC,$06,$8D,$DD,$06,$60,$AD,$0A,$03,$29,$08,$F0,$11,$AD,$DD,$06 +.db $F0,$06,$A9,$00,$85,$15,$F0,$06,$A5,$19,$09,$04,$D0,$10,$AD,$0A +.db $03,$29,$04,$F0,$11,$A9,$00,$8D,$DD,$06,$A5,$19,$29,$7B,$85,$19 +.db $A9,$10,$85,$15,$D0,$30,$AD,$DE,$06,$D0,$0A,$AD,$DC,$06,$F0,$05 +.db $A9,$00,$85,$14,$60,$AD,$0A,$03,$29,$02,$F0,$06,$A5,$19,$09,$41 +.db $D0,$0B,$AD,$0A,$03,$29,$01,$F0,$0C,$A5,$19,$29,$BE,$85,$19,$A9 +.db $10,$85,$14,$D0,$01,$60,$A9,$0E,$85,$1D,$60,$20,$12,$B8,$20,$3B +.db $B8,$A2,$0D,$A0,$00,$A5,$15,$F0,$02,$A0,$0A,$AD,$0A,$03,$29,$04 +.db $F0,$02,$A0,$07,$A9,$0D,$85,$32,$86,$1D,$A5,$17,$C5,$32,$90,$20 +.db $84,$1D,$A0,$01,$AD,$1F,$06,$C9,$01,$D0,$08,$AC,$23,$06,$A9,$01 +.db $8D,$23,$06,$8C,$1F,$06,$20,$4A,$A1,$A9,$00,$85,$E1,$8D,$2E,$06 +.db $60,$20,$12,$B8,$20,$3B,$B8,$A2,$0E,$A0,$00,$A5,$15,$F0,$02,$A0 +.db $0A,$A9,$0D,$85,$32,$20,$08,$B7,$60,$20,$12,$B8,$20,$3B,$B8,$A2 +.db $06,$A0,$07,$A9,$0D,$85,$32,$20,$08,$B7,$60,$20,$12,$B8,$20,$3B +.db $B8,$A2,$0D,$A0,$00,$AD,$0A,$03,$29,$04,$F0,$02,$A0,$07,$A5,$15 +.db $F0,$02,$A0,$0A,$A9,$03,$85,$32,$20,$08,$B7,$60,$20,$12,$B8,$20 +.db $3B,$B8,$A2,$0E,$A0,$00,$A5,$15,$F0,$02,$A0,$0A,$A9,$04,$85,$32 +.db $20,$08,$B7,$60,$20,$12,$B8,$20,$3B,$B8,$A2,$06,$A0,$07,$A9,$01 +.db $85,$32,$20,$08,$B7,$60,$20,$43,$B3,$20,$3B,$B8,$A2,$0D,$A0,$00 +.db $A5,$15,$F0,$02,$A0,$0A,$A9,$03,$85,$32,$20,$08,$B7,$60,$20,$43 +.db $B3,$20,$3B,$B8,$A2,$0E,$A0,$00,$A5,$15,$F0,$02,$A0,$0A,$A9,$07 +.db $85,$32,$20,$08,$B7,$60,$20,$43,$B3,$20,$3B,$B8,$A2,$06,$A0,$07 +.db $A9,$03,$85,$32,$20,$08,$B7,$60,$20,$43,$B3,$20,$3B,$B8,$A2,$00 +.db $AD,$0A,$03,$29,$03,$F0,$05,$A5,$15,$D0,$01,$E8,$86,$1D,$A2,$00 +.db $A0,$40,$20,$87,$B8,$A9,$06,$8D,$1F,$06,$20,$4A,$A1,$A9,$1A,$85 +.db $8F,$60,$AD,$0A,$03,$29,$01,$F0,$07,$A5,$19,$29,$FE,$4C,$2B,$B8 +.db $AD,$0A,$03,$29,$02,$F0,$13,$A5,$19,$09,$01,$85,$19,$A5,$14,$C9 +.db $10,$B0,$07,$A5,$14,$18,$69,$04,$85,$14,$60,$AD,$0C,$03,$29,$80 +.db $F0,$26,$AD,$1D,$06,$F0,$06,$A2,$04,$A0,$20,$D0,$11,$A2,$03,$A5 +.db $15,$D0,$15,$A0,$60,$AD,$0A,$03,$29,$04,$F0,$02,$A0,$30,$84,$15 +.db $86,$8F,$A5,$19,$09,$04,$85,$19,$60,$EE,$11,$06,$CC,$11,$06,$B0 +.db $0D,$A9,$00,$8D,$11,$06,$EE,$12,$06,$EC,$12,$06,$90,$03,$68,$68 +.db $60,$A9,$00,$8D,$12,$06,$60,$EE,$13,$06,$CC,$13,$06,$B0,$0D,$A9 +.db $00,$8D,$13,$06,$EE,$14,$06,$EC,$14,$06,$90,$03,$68,$68,$60,$A9 +.db $00,$8D,$14,$06,$60,$A5,$15,$D0,$29,$A9,$07,$85,$E1,$A6,$A4,$CA +.db $AD,$22,$06,$85,$19,$AD,$21,$06,$8D,$1F,$06,$A9,$01,$8D,$20,$06 +.db $A9,$07,$85,$1D,$20,$4A,$A1,$A2,$00,$A0,$14,$20,$69,$B8,$A9,$00 +.db $85,$E1,$60,$20,$43,$B3,$20,$E9,$B8,$A9,$05,$85,$1D,$A2,$00,$A0 +.db $14,$20,$69,$B8,$A9,$00,$85,$E1,$60,$A5,$15,$D0,$11,$AD,$0C,$03 +.db $29,$80,$F0,$0A,$A9,$60,$85,$15,$A5,$19,$09,$04,$85,$19,$60,$A5 +.db $14,$C9,$10,$B0,$07,$A5,$06,$29,$01,$4C,$16,$B9,$6A,$6A,$6A,$6A +.db $29,$0F,$A8,$B9,$82,$BA,$85,$28,$A5,$14,$C9,$03,$B0,$04,$A9,$00 +.db $85,$28,$A5,$28,$D0,$0B,$A5,$0A,$85,$0E,$A5,$0B,$85,$0F,$4C,$5A +.db $B9,$A5,$19,$29,$01,$F0,$10,$A5,$0B,$38,$E5,$28,$85,$0F,$A5,$0A +.db $E9,$00,$85,$0E,$4C,$5A,$B9,$A5,$12,$C9,$F0,$B0,$0D,$A5,$0B,$18 +.db $65,$28,$85,$0F,$A5,$0A,$69,$00,$85,$0E,$A5,$15,$4A,$4A,$4A,$4A +.db $AA,$BD,$92,$BA,$85,$2B,$A5,$15,$C9,$04,$B0,$04,$A9,$00,$85,$2B +.db $A5,$2B,$D0,$0B,$A5,$0C,$85,$10,$A5,$0D,$85,$11,$4C,$E7,$B9,$A5 +.db $19,$29,$04,$F0,$21,$A5,$13,$C9,$08,$90,$1B,$A5,$0D,$38,$E5,$2B +.db $85,$11,$A5,$0C,$E9,$00,$85,$10,$A5,$11,$C9,$F0,$90,$05,$38,$E9 +.db $10,$85,$11,$4C,$E7,$B9,$A5,$13,$C9,$E8,$90,$21,$A9,$22,$85,$8E +.db $A9,$00,$8D,$24,$06,$8D,$1F,$06,$AD,$F7,$04,$C9,$03,$F0,$05,$A9 +.db $00,$8D,$20,$06,$A9,$04,$85,$DF,$A9,$02,$85,$E0,$60,$A5,$0D,$18 +.db $65,$2B,$85,$11,$A5,$0C,$69,$00,$85,$10,$A5,$11,$C9,$F0,$90,$07 +.db $18,$69,$10,$85,$11,$E6,$10,$A5,$DF,$C9,$04,$F0,$06,$20,$A2,$BA +.db $20,$F7,$BB,$A5,$0F,$38,$E5,$0B,$85,$28,$A5,$0E,$E5,$0A,$10,$1B +.db $A5,$28,$49,$FF,$38,$69,$00,$C9,$07,$90,$0D,$A5,$0B,$38,$E9,$07 +.db $85,$0F,$A5,$0A,$E9,$00,$85,$0E,$4C,$2E,$BA,$A5,$28,$C9,$07,$90 +.db $0D,$A5,$0B,$18,$69,$07,$85,$0F,$A5,$0A,$69,$00,$85,$0E,$A5,$11 +.db $38,$E5,$0D,$85,$28,$A5,$10,$E5,$0C,$10,$26,$A5,$28,$49,$FF,$38 +.db $69,$00,$C9,$07,$90,$18,$A5,$0D,$38,$E9,$07,$85,$11,$A5,$0C,$E9 +.db $00,$85,$10,$A5,$11,$C9,$F0,$90,$05,$38,$E9,$10,$85,$11,$4C,$81 +.db $BA,$A5,$28,$C9,$07,$90,$1A,$A5,$0D,$18,$69,$07,$85,$11,$A5,$0C +.db $69,$00,$85,$10,$A5,$11,$C9,$F0,$90,$07,$18,$69,$10,$85,$11,$E6 +.db $10,$60,$00,$01,$02,$03,$04,$05,$06,$07,$07,$07,$07,$07,$07,$07 +.db $07,$07,$01,$01,$02,$03,$04,$05,$05,$05,$05,$05,$05,$05,$05,$05 +.db $05,$05,$A6,$1D,$BD,$D6,$BB,$18,$65,$0F,$85,$65,$A5,$0E,$69,$00 +.db $85,$64,$A5,$10,$85,$66,$A5,$11,$85,$67,$A9,$00,$85,$26,$A4,$66 +.db $B9,$FA,$04,$18,$65,$64,$A8,$AD,$F5,$04,$8D,$00,$80,$B1,$9F,$85 +.db $9E,$B1,$8C,$85,$99,$A8,$29,$1F,$09,$80,$85,$33,$A9,$00,$85,$32 +.db $98,$29,$20,$D0,$09,$AD,$F3,$04,$8D,$00,$80,$4C,$F4,$BA,$AD,$F4 +.db $04,$8D,$00,$80,$A4,$65,$A5,$67,$29,$F0,$19,$14,$BD,$A8,$B1,$32 +.db $A8,$AD,$F5,$04,$8D,$00,$80,$B1,$DA,$85,$96,$A9,$27,$8D,$03,$80 +.db $20,$00,$E0,$A9,$3F,$8D,$03,$80,$A5,$96,$C9,$F8,$D0,$06,$A5,$49 +.db $09,$01,$D0,$04,$A5,$49,$29,$FE,$85,$49,$A5,$26,$F0,$03,$4C,$BA +.db $BA,$A6,$1D,$BD,$D6,$BB,$0A,$85,$25,$A5,$65,$38,$E5,$25,$85,$65 +.db $A5,$64,$E9,$00,$85,$64,$A9,$00,$85,$26,$A4,$66,$B9,$FA,$04,$18 +.db $65,$64,$A8,$AD,$F5,$04,$8D,$00,$80,$B1,$9F,$85,$9E,$B1,$8C,$85 +.db $99,$A8,$29,$1F,$09,$80,$85,$33,$A9,$00,$85,$32,$98,$29,$20,$D0 +.db $09,$AD,$F3,$04,$8D,$00,$80,$4C,$80,$BB,$AD,$F4,$04,$8D,$00,$80 +.db $A4,$65,$A5,$67,$29,$F0,$19,$14,$BD,$A8,$B1,$32,$A8,$AD,$F5,$04 +.db $8D,$00,$80,$B1,$DA,$85,$96,$A9,$27,$8D,$03,$80,$20,$00,$E0,$A9 +.db $3F,$8D,$03,$80,$A5,$96,$C9,$F8,$D0,$06,$A5,$49,$09,$02,$D0,$04 +.db $A5,$49,$29,$FD,$85,$49,$A5,$26,$F0,$03,$4C,$46,$BB,$A6,$1D,$A5 +.db $65,$18,$7D,$D6,$BB,$85,$0F,$A5,$64,$69,$00,$85,$0E,$A5,$67,$85 +.db $11,$A5,$66,$85,$10,$60,$05,$05,$05,$05,$05,$05,$05,$05,$00,$05 +.db $05,$05,$05,$08,$05,$05,$05,$05,$05,$05,$05,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$05,$0A,$A0,$10,$AD,$24,$06,$F0,$02,$A0,$18 +.db $84,$2B,$A6,$1D,$BD,$D6,$BB,$18,$65,$0F,$85,$65,$A5,$0E,$69,$00 +.db $85,$64,$A5,$11,$38,$E5,$2B,$85,$67,$B0,$0A,$38,$E9,$10,$85,$67 +.db $A4,$10,$88,$84,$66,$A9,$00,$85,$26,$A4,$66,$B9,$FA,$04,$18,$65 +.db $64,$A8,$AD,$F5,$04,$8D,$00,$80,$B1,$9F,$85,$9E,$B1,$8C,$85,$99 +.db $A8,$29,$1F,$09,$80,$85,$33,$A9,$00,$85,$32,$98,$29,$20,$D0,$09 +.db $AD,$F3,$04,$8D,$00,$80,$4C,$5F,$BC,$AD,$F4,$04,$8D,$00,$80,$A4 +.db $65,$A5,$67,$29,$F0,$19,$14,$BD,$A8,$B1,$32,$A8,$AD,$F5,$04,$8D +.db $00,$80,$B1,$DA,$85,$96,$20,$14,$BE,$A6,$1D,$BD,$D6,$BB,$0A,$85 +.db $25,$A5,$65,$38,$E5,$25,$85,$65,$A5,$64,$E9,$00,$85,$64,$A4,$66 +.db $B9,$FA,$04,$18,$65,$64,$A8,$AD,$F5,$04,$8D,$00,$80,$B1,$9F,$85 +.db $9E,$B1,$8C,$85,$99,$A8,$29,$1F,$09,$80,$85,$33,$A9,$00,$85,$32 +.db $98,$29,$20,$D0,$09,$AD,$F3,$04,$8D,$00,$80,$4C,$C4,$BC,$AD,$F4 +.db $04,$8D,$00,$80,$A4,$65,$A5,$67,$29,$F0,$19,$14,$BD,$A8,$B1,$32 +.db $A8,$AD,$F5,$04,$8D,$00,$80,$B1,$DA,$85,$96,$20,$14,$BE,$20,$38 +.db $BE,$A6,$1D,$A5,$65,$18,$7D,$D6,$BB,$85,$0F,$A5,$64,$69,$00,$85 +.db $0E,$A0,$10,$AD,$24,$06,$F0,$02,$A0,$18,$84,$2B,$A5,$67,$18,$65 +.db $2B,$85,$11,$B0,$04,$C9,$F0,$90,$0A,$18,$69,$10,$85,$11,$A4,$66 +.db $C8,$84,$10,$60,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01,$01 +.db $01,$01,$01,$01,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02,$02 +.db $02,$02,$02,$02,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03,$03 +.db $03,$03,$03,$03,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04,$04 +.db $04,$04,$04,$04,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05,$05 +.db $05,$05,$05,$05,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06,$06 +.db $06,$06,$06,$06,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07,$07 +.db $07,$07,$07,$07,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08,$08 +.db $08,$08,$08,$08,$09,$09,$09,$09,$09,$09,$09,$09,$09,$09,$09,$09 +.db $09,$09,$09,$09,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A +.db $0A,$0A,$0A,$0A,$0B,$0B,$0B,$0B,$0B,$0B,$0B,$0B,$0B,$0B,$0B,$0B +.db $0B,$0B,$0B,$0B,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C,$0C +.db $0C,$0C,$0C,$0C,$0D,$0D,$0D,$0D,$0D,$0D,$0D,$0D,$0D,$0D,$0D,$0D +.db $0D,$0D,$0D,$0D,$0E,$0E,$0E,$0E,$0E,$0E,$0E,$0E,$0E,$0E,$0E,$0E +.db $0E,$0E,$0E,$0E,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F +.db $0F,$0F,$0F,$0F,$A5,$96,$C9,$70,$B0,$10,$C9,$60,$90,$0B,$29,$0F +.db $A8,$B9,$6C,$BE,$F0,$03,$4C,$7C,$BE,$60,$A9,$27,$8D,$03,$80,$20 +.db $00,$E0,$A9,$3F,$8D,$03,$80,$60,$AD,$DE,$06,$D0,$17,$AD,$DC,$06 +.db $D0,$12,$A5,$E1,$C9,$03,$D0,$0D,$A0,$00,$A5,$96,$C9,$F8,$F0,$01 +.db $C8,$8C,$DD,$06,$60,$A5,$1E,$C9,$08,$D0,$F9,$A5,$96,$C9,$F8,$D0 +.db $F3,$A9,$00,$85,$15,$85,$14,$A9,$03,$85,$E1,$60,$01,$01,$00,$01 +.db $00,$01,$00,$00,$01,$00,$00,$00,$00,$00,$00,$00,$A5,$19,$29,$04 +.db $F0,$2B,$A9,$04,$49,$FF,$25,$19,$85,$19,$A9,$10,$85,$15,$A5,$E1 +.db $C9,$03,$F0,$04,$A9,$0F,$85,$8F,$A5,$67,$18,$69,$10,$29,$F0,$85 +.db $67,$C9,$F0,$90,$07,$18,$69,$10,$85,$67,$E6,$66,$60,$A9,$00,$85 +.db $14,$A5,$0F,$38,$E5,$65,$A5,$0E,$E5,$64,$30,$10,$A5,$65,$18,$69 +.db $10,$29,$F0,$85,$65,$A5,$64,$69,$00,$85,$64,$60,$A5,$65,$38,$E9 +.db $10,$09,$0F,$85,$65,$A5,$64,$E9,$00,$85,$64,$60,$00,$28,$00,$2C +.db $A9,$00,$8D,$00,$20,$8D,$01,$20,$A5,$25,$0A,$AA,$A0,$00,$AD,$02 +.db $20,$BD,$D9,$BE,$8D,$06,$20,$BD,$D8,$BE,$8D,$06,$20,$B1,$32,$10 +.db $1A,$C9,$FF,$F0,$2A,$29,$7F,$85,$2B,$20,$30,$BF,$B1,$32,$8D,$07 +.db $20,$C6,$2B,$D0,$F4,$20,$30,$BF,$4C,$FD,$BE,$85,$2B,$20,$30,$BF +.db $B1,$32,$8D,$07,$20,$C6,$2B,$D0,$F9,$20,$30,$BF,$4C,$FD,$BE,$60 +.db $E6,$32,$D0,$02,$E6,$33,$60,$A8,$AB,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 diff --git a/stomper/smwstomp.asm b/stomper/smwstomp.asm new file mode 100644 index 0000000..3ea9e18 --- /dev/null +++ b/stomper/smwstomp.asm @@ -0,0 +1,450 @@ +;SMW Stomper +;A Demonstration of Smooth Mid-Screen Vertical Scrolling +;WITHOUT the need for an 8 scanline 'buffer' region +;Quietust, 2002/05/18 +temp: +temp_l: .block 1 +temp_h: .block 1 + +even_frame: .block 1 +shake_y: .block 1 +screen_x: .block 1 +screen_y: .block 1 +stomper_x: .block 1 +stomper_y: .block 1 +irq_num: .block 1 +stomper_mode: .block 1 +stomper_timer: .block 1 +stompershake_y: .block 1 + +.org $8000 +.include "smwstomp/smwnsf.dat" +.org $C000 +stomper: +.db $00,$00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$06,$0B,$0C,$0D +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$0F,$10,$11,$12,$13,$14,$0C,$15,$16,$09,$17,$18,$19,$1A +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$1B,$1C,$1D,$1E,$07,$08,$1F,$06,$0C,$20,$21,$22,$23,$24 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$25,$26,$27,$28,$29,$15,$2A,$2B,$2C,$2D,$15,$2E,$2F,$30 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$06,$0B,$0C,$0D +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$0F,$10,$11,$12,$13,$14,$0C,$15,$16,$09,$17,$18,$19,$1A +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$1B,$1C,$1D,$1E,$07,$08,$1F,$06,$0C,$20,$21,$22,$23,$24 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$25,$26,$27,$28,$29,$15,$2A,$2B,$2C,$2D,$15,$2E,$2F,$30 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$06,$0B,$0C,$0D +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$0F,$10,$11,$12,$13,$14,$0C,$15,$16,$09,$17,$18,$19,$1A +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$1B,$1C,$1D,$1E,$07,$08,$1F,$06,$0C,$20,$21,$22,$23,$24 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$25,$26,$27,$28,$29,$15,$2A,$2B,$2C,$2D,$15,$2E,$2F,$30 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$06,$0B,$0C,$0D +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$0F,$10,$11,$12,$13,$14,$0C,$15,$16,$09,$17,$18,$19,$1A +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$1B,$1C,$1D,$1E,$07,$08,$1F,$06,$0C,$20,$21,$22,$23,$24 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$25,$26,$27,$28,$29,$15,$2A,$2B,$2C,$2D,$15,$2E,$2F,$30 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$06,$0B,$0C,$0D +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$0F,$10,$11,$12,$13,$14,$0C,$15,$16,$09,$17,$18,$19,$1A +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$1B,$1C,$1D,$1E,$07,$08,$1F,$06,$0C,$20,$21,$22,$23,$24 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$25,$26,$27,$28,$29,$15,$2A,$2B,$2C,$2D,$15,$2E,$2F,$30 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$06,$0B,$0C,$0D +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$0F,$10,$11,$12,$13,$14,$0C,$15,$16,$09,$17,$18,$19,$1A +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$1B,$1C,$1D,$1E,$07,$08,$1F,$06,$0C,$20,$21,$22,$23,$24 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$25,$26,$27,$28,$29,$15,$2A,$2B,$2C,$2D,$15,$2E,$2F,$30 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$01,$02,$03,$04,$05,$06,$07,$08,$09,$0A,$06,$0B,$0C,$0D +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$0F,$10,$11,$12,$13,$14,$0C,$15,$16,$09,$17,$18,$19,$1A +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$1B,$1C,$1D,$1E,$07,$08,$1F,$06,$0C,$20,$21,$22,$23,$24 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$00,$25,$26,$27,$28,$29,$15,$2A,$2B,$2C,$2D,$15,$2E,$2F,$30 +.db $0E,$0E,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$31,$32,$33,$34,$35,$36,$37,$38,$39,$3A,$3B,$3C,$3D,$3E,$3F +.db $40,$41,$42,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $00,$43,$44,$45,$46,$47,$44,$45,$46,$47,$44,$45,$46,$47,$44,$45 +.db $46,$47,$48,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.db $44,$00,$00,$00,$00,$00,$00,$00,$44,$00,$00,$00,$00,$00,$00,$00 +.db $44,$00,$00,$00,$00,$00,$00,$00,$44,$00,$00,$00,$00,$00,$00,$00 +.db $44,$00,$00,$00,$00,$00,$00,$00,$44,$00,$00,$00,$00,$00,$00,$00 +.db $44,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +palette: +.db $2D,$0F,$07,$17,$2D,$0F,$27,$17,$2D,$0F,$0C,$1C,$2D,$0F,$0F,$0F + +.fill $E000-*,$00 +reset: SEI + CLD + LDX #$00 + STX $8000 ;reset PRG/CHR swap + STX $2000 + STX $2001 + INX + STX $A000 ;and mirroring +ppuinit: LDA $2002 + BPL ppuinit + DEX + BPL ppuinit + TXS + INX + LDA $2002 + LDY #$20 + STY $2006 + STX $2006 + + LDY #$80 ;init the 'playfield' nametable + LDA #$4E +init_vram_1: STA $2007 ;bricks (top) + DEY + BNE init_vram_1 + + LDY #$10 +init_vram_2: LDA #$4C ;ceiling pattern + STA $2007 + LDA #$4D + STA $2007 + DEY + BNE init_vram_2 + + LDA #$00 + LDY #$80 + LDX #$03 +init_vram_3: STA $2007 ;the room + DEY + BNE init_vram_3 + DEX + BNE init_vram_3 + + LDY #$10 +init_vram_5: LDA #$49 ;floor pattern + STA $2007 + LDA #$4A + STA $2007 + DEY + BNE init_vram_5 + + LDY #$80 + LDA #$4B +init_vram_6: STA $2007 ;more bricks (bottom) + DEY + BNE init_vram_6 + + LDY #$40 + LDA #$AA +init_vram_7: STA $2007 ;and attrib table + DEY + BNE init_vram_7 + + LDA #$28 + STA $2006 + LDA #$00 + STA $2006 + STA temp_l + LDA #$C0 + STA temp_h + LDY #$00 +init_vram_8: LDA (temp),y ;and now, the stomper + STA $2007 + INC temp_l + BNE skip1 + INC temp_h +skip1: LDA temp_h + CMP #$C4 + BNE init_vram_8 + + LDA #$3F + STA $2006 + STX $2006 +init_pal: LDA palette,x ;load palette + STA $2007 + INX + CPX #$10 + BNE init_pal + + LDX #$00 + LDY #$00 +init_chr: STX $8000 ;setup CHR banks + STY $8001 + INX + INY + CPX #$03 + BPL chr_not1k + INY +chr_not1k: CPX #$06 + BNE init_chr + LDY #$00 + STX $8000 + STY $8001 + INX + INY + STX $8000 + STY $8001 + + LDA #$C0 + STA $4017 + JSR sound_reset ;init the sound code (stolen from SMW pirate) + INC $0700 + LDA #$00 + STA shake_y + STA screen_x + STA screen_y + STA stomper_mode + STA stomper_timer + STA stompershake_y + LDA #$FF + STA stomper_y + LDA #$2C + JSR sound_init ;start playing the castle tune + LDA #$88 + STA $2000 + CLI + JMP waitframe + +nmi: CLC + LDA even_frame + ADC #$01 + AND #$03 + STA even_frame + BNE no_scroll + INC screen_x + INC stomper_x +no_scroll: LDA #$88 + STA $2000 + LDA screen_x + STA $2005 + LDA shake_y + STA $2005 + LDA #$88 + STA $2000 + LDA #$1E + STA $2001 ;main screen turn on + LDX stomper_mode ;okay, what are we doing? + BEQ stomp_init + DEX + BEQ stomp_wait + DEX + BEQ stomp_creep + DEX + BEQ stomp_fall + DEX + BEQ stomp_shake + DEX + BEQ stomp_riseshake + DEX + BEQ stomp_rise + JMP drawpipe + +stomp_init: INC stomper_mode ;setup, start a delay timer + LDA #$80 + STA stomper_timer + JMP drawpipe + +stomp_wait: DEC stomper_timer ;wait a bit before it appears + BEQ wait_done + JMP drawpipe + +stomp_creep: DEC stomper_timer ;creep down a little bit... + BEQ creep_alldone + LDA stomper_timer + AND #$03 + CMP #$03 + BNE creep_done + DEC stomper_y +creep_done: JMP drawpipe + +stomp_fall: DEC stomper_timer ;and then fall down at full speed + BEQ fall_alldone + DEC stomper_y + DEC stomper_y + DEC stomper_y + DEC stomper_y + JMP drawpipe +stomp_shake: DEC stomper_timer ;and slam into the floor + BEQ shake_alldone + LDA even_frame + AND #$01 + BEQ shake_up + LDA #$FE + STA shake_y + JMP shake_done +shake_up: LDA #$02 + STA shake_y +shake_done: JMP drawpipe + +stomp_riseshake:DEC stomper_timer ;keep shaking the floor a bit + BEQ riseshake_alldone ;while it's rising + INC stomper_y + INC stomper_y + LDA even_frame + AND #$01 + BEQ riseshake_up + LDA #$FE + STA shake_y + JMP riseshake_done +riseshake_up: LDA #$02 + STA shake_y +riseshake_done: STA stompershake_y + JMP drawpipe + +stomp_rise: DEC stomper_timer ;done shaking, rise back into the ceiling + BEQ rise_alldone + INC stomper_y + INC stomper_y + JMP drawpipe + +wait_done: INC stomper_mode + LDA #$4C + STA stomper_timer + LDA #$A0 + STA stomper_x + LDA #$EF + STA stomper_y + JMP drawpipe +creep_alldone: INC stomper_mode + LDA #$24 + STA stomper_timer + DEC stomper_y + JMP drawpipe +fall_alldone: INC stomper_mode + LDA #$40 + STA stomper_timer + LDA #$1D + JSR sound_init + LDA #$0F + JSR sound_init ;play a suitable sound + JMP drawpipe +shake_alldone: INC stomper_mode + LDA #$20 + STA stomper_timer + JMP drawpipe +riseshake_alldone: + INC stomper_mode + LDA #$32 + STA stomper_timer + LDA #$00 + STA shake_y + STA stompershake_y + JMP drawpipe +rise_alldone: LDA #$00 + STA stomper_mode + JMP drawpipe + +drawpipe: LDA stomper_y ;set up MMC3 interrupts + CMP #$80 + BPL maybe_pipe + CMP #$4F + BMI no_pipe + BPL yes_pipe +maybe_pipe: CMP #$EE + BPL no_pipe +yes_pipe: LDA #$27 + CLC + ADC shake_y + STA $C000 + STA $C001 + STA $E001 + LDA #$FF + STA irq_num +no_pipe: JSR sound_play ;play music + RTI + +irq: INC irq_num ;where is it? + BEQ first_irq ;(yeah, I *should* save regs) + BNE second_irq ;(but this is timing-critical code) + +first_irq: LDA stomper_y ;top of pipe + CLC + ADC stompershake_y + STA screen_y + ASL A + ASL A + AND #$E0 + LDY #$8A + STY $2000 + LDY screen_y + + LDX #$08 +spin1: DEX + BNE spin1 + + LDX stomper_x + STX $2005 + STY $2005 + STX $2005 + STA $2006 + STX $2005 + STY $2005 + LDA stomper_y + CLC + ADC #$12 + CLC + ADC stompershake_y + EOR #$FF + STA $E000 + STA $C000 + STA $C001 + STA $E001 + JMP end_irq + +second_irq: LDX stomper_y ;bottom of pipe + INX + TXA + CLC + ADC #$10 + EOR #$FF + CLC + ADC #$28 + CLC + ADC stompershake_y + STA screen_y + ASL A + ASL A + AND #$E0 + LDY #$88 + STY $2000 + LDY screen_y + + LDX #$05 +spin2: DEX + BNE spin2 + NOP + NOP + + LDX screen_x + STX $2005 + STY $2005 + STX $2005 + STA $2006 + STX $2005 + STY $2005 + STA $E000 +end_irq: RTI + +waitframe: JMP waitframe ;wait forever +sound_reset: .equ $85D6 +sound_init: .equ $8E2F +sound_play: .equ $862A + +.fill $FFFA-*,$00 +.org $FFFA ;interrupt vectors +.dw nmi +.dw reset +.dw irq + +.end diff --git a/stomper/smwstomp.chr b/stomper/smwstomp.chr new file mode 100644 index 0000000..e1bc65e Binary files /dev/null and b/stomper/smwstomp.chr differ diff --git a/stomper/smwstomp.imk b/stomper/smwstomp.imk new file mode 100644 index 0000000..415b79d --- /dev/null +++ b/stomper/smwstomp.imk @@ -0,0 +1,5 @@ +MAKEINES +smwstomp.nes +MAPR 4 +PROG smwstomp.obj +CHAR smwstomp.chr diff --git a/stomper/smwstomp.nes b/stomper/smwstomp.nes new file mode 100644 index 0000000..59610e0 Binary files /dev/null and b/stomper/smwstomp.nes differ diff --git a/stomper/smwstomp.umk b/stomper/smwstomp.umk new file mode 100644 index 0000000..5258e9e --- /dev/null +++ b/stomper/smwstomp.umk @@ -0,0 +1,7 @@ +MAKEUNIF +smwstomp.unif +MAPR NES-TLROM +NAME Super Mario World 'Stomper' demo +TVCI 0 +PRG0 smwstomp.obj +CHR0 smwstomp.chr diff --git a/stomper/smwstomp.unif b/stomper/smwstomp.unif new file mode 100644 index 0000000..5e2b390 Binary files /dev/null and b/stomper/smwstomp.unif differ diff --git a/stress/FIREWAVE.ASM b/stress/FIREWAVE.ASM new file mode 100644 index 0000000..25c311f --- /dev/null +++ b/stress/FIREWAVE.ASM @@ -0,0 +1,64 @@ + dc.b 2 + dc.b 1 + dc.b 2 + dc.b 3 + dc.b 3 + dc.b 4 + dc.b 4 + dc.b 3 + dc.b 3 + dc.b 2 + dc.b 2 + dc.b 1 + dc.b 1 + dc.b 1 + dc.b 2 + dc.b 2 + dc.b 2 + dc.b 2 + dc.b 1 + dc.b 1 + dc.b 1 + dc.b 1 + dc.b 2 + dc.b 3 + dc.b 4 + dc.b 5 + dc.b 5 + dc.b 5 + dc.b 5 + dc.b 4 + dc.b 3 + dc.b 2 + dc.b 1 + dc.b 1 + dc.b 0 + dc.b 1 + dc.b 1 + dc.b 1 + dc.b 1 + dc.b 1 + dc.b 1 + dc.b 1 + dc.b 1 + dc.b 2 + dc.b 3 + dc.b 4 + dc.b 5 + dc.b 5 + dc.b 6 + dc.b 5 + dc.b 5 + dc.b 3 + dc.b 2 + dc.b 1 + dc.b 0 + dc.b 0 + dc.b 1 + dc.b 2 + dc.b 2 + dc.b 1 + dc.b 0 + dc.b 0 + dc.b 1 + dc.b 2 diff --git a/stress/Header_Mapper0 b/stress/Header_Mapper0 new file mode 100644 index 0000000..57c2980 Binary files /dev/null and b/stress/Header_Mapper0 differ diff --git a/stress/Make_NEStress b/stress/Make_NEStress new file mode 100644 index 0000000..2e35572 --- /dev/null +++ b/stress/Make_NEStress @@ -0,0 +1,2 @@ +DAsm NEStress.asm -f3 -v3 +Join Header_Mapper0 a.out Tanks.CHR AS NEStress.NES diff --git a/stress/NEStress.NES b/stress/NEStress.NES new file mode 100644 index 0000000..db668a6 Binary files /dev/null and b/stress/NEStress.NES differ diff --git a/stress/NEStress.asm b/stress/NEStress.asm new file mode 100644 index 0000000..6893e5c --- /dev/null +++ b/stress/NEStress.asm @@ -0,0 +1,4861 @@ +;NEStress program +;------------------- +;Binary created using DAsm 2.12 running on WinUAE. + + PROCESSOR 6502 +OVERFLOWF EQU #$0006 ;To check the V-flag +TMPCOUNT EQU #$0007 ;Temporary Counter +READY EQU #$000E ;Is the game ready for the player's input? +CONTROLLER1 EQU #$0010 ;Controller 1 data +CONTROLLER2 EQU #$0011 ;Controller 2 data +CONTROLLER3 EQU #$0012 ;Controller 3 data +CONTROLLER4 EQU #$0013 ;Controller 4 data +CONTROLER1X EQU #$0014 ;Controller 1 Xdata +CONTROLER2X EQU #$0015 ;Controller 2 Xdata +P1RANDBASE EQU #$0018 ;Random number storage for player 1's position +P2RANDBASE EQU #$0019 ;Random number storage for player 2's position +FADESTATUS EQU #$001B ;Controls fading +;----------------------- +PALETTEPTR EQU #$24 ;Pointer to Current Palette. +SCORE EQU #$30 ;Score on the test. +SCORE_T EQU #$32 ;Temp reg for score. +;----------------------- +VBWAIT EQU #$0050 ;Delays updating of game data +VBDELAY EQU #$0051 ;Timing for fades... +VBPASS EQU #$0052 ;Flag that VB has passed. +VBODD EQU #$0053 ;Flag for even/odd VBs. +PPU2000 EQU #$0058 ;$2000 of PPU + +;------------------------------------------------------- +GAMESCREEN EQU #$0059 ;ID for which game screen is running... + ;.. sometimes also for what CHR-ROM in use... + ;0 = Tanks Screen + ;1 = Planets Screen + ;2 = Title Screen + ;3 = / + ;4 = Controls Screen + ;5 = Credits Screen + ;6 = Victory Screen +;--------------------------------------- +PLANETCHANGE EQU #$005A ;Flag to change planets on selection screen. + +IOREADS EQU #$0500 ;Storage of reads from $4016, 48 bytes. + +TEMPPAL EQU #$0600 ;Temporary storage for palette. +SOFTTERRAIN EQU #$06BF ;$700-64-1 + +;---------------------- +;Sprite-RAM +;---------------------- +BALLCHR EQU #$0701 ;Ball's Sprite +P1VHIY EQU #$0711 ;Sprites for Y,X Initial Velocity +P1VLOY EQU #$0715 +P1VHIX EQU #$0719 +P1VLOX EQU #$071D +P2VHIY EQU #$0721 ;Sprites for Y,X Initial Velocity +P2VLOY EQU #$0725 +P2VHIX EQU #$0729 +P2VLOX EQU #$072D +P1SCRSCORE EQU #$0731 ;Score on-screen +P2SCRSCORE EQU #$0735 +P1CHRPOSY EQU #$0750 ;Sprites for tank's position +P1CHRPOSX EQU #$0753 +P2CHRPOSY EQU #$0758 +P2CHRPOSX EQU #$075B +EXPLSPR1 EQU #$0741 ;Sprites for explosions +EXPLSPR2 EQU #$0745 ;" " +EXPLSPR3 EQU #$0749 ;" " +EXPLSPR4 EQU #$074D ;" " + + + ORG $8000 ;32Kb PRG-ROM, 8Kb CHR-ROM + + dc.b "FluBBa YaDa YaDa" + + +Reset_Routine SUBROUTINE + cld ;Clear decimal flag + sei ;Disable interrupts +;---------------------------------------------------------- + lda $2002 + sta P1RANDBASE ;Randomize Stuff??? + lda $4016 + sta P2RANDBASE + + +;---------------------------------------------------------- + +.WaitV01 lda $2002 + bpl .WaitV01 ;Wait for vertical blanking interval + ldx #$00 + stx $2000 + stx PPU2000 + stx $2001 ;Screen display off, amongst other things + dex + txs ;Top of stack at $1FF + +;Clear (most of) the NES' WRAM. + ldy #$07 ;To clear 8 x $100 bytes, from $0000 to $07FF. + sty $01 ;Store count value in $01 + ldy #$00 + sty $00 + lda #$00 +.Clear + sta ($00),y ;Clear $100 bytes + dey + bne .Clear + + dec $01 ;Decrement "banks" left counter + bpl .Clear ;Do next if >= 0 + + jsr WipePPU + lda #$00 ; What is filled in the SPRRAM + jsr ClearSPRRAM + +;------------------------------------------------------------------- +;******************************************************************* +;------------------------------------------------------------------- +;Title Screen +.TitleMain + lda #$02 + sta GAMESCREEN ;Set CHR-ROM for mapper 3?? + +TITLESCROLL EQU $0000 +TITLESCROLL1 EQU $0001 +TITLESCROLL2 EQU $0002 +TITLESELECT EQU $0003 +BUTTONDOWN EQU $0006 + + lda #$0D + jsr SetPalette + + lda #<.TitleMap + sta $00 + lda #>.TitleMap + sta $01 + lda #$00 ;Screen 0 + jsr PrintFullScreen + + lda #$00 + sta $2003 + lda #$4E ;Copy sprite to SPR-RAM? + sta $0700 + lda #$01 + sta $0701 + lda #$00 + sta $0702 + lda #$66 + sta $0703 + + lda .TitleCursorPos + sta $0704 + lda #$02 + sta $0705 + lda #$01 + sta $0706 + lda #$40 + sta $0707 + + lda #$00 + sta $2005 + sta $2005 + + sta TITLESCROLL + sta TITLESCROLL1 + sta TITLESCROLL2 + sta VBPASS + sta TITLESELECT + sta BUTTONDOWN + +;************************************ + + +;Enable vblank interrupts, etc. + lda #%10001000 + sta $2000 + lda #%00011110 ;Screen on, sprites on, show leftmost 8 pixels, colour + sta $2001 + +.TitleLoop + lda VBPASS + beq .TitleLoop ;Wait for VBlank to pass... + +.TitleLoop0 + lda $2002 + bmi .TitleLoop0 ;Yet again.... + +.TitleLoop0A + lda $2002 + and #$40 + bne .TitleLoop0A ;Wait for sprite 0 clear... + + lda #$00 + sta VBPASS + ldx TITLESCROLL + + + + +.TitleLoop1 + lda $2002 + and #%00010000 + bne .TitleLoop1 + + lda CONTROLLER1 ;Check for controller press + and #%00010000 + beq .TitleLoop0Z + jmp .ExitTitle +.TitleLoop0Z + + inc TITLESCROLL2 + lda TITLESCROLL2 + cmp #$02 + bne .NoUpdateTitleScroll + lda #$00 + sta TITLESCROLL2 + + lda .TitleSINTAB,X ;Set up wavy scrolling. + sta $2005 + lda #$00 + sta $2005 + inx + cpx #$40 + bne .NoUpdateTitleScroll + ldx #$00 +.NoUpdateTitleScroll + +.TitleLoop0B + lda $2002 + and #$40 + beq .TitleLoop1 ;Test for sprite 0 hit yet. + + lda #$00 + sta $2005 + sta $2005 + + lda CONTROLLER1 + and #%00100000 + beq .NoTitleSelect + lda BUTTONDOWN + bne .NoTitleStart + lda #$01 + sta BUTTONDOWN + inc TITLESELECT + ldx TITLESELECT + lda .TitleCursorPos,X + sta $0704 + lda TITLESELECT + cmp #$05 + bne .NoTitleSelectClear + lda #$00 + sta TITLESELECT + lda .TitleCursorPos + sta $0704 +.NoTitleSelectClear + jmp .NoTitleStart +.NoTitleSelect + lda #$00 + sta BUTTONDOWN + lda CONTROLLER1 + and #%11010000 + beq .NoTitleStart + jmp .ExitTitle +.NoTitleStart + jmp .TitleLoop + +.TitleCursorPos dc.b #$60,#$70,#$80,#$90,#$A0 + +.TitleSINTAB + dc.b 1,3,7,13,20,30,40,50,60,70,80,90,95,100,101,102,103,103,104,103,100,90,85,80,74,67,59,49,36 + INCLUDE FireWave.asm + +.TitleMap + dc.b "01234 First line 78901" + dc.b "0 I 1" + dc.b " AAAD AAAD AAAAAAAAAA 1" + dc.b " A D A D A D 1" + dc.b " A D AAAD A D A DA D 1" + dc.b " A DA DA D A D 1" + dc.b " A D A D A DDDD D 1" + dc.b " A DDDA D A D A D 1" + dc.b " A D A D A D A D 1" + dc.b " ADDD AAAD ADDD ADDD 1" + dc.b "0 000000000000000000001" + dc.b "0 1" + dc.b "0 PPU Test (Graphics) 1" + dc.b "0 1" + dc.b "0 pAPU Test (Sound) 1" + dc.b "0 1" + dc.b "0 CPU Test (Code) 1" + dc.b "0 1" + dc.b "0 IO-Ports (Controllers)1" + dc.b "0 1" + dc.b "0 Exit 1" + dc.b "0 1" + dc.b "0 1" + dc.b "0 ---------------------------- 1" + dc.b "0 1" + dc.b "0 1" + dc.b "0 1" + dc.b "0 1" + dc.b "0 1" + dc.b "01234 Last Line 78901" + + + INCLUDE SolarTitleNAM.asm + +.Mess000 + dc.b "--------------------------------",$0A + dc.b " * PPU Test 1: $2005/6/7. * ",$0A + dc.b "--------------------------------",$0A,$0A + + dc.b " PPU Normal write/read:",$0A + dc.b " PPU Write, read, write:",$0A + dc.b " PPU First read then write:",$0A + dc.b " PPU First read correct:",$0A + dc.b " PPU Mixed adr/data write:",$0A + dc.b " PPU $3000 Mirroring:",$0A + dc.b " PPU $3FFF-$0000 boundary:",$0A + dc.b " Mix writes to $2005/6:",$0A + dc.b " PPU Palette write/read:",$0A,$0A + + dc.b " Score: /9",$0A,$0A + + dc.b " Press Start",$0 + + +.Mess010 + dc.b "--------------------------------",$0A + dc.b " * PPU Test 2: Sprites. * ",$0A + dc.b "--------------------------------",$0A,$0A + + dc.b " Sprite Collision:",$0A + dc.b " Sprite Limiter:",$0A + dc.b " CPU write CPU read:",$0A + dc.b " DMA write CPU read:",$0A + dc.b " Dif. DMA write CPU read:",$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A + + dc.b " Score: /13",$0A,$0A + + dc.b " Press Start",$0 + +.Mess020 + dc.b "--------------------------------",$0A + dc.b " * PPU Test 3: VBl ($2002). * ",$0A + dc.b "--------------------------------",$0A,$0A + + dc.b " Bit 7 cleared after read:",$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A + + dc.b " Score: /14",$0A,$0A + + dc.b " Press Start",$0 + + +.Mess040 + dc.b "--------------------------------",$0A + dc.b " * pAPU Test 1: $4000-$4003. * ",$0A + dc.b "--------------------------------",$0A,$0A +.Mess041 + dc.b $0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A + dc.b " Score: /0",$0A,$0A + dc.b " Press Start",0 + + +.Mess080 + dc.b "--------------------------------",$A + dc.b " * CPU Test 1: Aritmethic ops * ",$A + dc.b "--------------------------------",$A,$A +.Mess081 + dc.b " CPU ADC:",$A + dc.b " CPU SBC:",$A + dc.b " CPU CMP:",$A + dc.b " CPU CPX:",$A + dc.b " CPU CPY:",$A + dc.b " CPU INC:",$A + dc.b " CPU DEC:",$A + dc.b " CPU INX:",$A + dc.b " CPU DEX:",$A + dc.b " CPU INY:",$A + dc.b " CPU DEY:",$A + dc.b " CPU ASL:",$A + dc.b " CPU LSR:",$A + dc.b " CPU ROL:",$A + dc.b " CPU ROR:",$A,$A,$A,$A,$A + dc.b " Score: /15",$A,$A + dc.b " Press Start",0 + +.Mess090 + dc.b "--------------------------------",$A + dc.b " * CPU Test 2: Logic ops * ",$A + dc.b "--------------------------------",$A,$A +.Mess091 + dc.b " CPU LDA:",$A + dc.b " CPU LDX:",$A + dc.b " CPU LDY:",$A + dc.b " CPU TAX:",$A + dc.b " CPU TAY:",$A + dc.b " CPU TSX:",$A + dc.b " CPU TXA:",$A + dc.b " CPU TXS:",$A + dc.b " CPU TYA:",$A + dc.b " CPU PLA:",$A + dc.b " CPU AND:",$A + dc.b " CPU EOR:",$A + dc.b " CPU ORA:",$A + dc.b " CPU BIT:",$A,$A,$A,$A,$A,$A + dc.b " Score: /29",$A,$A + dc.b " Press Start",0 + +.Mess0A0 + dc.b "--------------------------------",$0A + dc.b " * CPU Test 3: Addresses * ",$0A + dc.b "--------------------------------",$0A,$0A +.Mess0A1 + dc.b " Adr Immediate:",$0A + dc.b " Adr Zero Page:",$0A + dc.b " Adr Zero Page,X:",$0A + dc.b " Adr Absolute:",$0A + dc.b " Adr Absolute,X:",$0A + dc.b " Adr Absolute,Y:",$0A + dc.b " Adr (Indirect,X):",$0A + dc.b " Adr (Indirect),Y:",$0A + dc.b " JMP:",$0A + dc.b " JMP():",$0A + dc.b " JSR:",$0A + dc.b " RTS:",$0A + dc.b " RTI:",$0A,$0A,$0A,$0A,$0A,$0A,$0A + dc.b " Score: /42",$0A,$0A + dc.b " Press Start",0 + +.Mess0B0 + dc.b "--------------------------------",$0A + dc.b " * CPU Test 4: Misc * ",$0A + dc.b "--------------------------------",$0A,$0A +.Mess0B1 + dc.b " Write / read SR:",$0A + dc.b " BRK flags:",$0A + dc.b " BRK return adr:",$0A + dc.b " Stack adr:",$0A + dc.b " CPU RAM:",$0A + dc.b " CPU RAM Mirror:",$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A + dc.b " Score: /48",$0A,$0A + dc.b " Press Start",0 + + +.Mess0C0 + dc.b "--------------------------------",$0A + dc.b " * IO Test 1: $4016, $4017. * ",$0A + dc.b "--------------------------------",$0A,$0A,$0A +.Mess0C1 + dc.b " $4016",$0A + dc.b " Read 1-8:",$0A + dc.b " Read 9-16:",$0A + dc.b " Read 17-24:",$0A,$0A + dc.b " $4017",$0A + dc.b " Read 1-8:",$0A + dc.b " Read 9-16:",$0A + dc.b " Read 17-24:",$0A,$0A + dc.b $0A,$0A,$0A,$0A,$0A,$0A,$0A,$0A + dc.b " Score: /0",$0A,$0A + dc.b " Press Select & Start",0 + +.Mess0D0 + dc.b " -------------------",$0A + dc.b " | ^ |",$0A + dc.b " | |",$0A + dc.b " | v Se St B A |",$0A + dc.b " -------------------",$0A + dc.b " -------------------",$0A + dc.b " | ^ |",$0A + dc.b " | |",$0A + dc.b " | v Se St B A |",$0A + dc.b " -------------------",$0A + dc.b " -------------------",$0A + dc.b " | ^ |",$0A + dc.b " | |",$0A + dc.b " | v Se St B A |",$0A + dc.b " -------------------",$0A + dc.b " -------------------",$0A + dc.b " | ^ |",$0A + dc.b " | |",$0A + dc.b " | v Se St B A |",$0A + dc.b " -------------------",$0A + dc.b $0A,$0A,$0A + dc.b " Score: /0",$0A,$0A + dc.b " Press Select & Start",0 + + + +DrpcjrMess + dc.b "DrPC",0 +FamiMess + dc.b "Fami",0 +NesMess + dc.b "NES",0 +YesMess + dc.b "Yes",0 +NoMess + dc.b "No",0 +OkMess + dc.b "Ok",0 +ErrMess + dc.b "Error",0 +ErrMessZ + dc.b "Error in Z-Flag",0 +ErrMessN + dc.b "Error in N-Flag",0 +ErrMessV + dc.b "Error in V-Flag",0 +ErrMessC + dc.b "Error in C-Flag",0 +ErrMessD + dc.b "Error in D-Flag",0 +ErrMessI + dc.b "Error I-Flag",0 +ErrMessB + dc.b "Error B-Flag",0 +ErrMessR + dc.b "Error in result",0 +ErrMessW + dc.b "Error in wrap",0 +;---------------------------------------------------------------- +.ExitTitle + lda TITLESELECT + cmp #$00 + beq StartPPUTest + cmp #$01 + beq StartpAPUTest + cmp #$02 + beq StartCPUTest + cmp #$03 + beq StartIOTest + jmp TestEnd + +StartpAPUTest + jmp pAPUTest +StartPPUTest + jmp PPUTest +StartCPUTest + jmp CPUTest +StartIOTest +;---------------------------------------------------------------- +IOTest ;Test IO-ports (controllers) + +.WaitV10 + lda $2002 + bpl .WaitV10 ;Wait for vertical blanking interval + ldx #$00 + stx $2000 + stx $2001 ;Screen display off, amongst other things + + + jsr WipePPU + + lda #<.Mess0C0 + sta $00 + lda #>.Mess0C0 + sta $01 + lda #$20 + sta $02 + lda #$40 + sta $03 + jsr NESWriter0 +;----------------------------- + lda #%10001000 + sta $2000 + lda #%00011110 ;Screen on, sprites on, show leftmost 8 pixels, colour + sta $2001 + + lda #$00 + sta TMPCOUNT + +.IOTest00 + lda VBPASS + beq .IOTest00 ;Wait for VBlank to pass... + + clc + lda #$21 ;Set PPU address for the first 24 values. + sta $2006 + lda TMPCOUNT + asl + asl + cmp #$60 + bmi .SkipRow + adc #$40 +.SkipRow + adc #$0C + sta $2006 + + lda TMPCOUNT + clc + adc #$08 + tax + ldy #8 +.IOLoop1_0 + dex + lda $500,x + lsr + lsr + lsr + lsr + clc + adc #$30 + sta $2007 + lda $500,x + and #$0F + adc #$30 + sta $2007 + dey + bne .IOLoop1_0 + + clc + lda TMPCOUNT + adc #$08 + cmp #$30 + bne .NoOverflow + lda #$00 +.NoOverflow + sta TMPCOUNT + jmp .IOSkipBig + + +.IOSkipBig + lda #$00 + sta VBPASS + sta $2005 + sta $2005 + + lda CONTROLLER1 + eor #$30 ; Check for both [Select] and [Start]. + bne .IOTest00 + +;----------------------------- +IO1_End + ldx #$23 + ldy #$28 + jsr PrintScore + jsr ScrOnWaitKey + +;----------------------------- +.WaitV11 + lda $2002 + bpl .WaitV11 ;Wait for vertical blanking interval + ldx #$00 + stx $2000 + stx $2001 ;Screen display off, amongst other things + + + jsr WipePPU + + lda #<.Mess0D0 + sta $00 + lda #>.Mess0D0 + sta $01 + lda #$20 + sta $02 + lda #$40 + sta $03 + jsr NESWriter0 +;----------------------------- + lda #%10001000 + sta $2000 + lda #%00011110 ;Screen on, sprites on, show leftmost 8 pixels, colour + sta $2001 +.IOTest20 + lda VBPASS + beq .IOTest20 ;Wait for VBlank to pass... + +.IOLoop2 + lda #$20 ;Set PPU address. + sta $2006 + lda #$64 + sta $2006 + ldx #"^" + lda $503 ;Check Cont1 Up + and #1 + beq .NoCont1Up + ldx #1 +.NoCont1Up + stx $2007 + + lda #$20 ;Set PPU address. + sta $2006 + lda #$83 + sta $2006 + ldx #"<" + lda $501 ;Check Cont1 Left + and #1 + beq .NoCont1Left + ldx #1 +.NoCont1Left + stx $2007 + + lda #$20 ;Set PPU address. + sta $2006 + lda #$85 + sta $2006 + ldx #">" + lda $500 ;Check Cont1 Right + and #1 + beq .NoCont1Right + ldx #1 +.NoCont1Right + stx $2007 + + lda #$20 ;Set PPU address. + sta $2006 + lda #$A4 + sta $2006 + ldx #"V" + lda $502 ;Check Cont1 Down + and #1 + beq .NoCont1Down + ldx #1 +.NoCont1Down + stx $2007 + + + lda #$00 + sta VBPASS + sta $2005 + sta $2005 + + lda CONTROLLER1 + eor #$30 ; Check for both [Select] and [Start]. + bne .IOTest20 + + +;----------------------------- + jsr ScrOnWaitKey + +.TestNoKey1 + lda CONTROLLER1 + and #%11010000 + bne .TestNoKey1 +;----------------------------- + + jmp Reset_Routine + +;---------------------------------------------------------------- +;**************************************************************** +;---------------------------------------------------------------- +CPUTest ;Test Aritmethic operations + +.WaitV04 + lda $2002 + bpl .WaitV04 ;Wait for vertical blanking interval + ldx #$00 + stx $2000 + stx PPU2000 + stx $2001 ;Screen display off, amongst other things + + jsr WipePPU + + lda #<.Mess080 + sta $00 + lda #>.Mess080 + sta $01 + lda #$20 + sta $02 + lda #$40 + sta $03 + jsr NESWriter0 + + lda #%00011110 ;Background on, sprites on, show leftmost 8 pixels, colour + sta $2001 +;----------------------------- +.CPUTest00 ;CMP + lda #$21 ;Where the msg prints. + sta $02 + lda #$09 + sta $03 + + lda #$40 ;Setup $6 for V-Flag detection + sta OVERFLOWF + +.CPULoop00 + clv + lda #$82 + cmp #4 + beq .CPUError00Z + bmi .CPUError00N + bvs .CPUError00V + bcc .CPUError00C + + bit OVERFLOWF ;Set V-Flag + lda #$7e + cmp #$7e + bne .CPUError00Z + bmi .CPUError00N + bvc .CPUError00V + bcc .CPUError00C + + cmp #$7f + beq .CPUError00Z + bpl .CPUError00N + bvc .CPUError00V + bcs .CPUError00C + + clv + lda #$ff + cmp #4 + beq .CPUError00Z + bpl .CPUError00N + bvs .CPUError00V + bcc .CPUError00C + +.CPUOk00 + jsr WriteOk + inc SCORE + jmp .CPUTest01 + +.CPUError00Z + jsr WriteErrorZ + jmp .CPUTest01 +.CPUError00N + jsr WriteErrorN + jmp .CPUTest01 +.CPUError00V + jsr WriteErrorV + jmp .CPUTest01 +.CPUError00C + jsr WriteErrorC +;----------------------------- +.CPUTest01 ;CPX + lda #$21 ;Where the msg prints. + sta $02 + lda #$29 + sta $03 +.CPULoop01 + clc + clv + lda #$82 + ldx #$82 + cpx #4 + beq .CPUError01Z + bmi .CPUError01N + bvs .CPUError01V + bcc .CPUError01C + ldx #$7e + sbc #4 + cpx #$7e + bne .CPUError01Z + bmi .CPUError01N + bvc .CPUError01V + bcc .CPUError01C + cpx #$7f + beq .CPUError01Z + bpl .CPUError01N + bvc .CPUError01V + bcs .CPUError01C + ldx #$FF + sbc #$7E + cpx #4 + beq .CPUError01Z + bpl .CPUError01N + bvs .CPUError01V + bcc .CPUError01C + +.CPUOk01 + jsr WriteOk + inc SCORE + jmp .CPUTest02 + +.CPUError01Z + jsr WriteErrorZ + jmp .CPUTest02 +.CPUError01N + jsr WriteErrorN + jmp .CPUTest02 +.CPUError01V + jsr WriteErrorV + jmp .CPUTest02 +.CPUError01C + jsr WriteErrorC +;----------------------------- +.CPUTest02 ;CPY + lda #$21 ;Where the msg prints. + sta $02 + lda #$49 + sta $03 +.CPULoop02 + clc + clv + lda #$82 + ldy #$82 + cpy #4 + beq .CPUError02Z + bmi .CPUError02N + bvs .CPUError02V + bcc .CPUError02C + ldy #$7e + sbc #4 + cpy #$7e + bne .CPUError02Z + bmi .CPUError02N + bvc .CPUError02V + bcc .CPUError02C + cpy #$7f + beq .CPUError02Z + bpl .CPUError02N + bvc .CPUError02V + bcs .CPUError02C + ldy #$FF + sbc #$7E + cpy #4 + beq .CPUError02Z + bpl .CPUError02N + bvs .CPUError02V + bcc .CPUError02C + +.CPUOk02 + jsr WriteOk + inc SCORE + jmp .CPUTest1 + +.CPUError02Z + jsr WriteErrorZ + jmp .CPUTest1 +.CPUError02N + jsr WriteErrorN + jmp .CPUTest1 +.CPUError02V + jsr WriteErrorV + jmp .CPUTest1 +.CPUError02C + jsr WriteErrorC +;----------------------------- +.CPUTest1 ;ADC + lda #$20 ;Where the msg prints. + sta $02 + lda #$C9 + sta $03 +.CPULoop1 + clc + lda #$7a + adc #4 + beq .CPUError1Z + bmi .CPUError1N + bvs .CPUError1V + bcs .CPUError1C + adc #4 + beq .CPUError1Z + bpl .CPUError1N + bvc .CPUError1V + bcs .CPUError1C + adc #$70 + beq .CPUError1Z + bpl .CPUError1N + bvs .CPUError1V + bcs .CPUError1C + adc #$e + bne .CPUError1Z + bmi .CPUError1N + bvs .CPUError1V + bcc .CPUError1C + lda #$94 + adc #$7f + beq .CPUError1Z + bmi .CPUError1N + bvs .CPUError1V + bcc .CPUError1C + adc #$e + cmp #$23 + bne .CPUError1R + + clc + sed + lda #$08 + adc #$08 + cmp #$10 + cld + bne .CPUError1D + +.CPUOk1 + jsr WriteOk + inc SCORE + jmp .CPUTest2 + +.CPUError1Z + jsr WriteErrorZ + jmp .CPUTest2 +.CPUError1N + jsr WriteErrorN + jmp .CPUTest2 +.CPUError1V + jsr WriteErrorV + jmp .CPUTest2 +.CPUError1C + jsr WriteErrorC + jmp .CPUTest2 +.CPUError1R + jsr WriteErrorR + jmp .CPUTest2 +.CPUError1D + jsr WriteErrorD +;----------------------------- +.CPUTest2 ;SBC + lda #$20 ;Where the msg prints. + sta $02 + lda #$E9 + sta $03 +.CPULoop2 + clc + lda #$82 + sbc #$81 + bne .CPUError2R + sec + lda #$3A + sbc #$3A + bne .CPUError2R + + + clc + lda #$82 + sbc #4 + beq .CPUError2Z + bmi .CPUError2N + bvc .CPUError2V + bcc .CPUError2C + sbc #$7d + bne .CPUError2Z + bmi .CPUError2N + bvs .CPUError2V + bcc .CPUError2C + sbc #$7 + beq .CPUError2Z + bpl .CPUError2N + bvs .CPUError2V + bcs .CPUError2C + sbc #$79 + beq .CPUError2Z + bmi .CPUError2N + bvc .CPUError2V + bcc .CPUError2C + cmp #$7F + bne .CPUError2R + + clc + lda #$7E + sbc #$7F + beq .CPUError2Z + bpl .CPUError2N + bvs .CPUError2V + bcs .CPUError2C + + sec + sed + lda #$10 + sbc #$08 + cmp #$08 + cld + bne .CPUError2D + +.CPUOk2 + jsr WriteOk + inc SCORE + jmp .CPUTest3 + +.CPUError2Z + jsr WriteErrorZ + jmp .CPUTest3 +.CPUError2N + jsr WriteErrorN + jmp .CPUTest3 +.CPUError2V + jsr WriteErrorV + jmp .CPUTest3 +.CPUError2C + jsr WriteErrorC + jmp .CPUTest3 +.CPUError2R + jsr WriteErrorR + jmp .CPUTest3 +.CPUError2D + jsr WriteErrorD +;----------------------------- +.CPUTest3 ;INC + lda #$21 ;Where the msg prints. + sta $02 + lda #$69 + sta $03 + +.CPULoop3 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$fe + sta $04 + lda #0 + inc $04 + beq .CPUError3Z + bpl .CPUError3N + bvc .CPUError3V + bcc .CPUError3C + + clc + lda #1 + inc $04 + bne .CPUError3Z + bmi .CPUError3N + bvc .CPUError3V + bcs .CPUError3C + + sec + clv + lda #$7f + sta $04 + lda #0 + inc $04 + beq .CPUError3Z + bpl .CPUError3N + bvs .CPUError3V + bcc .CPUError3C + +.CPUOk3 + jsr WriteOk + inc SCORE + jmp .CPUTest4 + +.CPUError3Z + jsr WriteErrorZ + jmp .CPUTest4 +.CPUError3N + jsr WriteErrorN + jmp .CPUTest4 +.CPUError3V + jsr WriteErrorV + jmp .CPUTest4 +.CPUError3C + jsr WriteErrorC +;----------------------------- +.CPUTest4 ;DEC + lda #$21 ;Where the msg prints. + sta $02 + lda #$89 + sta $03 + +.CPULoop4 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$01 + sta $04 + lda #$fe + dec $04 + bne .CPUError4Z + bmi .CPUError4N + bvc .CPUError4V + bcc .CPUError4C + + clc + dec $04 + beq .CPUError4Z + bpl .CPUError4N + bvc .CPUError4V + bcs .CPUError4C + + sec + clv + lda #$80 + sta $04 + lda #0 + dec $04 + beq .CPUError4Z + bmi .CPUError4N + bvs .CPUError4V + bcc .CPUError4C + +.CPUOk4 + jsr WriteOk + inc SCORE + jmp .CPUTest5 + +.CPUError4Z + jsr WriteErrorZ + jmp .CPUTest5 +.CPUError4N + jsr WriteErrorN + jmp .CPUTest5 +.CPUError4V + jsr WriteErrorV + jmp .CPUTest5 +.CPUError4C + jsr WriteErrorC +;----------------------------- +.CPUTest5 ;INX + lda #$21 ;Where the msg prints. + sta $02 + lda #$A9 + sta $03 + +.CPULoop5 + bit OVERFLOWF ;Set V-Flag + sec + ldx #$fe + lda #0 + inx + beq .CPUError5Z + bpl .CPUError5N + bvc .CPUError5V + bcc .CPUError5C + + clc + lda #1 + inx + bne .CPUError5Z + bmi .CPUError5N + bvc .CPUError5V + bcs .CPUError5C + + sec + clv + ldx #$7f + lda #0 + inx + beq .CPUError5Z + bpl .CPUError5N + bvs .CPUError5V + bcc .CPUError5C + +.CPUOk5 + jsr WriteOk + inc SCORE + jmp .CPUTest6 + +.CPUError5Z + jsr WriteErrorZ + jmp .CPUTest6 +.CPUError5N + jsr WriteErrorN + jmp .CPUTest6 +.CPUError5V + jsr WriteErrorV + jmp .CPUTest6 +.CPUError5C + jsr WriteErrorC +;----------------------------- +.CPUTest6 ;DEX + lda #$21 ;Where the msg prints. + sta $02 + lda #$C9 + sta $03 + +.CPULoop6 + bit OVERFLOWF ;Set V-Flag. + sec + ldx #$01 + lda #$fe + dex + bne .CPUError6Z + bmi .CPUError6N + bvc .CPUError6V + bcc .CPUError6C + + clc + dex + beq .CPUError6Z + bpl .CPUError6N + bvc .CPUError6V + bcs .CPUError6C + + sec + clv + ldx #$80 + lda #0 + dex + beq .CPUError6Z + bmi .CPUError6N + bvs .CPUError6V + bcc .CPUError6C + +.CPUOk6 + jsr WriteOk + inc SCORE + jmp .CPUTest7 + +.CPUError6Z + jsr WriteErrorZ + jmp .CPUTest7 +.CPUError6N + jsr WriteErrorN + jmp .CPUTest7 +.CPUError6V + jsr WriteErrorV + jmp .CPUTest7 +.CPUError6C + jsr WriteErrorC +;----------------------------- +.CPUTest7 ;INY + lda #$21 ;Where the msg prints. + sta $02 + lda #$E9 + sta $03 + +.CPULoop7 + bit OVERFLOWF ;Set V-Flag. + sec + ldy #$fe + lda #0 + iny + beq .CPUError7Z + bpl .CPUError7N + bvc .CPUError7V + bcc .CPUError7C + + clc + lda #1 + iny + bne .CPUError7Z + bmi .CPUError7N + bvc .CPUError7V + bcs .CPUError7C + + sec + clv + ldy #$7f + lda #0 + iny + beq .CPUError7Z + bpl .CPUError7N + bvs .CPUError7V + bcc .CPUError7C + +.CPUOk7 + jsr WriteOk + inc SCORE + jmp .CPUTest8 + +.CPUError7Z + jsr WriteErrorZ + jmp .CPUTest8 +.CPUError7N + jsr WriteErrorN + jmp .CPUTest8 +.CPUError7V + jsr WriteErrorV + jmp .CPUTest8 +.CPUError7C + jsr WriteErrorC +;----------------------------- +.CPUTest8 ;DEY + lda #$22 ;Where the msg prints. + sta $02 + lda #$09 + sta $03 + +.CPULoop8 + bit OVERFLOWF ;Set V-Flag. + sec + ldy #$01 + lda #$fe + dey + bne .CPUError8Z + bmi .CPUError8N + bvc .CPUError8V + bcc .CPUError8C + + clc + dey + beq .CPUError8Z + bpl .CPUError8N + bvc .CPUError8V + bcs .CPUError8C + + sec + clv + ldy #$80 + lda #0 + dey + beq .CPUError8Z + bmi .CPUError8N + bvs .CPUError8V + bcc .CPUError8C + +.CPUOk8 + jsr WriteOk + inc SCORE + jmp .CPUTest9 + +.CPUError8Z + jsr WriteErrorZ + jmp .CPUTest9 +.CPUError8N + jsr WriteErrorN + jmp .CPUTest9 +.CPUError8V + jsr WriteErrorV + jmp .CPUTest9 +.CPUError8C + jsr WriteErrorC +;----------------------------- +.CPUTest9 ;ASL + lda #$22 ;Where the msg prints. + sta $02 + lda #$29 + sta $03 + +.CPULoop9 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$40 + asl + beq .CPUError9Z + bpl .CPUError9N + bvc .CPUError9V + bcs .CPUError9C + + clv + asl + bne .CPUError9Z + bmi .CPUError9N + bvs .CPUError9V + bcc .CPUError9C + +.CPUOk9 + inc SCORE + jsr WriteOk + jmp .CPUTest10 + +.CPUError9Z + jsr WriteErrorZ + jmp .CPUTest10 +.CPUError9N + jsr WriteErrorN + jmp .CPUTest10 +.CPUError9V + jsr WriteErrorV + jmp .CPUTest10 +.CPUError9C + jsr WriteErrorC +;----------------------------- +.CPUTest10 ;LSR + lda #$22 ;Where the msg prints. + sta $02 + lda #$49 + sta $03 + +.CPULoop10 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$02 + lsr + beq .CPUError10Z + bmi .CPUError10N + bvc .CPUError10V + bcs .CPUError10C + + clv + lsr + bne .CPUError10Z + bmi .CPUError10N + bvs .CPUError10V + bcc .CPUError10C + + lda #$C2 + lsr + beq .CPUError10Z + bmi .CPUError10N + bvs .CPUError10V + bcs .CPUError10C + +.CPUOk10 + jsr WriteOk + inc SCORE + jmp .CPUTest11 + +.CPUError10Z + jsr WriteErrorZ + jmp .CPUTest11 +.CPUError10N + jsr WriteErrorN + jmp .CPUTest11 +.CPUError10V + jsr WriteErrorV + jmp .CPUTest11 +.CPUError10C + jsr WriteErrorC +;----------------------------- +.CPUTest11 ;ROL + lda #$22 ;Where the msg prints. + sta $02 + lda #$69 + sta $03 + +.CPULoop11 + bit OVERFLOWF ;Set V-Flag. + sec + lda #0 + rol + beq .CPUError11Z + bmi .CPUError11N + bvc .CPUError11V + bcs .CPUError11C + + lda #$55 + rol + beq .CPUError11Z + bpl .CPUError11N + bvc .CPUError11V + bcs .CPUError11C + + clv + lda #$80 + rol + bne .CPUError11Z + bmi .CPUError11N + bvs .CPUError11V + bcc .CPUError11C + + lda #$C2 + rol + beq .CPUError11Z + bpl .CPUError11N + bvs .CPUError11V + bcc .CPUError11C + +.CPUOk11 + jsr WriteOk + inc SCORE + jmp .CPUTest12 + +.CPUError11Z + jsr WriteErrorZ + jmp .CPUTest12 +.CPUError11N + jsr WriteErrorN + jmp .CPUTest12 +.CPUError11V + jsr WriteErrorV + jmp .CPUTest12 +.CPUError11C + jsr WriteErrorC +;----------------------------- +.CPUTest12 ;ROR + lda #$22 ;Where the msg prints. + sta $02 + lda #$89 + sta $03 + +.CPULoop12 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$0e + ror + beq .CPUError12Z + bpl .CPUError12N + bvc .CPUError12V + bcs .CPUError12C + + ror + beq .CPUError12Z + bmi .CPUError12N + bvc .CPUError12V + bcc .CPUError12C + + clc + clv + lda #$01 + ror + bne .CPUError12Z + bmi .CPUError12N + bvs .CPUError12V + bcc .CPUError12C + + ror + beq .CPUError12Z + bpl .CPUError12N + bvs .CPUError12V + bcs .CPUError12C + +.CPUOk12 + jsr WriteOk + inc SCORE + jmp .CPUTest13 + +.CPUError12Z + jsr WriteErrorZ + jmp .CPUTest13 +.CPUError12N + jsr WriteErrorN + jmp .CPUTest13 +.CPUError12V + jsr WriteErrorV + jmp .CPUTest13 +.CPUError12C + jsr WriteErrorC +;----------------------------- +.CPUTest13 +CPU1_End + ldx #$23 + ldy #$28 + jsr PrintScore + jsr ScrOnWaitKey +;---------------------------------------------------------------- +CPUTest_2 ;Test Logical operations + +.WaitV05 + lda $2002 + bpl .WaitV05 ;Wait for vertical blanking interval + ldx #$00 + stx $2000 + stx $2001 ;Screen display off, amongst other things + + + jsr WipePPU + + lda #<.Mess090 + sta $00 + lda #>.Mess090 + sta $01 + lda #$20 + sta $02 + lda #$40 + sta $03 + jsr NESWriter0 + + lda #%00011110 ;Background on, sprites on, show leftmost 8 pixels, colour + sta $2001 +;----------------------------- +.CPUTest20 ;LDA + lda #$20 ;Where the msg prints. + sta $02 + lda #$C9 + sta $03 + +.CPULoop20 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$0e + beq .CPUError20Z + bmi .CPUError20N + bvc .CPUError20V + bcc .CPUError20C + + lda #0 + bne .CPUError20Z + bmi .CPUError20N + bvc .CPUError20V + bcc .CPUError20C + + clv + clc + lda #$aa + beq .CPUError20Z + bpl .CPUError20N + bvs .CPUError20V + bcs .CPUError20C + + lda #$c8 + beq .CPUError20Z + bpl .CPUError20N + bvs .CPUError20V + bcs .CPUError20C + +.CPUOk20 + jsr WriteOk + inc SCORE + jmp .CPUTest21 + +.CPUError20Z + jsr WriteErrorZ + jmp .CPUTest21 +.CPUError20N + jsr WriteErrorN + jmp .CPUTest21 +.CPUError20V + jsr WriteErrorV + jmp .CPUTest21 +.CPUError20C + jsr WriteErrorC +;----------------------------- +.CPUTest21 ;LDX + lda #$20 ;Where the msg prints. + sta $02 + lda #$E9 + sta $03 + +.CPULoop21 + bit OVERFLOWF ;Set V-Flag. + sec + ldx #$99 + beq .CPUError21Z + bpl .CPUError21N + bvc .CPUError21V + bcc .CPUError21C + + ldx #0 + bne .CPUError21Z + bmi .CPUError21N + bvc .CPUError21V + bcc .CPUError21C + + clv + clc + ldx #$ea + beq .CPUError21Z + bpl .CPUError21N + bvs .CPUError21V + bcs .CPUError21C + + ldx #$45 + beq .CPUError21Z + bmi .CPUError21N + bvs .CPUError21V + bcs .CPUError21C + +.CPUOk21 + jsr WriteOk + inc SCORE + jmp .CPUTest22 + +.CPUError21Z + jsr WriteErrorZ + jmp .CPUTest22 +.CPUError21N + jsr WriteErrorN + jmp .CPUTest22 +.CPUError21V + jsr WriteErrorV + jmp .CPUTest22 +.CPUError21C + jsr WriteErrorC +;----------------------------- +.CPUTest22 ;LDY + lda #$21 ;Where the msg prints. + sta $02 + lda #$09 + sta $03 + +.CPULoop22 + bit OVERFLOWF ;Set V-Flag. + sec + ldy #$b2 + beq .CPUError22Z + bpl .CPUError22N + bvc .CPUError22V + bcc .CPUError22C + + ldy #0 + bne .CPUError22Z + bmi .CPUError22N + bvc .CPUError22V + bcc .CPUError22C + + clv + clc + ldy #$aa + beq .CPUError22Z + bpl .CPUError22N + bvs .CPUError22V + bcs .CPUError22C + + ldy #$38 + beq .CPUError22Z + bmi .CPUError22N + bvs .CPUError22V + bcs .CPUError22C + +.CPUOk22 + jsr WriteOk + inc SCORE + jmp .CPUTest23 + +.CPUError22Z + jsr WriteErrorZ + jmp .CPUTest23 +.CPUError22N + jsr WriteErrorN + jmp .CPUTest23 +.CPUError22V + jsr WriteErrorV + jmp .CPUTest23 +.CPUError22C + jsr WriteErrorC +;----------------------------- +.CPUTest23 ;TAX + lda #$21 ;Where the msg prints. + sta $02 + lda #$29 + sta $03 + +.CPULoop23 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$0e + ldx #$90 + tax + beq .CPUError23Z + bmi .CPUError23N + bvc .CPUError23V + bcc .CPUError23C + + lda #0 + ldx #$ff + tax + bne .CPUError23Z + bmi .CPUError23N + bvc .CPUError23V + bcc .CPUError23C + + clv + clc + lda #$aa + ldx #0 + tax + beq .CPUError23Z + bpl .CPUError23N + bvs .CPUError23V + bcs .CPUError23C + + lda #$c8 + ldx #$32 + tax + beq .CPUError23Z + bpl .CPUError23N + bvs .CPUError23V + bcs .CPUError23C + +.CPUOk23 + jsr WriteOk + inc SCORE + jmp .CPUTest24 + +.CPUError23Z + jsr WriteErrorZ + jmp .CPUTest24 +.CPUError23N + jsr WriteErrorN + jmp .CPUTest24 +.CPUError23V + jsr WriteErrorV + jmp .CPUTest24 +.CPUError23C + jsr WriteErrorC +;----------------------------- +.CPUTest24 ;TAY + lda #$21 ;Where the msg prints. + sta $02 + lda #$49 + sta $03 + +.CPULoop24 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$0e + ldy #$90 + tay + beq .CPUError24Z + bmi .CPUError24N + bvc .CPUError24V + bcc .CPUError24C + + lda #0 + ldy #$ff + tay + bne .CPUError24Z + bmi .CPUError24N + bvc .CPUError24V + bcc .CPUError24C + + clv + clc + lda #$aa + ldy #0 + tay + beq .CPUError24Z + bpl .CPUError24N + bvs .CPUError24V + bcs .CPUError24C + + lda #$c8 + ldy #$32 + tay + beq .CPUError24Z + bpl .CPUError24N + bvs .CPUError24V + bcs .CPUError24C + +.CPUOk24 + jsr WriteOk + inc SCORE + jmp .CPUTest25 + +.CPUError24Z + jsr WriteErrorZ + jmp .CPUTest25 +.CPUError24N + jsr WriteErrorN + jmp .CPUTest25 +.CPUError24V + jsr WriteErrorV + jmp .CPUTest25 +.CPUError24C + jsr WriteErrorC +;----------------------------- +.CPUTest25 ;TSX + lda #$21 ;Where the msg prints. + sta $02 + lda #$69 + sta $03 + + tsx + txa + tay ; Save old S +.CPULoop25 + bit OVERFLOWF ;Set V-Flag. + sec + ldx #$25 + txs + ldx #$93 + tsx + beq .CPUError25Z + bmi .CPUError25N + bvc .CPUError25V + bcc .CPUError25C + + ldx #0 + txs + ldx #$ff + tsx + bne .CPUError25Z + bmi .CPUError25N + bvc .CPUError25V + bcc .CPUError25C + + clv + clc + ldx #$fa + txs + ldx #0 + tsx + beq .CPUError25Z + bpl .CPUError25N + bvs .CPUError25V + bcs .CPUError25C + + ldx #$b9 + txs + ldx #$7f + tsx + beq .CPUError25Z + bpl .CPUError25N + bvs .CPUError25V + bcs .CPUError25C + +.CPUOk25 + tya + tax + txs ; Get old S + jsr WriteOk + inc SCORE + jmp .CPUTest26 + +.CPUError25Z + tya + tax + txs ; Get old S + jsr WriteErrorZ + jmp .CPUTest26 +.CPUError25N + tya + tax + txs ; Get old S + jsr WriteErrorN + jmp .CPUTest26 +.CPUError25V + tya + tax + txs ; Get old S + jsr WriteErrorV + jmp .CPUTest26 +.CPUError25C + tya + tax + txs ; Get old S + jsr WriteErrorC +;----------------------------- +.CPUTest26 ;TXA + lda #$21 ;Where the msg prints. + sta $02 + lda #$89 + sta $03 + +.CPULoop26 + bit OVERFLOWF ;Set V-Flag. + sec + ldx #$0e + lda #$90 + txa + beq .CPUError26Z + bmi .CPUError26N + bvc .CPUError26V + bcc .CPUError26C + + ldx #0 + lda #$ff + txa + bne .CPUError26Z + bmi .CPUError26N + bvc .CPUError26V + bcc .CPUError26C + + clv + clc + ldx #$aa + lda #0 + txa + beq .CPUError26Z + bpl .CPUError26N + bvs .CPUError26V + bcs .CPUError26C + + ldx #$c8 + lda #$32 + txa + beq .CPUError26Z + bpl .CPUError26N + bvs .CPUError26V + bcs .CPUError26C + +.CPUOk26 + jsr WriteOk + inc SCORE + jmp .CPUTest27 + +.CPUError26Z + jsr WriteErrorZ + jmp .CPUTest27 +.CPUError26N + jsr WriteErrorN + jmp .CPUTest27 +.CPUError26V + jsr WriteErrorV + jmp .CPUTest27 +.CPUError26C + jsr WriteErrorC +;----------------------------- +.CPUTest27 ;TXS + lda #$21 ;Where the msg prints. + sta $02 + lda #$A9 + sta $03 + + tsx + txa + tay ; Save old S +.CPULoop27 + bit OVERFLOWF ;Set V-Flag. + sec + ldx #$90 + lda #$0e + txs + beq .CPUError27Z + bmi .CPUError27N + bvc .CPUError27V + bcc .CPUError27C + + ldx #$ff + lda #0 + txs + bne .CPUError27Z + bmi .CPUError27N + bvc .CPUError27V + bcc .CPUError27C + + clv + clc + ldx #0 + lda #$aa + txs + beq .CPUError27Z + bpl .CPUError27N + bvs .CPUError27V + bcs .CPUError27C + + ldx #$32 + lda #$c8 + txs + beq .CPUError27Z + bpl .CPUError27N + bvs .CPUError27V + bcs .CPUError27C + +.CPUOk27 + tya + tax + txs ; Get old S + jsr WriteOk + inc SCORE + jmp .CPUTest28 + +.CPUError27Z + tya + tax + txs ; Get old S + jsr WriteErrorZ + jmp .CPUTest28 +.CPUError27N + tya + tax + txs ; Get old S + jsr WriteErrorN + jmp .CPUTest28 +.CPUError27V + tya + tax + txs ; Get old S + jsr WriteErrorV + jmp .CPUTest28 +.CPUError27C + tya + tax + txs ; Get old S + jsr WriteErrorC +;----------------------------- +.CPUTest28 ;TYA + lda #$21 ;Where the msg prints. + sta $02 + lda #$C9 + sta $03 + +.CPULoop28 + bit OVERFLOWF ;Set V-Flag. + sec + ldy #$0e + lda #$90 + tya + beq .CPUError28Z + bmi .CPUError28N + bvc .CPUError28V + bcc .CPUError28C + + ldy #0 + lda #$ff + tya + bne .CPUError28Z + bmi .CPUError28N + bvc .CPUError28V + bcc .CPUError28C + + clv + clc + ldy #$aa + lda #0 + tya + beq .CPUError28Z + bpl .CPUError28N + bvs .CPUError28V + bcs .CPUError28C + + ldy #$c8 + lda #$32 + tya + beq .CPUError28Z + bpl .CPUError28N + bvs .CPUError28V + bcs .CPUError28C + +.CPUOk28 + jsr WriteOk + inc SCORE + jmp .CPUTest29 + +.CPUError28Z + jsr WriteErrorZ + jmp .CPUTest29 +.CPUError28N + jsr WriteErrorN + jmp .CPUTest29 +.CPUError28V + jsr WriteErrorV + jmp .CPUTest29 +.CPUError28C + jsr WriteErrorC +;----------------------------- +.CPUTest29 ;PLA + lda #$21 ;Where the msg prints. + sta $02 + lda #$E9 + sta $03 + +.CPULoop29 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$0e + pha + ldx #$90 + pla + beq .CPUError29Z + bmi .CPUError29N + bvc .CPUError29V + bcc .CPUError29C + + lda #0 + pha + ldx #$ff + pla + bne .CPUError29Z + bmi .CPUError29N + bvc .CPUError29V + bcc .CPUError29C + + clv + clc + lda #$aa + pha + ldx #0 + pla + beq .CPUError29Z + bpl .CPUError29N + bvs .CPUError29V + bcs .CPUError29C + + lda #$c8 + pha + ldx #$32 + pla + beq .CPUError29Z + bpl .CPUError29N + bvs .CPUError29V + bcs .CPUError29C + +.CPUOk29 + jsr WriteOk + inc SCORE + jmp .CPUTest30 + +.CPUError29Z + jsr WriteErrorZ + jmp .CPUTest30 +.CPUError29N + jsr WriteErrorN + jmp .CPUTest30 +.CPUError29V + jsr WriteErrorV + jmp .CPUTest30 +.CPUError29C + jsr WriteErrorC +;----------------------------- +.CPUTest30 ;AND + lda #$22 ;Where the msg prints. + sta $02 + lda #$09 + sta $03 + +.CPULoop30 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$C8 + ldx #$59 + and #$9F + beq .CPUError30Z + bpl .CPUError30N + bvc .CPUError30V + bcc .CPUError30C + + lda #0 + ldx #$FF + and #$C5 + bne .CPUError30Z + bmi .CPUError30N + bvc .CPUError30V + bcc .CPUError30C + + clv + clc + lda #$c8 + ldx #$83 + and #$37 + bne .CPUError30Z + bmi .CPUError30N + bvs .CPUError30V + bcs .CPUError30C + + lda #$AA + ldx #0 + and #$8F + beq .CPUError30Z + bpl .CPUError30N + bvs .CPUError30V + bcs .CPUError30C + + cmp #$8A + bne .CPUError30R + +.CPUOk30 + jsr WriteOk + inc SCORE + jmp .CPUTest31 + +.CPUError30Z + jsr WriteErrorZ + jmp .CPUTest31 +.CPUError30N + jsr WriteErrorN + jmp .CPUTest31 +.CPUError30V + jsr WriteErrorV + jmp .CPUTest31 +.CPUError30C + jsr WriteErrorC + jmp .CPUTest31 +.CPUError30R + jsr WriteErrorR +;----------------------------- +.CPUTest31 ;EOR + lda #$22 ;Where the msg prints. + sta $02 + lda #$29 + sta $03 + +.CPULoop31 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$9e + ldx #$90 + eor #$81 + beq .CPUError31Z + bmi .CPUError31N + bvc .CPUError31V + bcc .CPUError31C + + lda #$01 + ldx #$ff + eor #$01 + bne .CPUError31Z + bmi .CPUError31N + bvc .CPUError31V + bcc .CPUError31C + + clv + clc + lda #$aa + ldx #0 + eor #$71 + beq .CPUError31Z + bpl .CPUError31N + bvs .CPUError31V + bcs .CPUError31C + + lda #$C8 + ldx #$32 + eor #$66 + beq .CPUError31Z + bpl .CPUError31N + bvs .CPUError31V + bcs .CPUError31C + + cmp #$AE + bne .CPUError31R + +.CPUOk31 + jsr WriteOk + inc SCORE + jmp .CPUTest32 + +.CPUError31Z + jsr WriteErrorZ + jmp .CPUTest32 +.CPUError31N + jsr WriteErrorN + jmp .CPUTest32 +.CPUError31V + jsr WriteErrorV + jmp .CPUTest32 +.CPUError31C + jsr WriteErrorC + jmp .CPUTest32 +.CPUError31R + jsr WriteErrorR +;----------------------------- +.CPUTest32 ;ORA + lda #$22 ;Where the msg prints. + sta $02 + lda #$49 + sta $03 + +.CPULoop32 + bit OVERFLOWF ;Set V-Flag. + sec + lda #$0e + ldx #$90 + ora #$20 + beq .CPUError32Z + bmi .CPUError32N + bvc .CPUError32V + bcc .CPUError32C + + lda #$00 + ldx #$ff + ora #$00 + bne .CPUError32Z + bmi .CPUError32N + bvc .CPUError32V + bcc .CPUError32C + + clv + clc + lda #$0a + ldx #0 + ora #$a0 + beq .CPUError32Z + bpl .CPUError32N + bvs .CPUError32V + bcs .CPUError32C + + lda #$08 + ldx #$32 + ora #$c0 + beq .CPUError32Z + bpl .CPUError32N + bvs .CPUError32V + bcs .CPUError32C + + cmp #$C8 + bne .CPUError32R + +.CPUOk32 + jsr WriteOk + inc SCORE + jmp .CPUTest33 + +.CPUError32Z + jsr WriteErrorZ + jmp .CPUTest33 +.CPUError32N + jsr WriteErrorN + jmp .CPUTest33 +.CPUError32V + jsr WriteErrorV + jmp .CPUTest33 +.CPUError32C + jsr WriteErrorC + jmp .CPUTest33 +.CPUError32R + jsr WriteErrorR +;----------------------------- +.CPUTest33 ;BIT + lda #$22 ;Where the msg prints. + sta $02 + lda #$69 + sta $03 + +.CPULoop33 + sec + lda #$5E + sta $05 + lda #$97 + bit $05 + beq .CPUError33Z + bmi .CPUError33N + bvc .CPUError33V + bcc .CPUError33C + + lda #$05 + sta $05 + lda #$F0 + bit $05 + bne .CPUError33Z + bmi .CPUError33N + bvs .CPUError33V + bcc .CPUError33C + + clc + lda #$C5 + sta $05 + lda #$3A + bit $05 + bne .CPUError33Z + bpl .CPUError33N + bvc .CPUError33V + bcs .CPUError33C + + lda #$99 + sta $05 + lda #$6D + bit $05 + beq .CPUError33Z + bpl .CPUError33N + bvs .CPUError33V + bcs .CPUError33C + +.CPUOk33 + jsr WriteOk + inc SCORE + jmp .CPUTest34 + +.CPUError33Z + jsr WriteErrorZ + jmp .CPUTest34 +.CPUError33N + jsr WriteErrorN + jmp .CPUTest34 +.CPUError33V + jsr WriteErrorV + jmp .CPUTest34 +.CPUError33C + jsr WriteErrorC +;----------------------------- +.CPUTest34 +CPU2_End + ldx #$23 + ldy #$28 + jsr PrintScore + jsr ScrOnWaitKey + +;---------------------------------------------------------------- +CPUTest4 ;Test Address CPU operations + +.WaitVBl + lda $2002 + bpl .WaitVBl ;Wait for vertical blanking interval + ldx #$00 + stx $2000 + stx $2001 ;Screen display off, amongst other things + + + jsr WipePPU + + lda #<.Mess0A0 + sta $00 + lda #>.Mess0A0 + sta $01 + lda #$20 + sta $02 + lda #$40 + sta $03 + jsr NESWriter0 + + lda #%00011110 ;Background on, sprites on, show leftmost 8 pixels, colour + sta $2001 +;----------------------------- +.CPUTest40 ;Adr Imediate + lda #$20 ;Where the msg prints. + sta $02 + lda #$D2 + sta $03 + + + + lda #$5E + beq .CPUError40 ;Stupid isn't it? ;-) + bmi .CPUError40 + cmp #$23 ;How do you do a test for the imediate addressing? + beq .CPUError40 + cmp #$5E + bne .CPUError40 + lda #$82 + beq .CPUError40 + bpl .CPUError40 + +.CPUOk40 + jsr WriteOk + inc SCORE + jmp .CPUTest41 + +.CPUError40 + jsr WriteError +;----------------------------- +.CPUTest41 ;Adr Zero page + lda #$20 ;Where the msg prints. + sta $02 + lda #$F2 + sta $03 + + + + lda #0 + sta $05 + lda #$93 + sta $06 + lda #$45 + sta $07 + lda $05 + bne .CPUError41 ;Stupid isn't it? ;-) + bmi .CPUError41 + lda $06 ;How do you do a test for the Zero page addressing? + beq .CPUError41 + bpl .CPUError41 + lda $07 + beq .CPUError41 + bmi .CPUError41 + +.CPUOk41 + jsr WriteOk + inc SCORE + jmp .CPUTest42 + +.CPUError41 + jsr WriteError +;----------------------------- +.CPUTest42 ;Adr Zero page,X + lda #$21 ;Where the msg prints. + sta $02 + lda #$12 + sta $03 + + + lda #0 + sta $0106 + sta $05 + lda #$93 + sta $06 + lda #$45 + sta $07 + ldx #$04 + + lda $01,x + bne .CPUError42 + bmi .CPUError42 + lda $02,x + cmp #$93 + bne .CPUError42 + ldx #$08 + lda $FE,x + cmp #$93 + bne .CPUError42W + ldx #$FE + lda $08,x + beq .CPUError42W + bpl .CPUError42W + cmp $06 + bne .CPUError42W + cmp $0106 + beq .CPUError42W + ldx #0 + lda $07,x + beq .CPUError42 + bmi .CPUError42 + cmp #$45 + bne .CPUError42 + + +.CPUOk42 + jsr WriteOk + inc SCORE + jmp .CPUTest43 + +.CPUError42W + jsr WriteErrorW + jmp .CPUTest43 +.CPUError42 + jsr WriteError +;----------------------------- +.CPUTest43 ;Adr Absolute + lda #$21 ;Where the msg prints. + sta $02 + lda #$32 + sta $03 + + + + lda #$5E + sta $0200 + lda $FEFE + cmp #$AA + bne .CPUError43 + lda $0200 + cmp #$5E + bne .CPUError43 + lda $FEFF + cmp #$55 + bne .CPUError43 + lda $FE00 + cmp #$FF + bne .CPUError43 + + +.CPUOk43 + jsr WriteOk + inc SCORE + jmp .CPUTest44 + +.CPUError43 + jsr WriteError +;----------------------------- +.CPUTest44 ;Adr Absolute,X + lda #$21 ;Where the msg prints. + sta $02 + lda #$52 + sta $03 + + + + lda #$11 + sta $05 + lda #$5E + sta $0219 + ldx #$19 + lda $0200,x + cmp #$5E + bne .CPUError44 + lda $FEE6,x + cmp #$55 + bne .CPUError44 + lda $FEE7,x + cmp #$22 + bne .CPUError44W + ldx #$25 + lda $FFe0,x + cmp #$11 + bne .CPUError44W + ldx #$D9 + lda $FE25,x + cmp #$AA + bne .CPUError44 + + +.CPUOk44 + jsr WriteOk + inc SCORE + jmp .CPUTest45 +.CPUError44W + jsr WriteErrorW + jmp .CPUTest45 +.CPUError44 + jsr WriteError +;----------------------------- +.CPUTest45 ;Adr Absolute,Y + lda #$21 ;Where the msg prints. + sta $02 + lda #$72 + sta $03 + + + + lda #$11 + sta $05 + lda #$5E + sta $0219 + ldy #$19 + lda $0200,y + cmp #$5E + bne .CPUError45 + lda $FEE6,y + cmp #$55 + bne .CPUError45 + lda $FEE7,y + cmp #$22 + bne .CPUError45W + ldy #$26 + lda $FFDF,y + cmp #$11 + bne .CPUError45W + ldy #$DA + lda $FE24,y + cmp #$AA + bne .CPUError45 + +.CPUOk45 + jsr WriteOk + inc SCORE + jmp .CPUTest46 +.CPUError45W + jsr WriteErrorW + jmp .CPUTest46 +.CPUError45 + jsr WriteError +;----------------------------- +.CPUTest46 ;Adr (Indirect,X) + lda #$21 ;Where the msg prints. + sta $02 + lda #$92 + sta $03 + + + + lda #Ind_Values1 + sta $07 + lda #Ind_Values2 + sta $09 + ldx #0 + lda ($06,x) + cmp #$FF + bne .CPUError46 + ldx #1 + cmp ($05,x) + bne .CPUError46 + ldx #4 + lda ($04,x) + cmp #$AA + bne .CPUError46 + ldx #$27 + lda ($e1,x) + cmp #$AA + bne .CPUError46W + + +.CPUOk46 + jsr WriteOk + inc SCORE + jmp .CPUTest47 +.CPUError46W + jsr WriteErrorW + jmp .CPUTest47 +.CPUError46 + jsr WriteError +;----------------------------- +.CPUTest47 ;Adr (Indirect),Y + lda #$21 ;Where the msg prints. + sta $02 + lda #$B2 + sta $03 + + + + lda #Ind_Values1 + sta $07 + lda #Ind_Values2 + sta $09 + ldy #0 + lda ($06),y + cmp #$FF + bne .CPUError47 + ldy #3 + lda ($06),y + cmp #$BB + bne .CPUError47 + ldy #$FE + lda ($06),y + cmp #$AA + bne .CPUError47 + ldy #2 + lda ($08),y + cmp #$22 + bne .CPUError47 + ldy #1 + lda ($08),y + cmp #$55 + bne .CPUError47W + + +.CPUOk47 + jsr WriteOk + inc SCORE + jmp .CPUTest48 +.CPUError47W + jsr WriteErrorW + jmp .CPUTest48 +.CPUError47 + jsr WriteError +;----------------------------- +.CPUTest48 ;JMP + lda #$21 ;Where the msg prints. + sta $02 + lda #$D2 + sta $03 + + jmp .CPUOk48 ;What!?! + lda #0 + beq .CPUError48 + + +.CPUOk48 + jsr WriteOk + inc SCORE + jmp .CPUTest49 + +.CPUError48 + jsr WriteError +;----------------------------- +.CPUTest49 ;JMP() + lda #$21 ;Where the msg prints. + sta $02 + lda #$F2 + sta $03 + + lda #JmpStep2 + sta $201 + jmp ($200) + lda #0 + beq .CPUError49 +JmpStep2 + lda #>JmpTstOk + sta $200 + lda #JmpTstFail + sta $300 + jmp ($2FF) + + +CPUOk49 + jsr WriteOk + inc SCORE + jmp .CPUTest50 +CPUError49W + jsr WriteErrorW + jmp .CPUTest50 +.CPUError49 + jsr WriteError +;----------------------------- +.CPUTest50 ;JSR + lda #$22 ;Where the msg prints. + sta $02 + lda #$12 + sta $03 + clv + + jsr .JsrTst1 +.JsrDummy + bvc .CPUError51 + nop +.JsrTst1 + pla + tay + pla + cmp #>.JsrDummy + bne .CPUError51 + tya + cmp #<.JsrDummy-1 + bne .CPUError51 + +.CPUOk50 + jsr WriteOk + inc SCORE + jmp .CPUTest51 +.CPUError50 + jsr WriteError +;----------------------------- +.CPUTest51 ;RTS + lda #$22 ;Where the msg prints. + sta $02 + lda #$32 + sta $03 + + lda #>RtsTst1 + pha + lda #RtiTst1 + pha + lda #.Mess0B0 + sta $01 + lda #$20 + sta $02 + lda #$40 + sta $03 + jsr NESWriter0 + + lda #%00011110 ;Background on, sprites on, show leftmost 8 pixels, colour + sta $2001 +;----------------------------- +.CPUTest60 ;Test SR + lda #$20 ;Where the msg prints. + sta $02 + lda #$D1 + sta $03 + + lda #$FF + pha + plp + php + pla + cmp #$FF + bne .CPUError60 + + lda #$00 + pha + plp + php ; This actually sets the B flag. + sei + pla +; tay + cmp #$30 +; and #$10 + bne .CPUError60 +; tya +; cmp #$20 +; bne .CPUError60 + +.CPUOk60 + jsr WriteOk + inc SCORE + jmp .CPUTest61 +.CPUError60B + jsr WriteErrorB + jmp .CPUTest61 +.CPUError60 + jsr WriteError +;----------------------------------------------------------- +.CPUTest61 ;BRK flags + lda #$20 ;Where the msg prints. + sta $02 + lda #$F1 + sta $03 + + lda #$00 + sta $07 + pha ;Push A + plp ;Pull SR - Clear SVDIZC. + brk ;The BRK! +;-------------- + nop ;The BRK's return address is +1. + php ;Push SR - B should be set, I should be clear. + sei + pla ;Pull A + tay + and #$04 + bne .CPUError61I + tya + and #$10 + beq .CPUError61B + lda $07 ; Load SR from inside Interrupt + and #$10 + beq .CPUError61B + lda $07 ; Load SR from inside Interrupt + and #$04 + beq .CPUError61I + + + + lda #$00 + sta $07 + lda #$04 + pha ;Push A + plp ;Pull SR - Clear SVBDZC, set I. + brk ;The BRK! +;-------------- + nop ;The BRK's return address is +1. + php ;Push SR - B & I should be set. + pla ;Pull A + tay + and #$04 + beq .CPUError61I + tya + and #$10 + beq .CPUError61B + lda $07 ; Load SR from inside Interrupt + and #$10 + beq .CPUError61B + lda $07 ; Load SR from inside Interrupt + and #$04 + beq .CPUError61I + + +.CPUOk61 + jsr WriteOk + inc SCORE + jmp .CPUTest62 + +.CPUError61I + jsr WriteErrorI + jmp .CPUTest62 +.CPUError61B + jsr WriteErrorB + jmp .CPUTest62 +.CPUError61 + jsr WriteError +;----------------------------------------------------------- +.CPUTest62 ;BRK return address. + lda #$21 ;Where the msg prints. + sta $02 + lda #$11 + sta $03 + + brk ;The BRK! +;-------------- + dc.b $AD ;LDA $XXXX + bne .CPUOk62 + jmp .CPUError62 + + +.CPUOk62 + jsr WriteOk + inc SCORE + jmp .CPUTest63 + +.CPUError62 + jsr WriteError +;----------------------------------------------------------- +.CPUTest63 ;Stack pointer test + lda #$21 ;Where the msg prints. + sta $02 + lda #$31 + sta $03 + + lda #$55 + tsx + pha + cmp $100,x + bne .CPUError63 + pla + lda #$AA + tsx + pha + cmp $100,x + bne .CPUError63 + lda #$AA + sta $0100 + lda #$55 + sta $0200 + ldx #$FE + txs + pla + pla + cmp #$AA + bne .CPUError63W + + +.CPUOk63 + jsr WriteOk + inc SCORE + jmp .CPUTest64 +.CPUError63W + jsr WriteErrorW + jmp .CPUTest64 +.CPUError63 + jsr WriteError +;----------------------------------------------------------- +.CPUTest64 ;RAM test + lda #$21 ;Where the msg prints. + sta $02 + lda #$51 + sta $03 + + ldx #$06 ;To write 7 x $100 bytes, from $0100 to $07FF. + ldy #$07 ;To write 7 x $100 bytes, from $0100 to $07FF. + sty $05 ;Store count value in $05 + ldy #$00 + sty $04 + lda #$55 +.Clear2 + sta ($04),y ;Clear $100 bytes + dey + bne .Clear2 + dec $05 ;Decrement "banks" left counter + dex + bne .Clear2 ;Do next if > 0 + + ldx #$06 ;To test 7 x $100 bytes, from $0100 to $07FF. + ldy #$07 ;To test 7 x $100 bytes, from $0100 to $07FF. + sty $05 ;Store count value in $05 + ldy #$00 + sty $04 + lda #$55 +.Clear3 + cmp ($04),y ;Clear $100 bytes + bne .CPUError64 + dey + bne .Clear3 + dec $05 ;Decrement "banks" left counter + dex + bne .Clear3 ;Do next if > 0 + + + ldx #$06 ;To write 7 x $100 bytes, from $0100 to $07FF. + ldy #$07 ;To write 7 x $100 bytes, from $0100 to $07FF. + sty $05 ;Store count value in $05 + ldy #$00 + sty $04 + lda #$AA +.Clear4 + sta ($04),y ;Clear $100 bytes + dey + bne .Clear4 + dec $05 ;Decrement "banks" left counter + dex + bne .Clear4 ;Do next if > 0 + + ldx #$06 ;To test 7 x $100 bytes, from $0100 to $07FF. + ldy #$07 ;To test 7 x $100 bytes, from $0100 to $07FF. + sty $05 ;Store count value in $05 + ldy #$00 + sty $04 + lda #$AA +.Clear5 + cmp ($04),y ;Clear $100 bytes + bne .CPUError64 + dey + bne .Clear5 + dec $05 ;Decrement "banks" left counter + dex + bne .Clear5 ;Do next if > 0 + + +.CPUOk64 + jsr WriteOk + inc SCORE + jmp .CPUTest65 + +.CPUError64 + jsr WriteError +;----------------------------------------------------------- +.CPUTest65 ;RAM mirroring test + lda #$21 ;Where the msg prints. + sta $02 + lda #$71 + sta $03 + + lda #$55 + sta $0200 + lda #$AA + sta $0201 + lda #$00 + sta $0202 + lda #$FF + sta $0203 + + lda $0200 + cmp #$55 + bne .CPUError65 + lda $0A00 + cmp #$55 + bne .CPUError65 + lda $0A03 + cmp #$FF + bne .CPUError65 + lda $1201 + cmp #$AA + bne .CPUError65 + lda $1A02 + cmp #$00 + bne .CPUError65 + lda $1A03 + cmp #$FF + bne .CPUError65 + + lda #$00 + sta $1200 + lda $0200 + bne .CPUError65 + +.CPUOk65 + jsr WriteOk + inc SCORE + jmp .CPUTest66 + +.CPUError65 + jsr WriteError +;----------------------------------------------------------- +.CPUTest66 + +CPU6End + ldx #$23 + ldy #$28 + jsr PrintScore + jsr ScrOnWaitKey + + +.TestNoKey2 + lda CONTROLLER1 + and #%11010000 + bne .TestNoKey2 + + jmp Reset_Routine +;---------------------------------------------------------------- +;**************************************************************** +;---------------------------------------------------------------- +pAPUTest + +.WaitV03 + lda $2002 + bpl .WaitV03 ;Wait for vertical blanking interval + ldx #$00 + stx $2000 + stx PPU2000 + stx $2001 ;Screen display off, amongst other things + + + jsr WipePPU + + lda #<.Mess040 + sta $00 + lda #>.Mess040 + sta $01 + lda #$20 + sta $02 + lda #$40 + sta $03 + jsr NESWriter0 + +;----------------------------- +APUEnd + ldx #$23 + ldy #$28 + jsr PrintScore + jsr ScrOnWaitKey + +.TestNoKey3 + lda CONTROLLER1 + and #%11010000 + bne .TestNoKey3 + + jmp Reset_Routine + +;---------------------------------------------------------------- +;**************************************************************** +;---------------------------------------------------------------- +PPUTest + +.WaitV02 + lda $2002 + bpl .WaitV02 ;Wait for vertical blanking interval + ldx #$00 + stx $2000 + stx PPU2000 + stx $2001 ;Screen display off, amongst other things + + + jsr WipePPU + + lda #<.Mess000 + sta $00 + lda #>.Mess000 + sta $01 + lda #$21 + sta $02 + lda #$60 + sta $03 + jsr NESWriter0 + + lda #$00 + sta SCORE ;Clear score. +;---------------------------------- +.PPUTest0 + lda #$20 + sta $2006 + lda #$21 + sta $2006 + + ldx #$30 +.PPUWriteLoop0 + stx $2007 + inx + cpx #$3a + bne .PPUWriteLoop0 + + + lda #$20 + sta $2006 + lda #$21 + sta $2006 + + ldx #$30 + stx $04 + ldy #10 + lda $2007 + + lda #$21 ;Where the msg prints. + sta $02 + lda #$FB + sta $03 +.PPUReadLoop0 + lda $2007 + cmp $04 + bne .PPUError0 + inc $04 + dey + bne .PPUReadLoop0 + + +.PPUOk0 + jsr WriteOk + inc SCORE + jmp .PPUTest1 + +.PPUError0 + jsr WriteError +;----------------------------- +.PPUTest1 + lda #$20 + sta $2006 + lda #$41 + sta $2006 + + ldx #$30 +.PPUWriteLoop1 + stx $2007 + inx + cpx #$35 + bne .PPUWriteLoop1 + lda $2007 + inx +.PPUWriteLoop1_1 + stx $2007 + inx + cpx #$3a + bne .PPUWriteLoop1_1 + + + + lda #$20 + sta $2006 + lda #$46 + sta $2006 + + lda #$22 ; Where the msg prints. + sta $02 + lda #$1B + sta $03 + + ldx #$36 + stx $04 + ldy #04 + lda $2007 + lda $2007 + cmp #0 + bne .PPUError1 +.PPUReadLoop1 + lda $2007 + cmp $04 + bne .PPUError1 + inc $04 + dey + bne .PPUReadLoop1 + + + +.PPUOk1 + jsr WriteOk + inc SCORE + jmp .PPUTest2 + +.PPUError1 + jsr WriteError + +;----------------------------- +.PPUTest2 + lda #$20 + sta $2006 + lda #$60 + sta $2006 + lda $2007 + + ldx #$30 +.PPUWriteLoop2 + stx $2007 + inx + cpx #$3a + bne .PPUWriteLoop2 + + + lda #$20 + sta $2006 + lda #$61 + sta $2006 + + ldx #$30 + stx $04 + ldy #9 + lda $2007 + + lda #$22 ; Where the msg prints. + sta $02 + lda #$3b + sta $03 +.PPUReadLoop2 + lda $2007 + cmp $04 + bne .PPUError2 + inc $04 + dey + bne .PPUReadLoop2 + + +.PPUOk2 + jsr WriteOk + inc SCORE + jmp .PPUTest3 + +.PPUError2 + jsr WriteError +;----------------------------- +.PPUTest3 + lda #$20 + sta $2006 + lda #$81 + sta $2006 + + ldx #$30 +.PPUWriteLoop3 + stx $2007 + inx + cpx #$39 + bne .PPUWriteLoop3 + + ldx $2007 + lda #$20 + sta $2006 + lda #$8a + sta $2006 + stx $2007 + + lda #$20 + sta $2006 + lda #$81 + sta $2006 + + ldx #$30 + stx $04 + ldy #10 + lda $2007 + + lda #$22 ; Where the msg prints. + sta $02 + lda #$5B + sta $03 +.PPUReadLoop3 + lda $2007 + cmp $04 + bne .PPUError3 + inc $04 + dey + bne .PPUReadLoop3 + + +.PPUOk3 + jsr WriteOk + inc SCORE + jmp .PPUTest4 + +.PPUError3 + jsr WriteError +;----------------------------- +.PPUTest4 + lda #$20 + sta $2006 + lda #$A1 + sta $2006 + + ldx #$30 +.PPUWriteLoop4 + stx $2007 + inx + cpx #$35 + bne .Not5 + lda #$26 ; 1st Adr byte. + sta $2006 +.Not5 + cpx #$3A + bne .PPUWriteLoop4 + + lda #$01 ; 2nd adr byte. + sta $2006 + + lda #$20 + sta $2006 + lda #$A1 + sta $2006 + + ldx #$30 + stx $04 + ldy #10 + lda $2007 + + lda #$22 ; Where the msg prints. + sta $02 + lda #$7B + sta $03 +.PPUReadLoop4 + lda $2007 + cmp $04 + bne .PPUError4 + inc $04 + dey + bne .PPUReadLoop4 + + +.PPUOk4 + jsr WriteOk + inc SCORE + jmp .PPUTest5 + +.PPUError4 + jsr WriteError +;---------------------------------- +.PPUTest5 + lda #$30 + sta $2006 + lda #$C1 + sta $2006 + + ldx #$30 +.PPUWriteLoop5 + stx $2007 + inx + cpx #$3a + bne .PPUWriteLoop5 + + + lda #$20 + sta $2006 + lda #$C1 + sta $2006 + + ldx #$30 + stx $04 + ldy #10 + lda $2007 + + lda #$22 ;Where the msg prints. + sta $02 + lda #$9B + sta $03 +.PPUReadLoop5 + lda $2007 + cmp $04 + bne .PPUError5 + inc $04 + dey + bne .PPUReadLoop5 + + +.PPUOk5 + jsr WriteOk + inc SCORE + jmp .PPUTest6 + +.PPUError5 + jsr WriteError +;---------------------------------- +.PPUTest6 + lda #$3E ;Where to print test pattern. + sta $2006 + lda #$E1 + sta $2006 + + lda #$04 ;Set 32 byte increment for PPU. + sta $2000 + + ldx #$88 +.PPUIncLoop6 + lda $2007 + lda $2007 + dex + bne .PPUIncLoop6 + + lda #$00 ;Set 1 byte increment for PPU. + sta $2000 + + + ldx #$30 +.PPUWriteLoop6 + stx $2007 + inx + cpx #$3a + bne .PPUWriteLoop6 + + lda #$20 ;Where to read pattern. + sta $2006 + lda #$E1 + sta $2006 + + ldx #$30 + stx $04 + ldy #10 + lda $2007 + + lda #$22 ;Where the msg prints. + sta $02 + lda #$BB + sta $03 +.PPUReadLoop6 + lda $2007 + cmp $04 + bne .PPUError6 + inc $04 + dey + bne .PPUReadLoop6 + + +.PPUOk6 + jsr WriteOk + inc SCORE + jmp .PPUTest7 + +.PPUError6 + jsr WriteError +;----------------------------- +.PPUTest7 + lda #$24 + sta $2006 + lda #$05 + sta $2006 + + ldx #$33 +.PPUWriteLoop7 + stx $2007 + inx + cpx #$43 + bne .PPUWriteLoop7 + + lda #$25 + sta $2006 + lda #$1D + sta $2006 + + ldx #$01 +.PPUWriteLoop7b + stx $2007 + inx + cpx #$11 + bne .PPUWriteLoop7b + + lda #$24 + sta $2005 + lda #$05 + sta $2006 + + ldx #$33 + stx $04 + ldy #7 + + lda #$22 ; Where the msg prints. + sta $02 + lda #$DB + sta $03 + + lda $2007 +.PPUReadLoop7 + lda $2007 + cmp $04 + bne .PPU2Latches7 + inc $04 + dey + bne .PPUReadLoop7 + +.PPUDrpcjr7 + lda #DrpcjrMess + sta $01 + jsr NESWriter0 + inc SCORE + jmp PPU1_End + +.PPU2Latches7 + lda #$00 + sta $2005 + lda #$00 + sta $2006 + lda #$24 + sta $2006 + lda #$05 + sta $2006 + lda $2007 + +.PPUReadLoop7b + lda $2007 + cmp $04 + bne .PPUError7 + inc $04 + dey + bne .PPUReadLoop7b +.PPUNES7 + lda #NesMess + sta $01 + jsr NESWriter0 + inc SCORE + jmp .PPUTest8 + +.PPUError7 + jsr WriteError +;----------------------------- +.PPUTest8 + lda #$3F + sta $2006 + lda #$05 + sta $2006 + + ldx #$33 +.PPUWriteLoop8 + stx $2007 + inx + cpx #$3A + bne .PPUWriteLoop8 + + lda #$3F + sta $2006 + lda #$25 + sta $2006 + + ldx #$33 + stx $04 + ldy #7 + + lda #$22 ; Where the msg prints. + sta $02 + lda #$FB + sta $03 +.PPUReadLoop8 + lda $2007 + cmp $04 + bne .PPUPalNES8 + inc $04 + dey + bne .PPUReadLoop8 + + +.PPUDrpcjr8 + lda #DrpcjrMess + sta $01 + jsr NESWriter0 + inc SCORE + jmp PPU1_End + +.PPUPalNES8 + lda $2007 + cmp $04 + bne .PPUError8 + inc $04 + dey + bne .PPUPalNES8 +.PPUNES8 + lda #NesMess + sta $01 + jsr NESWriter0 + inc SCORE + jmp PPU1_End + +.PPUError8 + jsr WriteError +;---------------------------------------------------------------- +PPU1_End + ldx #$23 + ldy #$28 + jsr PrintScore + jsr ScrOnWaitKey +;---------------------------------------------------------------- +PPUTest_2 ; ! Test Sprites ! + +.WaitV06 + lda $2002 + bpl .WaitV06 ;Wait for vertical blanking interval + ldx #$00 + stx $2000 + stx $2001 ;Screen display off, amongst other things + + + jsr WipePPU + + lda #<.Mess010 + sta $00 + lda #>.Mess010 + sta $01 + lda #$20 + sta $02 + lda #$40 + sta $03 + jsr NESWriter0 +;----------------------------- +.PPUTest20 ; ! Test Sprite 0 collision detection ! + lda #$FF ; What is filled in the SPRRAM + jsr ClearSPRRAM + + lda #$20 ; Where the msg prints. + sta $02 + lda #$D4 + sta $03 + + lda #200 ;New y pos for spr0 + sta $700 + lda #$07 + sta $4014 ;Copy $700 to Spr-RAM + + lda #$18 + sta $2001 ; Sprite & Display On + + ldx #0 +.WaitV08 + lda $2002 + bpl .WaitV08 ;Wait for vertical blanking interval + stx $2005 + stx $2005 + stx $2006 + stx $2006 +.WaitV09 + lda $2002 + bmi .WaitV09 ; Stupid fix for bad emus. + +.PPUTestLoop20 + lda $2002 + asl + bmi .PPUError20 + bcc .PPUTestLoop20 ; Loop for one whole frame + + lda #80 ;New y pos for spr0 + sta $700 + lda #$07 + sta $4014 ;Copy $700 to Spr-RAM + + stx $2005 + stx $2005 + stx $2006 + stx $2006 +.WaitV0A + lda $2002 + bmi .WaitV0A ; Stupid fix for bad emus. + +.PPUTestLoop20_1 + lda $2002 + asl + bmi .PPUOk20 + bcc .PPUTestLoop20_1 ; Loop for one whole frame + jmp .PPUError20 + + lda #$08 + sta $2001 ; Sprite Off, Display On + stx $2005 + stx $2005 + stx $2006 + stx $2006 +.WaitV0B + lda $2002 + bmi .WaitV0B ; Stupid fix for bad emus. + +.PPUTestLoop20_2 + lda $2002 + asl + bmi .PPUError20 + bcc .PPUTestLoop20_2 ; Loop for one whole frame + + +.PPUOk20 + stx $2001 ; Sprite & Display Off + jsr WriteOk + inc SCORE + jmp .PPUTest21 + +.PPUError20 + stx $2001 ; Sprite & Display Off + jsr WriteError +;----------------------------- +.PPUTest21 ; Test Sprite Overflow ;-) + lda #$20 ; Where the msg prints. + sta $02 + lda #$F4 + sta $03 + + ldx #$00 + lda #$18 + sta $2001 ; Sprite & Display On + +.WaitV0C + lda $2002 + bpl .WaitV0C ;Wait for vertical blanking interval + +.WaitV0D + lda $2002 + bmi .WaitV0D ; Stupid fix for bad emus. + +.PPUTestLoop21 + lda $2002 + tay + and #$20 + bne .PPUError21 + tya + bpl .PPUTestLoop21 ; Loop for one whole frame + + lda #80 ;New y pos for spr0 + sta $704 + sta $708 + sta $70C + sta $710 + sta $714 + sta $718 + sta $71C + sta $720 + + lda #$07 + sta $4014 ;Copy $700 to Spr-RAM + +.WaitV0E + lda $2002 + bpl .WaitV0E ;Wait for vertical blanking interval + +.WaitV0F + lda $2002 + bmi .WaitV0F ; Stupid fix for bad emus. + +.PPUTestLoop21_1 + lda $2002 + tay + and #$20 + bne .PPUOk21 + tya + bpl .PPUTestLoop21_1 ; Loop for one whole frame + jmp .PPUError21 + +.PPUOk21 + stx $2001 ; Sprite & Display Off + jsr WriteOk + inc SCORE + jmp .PPUTest22 + +.PPUError21 + stx $2001 ; Sprite & Display Off + jsr WriteError +;----------------------------- +.PPUTest22 + lda #$21 ; Where the msg prints. + sta $02 + lda #$14 + sta $03 + + ldx #$00 + stx $2003 +.PPUWriteLoop22 + stx $2004 + inx + bne .PPUWriteLoop22 + + stx $04 + stx $2003 +.PPUReadLoop22 + lda $2004 + cmp $04 + bne .PPUError22 + inc $04 + inx + bne .PPUReadLoop22 + + +.PPUOk22 + jsr WriteOk + inc SCORE + jmp .PPUTest23 + +.PPUError22 + jsr WriteError +;----------------------------- +.PPUTest23 + lda #$21 ; Where the msg prints. + sta $02 + lda #$34 + sta $03 + + ldx #$00 + stx $2003 + txa + clc +.PPUWriteLoop23 + sta $0500,X + adc #$01 + inx + bne .PPUWriteLoop23 + lda #$05 + sta $4014 + + stx $04 + stx $2003 +.PPUReadLoop23 + lda $2004 + cmp $04 + bne .PPUError23 + inc $04 + inx + bne .PPUReadLoop23 + + +.PPUOk23 + jsr WriteOk + inc SCORE + jmp .PPUTest24 + +.PPUError23 + jsr WriteError +;----------------------------- +.PPUTest24 + lda #$21 ; Where the msg prints. + sta $02 + lda #$54 + sta $03 + + lda #$24 + sta $2003 + ldx #$00 + txa + clc +.PPUWriteLoop24 + sta $0500,X + adc #$01 + inx + bne .PPUWriteLoop24 + lda #$05 + sta $4014 + + stx $04 + lda #$24 + sta $2003 +.PPUReadLoop24 + lda $2004 + cmp $04 + bne .PPUError24 + inc $04 + inx + bne .PPUReadLoop24 + + +.PPUOk24 + jsr WriteOk + inc SCORE + jmp .PPUTest25 + +.PPUError24 + jsr WriteError +;----------------------------- +.PPUTest25 +;---------------------------------------------------------------- +PPU2_End + ldx #$23 + ldy #$28 + jsr PrintScore + jsr ScrOnWaitKey +;---------------------------------------------------------------- +PPUTest_3 ; VBl clear after read. +;----------------------------- +.WaitV07 + lda $2002 + bpl .WaitV07 ;Wait for vertical blanking interval + ldx #$00 + stx $2000 + stx $2001 ;Screen display off, amongst other things + + + jsr WipePPU + + lda #<.Mess020 + sta $00 + lda #>.Mess020 + sta $01 + lda #$20 + sta $02 + lda #$40 + sta $03 + jsr NESWriter0 +;----------------------------- +.PPUTest14 + lda #$08 + sta $2001 ; Display On + + lda #$20 ; Where the msg prints. + sta $02 + lda #$DA + sta $03 + +.PPUTestLoop14 + lda $2002 + bpl .PPUTestLoop14 + + lda #$00 + sta $2001 ; Display Off + lda $2002 + bmi .PPUError14 + + +.PPUOk14 + jsr WriteOk + inc SCORE + jmp .PPUTest15 + +.PPUError14 + jsr WriteError +;----------------------------- +.PPUTest15 + +PPU3_End + ldx #$23 + ldy #$28 + jsr PrintScore + jsr ScrOnWaitKey + +.TestNoKey5 + lda CONTROLLER1 + and #%11010000 + bne .TestNoKey5 + +;---------------------------------------------------------------- + jmp Reset_Routine +;---------------------------------------------------------------- + + + + + + + + + +;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX +; !!! Subroutines !!! +;---------------------------------------------------------------- +PrintScore SUBROUTINE ; need X & Y for pos. + stx $02 ; Where the msg prints. + sty $03 + lda #$20 + sta SCORE_T + lda SCORE + cmp #10 + bmi .singleScore + lda #$30 + sta SCORE_T + lda SCORE +.countMore + inc SCORE_T + sec + sbc #10 + cmp #10 + bpl .countMore + +.singleScore + clc + adc #$30 + sta SCORE_T+1 + lda #SCORE_T + sta $01 + jmp NESWriterS + +;---------------------------------------------------------------- + +TestEnd +;Enable vblank interrupts, etc. + lda #%10001000 + sta $2000 + lda #%00011110 ;Screen on, sprites on, show leftmost 8 pixels, colour + sta $2001 + + + +.Title2Loop + lda VBPASS + beq .Title2Loop ;Wait for VBlank to pass... + + +.alltid + jmp .alltid + + +;***************************************************** +;----------------------------------------------------- +;***************************************************** + +ScrOnWaitKey SUBROUTINE +;Enable vblank interrupts, etc. + lda #%10001000 + sta $2000 + lda #%00011110 ;Screen on, sprites on, show leftmost 8 pixels, colour + sta $2001 + +.TestNoKey + lda CONTROLLER1 + and #%11010000 + bne .TestNoKey +.WaitKey0 + lda CONTROLLER1 + and #%11010000 + beq .WaitKey0 + rts + +;***************************************************** +;----------------------------------------------------- +;***************************************************** + +WipePPU SUBROUTINE + lda #$20 + sta $2006 + lda #$00 + sta $2006 + + ldx #$00 + ldy #$10 +.ClearPPU sta $2007 + dex + bne .ClearPPU + dey + bne .ClearPPU + + lda #$00 + sta $2003 + tay +.ClearSpr sta $2004 + dey + bne .ClearSpr + rts + +;-------------------------------- +ClearSPRRAM SUBROUTINE ; Needs fill value in A + + ldx #$08 + stx $2003 +.ClearSPRRAM sta $0700,X ;$700 FOR NOW!!!! + sta $2004 + inx + bne .ClearSPRRAM + + rts +;-------------------------------------------------- + + + +PrintFullScreen SUBROUTINE +;-------------------------------------------------- +; Needs $00 & $01 for address of picture. +; Needs A for screen number (0-3). +;-------------------------------------------------- + asl + asl + adc #$20 ;Load Name and Attribute Table + sta $2006 + lda #$00 + sta $2006 + + ldy #$00 + ldx #$04 + +.LoadTitle + lda ($00),Y ;Load Title Image + sta $2007 + iny + bne .LoadTitle + inc $01 + dex + bne .LoadTitle + + rts + +;-------------------------------------------------- +WriteOk SUBROUTINE +;-------------------------------------------------- + lda #OkMess + sta $01 + jmp NESWriterS + +;-------------------------------------------------- +WriteError SUBROUTINE +;-------------------------------------------------- + lda #ErrMess + sta $01 + jmp NESWriterS + +;-------------------------------------------------- +WriteErrorZ SUBROUTINE +;-------------------------------------------------- + lda #ErrMessZ + sta $01 + jmp NESWriterS + +;-------------------------------------------------- +WriteErrorN SUBROUTINE +;-------------------------------------------------- + lda #ErrMessN + sta $01 + jmp NESWriterS + +;-------------------------------------------------- +WriteErrorV SUBROUTINE +;-------------------------------------------------- + lda #ErrMessV + sta $01 + jmp NESWriterS + +;-------------------------------------------------- +WriteErrorC SUBROUTINE +;-------------------------------------------------- + lda #ErrMessC + sta $01 + jmp NESWriterS + +;-------------------------------------------------- +WriteErrorD SUBROUTINE +;-------------------------------------------------- + lda #ErrMessD + sta $01 + jmp NESWriterS + +;-------------------------------------------------- +WriteErrorI SUBROUTINE +;-------------------------------------------------- + lda #ErrMessI + sta $01 + jmp NESWriterS + +;-------------------------------------------------- +WriteErrorB SUBROUTINE +;-------------------------------------------------- + lda #ErrMessB + sta $01 + jmp NESWriterS + +;-------------------------------------------------- +WriteErrorR SUBROUTINE +;-------------------------------------------------- + lda #ErrMessR + sta $01 + jmp NESWriterS + +;-------------------------------------------------- +WriteErrorW SUBROUTINE +;-------------------------------------------------- + lda #ErrMessW + sta $01 + jmp NESWriterS + +;-------------------------------------------------- +NESWriterS SUBROUTINE ;S as in Safe +.WriteLoopS + lda $2002 + bpl .WriteLoopS ;Wait for vertical blanking interval + jsr NESWriter0 + lda #$00 + sta $2005 + sta $2005 + rts +;-------------------------------------------------- +NESWriter0 SUBROUTINE +;-------------------------------------------------------------------------- +; Needs $00 & $01 as address to text, $02 & $03 as PPU destination address. +;-------------------------------------------------------------------------- + lda $02 + sta $2006 + lda $03 + sta $2006 + + ldy #$00 +.WriteChr + lda ($00),Y ;Load Title Image + beq .WriteEnd + cmp #$0A + beq .NewRow + sta $2007 +.WriteLoop + iny + bne .WriteChr + inc $01 + bne .WriteChr +.WriteEnd + rts + +.NewRow + clc + lda $03 + adc #$20 ;One whole row. + bcc .NewRow2 + inc $02 +.NewRow2 + and #$e0 + ldx $02 + stx $2006 + sta $03 + sta $2006 + jmp .WriteLoop +;-------------------------------------------------- + + + +SetPalette SUBROUTINE +;---------------------------------- +; INPUT: A: PALETTE NUMBER +;---------------------------------- + tax + lda #<.Palette + sta PALETTEPTR + lda #>.Palette + sta PALETTEPTR+1 ;Point to Palette + + cpx #$00 + beq .NoAddPalette +.AddPalette + lda PALETTEPTR + clc + adc #$20 ;Multiply Palette Index by 32 + sta PALETTEPTR + lda PALETTEPTR+1 + adc #$00 + sta PALETTEPTR+1 + dex + bne .AddPalette + +.NoAddPalette + ldx #$3F + stx $2006 + ldx #$00 + stx $2006 + + ldy #$00 + ldx #$20 ;Set BG & Sprite palettes. +.InitPal lda (PALETTEPTR),Y + sta $2007 + sta TEMPPAL,Y ;Store to palette copy + iny + dex + bne .InitPal + rts + +;********************************************************* +.Palette + INCLUDE TanksPal.ASM +;********************************************************* + + + +ToDecimal SUBROUTINE +;-------------------------------------------- +; INPUT: A: Number to convert from Hex to Dec +;-------------------------------------------- + sta $0100 + tya + pha + + lda $0100 + ldy #$FF + sec +.ToDecLoop1 + iny + sbc #$0A + bcs .ToDecLoop1 + adc #$0A + sta $0100 + tya + asl + asl + asl + asl + ora $0100 + sta $0100 + + pla + tay + lda $0100 + rts +;-------------------------------------------------- + + + +;-------------------------------------------------- +Fadeout SUBROUTINE + lda #$3F + sta $2006 + lda #$00 + sta $2006 + + ldx #$00 + stx $0100 + ldy #$20 +.FadePal + ldx $0100 + lda TEMPPAL,X + tax + lda .FadeIndex,X ;Get Fade info. + sta $2007 + ldx $0100 + sta TEMPPAL,X + inc $0100 + dey + bne .FadePal + + dec FADESTATUS + lda #$00 + sta $2005 + sta $2005 + lda #%10001000 + ora PPU2000 + sta PPU2000 + sta $2000 +; lda #%00011110 +; sta $2001 + + rts +;-------------------------------------------------- +.FadeIndex + INCLUDE TanksFadePal.ASM +;-------------------------------------------------- + + + +;---------------------------------------------------------------------- +; ! This is run every VBlank ! +;---------------------------------------------------------------------- +NMI_Routine SUBROUTINE + php + pha + txa + pha + tya + pha + + lda #$01 + sta VBPASS + lda VBODD + eor #$01 + sta VBODD + +;---------- READ JOYPAD ----------------------- + + ldx #$01 + stx $4016 + dex + stx $4016 + stx CONTROLLER1 + stx CONTROLLER2 + stx CONTROLLER3 + stx CONTROLLER4 + stx CONTROLER1X + stx CONTROLER2X + ldy #$05 ; Write to $0500. + sty $05 + ldy #$00 + sty $04 + +.ReadCont1_0 + ldy #8-1 ; Read 3 x 8 bits from $4016 +.ReadCont1 + lda $4016 ; Read controller port. + sta ($04),y + ror + lda CONTROLLER1,x + rol + sta CONTROLLER1,x + dey + bpl .ReadCont1 + lda $04 + adc #8 + sta $04 + inx + inx + cpx #6 + bne .ReadCont1_0 + + +;--------------------------------------------- +;--- TEST FOR FADEOUT ------------------------ + lda FADESTATUS + beq .NoFade + lda VBDELAY + bne .DelayFade + jsr Fadeout + lda #$04 + sta VBDELAY + jmp .NoFade +.DelayFade + dec VBDELAY +;--------------------------------------------- +.NoFade + + + lda GAMESCREEN + cmp #$01 + bne .NotGameScreenPlanets + lda VBWAIT + beq .NoPlanetChangeWait + dec VBWAIT +.NoPlanetChangeWait + lda PLANETCHANGE + beq .PlanetChangeEnd + lda #$00 + sta PLANETCHANGE + +; jsr DisplayPlanetName + +.PlanetChangeEnd + lda #$00 + sta $2005 + sta $2005 + + jmp .VBEnd + +.NotGameScreenPlanets + cmp #$02 + bne .NotGameScreenTitle + lda #$00 + sta TITLESCROLL2 + sta $2005 + sta $2005 + inc TITLESCROLL1 + lda TITLESCROLL1 + cmp #$02 + bne .NotTooWavyTitle + lda #$00 + sta TITLESCROLL1 + + inc TITLESCROLL + lda TITLESCROLL + cmp #$40 + bne .NotTooWavyTitle + lda #$00 + sta TITLESCROLL +.NotTooWavyTitle + jmp .VBEnd +.NotGameScreenTitle + + +; lda #$07 +; sta $4014 ;Copy $700 to Spr-RAM + +;--- SET SCROLLING ---------- + lda #$40 + lda #%10001001 +.SaveScrolling + sta $2000 + sta PPU2000 + lda #$00 + sta $2005 + sta $2005 + +.VBEnd + + lda #$07 + sta $4014 + pla + tay + pla + tax + pla + plp + rti +;-------------------------------------------------------- +; ! Here ends the VBlank routines ! +;-------------------------------------------------------- +IRQ_Routine + pha ;Push A + php ;Push SR + pla ;Pull SR to A + sta $07 ;Save SR in $7 + pla ;Pull A + rti + + + ORG $FC00,0 +JmpTstFail + jmp CPUError49W + + ORG $FD00,0 +JmpTstOk + jmp CPUOk49 + ORG $FDFC,0 + jmp CPUError49W + + + ORG $FE00,0 +Ind_Values1 + dc.b $FF + dc.b $88 + dc.b $44 + dc.b $BB + + ORG $FEFE,0 +Ind_Values2 + dc.b $AA + dc.b $55 + dc.b $22 + dc.b $33 + +;That's all the code. Now we just need to set the vector table approriately. + + ORG $FFFA,0 + dc.w NMI_Routine + dc.w Reset_Routine + dc.w IRQ_Routine ;Used to test the BRK instruction. + + +;The end. diff --git a/stress/NEStress.txt b/stress/NEStress.txt new file mode 100644 index 0000000..5a43e35 --- /dev/null +++ b/stress/NEStress.txt @@ -0,0 +1,196 @@ + NEStress - A NES stress/testing program. + + +PPU Test1: + + *PPU Normal Write/Read. + Just Writes a couple of bytes to a specific address + and reads them back by specifying the same address. + Then reads one "garbage" byte before comparing values. + If this doesn't work, then all other test will fail also. + + *PPU Write Read Write. + This tests if the PPU address is increased when doing a + read between writes (used by some "RARE" games). + + *PPU First Read Then Write. + This tests if the PPU address is increased after a first read. + + *PPU First Read Corret. + This tests the PPU read "pipe" to see if the first read is + just random or the actual value in the "pipe". + + *PPU Mixed Address / Data Write. + This test so that the PPU address isn't changed before both + bytes are written to the address register. + + *PPU $3000 Mirroring. + Tests the mirroring between $3000 and $2000. + Writes to $3xxx and reads from $2xxx plus the other way. + (Used by one version of Trojan at least) + + *PPU $3FFF-$0000 Wrap Around. + To test if the address is correctly reset to $0000 after + access to $3FFF. One emulator actually crashed during this test. + + *PPU Palette Write / Read. + This is actually very strange. + At least the Dr. PC Jr has this memory before the "pipe", + the NES doesn't, Famicoms behaviour is unknown. + This means there is no "garbage" read before the real data + (on Dr. PC Jr at least). + + +PPU Test2: + + *Sprite Collision. + Tests for accurate pixel detection collision. + Also test that there are no collisions if BGR or Sprites are turned off. + + *Sprite Limmiter. + Checks if the sprite max bit is set when 8 sprites or more are on the + same raster scanline. + + *Sprite data CPU write / read. + NOT CORRECT. + + *Sprite data DMA write / CPU read. + NOT CORRECT. + + *Sprite data diff DMA write / CPU read. + NOT CORRECT. + + +PPU Test3: + + *VBl test, bit 7 of $2002. + Checks if the bit is reset after a read. + + +CPU Tests: + These tests checks that all flags are set correctly and the results. + The only instructions that should set the overflow flag (V) is ADC, + SBC and BIT (Test2...). + +CPU Test1: + "Aritmethic" operations. + + *ADC/SBC Should set: V, C, N and Z. + + *CMP... Shold set: C, N and Z. + + *INC/DEC... Should set: N and Z. + + *ASL,ROL... Should set: C, N and Z. + + +CPU Test2: + Logical operations. + + *LDA, LDX and LDY should set: N and Z. + + *TAX, TAY, TSX, TXA and TYA should set: N and Z. + + *TXS shouldn't set any flags as nothing is moved to a data register. + + *PLA should set: N and Z. + + *AND, EOR and ORA should set: N and Z. + + *BIT should set: V, N and Z. + + +CPU Test3: + Address modes. + + *Imidiate: + A little hard to test actually =). + It just writes and reads an address. + + *Zero Page: + Same as Imidiate... + + *Zero Page,X: + This writes and reads with Zero Page and Zero Page,X. + Remember that Zero Page,X always writes to the zero page, + (wrap around). + + *Absolute: + Same as Imidiate... + + *Absolute,X: + This writes and reads with Absolute and Absolute,X. + + + *Absolute,Y: + This writes and reads with Absolute and Absolute,Y. + + *(Indirect,X): + This Writes and reads with Absolute and (Indirect,X). + First gets the address from (Zero Page+x), then gets the value + from that address. Remeber ZP wrap. + + *(Indirect),Y: + This Writes and reads with Absolute and (Indirect),Y. + First gets the address from (Zero Page) and adds Y to the fetched + address then gets the value from that address. + + *JMP: + Not much to say here, if you have come this far in the test it works. + + *JMP(): + First tests that the JMP() works correct then tests that the + address isn't read from the same page. + should read from $2FF and $300, not $2FF and $200. + + *JSR: + Tests that the jump is performed and that the return address + is correct + + *RTS: + This pushes an address on the stack and does the RTS, then + checks that it returned to the right address (pushed address + plus 1). + + *RTI: + Like the RTS, but pushes the Acumulator also. + Return address is the pushed address. + + +CPU Test4: + Misc. + + *Write / Read SR: + This tries to set all bits in the Status Register then + reads them back, this should return $FF. + The second test clears all bits in SR and reads them back, + This returns $30 now. The B (BRK) bit is only NULL when an + interrupt is taken. + + *BRK Flags: + Reads the SR flags inside the irq and after it. + + *BRK return address: + The BRK pushes PC+1 on the stack because the CPU always + reads at least 2 bytes for each instruction. + + *Stack Address: + This plays around with the stack and checks that it + wraps correctly. + + *CPU RAM: + This just writes to all the RAM locations and reads it back. + + *CPU RAM Mirroring: + This checks that $800, $1000 and $1800 is mirrored to $0. + + +IOTest: + First strobes $4016 then reads the port ($4016) 24 times and writes + it on the screen. + + go to next screen by first pressing Select & Start then start again. + + + + diff --git a/stress/SolarTitleNAM.asm b/stress/SolarTitleNAM.asm new file mode 100644 index 0000000..0ec3309 --- /dev/null +++ b/stress/SolarTitleNAM.asm @@ -0,0 +1,66 @@ +; solartitle.nam + + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $01, $00, $02, $03, $04, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $05, $06, $07, $08, $09, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $0a, $0b, $0c, $0d, $0e, $0f, $10, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $11, $12, $13, $14, $15, $16, $17, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $18, $19, $1a, $1b, $1c, $1d, $00, $1e, $1f, $20, $21, $22, $23, $24 + DC.B $00, $25, $26, $27, $28, $29, $2a, $2b, $1e, $1f, $20, $21, $22, $23, $24, $00 + DC.B $00, $00, $2c, $2d, $2e, $2f, $30, $31, $00, $32, $33, $34, $35, $36, $37, $38 + DC.B $00, $39, $3a, $3b, $3c, $3d, $3e, $00, $32, $33, $34, $35, $36, $37, $38, $00 + DC.B $00, $00, $00, $00, $3f, $40, $41, $00, $00, $42, $43, $44, $45, $46, $47, $2e + DC.B $00, $48, $00, $00, $49, $4a, $4b, $00, $42, $43, $44, $45, $46, $47, $2e, $00 + DC.B $00, $00, $00, $00, $4c, $4d, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $4e, $4f, $50, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $51, $52, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $53, $54, $55, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $56, $00, $00, $00, $00, $00, $00, $00, $00, $00, $57, $00, $00 + DC.B $00, $58, $59, $5a, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $5b, $00, $00, $00, $00, $5c, $5d, $5e, $5f, $60, $61 + DC.B $62, $63, $64, $65, $66, $67, $68, $00, $00, $00, $00, $69, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $6a, $6b, $6c, $6d, $6e, $6f + DC.B $70, $71, $72, $73, $74, $75, $76, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $69, $00, $5b, $00, $77, $78, $79, $7a, $7b, $7c + DC.B $7d, $7e, $7f, $80, $81, $82, $83, $00, $00, $84, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $5b, $00, $00, $00, $00, $00, $00, $00, $85, $86, $87, $88, $89, $8a + DC.B $8b, $8c, $8d, $8e, $8f, $90, $91, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $5b, $00, $00, $00, $00, $92, $00, $00, $00 + DC.B $00, $00, $00, $92, $00, $00, $00, $00, $00, $00, $93, $94, $95, $96, $97, $98 + DC.B $00, $99, $97, $98, $94, $9a, $00, $00, $00, $00, $9b, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $5b, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $00, $84, $00, $00, $00, $00, $5b, $00, $00 + DC.B $00, $9c, $9d, $00, $5b, $00, $00, $9e, $00, $00, $00, $00, $00, $00, $92, $00 + DC.B $00, $5b, $00, $00, $84, $00, $9e, $00, $00, $00, $5b, $00, $00, $00, $00, $00 + DC.B $00, $9f, $a0, $00, $00, $00, $00, $00, $00, $95, $a1, $a2, $00, $a3, $95, $a1 + DC.B $a4, $98, $95, $96, $96, $a2, $98, $00, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $69, $00, $00, $00, $00, $a4, $99, $95, $00, $a3, $95, $a1 + DC.B $a4, $98, $95, $96, $96, $a2, $98, $94, $69, $5b, $00, $00, $00, $00, $69, $00 + DC.B $00, $00, $92, $00, $00, $00, $5b, $00, $00, $00, $00, $92, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $69, $00, $00, $00, $9c, $9d, $00, $00, $00, $00 + DC.B $5b, $00, $00, $00, $00, $00, $00, $92, $00, $5b, $00, $9e, $a3, $95, $a1, $a4 + DC.B $98, $95, $96, $94, $5b, $00, $00, $00, $00, $00, $9f, $a0, $00, $00, $00, $5b + DC.B $00, $00, $69, $00, $9e, $00, $00, $5b, $00, $00, $00, $00, $00, $00, $00, $00 + DC.B $00, $00, $00, $00, $00, $00, $00, $84, $00, $00, $69, $00, $00, $00, $92, $00 + DC.B $00, $00, $00, $00, $00, $69, $00, $00, $00, $69, $00, $00, $a3, $98, $a2, $a5 + DC.B $a6, $a4, $94, $00, $9c, $9d, $00, $00, $92, $00, $00, $00, $00, $00, $00, $00 + DC.B $9b, $00, $9e, $00, $00, $00, $00, $9c, $9d, $00, $00, $00, $69, $00, $00, $92 + DC.B $00, $00, $00, $00, $9f, $a0, $00, $00, $00, $00, $00, $9e, $00, $69, $00, $92 + DC.B $00, $00, $00, $00, $92, $00, $69, $9f, $a0, $00, $84, $92, $00, $00, $84, $00 + DC.B $00, $00, $00, $69, $00, $00, $92, $00, $00, $5b, $00, $00, $00, $00, $00, $00 + DC.B $00, $a7, $a8, $a9, $aa, $00, $9e, $00, $00, $ab, $ac, $ad, $ad, $ad, $00, $a3 + DC.B $ae, $a3, $00, $af, $97, $ae, $a2, $94, $00, $00, $00, $00, $5b, $00, $69, $00 + DC.B $69, $b0, $b1, $b2, $b3, $00, $00, $92, $a3, $a3, $95, $b4, $a2, $96, $96, $b5 + DC.B $a5, $a6, $98, $a2, $a3, $a4, $b6, $a3, $97, $00, $00, $9e, $9c, $9d, $00, $00 + DC.B $00, $b7, $b8, $b9, $ba, $00, $69, $9c, $9d, $00, $92, $00, $84, $00, $9e, $00 + DC.B $00, $92, $00, $00, $9c, $9d, $00, $9e, $84, $00, $92, $5b, $9f, $a0, $92, $00 + DC.B $69, $00, $9e, $00, $00, $69, $9e, $9f, $a0, $92, $00, $00, $bb, $00, $92, $00 + DC.B $92, $00, $00, $9e, $9f, $a0, $69, $00, $92, $92, $00, $9e, $92, $00, $69, $00 + DC.B $40, $50, $10, $00, $50, $50, $00, $00, $04, $55, $55, $55, $05, $55, $55, $15 + DC.B $04, $21, $00, $03, $05, $01, $c0, $00, $c4, $08, $81, $a0, $a0, $60, $c0, $10 + DC.B $aa, $2e, $56, $54, $52, $5e, $a4, $82, $4e, $68, $2a, $55, $55, $ea, $a4, $c8 + DC.B $5e, $9e, $57, $5a, $58, $5e, $96, $ba, $06, $0d, $07, $0e, $0a, $09, $0b, $07 diff --git a/stress/Tanks.chr b/stress/Tanks.chr new file mode 100644 index 0000000..1f1252f Binary files /dev/null and b/stress/Tanks.chr differ diff --git a/stress/TanksFadePal.ASM b/stress/TanksFadePal.ASM new file mode 100644 index 0000000..6104263 --- /dev/null +++ b/stress/TanksFadePal.ASM @@ -0,0 +1,6 @@ +; tanksfade.pal + + DC.B $2d, $1d, $01, $02, $05, $06, $1d, $06, $09, $1d, $09, $0c, $1d, $0d, $0d, $0d + DC.B $00, $01, $02, $03, $04, $05, $06, $07, $08, $09, $0a, $0b, $0c, $2e, $0e, $0e + DC.B $3d, $11, $12, $13, $14, $15, $16, $17, $18, $19, $1a, $1b, $1c, $1d, $1e, $1e + DC.B $20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $2a, $2b, $2c, $10, $2e, $2e diff --git a/stress/TanksPal.ASM b/stress/TanksPal.ASM new file mode 100644 index 0000000..68e63a9 --- /dev/null +++ b/stress/TanksPal.ASM @@ -0,0 +1,46 @@ +; tankspal.bin + + DC.B $0d, $27, $37, $02, $0d, $17, $27, $12, $0d, $07, $17, $22, $0d, $06, $27, $38 + DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 + + DC.B $0d, $06, $10, $1e, $0d, $07, $00, $2e, $0d, $08, $2d, $1d, $0d, $06, $27, $38 + DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 + + DC.B $0d, $16, $26, $03, $0d, $06, $16, $04, $0d, $07, $06, $05, $0d, $06, $27, $38 + DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 + + DC.B $0d, $20, $32, $30, $0d, $28, $2a, $37, $0d, $16, $28, $27, $0d, $06, $27, $38 + DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 + + DC.B $0d, $28, $38, $06, $0d, $17, $28, $30, $0d, $17, $27, $16, $0d, $06, $27, $38 + DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 + + DC.B $0d, $3c, $20, $30, $0d, $3b, $3c, $3c, $0d, $2b, $3b, $2c, $0d, $06, $27, $38 + DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 + + DC.B $0d, $21, $31, $2c, $0d, $11, $21, $1c, $0d, $01, $11, $0c, $0d, $06, $27, $38 + DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $2c, $3c, $30, $0d, $00, $00, $00 + + DC.B $0d, $00, $10, $1e, $0d, $2d, $00, $2f, $0d, $0b, $2d, $1d, $0d, $06, $27, $38 + DC.B $0d, $0a, $1a, $2a, $2d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 + + DC.B $0d, $13, $23, $16, $0d, $03, $13, $27, $0d, $01, $03, $38, $0d, $06, $27, $38 + DC.B $0d, $0a, $1a, $2a, $0d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 + + DC.B $0d, $17, $27, $16, $0d, $07, $17, $28, $0d, $0c, $1b, $29, $0d, $06, $27, $38 + DC.B $0d, $0a, $1a, $2a, $0d, $05, $26, $37, $0d, $00, $00, $00, $0d, $00, $00, $00 + + DC.B $0d, $2d, $10, $20, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d + DC.B $0d, $06, $16, $36, $0d, $0a, $2a, $3a, $0d, $00, $10, $20, $0d, $0d, $0d, $0d + + DC.B $0d, $02, $27, $20, $0d, $08, $27, $37, $0d, $11, $21, $31, $0d, $07, $16, $28 + DC.B $0d, $1c, $3c, $20, $0d, $08, $00, $10, $0d, $03, $13, $10, $0d, $03, $06, $16 + + DC.B $0d, $06, $1a, $38, $0d, $01, $00, $30, $0d, $01, $11, $21, $0d, $21, $31, $30 + DC.B $0d, $07, $17, $27, $0d, $16, $26, $37, $0d, $12, $3c, $30, $0d, $06, $27, $38 + + DC.B $0d, $10, $10, $10, $0d, $16, $28, $39, $0d, $2d, $10, $30, $0d, $1d, $01, $11 + DC.B $0d, $1d, $28, $17, $0d, $1c, $3c, $20, $0d, $19, $30, $30, $0d, $0d, $0d, $0d + + DC.B $0d, $06, $16, $26, $0d, $07, $17, $27, $0d, $08, $18, $28, $0d, $09, $19, $29 + DC.B $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d, $0d diff --git a/test_roms.xml b/test_roms.xml new file mode 100644 index 0000000..6f61c9b --- /dev/null +++ b/test_roms.xml @@ -0,0 +1,723 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tutor/make.bat b/tutor/make.bat new file mode 100644 index 0000000..40a6245 --- /dev/null +++ b/tutor/make.bat @@ -0,0 +1 @@ +asm6 tutor.asm tutor.nes diff --git a/tutor/tutor.asm b/tutor/tutor.asm new file mode 100644 index 0000000..33912e8 --- /dev/null +++ b/tutor/tutor.asm @@ -0,0 +1,23 @@ +; iNES header + +; iNES identifier +.byte "NES",$1a + +; Number of PRG-ROM blocks +.byte $01 + +; Number of CHR-ROM blocks +.byte $01 + +; ROM control bytes: Horizontal mirroring, no SRAM +; or trainer, Mapper #0 +.byte $00, $00 + +; Filler +.byte $00,$00,$00,$00,$00,$00,$00,$00 + +; PRG-ROM +.include "tutorprg.asm" + +; CHR-ROM +.include "tutorchr.asm" \ No newline at end of file diff --git a/tutor/tutor.nes b/tutor/tutor.nes new file mode 100644 index 0000000..aab0283 Binary files /dev/null and b/tutor/tutor.nes differ diff --git a/tutor/tutorchr.asm b/tutor/tutorchr.asm new file mode 100644 index 0000000..b401a72 --- /dev/null +++ b/tutor/tutorchr.asm @@ -0,0 +1,90 @@ +; CHR-ROM file for the NES 101 tutorial +; Kinda sorta copyright Michael Martin, 2001,2. +; More honestly copyright Commodore Business Machines 1982. + +; The first 4K block (256 characters) is where our backgrounds come from. +; These were extracted from the Commodore 64's character ROM, and modified +; to match the NES' character definition schemas. The background is color +; 2 on the palette, and the letterforms themselves color 3. The characters +; are given tile numbers that correspond to their ASCII codes, to simplify +; the creation of the data files. + +; The first 20 character locations are blank. +.dsb $0200 +.byte $00,$00,$00,$00,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 32 +.byte $18,$18,$18,$18,$00,$00,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 33 +.byte $66,$66,$66,$00,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 34 +.byte $66,$66,$FF,$66,$FF,$66,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 35 +.byte $18,$3E,$60,$3C,$06,$7C,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 36 +.byte $62,$66,$0C,$18,$30,$66,$46,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 37 +.byte $3C,$66,$3C,$38,$67,$66,$3F,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 38 +.byte $06,$0C,$18,$00,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 39 +.byte $0C,$18,$30,$30,$30,$18,$0C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 40 +.byte $30,$18,$0C,$0C,$0C,$18,$30,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 41 +.byte $00,$66,$3C,$FF,$3C,$66,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 42 +.byte $00,$18,$18,$7E,$18,$18,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 43 +.byte $00,$00,$00,$00,$00,$18,$18,$30,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 44 +.byte $00,$00,$00,$7E,$00,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 45 +.byte $00,$00,$00,$00,$00,$18,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 46 +.byte $00,$03,$06,$0C,$18,$30,$60,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 47 +.byte $3C,$66,$6E,$76,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 48 +.byte $18,$18,$38,$18,$18,$18,$7E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 49 +.byte $3C,$66,$06,$0C,$30,$60,$7E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 50 +.byte $3C,$66,$06,$1C,$06,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 51 +.byte $06,$0E,$1E,$66,$7F,$06,$06,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 52 +.byte $7E,$60,$7C,$06,$06,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 53 +.byte $3C,$66,$60,$7C,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 54 +.byte $7E,$66,$0C,$18,$18,$18,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 55 +.byte $3C,$66,$66,$3C,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 56 +.byte $3C,$66,$66,$3E,$06,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 57 +.byte $00,$00,$18,$00,$00,$18,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 58 +.byte $00,$00,$18,$00,$00,$18,$18,$30,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 59 +.byte $0E,$18,$30,$60,$30,$18,$0E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 60 +.byte $00,$00,$7E,$00,$7E,$00,$00,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 61 +.byte $70,$18,$0C,$06,$0C,$18,$70,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 62 +.byte $3C,$66,$06,$0C,$18,$00,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 63 +.byte $3C,$66,$6E,$6E,$60,$62,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 64 +.byte $18,$3C,$66,$7E,$66,$66,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 65 +.byte $7C,$66,$66,$7C,$66,$66,$7C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 66 +.byte $3C,$66,$60,$60,$60,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 67 +.byte $78,$6C,$66,$66,$66,$6C,$78,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 68 +.byte $7E,$60,$60,$78,$60,$60,$7E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 69 +.byte $7E,$60,$60,$78,$60,$60,$60,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 70 +.byte $3C,$66,$60,$6E,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 71 +.byte $66,$66,$66,$7E,$66,$66,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 72 +.byte $3C,$18,$18,$18,$18,$18,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 73 +.byte $1E,$0C,$0C,$0C,$0C,$6C,$38,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 74 +.byte $66,$6C,$78,$70,$78,$6C,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 75 +.byte $60,$60,$60,$60,$60,$60,$7E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 76 +.byte $63,$77,$7F,$6B,$63,$63,$63,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 77 +.byte $66,$76,$7E,$7E,$6E,$66,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 78 +.byte $3C,$66,$66,$66,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 79 +.byte $7C,$66,$66,$7C,$60,$60,$60,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 80 +.byte $3C,$66,$66,$66,$66,$3C,$0E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 81 +.byte $7C,$66,$66,$7C,$78,$6C,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 82 +.byte $3C,$66,$60,$3C,$06,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 83 +.byte $7E,$18,$18,$18,$18,$18,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 84 +.byte $66,$66,$66,$66,$66,$66,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 85 +.byte $66,$66,$66,$66,$66,$3C,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 86 +.byte $63,$63,$63,$6B,$7F,$77,$63,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 87 +.byte $66,$66,$3C,$18,$3C,$66,$66,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 88 +.byte $66,$66,$66,$3C,$18,$18,$18,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 89 +.byte $7E,$06,$0C,$18,$30,$60,$7E,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 90 +.byte $3C,$30,$30,$30,$30,$30,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 91 +.byte $0C,$12,$30,$7C,$30,$62,$FC,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 92 +.byte $3C,$0C,$0C,$0C,$0C,$0C,$3C,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 93 +.byte $00,$18,$3C,$7E,$18,$18,$18,$18,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 94 +.byte $00,$10,$30,$7F,$7F,$30,$10,$00,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ; Character 95 + +; Fill the rest of the first CHR-ROM block with zeroes. +.align $1000 + +; Here begins the second 4K block. The sprites (all one of them) get their data +; from this page. + +.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 ; Character 0: Blank +.byte $18,$24,$66,$99,$99,$66,$24,$18,$00,$18,$18,$66,$66,$18,$18,$00 ; Character 1: Diamond sprite + +; Fill the rest of the CHR-ROM block with zeroes, giving us exactly 8K of data, which +; is what we want and need. +.align $1000 diff --git a/tutor/tutorprg.asm b/tutor/tutorprg.asm new file mode 100644 index 0000000..445f2a2 --- /dev/null +++ b/tutor/tutorprg.asm @@ -0,0 +1,331 @@ +; Program code for NES 101 Tutorial +; Code by Michael Martin, 2001-2 + +; Assign the sprite page to page 2. +sprite = $200 + +; Allocate memory in the zero page segment. If we +; really wanted to, we could scatter these declarations +; through the code (P65 1.1 lets us do so) but not all +; assemblers allow this, and it doesn't help clarity +; any on this program. So the heck with it. + +; As a side note, P65 doesn't really grant any special +; status to any of the segments you use, and only has +; "text" and "data" built in. This means that "zp" +; could be named whatever we wanted, and it also means +; that we have to tell it where to start from (it's the +; zero page, so we start it from zero, naturally). + +enum $000 ;asm6's way of declaring vars in ram + +dx .byte 0 +a .byte 0 +scroll .byte 0 + +ende + +; If we had a normal data segment, it would have an .org $0300, so +; that it doesn't stomp on our sprite data. + +; Actual program code. We only have one PRG-ROM chip here, so the +; origin is $C000. +.org $C000 + +reset: sei + cld + ; Wait two VBLANKs. +- lda $2002 + bpl - +- lda $2002 + bpl - + + ; Clear out RAM. + lda #$00 + ldx #$00 +- sta $000,x + sta $100,x + sta $200,x + sta $300,x + sta $400,x + sta $500,x + sta $600,x + sta $700,x + inx + bne - + + ; Reset the stack pointer. + ldx #$FF + txs + + ; Disable all graphics. + lda #$00 + sta $2000 + sta $2001 + + jsr init_graphics + jsr init_input + jsr init_sound + + ; Set basic PPU registers. Load background from $0000, + ; sprites from $1000, and the name table from $2000. + lda #%10001000 + sta $2000 + lda #%00011110 + sta $2001 + + cli + + ; Transfer control to the VBLANK routines. +loop: jmp loop + +init_graphics: + jsr init_sprites + jsr load_palette + jsr load_name_tables + jsr init_scrolling + rts + +init_input: + ; The A button starts out not-pressed. + lda #$00 + sta a + rts + +init_sound: + ; initialize sound hardware + lda #$01 + sta $4015 + lda #$00 + sta $4001 + lda #$40 + sta $4017 + rts + +init_sprites: + ; Clear page #2, which we'll use to hold sprite data + lda #$00 + ldx #$00 +- sta sprite, x + inx + bne - + + ; initialize Sprite 0 + lda #$70 + sta sprite ; Y coordinate + lda #$01 + sta sprite+1 ; Pattern number + sta sprite+3 ; X coordinate + ; sprite+2, color, stays 0. + + ; Set initial value of dx + lda #$01 + sta dx + rts + +; Load palette into $3F00 +load_palette: + lda #$3F + ldx #$00 + sta $2006 + stx $2006 +- lda palette,x + sta $2007 + inx + cpx #$20 + bne - + rts + +load_name_tables: +; Jam some text into the first name table (at $2400, thanks to mirroring) + ldy #$00 + ldx #$04 + lda #bg + sta $11 + lda #$24 + sta $2006 + lda #$00 + sta $2006 +- lda ($10),y + sta $2007 + iny + bne - + inc $11 + dex + bne - + +; Clear out the Name Table at $2800 (where we already are. Yay.) + ldy #$00 + ldx #$04 + lda #$00 +- sta $2007 + iny + bne - + dex + bne - + rts + +init_scrolling: + lda #240 + sta scroll + rts + +update_sprite: + lda #>sprite + sta $4014 ; Jam page $200-$2FF into SPR-RAM + + lda sprite+3 + beq hit_left + cmp #255-8 + bne edge_done + ; Hit right + ldx #$FF + stx dx + jsr high_c + jmp edge_done +hit_left: + ldx #$01 + stx dx + jsr high_c + +edge_done: ; update X and store it. + clc + adc dx + sta sprite+3 + rts + +react_to_input: + lda #$01 ; strobe joypad + sta $4016 + lda #$00 + sta $4016 + + lda $4016 ; Is the A button down? + and #1 + beq not_a + ldx a + bne a_done ; Only react if the A button wasn't down last time. + sta a ; Store the 1 in local variable 'a' so that we this is + jsr reverse_dx ; only called once per press. + jmp a_done +not_a: sta a ; A has been released, so put that zero into 'a'. +a_done: lda $4016 ; B does nothing + lda $4016 ; Select does nothing + lda $4016 ; Start does nothing + lda $4016 ; Up + and #1 + beq not_up + ldx sprite ; Load Y value + cpx #7 + beq not_up ; No going past the top of the screen + dex + stx sprite +not_up: lda $4016 ; Down + and #1 + beq not_dn + ldx sprite + cpx #223 ; No going past the bottom of the screen. + beq not_dn + inx + stx sprite +not_dn: rts ; Ignore left and right, we don't use 'em + +reverse_dx: + lda #$FF + eor dx + clc + adc #$01 + sta dx + jsr low_c + rts + +scroll_screen: + ldx #$00 ; Reset VRAM + stx $2006 + stx $2006 + + ldx scroll ; Do we need to scroll at all? + beq no_scroll + dex + stx scroll + lda #$00 + sta $2005 ; Write 0 for Horiz. Scroll value + stx $2005 ; Write the value of 'scroll' for Vert. Scroll value + +no_scroll: + rts + +low_c: + pha + lda #$84 + sta $4000 + lda #$AA + sta $4002 + lda #$09 + sta $4003 + pla + rts + +high_c: + pha + lda #$86 + sta $4000 + lda #$69 + sta $4002 + lda #$08 + sta $4003 + pla + rts + +vblank: jsr scroll_screen + jsr update_sprite + jsr react_to_input +irq: rti + +; palette data +palette: +.byte $0E,$00,$0E,$19,$00,$00,$00,$00,$00,$00,$00,$00,$01,$00,$01,$21 +.byte $0E,$20,$22,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 + +; Background data +bg: +.byte " " +.byte "12345678901234567890123456789012" +.byte " " +.byte " " +.byte " PRESENTING NES 101: " +.byte " A GUIDE FOR OTHERWISE " +.byte " EXPERIENCED PROGRAMMERS " +.byte " " +.byte " TUTORIAL FILE BY " +.byte " MICHAEL MARTIN " +.byte " " +.byte " " +.byte " " +.byte " PRESS UP AND DOWN TO SHIFT " +.byte " THE SPRITE " +.byte " " +.byte " PRESS A TO REVERSE DIRECTION" +.byte " " +.byte " " +.byte " " +.byte " " +.byte " " +.byte " " +.byte "CHARACTER SET HIJACKED FROM " +.byte "COMMODORE BUSINESS MACHINES " +.byte " (C64'S CHARACTER ROM)" +.byte " " +.byte "READY. " +.byte " " +.byte " " +; Attribute table +.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.byte $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00 +.byte $00,$00,$00,$00,$00,$00,$00,$00,$F0,$F0,$F0,$F0,$F0,$F0,$F0,$F0 +.byte $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF,$0F,$0F,$0F,$0F,$0F,$0F,$0F,$0F + +.pad $FFFA +.word vblank, reset, irq diff --git a/tvpassfail/README.txt b/tvpassfail/README.txt new file mode 100644 index 0000000..4efda2a --- /dev/null +++ b/tvpassfail/README.txt @@ -0,0 +1,51 @@ +TV pass or fail? + +This program is designed for NES and tests various aspects of the +display it is connected to. Press the A Button to switch screens. + +_____________________________________________________________________ +NTSC chroma/luma crosstalk + +The PPU in the PlayChoice arcade system generates RGB video, with +red, green, and blue color information on separate cables. + +The PPU in the NES generates composite video, with chroma (color) +and luma (brightness) information carried on one cable at different +frequency bands. To keep the circuit cheap, it does not perform +proper filtering to keep the chroma from bleeding into the luma. +This especially has an effect on 45 degree diagonal lines. But an +accurate emulator must preserve the same artifacts, as games such as +Blaster Master rely on them to create the richest color palette. + +This screen displays something noticeably different on an NTSC NES +PPU vs. the RGB PPU that most PC based NES emulators emulate. + +Display on RGB system: Display on NTSC system: +,---------. ,---------. +| ===== | | ===== | +|%%%%%%%%%| | PASS! | +| | | | +| PRESS A | | PRESS A | +`---------' `---------' + +_____________________________________________________________________ +Pixel aspect ratio + +PC displays most commonly generate square pixels. A square pixel +on an NTSC display is 7/12 of a chroma cycle wide, but the NES PPU +did not generate square pixels. Instead, it generated pixels 8/12 +of a chroma cycle wide, which are somewhat wider than they are tall. +This made games' graphics appear stretched. If they are displayed +with square pixels on a PC based emulator, graphics will not appear +with the intended proportions. + +This screen shows three rectangles. One is a square on NTSC NES +and PlayChoice, one is a square on PAL NES, and one is a square +with square pixels. + +_____________________________________________________________________ +Legal + +Copyright 2007 Damian Yerrick +Do not distribute this quick and dirty preview version to the public +until it has been tested on an NES. diff --git a/tvpassfail/health.nam b/tvpassfail/health.nam new file mode 100644 index 0000000..f627353 Binary files /dev/null and b/tvpassfail/health.nam differ diff --git a/tvpassfail/mk.bat b/tvpassfail/mk.bat new file mode 100644 index 0000000..d85f952 --- /dev/null +++ b/tvpassfail/mk.bat @@ -0,0 +1,7 @@ +\nesdev\cc65\bin\ca65 tv.s +if errorlevel 1 goto End +\nesdev\cc65\bin\ld65 tv.o -C nes.ini -o tv.prg --mapfile map.txt +if errorlevel 1 goto End +copy /b tv.prg+tv.chr tv.nes +tv.nes +:End \ No newline at end of file diff --git a/tvpassfail/nes.ini b/tvpassfail/nes.ini new file mode 100644 index 0000000..b10c88e --- /dev/null +++ b/tvpassfail/nes.ini @@ -0,0 +1,23 @@ +MEMORY { + ZP: start = $10, size = $f0, type = rw; + # use first $10 zeropage locations as locals + HEADER: start = $7f00, size = $0010, type = ro, file = %O; + RAM: start = $0300, size = $0500, type = rw; + ROM: start = $C000, size = $8000, type = ro, file = %O; +} + +SEGMENTS { + INESHDR: load = HEADER, type = ro, align = $10; + ZEROPAGE: load = ZP, type = zp; + DATA: load = ROM, run = RAM, type = rw, define = yes, align = $100; + BSS: load = RAM, type = bss, define = yes, align = $100; + CODE: load = ROM, type = ro, align = $100; + RODATA: load = ROM, type = ro, align = $100; + DMC: load = ROM, type = ro, align = $40; + VECTORS: load = ROM, type = ro, start = $FFFA; +} + +FILES { + %O: format = bin; +} + diff --git a/tvpassfail/palsquare.nam b/tvpassfail/palsquare.nam new file mode 100644 index 0000000..2921b2b Binary files /dev/null and b/tvpassfail/palsquare.nam differ diff --git a/tvpassfail/square.nam b/tvpassfail/square.nam new file mode 100644 index 0000000..72b106d Binary files /dev/null and b/tvpassfail/square.nam differ diff --git a/tvpassfail/square.pal b/tvpassfail/square.pal new file mode 100644 index 0000000..d0c53aa Binary files /dev/null and b/tvpassfail/square.pal differ diff --git a/tvpassfail/tiled.pal b/tvpassfail/tiled.pal new file mode 100644 index 0000000..c5a07b1 Binary files /dev/null and b/tvpassfail/tiled.pal differ diff --git a/tvpassfail/tv.chr b/tvpassfail/tv.chr new file mode 100644 index 0000000..b398a3d Binary files /dev/null and b/tvpassfail/tv.chr differ diff --git a/tvpassfail/tv.nam b/tvpassfail/tv.nam new file mode 100644 index 0000000..c069bb9 Binary files /dev/null and b/tvpassfail/tv.nam differ diff --git a/tvpassfail/tv.nes b/tvpassfail/tv.nes new file mode 100644 index 0000000..4ccf909 Binary files /dev/null and b/tvpassfail/tv.nes differ diff --git a/tvpassfail/tv.pal b/tvpassfail/tv.pal new file mode 100644 index 0000000..052b79f Binary files /dev/null and b/tvpassfail/tv.pal differ diff --git a/tvpassfail/tv.s b/tvpassfail/tv.s new file mode 100644 index 0000000..7e391f4 --- /dev/null +++ b/tvpassfail/tv.s @@ -0,0 +1,361 @@ +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +PPUSCROLL = $2005 +PPUADDR = $2006 +PPUDATA = $2007 + +P1 = $4016 +P2 = $4017 + + +.segment "ZEROPAGE" +retraces: .res 3 +isPAL: .res 1 +palstate: .res 1 +joy1: .res 1 +last_joy1: .res 1 +joy1new: .res 1 + +.segment "VECTORS" + .addr nmi, reset, irq + +.segment "INESHDR" + + .byt "NES", 26 + .byt 1 ; number of 16 KB program segments + .byt 1 ; number of 8 KB chr segments + .byt 0 ; mapper, mirroring, etc + .byt 0 ; extended mapper info + .byt 0,0,0,0,0,0,0,0 ; f you DiskDude + +.segment "CODE" +nmi: + inc retraces +irq: + rti + +reset: + sei + lda #0 + sta PPUCTRL + sta PPUMASK + cld + ldy #$40 + sty P2 + + ldx #$FF + txs + +@warmup1: + bit PPUSTATUS + bpl @warmup1 + +; we have nearly 29000 cycles to init other parts of the NES +; so do it while waiting for the PPU to signal that it's warming up + +@clearZP: + sta $00,x + dex + bne @clearZP + +; done with tasks; wait for warmup + +@warmup2: + bit PPUSTATUS + bpl @warmup2 + + jsr detectPAL + + +; Display television test + +; Set palette + ldx #0 + stx PPUMASK + jsr setGrayPalette + +.segment "RODATA" +healthScreen: + .incbin "health.nam" +.segment "CODE" + ldx #healthScreen + lda #$20 + jsr copyNT + jsr displayLoop + + + + + +tvTest: + + ; Skip this test on PAL + lda isPAL + beq :+ + jmp aspectRatioTest +: + +; Set up initial palette + ldx #0 + stx PPUMASK + ldy #$3F + sty PPUADDR + stx PPUADDR + sty PPUDATA + sty PPUDATA + sty PPUDATA + sty PPUDATA + sty PPUDATA + stx PPUDATA + lda #$10 + sta PPUDATA + lda #$30 + sta PPUDATA + + +; Set up test screen +.segment "RODATA" +tvScreen: + .incbin "tv.nam" +.segment "CODE" + ldx #tvScreen + lda #$20 + jsr copyNT + +tvLoop: + ; wait for vertical blanking NMI + lda PPUSTATUS + lda #$80 + sta PPUCTRL + lda retraces +: + cmp retraces + beq :- + lda PPUSTATUS ; acknowledge NMI + +; Rewrite palette + lda #0 + sta PPUMASK + inc palstate + lda palstate + cmp #192 + bcc :+ + lda #0 + sta palstate +: + lsr a + lsr a + lsr a + lsr a + clc + adc #$21 + + ldy #$3F + sty PPUADDR + ldy #1 + sty PPUADDR + + sta PPUDATA + clc + adc #4 + cmp #$2D + bcc :+ + sbc #$0C +: + sta PPUDATA + clc + adc #4 + cmp #$2D + bcc :+ + sbc #$0C +: + sta PPUDATA + +; Turn on screen + lda #$00 + sta PPUSCROLL + sta PPUSCROLL + lda #$80 + sta PPUCTRL + lda #%00001010 + sta PPUMASK + +; Read controller + jsr readPad + lda joy1new + and #$90 + beq tvLoop + + +aspectRatioTest: +; Set palette + ldx #0 + stx PPUMASK + jsr setGrayPalette + +.segment "RODATA" +ntscAspectScreen: + .incbin "square.nam" +palAspectScreen: + .incbin "palsquare.nam" +.segment "CODE" + + lda isPAL + beq @copyNTSC + ldx #palAspectScreen + bne @gotoCopy +@copyNTSC: + ldx #ntscAspectScreen +@gotoCopy: + lda #$20 + jsr copyNT + +aspectLoop: + jsr displayLoop + jmp tvTest + + + + + + + + +.proc setGrayPalette + ldy #$3F + ldx #0 + sty PPUADDR + stx PPUADDR + sty PPUDATA + stx PPUDATA + lda #$10 + sta PPUDATA + lda #$30 + sta PPUDATA + rts +.endproc + + +;;; +; Runs the game loop for one display, +; waiting for the player to press A. +.proc displayLoop + ; wait for vertical blanking NMI + lda PPUSTATUS + lda #$80 + sta PPUCTRL + lda retraces + +waitForNMI: + cmp retraces + beq waitForNMI + lda PPUSTATUS ; acknowledge NMI + +; Turn on screen + lda #$00 + sta PPUSCROLL + sta PPUSCROLL + lda #$80 + sta PPUCTRL + lda #%00001010 + sta PPUMASK + +; Read controller + jsr readPad + lda joy1new + and #$90 + beq displayLoop + + rts +.endproc + +;;; +; Copies a nametable data from PRG ROM to VRAM. +; @param X bits 7-0 of source address +; @param Y bits 15-8 of source address +; @param A bits 15-8 of destination address +copyNT: + stx 0 + sty 1 + sta PPUADDR + ldy #0 + sty PPUADDR + ldx #4 + ldx #4 +@loop: + lda (0),y + sta PPUDATA + iny + bne @loop + inc 1 + dex + bne @loop + rts + +readPad: +; Strobe the joypad and set up the last frame joy state. + lda #1 + sta P1 + lda joy1 + sta last_joy1 + ldx #8 + lda #0 + sta P1 + +; Read the joypad out serially. +@loop: + lda P1 + lsr a + rol joy1 + dex + bne @loop + +; Compute which buttons were newly pressed. + lda last_joy1 + eor #$FF + and joy1 + sta joy1new + rts + +;;; +; Sets isPAL to nonzero if the machine is on 50 Hz timing. +.proc detectPAL + + lda PPUSTATUS ; acknowledge any pending NMI + + ; wait for vblank with the screen turned off + lda #$80 + sta PPUCTRL + asl a + sta PPUMASK + lda retraces +: + cmp retraces + beq :- + lda retraces + + ; wait 24 * 1285 cycles, longer than the NTSC + ; frame period but shorter than that of PAL + ldx #24 + ldy #0 +: + dey + bne :- + dex + bne :- + + ; compare to retrace count before waiting + ; if we've had a vblank, then we're NTSC + ; otherwise we're PAL + cmp retraces + bne notPAL + lda #1 + sta isPAL +notPAL: + rts +.endproc diff --git a/tvpassfail/tvpassfail.zip b/tvpassfail/tvpassfail.zip new file mode 100644 index 0000000..3aab0d2 Binary files /dev/null and b/tvpassfail/tvpassfail.zip differ diff --git a/vbl_nmi_timing/1.frame_basics.nes b/vbl_nmi_timing/1.frame_basics.nes new file mode 100644 index 0000000..8eece87 Binary files /dev/null and b/vbl_nmi_timing/1.frame_basics.nes differ diff --git a/vbl_nmi_timing/2.vbl_timing.nes b/vbl_nmi_timing/2.vbl_timing.nes new file mode 100644 index 0000000..3a45ec8 Binary files /dev/null and b/vbl_nmi_timing/2.vbl_timing.nes differ diff --git a/vbl_nmi_timing/3.even_odd_frames.nes b/vbl_nmi_timing/3.even_odd_frames.nes new file mode 100644 index 0000000..535cb44 Binary files /dev/null and b/vbl_nmi_timing/3.even_odd_frames.nes differ diff --git a/vbl_nmi_timing/4.vbl_clear_timing.nes b/vbl_nmi_timing/4.vbl_clear_timing.nes new file mode 100644 index 0000000..939f452 Binary files /dev/null and b/vbl_nmi_timing/4.vbl_clear_timing.nes differ diff --git a/vbl_nmi_timing/5.nmi_suppression.nes b/vbl_nmi_timing/5.nmi_suppression.nes new file mode 100644 index 0000000..5677690 Binary files /dev/null and b/vbl_nmi_timing/5.nmi_suppression.nes differ diff --git a/vbl_nmi_timing/6.nmi_disable.nes b/vbl_nmi_timing/6.nmi_disable.nes new file mode 100644 index 0000000..75a3a2c Binary files /dev/null and b/vbl_nmi_timing/6.nmi_disable.nes differ diff --git a/vbl_nmi_timing/7.nmi_timing.nes b/vbl_nmi_timing/7.nmi_timing.nes new file mode 100644 index 0000000..f777a9c Binary files /dev/null and b/vbl_nmi_timing/7.nmi_timing.nes differ diff --git a/vbl_nmi_timing/readme.txt b/vbl_nmi_timing/readme.txt new file mode 100644 index 0000000..792aa53 --- /dev/null +++ b/vbl_nmi_timing/readme.txt @@ -0,0 +1,121 @@ +NTSC NES PPU VBL/NMI Timing Tests +--------------------------------- +These ROMs test the timing of the VBL flag and NMI to an accuracy of a +single PPU clock, and also check special cases. They have been tested on +an actual NES and all give a passing result. Sometimes the NES starts up +with a different PPU timing that causes some of the tests to fail; these +tests don't check that timing arrangement. + +Each ROM runs several tests and reports the result on screen and by +beeping a number of times. See below for the meaning of failure codes +for each test. It's best to run the tests in order, because later ROMs +depend on things tested by earlier ROMs and will give erroneous results +if any earlier ones failed. + +Source code for each test is included, and most tests are clearly +divided into sections. Support code is also included, but it runs on a +custom devcart and assembler so it will require some effort to assemble. +Contact me if you'd like assistance porting them to your setup. + + +1.frame_basics +-------------- +Tests basic VBL flag operation and general timing of PPU frames. + +2) VBL flag isn't being set +3) VBL flag should be cleared after being read +4) PPU frame with BG enabled is too short +5) PPU frame with BG enabled is too long +6) PPU frame with BG disabled is too short +7) PPU frame with BG disabled is too long + + +2.vbl_timing +------------ +Tests timing of VBL being set, and special case where reading VBL flag +as it would be set causes it to not be set for that frame. + +2) Flag should read as clear 3 PPU clocks before VBL +3) Flag should read as set 0 PPU clocks after VBL +4) Flag should read as clear 2 PPU clocks before VBL +5) Flag should read as set 1 PPU clock after VBL +6) Flag should read as clear 1 PPU clock before VBL +7) Flag should read as set 2 PPU clocks after VBL +8) Reading 1 PPU clock before VBL should suppress setting + + +3.even_odd_frames +----------------- +Test clock skipped when BG is enabled on odd PPU frames. Tests +enable/disable BG during 5 consecutive frames, then see how many clocks +were skipped. Patterns are shown as XXXXX, where each X can either be B +(BG enabled) or - (BG disabled). + +2) Pattern ----- should not skip any clocks +3) Pattern BB--- should skip 1 clock +4) Pattern B--B- (one even, one odd) should skip 1 clock +5) Pattern -B--B (one odd, one even) should skip 1 clock +6) Pattern BB-BB (two pairs) should skip 2 clocks + + +4.vbl_clear_timing +------------------ +Tests timing of VBL flag clearing. + +2) Cleared 3 or more PPU clocks too early +3) Cleared 2 PPU clocks too early +4) Cleared 1 PPU clock too early +5) Cleared 3 or more PPU clocks too late +6) Cleared 2 PPU clocks too late +7) Cleared 1 PPU clock too late + + +5.nmi_suppression +----------------- +Tests timing of NMI suppression when reading VBL flag just as it's set, +and that this doesn't occur when reading one clock before or after. + +2) Reading flag 3 PPU clocks before set shouldn't suppress NMI +3) Reading flag when it's set should suppress NMI +4) Reading flag 3 PPU clocks after set shouldn't suppress NMI +5) Reading flag 2 PPU clocks before set shouldn't suppress NMI +6) Reading flag 1 PPU clock after set should suppress NMI +7) Reading flag 4 PPU clocks after set shouldn't suppress NMI +8) Reading flag 4 PPU clocks before set shouldn't suppress NMI +9) Reading flag 1 PPU clock before set should suppress NMI +10)Reading flag 2 PPU clocks after set shouldn't suppress NMI + +432101234 +---+?+--- + + +6.nmi_disable +------------- +Tests NMI occurrence when disabling NMI just as VBL flag is set, and +just after. + +2) NMI shouldn't occur when disabled 0 PPU clocks after VBL +3) NMI should occur when disabled 3 PPU clocks after VBL +4) NMI shouldn't occur when disabled 1 PPU clock after VBL +5) NMI should occur when disabled 4 PPU clocks after VBL +6) NMI shouldn't occur when disabled 1 PPU clock before VBL +7) NMI should occur when disabled 2 PPU clocks after VBL + + +7.nmi_timing +------------ +Tests timing of NMI and immediate occurrence when enabled with VBL flag +already set. + +2) NMI occurred 3 or more PPU clocks too early +3) NMI occurred 2 PPU clocks too early +4) NMI occurred 1 PPU clock too early +5) NMI occurred 3 or more PPU clocks too late +6) NMI occurred 2 PPU clocks too late +7) NMI occurred 1 PPU clock too late +8) NMI should occur if enabled when VBL already set +9) NMI enabled when VBL already set should delay 1 instruction +10)NMI should be possible multiple times in VBL + +-- +Shay Green (swap to e-mail) diff --git a/vbl_nmi_timing/source/1.frame_basics.a b/vbl_nmi_timing/source/1.frame_basics.a new file mode 100644 index 0000000..f91695b --- /dev/null +++ b/vbl_nmi_timing/source/1.frame_basics.a @@ -0,0 +1,104 @@ +; Tests basic VBL flag operation and general timing of PPU frames. + + .include "prefix_ppu.a" + +test_name: + .db "PPU FRAME BASICS",0 + +delay_119121: + ldy #100 ; 119118 delay + lda #237 + jmp delay_ya1 + +begin_and_wait: +: bit $2002 + bpl - + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + jsr delay_119121 + rts + +reset: + jsr begin_ppu_test + + jsr wait_vbl + ldy #25 ; 29800 delay + lda #237 + jsr delay_ya8 + lda #2;) VBL flag isn't being set + lda $2002 + and #$80 + jsr error_if_eq + + lda #3;) VBL flag should be cleared after being read + lda $2002 + and #$80 + jsr error_if_ne + + jsr wait_vbl + lda #$08 ; 6 enable bg + sta $2001 + jsr begin_and_wait + ldx $2002 + nop + nop + ldy $2002 + + lda #4;) PPU frame with BG enabled is too short + sta result + txa + and #$80 + jsr error_if_ne + + lda #5;) PPU frame with BG enabled is too long + sta result + tya + and #$80 + jsr error_if_eq + + jsr wait_vbl + lda #$00 ; 6 disable bg + sta $2001 + jsr begin_and_wait + nop + nop + nop + nop + nop + nop + nop + ldx $2002 + nop + nop + ldy $2002 + + lda #6;) PPU frame with BG disabled is too short + sta result + txa + and #$80 + jsr error_if_ne + + lda #7;) PPU frame with BG disabled is too long + sta result + tya + and #$80 + jsr error_if_eq + + jmp tests_passed diff --git a/vbl_nmi_timing/source/2.vbl_timing.a b/vbl_nmi_timing/source/2.vbl_timing.a new file mode 100644 index 0000000..4563904 --- /dev/null +++ b/vbl_nmi_timing/source/2.vbl_timing.a @@ -0,0 +1,74 @@ +; Tests timing of VBL being set, and special case where reading VBL flag +; as it would be set causes it to not be set for that frame. + + .include "prefix_ppu.a" + +test_name: + .db "VBL TIMING",0 + +test_too_soon: + jsr delay_20 + lda $2002 + and #$80 + jsr error_if_ne + lda $2002 + and #$80 + jsr error_if_eq + rts + +test_too_late: + jsr delay_21 + lda $2002 + and #$80 + jsr error_if_eq + lda $2002 + and #$80 + jsr error_if_ne + rts + .code + +reset: + jsr begin_ppu_test + + lda #2;) Flag should read as clear 3 PPU clocks before VBL + sta $@ diff --git a/volume_tests/nes.ini b/volume_tests/nes.ini new file mode 100644 index 0000000..6f47490 --- /dev/null +++ b/volume_tests/nes.ini @@ -0,0 +1,22 @@ +MEMORY { + ZP: start = $10, size = $f0, type = rw; + # use first $10 zeropage locations as locals + HEADER: start = $0000, size = $0010, type = ro, file = %O, fill=yes, fillval=$00; + RAM: start = $0300, size = $0500, type = rw; + ROM: start = $C000, size = $4000, type = ro, file = %O, fill=yes, fillval=$FF; +} + +SEGMENTS { + INESHDR: load = HEADER, type = ro, align = $10; + ZEROPAGE: load = ZP, type = zp; + BSS: load = RAM, type = bss, define = yes, align = $100; + CODE: load = ROM, type = ro, align = $100; + RODATA: load = ROM, type = ro, align = $100; + DMC: load = ROM, type = ro, start = $C000, optional = yes; + VECTORS: load = ROM, type = ro, start = $FFFA; +} + +FILES { + %O: format = bin; +} + diff --git a/volume_tests/obj/nes/khan b/volume_tests/obj/nes/khan new file mode 100644 index 0000000..e69de29 diff --git a/volume_tests/recordings/fceux.ogg b/volume_tests/recordings/fceux.ogg new file mode 100644 index 0000000..0b2f667 Binary files /dev/null and b/volume_tests/recordings/fceux.ogg differ diff --git a/volume_tests/recordings/nes-001.ogg b/volume_tests/recordings/nes-001.ogg new file mode 100644 index 0000000..d7b9be0 Binary files /dev/null and b/volume_tests/recordings/nes-001.ogg differ diff --git a/volume_tests/recordings/nestopia.ogg b/volume_tests/recordings/nestopia.ogg new file mode 100644 index 0000000..da60997 Binary files /dev/null and b/volume_tests/recordings/nestopia.ogg differ diff --git a/volume_tests/recordings/nintendulator.ogg b/volume_tests/recordings/nintendulator.ogg new file mode 100644 index 0000000..d681bce Binary files /dev/null and b/volume_tests/recordings/nintendulator.ogg differ diff --git a/volume_tests/src/hello.s b/volume_tests/src/hello.s new file mode 100644 index 0000000..024b33c --- /dev/null +++ b/volume_tests/src/hello.s @@ -0,0 +1,126 @@ +; Copyright (c) 2009 Damian Yerrick +; +; This work is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any +; damages arising from the use of this work. +; +; Permission is granted to anyone to use this work for any +; purpose, including commercial applications, and to alter it and +; redistribute it freely, subject to the following restrictions: +; +; 1. The origin of this work must not be misrepresented; you +; must not claim that you wrote the original work. If you use +; this work in a product, an acknowledgment in the product +; documentation would be appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, +; and must not be misrepresented as being the original work. +; 3. This notice may not be removed or altered from any +; source distribution. +; +; The term "source" refers to the preferred form of a work for making +; changes to it. + +.p02 + +PPUCTRL = $2000 +PPUMASK = $2001 +PPUSTATUS = $2002 +PPUADDR = $2006 +PPUDATA = $2007 +OAM_DMA = $4014 +P1 = $4016 +P2 = $4017 + +.exportzp cur_keys, new_keys, retraces +.import read_pads +.import init_sound, volume_test + +.segment "INESHDR" + .byt "NES", $1a + .byt 1, 1, 0, 0 + .res 8 + +.segment "ZEROPAGE" + +cur_keys: .res 2 +new_keys: .res 2 +retraces: .res 1 +psg_sfx_state: .res 16 + + +.segment "CODE" +nmi: + inc retraces +irq: + rti + +reset: + sei + ldx #0 + stx PPUCTRL + stx PPUMASK + dex + txs + lda #$40 + sta P2 + bit PPUSTATUS + cld ; no effect on NES; helps in generic 6502 debuggers + + jsr init_sound +@waitvbl1: + bit PPUSTATUS + bpl @waitvbl1 + lda #0 + tax +@clrram: + sta 0,x + sta $100,x + sta $300,x + sta $400,x + sta $500,x + sta $600,x + sta $700,x + inx + bne @clrram +@waitvbl2: + bit PPUSTATUS + bpl @waitvbl2 + + ; write palette + lda #$3F + sta PPUADDR + ldx #$00 + stx PPUADDR + lda #$0D + sta PPUDATA + lda #$1A + sta PPUDATA + lda #$2A + sta PPUDATA + lda #$3A + sta PPUDATA + stx PPUADDR + stx PPUADDR + + lda #%10000000 + sta PPUCTRL + +forever: + lda retraces +: + cmp retraces + beq :- + jsr read_pads + + lda new_keys + bpl @not_p1A + jsr volume_test +@not_p1A: + + jmp forever + + + + +.segment "VECTORS" + .addr nmi, reset, irq \ No newline at end of file diff --git a/volume_tests/src/pads.s b/volume_tests/src/pads.s new file mode 100644 index 0000000..d3ee979 --- /dev/null +++ b/volume_tests/src/pads.s @@ -0,0 +1,81 @@ +; Copyright (c) 2009 Damian Yerrick +; +; This work is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any +; damages arising from the use of this work. +; +; Permission is granted to anyone to use this work for any +; purpose, including commercial applications, and to alter it and +; redistribute it freely, subject to the following restrictions: +; +; 1. The origin of this work must not be misrepresented; you +; must not claim that you wrote the original work. If you use +; this work in a product, an acknowledgment in the product +; documentation would be appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, +; and must not be misrepresented as being the original work. +; 3. This notice may not be removed or altered from any +; source distribution. +; +; The term "source" refers to the preferred form of a work for making +; changes to it. + + +JOY1 = $4016 +JOY2 = $4017 + + +.export read_pads +.importzp cur_keys, new_keys +read_pads: + + ; store the current keypress state to detect key-down later + lda cur_keys + sta 4 + lda cur_keys+1 + sta 5 + + ; read the joypads twice + jsr read_pads_once + lda 0 + sta 2 + lda 1 + sta 3 + jsr read_pads_once + + ldx #1 +@fixupKeys: + + ; if the player's keys read out the same ways both times, update + lda 0,x + cmp 2,x + bne @dontUpdateGlitch + sta cur_keys,x +@dontUpdateGlitch: + + lda 4,x ; A = keys that were down last frame + eor #$FF ; A = keys that were up last frame + and cur_keys,x ; A = keys down now and up last frame + sta new_keys,x + dex + bpl @fixupKeys + rts + +read_pads_once: + lda #1 + sta 0 + sta 1 + sta JOY1 + lda #0 + sta JOY1 + loop: + lda JOY1 + and #$03 + cmp #1 + rol 0 + lda JOY2 + and #$03 + cmp #1 + rol 1 + bcc loop + rts diff --git a/volume_tests/src/sound.s b/volume_tests/src/sound.s new file mode 100644 index 0000000..a52509d --- /dev/null +++ b/volume_tests/src/sound.s @@ -0,0 +1,285 @@ +; Copyright (c) 2009 Damian Yerrick +; +; This work is provided 'as-is', without any express or implied +; warranty. In no event will the authors be held liable for any +; damages arising from the use of this work. +; +; Permission is granted to anyone to use this work for any +; purpose, including commercial applications, and to alter it and +; redistribute it freely, subject to the following restrictions: +; +; 1. The origin of this work must not be misrepresented; you +; must not claim that you wrote the original work. If you use +; this work in a product, an acknowledgment in the product +; documentation would be appreciated but is not required. +; 2. Altered source versions must be plainly marked as such, +; and must not be misrepresented as being the original work. +; 3. This notice may not be removed or altered from any +; source distribution. +; +; The term "source" refers to the preferred form of a work for making +; changes to it. + + +.export init_sound, volume_test +.importzp retraces + +SNDCHN = $4015 + +.segment "ZEROPAGE" +level4011: .res 1 + +.segment "CODE" + +;; +; Initializes all sound channels. +; +.proc init_sound + ldx #$00 + stx SNDCHN + lda #$0F + sta SNDCHN + lda #$30 + sta $4000 + sta $4004 + sta $400C + lda #8 + sta $4001 + sta $4005 + stx $4003 + stx $4007 + stx $400F + stx $4011 + rts +.endproc + +;; +.proc volume_test + ; 1. square wave 1 + ; 2. square wave 1 + 2 + ; 3. triangle + ; 4. noise + ; 5. raw dac + + lda #0 + sta level4011 + sta $4011 + ldy #4 + jsr wait_frames + + loop4011: + lda level4011 + sta $4011 + + ldy #0 + jsr test_square_waves + ldy #1 + jsr test_square_waves + jsr test_triangle_wave + ldy #2 + jsr test_square_waves + jsr test_raw_4011 + + lda level4011 + clc + adc #$30 + bmi done + tax + jsr ramp_4011_to_x + jmp loop4011 + done: + + rts +.endproc + +.proc ramp_4011_to_x + cpx level4011 + beq done + bcc done + inc level4011 + lda level4011 + sta $4011 + ldy #200 + : + dey + bne :- + jmp ramp_4011_to_x +done: + rts +.endproc + + +;; +; y: nonzero if using $4004; zero if only using $4000 +.proc test_square_waves + +mask4000 = 0 +mask4004 = 1 +mask400C = 2 +volumeCountdown = 3 +duty = 4 + + cpy #1 + beq mode1 + cpy #2 + beq mode2 + + ; mode 0: one square wave, four duty cycles + ldy #$FF + sty mask4000 + iny + sty mask4004 + sty mask400C + jmp begin_test + +mode1: + ; two unison square waves, four duty cycles + ldy #$FF + sty mask4000 + sty mask4004 + iny + sty mask400C + jmp begin_test + +mode2: + ; one noise wave, two duty cycles + ldy #$FF + sty mask400C + iny + sty mask4000 + sty mask4004 + +begin_test: + lda #$30 + sta duty + doOneDuty: + lda #15 + sta volumeCountdown + lda #$30 + sta $4000 + sta $4004 + sta $400C + lda #111 + sta $4002 + sta $4006 + lda duty + ora #$02 + sta $400E + lda #0 + sta $4003 + sta $4007 + sta $400F + + ldy #8 + jsr wait_frames + + doOneVolume: + lda volumeCountdown + ora duty + and mask4000 + sta $4000 + lda volumeCountdown + ora duty + and mask4004 + sta $4004 + lda volumeCountdown + ora #$30 + and mask400C + sta $400C + ldy #4 + jsr wait_frames + dec volumeCountdown + bpl doOneVolume + + ; go to next duty + ; in mode 0, 1: $30, $70, $B0, $F0 + ; in mode 2: $30, $F0 + lda mask400C + and #$80 + ora duty + clc + adc #$40 + sta duty + bcc doOneDuty + rts +.endproc + +.proc test_triangle_wave + lda #$00 + sta $4008 + ldy #8 + jsr wait_frames + lda #$81 + sta $4008 + lda #55 + sta $400A + lda #0 + sta $400B + ldy #60 + jsr wait_frames + lda #0 + sta $4008 + ldy #4 + jsr wait_frames + rts +.endproc + +.proc test_raw_4011 + lda #<(65536-997) + sta 0 + lda #>(65536-997) + sta 1 + lda level4011 + sta $4011 + + clc + adc #30 + sta 2 + ldy #8 + jsr wait_frames + loop: + + ; The top and bottom halves of this wave are 896 cycles each. + ; TOP HALF: 7 to move speaker, 889 to waste + ldx 2 + stx $4011 + ldy #890/5 + : + dey + bne :- + + ; BOTTOM HALF: 7 to move speaker, 868 to waste, 21 to loop + lda level4011 + sta $4011 + ldy #865/5 + : + dey + bne :- + lsr a + lsr a + sec + lda #0 + adc 0 + sta 0 + lda #0 + adc 1 + sta 1 + bcc loop + + ldy #20 + jsr wait_frames + +waste12: + rts +.endproc + + +.proc wait_frames + lda retraces +: + cmp retraces + beq :- + dey + bne wait_frames + rts +.endproc diff --git a/volume_tests/volumes.nes b/volume_tests/volumes.nes new file mode 100644 index 0000000..774061d Binary files /dev/null and b/volume_tests/volumes.nes differ diff --git a/volume_tests/zip.in b/volume_tests/zip.in new file mode 100644 index 0000000..ac53f13 --- /dev/null +++ b/volume_tests/zip.in @@ -0,0 +1,14 @@ +obj/nes/khan +empty.chr +makefile +nes.ini +volumes.nes +recordings/nes-001.ogg +recordings/nestopia.ogg +recordings/nintendulator.ogg +recordings/fceux.ogg +src/pads.s +src/hello.s +src/sound.s +README.txt +zip.in \ No newline at end of file diff --git a/window5/colorwin.asm b/window5/colorwin.asm new file mode 100644 index 0000000..f22afe2 --- /dev/null +++ b/window5/colorwin.asm @@ -0,0 +1,488 @@ +**** PRIOR DEFINITIONS **** + +;.define PAL +.include "maping.asm" ;This tells the ROM definition for the CNROM hardware + +.asctable +MAP "0" TO "9" = $10 +MAP "A" TO "Z" = $20 +MAP "=" = $1c +MAP "?" = $1e +MAP " " = $1f +.enda + +.ramsection "NMIVars" SLOT 0 + ;Thoose variables are used in the NMI routine + ;Don't do calculations with them if a VBlank is pending + ;set them after the calculation +SpriteDMAFlag db ;This tells if sprite DMA is ready in a frame +PalFlag db ;Check if palette uploaded is needed +NMIFlag db ;Check if a NMI is pending or not +ScrollH db +ScrollV db +M2000 db +FrameCounter db +NMITemp db +NMITemp2 db +NMITemp3 db +NMITemp4 db +nmi_sync_count db +.ends + +.struct SpriteDMA ;Structure for the Sprites +PosV db +TileNmr db +Palette db +PosH db +.endst + ;$200-$2ff is reserved for sprite DMA +.enum $200 +SpriteBuffer INSTANCEOF SpriteDMA 64 +.ende + +.ramsection "RAM Code" SLOT 1 + +RAMTimedCode dsb 39+5 + +.ends + +; Delays Delay cycles, up to around 65536. Delay must be >= 45. +; Preserved: X, Y +.macro DELAY args Delay + lda #<((Delay) - 2 - 25 - 2 - 16) + jsr delay_a_25_cycles + lda #>((Delay) - 2 - 25 - 2 - 16) + jsr delay_256a_16_cycles +.endm + + +****** ROM aera begins here ******* + +.bank 0 SLOT 2 +.orga $c000 + +*********************** +*** Wait for a VBlank *** +*********************** + +.section "WaitNMI" FREE +WaitNMI + inc NMIFlag ;Typical wait for next frame +- lda NMIFlag + beq + + jmp - ++ rts +.ends + +.section "NMIRoutine" FREE + +NMI + pha + txa + pha + tya + pha ;13 + lda #$00 + sta SpriteDMAFlag ;17 ;Sprite buffer is read + sta $2003.w ;21 + jsr begin_nmi_sync + lda #$02 ;23 + sta $4014.w ;36 ;Do sprite DMA at $200 + lda ScrollH ;39 + sta $2005.w ;43 + lda ScrollV ;46 + sta $2005.w ;50 + lda M2000 ;53 + sta $2000.w ;57 + lda #$00 ;59 ;Clears the flag to stop waiting the frame + sta NMIFlag ;62 + + DELAY 1715 - 22 - 38 + +.ifdef PAL + ; Just delay the extra amount PAL needs in comparison to NTSC + DELAY 6900 - 1715 +.endif + + jsr end_nmi_sync + jsr Effect + jsr FillBlankSprites + + pla ;Restore registers + tay + pla + tax + pla +IRQ rti +.ends + +************** +* VRAM Stuff ** +************** + +.section "PPU" FREE +ClearVRAM + jsr ClrPalette ;This clears *everything* in the PPU + jsr ClrNamTbl ;This should be called only when rendering is off, of course + jmp Clr2ndNamTbl + +ClrPalette + lda #$3f + sta $2006 + ldy #$00 + sty $2006.w +- sta $2007 ;Clear both buffer and the actual palette + iny ;Shall be called when rendering is off + cpy #$20 + bne - + rts + +Clr2ndNamTbl + lda #$28 ;Clears the name tables + bne SetNamAdress +ClrNamTbl + lda #$20 +SetNamAdress + sta $2006 ;Begin at $2000/$2400 (vertical mirroring) + lda #$00 + sta $2006 + lda #$60 + ldx #$1e ;Clears 30 rows +_screenloop + ldy #$20 +_rowloop + sta $2007 + dey + bne _rowloop + dex + bne _screenloop + lda #$00 + ldx #$40 ;Clear attribute table +_attribloop + sta $2007 + dex + bne _attribloop + rts + +ClrSprRam ;Clears the Sprite buffer at $200 + ldx #$00 + beq _ClearRemainingSprites + +FillBlankSprites ;This does just clears the unused sprites in $200-$2ff + lda SpriteDMAFlag + bne _endClrSprRam +_ClearRemainingSprites + lda #$f0 +- sta $200,X + inx + bne - +_endClrSprRam + inc SpriteDMAFlag + rts +.ends + +.section "RESET" FREE + +RESET ;The programm will start here + sei + ldx #$ff + txs ;Init stack pointer + inx + stx $2000.w + stx $2001.w ;Turn off rendering and interrupts + txa +_initRAMloop + sta $0,X + sta $100.w,X + sta $200.w,X + sta $300.w,X + sta $400.w,X + sta $500.w,X + sta $600.w,X + sta $700.w,X + inx + bne _initRAMloop + lda #$40 + sta $4017.w ;Set sound clock + lda #$00 + sta $4015.w +- bit $2002.w + bpl - +- bit $2002.w ;Wait for several VBlanks + bpl - + jsr ClearVRAM ;Clear the whole PPU + jsr ClrSprRam + jsr LoadTextBox + jsr LoadDefaultPal + jsr init_nmi_sync ;intitial sync + lda #$1e + sta $2001 + ldx #38+5 +- lda VeryTimedCode.w,X + sta RAMTimedCode.w,X + dex ;Copy timed code to RAM + bpl - +- jmp - ;endless loop + +LoadDefaultPal + bit $2002 + lda #$3f + sta $2006 + lda #$00 + sta $2006 + ldx #$00 +- lda Pal.w,X + sta $2007 + inx + cpx #$20 + bne - + rts + +Pal + .db $0f, $09, $20, $30 + .db $0f, $0f, $0f, $0f + .db $0f, $0f, $0f, $0f + .db $0f, $0f, $0f, $0f + .db $0f, $0f, $0f, $0f + .db $0f, $0f, $0f, $0f + .db $0f, $0f, $0f, $0f + .db $0f, $0f, $0f, $0f + +LoadTextBox + lda #$22 + sta $2006 + lda #$00 + sta $2006 ;VRAM Adress + ldx #$00 +- lda TextData.w,X + sta $2007 + inx + cpx #$c0 + bne - + rts + +TextData + .db $06, $00 +.rept 28 + .db $01 +.endr + .db $02, $06 + .db $06, $03 + .asc " " + .db $03, $06 + .db $06, $03 + .asc " THIS IS A DUMMY TEXT BOX " + .db $03, $06 + .db $06, $03 + .asc " " + .db $03, $06 + .db $06, $03 + .asc " IS THAT BACKGROUND NICE? " + .db $03, $06 + + .db $06, $04 +.rept 28 + .db $01 +.endr + .db $05, $06 +.ends + +.include "win_timing.asm" + +*** Dirty *** +; Delays A cycles + overhead +; Preserved: X, Y +; Time: A+25 cycles (including JSR and RTS) + +.section "Delay" ALIGN $100 FREE +- sbc #7 ; carry set by CMP +delay_a_25_cycles: + cmp #7 + bcs - ; do multiples of 7 + lsr a ; bit 0 + bcs + ++ ; A=clocks/2, either 0,1,2,3 + beq + ; 0: 5 + lsr a + beq ++ ; 1: 7 + bcc ++ ; 2: 9 ++ bne ++ ; 3: 11 +++ rts ; (thanks to dclxvi for the algorithm) + + +; Delays A*256 cycles + overhead +; Preserved: X, Y +; Time: A*256+16 cycles (including JSR and RTS) +delay_256a_16_cycles + cmp #0 + bne + + rts ++ +- pha + lda #256-19-22 + jsr delay_a_25_cycles + pla + sec + sbc #1 + bne - + rts + +begin_nmi_sync + lda nmi_sync_count + and #$02 + beq + ++ rts + +end_nmi_sync: + lda nmi_sync_count + inc nmi_sync_count + and #$02 + bne + ++ lda $2002 + bmi + ++ bmi + ++ rts +.ends + +.section "Init_Sync" ALIGN $100 FREE + +.ifndef PAL +; Initializes synchronization and enables NMI +; Preserved: X, Y +; Time: 15 frames average, 28 frames max + +init_nmi_sync + ; Disable interrupts and rendering + sei + lda #0 + sta $2000 + sta $2001 + + ; Coarse synchronize + bit $2002 +init_nmi_sync_1 + bit $2002 + bpl init_nmi_sync_1 + + ; Synchronize to odd CPU cycle + sta $4014 + + ; Fine synchronize + lda #3 +init_nmi_sync_2 + sta nmi_sync_count + bit $2002 + bit $2002 + php + eor #$02 + nop + nop + plp + bpl init_nmi_sync_2 + + ; Delay one frame +init_nmi_sync_3 + bit $2002 + bpl init_nmi_sync_3 + + ; Enable rendering long enough for frame to + ; be shortened if it's a short one, but not long + ; enough that background will get displayed. + lda #$08 + sta $2001 + + ; Can reduce delay by up to 5 and this still works, + ; so there's a good margin. + ; delay 2377 + lda #216 +init_nmi_sync_4 + nop + nop + sec + sbc #1 + bne init_nmi_sync_4 + + sta $2001 + + lda nmi_sync_count + + ; Wait for this and next frame to finish. + ; If this frame was short, loop ends. If it was + ; long, loop runs for a third frame. +init_nmi_sync_5 + bit $2002 + bit $2002 + php + eor #$02 + sta nmi_sync_count + nop + nop + plp + bpl init_nmi_sync_5 + + ; Enable NMI + lda #$80 + sta $2000 + sta M2000 + rts + +.else + +; Initializes synchronization and enables NMI on PAL NES +; Preserved: X, Y +; Time: about 20 frames +init_nmi_sync + ; NMI will first occur within frame 2 after + ; synchronization + lda #2 + sta nmi_sync_count + + ; Disable interrupts and rendering + sei + lda #0 + sta $2000 + sta $2001 + + ; Coarse synchronize + bit $2002 +init_nmi_sync_pal_1 + bit $2002 + bpl init_nmi_sync_pal_1 + + ; Synchronize to odd CPU cycle + sta $4014 + bit <0 + + ; Fine synchronize +init_nmi_sync_pal_2 + bit <0 + nop + bit $2002 + bit $2002 + bpl init_nmi_sync_pal_2 + + ; Enable NMI + lda #$80 + sta $2000 + sta M2000 + + rts +.endif +.ends + +************************* +**** INTERUPTS VECTORS **** +************************* + +.orga $fffa +.section "vectors" FORCE +.dw NMI +.dw RESET ;Thoose are the actual interupts vectors +.dw IRQ +.ends + +.bank 1 SLOT 3 +.orga $0000 +.section "graph" +.incbin "colorwin.chr" +.ends \ No newline at end of file diff --git a/window5/colorwin.chr b/window5/colorwin.chr new file mode 100644 index 0000000..acdbf0a Binary files /dev/null and b/window5/colorwin.chr differ diff --git a/window5/colorwin_ntsc.nes b/window5/colorwin_ntsc.nes new file mode 100644 index 0000000..ad612df Binary files /dev/null and b/window5/colorwin_ntsc.nes differ diff --git a/window5/colorwin_pal.nes b/window5/colorwin_pal.nes new file mode 100644 index 0000000..39921a5 Binary files /dev/null and b/window5/colorwin_pal.nes differ diff --git a/window5/header b/window5/header new file mode 100644 index 0000000..d5ba821 Binary files /dev/null and b/window5/header differ diff --git a/window5/linkfile b/window5/linkfile new file mode 100644 index 0000000..a7a8ffe --- /dev/null +++ b/window5/linkfile @@ -0,0 +1,5 @@ +[objects] +colorwin.o + +[header] +header diff --git a/window5/maping.asm b/window5/maping.asm new file mode 100644 index 0000000..5f6fb31 --- /dev/null +++ b/window5/maping.asm @@ -0,0 +1,25 @@ +;Defines NES's RAM for any games witout SRAM +;Stack isn't defined, it's use is reserved +;Also $200-$2ff is reserved for SpriteRam and isn't defined here + +.memorymap +defaultslot 0 +slotsize $100 +slot 0 $0 ;0 page RAM +slotsize $500 +slot 1 $300 ;BSS RAM +slotsize $4000 ;PRG ROM slot (32kb) +slot 2 $c000 +slotsize $2000 ;CHR ROM slot (8kb) +slot 3 $0 +.endme + +;Define a CNROM structure with 32kb PRG and 32kb CHR + +.rombankmap +bankstotal 2 +banksize $4000 ;1x 16kb PRG +banks 1 +banksize $2000 ;1x 8kb CHR +banks 1 +.endro \ No newline at end of file diff --git a/window5/win_timing.asm b/window5/win_timing.asm new file mode 100644 index 0000000..c58fb93 --- /dev/null +++ b/window5/win_timing.asm @@ -0,0 +1,169 @@ +.ramsection "TimingVars" SLOT 0 + +FractionTiming db +ScanlineCount db +Temp db +.ends + +;X = $f8 = 248 +;Y = $7f = 127 +;Pixel = 341*Y + X = 43555 +;N1 = (Pixel + 290) / 3 = 14615 (NTSC) +;N2 = (Pixel * 5 + 1444) / 16 = 13701.18 = 13701 (PAL) +.section "Win_Timing" ALIGN $100 FREE +Effect +.ifdef PAL + lda #255 ;8 cycles (6 for jsr) +.else + lda #85*2 ;8 cycles (6 for jsr) +.endif + sta FractionTiming ;11 + lda #$ff ;13 + sta ScanlineCount ;16 + +.ifdef PAL + DELAY 13666 +.else + DELAY 14580 + +.endif + +TimedCode + ; Best + jmp RAMTimedCode ; 337 +_timedLoop + lda FractionTiming ; 5 + clc ; 11 +.ifndef PAL + adc #$aa ; 15 +.else adc #$90 +.endif sta FractionTiming ; 24 + bcs + ; 33 ++ inc ScanlineCount ; 48 (add 3 cycles each 3 frames here, to compensate the effect) + ldx ScanlineCount.w ; 57 + lda ColorTable.w,X ; 69 + bmi _noColorWrite ; 75 + sta RAMTimedCode+3.w ; 87 Write color + lda PPUAdrLTable.w,X ; 99 + sta RAMTimedCode+24.w ; 111 + lda PPUAdrHTable.w,X ; 123 + sta RAMTimedCode+5.w ; 135 +.ifndef PAL + pha + pla +.endif + jmp TimedCode ; 186 (one less than previous frame) + +_noColorWrite ; 78 + nop + nop + cmp #$ff ; 84 + beq _windowColorEnd ; 90 + ;lda #$00 + ;sta $2005 + ;sta $2005 + + ldx #12 ; 132 +- dex ; (12*15=180 cycles -> 126+180=306) + bne - ; 303 (3 less because the branch didn't happen last time) +.ifndef PAL + pha + pla +.endif ldx FractionTiming + ldx FractionTiming + jmp _timedLoop ; 339 + +_windowColorEnd ; 93 + ldx #7 +- dex + bne - + bit $2002 + ldx #$3f + stx $2006.w + lda #$00 + ldx #$0f + ldy #$1e + sta $2001 + sta $2006 + stx $2007.w + sty $2001.w + lda ScanlineCount + tax + and #$03 + tay + lda PPUAdrHTable.w,Y + ora #$01 + sta $2006.w + lda PPUAdrLTable.w,X + sta $2006.w + lda ScrollH + sta $2005 + lda M2000 + sta $2000 + rts + + +;HBlank writes : + +;$2006:=$3f, $00 +;$2001:=$00 +;$2007:=$xx = 3 +;$2006:=$xx, $xx = 5, 27 +;$2001:=$1e Best Worst + +VeryTimedCode + lda #$3f ; 187 + ldx #$00 ; 193 + ldy #$00 ; 199 + bit $2002.w ; 205 + sta $2006.w ; 217 + lda #$00 ; 229 + sta $2001.w ; 235 214 + sta $2006.w ; 247 226 + ; *** Begining of actual HBlank (on best case) + sty $2006.w ; 259 238 + lda #$00 ; 271 250 ; rewritten + ; Begining of the actual HBlank in worst case (glitches !) + ldy #$1e ; 277 256 + stx $2007.w ; 283 262 + sta $2006.w ; 295 274 + sty $2001.w ; 307 286 +.ifdef PAL + lda #4 ; 319 298 +.else + lda #4 ; 319 298 +.endif + sta $2005.w ; 325 304 + jmp _timedLoop ; 337 316 +.ends + +.section "TuningTables" ALIGN $100 FREE +ColorTable + .db $2c, $3c, $2c, $3c, $f9, $f0, $f9, $fe + .db $2c, $2c, $3c, $31, $21, $21, $31, $21 + + .db $31, $21, $11, $21, $f8, $f0, $f8, $fe + .db $21, $11, $21, $22, $12, $22, $12, $12 + + .db $22, $13, $23, $13, $f7, $f0, $f7, $fe + .db $14, $04, $14, $04, $f7, $f0, $f7, $fe + .db $ff + +PPUAdrHTable + .db $02, $12, $22, $32, $02, $12, $22, $32 + .db $02, $12, $22, $32, $02, $12, $22, $32 + .db $02, $12, $22, $32, $02, $12, $22, $32 + .db $02, $12, $22, $32, $02, $12, $22, $32 + .db $02, $12, $22, $32, $02, $12, $22, $32 + .db $02, $12, $22, $32, $02, $12, $22, $32 + .db $02, $12, $22, $32, $02, $12, $22, $32 + .db $02, $12, $22, $32, $02, $12, $22, $32 + +PPUAdrLTable + .db $00, $00, $00, $00, $00, $00, $00, $00 + .db $20, $20, $20, $20, $20, $20, $20, $20 + .db $40, $40, $40, $40, $40, $40, $40, $40 + .db $60, $60, $60, $60, $60, $60, $60, $60 + .db $80, $80, $80, $80, $80, $80, $80, $80 + .db $a0, $a0, $a0, $a0, $a0, $a0, $a0, $a0 +.ends \ No newline at end of file