a bunch more work

This commit is contained in:
Tyrel Souza 2022-12-04 23:13:27 -05:00
parent 5b73cf27be
commit cf00fcdaa4
49 changed files with 1681 additions and 1 deletions

BIN
atlantico/._atlantico.asm Executable file

Binary file not shown.

BIN
atlantico/._nes.cfg Executable file

Binary file not shown.

BIN
atlantico/._reset.inc Executable file

Binary file not shown.

BIN
atlantico/._utils.inc Executable file

Binary file not shown.

18
atlantico/Makefile Executable file
View File

@ -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"

17
atlantico/actor.inc Normal file
View File

@ -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

707
atlantico/atlantico.asm Executable file
View File

@ -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 lo-byte
sta SourceAddr ; save result of offset back to the source address low byte
lda SourceAddr+1 ; hibyte of the column source address
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 ; lo byte of where attrib is in rom
sta SourceAddr
lda SourceAddr+1
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

BIN
atlantico/atlantico.chr Executable file

Binary file not shown.

BIN
atlantico/atlantico.nes Normal file

Binary file not shown.

BIN
atlantico/atlantico.o Normal file

Binary file not shown.

29
atlantico/consts.inc Executable file
View File

@ -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

13
atlantico/header.inc Executable file
View File

@ -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

16
atlantico/nes.cfg Executable file
View File

@ -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;
}

43
atlantico/reset.inc Executable file
View File

@ -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

49
atlantico/tagbardebug.log Normal file
View File

@ -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

37
atlantico/utils.inc Executable file
View File

@ -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 #<addr ; Fetch the lo-byte from 16-bit address
sta PPU_ADDR ; Store the lo-byte into PPU_ADDR $2006
.endmacro
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Macro to send a value to the PPU_DATA (at $2007)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.macro PPU_SETDATA val
lda val
sta PPU_DATA ; Send value to PPU register at $2007
.endmacro
.macro PUSH_REGS
pha ; Push A to the stack
txa ; transfer x to acc
pha ; Push X to the stack
tya ; transfer y to the acc
pha ; Push Y to the stack
php ; Push process status flags to the stack
.endmacro
.macro PULL_REGS
plp ; restore the status flags from the stack
pla ; restore the old value of Y from the stack into A
tay ; transfer
pla ; restore the old value of X from the stack into A
tax ; transfer
pla ; Pull a from the stack
.endmacro

10
bgnam.py Normal file
View File

@ -0,0 +1,10 @@
with open('background.nam', 'rb') as f:
i = 0
while True:
hexdata = f.read(1).hex()
if len(hexdata) == 0:
break
if i % 32 == 0:
print()
print('$' + hexdata.upper(), end = ',')
i = i + 1

BIN
clearmem.nes Normal file

Binary file not shown.

BIN
clearmem.o Normal file

Binary file not shown.

View File

@ -15,4 +15,4 @@ clean:
# Rule to run the final cartridge .nes file in the FCEUX emulator # Rule to run the final cartridge .nes file in the FCEUX emulator
############################################################################### ###############################################################################
run: run:
fceux helloppu.nes on-workspace 1 "fceux helloppu.nes"

BIN
helloppu/helloppu.nes Normal file

Binary file not shown.

BIN
helloppu/helloppu.o Normal file

Binary file not shown.

BIN
loadbackground/._Makefile Executable file

Binary file not shown.

Binary file not shown.

BIN
loadbackground/._nes.cfg Executable file

Binary file not shown.

18
loadbackground/Makefile Executable file
View File

@ -0,0 +1,18 @@
###############################################################################
# Rule to assemble and link all assembly files
###############################################################################
build:
ca65 loadbackground.asm -o loadbackground.o
ld65 -C nes.cfg loadbackground.o -o loadbackground.nes
###############################################################################
# Rule to remove all object (.o) files and cartridge (.nes) files
###############################################################################
clean:
rm *.o *.nes
###############################################################################
# Rule to run the final cartridge .nes file in the FCEUX emulator
###############################################################################
run:
on-workspace 1 "fceux loadbackground.nes"

BIN
loadbackground/background.nam Executable file

Binary file not shown.

31
loadbackground/consts.inc Executable file
View File

