Progress Thread - Project Blue

Moderator: Moderators

User avatar
toggle switch
Posts: 131
Joined: Fri Sep 30, 2016 8:57 pm

Re: Progress Thread - Project Blue

Post by toggle switch » Wed Feb 07, 2018 8:33 am

honestly i'll probably need some help moving to another mapper.

is there a document somewhere that covers linker files and how they work? or a sample GTROM project in NESICIDE? i can't seem to find any relevant pages on how this stuff works and how to set it up.

tepples
Posts: 21752
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Progress Thread - Project Blue

Post by tepples » Wed Feb 07, 2018 8:37 am

The official documentation of linker configuration files is ld65 Users Guide. You'll need a MEMORY area for each 32K bank and probably for the pseudo-fixed bank. If you want, I could make a BNROM version of my SNROM/UNROM template. This should help because GTROM is the same as BNROM except for the port moved down to $5000 and bank switching of CHR RAM and nametable memory.

User avatar
pubby
Posts: 548
Joined: Thu Mar 31, 2016 11:15 am

Re: Progress Thread - Project Blue

Post by pubby » Wed Feb 07, 2018 8:41 am

I setup a gtrom configuration for ca65 last week. You also need to do some trickery to setup the fixed segments. I can post that too if you want.

Code: Select all

MEMORY {
    ZP:     start = $00, size = $100, type = rw;
    HEADER: start = 0, size = $0010, type = ro, file = %O, fill=yes, fillval=$00;
    RAM:    start = $0300, size = $0500, type = rw;

    PRG0:     start = $8000, size = $8000, file = %O, fill = yes, fillval = $0, bank=0;
    PRG1:     start = $8000, size = $8000, file = %O, fill = yes, fillval = $1, bank=1;
    PRG2:     start = $8000, size = $8000, file = %O, fill = yes, fillval = $2, bank=2;
    PRG3:     start = $8000, size = $8000, file = %O, fill = yes, fillval = $3, bank=3;
    PRG4:     start = $8000, size = $8000, file = %O, fill = yes, fillval = $4, bank=4;
    PRG5:     start = $8000, size = $8000, file = %O, fill = yes, fillval = $5, bank=5;
    PRG6:     start = $8000, size = $8000, file = %O, fill = yes, fillval = $6, bank=6;
    PRG7:     start = $8000, size = $8000, file = %O, fill = yes, fillval = $7, bank=7;
    PRG8:     start = $8000, size = $8000, file = %O, fill = yes, fillval = $8, bank=8;
    PRG9:     start = $8000, size = $8000, file = %O, fill = yes, fillval = $9, bank=9;
    PRG10:    start = $8000, size = $8000, file = %O, fill = yes, fillval = $A, bank=10;
    PRG11:    start = $8000, size = $8000, file = %O, fill = yes, fillval = $B, bank=11;
    PRG12:    start = $8000, size = $8000, file = %O, fill = yes, fillval = $C, bank=12;
    PRG13:    start = $8000, size = $8000, file = %O, fill = yes, fillval = $D, bank=13;
    PRG14:    start = $8000, size = $8000, file = %O, fill = yes, fillval = $E, bank=14;
    PRG15:    start = $8000, size = $8000, file = %O, fill = yes, fillval = $F, bank=15;

}

