Setting inesprg to 2 makes game stop running

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

Jay John
Posts: 16
Joined: Fri Mar 22, 2019 9:03 pm

Setting inesprg to 2 makes game stop running

Post by Jay John »

The mapper is set to MMC1.
I ran out of space in the two default banks, but when I set inesprg to anything other than 1 it makes the game freeze before starting.
Now, I've heard that the bank MMC1 starts is random, but I've tried putting the init code in every single bank and it didn't help, besides the init code is in the fixed bank, which leads me to believe I must have forgotten to put something somewhere, but where?

Is there something I'm missing?
Does anyone know where I can find a simple sample code for the MMC1?
lidnariq
Posts: 10677
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Setting inesprg to 2 makes game stop running

Post by lidnariq »

Tepples has some sample code here:
https://github.com/pinobatch/snrom-template

I'd suggest trying with some debugging emulator, such as Mesen or the windows build of FCEUX.
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Setting inesprg to 2 makes game stop running

Post by koitsu »

To expand a bit on lidnariq's statement: This part of the MMC1 page (see paragraph after register description) explains the behaviour. If you read the content and the referenced threads, you'll see that there is some variance in which PRG bank different MMC1 chips tend to start in.

Thus, from a programmer's perspective (today), the universal solution is to put your startup code (what the RESET vector points to) at the top of every bank -- and that includes the vectors themselves. That startup code should reset the MMC1 to put it into a known state where you don't have to worry.

Tepples' snrom-template code does this through use of the STUBxx segments you see in the ld65 cfg file, combined with the startup code that consists of 16 bytes (which includes the vectors) which references said STUBxx entries. The code in resetstub_entry is what the RESET vector points to; the code is commented fairly clearly so it should make sense.

You can customise that template to work with something smaller than 2mbit; he went with the largest PRG scenario but it works universally.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Setting inesprg to 2 makes game stop running

Post by tokumaru »

Jay John wrote:I ran out of space in the two default banks, but when I set inesprg to anything other than 1 it makes the game freeze before starting.
You forgot to mention which assembler you're using, but from the above I guess it's NESASM?
I must have forgotten to put something somewhere, but where?
Can't say without seeing any code.
Jay John
Posts: 16
Joined: Fri Mar 22, 2019 9:03 pm

Re: Setting inesprg to 2 makes game stop running

Post by Jay John »

tokumaru wrote:You forgot to mention which assembler you're using, but from the above I guess it's NESASM?.
Yes, that's correct.

So, I added this in every bank and it now seems to be working.

Code: Select all

resetstub_entry:
  sei
  ldx #$FF
  txs
  stx $FFF2
  jmp reset_handler
  .addr nmi_handler, resetstub_entry, irq_handler
But I can't switch the prg banks (the chr switches work fine).
This is the code I'm using to do so:

Code: Select all

	lda bankswitch
	sta $E000
	lsr a
	sta $E000
	lsr a
	sta $E000
	lsr a
	sta $E000
	lsr a	
	sta $E000
	rts	

First bank is fixed at bank 0 and I want to switch the last bank from bank 1 to 3, but this code is doing nothing.
Do I have to put a specific number on the "bankswitch" variable? It's currently loaded with 2, but I also tried 1 and 3.
lidnariq
Posts: 10677
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

Re: Setting inesprg to 2 makes game stop running

Post by lidnariq »

Have you already written to the register at $8000? If not, you should do that first. If yes, what did you write there?
Jay John
Posts: 16
Joined: Fri Mar 22, 2019 9:03 pm

Re: Setting inesprg to 2 makes game stop running

Post by Jay John »

lidnariq wrote:Have you already written to the register at $8000? If not, you should do that first. If yes, what did you write there?
Last time I've written there is when I changed mirroring

Code: Select all

ldvermir:
	lda #$80
	sta $8000
	lda #%00011010
	sta $8000	
	lsr a 
	sta $8000	
	lsr a
	sta $8000	
	lsr a
	sta $8000	
	lsr a
	sta $8000
	rts
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Setting inesprg to 2 makes game stop running

Post by tokumaru »

Let's get something out of the way: NESASM requires everything to be formatted with these stupid 8KB banks, but the NES itself has no concept of banks whatsoever, and MMC1 banks are 16KB, so the numbering you use for NESASM is not the same you'll use with the MMC1. NESASM banks 0 and 1 will be MMC1 bank 0, NESASM banks 2 and 3 will be MMC1 bank 1, and so on. The iNES header assumes 16KB banks regardless of the mapper.
Jay John wrote:

Code: Select all