@ -0,0 +1,31 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Constants for 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

13
loadbackground/header.inc Executable file
View File

@ -0,0 +1,13 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The iNES header (contains a total of 16 bytes with the flags at $7F00)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.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 %00000000 ; Horz 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

306
loadbackground/loadbackground.asm Executable file
View File

@ -0,0 +1,306 @@
; HEADER
.include "consts.inc"
.include "header.inc"
.include "reset.inc"
.include "utils.inc"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.segment "ZEROPAGE"
Buttons: .res 1 ; Button bytes
XPos: .res 1 ; Char X position
YPos: .res 1 ; Char Y Position
Frame: .res 1 ; Reserve 1 byte to store the number of frames
Clock60: .res 1 ; Store a counter that increments every second (60 frames)
BgPtr: .res 2 ; Reserve 2 bytes store a ptr to bg address
; Store low then high bytes (little endian)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PRG-ROM code located at $8000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.segment "CODE"
; 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
; LoadBackground
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Subroutine to load the background 255 tiles in first nametable
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.proc LoadBackground
lda #<BackgroundData
sta BgPtr
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

Binary file not shown.

Binary file not shown.

BIN
loadbackground/mario.chr Executable file

Binary file not shown.

16
loadbackground/nes.cfg Executable file
View File

@ -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;
}

43
loadbackground/reset.inc Executable file
View File

@ -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

12
loadbackground/utils.inc Normal file
View File

@ -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 #<addr ; < means get low byte from `addr`
sta PPU_ADDR
.endmacro
.macro PPU_SETDATA val
lda val
sta PPU_DATA
.endmacro

BIN
loadtext/._Makefile Executable file

Binary file not shown.

BIN
loadtext/._loadtext.asm Executable file

Binary file not shown.

BIN
loadtext/._nes.cfg Executable file

Binary file not shown.

18
loadtext/Makefile Executable file
View File

@ -0,0 +1,18 @@
###############################################################################
# Rule to assemble and link all assembly files
###############################################################################
build:
ca65 loadtext.asm -o loadtext.o
ld65 -C nes.cfg loadtext.o -o loadtext.nes
###############################################################################
# Rule to remove all object (.o) files and cartridge (.nes) files
###############################################################################
clean:
rm *.o *.nes
###############################################################################
# Rule to run the final cartridge .nes file in the FCEUX emulator
###############################################################################
run:
fceux loadtext.nes

11
loadtext/consts.inc Executable file
View File

@ -0,0 +1,11 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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

13
loadtext/header.inc Executable file
View File

@ -0,0 +1,13 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; The iNES header (contains a total of 16 bytes with the flags at $7F00)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.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 %00000000 ; Horz 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

193
loadtext/loadtext.asm Executable file
View File

@ -0,0 +1,193 @@
.include "consts.inc"
.include "header.inc"
.include "reset.inc"
.include "utils.inc"
.segment "ZEROPAGE"
Frame: .res 1 ; Reserve 1 byte to store the number of frames
Clock60: .res 1 ; Reserve 1 byte to store a counter that increments every second (60 frames)
BgPtr: .res 2 ; Reserve 2 bytes (16 bits) to store a pointer to the background address
; (we store first the lo-byte, and immediately after, the hi-byte) - little endian
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; PRG-ROM code located at $8000
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.segment "CODE"
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Subroutine to load tiles and attributes into the first nametable
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
.proc LoadBackground
lda #<BackgroundData ; Fetch the lo-byte of BackgroundData address
sta BgPtr
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

BIN
loadtext/loadtext.nes Normal file

Binary file not shown.

BIN
loadtext/loadtext.o Normal file

Binary file not shown.

BIN
loadtext/mario.chr Executable file

Binary file not shown.

16
loadtext/nes.cfg Executable file
View File

@ -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;
}

39
loadtext/reset.inc Executable file
View File

@ -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

12
loadtext/utils.inc Executable file
View File

@ -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 #<addr ; Fetch the lo-byte from 16-bit address
sta PPU_ADDR ; Store the lo-byte into PPU_ADDR $2006
.endmacro
.macro PPU_SETDATA val
lda val
sta PPU_DATA ; Send value to PPU register $2007
.endmacro