wla-dx icon indicating copy to clipboard operation
wla-dx copied to clipboard

Multiple ROM Bank Allocation / Auto-split

Open bazz1tv opened this issue 8 years ago • 23 comments

In a situation where the developer is including a lot of data bin files, it would be convenient that the assembler allow the developer to fill many ROM banks, where WLA would automatically split bin files at the proper places. (or a DATAROM BANK MAP for more complex non-contiguous mapping purposes)..

App Developer is responsible for creating Driver Code that, given a label and datasize, could traverse the ROM banks as necessary to process the data as a whole.

Does WLA have anything like this so I can automatically manage my data banks??

Extra thought: A similar idea could even be used for Code ROM Banks -- a number of banks allocated for code, and when the assembler detects that one bank is filled, it automatically [if needed on some system] adds a jump instruction to the next code bank.

In both cases, this helps makes the most of the ROM space, where manual management [to me] seems too difficult to deal with.

bazz1tv avatar Sep 04 '15 17:09 bazz1tv

I wonder if it would work if I didn't put that data into a .SECTION, but I assume the use of .BANK will restrict the data to only fitting in that one bank.

bazz1tv avatar Sep 04 '15 23:09 bazz1tv

I just realized a potential work-around -- but only for the "DATABANK" idea [wouldn't work for code-bank]. Just make some ROMBANKS in the ROMBANKMAP [this is what I use anyways] that are actually several real ROMBANKS in size. You probably must also make a SLOT big enough to handle the data.

In the case of data [not code], this is OK since raw data is easier to manually memory map. Still the developer has to make code to properly fetch this kind of data as explained in the 1st post. (no problem)

bazz1tv avatar Sep 05 '15 14:09 bazz1tv

Hm, I just realized tho, in this case, :label won't work properly to get the correct data bank.. hmm...

In the context of my current project (huc6280) I think I can figure out an alternative solution. Since banks are only $2000 bytes a piece, the address of the label can be parsed. Considering I am using a slot start address of $0000, I can do #>label >> 5 to see how many banks from the load_bank the label is at.

bazz1tv avatar Sep 05 '15 15:09 bazz1tv

I must also mask the bank(page) from the 16-bit address by doing and #$1f on the high byte of the label address.. This is to make an address like $2014 become $0014 while the logic in the previous paragraph actually adds this interval-of-bank_page (banks' and pages' meanings are interchangeable in this context) to the real bank number as PCE understands it to be.

So this workaround makes things kind of ugly to understand.. I wonder if WLA could be modified to deal with it smoothly -- having a real databank system where label's starting bank :label is automatically provided by WLA in a multi-bank data section.

bazz1tv avatar Sep 05 '15 15:09 bazz1tv

here is some example code taken from my engine to provide proof-of-concept. You can't compile it, but I assure you it works and this demonstrates the core concepts with specific application to huc6280. What it doesn't show is the additional code that automatically maps pages for loads of data > page_size [$2000 bytes].

I also tested a data load address that is 24 bytes wide ($12000) to see if WLA had a problem but it works since all the >16-bit math occurs during build, with the final addresses fitting in the bounds of the capability of the system (tested).

.IFNDEF MEMORYMAP_I
.DEFINE MEMORYMAP_I

.define PAGE_SIZE $2000

.enum 8
ZP_SLOT         db
RAM_SLOT        db
RAM_TMPABS_SLOT db
DATA_SLOT       db
.ende

; BANKS
.enum $0
BANK_RESET          db
BANK_XPMP           db
BANK_PCM            db
BANK_LIB            db
; Add more banks here

;
BANK_DATA           db
NUM_WLA_BANKS       db
.ende

.DEFINE DATABANK_SIZE $40000    ; 32 real ROM banks
.DEFINE NUM_DATABANKS DATABANK_SIZE / $2000
.DEFINE NUM_PCEBANKS (NUM_WLA_BANKS-1) + NUM_DATABANKS
.PRINTT "NUM_PCEBANKS = $"
.PRINTV HEX NUM_PCEBANKS
.PRINTT "\n"

; specify banks for RAM_TMPABS_SLOT
.enum 0
RAMBANK_TMPABS_TITLESCREEN     db
.ende

.memorymap
    defaultslot 0
    slotsize $2000
    slot 0 $0000
    slot 1 $2000
    slot 2 $4000
    slot 3 $6000
    slot 4 $8000
    slot 5 $A000
    slot 6 $C000
    slot 7 $E000
    ; RAM slots
    slot ZP_SLOT            $2000 $100  ; Zero-Page
    slot RAM_SLOT           $2200 $1d00 ; skipped stack - leave last $100 bytes open for tmp modules
    slot RAM_TMPABS_SLOT    $3f00 $100  ; tmp abs RAM for different modules that use it individually
    slot DATA_SLOT          $0000 DATABANK_SIZE
.endme

; PAGES
.enum 0
PAGE_IO         db ; 0 0000-1fff
PAGE_RAM        db ; 1 2000-3fff
PAGE_XPMP       db ; 2 4000-5fff
PAGE_ARB1       db ; 3 6000-7fff
PAGE_ARB2       db ; 4 8000-9fff
PAGE_LIBCODE    db ; 5 a000-bfff
PAGE_PCM        db ; 6 c000-dfff
PAGE_RESET      db ; 7 e000-ffff
.ende

.define IO_ADDR     PAGE_IO*PAGE_SIZE

.define WRAM_ADDR   PAGE_RAM*PAGE_SIZE

.ENDIF
.rombankmap
    bankstotal NUM_WLA_BANKS

    banksize PAGE_SIZE
    banks NUM_WLA_BANKS-1

    banksize DATABANK_SIZE ; Bank 8
    banks 1
.endro
.bank BANK_DATA slot DATA_SLOT
.org $12000
.section "data2-gfx" force
cursor_pic:
    .incbin "../engine/gfx/phoenix/cursor.pcepic"
cursor_pic_end:
.ends
; ADDR suffix because WLA DX cannot take in a argument that could be # or $
.macro UP_VRAM_DBANK_ADDR ARGS label, vram_addr, words
    ; calculate PCE bank from WLA databank
    lda #>(\1 >> 5)    ; converts page-address into bank number
    clc
    adc #:\1    ; proper bank number
    sta <bl

    lda #<\1
    sta <si
    lda #>\1
    and #$1f    ; mask out page
    sta <si + 1




    lda \2.w + 0
    sta <di
    lda \2.w + 1
    sta <di + 1
    lda #<(\3>>1) ; bytes
    sta <cl
    lda #>(\3>>1) ; bytes
    sta <ch
    jsr load_vram
.endm

bazz1tv avatar Sep 05 '15 16:09 bazz1tv

once again I did a lot of editing, so if you're reading email you'll want to see the website.

bazz1tv avatar Sep 05 '15 16:09 bazz1tv

This sounds basically the same as issue #36. I do have a convoluted workaround for it, but it requires hard-coding the start address of your data.

Stewmath avatar Jan 15 '16 16:01 Stewmath

I agree that this is the same problem as #36. It is also apparent that any solution will require per-system details. Of course, we may also be able to segment independent details which are common across systems.

While I have no plans of making explicit contributions to WLA DX at this point -- I believe there is potential in the solution I came up with. I'd like to elaborate on the finer points of it -- expose the system-(in)dependent parts, and that will be here for someone to hopefully inspire them -- that they may see how the pieces I created fit into a bigger solution.

Pros

  • It requires no hard-coding of data addresses. Simply specify how big.
  • can span as many banks as possible/necessary.

note: if you specify a size that doesn't actually comply with your system's specs then of course you will run into trouble. Knowledge of your target system is implied.

Cons The solution I came up with satisfied my needs -- it only uses one of these super-spanning banks to seamlessly/easily organize data -- it would need to be modified to support more banks, or different placement (eg. not at the end of bank map).

Implementation In order to maintain a ROMBANKMAP with the new notion of a DATABANK (which spans many banks) -- I had to create a way to identify banks as WLA sees them, and banks as the target system does. For my example which targets PC-Engine (PCE), I use the terms WLA Bank and PCE Bank.

Let's see if I can walk thru the code once again, this time making it clear. Any parts I skip are parts that are simply too PCE-specific to matter. Let's begin:

.enum 8
ZP_SLOT         db
RAM_SLOT        db
RAM_TMPABS_SLOT db
DATA_SLOT       db
.ende

optional: This is simply a project design decision -- I found that automating the Slot numbers provides convenience -- especially for systems/projects with lots of slots. But, it's not imperative to automate your slot numberings for the Data Bank Implementation.

; WLA BANKS
.enum $0
BANK_RESET          db
BANK_XPMP           db
BANK_PCM            db
BANK_LIB            db
; Add more banks here

;
BANK_DATA           db
NUM_WLA_BANKS       db
.ende

required: I have my banks in an enum so that NUM_WLA_BANKS can automatically be generated. I decided to have BANK_DATA be the last bank -- it makes the complex bank calculations only required for the databank -- but placing it in the middle or beginning shouldn't be an impossible task, yet not one I want to consider.

.DEFINE DATABANK_SIZE $40000    ; 32 real ROM banks
.DEFINE NUM_DATABANKS DATABANK_SIZE / $2000

I define the databank size in bytes, and I derive the number of PCE banks it has (target-system banks, if you will).

.DEFINE NUM_PCEBANKS (NUM_WLA_BANKS-1) + NUM_DATABANKS

Here I automatically deduce the total number of PCE (target-system) banks in the whole program. I use NUM_WLA_BANKS - 1 since the DATA_BANK that is present in the "wla-banks-enum" should not be counted as a PCE bank.

.PRINTT "NUM_PCEBANKS = $"
.PRINTV HEX NUM_PCEBANKS
.PRINTT "\n"

Then, I print out the result to the console during assembly/linking just for debugging.. (I hate that WLA requires 3 directives just to print that string, but that's for another issue :stuck_out_tongue:)

.memorymap
;...
;...
    slot DATA_SLOT          $0000 DATABANK_SIZE
.endme

DATA_SLOT is simply a container slot for the Data Bank, which is why we can have it start at $0000. The DATABANK_SIZE comes from a prior automatic calculation :smile:

Let's use the following example:

.bank BANK_DATA slot DATA_SLOT
.org $0
.section "data2-gfx"
cursor_pic:
    .incbin "../engine/gfx/phoenix/cursor.pcepic"
cursor_pic_end:
.ends

cursor_pic references $0000 in the DATA_SLOT. That label can be provided to special macros which can automatically access the real locations of the data.

In my PCE implementation, these macros work by using the 16-bit address and bank address of the target label -- ie cursor_pic and :cursor_pic

Because I've placed the Data Bank as the last bank in the WLA bank map, and all other banks are the normal PCE (target) bank size, it turns out that the WLA bank number of the Data Bank matches its PCE (target) bank number. So, this is a convenience I could create on my PCE project -- a more proper calculation would need to be made for a wider solution. But, to finish out, here is how the target address is obtained from the label that is in the databank/dataslot:

  • Use :label -- which provides WLA bank -- to get the base-target-bank (based on the previously explanation, no additional math was necessary to derive the target bank from the WLA bank). In any case, let's say you've derived the base_bank
  • base_bank + (label / bank_size) = target_bank
  • label % bank_size = target_offset

target_bank:target_offset

I just made that math on the spot -- but it looks correct.

.rombankmap
    bankstotal NUM_WLA_BANKS

    banksize PAGE_SIZE
    banks NUM_WLA_BANKS-1

    banksize DATABANK_SIZE ; Bank 8
    banks 1
.endro

Here we see the automatically generated NUM_WLA_BANKS being used in 2 places.

I won't bother explaining the rest - I've already explained the ideology behind the macro, it shouldn't be necessary to show the PCE implementation... Besides, it gets really gritty on PCE specifics / bit twiddling.

Conclusion

In my implementation, I did not actually change WLA DX source code, it's all done on top. If this became a legitimate contribution to WLA DX, the internalization of these implementation details would hopefully result in cleaner looking source code, since all of the complexity would be taken care of under the hood.

I recommend starting the incorporation of this into WLA DX modularly. Start only with a single system and the system-independent layer. Draft the design considerations that are necessary. Here are a couple I can think of (test cases could be developed):

  • Arbitrary placement of these new banks
  • Avoid and test against assumptions I created (ie. I ensured and drew calculations around data-bank's wla-bank-num == target-bank-num) Bad me!
  • Be able to create and use > 1 of these special banks, contrary to the single impl I created.

Build a PoC, make it work for that one system -- then go on from there.

bazz1tv avatar Jan 15 '16 18:01 bazz1tv

I just made some major edits to the previous post, so please check it on Github and not your (old version) email. Thanks

P.S. This was an unplanned thing, I may not have time to comment or follow-up. I hope it helps!

bazz1tv avatar Jan 15 '16 18:01 bazz1tv

Very interesting solution. It'd still be cleaner if the linker supported it, but I don't really care that much at this point since I've got it working pretty decently (and without needing multiple bank tracking systems :P). Incidentally, this solution probably won't work in my case due to the need to place data in very specific locations, but for normal projects I imagine it'll work just fine.

Anyway I'll close #36.

Stewmath avatar Jan 15 '16 19:01 Stewmath

Everyone, I think I just came up with a good solution. I hope you look over this rough draft under scrutiny and address every small detail.

Forget my Data Bank implementation -- there are simpler solutions when it comes to modifying WLA DX

Let's realign ourselves with the thinking of @vhelin in #36

The linker would need to be modified so that it would split sections that overflow from a bank to the next one, into two, and I'm not 100% sure this moment how I'd handle e.g. 16bit pending computations where the 1st byte would go into bank A, and the 2nd byte into bank A+1... Also, what would be the slot for the code/data that goes into bank A+1?

Let's break that into 2 main points, taking the latter first:

...what would be the slot for the code/data that goes into bank A+1?

Why don't we develop into WLA DX an optional new directive that allows users to specify a map between ROMBanks and Slots!! Not being one for unique names, I'll (temporarily) call it the .ROMBANKSLOTMAP

Its implementation should be similar to ROMBANKMAP, where repeated assignment of the same slot to ascending banks can easily be assigned. Here is an example of such a thing:

.ROMBANKSLOTMAP
    banks 5
    slot 1
.ENDROMBANKSLOTMAP

In this case, I've told WLA that Rom banks 0-4 will use slot 1.

Of course, WLA must confirm the presence of a ROMBANKMAP and each slot's existence for this to work. It should also make sure that the number of banks found in the ROMBANKSLOTMAP matches the total banks in the ROMBANKMAP

As stated, the actual use of ROMBANKSLOTMAP can be optional in a project, only required when the programmer wants to perform cross-bank situations.

a cross-banking .SECTION can be referenced as having this special property by adding to the list of "FREE, SEMIFREE, FORCE, etc" a new keyword, perhaps OVERFLOW -- strong consideration should be placed here on the allowed / recommended combinations of keywords with OVERFLOW ie. OVERFLOW SEMIFREE -- and this combo may require modification of WLA's .SECTION directive parsing code to allow for the new keyword combinations.

Here there is an opportunity to discuss whether usage of SLOT in a .BANK directive can override the ROMBANKSLOTMAP

There is a concern over cross-Bank situations that are non-contiguous in the target system's memory map and likely to cause error -- but I'm not sure WLA is knowledgeable enough to warn the user of such situations -- and whether WLA can take the opportunity to warn users that a non-contiguous cross-banking situation has been created by the user.

I'm not 100% sure this moment how I'd handle e.g. 16bit pending computations where the 1st byte would go into bank A, and the 2nd byte into bank A+1

That would likely require knowledge of whether target system is even capable of executing code cross-bank. For instance, CPU might wrap around current bank.

Perhaps we could build some kind knowledge base per system that WLA checks into during assembly/linking -- if it realizes it is working with code cross-bank on a system that is unsupported -- it should stop and error out. On a system that is supported, it will work, but warn user if it notices a non-contiguous cross-bank circumstance. Of course, there is the aforementioned situation where WLA might not be knowledgeable enough to alert the user that in the target system's memory map, the ROM will not be mapped / executed contiguously. But hey, this is low level coding after all, some burden will fall on the coder.

bazz1tv avatar Jan 15 '16 23:01 bazz1tv

Necroing this because I'm discovering this limitation too; Q&A at SMSPower: http://www.smspower.org/forums/17379-WLADXHowToHandleDataThatSpansTwoBanksWithoutCleanSplit

I think a quicker, easier, cheaper solution is possible compared to a more comprehensive overhaul as preposed here. I'm fine with a quick-n-dirty solution if it means I get to use it now rather than a better solution later (or never).

The BANK directive wherever it is used (including within link files) could be expanded to allow for multiple banks. There are just two different packing methods required for fitting sections across banks. The first is the current behaviour where sections are packed within banks and no sections can cross the border. The second methodology is to allow data to cross a bank boundary as if it were not there.

Therefore, the BANK directive needs to be able to select three possible modes:

  1. Single bank -- default, current behaviour. The section is assigned to a single bank
  2. Multiple banks -- more than one bank can be used but any one section cannot cross the border
  3. Contiguous banks -- sections are continuously packed with no regard for bank borders

Kroc avatar Feb 27 '19 21:02 Kroc

The BANK directive wherever it is used (including within link files) could be expanded to allow for multiple banks. There are just two different packing methods required for fitting sections across banks. The first is the current behaviour where sections are packed within banks and no sections can cross the border. The second methodology is to allow data to cross a bank boundary as if it were not there.

Therefore, the BANK directive needs to be able to select three possible modes:

1. Single bank -- default, current behaviour. The section is assigned to a single bank

2. Multiple banks -- more than one bank can be used but any one section cannot cross the border

3. Contiguous banks -- sections are continuously packed with no regard for bank borders

I think it'd be better to give this mode number/definition to .SECTION instead of .BANK as it's a section specific attribute.

Bazzinotti's .ROMBANKSLOTMAP sounds like a working way to define multibank areas. Perhaps something like

.ROMBANKSOVERFLOWMAP banks 0 to 4 slot 0 banks 5 to 6 slot 1 .ENDROMBANKSOVERFLOWMAP

might be clearer? This would define two multibank areas for section placement. Then

.bank 2 .section "sectionA" free overflow ... .ends

would be placed somewhere inside the first multibank area, but completely inside one bank, not split between two or more banks.

.bank 5 .section "sectionB" free overflow allowsplit ... .ends

This section would be placed somewhere inside the second multibank area, and could also be placed on the border of two banks 5 and 6.

How's this?

vhelin avatar Feb 28 '19 18:02 vhelin

I've started to work on this. But I have a question: Is the slot definition here necessary?

.ROMBANKSOVERFLOWMAP banks 0 to 4 slot 0 banks 5 to 6 slot 1 .ENDROMBANKSOVERFLOWMAP

What if we just used the slot that's assigned to the section already when the section is defined? Giving the slot in .ROMBANKSOVERFLOWMAP would be less flexible...

vhelin avatar Mar 03 '19 18:03 vhelin

Sorry, somehow I missed your comment three days ago! :(

I get a feeling you're defining the wrong thing -- the ultimate goal should be that no bank numbers should appear in the source code and all bank numbers get assigned by the linker. Having bank numbers in the source code limits the flexibility; as an example of the kind of flexibility needed, I'm modifying C64 Elite to run from cartridge. The same source code has to work from disk, which has no ROM banking, to a cartridge image that now has a completely different memory-layout and now with banking. This I am able to do with ca65 because the linker determines the addresses in the memory layout.

Think of the slots as "windows" into the ROM, with the addition of combining slots into a bigger window. The bank numbers do not matter; these are assigned by the Section. The size of the window determines how the bank borders are handled. Given that multiple sections of code / data can be packed into a bank, the actual bank number has no relevance to the overflow, such as you are defining with .ROMBANKSOVERFLOWMAP -- it would be entirely possible to fall 1.5 banks with one kind of slot window and a further 1.5 banks with a different slot window, even interleaved.

That is to say that different code sees a different memory map; in terms of WLA-DX that means that the size and positions of Slots are effectively not universal. The solution to the entire bank overflow problem is to allow each piece of code/data (Section) to see the memory map it wants to.

I think this would entail allowing multiple, overlapping slots to be defined, each able to define the overflow strategy they employ.

.MEMORYMAP
    .SLOT "SLOT_0"   START $0000 SIZE $4000
    .SLOT "SLOT_1"   START $4000 SIZE $4000
    .SLOT "SLOT_2"   START $8000 SIZE $4000
    .SLOT "SLOT_01"  START $0000 SIZE $8000
    .SLOT "SLOT_12"  START $4000 SIZE $8000
    .SLOT "SLOT_012" START $0000 SIZE $C000 
    .DEFAULTSLOT "SLOT_012"
.ENDME

From this definition we already know how many banks fit into each Slot (given 16 KB banks) so that probably does not need explicitly defining.

Since each Section can select its own Slot, each piece of code/data can therefore select the "expected memory map" and the bank-overflow strategy automatically used by the packing-algorithm.

Kroc avatar Mar 03 '19 19:03 Kroc

There are a total of three different overflow-handling behaviours for the defined Slots.

  1. No-Overflow: We can think of this as WLA-DX's default behaviour. WLA-DX terminates if the slot overflowed. There is no scheme defined for paging.

  2. "page" mode: A Slot that defines a "page" mode (probably by some kind of attribute on the slot's memory-map entry) will begin using the next numbered bank when the Slot becomes full. Code/Data will not be split across the border. Each Section assigned must fit into the Slot.

  3. "flow" mode: The Slot is shifted down by one bank. E.g. a Slot that is 32KB wide, currently filling banks 4 & 5, would switch to banks 5 & 6 and continue filling at bank 6

Kroc avatar Mar 03 '19 19:03 Kroc

The importance of the "only Slots matter" view above is that the border between banks is automatically and intelligently handled for Slots bigger than one bank. For example, a Slot of 48 KB will assign to banks 0, 1 & 2 to begin with. Data will automatically flow over the bank 0 & 1 border without problem because the Slot is wide enough. When bank 2 is full, so is the Slot -- what happens next is determined by the properties of the Slot and because we can define multiple Slots across the same address-space, different code can use different banking schemes according to its own implementation. One Slot does not have to force a particular paging scheme on all code/data that occupies that Slot.

Kroc avatar Mar 03 '19 19:03 Kroc

Sorry, somehow I missed your comment three days ago! :(

I get a feeling you're defining the wrong thing -- the ultimate goal should be that no bank numbers should appear in the source code and all bank numbers get assigned by the linker. Having bank numbers in the source code limits the flexibility; as an example of the kind of flexibility needed, I'm modifying C64 Elite to run from cartridge. The same source code has to work from disk, which has no ROM banking, to a cartridge image that now has a completely different memory-layout and now with banking. This I am able to do with ca65 because the linker determines the addresses in the memory layout.

If you have the same computer the same memory layout applies with disks and ROM modules? At least in c64's case the slots are the same (same .MEMORYMAP):

https://www.c64-wiki.com/wiki/Memory_Map

What's different is what you have in those slots?

Think of the slots as "windows" into the ROM, with the addition of combining slots into a bigger

I'd really like to think of slots as places in the CPU address space where stuff is put into. Some slots deal with RAM, others with ROM or I/O. :)

window. The bank numbers do not matter; these are assigned by the Section. The size of the window determines how the bank borders are handled. Given that multiple sections of code / data can be packed into a bank, the actual bank number has no relevance to the overflow, such as you are defining with .ROMBANKSOVERFLOWMAP -- it would be entirely possible to fall 1.5 banks with one kind of slot window and a further 1.5 banks with a different slot window, even interleaved.

That is to say that different code sees a different memory map; in terms of WLA-DX that means that the size and positions of Slots are effectively not universal. The solution to the entire bank overflow problem is to allow each piece of code/data (Section) to see the memory map it wants to.

Could this be a solution to this problem:

.ROMBANKSOVERFLOWMAP banks 0 to 2 slots 1 to 3 banks 3 to 5 slots 1 to 3 .ENDROMBANKSOVERFLOWMAP

when each bank is e.g. 8K and each slot is e.g. 8K. In my idea that slot would be dropped off from .ROMBANKSOVERFLOWMAP the linker would just have used consecutive slots automatically, but defining the slots range is really a much clearer way and should not cause confusion.

I think this would entail allowing multiple, overlapping slots to be defined, each able to define the overflow strategy they employ.

.MEMORYMAP
    .SLOT "SLOT_0"   START $0000 SIZE $4000
    .SLOT "SLOT_1"   START $4000 SIZE $4000
    .SLOT "SLOT_2"   START $8000 SIZE $4000
    .SLOT "SLOT_01"  START $0000 SIZE $8000
    .SLOT "SLOT_12"  START $4000 SIZE $8000
    .SLOT "SLOT_012" START $0000 SIZE $C000 
    .DEFAULTSLOT "SLOT_012"
.ENDME

From this definition we already know how many banks fit into each Slot (given 16 KB banks) so that probably does not need explicitly defining.

Since each Section can select its own Slot, each piece of code/data can therefore select the "expected memory map" and the bank-overflow strategy automatically used by the packing-algorithm.

The slot is actually not a problem in the implementation of overflow, it's the banks as code is put inside banks, and somethings the code needs to get the bank number for a label for example.

Anyway, an example:

.MEMORYMAP
  DEFAULTSLOT 0
  SLOT 0 START $0000 SIZE $2000
  SLOT 1 START $2000 SIZE $2000
  SLOT 2 START $4000 SIZE $2000
  SLOT 3 START $6000 SIZE $2000
.ENDME

If this is what the hardware gives it would feel weird to define a slot 4 from $0000 to $8000 if we wanted to use slots 0-3 for overflow as there really is no such slot 4 in the hardware - we'd be mixing real slots and virtual slots. So instead of mixing these I suggest we'll define slot ranges.

Would this be ok?

vhelin avatar Mar 03 '19 22:03 vhelin

The importance of the "only Slots matter" view above is that the border between banks is automatically and intelligently handled for Slots bigger than one bank. For example, a Slot of 48 KB will assign to banks 0, 1 & 2 to begin with. Data will automatically flow over the bank 0 & 1 border without problem because the Slot is wide enough. When bank 2 is full, so is the Slot -- what happens next is determined by the properties of the Slot and because we can define multiple Slots across the same address-space, different code can use different banking schemes according to its own implementation. One Slot does not have to force a particular paging scheme on all code/data that occupies that Slot.

Is there a computer where you can runtime change the type of what a slot contains? Like first some slot n of 4K has 4k ROM banks inside, but then a bit flips and slot n now has two swappable 2K RAM banks inside of it?

I have to admit that I don't understand the benefits of this "only slots matter" design. :)

vhelin avatar Mar 03 '19 23:03 vhelin

There are a total of three different overflow-handling behaviours for the defined Slots.

1. No-Overflow: We can think of this as WLA-DX's default behaviour. WLA-DX terminates if the slot overflowed. There is no scheme defined for paging.

2. "page" mode: A Slot that defines a "page" mode (probably by some kind of attribute on the slot's memory-map entry) will begin using the next numbered bank when the Slot becomes full. Code/Data _will not_ be split across the border. Each Section assigned _must_ fit into the Slot.

The current way WLA works it's banks that are filled. Slots are just holes in the memory where banks are inserted into. With border you mean slot borders or bank borders? I assume bank borders. And yes, if we define bank (and slot) ranges, then each section must fit completely inside one range.

3. "flow" mode: The Slot is shifted down by one bank. E.g. a Slot that is 32KB wide, currently filling banks 4 & 5, would switch to banks 5 & 6 and continue filling at bank 6

This is something I'll leave for later. Version 1 of overflow sections should work without it. :)

vhelin avatar Mar 03 '19 23:03 vhelin

I have to say that WLA is more geared towards architectures where the code is inside ROM modules. Program file support was added later, and in situations where you just create a program and load that into RAM for execution, "only slots matter" makes more sense. But we cannot drop supporting ROM banks as that would totally kill the console (NES, SNES, SMS...) support. But perhaps we could somehow make program file support simpler and better?

vhelin avatar Mar 03 '19 23:03 vhelin

I gave the "only slots matter" concept more time and now think that perhaps some sort of a compromise would be good where one can give sections ROM bank numbers, but if they are not given then the sections could be placed consecutively in memory so that no section crosses multislot boundaries, or something like that. Then RAM loaded programs were easier to write using WLA DX.

In some architectures it's a must that some important sections are places in bank 0, which is why I don't like about the idea of dropping ROM bank support. But I'm sure there is some way RAM loaded programs could be written easier...

vhelin avatar Mar 06 '19 15:03 vhelin

I'm always working in mind of RAM slots+banks too :)

Perhaps to avoid over-saturating slots as a meta-structure rather than based on directly on hardware, there could be a separate meta-structure to handle the combination of slots; I would call this a "WINDOW", but other terms may suffice. A simpler method may be to extend the SLOT property of .SECTIONs to accept multiple slot numbers, e.g. .SECTION "big_rom" SLOT 1, 2 BANK 3 TO 6

Kroc avatar Mar 06 '19 21:03 Kroc