resetstub_entry:
  sei
  ldx #$FF
  txs
  stx $FFF2
  jmp reset_handler
  .addr nmi_handler, resetstub_entry, irq_handler
This is to reset the MMC1, right? Why use address $FFF2, though? I mean, I guess it works, but why not use $8000 and keep things less cryptic?

Code: Select all

	lda bankswitch
	sta $E000
	lsr a
	sta $E000
	lsr a
	sta $E000
	lsr a
	sta $E000
	lsr a	
	sta $E000
	rts	
This is in the fixed bank, right?
First bank is fixed at bank 0
While the MMC1 has a PRG switching mode that fixes the first bank to $8000-$BFFF, but most people would prefer to hardwire the last bank to $C000-$FFFF, as that's way more common, and it's the default configuration.
Do I have to put a specific number on the "bankswitch" variable? It's currently loaded with 2, but I also tried 1 and 3.
Just keep in mind what I said above: NESASM banks are 8KB, MMC1 banks are 16KB.
Jay John
Posts: 16
Joined: Fri Mar 22, 2019 9:03 pm

Re: Setting inesprg to 2 makes game stop running

Post by Jay John »

tokumaru wrote:Let's get something out of the way: NESASM requires everything to be formatted with these stupid 8KB banks, but the NES itself has no concept of banks whatsoever, and MMC1 banks are 16KB, so the numbering you use for NESASM is not the same you'll use with the MMC1. NESASM banks 0 and 1 will be MMC1 bank 0, NESASM banks 2 and 3 will be MMC1 bank 1, and so on. The iNES header assumes 16KB banks regardless of the mapper.
Oh, I've been meaning to change the assembler, but never got around to it.

So, let me see if I got this.
If I were to order them by memory, then on NESASM it'd be:

Code: Select all

    .bank 0 
    .org $8000
;code

    .bank 1 
    .org $A000
;code

    .bank 2 
    .org $C000
;code

    .bank 3 
    .org $E000
;code
While on other assemblers it'd be:

Code: Select all

    .bank 0 
    .org $8000
;code

    .bank 1
    .org $C000
;code
"This is to reset the MMC1, right? Why use address $FFF2, though? I mean, I guess it works, but why not use $8000 and keep things less cryptic?
Because it's how it was written in that github file, but thanks for the heads up, it makes it a lot easier to understand it.
This is in the fixed bank, right?
Yes
While the MMC1 has a PRG switching mode that fixes the first bank to $8000-$BFFF, but most people would prefer to hardwire the last bank to $C000-$FFFF, as that's way more common, and it's the default configuration.
It's just to get the hang of it, at the moment most of the code is organized without bank shifting in mind, I'll definitely be shuffling things around to better accommodate it later.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Setting inesprg to 2 makes game stop running

Post by tokumaru »

Jay John wrote:While on other assemblers it'd be:

Code: Select all

    .bank 0 
    .org $8000
;code

    .bank 1
    .org $C000
