Skip to content

Commit

Permalink
Fix various issues with MMC1.
Browse files Browse the repository at this point in the history
  • Loading branch information
mysterymath committed Oct 8, 2022
1 parent 219420a commit 4d03950
Show file tree
Hide file tree
Showing 7 changed files with 182 additions and 85 deletions.
2 changes: 1 addition & 1 deletion mos-platform/nes-mmc1/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ add_platform_object_file(nes-mmc1-init-prg-rom-32-banked-o
add_platform_object_file(nes-mmc1-init-prg-ram-0-o
init-prg-ram-0.o init-prg-ram-0.s)

add_platform_library(nes-mmc1-c bank.c)
add_platform_library(nes-mmc1-c bank.c bank.s)
merge_libraries(nes-mmc1-c common-c)
2 changes: 2 additions & 0 deletions mos-platform/nes-mmc1/_prg-rom-banked.ld
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ SECTIONS {
.prg_rom_0 : OVERLAY { *(.prg_rom_0 .prg_rom_0.*) } >prg_rom_0_vma AT>prg_rom_0_lma
.prg_rom_1 : OVERLAY { *(.prg_rom_1 .prg_rom_1.*) } >prg_rom_1_vma AT>prg_rom_1_lma
.prg_rom_2 : OVERLAY { *(.prg_rom_2 .prg_rom_2.*) } >prg_rom_2_vma AT>prg_rom_2_lma
.prg_rom_3 : OVERLAY { *(.prg_rom_3 .prg_rom_3.*) } >prg_rom_3_vma AT>prg_rom_3_lma
.prg_rom_4 : OVERLAY { *(.prg_rom_4 .prg_rom_4.*) } >prg_rom_4_vma AT>prg_rom_4_lma
.prg_rom_5 : OVERLAY { *(.prg_rom_5 .prg_rom_5.*) } >prg_rom_5_vma AT>prg_rom_5_lma
.prg_rom_6 : OVERLAY { *(.prg_rom_6 .prg_rom_6.*) } >prg_rom_6_vma AT>prg_rom_6_lma
Expand All @@ -71,4 +72,5 @@ SECTIONS {
.prg_rom_12 : OVERLAY { *(.prg_rom_12 .prg_rom_12.*) } >prg_rom_12_vma AT>prg_rom_12_lma
.prg_rom_13 : OVERLAY { *(.prg_rom_13 .prg_rom_13.*) } >prg_rom_13_vma AT>prg_rom_13_lma
.prg_rom_14 : OVERLAY { *(.prg_rom_14 .prg_rom_14.*) } >prg_rom_14_vma AT>prg_rom_14_lma
.prg_rom_15 : OVERLAY { *(.prg_rom_15 .prg_rom_15.*) } >prg_rom_15_vma AT>prg_rom_15_lma
}
81 changes: 33 additions & 48 deletions mos-platform/nes-mmc1/bank.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,18 @@

#include <peekpoke.h>

static char PRG_BANK;
static char CHR_BANK0;
static char CHR_BANK1;
static char MMC1_CTRL_NMI;
__attribute__((section(".zp.bss"))) char _PRG_BANK;
__attribute__((section(".zp.bss"))) char _CHR_BANK0;
__attribute__((section(".zp.bss"))) char _CHR_BANK1;
__attribute__((section(".zp.data"))) char _MMC1_CTRL_NMI = 0x1f;

char _CHR_BANK0_CUR;
__attribute__((section(".zp.bss"))) char _CHR_BANK0_CUR;
extern __attribute__((
weak, alias("_CHR_BANK0_CUR"))) volatile const char CHR_BANK0_CUR;
char _CHR_BANK1_CUR;
__attribute__((section(".zp.bss"))) char _CHR_BANK1_CUR;
extern __attribute__((
weak, alias("_CHR_BANK1_CUR"))) volatile const char CHR_BANK1_CUR;
char _MMC1_CTRL_CUR;
__attribute__((section(".zp.bss"))) char _MMC1_CTRL_CUR;
extern __attribute__((
weak, alias("_MMC1_CTRL_CUR"))) volatile const char MMC1_CTRL_CUR;

Expand All @@ -49,9 +49,12 @@ extern __attribute__((
#define MMC1_PRG 0xe000

// Incrementing a 0xff ROM byte resets the MMC.
__attribute__((used)) static const char reset_mmc1_byte = 0xff;
__attribute__((used)) const char __reset_mmc1_byte = 0xff;
static void reset_shift_register(void) {
__attribute__((leaf)) asm volatile("inc __reset_mmc1_byte");
}

static volatile char IN_PROGRESS = 0;
volatile char _IN_PROGRESS = 0;

__attribute__((always_inline)) static inline void
mmc1_register_write(unsigned addr, char val) {
Expand All @@ -68,81 +71,63 @@ mmc1_register_write(unsigned addr, char val) {

__attribute__((always_inline)) static inline void
mmc1_register_write_retry(unsigned addr, char val) {
// May be interrupting another write, so reset the shift register.
asm volatile("inc reset_mmc1_byte");
do {
IN_PROGRESS = 1;
_IN_PROGRESS = 1;
reset_shift_register();
mmc1_register_write(addr, val);
// Was interrupted, and we may have written a few bytes afterwards, so reset
// the shift register and try again until successful.
if (!IN_PROGRESS)
asm volatile("inc reset_mmc1_byte");
} while (!IN_PROGRESS);
IN_PROGRESS = 0;
} while (!_IN_PROGRESS);
_IN_PROGRESS = 0;
}

__attribute__((weak)) void banked_call(char bankId, void (*method)(void)) {
__attribute__((noinline, weak)) void banked_call(char bank_id,
void (*method)(void)) {
char old_id = get_prg_bank();
set_prg_bank(bank_id);
method();
set_prg_bank(old_id);
}

__attribute__((weak)) void set_prg_bank(char bank_id) {
PRG_BANK = bank_id;
mmc1_register_write_retry(MMC1_PRG, bank_id);
__attribute__((weak)) void set_chr_bank_0(char bank_id) {
_CHR_BANK0 = bank_id;
}

__attribute__((weak)) char get_prg_bank(void) { return PRG_BANK; }

__attribute__((weak)) void set_chr_bank_0(char bank_id) { CHR_BANK0 = bank_id; }

__attribute__((weak)) void set_chr_bank_1(char bank_id) { CHR_BANK1 = bank_id; }
__attribute__((weak)) void set_chr_bank_1(char bank_id) {
_CHR_BANK1 = bank_id;
}

__attribute__((weak)) void split_chr_bank_0(char bank_id) {
asm volatile("inc reset_mmc1_byte");
reset_shift_register();
mmc1_register_write(MMC1_CHR0, bank_id);
IN_PROGRESS = 0;
_IN_PROGRESS = 0;
_CHR_BANK0_CUR = bank_id;
}

__attribute__((weak)) void split_chr_bank_1(char bank_id) {
asm volatile("inc reset_mmc1_byte");
reset_shift_register();
mmc1_register_write(MMC1_CHR1, bank_id);
IN_PROGRESS = 0;
_IN_PROGRESS = 0;
_CHR_BANK1_CUR = bank_id;
}

__attribute__((weak)) void set_chr_bank_0_retry(char bank_id) {
CHR_BANK0 = bank_id;
_CHR_BANK0 = bank_id;
mmc1_register_write_retry(MMC1_CHR0, bank_id);
}

__attribute__((weak)) void set_chr_bank_1_retry(char bank_id) {
CHR_BANK1 = bank_id;
_CHR_BANK1 = bank_id;
mmc1_register_write_retry(MMC1_CHR1, bank_id);
}

__attribute__((weak)) void set_mirroring(char mirroring) {
MMC1_CTRL_NMI &= 0b11100;
MMC1_CTRL_NMI |= mirroring & 0b11;
_MMC1_CTRL_NMI &= 0b11100;
_MMC1_CTRL_NMI |= mirroring & 0b11;
}

__attribute__((weak)) void set_mmc1_ctrl(char value) {
MMC1_CTRL_NMI = value;
_MMC1_CTRL_NMI = value;
mmc1_register_write_retry(MMC1_CTRL, value);
_MMC1_CTRL_CUR = value;
}

// some things deleted

void __nmi_bank_handler(void) {
asm volatile("inc reset_mmc1_byte");
mmc1_register_write(MMC1_CHR0, CHR_BANK0);
_CHR_BANK0_CUR = CHR_BANK0;
mmc1_register_write(MMC1_CHR1, CHR_BANK1);
_CHR_BANK1_CUR = CHR_BANK0;
mmc1_register_write(MMC1_CTRL, MMC1_CTRL_NMI);
_MMC1_CTRL_CUR = MMC1_CTRL_NMI;
IN_PROGRESS = 0;
}
__attribute__((weak, alias("__nmi_bank_handler"))) void nmi_bank_handler(void);
18 changes: 3 additions & 15 deletions mos-platform/nes-mmc1/bank.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,25 +44,16 @@ extern volatile const char MMC1_CTRL_CUR;
// Switch to another bank and call this function.
// Note: Using banked_call to call a second function from within
// another banked_call is safe.
void banked_call(char bankId, void (*method)(void));

// Internal function used by banked_call(), don't call this directly.
// Switch to the given bank, and keep track of the current bank, so
// that we may jump back to it as needed.
void bank_push(char bankId);

// Internal function used by banked_call(), don't call this directly.
// Go back to the last bank pushed on using bank_push.
void bank_pop(void);
void banked_call(char bank_id, void (*method)(void));

// Switch to the given bank (to $8000-bfff). Your prior bank is not saved.
// Can be used for reading data with a function in the fixed bank.
// bank_id: The bank to switch to.
void set_prg_bank(char bank_id);
__attribute__((leaf)) void set_prg_bank(char bank_id);

// Get the current PRG bank at $8000-bfff.
// returns: The current bank.
char get_prg_bank(void);
__attribute__((leaf)) char get_prg_bank(void);

// Set the current 1st 4k chr bank to the bank with this id.
// this will take effect at the next frame
Expand Down Expand Up @@ -121,9 +112,6 @@ void set_mmc1_ctrl(char value);

// some things deleted

// To be called on NMI to set graphics-affecting banks.
void nmi_bank_handler(void);

#ifdef __cplusplus
}
#endif
Expand Down
88 changes: 88 additions & 0 deletions mos-platform/nes-mmc1/bank.s
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
; Copyright 2022 LLVM-MOS Project
; Licensed under the Apache License, Version 2.0 with LLVM Exceptions.
; See https://github.com/llvm-mos/llvm-mos-sdk/blob/main/LICENSE for license
; information.
;
; Copyright 2019 Doug Fraker
; Copyright 2018 Christopher Parker
;
; Permission is hereby granted, free of charge, to any person obtaining a copy
; of this software and associated documentation files (the "Software"), to deal
; in the Software without restriction, including without limitation the rights
; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
; copies of the Software, and to permit persons to whom the Software is
; furnished to do so, subject to the following conditions:
;
; The above copyright notice and this permission notice shall be included in
; all copies or substantial portions of the Software.
;
; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
; SOFTWARE.

MMC1_CTRL = $8000
MMC1_CHR0 = $a000
MMC1_CHR1 = $c000
MMC1_PRG = $e000

; macro to write to an mmc1 register, which goes one bit at a time, 5 bits wide.
.macro mmc1_register_write addr
.rept 4
sta \addr
lsr
.endr
sta \addr
.endmacro

.section .text.nmi_bank_handler,"ax",@progbits
.globl __nmi_bank_handler
.weak nmi_bank_handler
nmi_bank_handler:
__nmi_bank_handler:
inc __reset_mmc1_byte
lda _CHR_BANK0
sta _CHR_BANK0_CUR
mmc1_register_write MMC1_CHR0
lda _CHR_BANK1
sta _CHR_BANK1_CUR
mmc1_register_write MMC1_CHR1
lda _MMC1_CTRL_NMI
sta _MMC1_CTRL_CUR
mmc1_register_write MMC1_CTRL
lda #0
sta _IN_PROGRESS
rts

.section .text.get_prg_bank,"ax",@progbits
.globl __get_prg_bank
.weak get_prg_bank
__get_prg_bank:
get_prg_bank:
lda _PRG_BANK
rts

.section .text.set_prg_bank,"ax",@progbits
.globl __set_prg_bank
.weak set_prg_bank
__set_prg_bank:
set_prg_bank:
tay
.Lset:
inc __reset_mmc1_byte
ldx #1
stx _IN_PROGRESS
mmc1_register_write MMC1_PRG
ldx _IN_PROGRESS
beq .Lretry
dex
stx _IN_PROGRESS
sty _PRG_BANK
rts
.Lretry:
tya
jmp .Lset

34 changes: 34 additions & 0 deletions mos-platform/nes-mmc1/common.ld
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,23 @@ MEMORY {
chr_rom_29 : ORIGIN = 0x0201d000, LENGTH = __chr_rom_size >= 128 ? 0x1000 : 0
chr_rom_30 : ORIGIN = 0x0201e000, LENGTH = __chr_rom_size >= 128 ? 0x1000 : 0
chr_rom_31 : ORIGIN = 0x0201f000, LENGTH = __chr_rom_size >= 128 ? 0x1000 : 0

chr_rom_0_1 : ORIGIN = 0x02000000, LENGTH = __chr_rom_size >= 8 ? 0x2000 : 0
chr_rom_2_3 : ORIGIN = 0x02002000, LENGTH = __chr_rom_size >= 16 ? 0x2000 : 0
chr_rom_4_5 : ORIGIN = 0x02004000, LENGTH = __chr_rom_size >= 32 ? 0x2000 : 0
chr_rom_6_7 : ORIGIN = 0x02006000, LENGTH = __chr_rom_size >= 32 ? 0x2000 : 0
chr_rom_8_9 : ORIGIN = 0x02008000, LENGTH = __chr_rom_size >= 64 ? 0x2000 : 0
chr_rom_10_11 : ORIGIN = 0x0200a000, LENGTH = __chr_rom_size >= 64 ? 0x2000 : 0
chr_rom_12_13 : ORIGIN = 0x0200c000, LENGTH = __chr_rom_size >= 64 ? 0x2000 : 0
chr_rom_14_15 : ORIGIN = 0x0200e000, LENGTH = __chr_rom_size >= 64 ? 0x2000 : 0
chr_rom_16_17 : ORIGIN = 0x02010000, LENGTH = __chr_rom_size >= 128 ? 0x2000 : 0
chr_rom_18_19 : ORIGIN = 0x02012000, LENGTH = __chr_rom_size >= 128 ? 0x2000 : 0
chr_rom_20_21 : ORIGIN = 0x02014000, LENGTH = __chr_rom_size >= 128 ? 0x2000 : 0
chr_rom_22_23 : ORIGIN = 0x02016000, LENGTH = __chr_rom_size >= 128 ? 0x2000 : 0
chr_rom_24_25 : ORIGIN = 0x02018000, LENGTH = __chr_rom_size >= 128 ? 0x2000 : 0
chr_rom_26_27 : ORIGIN = 0x0201a000, LENGTH = __chr_rom_size >= 128 ? 0x2000 : 0
chr_rom_28_29 : ORIGIN = 0x0201c000, LENGTH = __chr_rom_size >= 128 ? 0x2000 : 0
chr_rom_30_31 : ORIGIN = 0x0201e000, LENGTH = __chr_rom_size >= 128 ? 0x2000 : 0
}

SECTIONS {
Expand Down Expand Up @@ -107,6 +124,23 @@ SECTIONS {
.chr_rom_29 : { KEEP(*(.chr_rom_29 .chr_rom_29.*)) } >chr_rom_29
.chr_rom_30 : { KEEP(*(.chr_rom_30 .chr_rom_30.*)) } >chr_rom_30
.chr_rom_31 : { KEEP(*(.chr_rom_31 .chr_rom_31.*)) } >chr_rom_31

.chr_rom_0_1 : { KEEP(*(.chr_rom_0_1 .chr_rom_0_1.*)) } >chr_rom_0_1
.chr_rom_2_3 : { KEEP(*(.chr_rom_2_3 .chr_rom_2_3.*)) } >chr_rom_2_3
.chr_rom_4_5 : { KEEP(*(.chr_rom_4_5 .chr_rom_4_5.*)) } >chr_rom_4_5
.chr_rom_6_7 : { KEEP(*(.chr_rom_6_7 .chr_rom_6_7.*)) } >chr_rom_6_7
.chr_rom_8_9 : { KEEP(*(.chr_rom_8_9 .chr_rom_8_9.*)) } >chr_rom_8_9
.chr_rom_10_11 : { KEEP(*(.chr_rom_10_11 .chr_rom_10_11.*)) } >chr_rom_10_11
.chr_rom_12_13 : { KEEP(*(.chr_rom_12_13 .chr_rom_12_13.*)) } >chr_rom_12_13
.chr_rom_14_15 : { KEEP(*(.chr_rom_14_15 .chr_rom_14_15.*)) } >chr_rom_14_15
.chr_rom_16_17 : { KEEP(*(.chr_rom_16_17 .chr_rom_16_17.*)) } >chr_rom_16_17
.chr_rom_18_19 : { KEEP(*(.chr_rom_18_19 .chr_rom_18_19.*)) } >chr_rom_18_19
.chr_rom_20_21 : { KEEP(*(.chr_rom_20_21 .chr_rom_20_21.*)) } >chr_rom_20_21
.chr_rom_22_23 : { KEEP(*(.chr_rom_22_23 .chr_rom_22_23.*)) } >chr_rom_22_23
.chr_rom_24_25 : { KEEP(*(.chr_rom_24_25 .chr_rom_24_25.*)) } >chr_rom_24_25
.chr_rom_26_27 : { KEEP(*(.chr_rom_26_27 .chr_rom_26_27.*)) } >chr_rom_26_27
.chr_rom_28_29 : { KEEP(*(.chr_rom_28_29 .chr_rom_28_29.*)) } >chr_rom_28_29
.chr_rom_30_31 : { KEEP(*(.chr_rom_30_31 .chr_rom_30_31.*)) } >chr_rom_30_31
}

/* Provide a mask selecting the significant bits of a CHR bank number. */
Expand Down
Loading

0 comments on commit 4d03950

Please sign in to comment.