nesdoug / 04_FullBG

NES in C
Other
4 stars 2 forks source link

Multiple CHR files #1

Closed R1gh1 closed 5 years ago

R1gh1 commented 5 years ago

Hello,

First of all a big big thanks for your awesome work on the NES development scene. The tutorials you wrote are great and really helpful !

I'm opening this issue as I would like to understand how it is possible for a game to include multiples .chr files. The main idea behind this question is the following scenario:

  1. A Title screen is using Title.chr for rendering a background.
  2. When the start button is pressed, the game start and a new Game.chr file should be loaded in order to render the game BG and Sprites.

I hope my question is clear enough. If not don't hesitate to ask for more details.

Have a nice day,

R1gh1 commented 5 years ago

Hi again,

In case someone is interested about the solution for my previous question here you have the details:

  1. Define the right ROM type into _nrom_32kvert.cfg in the SYMBOLS as follow

    NES_MAPPER:    type = weak, value = 3;    # mapper number, 0 = NROM , 3 for CNROM

    In fact :

    • NES-CNROM - Max. 32K PRG (Bus conflicts), Max. 32K CHR.
    • NES-NROM-128 - 16K PRG, 8K CHR. This mean that if we want multiples CHR files we need to use a CNROM
  2. Define the number of CHR banks you want to use into the _nrom_32kvert.cfg SYMBOLS as follow:

    NES_CHR_BANKS:    type = weak, value = 2;    # number of 8K CHR banks
  3. Add an additional CHR section into SEGMENTS definition into _nrom_32kvert.cfg

    CHARS:    load = CHR,            type = rw;
    CHARS:    load = CHR,            type = rw;
  4. Define the CHR segment into the crt0.s file and include the .chr file you need:

    
    .segment "CHAR0"
    
    .incbin "yourCHRFile0.chr"

.segment "CHAR1"

.incbin "yourCHRFile0.chr"