SEGMENTS {
    INESHDR:  load = HEADER, type = ro, align = $10;
    ZEROPAGE: load = ZP, type = zp;
    BSS:      load = RAM, type = bss, define = yes, align = $100;

    FIXED0:   load = PRG0, type = ro, align = $100;
    B0:       load = PRG0, type = ro, align = $100;
    VECTORS0: load = PRG0, type = ro, start = $FFFA;

    FIXED1:   load = PRG1, type = ro, align = $100;
    B1:       load = PRG1, type = ro, align = $100;
    VECTORS1: load = PRG1, type = ro, start = $FFFA;

    FIXED2:   load = PRG2, type = ro, align = $100;
    B2:       load = PRG2, type = ro, align = $100;
    VECTORS2: load = PRG2, type = ro, start = $FFFA;

    FIXED3:   load = PRG3, type = ro, align = $100;
    B3:       load = PRG3, type = ro, align = $100;
    VECTORS3: load = PRG3, type = ro, start = $FFFA;

    FIXED4:   load = PRG4, type = ro, align = $100;
    B4:       load = PRG4, type = ro, align = $100;
    VECTORS4: load = PRG4, type = ro, start = $FFFA;

    FIXED5:   load = PRG5, type = ro, align = $100;
    B5:       load = PRG5, type = ro, align = $100;
    VECTORS5: load = PRG5, type = ro, start = $FFFA;

    FIXED6:   load = PRG6, type = ro, align = $100;
    B6:       load = PRG6, type = ro, align = $100;
    VECTORS6: load = PRG6, type = ro, start = $FFFA;

    FIXED7:   load = PRG7, type = ro, align = $100;
    B7:       load = PRG7, type = ro, align = $100;
    VECTORS7: load = PRG7, type = ro, start = $FFFA;

    FIXED8:   load = PRG8, type = ro, align = $100;
    B8:       load = PRG8, type = ro, align = $100;
    VECTORS8: load = PRG8, type = ro, start = $FFFA;

    FIXED9:   load = PRG9, type = ro, align = $100;
    B9:       load = PRG9, type = ro, align = $100;
    VECTORS9: load = PRG9, type = ro, start = $FFFA;

    FIXED10:   load = PRG10, type = ro, align = $100;
    B10:       load = PRG10, type = ro, align = $100;
    VECTORS10: load = PRG10, type = ro, start = $FFFA;

    FIXED11:   load = PRG11, type = ro, align = $100;
    B11:       load = PRG11, type = ro, align = $100;
    VECTORS11: load = PRG11, type = ro, start = $FFFA;

    FIXED12:   load = PRG12, type = ro, align = $100;
    B12:       load = PRG12, type = ro, align = $100;
    VECTORS12: load = PRG12, type = ro, start = $FFFA;

    FIXED13:   load = PRG13, type = ro, align = $100;
    B13:       load = PRG13, type = ro, align = $100;
    VECTORS13: load = PRG13, type = ro, start = $FFFA;

    FIXED14:   load = PRG14, type = ro, align = $100;
    B14:       load = PRG14, type = ro, align = $100;
    VECTORS14: load = PRG14, type = ro, start = $FFFA;

    FIXED15:   load = PRG15, type = ro, align = $100;
    B15:       load = PRG15, type = ro, align = $100;
    VECTORS15: load = PRG15, type = ro, start = $FFFA;
}

FILES {
    %O: format = bin;
}


User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2032
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

Re: Progress Thread - Project Blue

Post by FrankenGraphics » Wed Feb 07, 2018 8:53 am

Ideally you'd want to be able to

1)define your "pseudo fixed bank" as a segment.
2)in source, place whatever needs to be fixed (ie the bare game engine + probably also music driver) in that segment.
3)instruct the linker to paste that segment at the same addr in each bank it needs to be in.

That way the "fixed bank" is always the size of the engine and not a byte more, which leaves optimal and maximized space for level/song data - and any plausible extras; like if there were level-specific functions or tables for example.
http://www.frankengraphics.com - personal NES blog

User avatar
pubby
Posts: 548
Joined: Thu Mar 31, 2016 11:15 am

Re: Progress Thread - Project Blue

Post by pubby » Wed Feb 07, 2018 9:20 am

Having the engine in the fixed bank makes the programming easier, but it wastes a lot of space. Each fixed byte takes up 16 bytes in the ROM, which is a lot!

Technically, all you need in the fixed bank is the vectors, their handlers, and a trampoline for bank switches. Also DPCM data, if you're using that. The engine code can be placed in a separate 32k bank, and the music engine + songs can exist in their own 32 bank. I think this is how Lizard does things.

But this is venturing off into a tangent. My apologies.

User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2032
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

Re: Progress Thread - Project Blue

Post by FrankenGraphics » Wed Feb 07, 2018 9:32 am

Might be a tangent for the compo, but it's a very useful discussion for the project, so please don't feel you need to hold back! It's all very informative, so thanks!

Hadn't even considered that. I don't have the source and am not sure i could make sense of it either, but i think we're pretty tight on cycles, NMI-wise especially.
Would trampolines like that be a significant increase in cycles?
Each fixed byte takes up 16 bytes in the ROM, which is a lot!
Only if you need to paste the engine across all banks. As i see it, you'd only need to do so across in-game engine/level banks. A bank strictly for storing graphics to be loaded in bulk between levels wouldn't need it, and titles, cutscenes and such wouldn't need it either. But yeah, i think we'd be looking at 12-14 bytes per "fixed" byte with this method. Fortunately, there's 512k of them if we go this path. On the other hand, *some* trampoline would need to be written at least for loading stuff at certain points.
http://www.frankengraphics.com - personal NES blog

User avatar
toggle switch
Posts: 131
Joined: Fri Sep 30, 2016 8:57 pm

