;;;
;; This is an example on how you can persist data through different console
;; resets. That is, if you watch the value on memory at $6001, on each reset you
;; will see that it gets incremented. Depending on the emulator you can further
;; inspect this. For example, FCEUX stores saved data to the $HOME/.fceux/sav
;; directory (at least on my Linux machine). In there just execute `hexdump -C
;; persist.sav` and you should get what is actually persisted on the address
;; range $6000-$7FFF. In our case, you only need to care about the first two
;; bytes from this dump, which represent $6000 and $6001 respectively.
;;
;; Last but not least, all of this is done thanks to the MMC1 chip. Thus, this
;; file is also a minimalistic example on how to configure it, even if it
;; doesn't take full advantage of it (e.g. we are not doing any bank switching).
;; A base configuration for this chip can be found in `config/mmc1.cfg`. For
;; better grasping bank switching though refer to other examples like
;; `basics/unrom.s`.

.segment "HEADER"
    .byte 'N', 'E', 'S', $1A
    .byte $02
    .byte $01

    ;; Some notes:
    ;;   - bit 0: it controls the mirroring. Here it's unset, so horizontal
    ;;            mirroring is applied, but this does not matter for MMC1
    ;;            because mirroring for this chip is done programmatically, not
    ;;            on hardware. Notice this when we configure the chip below.
    ;;   - bit 1: contains battery-backed PRG RAM ($6000-7FFF)
    ;;   - bit 7-4: the lower nibble of the mapper (#%0001 for MMC1)
    .byte $12
    .byte $00

.segment "VECTORS"
    .addr nmi
    .addr reset
    .addr irq

;; Unused. This is the swappable PRG-ROM bank as per `config/mmc1.cfg`.
.segment "BANK0"
.byte $00

;; This is the fixed PRG-ROM bank as per `config/mmc1.cfg`.
.segment "BANK1"

;; Unused
.proc nmi
    rti
.endproc

;; Unused
.proc irq
    rti
.endproc

;; Check `basics/sprite.s` for a deeper look on the logic below. I have only
;; added comments to MMC1-specific stuff.
.proc reset
    sei
    cld
    ldx #$40
    stx $4017

    ldx #$FF
    txs

    inx
    stx $2000
    stx $2001
    stx $4010

    ;; In order to reset the MMC1 chip you need to set the bit 7 of the data and
    ;; point to any address range of $8000-$FFFF. If you want to be sure about
    ;; it, you can simply do: `lda #%10000000` and then `sta $8000`. This will
    ;; ensure that the chip is reset. That being said, a micro-optimization can
    ;; be performed by taking advantage that the "BANK1" segment resides above
    ;; address $8000. Then, you can use the `inc` instruction which first writes
    ;; the old value before the incremented one. All having thus the same effect
    ;; but saving 2 bytes. I know it's somewhat obfuscated and it's done for
    ;; just two bytes, but it's so simple it's even worth it.
reset_mmc1:
    inc reset_mmc1

    bit $2002
@vblankwait1:
    bit $2002
    bpl @vblankwait1

    ldx #0
    lda #0
@ram_reset_loop:
    sta $000, x
    sta $100, x
    sta $200, x
    sta $300, x
    sta $400, x
    sta $500, x
    sta $600, x
    sta $700, x
    inx
    bne @ram_reset_loop

@vblankwait2:
    bit $2002
    bpl @vblankwait2

    ;; In order to configure the MMC1 chip you have to perform 5 writes into any
    ;; address within the range $8000-$9FFF. This is because the MMC1 was built
    ;; through a serial port. Thus, similarly to when we read controllers (see
    ;; `basics/input.s`), only the lowest bit is read at a time. The control
    ;; register is 5 bits long, and that's why we need to write to it 5 times
    ;; (bit0 -> .. -> bit4).
    ;;
    ;; Let's see what this value being written means:
    ;;   - bits 0-1: the mirroring. We set it to `11` for horizontal mirroring.
    ;;               That's why it did not really matter what we had at the iNES
    ;;               header: mirroring is programmatically set, not
    ;;               hardware-bound.
    ;;   - bit 2: we have two regions (see bit 3): $8000-$BFFF and $C000-$FFFF.
    ;;            This bit configures which one of them is fixed and which can
    ;;            be swapped. We set this bit to one, meaning that $8000-$BFFF
    ;;            is swappable while the other is fixed to the last bank of PRG.
    ;;   - bit 3: swappable PRG size. If set to 0, then 32KB of memory is
    ;;            assumed, and thus bit 2 is ignored (i.e. the whole thing is to
    ;;            be swapped at once). Otherwise, if set to 1, then 16KB is
    ;;            assumed, meaning that we can establish two regions from
    ;;            $8000-$FFFF, and through bit 2 we have established which of
    ;;            them is swappable. In our case, we have established
    ;;            $8000-$BFFF as swappable, which is 16KB of size and thus we
    ;;            need to set this bit.
    lda #%00001111
    sta $8000
    lsr
    sta $8000
    lsr
    sta $8000
    lsr
    sta $8000
    lsr
    sta $8000

    ;; And now we configure the bank selector, which is simply bank 0. Bit 4
    ;; also enables PRG RAM when set to 0, which we must set this way if we want
    ;; things to actually persist. Later on you would need to write to
    ;; $E000-$FFFF if you wanted to perform a bank switch, but on this example
    ;; we don't need any of that.
    lda #0
    sta $E000
    sta $E000
    sta $E000
    sta $E000
    sta $E000

    ;;;
    ;; NOTE: configuration/reset is done, the code below is our actual program :D

    ;; We store at $6000 a boolean value which means: has this ever been
    ;; initialized? If this does not equal to 1, then we are sure it has never
    ;; been initialized and we do it now. Otherwise, we just increment the value
    ;; we store at $6001.
    lda $6000
    cmp #1
    bne @init_store
    inc $6001
    jmp @loop
@init_store:
    lda #1
    sta $6000
    lda #0
    sta $6001

    ;; Loop forever, there's nothing to be done here.
@loop:
    jmp @loop
.endproc

;; Unused
.segment "CHR0"
.byte $00

;; Unused
.segment "CHR1"
.byte $00
