diff --git a/atlantico/._atlantico.asm b/atlantico/._atlantico.asm new file mode 100755 index 0000000..e806e09 Binary files /dev/null and b/atlantico/._atlantico.asm differ diff --git a/atlantico/._nes.cfg b/atlantico/._nes.cfg new file mode 100755 index 0000000..f06e1f5 Binary files /dev/null and b/atlantico/._nes.cfg differ diff --git a/atlantico/._reset.inc b/atlantico/._reset.inc new file mode 100755 index 0000000..5b40f69 Binary files /dev/null and b/atlantico/._reset.inc differ diff --git a/atlantico/._utils.inc b/atlantico/._utils.inc new file mode 100755 index 0000000..ca10b18 Binary files /dev/null and b/atlantico/._utils.inc differ diff --git a/atlantico/Makefile b/atlantico/Makefile new file mode 100755 index 0000000..317fe0d --- /dev/null +++ b/atlantico/Makefile @@ -0,0 +1,18 @@ +############################################################################### +# Rule to assemble and link all assembly files +############################################################################### +build: + ca65 atlantico.asm -o atlantico.o + ld65 -C nes.cfg atlantico.o -o atlantico.nes + +############################################################################### +# Rule to remove all object (.o) and cartridge (.nes) files +############################################################################### +clean: + rm *.o *.nes + +############################################################################### +# Rule to run the final cartridge .nes file in the FCEUX emulator +############################################################################### +run: + on-workspace 6 "fceux atlantico.nes" diff --git a/atlantico/actor.inc b/atlantico/actor.inc new file mode 100644 index 0000000..b707c9f --- /dev/null +++ b/atlantico/actor.inc @@ -0,0 +1,17 @@ +.enum ActorType + NULL = 0 + PLAYER = 1 + SUBMARINE = 2 + AIRPLANE = 3 + MISSILE = 4 + BOMB = 5 + SPRITE0 = 6 +.endenum + +.struct Actor + Type .byte + XPos .byte + YPos .byte + XVel .byte + YVel .byte +.endstruct diff --git a/atlantico/atlantico.asm b/atlantico/atlantico.asm new file mode 100755 index 0000000..0d65b1e --- /dev/null +++ b/atlantico/atlantico.asm @@ -0,0 +1,707 @@ +.include "consts.inc" +.include "header.inc" +.include "actor.inc" +.include "reset.inc" +.include "utils.inc" + +.segment "ZEROPAGE" +Buttons: .res 1 ; Pressed buttons (A|B|Select|Start|Up|Dwn|Lft|Rgt) + +XPos: .res 2 ; Player X 16-bit position (8.8 fixed-point): hi+lo/256px +YPos: .res 2 ; Player Y 16-bit position (8.8 fixed-point): hi+lo/256px + +XVel: .res 1 ; Player X (signed) velocity (in pixels per 256 frames) +YVel: .res 1 ; Player Y (signed) velocity (in pixels per 256 frames) + +Frame: .res 1 ; Counts frames (0 to 255 and repeats) +IsDrawComplete: .res 1 ; Flag to indicate when vblank is done drawing +Clock60: .res 1 ; Counter that increments per second (60 frames) +BgPtr: .res 2 ; Pointer to background address - 16bits (lo,hi) + +XScroll: .res 1 ; Store the horizontal scroll position +CurrNametable: .res 1 ; Store the current starting nametable (0 or 1) +Column: .res 1 ; Stores the column (of tiles) we are in the level +NewColAddr: .res 2 ; The destination address of the new column in PPU +SourceAddr: .res 2 ; The source address in ROM of the new column tiles + +ParamType: .res 1 ; Used as param to subrouting +ParamXPos: .res 1 ; Used as param to subrouting +ParamYPos: .res 1 ; Used as param to subrouting +ParamXVel: .res 1 ; Used as param to subrouting +ParamYVel: .res 1 ; Used as param to subrouting + +; Store enough space for an array of actors +ActorsArray: .res MAX_ACTORS * .sizeof(Actor) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; PRG-ROM code located at $8000 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.segment "CODE" + +;ReadControllers +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Routine to read controller state and store it inside "Buttons" in RAM +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc ReadControllers + lda #1 ; A = 1 + sta Buttons ; Buttons = 1 + sta JOYPAD1 ; Set Latch=1 to begin 'Input'/collection mode + lsr ; A = 0 + sta JOYPAD1 ; Set Latch=0 to begin 'Output' mode +LoopButtons: + lda JOYPAD1 ; This reads a bit from the controller data line and inverts its value, + ; And also sends a signal to the Clock line to shift the bits + lsr ; We shift-right to place that 1-bit we just read into the Carry flag + rol Buttons ; Rotate bits left, placing the Carry value into the 1st bit of 'Buttons' in RAM + bcc LoopButtons ; Loop until Carry is set (from that initial 1 we loaded inside Buttons) + rts +.endproc + +;LoadPalette +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Subroutine to load all 32 color palette values from ROM +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc LoadPalette + PPU_SETADDR $3F00 + ldy #0 ; Y = 0 +: lda PaletteData,y ; Lookup byte in ROM + sta PPU_DATA ; Set value to send to PPU_DATA + iny ; Y++ + cpy #32 ; Is Y equal to 32? + bne :- ; Not yet, keep looping + rts ; Return from subroutine +.endproc + + +;DrawNewColumn +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Routine to draw a new column of tiles off-screen as we scroll horizontally +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc DrawNewColumn + ; [ Destination ] + lda XScroll ;Set newColAddr low and hi byte + lsr + lsr + lsr ; shift left three times to divide Xscroll by 3 + sta NewColAddr ; set the lo byte + + lda CurrNametable ; hi byte comes from nametable + eor #1 ; invert low bit (0 or 1) + asl + asl ; multiply by 4 (A is $00 or $04) + clc + adc #$20 ; add $20 (A is $20 or $24) for nametable 0 or 1 + sta NewColAddr+1 ; set the hi0-byte of the column address ($20xx or $24xx) + ;;;;;;;;;;; + + ; [SourceAddr low and hi byte] + lda Column ; Multiply by 32 to compute the offset + asl ; *2 + asl ; *4 + asl ; *8 + asl ; *16 + asl ; *32 + sta SourceAddr ; store lo byte of column source address + + lda Column + lsr ; /2 + lsr ; /4 + lsr ; Divide current column by 8 using 3 shift rights + sta SourceAddr+1 ; store hi byte of column source address + + ; here we'll add the offset the column source address with the address where the backggroun + lda SourceAddr ; lo byte of the column data + offset = address to load column data from + clc + adc #BackgroundData ; add the hi byte + sta SourceAddr+1 ; add the result of the offset back to the source address hi byte + + ;;; Loop all tiles from column, sending them to destination + DrawColumn: + lda #%00000100 ; + sta PPU_CTRL ; Tell the PPU that the increments will be +32 mode + + lda PPU_STATUS ; reset latch + lda NewColAddr+1 ; + sta PPU_ADDR ; set hi-byte of start address + lda NewColAddr ; + sta PPU_ADDR ; set lo-byte of start address + + ldx #30 ; Looping 30 times + ldy #0 ; + DrawColumnLoop: ; + lda (SourceAddr), y ; Copy from the address of the column source + y offset + sta PPU_DATA ; + iny ; Y++ + dex ; X-- + bne DrawColumnLoop ; loop 30 times + + rts +.endproc + +;DrawNewAttribs +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Routine to draw a attributes off-screen eveery 32 pixels +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc DrawNewAttribs + lda CurrNametable + eor #1 ; invert low bit + asl ; Multiply by 2 (00 or 02) + asl ; multiply by 2 again (00, or 04) + clc + adc #$23 ; Add high byte of attribute 23-- or 27-- + sta NewColAddr+1 + + lda XScroll + lsr + lsr + lsr + lsr + lsr + clc + adc #$C0 + sta NewColAddr ; Low Byte + + ;; Source Address + lda Column ; Column/4 *8 since each row is 8bytes + and #%11111100 ; mask the lowest two bits to get the closest lowest multiple of 4 + asl ; one shift left - multiply by 2 + sta SourceAddr ; store low byte of offset in rom + + lda Column ; compute high + lsr ;/2 + lsr + lsr + lsr + lsr + lsr + lsr ; /128 + sta SourceAddr+1 ; store high byte + + lda SourceAddr + clc + adc #AttributeData ; hi byte of where attrib is in rom + sta SourceAddr+1 + + DrawAttribute: + bit PPU_STATUS + ldy #0 ; reset latch and back to 0 + DrawAttribLoop: + lda NewColAddr+1 + sta PPU_ADDR ; high byte of ppu dest + lda NewColAddr + sta PPU_ADDR ; low byte + lda (SourceAddr),y ; fetch attribute data + sta PPU_DATA ; store new data into ppu memory + iny + cpy #8 + beq :+ ; loop 8 times + lda NewColAddr + clc + adc #8 + sta NewColAddr ; next addr will be at NewColAddr +8 + jmp DrawAttribLoop + : + rts +.endproc + +;AddNewActor +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Subroutine to add new actor to the array in the first empty slot found +;; Params: ParamType, ParamXPos, ParamYPos, ParamXVel, ParamYVel +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc AddNewActor + ldx #0 + ArrayLoop: + cpx #MAX_ACTORS * .sizeof(Actor); too many actors, skip + beq EndRoutine + lda ActorsArray+Actor::Type,x + cmp #ActorType::NULL + beq AddNewActorToArray ; If found empty slot, add to position + NextActor: + txa + clc + adc #.sizeof(Actor) + tax + jmp ArrayLoop + AddNewActorToArray: + lda ParamType + sta ActorsArray+Actor::Type,x + lda ParamXPos + sta ActorsArray+Actor::XPos,x + lda ParamYPos + sta ActorsArray+Actor::YPos,x + lda ParamXVel + sta ActorsArray+Actor::XVel,x + lda ParamYVel + sta ActorsArray+Actor::YVel,x + EndRoutine: + rts +.endproc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Reset handler (called when the NES resets or powers on) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Reset: + INIT_NES ; Macro to initialize the NES to a known state + +InitVariables: + lda #0 + sta Frame ; Frame = 0 + sta Clock60 ; Clock60 = 0 + sta XScroll ; XScroll = 0 + sta CurrNametable ; CurrNametable = 0 + sta Column ; Column = 0 + lda #113 + sta XPos + lda #165 + sta YPos + +Main: + jsr LoadPalette ; Call LoadPalette subroutine to load 32 colors into our palette + +AddSprite0: + lda #ActorType::SPRITE0 + sta ParamType + lda #0 + sta ParamXPos + lda #27 + sta ParamYPos + lda #0 + sta ParamXVel + sta ParamYVel + jsr AddNewActor + +AddPlayer: + lda #ActorType::PLAYER + sta ParamType + lda XPos + sta ParamXPos + lda YPos + sta ParamYPos + lda #0 + sta ParamXVel + sta ParamYVel + jsr AddNewActor + +InitBackgroundTiles: + lda #1 + sta CurrNametable ;current nametable= 1 + lda #0 + sta XScroll + sta Column +InitBackgroundLoop: + jsr DrawNewColumn ; draw all rows of new column + + lda XScroll + clc + adc #8 + sta XScroll ; xscroll += 8 + + inc Column ;column++ + + lda Column + cmp #32 + bne InitBackgroundLoop ; loop 32 times to repeat all 32 columns of the first nametable + + lda #0 + sta CurrNametable ;current nametable = 0 + lda #1 + sta XScroll ;Scroll = 1 + + jsr DrawNewColumn ; draw first column of 2nd nametable + inc Column ; Column ++ + + lda #%00000000 + sta PPU_CTRL ; PPU back to +1 mode + +InitAttribs: + lda #1 + sta CurrNametable ;current nametable= 1 + lda #0 + sta XScroll + sta Column +InitAttribsLoop: + jsr DrawNewAttribs ; draw all rows of new column + lda XScroll + clc + adc #32 + sta XScroll ; xscroll += 32 + + lda Column + clc + adc #4 + sta Column + cmp #32 + bne InitAttribsLoop ; loop 32 times to repeat all 32 columns of the first nametable + + lda #0 + sta CurrNametable ;current nametable = 0 + lda #1 + sta XScroll ;Scroll = 1 + jsr DrawNewAttribs ; draw first column of 2nd nametable + + inc Column + +EnableRendering: + lda #%10010000 ; Enable NMI and set background to use the 2nd pattern table (at $1000) + sta PPU_CTRL + lda #0 + sta PPU_SCROLL ; Disable scroll in X + sta PPU_SCROLL ; Disable scroll in Y + lda #%00011110 + sta PPU_MASK ; Set PPU_MASK bits to render the background + +;;;;;;; START GAME LOOP +GameLoop: + ;Perform Game Logic Here + jsr ReadControllers + +CheckAButton: + lda Buttons + and #BUTTON_A + beq :+ + lda YPos + lda #ActorType::MISSILE + sta ParamType + lda XPos + sta ParamXPos + lda YPos + sta ParamYPos + lda #0 + sta ParamXVel + lda #255 + sta ParamYVel + jsr AddNewActor + : + + ;; TODO + ;;------------------ + ;jsr SpawnActors + ;jsr UpdateActors + ;jsr RenderActors + ;;------------------ + + WaitForVBlank: ; we lock execution + lda IsDrawComplete ; check and only perform game loop call once NMI is done drawing + beq WaitForVBlank ; otherwise keep looping + + lda #0 ; + sta IsDrawComplete ; set back to 0 + + jmp GameLoop; Force an infinite execution loop +;;;;;;; END GAME LOOP + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NMI interrupt handler +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NMI: + PUSH_REGS ; push registers to the stack + + inc Frame ; Frame++ + +OAMStartDMACopy: ; DMA copy of OAM data from RAM to PPU + lda #$02 ; Every frame, we copy spite data starting at $02** + sta PPU_OAM_DMA ; The OAM-DMA copy starts when we write to $4014 + +NewColumnCheck: + lda XScroll + and #%00000111 ; Check if the scroll a multiple of 8 + bne EndColCheck ; If it isn't, we still don't need to draw a new column + jsr DrawNewColumn ; If it is a multiple of 8, we proceed to draw a new column of tiles! + + Clamp128Cols: + lda Column + clc + adc #1 + and #%01111111 ; drop left most bit to wrap around 128 + sta Column ; clamping the value to never go over 128 + + EndColCheck: + +NewAttribsCheck: + lda XScroll + and #%00011111 + bne :+ + jsr DrawNewAttribs + : + +;SetPPUNoScroll: +; lda #0 +; sta PPU_SCROLL +; sta PPU_SCROLL +; +;EnablePPUSprite0: +; lda #%10010000 +; sta PPU_CTRL +; lda #%00011110 +; sta PPU_MASK +; +;WaitForNoSprite0: +; lda PPU_STATUS +; and #%01000000 +; bne WaitForNoSprite0 +; +;WaitForSprite0: +; lda PPU_STATUS +; and #%01000000 ; PPU address $2002 bit 6 is the sprite hit flag +; beq WaitForSprite0 ; loop until we do NOT have a sprite 0 hit + +ScrollBackground: + inc XScroll ; XScroll++ + lda XScroll + bne :+ ; Check if XScroll rolled back to 0, then we swap nametables! + lda CurrNametable + eor #1 ; An XOR with %00000001 will flip the right-most bit. + sta CurrNametable ; If it was 0, it becomes 1. If it was 1, it becomes 0. + : + lda XScroll + sta PPU_SCROLL ; Set the horizontal X scroll first + lda #0 + sta PPU_SCROLL ; No vertical scrolling + +RefreshRendering: + lda #%10010000 ; Enable NMI, sprites from Pattern Table 0, background from Pattern Table 1 + ora CurrNametable ; OR with CurrNametable (0 or 1) to set PPU_CTRL bit-0 (starting nametable) + sta PPU_CTRL + lda #%00011110 ; Enable sprites, enable background, no clipping on left side + sta PPU_MASK + +SetGameClock: + lda Frame ; Increment Clock60 every time we reach 60 frames (NTSC = 60Hz) + cmp #60 ; Is Frame equal to #60? + bne :+ ; If not, bypass Clock60 increment + inc Clock60 ; But if it is 60, then increment Clock60 and zero Frame counter + lda #0 + sta Frame +: + +SetDrawComplete: + lda #1 + sta IsDrawComplete + inc Frame + + + PULL_REGS + rti ; Return from interrupt + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IRQ interrupt handler +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IRQ: + rti ; Return from interrupt + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Hardcoded list of color values in ROM to be loaded by the PPU +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +PaletteData: +.byte $1C,$0F,$22,$1C, $1C,$37,$3D,$0F, $1C,$37,$3D,$30, $1C,$0F,$3D,$30 ; Background palette +.byte $1C,$0F,$2D,$10, $1C,$0F,$20,$27, $1C,$2D,$38,$18, $1C,$0F,$1A,$32 ; Sprite palette + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Background data (contains 4 screens that should scroll horizontally) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +BackgroundData: +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$23,$33,$15,$21,$12,$00,$31,$31,$31,$55,$56,$00,$00 ; ---> screen column 1 (from top to bottom) +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$42,$21,$21,$21,$24,$34,$15,$15,$12,$00,$31,$31,$53,$56,$56,$00,$00 ; ---> screen column 2 (from top to bottom) +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$43,$21,$21,$21,$14,$11,$3e,$15,$12,$00,$00,$00,$31,$52,$56,$00,$00 ; ---> screen column 3 (from top to bottom) +.byte $13,$13,$7f,$13,$20,$21,$21,$21,$21,$21,$21,$21,$42,$21,$21,$44,$21,$21,$21,$14,$11,$3f,$15,$12,$00,$00,$00,$31,$5a,$56,$00,$00 ; ... +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$45,$21,$21,$21,$22,$32,$15,$15,$12,$00,$00,$00,$31,$58,$56,$00,$00 ; ... +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$46,$21,$21,$21,$26,$36,$15,$15,$12,$00,$00,$00,$51,$5c,$56,$00,$00 ; ... +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$27,$37,$15,$15,$12,$00,$00,$00,$00,$58,$56,$00,$00 +.byte $13,$13,$61,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$28,$38,$15,$15,$12,$00,$00,$00,$00,$5c,$56,$00,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$57,$56,$00,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$47,$21,$21,$21,$48,$21,$21,$22,$32,$3e,$15,$12,$00,$00,$00,$00,$58,$56,$00,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$42,$21,$21,$21,$4a,$21,$21,$23,$33,$4e,$15,$12,$00,$00,$00,$00,$59,$56,$00,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$43,$21,$21,$21,$21,$21,$21,$24,$34,$3f,$15,$12,$00,$00,$00,$00,$58,$56,$00,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$57,$56,$00,$00 +.byte $13,$13,$6c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$59,$56,$00,$00 +.byte $13,$13,$78,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$15,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$00,$00 +.byte $13,$13,$7b,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$15,$15,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$53,$56,$00,$00 +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$15,$21,$21,$25,$35,$15,$15,$12,$00,$60,$00,$00,$54,$56,$00,$00 +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$26,$36,$15,$15,$12,$00,$00,$00,$00,$58,$56,$00,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$27,$37,$15,$15,$12,$00,$00,$00,$00,$58,$56,$00,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$48,$21,$21,$15,$27,$37,$15,$15,$12,$00,$00,$00,$00,$5d,$55,$00,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$21,$28,$38,$3e,$21,$12,$00,$00,$00,$00,$58,$56,$00,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$21,$22,$35,$3f,$21,$12,$00,$00,$00,$00,$58,$56,$00,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$21,$26,$36,$3f,$21,$12,$00,$00,$00,$00,$57,$56,$00,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$4a,$21,$21,$21,$27,$37,$21,$15,$12,$00,$00,$00,$00,$58,$56,$00,$00 +.byte $13,$13,$76,$13,$20,$21,$21,$21,$21,$21,$21,$21,$42,$21,$21,$21,$21,$21,$21,$28,$38,$15,$15,$12,$00,$00,$00,$00,$58,$56,$00,$00 +.byte $13,$13,$72,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$21,$14,$11,$3e,$21,$12,$00,$00,$00,$00,$59,$56,$00,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$43,$21,$21,$21,$21,$21,$21,$14,$11,$4e,$21,$12,$00,$00,$00,$51,$59,$56,$00,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$21,$14,$11,$3f,$15,$12,$00,$00,$00,$00,$5c,$56,$00,$00 +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$21,$29,$39,$21,$21,$12,$00,$00,$00,$00,$55,$56,$00,$00 +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$21,$48,$21,$2c,$2a,$3a,$3c,$21,$12,$00,$00,$00,$54,$56,$56,$00,$00 +.byte $13,$13,$65,$13,$20,$21,$21,$21,$21,$21,$21,$21,$46,$21,$21,$21,$4a,$21,$2d,$2a,$3a,$3d,$15,$12,$00,$00,$00,$00,$52,$56,$00,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$2b,$3b,$15,$15,$12,$00,$00,$00,$00,$57,$56,$00,$00 + +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$21,$12,$00,$31,$31,$31,$55,$56,$ff,$9a +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$15,$21,$14,$11,$15,$15,$12,$00,$31,$31,$53,$56,$56,$ff,$5a +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$15,$15,$21,$14,$11,$3e,$15,$12,$00,$00,$00,$31,$52,$56,$ff,$5a +.byte $13,$13,$7f,$13,$20,$21,$21,$21,$21,$21,$21,$21,$42,$21,$21,$15,$15,$15,$21,$14,$11,$3f,$15,$12,$00,$00,$00,$31,$5a,$56,$ff,$56 +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$15,$15,$15,$21,$14,$11,$15,$15,$12,$00,$00,$00,$31,$58,$56,$ff,$59 +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$15,$15,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$51,$5c,$56,$ff,$5a +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$15,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$ff,$5a +.byte $13,$13,$61,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$5c,$56,$ff,$5a +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$57,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$47,$21,$21,$21,$48,$21,$21,$14,$11,$3e,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$42,$21,$21,$21,$4a,$21,$21,$14,$11,$4e,$15,$12,$00,$00,$00,$00,$59,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$43,$21,$21,$21,$21,$21,$21,$25,$35,$3f,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$21,$26,$36,$15,$15,$12,$00,$00,$00,$00,$57,$56,$aa,$00 +.byte $13,$13,$6c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$21,$21,$21,$21,$27,$37,$15,$15,$12,$00,$00,$00,$00,$59,$56,$aa,$00 +.byte $13,$13,$78,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$15,$21,$21,$21,$28,$38,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$7b,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$15,$15,$21,$21,$29,$39,$15,$15,$12,$00,$00,$00,$00,$53,$56,$aa,$00 +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$15,$21,$1f,$2a,$3a,$3c,$15,$12,$00,$61,$00,$00,$54,$56,$aa,$00 +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$28,$3b,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$48,$21,$21,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$5d,$56,$aa,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$21,$14,$11,$3e,$21,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$21,$14,$11,$3f,$21,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$21,$14,$11,$3f,$21,$12,$00,$00,$00,$00,$57,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$4a,$21,$21,$21,$14,$11,$21,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$76,$13,$20,$21,$21,$21,$21,$21,$21,$21,$42,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$5a,$00 +.byte $13,$13,$72,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$21,$14,$11,$3e,$21,$12,$00,$00,$00,$00,$59,$56,$9a,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$43,$21,$21,$21,$21,$21,$21,$22,$32,$4e,$21,$12,$00,$00,$00,$51,$59,$56,$aa,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$21,$23,$33,$3f,$15,$12,$00,$00,$00,$00,$5c,$56,$6a,$00 +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$21,$24,$34,$21,$21,$12,$00,$00,$00,$00,$55,$56,$9a,$00 +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$21,$48,$15,$15,$14,$11,$15,$21,$12,$00,$00,$00,$54,$56,$56,$aa,$00 +.byte $13,$13,$65,$13,$20,$21,$21,$21,$21,$21,$21,$21,$46,$21,$21,$21,$4a,$21,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$52,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$57,$56,$aa,$00 + +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$21,$12,$00,$31,$31,$31,$58,$56,$ff,$9a +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$42,$21,$21,$21,$14,$11,$15,$15,$12,$00,$31,$31,$00,$5d,$56,$ff,$5a +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$43,$21,$21,$21,$14,$11,$3e,$15,$12,$00,$00,$00,$31,$58,$56,$ff,$5a +.byte $13,$13,$7f,$13,$20,$21,$21,$21,$21,$21,$21,$21,$42,$21,$21,$44,$21,$21,$21,$14,$11,$3f,$15,$12,$00,$00,$00,$31,$58,$56,$ff,$aa +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$45,$21,$21,$21,$22,$32,$15,$15,$12,$00,$00,$00,$31,$58,$56,$ff,$56 +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$46,$21,$21,$21,$26,$36,$15,$15,$12,$00,$00,$00,$51,$58,$56,$ff,$9a +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$27,$37,$15,$15,$12,$00,$00,$00,$00,$58,$56,$ff,$59 +.byte $13,$13,$61,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$28,$38,$15,$15,$12,$00,$00,$00,$00,$55,$56,$ff,$5a +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$57,$56,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$47,$21,$21,$21,$48,$21,$21,$22,$32,$3e,$15,$12,$00,$00,$00,$00,$52,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$42,$21,$21,$21,$4a,$21,$21,$23,$33,$4e,$15,$12,$00,$00,$00,$00,$53,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$43,$21,$21,$21,$21,$21,$21,$24,$34,$3f,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$6c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$78,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$7b,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$59,$56,$aa,$00 +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$62,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$29,$39,$15,$15,$12,$00,$00,$00,$00,$59,$56,$aa,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$1f,$2a,$3a,$3d,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$48,$21,$15,$2d,$2a,$3a,$3c,$15,$12,$00,$00,$00,$00,$5b,$56,$aa,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$2f,$2a,$3a,$3d,$21,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$21,$28,$3b,$3e,$21,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$49,$21,$21,$21,$14,$11,$4e,$21,$12,$00,$00,$00,$51,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$4a,$21,$21,$21,$14,$11,$21,$15,$12,$00,$00,$00,$51,$58,$56,$aa,$00 +.byte $13,$13,$76,$13,$20,$21,$21,$21,$21,$21,$21,$21,$42,$21,$21,$21,$21,$21,$15,$29,$39,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$72,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$15,$2c,$2a,$3a,$3e,$21,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$43,$21,$21,$21,$21,$21,$2e,$2a,$3a,$4e,$21,$12,$00,$00,$00,$51,$58,$56,$aa,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$1f,$2a,$3a,$3f,$15,$12,$00,$00,$00,$00,$5d,$56,$aa,$00 +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$15,$28,$3b,$3f,$21,$12,$00,$00,$00,$00,$57,$56,$aa,$00 +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$45,$21,$21,$21,$48,$21,$15,$14,$11,$15,$21,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$65,$13,$20,$21,$21,$21,$21,$21,$21,$21,$46,$21,$21,$21,$4a,$21,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 + +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$21,$12,$00,$31,$31,$31,$58,$56,$ff,$9a +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$14,$11,$15,$15,$12,$00,$31,$31,$00,$58,$56,$ff,$5a +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$31,$58,$56,$ff,$5a +.byte $13,$13,$7f,$13,$20,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$15,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$31,$54,$56,$ff,$59 +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$15,$21,$21,$21,$14,$11,$3e,$15,$12,$00,$00,$00,$31,$54,$56,$ff,$56 +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$42,$15,$21,$21,$15,$21,$21,$21,$14,$11,$4e,$15,$12,$00,$00,$00,$51,$58,$56,$ff,$5a +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$43,$21,$21,$21,$21,$21,$21,$21,$14,$11,$4e,$15,$12,$00,$00,$00,$00,$58,$56,$ff,$59 +.byte $13,$13,$61,$13,$20,$21,$21,$21,$21,$21,$21,$44,$21,$21,$21,$21,$21,$21,$21,$14,$11,$3f,$15,$12,$00,$00,$00,$00,$58,$56,$ff,$5a +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$45,$21,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$47,$15,$21,$21,$21,$15,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$53,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$15,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$21,$21,$21,$14,$11,$15,$15,$12,$00,$00,$00,$00,$57,$56,$aa,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$21,$21,$21,$29,$39,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$6c,$13,$20,$21,$21,$21,$21,$21,$48,$21,$15,$21,$21,$21,$21,$1d,$1e,$2a,$3a,$3c,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$78,$13,$20,$21,$21,$21,$21,$21,$49,$21,$21,$21,$21,$21,$21,$21,$21,$2b,$3b,$3e,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$7b,$13,$20,$21,$21,$21,$21,$21,$4a,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$4e,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$6e,$13,$20,$21,$21,$21,$21,$15,$48,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$4e,$15,$12,$00,$63,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$49,$21,$21,$21,$21,$21,$21,$21,$21,$14,$11,$3f,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$4a,$21,$21,$21,$21,$21,$21,$15,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$59,$56,$aa,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$15,$21,$15,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$59,$56,$aa,$00 +.byte $13,$13,$60,$13,$20,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$15,$21,$21,$15,$14,$11,$15,$21,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$42,$21,$21,$21,$15,$21,$21,$21,$29,$39,$15,$21,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$43,$21,$21,$21,$15,$21,$21,$2c,$2a,$3a,$3c,$21,$12,$00,$00,$00,$50,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$44,$15,$21,$21,$15,$21,$21,$2d,$2a,$3a,$3e,$15,$12,$00,$00,$00,$50,$58,$56,$aa,$00 +.byte $13,$13,$76,$13,$20,$21,$21,$21,$21,$21,$21,$45,$15,$21,$21,$21,$21,$21,$15,$2b,$3b,$3f,$15,$12,$00,$00,$00,$00,$54,$56,$aa,$00 +.byte $13,$13,$72,$13,$20,$21,$21,$21,$21,$21,$21,$46,$15,$21,$21,$21,$21,$15,$15,$14,$11,$3f,$21,$12,$00,$00,$00,$00,$59,$56,$aa,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$21,$21,$15,$14,$11,$15,$21,$12,$00,$00,$00,$51,$58,$56,$aa,$00 +.byte $13,$13,$7c,$13,$20,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$21,$21,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$75,$13,$20,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$21,$21,$15,$14,$11,$15,$21,$12,$00,$00,$00,$00,$5d,$56,$aa,$00 +.byte $13,$13,$84,$13,$20,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$15,$21,$15,$14,$11,$15,$21,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$65,$13,$20,$21,$21,$21,$21,$21,$21,$21,$15,$21,$21,$21,$15,$15,$15,$14,$11,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 +.byte $13,$13,$13,$13,$20,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$21,$22,$32,$15,$15,$12,$00,$00,$00,$00,$58,$56,$aa,$00 + +AttributeData: +.byte $FF,$AA,$AA,$AA,$9A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$6A,$A6,$00,$00,$00 +.byte $FF,$AA,$AA,$9A,$59,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$9A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 + +.byte $FF,$AA,$AA,$5A,$9A,$00,$00,$00 +.byte $FF,$AA,$AA,$9A,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$6A,$56,$00,$00,$00 +.byte $FF,$AA,$AA,$9A,$59,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 + +.byte $FF,$AA,$AA,$AA,$9A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$AA,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$56,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$9A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$59,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 + +.byte $FF,$AA,$AA,$AA,$9A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$59,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$56,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$59,$00,$00,$00 +.byte $FF,$AA,$AA,$AA,$5A,$00,$00,$00 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; This is the OAM sprite attribute data data we will use in our game. +;; We have only one big metasprite that is composed of 2x4 hardware sprites. +;; The OAM is organized in sets of 4 bytes per tile. +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +SpriteData: +; Y tile# attributes +.byte $27, $70, %00100001, $6 ; [] Sprite 0, used to split screen +; Y tile# attributes X +.byte $A6, $60, %00000000, $70 ; _______________ +.byte $A6, $61, %00000000, $78 ; \ o o o o o / <-- Ship (4 tiles) +.byte $A6, $62, %00000000, $80 ; \___________/ +.byte $A6, $63, %00000000, $88 ; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Here we add the CHR-ROM data, included from an external .CHR file +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.segment "CHARS" +.incbin "atlantico.chr" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Vectors with the addresses of the handlers that we always add at $FFFA +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.segment "VECTORS" +.word NMI ; Address (2 bytes) of the NMI handler +.word Reset ; Address (2 bytes) of the Reset handler +.word IRQ ; Address (2 bytes) of the IRQ handler diff --git a/atlantico/atlantico.chr b/atlantico/atlantico.chr new file mode 100755 index 0000000..3fb49e3 Binary files /dev/null and b/atlantico/atlantico.chr differ diff --git a/atlantico/atlantico.nes b/atlantico/atlantico.nes new file mode 100644 index 0000000..42ea5d8 Binary files /dev/null and b/atlantico/atlantico.nes differ diff --git a/atlantico/atlantico.o b/atlantico/atlantico.o new file mode 100644 index 0000000..3134f7c Binary files /dev/null and b/atlantico/atlantico.o differ diff --git a/atlantico/consts.inc b/atlantico/consts.inc new file mode 100755 index 0000000..867d4b7 --- /dev/null +++ b/atlantico/consts.inc @@ -0,0 +1,29 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Constants for PPU registers mapped from addresses $2000 to $2007 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +PPU_CTRL = $2000 +PPU_MASK = $2001 +PPU_STATUS = $2002 +OAM_ADDR = $2003 +OAM_DATA = $2004 +PPU_SCROLL = $2005 +PPU_ADDR = $2006 +PPU_DATA = $2007 +PPU_OAM_DMA = $4014 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Controller ports and buttons +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +JOYPAD1 = $4016 +JOYPAD2 = $4017 + +BUTTON_A = $80 ; 10000000 +BUTTON_B = $40 ; 01000000 +BUTTON_SELECT = $20 ; 00100000 +BUTTON_START = $10 ; 00010000 +BUTTON_UP = $08 ; 00001000 +BUTTON_DOWN = $04 ; 00000100 +BUTTON_LEFT = $02 ; 00000010 +BUTTON_RIGHT = $01 ; 00000001 + +MAX_ACTORS = 10 diff --git a/atlantico/header.inc b/atlantico/header.inc new file mode 100755 index 0000000..7dc8f99 --- /dev/null +++ b/atlantico/header.inc @@ -0,0 +1,13 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; The iNES header (contains a total of 16 bytes with the flags at $7FF0) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.segment "HEADER" +.byte $4E,$45,$53,$1A ; 4 bytes with the characters 'N','E','S','\n' +.byte $02 ; How many 16KB of PRG-ROM we'll use (=32KB) +.byte $01 ; How many 8KB of CHR-ROM we'll use (=8KB) +.byte %00000001 ; Vertical mirroring, no battery, mapper 0 +.byte %00000000 ; mapper 0, playchoice, NES 2.0 +.byte $00 ; No PRG-RAM +.byte $00 ; NTSC TV format +.byte $00 ; Extra flags for TV format and PRG-RAM +.byte $00,$00,$00,$00,$00 ; Unused padding to complete 16 bytes of header diff --git a/atlantico/nes.cfg b/atlantico/nes.cfg new file mode 100755 index 0000000..7daa74b --- /dev/null +++ b/atlantico/nes.cfg @@ -0,0 +1,16 @@ +MEMORY { + ZP: start = $0000, size = $0100, type = rw, file = ""; + OAM: start = $0200, size = $0100, type = rw, file = ""; + RAM: start = $0300, size = $0500, type = rw, file = ""; + HDR: start = $7FF0, size = $0010, type = ro, file = %O, fill = yes, fillval = $00; + PRG: start = $8000, size = $8000, type = ro, file = %O, fill = yes, fillval = $00; + CHR: start = $0000, size = $2000, type = ro, file = %O, fill = yes, fillval = $00; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + HEADER: load = HDR, type = ro; + CODE: load = PRG, type = ro, start = $8000; + CHARS: load = CHR, type = ro, optional = yes; + VECTORS: load = PRG, type = ro, start = $FFFA; +} \ No newline at end of file diff --git a/atlantico/reset.inc b/atlantico/reset.inc new file mode 100755 index 0000000..9e924fa --- /dev/null +++ b/atlantico/reset.inc @@ -0,0 +1,43 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Macro to initialize the NES by disabling rendering and clearing RAM values +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.macro INIT_NES + sei ; Disable all IRQ interrupts + cld ; Clear decimal mode (not supported by the NES) + ldx #$FF + txs ; Initialize the stack pointer at address $FF + + inx ; Increment X, causing a rolloff from $FF to $00 + stx PPU_CTRL ; Disable NMI + stx PPU_MASK ; Disable rendering (masking background and sprites) + stx $4010 ; Disable DMC IRQs + + lda #$40 + sta $4017 ; Disable APU frame IRQ + + bit PPU_STATUS ; Read from PPU_STATUS to reset the VBlank flag +Wait1stVBlank: ; Wait for the first VBlank from the PPU + bit PPU_STATUS ; Perform a bit-wise check with the PPU_STATUS port + bpl Wait1stVBlank ; Loop until bit-7 (sign bit) is 1 (inside VBlank) + + txa ; A = 0 +ClearRAM: + sta $0000,x ; Zero RAM addresses from $0000 to $00FF + sta $0100,x ; Zero RAM addresses from $0100 to $01FF + + lda #$FF ; We cannot load $0200-$02FF (OAM) with zero + sta $0200,x ; So we load it with $FF (all sprites off-screen) + + lda #0 ; And we proceed to zero the next ranges + sta $0300,x ; Zero RAM addresses from $0300 to $03FF + sta $0400,x ; Zero RAM addresses from $0400 to $04FF + sta $0500,x ; Zero RAM addresses from $0500 to $05FF + sta $0600,x ; Zero RAM addresses from $0600 to $06FF + sta $0700,x ; Zero RAM addresses from $0700 to $07FF + inx + bne ClearRAM + +Wait2ndVBlank: ; Wait for the second VBlank from the PPU + bit PPU_STATUS ; Perform a bit-wise check with the PPU_STATUS port + bpl Wait2ndVBlank ; Loop until bit-7 (sign bit) is 1 (inside VBlank) +.endmacro diff --git a/atlantico/tagbardebug.log b/atlantico/tagbardebug.log new file mode 100644 index 0000000..bf7248e --- /dev/null +++ b/atlantico/tagbardebug.log @@ -0,0 +1,49 @@ + + +NVIM v0.8.1 +Build type: Debug +LuaJIT 2.1.0-beta3 +Compilation: /usr/bin/cc -DNVIM_TS_HAS_SET_MATCH_LIMIT -DNVIM_TS_HAS_SET_ALLOCATOR -g -Wall -Wextra -pedantic -Wno-unused-parameter -Wstrict-prototypes -std=gnu99 -Wshadow -Wconversion -Wdouble-promotion -Wmissing-noreturn -Wmissing-format-attribute -Wmissing-prototypes -Wimplicit-fallthrough -Wvla -fstack-protector-strong -fno-common -fdiagnostics-color=always -DINCLUDE_GENERATED_DECLARATIONS -D_GNU_SOURCE -DNVIM_MSGPACK_HAS_FLOAT32 -DNVIM_UNIBI_HAS_VAR_FROM -DMIN_LOG_LEVEL=1 -DEXITFREE -I/opt/nightlies/neovim/build/cmake.config -I/opt/nightlies/neovim/src -I/opt/nightlies/neovim/.deps/usr/include -I/usr/include -I/opt/nightlies/neovim/build/src/nvim/auto -I/opt/nightlies/neovim/build/include +Compiled by tyrel@blackbox-jr + +Features: +acl +iconv +tui +See ":help feature-compile" + + system vimrc file: "$VIM/sysinit.vim" + fall-back for $VIM: "/usr/local/share/nvim" + +Run :checkhealth for more info1970-01-01 21:57:14.324701: goto_win(): wincmd p, 1 +1970-01-01 21:57:14.325123: goto_win(): 2wincmd w, 1 +1970-01-01 21:57:14.333705: goto_win(): 2wincmd w, 1 +1970-01-01 21:57:14.333977: goto_win(): 3wincmd w, 1 +1970-01-01 21:57:14.335883: AutoUpdate called [/home/tyrel/code/6502nes/atlantico/] +1970-01-01 21:57:14.336102: Vim filetype: 'asm_ca65', sanitized filetype: 'asm_ca65' +1970-01-01 21:57:14.336214: Checking if file is valid [/home/tyrel/code/6502nes/atlantico/] +1970-01-01 21:57:14.336360: File not readable +1970-01-01 21:57:14.336459: Not a valid file, stopping processing +1970-01-01 21:57:14.340545: AutoUpdate called [/home/tyrel/code/6502nes/atlantico/] +1970-01-01 21:57:14.340720: Vim filetype: 'asm_ca65', sanitized filetype: 'asm_ca65' +1970-01-01 21:57:14.340811: Checking if file is valid [/home/tyrel/code/6502nes/atlantico/] +1970-01-01 21:57:14.340927: File not readable +1970-01-01 21:57:14.341007: Not a valid file, stopping processing +1970-01-01 21:57:14.349687: AutoUpdate called [term://~/code/6502nes/atlantico//146139:/bin/fish] +1970-01-01 21:57:14.349788: Vim filetype: 'floaterm', sanitized filetype: 'floaterm' +1970-01-01 21:57:14.349826: Checking if file is valid [term://~/code/6502nes/atlantico//146139:/bin/fish] +1970-01-01 21:57:14.349900: File not readable +1970-01-01 21:57:14.349932: Not a valid file, stopping processing +1970-01-01 21:57:15.291586: AutoUpdate called [/home/tyrel/code/6502nes/atlantico/__Tagbar__.1] +1970-01-01 21:57:15.291682: In Tagbar window, stopping processing +1970-01-01 21:57:15.574356: ToggleWindow called +1970-01-01 21:57:15.574851: CloseWindow called +1970-01-01 21:57:15.597904: AutoUpdate called [/home/tyrel/code/6502nes/atlantico/atlantico.asm] +1970-01-01 21:57:15.597985: Vim filetype: 'asm_ca65', sanitized filetype: 'asm_ca65' +1970-01-01 21:57:15.598027: Checking if file is valid [/home/tyrel/code/6502nes/atlantico/atlantico.asm] +1970-01-01 21:57:15.598108: Unsupported filetype: asm_ca65 +1970-01-01 21:57:15.598141: Not a valid file, stopping processing +1970-01-01 21:57:15.598199: goto_win(): wincmd p, 0 +1970-01-01 21:57:15.598267: CloseWindow finished +1970-01-01 21:57:19.699439: AutoUpdate called [/home/tyrel/code/6502nes/atlantico/atlantico.asm] +1970-01-01 21:57:19.699592: Vim filetype: 'asm_ca65', sanitized filetype: 'asm_ca65' +1970-01-01 21:57:19.699667: Checking if file is valid [/home/tyrel/code/6502nes/atlantico/atlantico.asm] +1970-01-01 21:57:19.699815: Unsupported filetype: asm_ca65 +1970-01-01 21:57:19.699876: Not a valid file, stopping processing diff --git a/atlantico/utils.inc b/atlantico/utils.inc new file mode 100755 index 0000000..b21f1f6 --- /dev/null +++ b/atlantico/utils.inc @@ -0,0 +1,37 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Macro to set a 16-bit address to the PPU_ADDR register (at $2006) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.macro PPU_SETADDR addr + bit PPU_STATUS ; Read from PPU_STATUS to reset the address latch + lda #>addr ; Fetch the hi-byte from 16-bit address + sta PPU_ADDR ; Store the hi-byte into PPU_ADDR $2006 + lda #BackgroundData + sta BgPtr+1 + + PPU_SETADDR $2000 ; nametable 0 + + ldx #$00 ; high byte (0-3) + ldy #$00 ; low byte (0-FF) + +OuterLoop: +InnerLoop: + lda (BgPtr),y ; fetch the value pointed by bgptr+y + sta PPU_DATA ; store value in ppu data + iny ; y++ + cpy #0 ; if y == 0 then wrapped + beq IncreaseHiByte ; then: increase hi byte + jmp InnerLoop ; else: continue inner loop +IncreaseHiByte: + inc BgPtr+1 ; we increment the hi byte pointer to point to the next bg section + inx ; X++ + cpx #4 ; break condition (4 sections bg) + bne OuterLoop ; if x is stil not 4, loop back to outer loop + + rts ; Return from subroutine +.endproc +; LoadText +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Subroutine to load text in the nametable until it finds a 0-terminator +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc LoadText + PPU_SETADDR $21CB + + ldy #0 ;y=0 +Loop: + lda TextMessage,y ; Fetch character byte from ROM + beq EndLoop ; If the character is 0, end loop + + cmp #32 ; Compare to ASCII 32 + bne DrawLetter ; if its not, draw the letter +DrawSpace: + lda #$24 ; tile $24 is a space + sta PPU_DATA ; Store PPU_DATA with the empty tile + jmp NextChar ; go to next character +DrawLetter: + sec ; set carry + sbc #55 ; subtract 55 from the ASCII value, to get the character tile as chars start at 10 + sta PPU_DATA ; store the data and advance the PPU_ADDR +NextChar: + iny ; y++, next character from the string + jmp Loop ; go back to top of loop + +EndLoop: + rts ; Return from subroutine +.endproc +; LoadSprites +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Subroutine to load all 16 sprites into OAM-RAM starting at $0200 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc LoadSprites + ldx #0 ; +LoadSprites: ; + lda SpriteData,x ; fetch bytes from SpriteData lookup table + sta $0200,x ; store bytes starting at OAM $0200 + inx ; X++ + cpx #32; + bne LoadSprites ; Loop 16 times (4 hardware sprites, 4 bytes each) + rts ; Return from subroutine +.endproc +;ReadControllers +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Reset handler (called when the NES resets or powers on) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc ReadControllers + lda #1 ; A=1 + sta Buttons ; Buttons=1 (This will carry to the bcc, not an input value) + + sta JOYPAD1 ; Set latch to 1 to begin input + lsr ; A=0 by shifting right, quicker maybe + sta JOYPAD1 ; Set latch to 0 to begin output +LoopButtons: + lda JOYPAD1 ; 1. reads a bit from the controller line and inverts its value + ; 2. sends a signal to the clock line to shift the bits inside the controller + + lsr ; we shift right to place that 1bit + rol Buttons ; rolls bits to the left, placing the carry inside the first bit of `Buttons` in ram + bcc LoopButtons ; loop until carry is set (from that initial 1 we had inside buttons + rts +.endproc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Reset handler (called when the NES resets or powers on) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Reset: + INIT_NES ; Macro to initialize the NES to a known state + +InitVariables: + lda #0 + sta Frame + sta Clock60 + + + ldx #0 + lda SpriteData,x + sta YPos + inx + inx + inx + lda SpriteData,x + sta XPos + + +Main: + jsr LoadPalette ; Jump to subroutine LoadPalette + jsr LoadBackground ; Jump to subroutine LoadBackground + jsr LoadSprites + +EnablePPURendering: + lda #%10010000 ; enable NMI and set bg to use second pattern table + sta PPU_CTRL + lda #0 ; disable ppu scrolling + sta PPU_SCROLL ; latch (in X) + sta PPU_SCROLL ; latch (in y) + lda #%00011110 + sta PPU_MASK + +LoopForever: + jmp LoopForever ; Force an infinite execution loop + +; NMI +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NMI interrupt handler +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NMI: + inc Frame ; Frame ++ + ; ------------------------------------- + ; As soon as we enter the NMI handler, start OAM copy + lda #$02 ; every frame we copy sprite data starting at $02** + sta PPU_OAM_DMA ; The OAM DMA Copy starts when we write to PPU_OAM_DMA + ; ------------------------------------- + + jsr ReadControllers ; Jump to the subroutine that checks the controllers + +CheckRightButton: + lda Buttons + and #BUTTON_RIGHT + beq CheckLeftButton + inc XPos ; X++ +CheckLeftButton: + lda Buttons + and #BUTTON_LEFT + beq CheckDownButton + dec XPos ; X-- +CheckDownButton: + lda Buttons + and #BUTTON_DOWN + beq CheckUpButton + inc YPos ; Y++ +CheckUpButton: + lda Buttons + and #BUTTON_UP + beq :+ + dec YPos ; Y-- +: + +UpdateSpritePosition: + lda XPos + sta $0203 + sta $020B + clc + adc #8 + sta $0207 + sta $020F + + lda YPos + sta $0200 + sta $0204 + clc + adc #8 + sta $0208 + sta $020C + +;;;; + lda Frame ; increment clock60 every time the frame reaches 60 + cmp #60 ; frame equal to 60? + bne :+ ; branch to end if not 60, carry on! + inc Clock60 + lda #0 + sta Frame ; Zero Frame Counter +: + + rti ; Return from interrupt + +; IRQ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IRQ interrupt handler +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IRQ: + rti ; Return from interrupt + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Hardcoded list of color values in ROM to be loaded by the PPU +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +PaletteData: +.byte $22,$29,$1A,$0f, $22,$36,$17,$0f, $22,$30,$21,$0f, $22,$27,$17,$0f ; Background CP 00 01 10 11 +.byte $22,$16,$27,$18, $22,$1a,$30,$27, $22,$16,$30,$27, $22,$0f,$36,$17 ; sprite CP 00 01 10 11 + +;BackgroundData +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Background data with tile numbers that must be copied to the nametable +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +BackgroundData: +.incbin "background.nam" + + +;SpriteData +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; This is the OAM sprite attribute data data we will use in our game +;; We have only one big metasprite that is composed of 4 hardware sprites +;; the OAM is organized in sets of 4 bytes per tile +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Sprite Attribute Byte: +; ---------------------- +; 76543210 +; ||| || +; ||| ++- Color Palette of Sprite: Choose which set of 4 from the 16 colors to use +; ||| +; ||+------ Priority (0: in front of BG, 1: Behind BG) +; |+------- Flip Horizontally +; +-------- Flip Vertically +SpriteData: +;-------------------------------- +; Mario: +; Y tile# attributes X +.byte $AE, $3A, %00000000, $98 +.byte $AE, $37, %00000000, $A0 +.byte $B6, $4F, %00000000, $98 +.byte $B6, $4F, %01000000, $A0 +;-------------------------------- +; Goomba: +; Y tile# attributes X +.byte $93, $70, %00100011, $C7 +.byte $93, $71, %00100011, $CF +.byte $9B, $72, %00100011, $C7 +.byte $9B, $73, %00100011, $CF +;-------------------------------- +;End Sprites + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Hardcoded ASCII message stored in ROM with 0-terminator +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +TextMessage: +.byte "TYREL SOUZA",$0 + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Here we add the CHR-ROM data, included from an external .CHR file +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.segment "CHARS" +.incbin "mario.chr" +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Vectors with the addresses of the handlers that we always add at $FFFA +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.segment "VECTORS" +.word NMI ; Address (2 bytes) of the NMI handler +.word Reset ; Address (2 bytes) of the Reset handler +.word IRQ ; Address (2 bytes) of the IRQ handler diff --git a/loadbackground/loadbackground.nes b/loadbackground/loadbackground.nes new file mode 100644 index 0000000..df3623b Binary files /dev/null and b/loadbackground/loadbackground.nes differ diff --git a/loadbackground/loadbackground.o b/loadbackground/loadbackground.o new file mode 100644 index 0000000..82d990c Binary files /dev/null and b/loadbackground/loadbackground.o differ diff --git a/loadbackground/mario.chr b/loadbackground/mario.chr new file mode 100755 index 0000000..0e3a3cb Binary files /dev/null and b/loadbackground/mario.chr differ diff --git a/loadbackground/nes.cfg b/loadbackground/nes.cfg new file mode 100755 index 0000000..7daa74b --- /dev/null +++ b/loadbackground/nes.cfg @@ -0,0 +1,16 @@ +MEMORY { + ZP: start = $0000, size = $0100, type = rw, file = ""; + OAM: start = $0200, size = $0100, type = rw, file = ""; + RAM: start = $0300, size = $0500, type = rw, file = ""; + HDR: start = $7FF0, size = $0010, type = ro, file = %O, fill = yes, fillval = $00; + PRG: start = $8000, size = $8000, type = ro, file = %O, fill = yes, fillval = $00; + CHR: start = $0000, size = $2000, type = ro, file = %O, fill = yes, fillval = $00; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + HEADER: load = HDR, type = ro; + CODE: load = PRG, type = ro, start = $8000; + CHARS: load = CHR, type = ro, optional = yes; + VECTORS: load = PRG, type = ro, start = $FFFA; +} \ No newline at end of file diff --git a/loadbackground/reset.inc b/loadbackground/reset.inc new file mode 100755 index 0000000..bbd04e4 --- /dev/null +++ b/loadbackground/reset.inc @@ -0,0 +1,43 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Macro to initialize the NES by disabling rendering and clearing RAM values +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.macro INIT_NES + sei ; Disable all IRQ interrupts + cld ; Clear decimal mode (not supported by the NES) + ldx #$FF + txs ; Initialize the stack pointer at address $FF + + inx ; Increment X, causing a rolloff from $FF to $00 + stx PPU_CTRL ; Disable NMI + stx PPU_MASK ; Disable rendering (masking background and sprites) + stx $4010 ; Disable DMC IRQs + + lda #$40 + sta $4017 ; Disable APU frame IRQ + + bit PPU_STATUS ; Read from PPU_STATUS to reset the VBlank flag +Wait1stVBlank: ; Wait for the first VBlank from the PPU + bit PPU_STATUS ; Perform a bit-wise check with the PPU_STATUS port + bpl Wait1stVBlank ; Loop until bit-7 (sign bit) is 1 (inside VBlank) + + txa ; A = 0 +ClearRAM: + sta $0000,x ; Zero RAM addresses from $0000 to $00FF + sta $0100,x ; Zero RAM addresses from $0100 to $01FF + + lda #$FF ; We init OAM data with $FF to hide sprites + sta $0200,x ; Zero RAM addresses from $0200 to $02FF with $FF + lda #0 + + sta $0300,x ; Zero RAM addresses from $0300 to $03FF + sta $0400,x ; Zero RAM addresses from $0400 to $04FF + sta $0500,x ; Zero RAM addresses from $0500 to $05FF + sta $0600,x ; Zero RAM addresses from $0600 to $06FF + sta $0700,x ; Zero RAM addresses from $0700 to $07FF + inx + bne ClearRAM + +Wait2ndVBlank: ; Wait for the second VBlank from the PPU + bit PPU_STATUS ; Perform a bit-wise check with the PPU_STATUS port + bpl Wait2ndVBlank ; Loop until bit-7 (sign bit) is 1 (inside VBlank) +.endmacro diff --git a/loadbackground/utils.inc b/loadbackground/utils.inc new file mode 100644 index 0000000..09f23ee --- /dev/null +++ b/loadbackground/utils.inc @@ -0,0 +1,12 @@ +.macro PPU_SETADDR addr + bit PPU_STATUS ; read from PPU_STATUS to reset latch + lda #>addr ; > means get high byte from `addr` + sta PPU_ADDR + lda #BackgroundData ; Fetch the hi-byte of BackgroundData address + sta BgPtr+1 + + PPU_SETADDR $2000 + + ldx #$00 ; X = 0 --> x is the outer loop index (hi-byte) from $0 to $4 + ldy #$00 ; Y = 0 --> y is the inner loop index (lo-byte) from $0 to $FF +OuterLoop: +InnerLoop: + lda (BgPtr),y ; Fetch the value *pointed* by BgPtr + Y + sta PPU_DATA ; Store in the PPU data + iny ; Y++ + cpy #0 ; If Y == 0 (wrapped around 256 times)? + beq IncreaseHiByte ; Then: we need to increase the hi-byte + jmp InnerLoop ; Else: Continue with the inner loop +IncreaseHiByte: + inc BgPtr+1 ; We increment the hi-byte pointer to point to the next background section (next 255-chunk) + inx ; X++ + cpx #4 ; Compare X with #4 + bne OuterLoop ; If X is still not 4, then we keep looping back to the outer loop + rts ; Return from subroutine +.endproc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Subroutine to load text in the nametable until it finds a 0-terminator +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.proc LoadText + + ;; TODO: + ;; Loop all characters from ROM and load/display the text in the nametable. + ;; Bonus points if you code manages to display an empty tile for [space]. + + rts ; Return from subroutine +.endproc + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Reset handler (called when the NES resets or powers on) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +Reset: + INIT_NES ; Macro to initialize the NES to a known state + + lda #0 + sta Frame ; Frame = 0 + sta Clock60 ; Clock60 = 0 + +Main: + jsr LoadPalette ; Call LoadPalette subroutine to load 32 colors into our palette + jsr LoadBackground ; Call LoadBackground subroutine to load a full nametable of tiles and attributes + jsr LoadText ; Call LoadText subroutine to draw the text message on the nametable + +EnablePPURendering: + lda #%10010000 ; Enable NMI and set background to use the 2nd pattern table (at $1000) + sta PPU_CTRL + lda #0 + sta PPU_SCROLL ; Disable scroll in X + sta PPU_SCROLL ; Disable scroll in Y + lda #%00011110 + sta PPU_MASK ; Set PPU_MASK bits to render the background + +LoopForever: + jmp LoopForever ; Force an infinite execution loop + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; NMI interrupt handler +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +NMI: + inc Frame ; Frame++ + + lda Frame ; Increment Clock60 every time we reach 60 frames (NTSC = 60Hz) + cmp #60 ; Is Frame equal to #60? + bne :+ ; If not, bypass Clock60 increment + inc Clock60 ; But if it is 60, then increment Clock60 and zero Frame counter + lda #0 + sta Frame +: + rti ; Return from interrupt + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IRQ interrupt handler +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +IRQ: + rti ; Return from interrupt + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Hardcoded list of color values in ROM to be loaded by the PPU +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +PaletteData: +.byte $22,$29,$1A,$0F, $22,$36,$17,$0F, $22,$30,$21,$0F, $22,$27,$17,$0F ; Background palette +.byte $22,$16,$27,$18, $22,$1A,$30,$27, $22,$16,$30,$27, $22,$0F,$36,$17 ; Sprite palette + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Background data with tile numbers that must be copied to the nametable +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +BackgroundData: +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$36,$37,$36,$37,$36,$37,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$35,$25,$25,$25,$25,$25,$25,$38,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$36,$37,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$39,$3A,$3B,$3A,$3B,$3A,$3B,$3C,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$35,$25,$25,$38,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$39,$3A,$3B,$3C,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$53,$54,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$55,$56,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$53,$54,$24,$24,$24,$24,$24,$24,$24,$24,$45,$45,$53,$54,$45,$45,$53,$54,$45,$45,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$55,$56,$24,$24,$24,$24,$24,$24,$24,$24,$47,$47,$55,$56,$47,$47,$55,$56,$47,$47,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$60,$61,$62,$63,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$24,$31,$32,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$24,$64,$65,$66,$67,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$24,$30,$26,$34,$33,$24,$24,$24,$24,$36,$37,$36,$37,$24,$24,$24,$24,$24,$24,$24,$68,$69,$26,$6A,$24,$24,$24,$24 +.byte $24,$24,$24,$24,$30,$26,$26,$26,$26,$33,$24,$24,$35,$25,$25,$25,$25,$38,$24,$24,$24,$24,$24,$24,$68,$69,$26,$6A,$24,$24,$24,$24 +.byte $B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5 +.byte $B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7 +.byte $B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B6 +.byte $B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7 +.byte $B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5,$B4,$B5 +.byte $B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7,$B6,$B7 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Attributes tell which palette is used by a group of tiles in the nametable +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +AttributeData: +.byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 +.byte %00000000, %10101010, %10101010, %00000000, %00000000, %00000000, %10101010, %00000000 +.byte %00000000, %00000000, %00000000, %00000000, %11111111, %00000000, %00000000, %00000000 +.byte %00000000, %00000000, %10101010, %10101010, %10101010, %10101010, %00000000, %00000000 +.byte %11111111, %00000000, %00000000, %00001111, %00001111, %00000011, %00000000, %00000000 +.byte %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000, %00000000 +.byte %11111111, %11111111, %11111111, %11111111, %11111111, %11111111, %11111111, %11111111 +.byte %11111111, %11111111, %11111111, %11111111, %11111111, %11111111, %11111111, %11111111 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Hardcoded ASCII message stored in ROM with 0-terminator +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +TextMessage: +.byte "HELLO WORLD",$0 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Here we add the CHR-ROM data, included from an external .CHR file +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.segment "CHARS" +.incbin "mario.chr" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Vectors with the addresses of the handlers that we always add at $FFFA +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.segment "VECTORS" +.word NMI ; Address (2 bytes) of the NMI handler +.word Reset ; Address (2 bytes) of the Reset handler +.word IRQ ; Address (2 bytes) of the IRQ handler diff --git a/loadtext/loadtext.nes b/loadtext/loadtext.nes new file mode 100644 index 0000000..378f4c2 Binary files /dev/null and b/loadtext/loadtext.nes differ diff --git a/loadtext/loadtext.o b/loadtext/loadtext.o new file mode 100644 index 0000000..2660b08 Binary files /dev/null and b/loadtext/loadtext.o differ diff --git a/loadtext/mario.chr b/loadtext/mario.chr new file mode 100755 index 0000000..0e3a3cb Binary files /dev/null and b/loadtext/mario.chr differ diff --git a/loadtext/nes.cfg b/loadtext/nes.cfg new file mode 100755 index 0000000..7daa74b --- /dev/null +++ b/loadtext/nes.cfg @@ -0,0 +1,16 @@ +MEMORY { + ZP: start = $0000, size = $0100, type = rw, file = ""; + OAM: start = $0200, size = $0100, type = rw, file = ""; + RAM: start = $0300, size = $0500, type = rw, file = ""; + HDR: start = $7FF0, size = $0010, type = ro, file = %O, fill = yes, fillval = $00; + PRG: start = $8000, size = $8000, type = ro, file = %O, fill = yes, fillval = $00; + CHR: start = $0000, size = $2000, type = ro, file = %O, fill = yes, fillval = $00; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + HEADER: load = HDR, type = ro; + CODE: load = PRG, type = ro, start = $8000; + CHARS: load = CHR, type = ro, optional = yes; + VECTORS: load = PRG, type = ro, start = $FFFA; +} \ No newline at end of file diff --git a/loadtext/reset.inc b/loadtext/reset.inc new file mode 100755 index 0000000..7895424 --- /dev/null +++ b/loadtext/reset.inc @@ -0,0 +1,39 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Macro to initialize the NES by disabling rendering and clearing RAM values +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.macro INIT_NES + sei ; Disable all IRQ interrupts + cld ; Clear decimal mode (not supported by the NES) + ldx #$FF + txs ; Initialize the stack pointer at address $FF + + inx ; Increment X, causing a rolloff from $FF to $00 + stx PPU_CTRL ; Disable NMI + stx PPU_MASK ; Disable rendering (masking background and sprites) + stx $4010 ; Disable DMC IRQs + + lda #$40 + sta $4017 ; Disable APU frame IRQ + + bit PPU_STATUS ; Read from PPU_STATUS to reset the VBlank flag +Wait1stVBlank: ; Wait for the first VBlank from the PPU + bit PPU_STATUS ; Perform a bit-wise check with the PPU_STATUS port + bpl Wait1stVBlank ; Loop until bit-7 (sign bit) is 1 (inside VBlank) + + txa ; A = 0 +ClearRAM: + sta $0000,x ; Zero RAM addresses from $0000 to $00FF + sta $0100,x ; Zero RAM addresses from $0100 to $01FF + sta $0200,x ; Zero RAM addresses from $0200 to $02FF + sta $0300,x ; Zero RAM addresses from $0300 to $03FF + sta $0400,x ; Zero RAM addresses from $0400 to $04FF + sta $0500,x ; Zero RAM addresses from $0500 to $05FF + sta $0600,x ; Zero RAM addresses from $0600 to $06FF + sta $0700,x ; Zero RAM addresses from $0700 to $07FF + inx + bne ClearRAM + +Wait2ndVBlank: ; Wait for the second VBlank from the PPU + bit PPU_STATUS ; Perform a bit-wise check with the PPU_STATUS port + bpl Wait2ndVBlank ; Loop until bit-7 (sign bit) is 1 (inside VBlank) +.endmacro diff --git a/loadtext/utils.inc b/loadtext/utils.inc new file mode 100755 index 0000000..47c9536 --- /dev/null +++ b/loadtext/utils.inc @@ -0,0 +1,12 @@ +.macro PPU_SETADDR addr + bit PPU_STATUS ; Read from PPU_STATUS to reset my address latch + lda #>addr ; Fetch the hi-byte from 16-bit address + sta PPU_ADDR ; Store the hi-byte into PPU_ADDR $2006 + lda #