Re: Progress Thread - Project Blue

Post by toggle switch » Wed Feb 07, 2018 9:47 am

honestly, the efficiency of data storage is on the bottom rung of things i care about here. most important is to free up some space, any space. even the inefficient way should yield about 8k, which is far more than i need.
Having the engine in the fixed bank makes the programming easier,
priority #1. i'm a beginner here, this is my first real project. i'm not interested in making the best thing ever, just a fun game that people can enjoy in a style that i like.

thanks for the help, though, i really do appreciate it!

User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2032
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

Re: Progress Thread - Project Blue

Post by FrankenGraphics » Wed Feb 07, 2018 9:51 am

It's a good thing we can throw lots of inexpensive ROM on the problem post compo.
http://www.frankengraphics.com - personal NES blog

User avatar
toggle switch
Posts: 131
Joined: Fri Sep 30, 2016 8:57 pm

Re: Progress Thread - Project Blue

Post by toggle switch » Wed Feb 07, 2018 9:54 am

do you just keep the 'fixed' bank in it's own file, and the use .incbin to include it in each separate bank, or what?

tepples
Posts: 21752
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Progress Thread - Project Blue

Post by tepples » Wed Feb 07, 2018 9:58 am

toggle switch wrote:do you just keep the 'fixed' bank in it's own file, and the use .incbin to include it in each separate bank, or what?
With regard to detailed working steps for causing ca65 and ld65 to emit the code repeated in all banks, I cannot give an authoritative answer for several hours because I am not at a PC on which my NESdev toolchain (Git, Make, cc65, Python, GIMP, Windows or Wine, and FCEUX debugger) is installed. In the meantime, I'll answer the following question:
FrankenGraphics wrote:Would trampolines like that be a significant increase in cycles?
I'll sketch an untested trampoline for BNROM here for the purpose of estimating how many cycles it would use. Some changes are needed for GTROM because PRG bank is in the same port as CHR and nametable switching, but they should be minimal.

Code: Select all

; These parts go in all banks, using a method to be determined
; once I am at a PC on which my NESdev toolchain is installed

; A ROM location reflecting which bank is currently switched in
current_bank: .byte I

.proc nmi_handler_prolog
  pha
  txa
  pha
  tya
  pha

  ; Save previous bank
  lda current_bank  ; a location in ROM
  pha
immediate_stmt:
  lda #<.bank(nmi_handler_body)
  sta immediate_stmt

  jmp nmi_handler_body
.endproc

.proc nmi_handler_epilog
  pla
  tax
  sta identity,x

  pla
  tay
  pla
  tax
  pla
  rti
.endproc

; This part goes in any bank
.segment "MUSICCODE"
.proc nmi_handler_body
  ; TODO: Push display list to OAM
  ; TODO: Push VRAM changes to CHR RAM and nametable
  ; TODO: Call music engine
  ; (Watch out for reentrancy when the NMI might interrupt
  ; changing music or playing a sound effect!)
  jmp nmi_handler_epilog
.endproc

identity:
  .byte 0, 1, 2, 3
An NMI handler that does significant work will already be saving and restoring AXY. The parts of this not in a "typical" NMI handler are saving the old bank, switching to the new bank, and the JMP in and out of the body, and restoring the old bank, which total about 30 cycles.

The preceding doesn't include the logic to put the prolog and epilog in all banks. To make that, I would need to be at a PC on which my NESdev toolchain is installed.
FrankenGraphics wrote:A bank strictly for storing graphics to be loaded in bulk between levels wouldn't need it
It'd still need the trampolines in case graphics loading is interrupted, and it'd need the code for decompressing said graphics.

User avatar
FrankenGraphics
Formerly WheelInventor
Posts: 2032
Joined: Thu Apr 14, 2016 2:55 am
Location: Gothenburg, Sweden
Contact:

Re: Progress Thread - Project Blue

Post by FrankenGraphics » Wed Feb 07, 2018 10:08 am

do you just keep the 'fixed' bank in it's own file, and the use .incbin to include it in each separate bank, or what?
i think there are three ways; somewhat combinable:

.segment [name] = anything between this directive and the next .segment can be used by the linker to paste the segment exactly where you want it in ROM. this should give you the most convenient control.

.inc or .include [filename] = includes a separate source file - you still need to define a .segment, though.

.incbin [filename] = includes a separate binary as raw data. This isn't preferable imo as you need to pre-assemble it. It's mainly for assets. also, .segment is still what gives you control over where it is placed in ROM.

