; SLROM+NROM X IN 1 Multicart Menu ; By Farid (2015/05/12) ; WWW.Microbaz.Blogfa.Com ; Use Notepad++ for the best view ;;;;;;;;;;;;;iNES Header;;;;;;;;;;;;; .inesprg 2 ; 2 x 16KB PRG code .ineschr 1 ; 1 x 8KB CHR data .inesmap 0 ; Mapper 0 = NROM .inesmir 1 ; Background mirroring , 0=Horizontal , 1=Vertical ;;;;;;;;;;;;;Host game hacking data;;;;;;;;;;;;; PRG_BANK = $03 ; PRG bank number from 00 ~ 0F (PRG free address / 4000 = PRG bank number) CHR_BANK1 = $19 ; CHR bank number for PPU $0000 from 00 ~ 1F (TLP Page down count --> Convert to hex) CHR_BANK2 = $19 ; CHR bank number for PPU $1000 from 00 ~ 1F (TLP Page down count --> Convert to hex) ORG_RST = $C000 ; Original reset vector of the host game HARD_FREE = $FF00 ; Address of free space on the hard wired bank of the host game (need 50h free bytes) X_IN_ONE = $08 ; How many games are inside the cartridge (Min:$02, Max:$08) ;;;;;;;;;;;;;RAM Info;;;;;;;;;;;;; nam_low = $0000 ; Name table address pointer low byte nam_high = $0001 ; Name table address pointer high byte joy_pad = $0002 ; Joy pad1 data rom_sel = $0003 ; Current selected ROM switch_code = $0004 ; Game switch code ($0004 ~ $0009) spr_ram = $0200 ; Arrow sprite data ($0200 ~ $0203) ;****************************Raw Data****************************** ;;;;;;;;;;;;;Bank0 8000~9FFF;;;;;;;;;;;;; .bank 0 .org $8000 ;;;Empty ;;;;;;;;;;;;;Bank1 A000~BFFF;;;;;;;;;;;;; .bank 1 .org $B900 ; It is easy to find a bank with enough free space at the end of it ;;;;;;;;;;;;;Name table + Attribute data;;;;;;;;;;;;; nam_att: .incbin "menu.nam" ;;;;;;;;;;;;;Palette data;;;;;;;;;;;;; pal_data: .incbin "menu.pal" ;;;;;;;;;;;;;Arrow sprite data;;;;;;;;;;;;; arrow_spr: .db $36,$3C,$00,$38 ; Vertical, Tile, Effects, Horizontal ;;;;;;;;;;;;;Pointer vertical move data;;;;;;;;;;;;; ver_mov: .db $36,$46,$56,$66,$76,$86,$96,$A6 ; Arrow on ROM1, ROM2, ROM3, ROM4, ROM5, ROM6, ROM7, ROM8 ;;;;;;;;;;;;;Games switch registers;;;;;;;;;;;; switch_reg .db $01,$21,$41,$61,$81,$A1,$C1,$E1 ; Register of ROM1, ROM2, ROM3, ROM4, ROM5, ROM6, ROM7, ROM8 ;****************************Game switch routine (will run on ram)****************************** game_switch: STA $6800 ; Multicart large bank switch ;;;;;;;;;;;;;CHR bank switch;;;;;;;;;;;;; LDA #$80 STA $A000 ; Reset MMC1 register 1 LDA #$00 ; CHR bank number for PPU $0000 STA $A000 LSR A STA $A000 LSR A STA $A000 LSR A STA $A000 LSR A STA $A000 LDA #$80 STA $C000 ; Reset MMC1 register 1 LDA #$01 ; CHR bank number for PPU $1000 STA $C000 LSR A STA $C000 LSR A STA $C000 LSR A STA $C000 LSR A STA $C000 ;;;;;;;;;;;;;PRG Bank Switch;;;;;;;;;;;;; LDA #$80 STA $E000 ; Reset MMC1 register 3 LDA #$00 ; PRG bank number STA $E000 LSR A STA $E000 LSR A STA $E000 LSR A STA $E000 LSR A STA $E000 JMP [$FFFC] ;****************************Main Code****************************** main_code: ;;;;;;;;;;;;;Warm up;;;;;;;;;;;;; LDX #$00 warmup_loop: BIT $2002 BPL warmup_loop ; Do loop until vblank starts INX CPX #$04 ; How many frames for warm up BNE warmup_loop ;;;;;;;;;;;;;Clear CPU ram;;;;;;;;;;;;; LDA #$00 LDX #$00 clear_loop: STA $0000,x STA $0100,x STA $0200,x STA $0300,x STA $0400,x STA $0500,x STA $0600,x STA $0700,x INX BNE clear_loop ;;;;;;;;;;;;;Save game switch data to ram;;;;;;;;;;;;; LDX #$00 transfer_loop: LDA game_switch,x STA switch_code,x INX CPX #$54 ; How many bytes for game switch code BNE transfer_loop ;;;;;;;;;;;;;Save arrow sprites data to ram;;;;;;;;;;;;; LDX #$00 spr_loop: LDA arrow_spr,x ; Load arrow sprite data STA spr_ram,x ; Save arrow sprite data to ram INX CPX #$04 ; How many bytes for arrow sprites data BNE spr_loop ;;;;;;;;;;;;;CHR bank switch;;;;;;;;;;;;; JSR vblank_wait LDA #$80 STA $A000 ; Reset MMC1 register 1 LDA #CHR_BANK1 ; CHR bank number for PPU $0000 from 00 ~ 1F (TLP Page down count --> Convert to hex) STA $A000 LSR A STA $A000 LSR A STA $A000 LSR A STA $A000 LSR A STA $A000 LDA #$80 STA $C000 ; Reset MMC1 register 1 LDA #CHR_BANK2 ; CHR bank number for PPU $1000 from 00 ~ 1F (TLP Page down count --> Convert to hex) STA $C000 LSR A STA $C000 LSR A STA $C000 LSR A STA $C000 LSR A STA $C000 ;;;;;;;;;;;;;Palette routine;;;;;;;;;;;;; JSR vblank_wait LDA $2002 ; read PPU status to reset the high/low latch LDA #$3F STA $2006 LDA #$00 STA $2006 LDX #$00 palette_loop: LDA pal_data,x STA $2007 INX CPX #$20 ; How many bytes for palette data BNE palette_loop ;;;;;;;;;;;;;Name table + Attribute;;;;;;;;;;;;; JSR vblank_wait LDA $2002 ; Read PPU status to reset the high/low latch LDA #$20 STA $2006 ; Write the high byte of $2000 address LDA #$00 STA $2006 ; Write the low byte of $2000 address LDA #$00 ; Name table address pointer low byte STA nam_low LDA #$B9 ; Name table address pointer high byte STA nam_high LDX #$00 LDY #$00 nam_loop: LDA [nam_low], y ; Load name table and attribute data STA $2007 INY CPY #$00 ; Small loop runs 100 times BNE nam_loop INC nam_high INX CPX #$04 ; Big loop runs 4 times BNE nam_loop ;;;;;;;;;;;;;Show screen;;;;;;;;;;;;; JSR vblank_wait JSR spr_to_oam JSR no_scroll LDA #$00 STA $2000 ; Disable NMI LDA #%00011000 ; Show background , Show sprites STA $2001 ; Enable rendering LDA #$00 STA rom_sel ; On start up arrow is on the first game ;;;;;;;;;;;;;Infinite Loop;;;;;;;;;;;;; infinit_loop: JSR pad_read JSR pad_check JMP infinit_loop ;****************************Sub Routines****************************** ;;;;;;;;;;;;;Vblank wait;;;;;;;;;;;;; vblank_wait: BIT $2002 ; Skip any halfway vblank vblank_loop: BIT $2002 BPL vblank_loop ; Do loop until a new vblank starts RTS ;----------------------- ;;;;;;;;;;;;;Sprites to OAM;;;;;;;;;;;;; spr_to_oam: LDA #$00 ; Sprites to OAM Transfer STA $2003 LDA #$02 ; Sprites address on the ram ($0200) STA $4014 RTS ;----------------------- ;;;;;;;;;;;;;Scroll reset;;;;;;;;;;;;; no_scroll: LDA #$00 STA $2005 STA $2005 RTS ;----------------------- ;;;;;;;;;;;;;Sound routine;;;;;;;;;;;;; sound: LDA #$0F STA $4015 ; Enable sound channels LDA #$1F STA $4004 LDA #$99 STA $4005 LDA #$EF STA $4006 LDA #$08 STA $4007 RTS ;----------------------- ;;;;;;;;;;;;;Controller pad read;;;;;;;;;;;;; pad_read: LDA #$01 STA $4016 LDA #$00 STA $4016 LDX #$00 pad_loop: LDA $4016 LSR A ROL joy_pad ; Controller pad read buffer INX CPX #$08 ; Read status of all eight keys BNE pad_loop RTS ;----------------------- ;;;;;;;;;;;;;Controller pad check;;;;;;;;;;;;; pad_check: LDA #$08 ; Check for up button press CMP joy_pad BEQ up_press LDA #$04 ; Check for down button press CMP joy_pad BEQ down_press LDA #$10 ; Check for start button press CMP joy_pad BEQ start_press ; If start button was pressed then time to run a game! RTS ;----------------------- ;;;;;;;;;;;;;Button release;;;;;;;;;;;;; button_release: JSR pad_read LDA #$00 CMP joy_pad BNE button_release ; Do loop until all buttons are released LDX #$00 delay_loop: JSR vblank_wait INX CPX #$08 ; How many frames for delay before the next pad check BNE delay_loop RTS ;----------------------- ;;;;;;;;;;;;;Up button press;;;;;;;;;;;;; up_press: LDA rom_sel ; Current selected ROM CMP #$00 ; Up limit BEQ is_first ; Check if arrow was on the first game DEC rom_sel ; Update selected ROM LDX rom_sel LDA ver_mov,x ; Load arrow move data STA spr_ram ; Update arrow vertical position JSR vblank_wait JSR spr_to_oam JSR no_scroll JSR sound JSR button_release ; Check if the button is released is_first: RTS ;----------------------- ;;;;;;;;;;;;;Down button press;;;;;;;;;;;;; down_press: LDA rom_sel ; Current selected ROM CMP #X_IN_ONE-1 ; Down limit BEQ is_last ; Check if arrow was on the last game INC rom_sel ; Update selected ROM LDX rom_sel LDA ver_mov,x ; Load arrow move data STA spr_ram ; Update arrow vertical position JSR vblank_wait JSR spr_to_oam JSR no_scroll JSR sound JSR button_release ; Check if the button is released is_last RTS ;----------------------- ;;;;;;;;;;;;;Start button press;;;;;;;;;;;;; start_press: JSR button_release ; Check if the button is released LDA #$00 STA $2000 STA $2001 STA $4004 STA $4005 STA $4006 STA $4007 STA $4015 LDX rom_sel LDA switch_reg,x CPX #$00 BEQ game1 ; Check if ROM1 is going to run JMP switch_code ; Game switch routine game1: STA $6800 ; Multicart game select register JMP ORG_RST ; Original reset vector of the host game ;;;;;;;;;;;;;Bank2 C000~DFFF;;;;;;;;;;;;; .bank 2 .org $C000 ;;;Empty ;;;;;;;;;;;;;Bank3 E000~FFFF;;;;;;;;;;;;; .bank 3 .org HARD_FREE RESET: SEI ; Disable IRQs CLD ; Disable decimal mode LDA #$40 STA $4017 ; Disable APU frame IRQ LDX #$FF TXS ; Set up stack LDA #$00 STA $2000 ; Disable NMI STA $2001 ; Disable rendering STA $4010 ; Disable DMC IRQs ;;;;;;;;;;;;;MMC1 Control;;;;;;;;;;;;; LDA #$80 STA $8000 ; Reset MMC1 register 0 LDA #%00011110 STA $8000 LSR A STA $8000 LSR A STA $8000 LSR A STA $8000 LSR A STA $8000 ;;;;;;;;;;;;;PRG Bank Switch;;;;;;;;;;;;; LDA #$80 STA $E000 ; Reset MMC1 register 3 LDA #PRG_BANK ; PRG bank number (PRG free address / 4000 = PRG bank number) STA $E000 LSR A STA $E000 LSR A STA $E000 LSR A STA $E000 LSR A STA $E000 JMP main_code NMI: RTI ; Original NMI routine of the host game IRQ: RTI ; Original IRQ routine of the host game ;;;;;;;;;;;;;Vectors;;;;;;;;;;;;; .org $FFFA .dw NMI .dw RESET .dw IRQ ;;;;;;;;;;;;;CHR Data;;;;;;;;;;;;; .bank 4 .org $0000 .incbin "menu.chr"