Offline
rochester, ny

hey neil, i want to make a nintendo program that has a sav file, are there any good tutorials or resources online that you can point me to that will help me figure this out?

Offline
WOW MAN!
nickmaynard wrote:

hey neil, i want to make a nintendo program that has a sav file, are there any good tutorials or resources online that you can point me to that will help me figure this out?

It's fairly straightforward - you need to change your ROM configuration to use MMC1 mapper (I think that's the minimum you need mapper-wise) and you'll need to change the ROM header. After that, NVRAM (battery-backed RAM) is mapped at $6000-$7FFF and you can just read and write to it like normal RAM.

I'll send you some code.

Offline
rochester, ny
neilbaldwin wrote:
nickmaynard wrote:

hey neil, i want to make a nintendo program that has a sav file, are there any good tutorials or resources online that you can point me to that will help me figure this out?

It's fairly straightforward - you need to change your ROM configuration to use MMC1 mapper (I think that's the minimum you need mapper-wise) and you'll need to change the ROM header. After that, NVRAM (battery-backed RAM) is mapped at $6000-$7FFF and you can just read and write to it like normal RAM.

I'll send you some code.

wow, that would be really great neil. thanks!

Offline
WOW MAN!

Actually, turns out you don't even need MMC1 (in Nestopia at least). Here's some bare-bones code that will write 00-FF in the first 256 bytes of a .sav file. The key part is setting bit 1 of the 6th byte of the iNES header (http://wiki.nesdev.com/w/index.php/INES)

                ;NES Header
                DB "NES",$1A
                DB 2            ;number of PRG ROMs
                DB 0            ;number of CHR ROMs
                DB %00000010    ; BIT 1 = enable(1)/disable(0) battery RAM
                DB %00000000
                DB 0,0,0,0,0,0,0,0

WRAM            EQU $6000
                
                .ORG $8000
RESET           sei
                ldx #$FF
                txs

                ldx #$00
@a              txa
                sta WRAM,x
                inx
                bne @a

LOOP            jmp LOOP

NMI             RTI

IRQ             RTI

                ORG $FFFA
                DW NMI,RESET,IRQ
                

Last edited by neilbaldwin (Jan 8, 2011 11:26 pm)

Offline
rochester, ny

awesome, thanks for the code. some of this is still over my head but this is a great place to start learning.

Offline
WOW MAN!

I realised afterwards that some of that might be confusing (or that your code for the Chords program is set up a little differently) - I wanted to give you something that would compile and work in ASM6 (I guessed that was what you used?)

Really, the only parts you need that can be transplanted in your own code is setting bit 1 of the 6th header byte and then knowing you just read/write addresses $6000-$7FFF to access the battery RAM. The rest of my example is just reset code and the necessary layout to make a working ROM.

Offline
rochester, ny
neilbaldwin wrote:

I realised afterwards that some of that might be confusing (or that your code for the Chords program is set up a little differently) - I wanted to give you something that would compile and work in ASM6 (I guessed that was what you used?)

Really, the only parts you need that can be transplanted in your own code is setting bit 1 of the 6th header byte and then knowing you just read/write addresses $6000-$7FFF to access the battery RAM. The rest of my example is just reset code and the necessary layout to make a working ROM.

yeah, i actually think i get it. i tried out your code and saw that it wrote that info to the sav file. i'm going to set up a little test program where you can set a variable, then save it, then see if the program can call that information back up when i reopen the program.

thanks a lot for the help!

Offline
WOW MAN!

LOL just spotted a schoolboy error - I'd wrote 'sta WRAM,y' instead of 'sta WRAM,x'

Fixed now smile

Offline
rochester, ny

so, i've been messing with this a lot and i've got a quick question. can you tell me why THIS code seems to work...

    .ORG $7ff0
Header:
    .db "NES", $1a
    .db $02
    .db $01
    .db %00000010
    .db %00000000
    .db $00
    .db $00
    .db $00
    .db $00
    .db $00
    .db $00
    .db $00
    .db $00

WRAM     EQU $6000

     .org $8000

Reset:
    SEI
    ldx #$FF
    txs

    lda #$25
    sta WRAM

LOOP:    jmp LOOP
NMI:    RTI
IRQ:    RTI

    .ORG $fffa
    .dw NMI
    .dw Reset
    .dw IRQ

...while this code does NOT work?

    .ORG $7ff0
Header:
    .db "NES", $1a
    .db $02
    .db $01
    .db %00000010
    .db %00000000
        .db $00
        .db $00
        .db $00
        .db $00
        .db $00
        .db $00
        .db $00
        .db $00

WRAM     EQU $6000

        .org $8000

Reset:
        SEI
    ldx #$FF
    txs

LOOP:    jmp LOOP
NMI:    


    lda #$25
    sta WRAM

    RTI
IRQ:    RTI

    .ORG $fffa
    .dw NMI
    .dw Reset
    .dw IRQ

the code looks kind of wonky when i paste it in here, but it's not all weirdly tabbed in my .asm file. #$25 is just a totally random value, by the way.

Last edited by nickmaynard (Jan 10, 2011 5:46 am)

Offline
WOW MAN!

It's because the NMI never gets triggered. You need to enable NMIs through the PPU Control Register 1 ($2000). To enable the NMI you just set bit 7. Put this somewhere before the LOOP;

lda #$80
sta $2000

The PPU control registers do a lot more so you should be familiar with the various functions they provide;

http://nesdev.parodius.com/NinTech.txt

(near the bottom of the page is a table listing what each bit of PPU 1/2 Control and PPU Status does)

It's also common practice to 'acknowledge' the NMI in your NMI code by 'reading' the PPU Status Register ($2002)

bit $2002

at the start of the NMI.

Something else you should do in the NMI is save the contexts of the A, X and Y registers - this is because the NMI can occur at any point in your background loop and once the NMI has finished, the register contents could have been changed. You use the stack instructions PHA and PLA.

NMI:
    pha                ;push A onto stack
    txa                ;transfer X to A
    pha                ;push A onto stack
    tya                ;transfer Y to A
    pha                ;push A onto stack

    bit $2000                ;acknowledge interrupt
   
    ;do other stuff

    pla                ;pull top of stack to A
    tay                ;transfer A to Y
    pla                ;pull next by off stack to A
    tax                ;transfer A to X
    pla                ;pull A off stack
    rti

The stack is a LIFO buffer (last in, first out) so you restore the registers in the reverse order to what you saved them. Because you can only push (PHA) and pull (PLA) a value to/from the A register, in order to save the content of X and Y you transfer them to the accumulaor (A) first when saving, and then reverse the procedure for restoring.

Edited to add:

In fact, most people clear the two PPU Control registers at the start of the reset code too:

PPU0 EQU $2000
PPU1 EQU $2001

RESET:
    sei
    lda #$00
    sta PPU0
    sta PPU1
    ldx #$FF
    txs
    
    etc.

Last edited by neilbaldwin (Jan 10, 2011 9:11 am)

Offline
New York City

This thread has turned sexy.

Offline
WOW MAN!

Sorry, that happens a lot to stuff I get involved with. It's a constant burden.

Offline
WOW MAN!

I was thinking about this today. This kind of topic is not really music-releated and so if a moderator wants to move it I don't mind. Maybe it's worth having a coding/programming section? I know it's slightly off-topic for the whole board but it has a tangential connection. Nick's chord program and also probably things like Litewall could be moved there.

Offline
Unsubscribe

I think its more than on topic, and interesting to read, even if  barely understand anything.

Offline
WOW MAN!
herr_prof wrote:

I think its more than on topic, and interesting to read, even if  barely understand anything.

Ah cool. I'm more than happy to answer general programming questions like this in the forum then if everyone else is.

smile

Offline
rochester, ny

awesome! it works perfectly now neil. thanks for the lesson.

there's so much about this i don't know yet and most of the documentation i've been able to find assumes a certain level of coding experience that i just don't have. so it's really helpful for you to go through some of this with me.