Correct me if i'm wrong, but I don't think you can escape using .segment in any way without complicating stuff further.


Still, i've seen at least one instance where partytimehexcellent .incbinned the famitracker driver + music data at a specified ROM addr to be done with it. This was done to a simpler mapper using another assembler without a linker, though. with the cc65 suite (nesicide included), the linker is supposed to do ROM organization stuff for you - via the linker cfg.
Last edited by FrankenGraphics on Wed Feb 07, 2018 10:13 am, edited 1 time in total.
http://www.frankengraphics.com - personal NES blog

User avatar
pubby
Posts: 548
Joined: Thu Mar 31, 2016 11:15 am

Re: Progress Thread - Project Blue

Post by pubby » Wed Feb 07, 2018 10:11 am

honestly, the efficiency of data storage is on the bottom rung of things i care about here
You could just code it as a 256K UNROM and then use membler's converter program to convert it into 512K GTROM.
toggle switch wrote:do you just keep the 'fixed' bank in it's own file, and the use .incbin to include it in each separate bank, or what?
I put the code in a macro then do the fixed banks like this:

Code: Select all

.macro fixed_impl
    ; fixed code goes here
.endmacro

.repeat 16, i
    .segment .concat("FIXED", .string(i))
    .byt i ; Store the current bank at $8000
    .if i = 0
        fixed_impl
    .else
        .scope .ident(.concat("dummy_scope", .string(i)))
            fixed_impl
        .endscope
    .endif
.endrepeat
I've had problems when I tried the include/incbin approach.

User avatar
toggle switch
Posts: 131
Joined: Fri Sep 30, 2016 8:57 pm

Re: Progress Thread - Project Blue

Post by toggle switch » Wed Feb 07, 2018 10:13 am

thanks. i think the macro method should work for me... wasn't aware i could have a macro containing procs and other macros.

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

Re: Progress Thread - Project Blue

Post by tokumaru » Wed Feb 07, 2018 12:04 pm

It would be a waste to replicate the entire engine across multiple banks! What I normally do is put data and functions that use that specific type of data in their own banks. For example, all banks that contain level maps will include copies of the routines that prepare rows and columns of tiles for scrolling, and also the routines that test for collisions between objects and level geometry. Banks with compressed graphics will have a copy of the decompression routine, and so on. It's the same principle of having audio data in the same bank as the audio driver.

Switching banks in simple mappers like GTROM is pretty fast, so that shouldn't have a significant impact on your vblank bandwidth. I'm obsessed with making the most out of the vblank time myself, so I don't even push A, X and Y to the stack during an expected vblank, I only do it on unexpected vblanks (i.e. lag frames). This is controlled by a flag on bit 7 of some variable (e.g. "FrameRead") so that it can be checked with BIT without corrupting any registers. The main thread waits for vblank by waiting for this flag to be cleared, and the NMI handler will only bother saving A, X and Y when the flag is already clear when the NMI fires.

In the main thread:

Code: Select all

  dec FrameReady ;$00 -> $ff
WaitForVblank:
  bit FrameReady
  bmi WaitForVblank
In the NMI:

Code: Select all

HandleLagFrame:
  pha
  txa
  pha
  tya
  pha
  jmp DoCommonStuff

NMI:
  bit FrameReady
  bpl HandleLagFrame
  ;(update vram)

DoCommonStuff:
  ;(handle the scroll and other raster effects)
  ;(handle audio updates)

  inc FrameReady
  beq Return
  dec FrameReady
  pla
  tay
  pla
  tax
  pla

Return:
  rti
Something along these lines. Saves a few cycles if you're really really tight on vblank time.

As for simulating a fixed bank when using 32KB PRG-ROM switching, you can .include the "fixed bank", but you have to do something about the repeated labels. One thing you can do is include the file normally only once, and all other times surround it in a new scope:

Code: Select all

.segment "FIXED0"
.scope
  .include "fixed.asm"
.endscope

.segment "FIXED1"
.scope
  .include "fixed.asm"
.endscope

.segment "FIXED2"
.scope
  .include "fixed.asm"
.endscope

;(...)
.segment "FIXED15"
.include "fixed.asm"
It's similar to the macro approach, but with includes. If you need to put the bank number in each bank, you can just do .byte <.bank(*).

calima
Posts: 1018
Joined: Tue Oct 06, 2015 10:16 am

Re: Progress Thread - Project Blue

Post by calima » Wed Feb 07, 2018 12:17 pm

ld65 does not support pasting a segment to multiple places.

Post Reply