Compare commits

...

2 Commits

5 changed files with 110 additions and 33 deletions

4
.gitignore vendored
View File

@ -1,3 +1,3 @@
src/smb.chr
src/smbdis.o
src/chars.bin
src/*.o
target/

View File

@ -24,7 +24,7 @@ $ cd super_mario_bros_tale_of_the_black_koopa
Copy the graphics file:
```shell
$ cp <path_to_the_graphics_file> src/smb.chr
$ cp <path_to_the_graphics_file> src/chars.bin
```
### Building
@ -32,12 +32,12 @@ $ cp <path_to_the_graphics_file> src/smb.chr
To build the binary, execute:
```shell
$ cl65 -t nes -o target/smb.nes -C src/smb.cfg src/smbdis.asm
$ cl65 -t nes -o target/super_mario_bros_tale_of_the_black_koopa.nes -C src/linker.cfg src/main.asm
```
### Running
To run the game, open the file `target/smb.nes` in your favorite NES emulator. Alternatively you can run it on original hardware.
To run the game, open the file `target/super_mario_bros_tale_of_the_black_koopa.nes` in your favorite NES emulator. Alternatively you can run it on original hardware.
### Playing

25
src/linker.cfg Normal file
View File

@ -0,0 +1,25 @@
MEMORY {
ZP: file = "", start = $0000, size = $0100;
OAM: file = "", start = $0200, size = $0100, define = yes;
RAM: file = "", start = $0300, size = $0500;
HDR: file = %O, start = $0000, size = $0010, fill = yes;
PRG0: file = %O, start = $8000, size = $4000, fill = yes, fillval = $FF;
PRG1: file = %O, start = $8000, size = $4000, fill = yes, fillval = $FF;
PRG2: file = %O, start = $8000, size = $4000, fill = yes, fillval = $FF;
PRG3: file = %O, start = $C000, size = $3FFA, fill = yes, fillval = $FF;
PRGV: file = %O, start = $FFFA, size = $0006, fill = yes, fillval = $FF;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, optional = yes;
SPRITE: load = OAM, type = bss, optional = yes;
BSS: load = RAM, type = bss, optional = yes;
HEADER: load = HDR, type = ro;
STARTUP: load = PRG0, type = ro;
CHARS: load = PRG2, type = ro, optional = yes;
COPYCHARS: load = PRG2, type = ro, optional = yes;
SOUNDENGINE: load = PRG2, type = ro, optional = yes;
CODE: load = PRG3, type = ro, optional = yes;
BANKSWITCH: load = PRG3, type = ro, optional = yes;
VECTORS: load = PRGV, type = ro;
}

View File

@ -529,6 +529,9 @@ PauseModeFlag = $07c6
GroundMusicHeaderOfs = $07c7
AltRegContentFlag = $07ca
CurrentBank = $15 ; The mapper is read-only; need to track its state separately
CharsCopy = $41 ; Reserving $41 and $42
;-------------------------------------------------------------------------------------
;CONSTANTS
@ -647,14 +650,14 @@ GameOverModeValue = 3
.segment "HEADER"
.byte "NES", $1a ; Identification String "NES<EOF>"
.byte 2 ; PRG-ROM is 32 KiB
.byte 1 ; CHR-ROM is 16 KiB
.byte %00000001 ; Mapper 0 (NROM), Vertical hard-wired mirroring (horizontal arrangement)
.byte 4 ; PRG-ROM is 64 KiB
.byte 0 ; CHR-ROM is 0 KiB
.byte %00100001 ; Mapper 2 (UNROM), Vertical hard-wired mirroring (horizontal arrangement)
.byte %00001000 ; NES 2.0 header format, Nintendo Entertainment System/Family Computer
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000000
.byte %00000111 ; CHR-RAM is 8192 bytes
.byte %00000010 ; Multiple-region
.byte %00000000
.byte %00000000
@ -697,6 +700,7 @@ ColdBoot: jsr InitializeMemory ;clear memory using pointer in Y
jsr MoveAllSpritesOffscreen
jsr InitializeNameTables ;initialize both name tables
inc DisableScreenFlag ;set flag to disable screen output
jsr CopyCharsBankSwitch
lda Mirror_PPU_CTRL_REG1
ora #%10000000 ;enable NMIs
jsr WritePPUReg1
@ -766,7 +770,7 @@ InitBuffer: ldx VRAM_Buffer_Offset,y
sta VRAM_Buffer_AddrCtrl ;reinit address control to $0301
lda Mirror_PPU_CTRL_REG2 ;copy mirror of $2001 to register
sta PPU_CTRL_REG2
jsr SoundEngine ;play sound
jsr SoundEngineBankSwitch ;play sound
jsr ReadJoypads ;read joypads
jsr PauseRoutine ;handle pause
jsr UpdateTopScore
@ -835,7 +839,7 @@ SkipMainOper: lda PPU_STATUS ;reset flip-flop
pla
ora #%10000000 ;reactivate NMIs
sta PPU_CTRL_REG1
rti ;we are done until the next frame!
rts ;we are done until the next frame!
;-------------------------------------------------------------------------------------
@ -7685,6 +7689,8 @@ RedPTroopaGrav:
ldx ObjectOffset ;get enemy object offset and leave
rts
.segment "CODE"
;-------------------------------------------------------------------------------------
;$00 - used for downward force
;$01 - used for upward force
@ -15031,6 +15037,8 @@ SetHFAt: ora $04 ;add other OAM attributes if necessary
;-------------------------------------------------------------------------------------
.segment "SOUNDENGINE"
SoundEngine:
lda OperMode ;are we in title screen mode?
bne SndOn
@ -16335,14 +16343,77 @@ BrickShatterEnvData:
;-------------------------------------------------------------------------------------
.segment "BANKSWITCH"
StartBankSwitch:
ldx #$00
jsr BankSwitch
jmp Start
NonMaskableInterruptBankSwitch:
ldx #$00
jsr BankSwitchNoSave
jsr NonMaskableInterrupt
ldx CurrentBank
jsr BankSwitchNoSave
rti
CopyCharsBankSwitch:
ldx #$02
jsr BankSwitch
jsr CopyChars
ldx #$00
jsr BankSwitch
rts
SoundEngineBankSwitch:
ldx #$02
jsr BankSwitchNoSave
jsr SoundEngine
ldx #$00
jsr BankSwitchNoSave
rts
BankSwitch:
stx CurrentBank ; save the current bank in RAM so the NMI handler can restore it
BankSwitchNoSave:
lda BankTable,x ; remap from the convenient banks numbered 0 through 2 to the value needed by the hardware
sta BankTable,x ; and write it to the corresponding entry, switching banks
rts
BankTable:
.byte $00, $01, $02
.segment "VECTORS"
;INTERRUPT VECTORS
.addr NonMaskableInterrupt
.addr Start
.addr NonMaskableInterruptBankSwitch
.addr StartBankSwitch
.addr $fff0 ;unused
.segment "CHARS"
.incbin "smb.chr"
Chars: .incbin "chars.bin"
.segment "COPYCHARS"
CopyChars:
lda #<Chars ; load the source address into a pointer in zero page
sta CharsCopy
lda #>Chars
sta CharsCopy+1
ldy #$0 ; starting index into the first page
sty PPU_CTRL_REG2 ; turn off rendering just in case
sty PPU_ADDRESS ; load the destination address into the PPU
sty PPU_ADDRESS
ldx #$20 ; number of 256-byte pages to copy
LoopCopyChars:
lda (CharsCopy),y ; copy one byte
sta PPU_DATA
iny
bne LoopCopyChars ; repeat until we finish the page
inc CharsCopy+1 ; go to the next page
dex
bne LoopCopyChars ; repeat until we've copied enough pages
rts

View File

@ -1,19 +0,0 @@
MEMORY {
ZP: file = "", start = $0000, size = $0100;
OAM: file = "", start = $0200, size = $0100, define = yes;
RAM: file = "", start = $0300, size = $0500;
HDR: file = %O, start = $0000, size = $0010, fill = yes;
PRG0: file = %O, start = $8000, size = $7FFA, fill = yes, fillval = $FF;
PRGV: file = %O, start = $FFFA, size = $0006, fill = yes, fillval = $FF;
CHR: file = %O, start = $0000, size = $2000, fill = yes, fillval = $FF;
}
SEGMENTS {
ZEROPAGE: load = ZP, type = zp, optional = yes;
SPRITE: load = OAM, type = bss, optional = yes;
BSS: load = RAM, type = bss, optional = yes;
HEADER: load = HDR, type = ro;
STARTUP: load = PRG0, type = ro;
VECTORS: load = PRGV, type = ro;
CHARS: load = CHR, type = ro, optional = yes;
}