5. When you want to switch to the second CHR bank you can use the following function into your program:
```c
static void bankswitch(const unsigned char chrNum) {

    static const unsigned char arr[] = {
        0, 1, 2, 3, 4
    };

    (unsigned char) arr[chrNum] = chrNum;
}

void main (void) {

    ppu_off(); // screen off

    //switch to CHR bank 1
    bankswitch(1);

    ppu_on_all(); // turn on screen

    while (1){  
    //Do stuff here ...

    }
}

This normally should perform a bank switch and load the second CHR file.

As an annex, the full nrom_32k_vert.cfg:

MEMORY {
#RAM Addresses:
    # Zero page
    ZP: start = $00, size = $100, type = rw, define = yes;
    #note, the c compiler + neslib + famitone2 use about 60 zp addresses, I think

    #note OAM: start = $0200, size = $0100, define = yes;
    #note, sprites stored here in the RAM

    RAM: start = $0300, size = $0400, define = yes;
    #note VRAM_BUFFER: start = $700, size = 128, define = yes;

#INES Header:
    HEADER: start = $0, size = $10, file = %O ,fill = yes;

#ROM Addresses:
    PRG: start = $8000, size = $8000, file = %O ,fill = yes, define = yes;

#1 Bank of 8K CHR ROM
    CHR: start = $0000, size = $2000, file = %O, fill = yes;

    CHR1: start = $0000, size = $2000, file = %O, fill = yes;

}

SEGMENTS {
    HEADER:   load = HEADER,         type = ro;
    STARTUP:  load = PRG,            type = ro,  define = yes;
    LOWCODE:  load = PRG,            type = ro,                optional = yes;
    INIT:     load = PRG,            type = ro,  define = yes, optional = yes;
    CODE:     load = PRG,            type = ro,  define = yes;
    RODATA:   load = PRG,            type = ro,  define = yes;
    DATA:     load = PRG, run = RAM, type = rw,  define = yes;
    CHAR0:    load = CHR,            type = rw;
    CHAR1:    load = CHR1,           type = rw;
    BSS:      load = RAM,            type = bss, define = yes;
    HEAP:     load = RAM,            type = bss, optional = yes;
    ZEROPAGE: load = ZP,             type = zp;
    ONCE:     load = PRG,            type = ro,  define = yes, optional = yes;

    SAMPLES:  load = PRG, start = $f000, type = ro, optional = yes;
    VECTORS:  load = PRG, start = $fffa, type = ro;
}

#removed CONDES features

SYMBOLS {

    __STACKSIZE__: type = weak, value = $0100;     # 1 page stack
    __STACK_START__: type = weak, value = $0700;

    NES_MAPPER:   type = weak, value = 3;           # mapper number, 0 = NROM , 3 for CNROM
    NES_PRG_BANKS:   type = weak,  value = 2;           # number of 16K PRG banks, change to 2 for NROM256
    NES_CHR_BANKS:    type = weak, value = 2;           # number of 8K CHR banks
    NES_MIRRORING:    type = weak, value = 1;           # 0 horizontal, 1 vertical, 8 four screen

}

And the crt0.s file :

; Startup code for cc65 and Shiru's NES library
; based on code by Groepaz/Hitmen <groepaz@gmx.net>, Ullrich von Bassewitz <uz@cc65.org>

FT_BASE_ADR     = $0100     ;page in RAM, should be $xx00
FT_DPCM_OFF     = $f000     ;$c000..$ffc0, 64-byte steps
FT_SFX_STREAMS  = 1         ;number of sound effects played at once, 1..4

FT_THREAD       = 1     ;undefine if you call sound effects in the same thread as sound update
FT_PAL_SUPPORT  = 1     ;undefine to exclude PAL support
FT_NTSC_SUPPORT = 1     ;undefine to exclude NTSC support
FT_DPCM_ENABLE  = 0     ;undefine to exclude all DMC code
FT_SFX_ENABLE   = 1     ;undefine to exclude all sound effects code

;REMOVED initlib
;this called the CONDES function

    .export _exit,__STARTUP__:absolute=1
    .import push0,popa,popax,_main,zerobss,copydata

; Linker generated symbols
    .import __STACK_START__   ,__STACKSIZE__ ;changed
    .import __ROM0_START__  ,__ROM0_SIZE__
    .import __STARTUP_LOAD__,__STARTUP_RUN__,__STARTUP_SIZE__
    .import __CODE_LOAD__   ,__CODE_RUN__   ,__CODE_SIZE__
    .import __RODATA_LOAD__ ,__RODATA_RUN__ ,__RODATA_SIZE__
    .import NES_MAPPER, NES_PRG_BANKS, NES_CHR_BANKS, NES_MIRRORING

    .importzp _PAD_STATE, _PAD_STATET ;added
    .include "zeropage.inc"

PPU_CTRL    =$2000
PPU_MASK    =$2001
PPU_STATUS  =$2002
PPU_OAM_ADDR=$2003
PPU_OAM_DATA=$2004
PPU_SCROLL  =$2005
PPU_ADDR    =$2006
PPU_DATA    =$2007
PPU_OAM_DMA =$4014
PPU_FRAMECNT=$4017
DMC_FREQ    =$4010
CTRL_PORT1  =$4016
CTRL_PORT2  =$4017

OAM_BUF     =$0200
PAL_BUF     =$01c0
VRAM_BUF    =$0700

.segment "ZEROPAGE"

NTSC_MODE:          .res 1
FRAME_CNT1:         .res 1
FRAME_CNT2:         .res 1
VRAM_UPDATE:        .res 1
NAME_UPD_ADR:       .res 2
NAME_UPD_ENABLE:    .res 1
PAL_UPDATE:         .res 1
PAL_BG_PTR:         .res 2
PAL_SPR_PTR:        .res 2
SCROLL_X:           .res 1
SCROLL_Y:           .res 1
SCROLL_X1:          .res 1
SCROLL_Y1:          .res 1
PAD_STATE:          .res 2      ;one byte per controller
PAD_STATEP:         .res 2
PAD_STATET:         .res 2
PPU_CTRL_VAR:       .res 1
PPU_CTRL_VAR1:      .res 1
PPU_MASK_VAR:       .res 1
RAND_SEED:          .res 2
FT_TEMP:            .res 3

TEMP:               .res 11

PAD_BUF     =TEMP+1

PTR         =TEMP   ;word
LEN         =TEMP+2 ;word
NEXTSPR     =TEMP+4
SCRX        =TEMP+5
SCRY        =TEMP+6
SRC         =TEMP+7 ;word
DST         =TEMP+9 ;word

RLE_LOW     =TEMP
RLE_HIGH    =TEMP+1
RLE_TAG     =TEMP+2
RLE_BYTE    =TEMP+3

;nesdoug code requires
VRAM_INDEX:         .res 1
META_PTR:           .res 2
DATA_PTR:           .res 2

.segment "HEADER"

    .byte $4e,$45,$53,$1a
    .byte <NES_PRG_BANKS
    .byte <NES_CHR_BANKS
    .byte <NES_MIRRORING|(<NES_MAPPER<<4)
    .byte <NES_MAPPER&$f0
    .res 8,0

.segment "STARTUP"

start:
_exit:

    sei
    cld
    ldx #$40
    stx CTRL_PORT2
    ldx #$ff
    txs
    inx
    stx PPU_MASK
    stx DMC_FREQ
    stx PPU_CTRL        ;no NMI

initPPU:
    bit PPU_STATUS
@1:
    bit PPU_STATUS
    bpl @1
@2:
    bit PPU_STATUS
    bpl @2

clearPalette:
    lda #$3f
    sta PPU_ADDR
    stx PPU_ADDR
    lda #$0f
    ldx #$20
@1:
    sta PPU_DATA
    dex
    bne @1

clearVRAM:
    txa
    ldy #$20
    sty PPU_ADDR
    sta PPU_ADDR
    ldy #$10
@1:
    sta PPU_DATA
    inx
    bne @1
    dey
    bne @1

clearRAM:
    txa
@1:
    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 @1

    lda #4
    jsr _pal_bright
    jsr _pal_clear
    jsr _oam_clear

    jsr zerobss
    jsr copydata

    lda #<(__STACK_START__+__STACKSIZE__) ;changed
    sta sp
    lda #>(__STACK_START__+__STACKSIZE__)
    sta sp+1            ; Set argument stack ptr

;   jsr initlib
; removed. this called the CONDES function

    lda #%10000000
    sta <PPU_CTRL_VAR
    sta PPU_CTRL        ;enable NMI
    lda #%00000110
    sta <PPU_MASK_VAR

waitSync3:
    lda <FRAME_CNT1
@1:
    cmp <FRAME_CNT1
    beq @1

detectNTSC:
    ldx #52             ;blargg's code
    ldy #24
@1:
    dex
    bne @1
    dey
    bne @1

    lda PPU_STATUS
    and #$80
    sta <NTSC_MODE

    jsr _ppu_off

    lda #0
    ldx #0
    jsr _set_vram_update

    ldx #<music_data
    ldy #>music_data
    lda <NTSC_MODE
    jsr FamiToneInit

    .if(FT_SFX_ENABLE)
    ldx #<sounds_data
    ldy #>sounds_data
    jsr FamiToneSfxInit
    .endif

    lda #$fd
    sta <RAND_SEED
    sta <RAND_SEED+1

    lda #0
    sta PPU_SCROLL
    sta PPU_SCROLL

    jmp _main           ;no parameters

    .include "LIB/neslib.s"
    .include "LIB/nesdoug.s"
    .include "MUSIC/famitone2.s"

.segment "RODATA"

music_data:
;   .include "music.s"

    .if(FT_SFX_ENABLE)
sounds_data:
;   .include "sounds.s"
    .endif

.segment "SAMPLES"
;   .incbin "music_dpcm.bin"

.segment "VECTORS"

    .word nmi   ;$fffa vblank nmi
    .word start ;$fffc reset
    .word irq   ;$fffe irq / brk

.segment "CHAR0"

    .incbin "yourCHRFile0.chr"

.segment "CHAR1"

    .incbin "yourCHRFile0.chr"