;code
Kinda, but not exactly... directives and general code organization change from assembler to assembler (e.g. most assemblers don't have a ".bank" directive). My main point is that, unlike NESASM, assemblers don't usually force the programmer to use a specific bank size, which is a good thing because bank sizes vary from mapper to mapper.
Nicole
Posts: 218
Joined: Sun Mar 27, 2016 7:56 pm

Re: Setting inesprg to 2 makes game stop running

Post by Nicole »

For instance, asm6 (the assembler I prefer) has no built-in concept of banks at all, instead giving you very direct control over the layout of the file it outputs, so you might have something like this:

Code: Select all

; iNES header
    .db "NES",$1a
    ; etc.

; bank 0
    .base $8000
    ; code...
    .org $c000      ; pad to $c000

; bank 1
    .base $c000
    ; code...
    .org $fffa      ; pad to $fffa
    .dw nmi, reset, irq

; CHR banks
    .incbin "tiles0.chr"
    .incbin "tiles1.chr"
    ; etc.
Jay John
Posts: 16
Joined: Fri Mar 22, 2019 9:03 pm

Re: Setting inesprg to 2 makes game stop running

Post by Jay John »

tokumaru wrote:Kinda, but not exactly... directives and general code organization change from assembler to assembler (e.g. most assemblers don't have a ".bank" directive). My main point is that, unlike NESASM, assemblers don't usually force the programmer to use a specific bank size, which is a good thing because bank sizes vary from mapper to mapper.
Alright, so I've set up the banks that way. But do I have to change anything else? It doesn't want to load the $C000 and $E000 banks.
Jay John
Posts: 16
Joined: Fri Mar 22, 2019 9:03 pm

Re: Setting inesprg to 2 makes game stop running

Post by Jay John »

Anyone?
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Setting inesprg to 2 makes game stop running

Post by tokumaru »

Can you show the complete structure of your source code? You can omit unrelated logic/data, but I'd like to see everything from the iNES directives, going through all the .banks and .orgs, mapper writes, reset stubs and interrupt vectors. It's hard to tell what's happening when looking at just bits and pieces that don't look wrong by themselves, but may not be in the correct places or something.
Jay John
Posts: 16
Joined: Fri Mar 22, 2019 9:03 pm

Re: Setting inesprg to 2 makes game stop running

Post by Jay John »

tokumaru wrote:Can you show the complete structure of your source code? You can omit unrelated logic/data, but I'd like to see everything from the iNES directives, going through all the .banks and .orgs, mapper writes, reset stubs and interrupt vectors. It's hard to tell what's happening when looking at just bits and pieces that don't look wrong by themselves, but may not be in the correct places or something.

Code: Select all

	.inesprg 2
	.ineschr 3
	.inesmap 1
	.inesmir %10

	.bank 0
	.org $0000
;variables

	.org $8000
	
start:
	sei
	cld
	ldx #$40
	stx $4017

	jsr ldhormir
	lda bankcheckb
	cmp #$FF
	beq .start2	
	jsr bankswitch0000bck		
	jsr bankswitchC000for		
.start2:
	jsr off
	jsr vblank
	jsr vblank
	jsr ldscrolldefault
;code
;jmp to bank1

off:    
	lda #%10001000
	sta $2000
	lda #%00000000
	sta $2001
	rts
on:
	lda #%10001000
	sta $2000
	lda #%00011110
	sta $2001
	rts

ldscrolldefault
	lda #$00
	sta scrollh
	lda #$00
	sta scrollv
	rts

vblank:
	bit $2002
	bpl vblank
	rts	
	
bankswitch0000bck:
	lda bankswitch
	sta $A000
	asl a
	sta $A000
	asl a
	sta $A000
	asl a
	sta $A000
	asl a	
	sta $A000	
	lda bankcheckb
	sec
	sbc #$01
	sta bankcheckb
	rts

bankswitch0000for:
	lda bankswitch
	sta $A000
	lsr a
	sta $A000
	lsr a
	sta $A000
	lsr a
	sta $A000
	lsr a	
	sta $A000	
	lsr a	
	sta $A000
	lda bankcheckb
	clc
	adc #$01		
	sta bankcheckb
	rts	
	
bankswitchC000for:	
	lda bankswitch
	sta $C000
	lsr a
	sta $C000
	lsr a
	sta $C000
	lsr a
	sta $C000
	lsr a	
	sta $C000
	lda bankchecks
	clc
	adc #$01
	sta bankchecks
	rts	
	
ldhormir:
	lda #$80
	sta $8000
	lda #%00011011
	sta $8000	
	lsr a 
	sta $8000	
	lsr a
	sta $8000	
	lsr a
	sta $8000	
	lsr a
	sta $8000
	rts
	
ldvermir:
	lda #$80
	sta $8000
	lda #%00011010
	sta $8000	
	lsr a 
	sta $8000	
	lsr a
	sta $8000	
	lsr a
	sta $8000	
	lsr a
	sta $8000
	rts

	.org $9FF0
bfind0	
	sei
	ldx #$FF
	txs
	stx $8000  
	jmp start
	.dw NMI
	.dw bfind0
	.dw bfind0	

NMI:
	lda $2002
	lda #$7E
	sta $2002
	lda #$00
	sta $2003
	lda #$00
	sta $2006
	sta $2006	
	sta $2005
	sta $2005
;code
	rts
	
	.bank 1
	.org $A000

	jsr ldvermir
	jsr bankswitch0000for
	jsr bankswitch0000for
;code
	jsr bankswitch0000bck
;jmp to bank2


	.org $BFF0
bfind	
	sei
	ldx #$FF
	txs
	stx $8000
	jmp start
	.dw NMI
	.dw bfind
	.dw bfind	
	
	.bank 2
	.org $C000
;the code that's not running

	.org $DFF0
bfind2	
	sei
	ldx #$FF
	txs
	stx $8000 
	jmp start
	.dw NMI
	.dw bfind2
	.dw bfind2		
	
	.bank 3
	.org $E000
;empty		
	
	.org $FFF0
bfind3
	sei
	ldx #$FF
	txs
	stx $8000
	jmp start
	.dw NMI
	.dw bfind3
	.dw bfind3		
	
	.bank 4
	.org $0000
;tile data

	.bank 5
	.org $0000
;sprite data

	.bank 6
	.org $0000
;tile data
There may be stuff in the code I missed, but most of it is here.
Post Reply