Jump to content
IGNORED

ROM Segment questions


cmadruga

Recommended Posts

Got a few questions, would appreciate help from the experts.

Questions below assume the following segmentation as described on 42K.bas:

 

REM -------------------------------------------------------------------------
REM 42K ROM memory map
REM -------------------------------------------------------------------------
REM SEGMENT ADDRESS RANGE SIZE NOTES
REM ======= ============== ===== =====
REM 0 $5000 to $6FFF $2000 - Default segment
REM 1 $A000 to $BFFF $2000
REM 2 $C040 to $FFFF $3FC0 - Avoid STIC aliasing.
REM 3 $2100 to $2FFF $0F00
REM 4 $7100 to $7FFF $0F00 - Avoid EXEC's ECS ROM probe.
REM 5 $4810 to $4FFF $0800 - Account for game's ECS ROM mapping out code.
REM -------------------------------------------------------------------------

 

 

Questions:

 

1- what is the easiest way to measure space still available within each segment, and % already used by my program and data?

2- should I generally aim to occupy 100% of one segment before starting to use the next? Or would leaving some buffer space available be advisable for whatever reason?

3- between using one larger segment, or 2+ smaller ones, is it all the same? Any benefits to opting to use more, smaller segments?

4- does the choice of segment have any impact on performance, compatibility or ability to run the code on real hardware? I noticed the "avoid" notes on the table above, but don't quite get the practical meaning, or what needs to be done differently on IntyBASIC for instance.

 

Thank you

 

 

Link to comment
Share on other sites

You can look at the CFG file generated by the assembler. It will tell you which segments you already have used. If you enable generation of list files, also an assembler directive, you will get the assembler code with exact addresses used so you can see exactly where in your source code a segment break occurs. As a bonus, you can follow the instructions in the IntyBASIC manual to enable full debugging within the emulator, setting breakpoints and more.

 

Generally I believe you should fill each segment as closely as you can. Note that currently the IntyBASIC compiler itself doesn't skip over to the next safe segment, so e.g. after it has reached $6FFF it continues at $7000 which collides with ECS ROM if you have it enabled. That is why you need the ASM ORG directive in the BASIC code to manually switch segment once you are getting close to filling one.

 

For emulation purposes, I believe it doesn't make a difference which segments you use. If you'd ever put your program on a cartridge, the segments used might have some importance, perhaps in the order as described in the map above?

 

The "Avoid" means e.g. segment 4 starts at $7100 and not $7000 as you would imagine, and segment 2 starts at $C040 instead of $C000, as the first addresses would mean conflicts which are avoided this way.

Link to comment
Share on other sites

Typically, you'll just fill one segment before going on to the next one since that is the simplest for developers as they write their code.

 

There are some exceptions, though. If your game has a sharp demarcation between "code" and "data", it sometimes makes sense to put all the code in a few segments and then put the data in completely different segments. For example, having a general game level engine separate from large blobs of data for each game level where the game level data needs to be contiguous (ex: the data for one level can't be split into $6F00-$6FFF and $A000-$A2FF). This is mainly done by the dev to make organizing their game's layout easier for the developer. Further, if you start to use bankswapping to go beyond 42K, then the data (or code) you swap in and out is done 1 segment at a time. Note: I don't recall if IntyBASIC supports bankswapping.

Link to comment
Share on other sites

I really wish I could talk nanochess into integrating something like cart.mac into intybasic_prologue/epilogue. I know I posted a version awhile back.

 

My cart.mac header adds a set of macros that implement bookkeeping and cleaner ROM-segment switching. This makes it easier to partition your code among the various ROM segments. I am working with one programmer who is using it to great effect. The cart.mac header also provides a nice report at assembly time of how much space you're using in each ROM segment.

 

 

Typically, you'll just fill one segment before going on to the next one since that is the simplest for developers as they write their code.

 

Another exception to this might be if your "allocation granules" (data tables, functions) vary widely in size. If you have a large data table or function, and it's near the end of a ROM segment and doesn't fit, then merely bumping to the next segment will end up potentially leaving a big gap of unused ROM. Going back and backfilling that can be annoying if you don't have a good method for doing so.

  • Like 2
Link to comment
Share on other sites

Just to explicitly answer your questions ...

 

1- what is the easiest way to measure space still available within each segment, and % already used by my program and data?

 

As carlsson said, right now, the easiest way is to check out the "CFG" file generated by the assembler (the other half of the "BIN+CFG" package). Alternatively, you can account it by hand by reading the assembler generated listing file (if you enabled that in the command line).

 

2- should I generally aim to occupy 100% of one segment before starting to use the next? Or would leaving some buffer space available be advisable for whatever reason?

 

Generally, you should aim to fully fill a segment before moving to the next, but it is up to you. As intvnut suggested, leaving large gaps in multiple segments may cause you trouble down the line, as your program grows and needs additional space. Backfilling large data tables across splintered segments can be a big headache.

 

Technically, it does not matter. To the Intellivision, it is just a single memory space, and it will access it in the same way, at the same speed, wherever it is.

 

The only requirement is that contiguous data tables and subroutine procedures must exist wholly within a single segment. That is, a procedure or a large data array cannot straddle multiple segments.

 

 

3- between using one larger segment, or 2+ smaller ones, is it all the same? Any benefits to opting to use more, smaller segments?

 

Technically, it is all the same. The only benefit of using multiple smaller segments is to avoid the headache of having to allocate your program across them when one fills; provided you already know that your needs will be met by the target segments.

 

For example, in my program, I knew I had a lot of graphics and music data, so I designated the largest segment (ROMEG #2: $C040 - $FFFF = 16K) as a "data segment," and the default 8K segment at $5000 (ROMSEG #0) as the "code segment."

 

Thus, all code modules would be included in segment #0 and all data tables in segment #2. Then, for most of my development, I didn't have to worry about my program overrunning its ROM space.

 

All that said, keep in mind that this may get you up to speed quickly, but it doesn't absolve you from tweaking the allocation further, especially if your program grows larger than those two segments. In my case, I eventually ran out of data space in ROMSEG #2 and had to put the overflow in additional segments, eventually using them all.

 

4- does the choice of segment have any impact on performance, compatibility or ability to run the code on real hardware? I noticed the "avoid" notes on the table above, but don't quite get the practical meaning, or what needs to be done differently on IntyBASIC for instance.

 

There is no impact at all on performance, but compatibility and ability to run the code on real hardware is limited by the choice of cartridge PCB you choose to use. The segments described in the 42K.bas example program are the most commonly used by home-brewers because they are the parts of the memory map accessible to the CPU which were largely reserved for future expansion but left otherwise unused. (The Intellivision's memory map is truly a mess!)

 

Those segments are currently supported by the CC3, the Intellicart, the JLP cartridge family from LTO productions, and the LTO Flash!. Beyond those very well known ones, you'll have to discuss the supported ROM memory map and features of the individual platform you choose, with its manufacturer.

 

The warning about the "EXEC's ECS ROM probe" is related to the way the Intellivision Master Component looks for and detects the ECS. During the bootstrapping routine, the EXEC probes the $7000 memory block to see if there is something there. Typically, this block is undefined in the Master Component, but peripherals like the ECS occupy it. If the EXEC notices that there is something there, it will jump and execute it, essentially transferring control to the peripheral device.

 

We don't really want to obstruct that mechanism so what we do in home-brewed programs is to skip the very first block in that memory range ($7000 - $70FF) in our memory allocations, effectively starting the segment at $7100. That's it.

 

Therefore, when the Master Component probes that address, it will find nothing and continue as normal; or if the ECS is present, it will boot it as normal.

 

The second comment regarding the "game's ECS ROM mapping out code" is the other part of that ECS equation. So now that you skipped the EXEC peripheral probe memory block, if the user plugs in an ECS cartridge, it will boot up normally, and its own version of the EXEC will take a chunk of ROM designated specifically for itself.

 

However, as a home-brewer, you do not intend to use the ECS EXEC at all, and so you would prefer to make that memory space available to your program. There's a specific trick we do for this which involves hooking into the device probe address at $7000 and inserting a small bootstrap code that essentially bank-switches the ECS ROM to another bank, sending it out into the weeds, and freeing the memory range $4800 - $4FFF for your program.

 

Thus, that segment will only be available if your program performs that bootstrapping trick, which means that your target platform must support bank-switching. That's what the warning means: ROMSEG #5 is only available provided you account for your program mapping out the ECS ROM. That's it.

 

-dZ.

Edited by DZ-Jay
  • Like 1
Link to comment
Share on other sites

Thanks folks, as usual the amount of information shared is fantastic.

 

The cart.mac approach sounds interesting and hopefully will get incorporated as standard practice if that would be beneficial to all. I wonder how that segment utilization report looks like...

In the meanwhile, I guess I will start reading the .cfg and .lst as suggested.

 

I don't think I get entirely the whole bankswitching/bankswapping discusssion, but it's ok. I will get there over time.

 

The tips on how to make the most of each segment by taking into account "code" and "data" and "allocation granules" (that was a new one...) all made intuitive sense.

Link to comment
Share on other sites

I don't think I get entirely the whole bankswitching/bankswapping discusssion, but it's ok. I will get there over time.

No worries, it is not something critical to understand for using the segments.

 

In any case, you can think of the $7000 location as the "startup folder" of the Intellivision: when it boots up, it first looks there to see if there is anything, and if so, executes it; if not, it continues as normal.

 

So, unless you intend to override the bootstrap sequence, there is no need to put something there. In fact, you are better off leaving that address blank so as not to disturb the built-in initialization process.

 

This is why we start the segment in that range at $7100, so as to leave that "startup folder" empty and untouched.

 

That's the normal Master Component behaviour.

 

Now, if the user plugs in his ECS, it does put something at that address in order to take over control of the bootstrap sequence.

 

It also reserves a chunk of the memory space at the $4800 range for its own operating system in ROM. That's segment #5.

 

(Remember that these gaps in the memory space were left by the designers for future expansion, and the ECS is one such device.)

 

So, with the ECS plugged in, you have now one entire segment (#5) of memory unavailable to your program, because it's taken by the ECS EXEC software.

 

That's fine, and your program will run normally without problems. It just will not have that chunk of ROM address space for its own use.

 

The thing is, since your program doesn't use the ECS EXEC at all, that means that the ECS is essentially robbing your program of valuable memory real estate for absolutely no reason. :mad:

 

So we trick the Intellivision and the ECS using bank-switching. In the Mattel-style of bank-switching (which is supported by the the Intellicart, the CC3, and LTO technologies), each (I think) 2K 4K chunk of memory addresses can be subsequently split into 16 overlapping storage sections or "banks".

 

Essentially, each 2K 4K block of memory is treated as a matrix, or a stack of storage spaces, all layered on top of each other; in which the CPU can view only one bank at a time. They're all there, all 16 banks per chunk, but you instruct the CPU to point to a specific bank at a time. Think of it as multiple "pages" of the same storage space, mutually exclusive to each other, but sharing an address space.

 

So, here's the trick we do: we first install a short amount of code in that special $7000 "startup" address to intercept the bootstrap process. This code tells the CPU to view the memory range of segment #5 not as the default storage (which is occupied by the ECS ROM) but as a higher bank of read-write memory. In other words, we "bank-switch" that range to an unused page in the 16-page matrix.

 

The end result is that when your program uses that segment, it's actually using a different page which overlaps the ECS ROM. Since the CPU has been instructed to bank-switch to that page, that's all it sees, and therefore ignores the ECS ROM altogether.

 

Does this make sense?

 

Like I said, it's not crucial to understand any of this. Just be aware that for ROM segment 5 to be available, your target platform must support bank-switching, or must NOT accept the ECS plugged in. IntyBASIC already takes care of setting up the bootstrapping appropriately in your program if you enable ECS support.

 

dZ.

Edited by DZ-Jay
  • Like 1
Link to comment
Share on other sites

 

Minor item: the Intellivision's chunks are 4K in size. Other than that, DZ-Jay's description is spot on

You know, I originally wrote 4K, but then I looked it up in the Intellivision Wiki and it specified that the bank-switching was done in 2K increments.

 

Can someone confirm which is it?

Link to comment
Share on other sites

Essentially, each 2K block of memory is treated as a matrix, or a stack of storage spaces, all layered on top of each other; in which the CPU can view only one bank at a time. They're all there, all 16 banks per chunk, but you instruct the CPU to point to a specific bank at a time. Think of it as multiple "pages" of the same storage space, mutually exclusive to each other, but sharing an address space.

Ok... bear with me... maybe an example would help me further here.

Suppose I want to poke something on one of those 16 banks. How would that instruction differ from poking into the same address, but under a non-bankswitched design?

Link to comment
Share on other sites

Ok... bear with me... maybe an example would help me further here.

Suppose I want to poke something on one of those 16 banks. How would that instruction differ from poking into the same address, but under a non-bankswitched design?

Don't confuse the "banks" with the ROM segments. Those 16 banks are only available for bank switching. If you are not bank-switching, then it's essentially everything on bank zero.

 

Bank-switching essentially allows you to expand the available memory pool for your program by reusing the same memory space with different code or data.

 

It is important to note, however, that each page or bank is mutually exclusive on the same memory space. That is, the CPU will only see --for a particular memory range -- one bank at a time. You will have to switch to a different bank to let it see what's there.

 

Again, this is different from using multiple ROM segments. The ROM segments that started this discussion are basically the memory addresses of the Intellivision available to a program, which are scattered across multiple ranges because the entire memory map is shared with the on-board components, peripherals, and other devices, in a non-contiguous way.

 

dZ.

Link to comment
Share on other sites

Wait, I misunderstood your question. The poking of a memory address in a different bank than the default one (bank zero), is the same as it is on any other bank, with or without bank-switching.

 

The difference is that to access a different bank, you first need to "switch" by instructing the CPU to view a specific range of addresses on a different bank. This requires a special poke to a particular address.

 

Once that is done, the CPU will continue using the given bank when accessing that range of memory until you switch it again.

 

To start with, the CPU views everything as "bank zero," and you have a full memory space with 5 segments for a total of 42K. As you need to access different banks, you switch to make them visible as needed.

 

I hope this answers your question.

 

Bank-switching is powerful, but requires some planning since each bank for a given range exists on it's own mutually exclusive plane.

 

dZ.

Link to comment
Share on other sites

Got it. So its an extra poke to switch banks, then it becomes transparent. Thanks for all the information!

 

Yes, that's it. That's also the reason why the "trick" to ignore the ECS EXEC works: we switch banks on that range, and from then on, using ROM Segment #5 works as you'd expect, transparently, while the CPU is actually accessing a different bank.

 

Perhaps nanochess can provide additional information ways to do bank-switching from IntyBASIC. I do not know if there is some built-in support or a special pattern for it.

 

-dZ.

Link to comment
Share on other sites

The cart.mac approach sounds interesting and hopefully will get incorporated as standard practice if that would be beneficial to all. I wonder how that segment utilization report looks like...

 

 

It looks something like this. I don't have the latest cart.mac around at the moment, so this is from my own framework, but the output is very similar. The idea is to give you information on how much is used on each segment. I believe that the cart.mac output also includes the range of address for each segment, not just its number.

ROM USAGE:
    ==============================================================
        Segment        Type       Size       Used      Available
    ==============================================================
    ROM Seg #0        16-bit        8K         2537     5655 words
    ROM Seg #1        16-bit        8K            0     8192 words
    ROM Seg #2        16-bit       16K            0    16320 words
    ROM Seg #3        16-bit        4K            0     4096 words
    ROM Seg #4        16-bit        4K            0     3840 words
    ROM Seg #5        16-bit        2K           46     2002 words
    ==============================================================
             TOTAL:   16-bit       42K         2583    40105 words
    ==============================================================

Link to comment
Share on other sites

You know, I originally wrote 4K, but then I looked it up in the Intellivision Wiki and it specified that the bank-switching was done in 2K increments.

 

Can someone confirm which is it?

The Intellivision Wiki page outlines how "bank swapping" works via the Intellicart and the CC3, which uses a bank of 32 registers to map 2K ranges of the cartridges RAM/ROM addresses into the main CPU's address space (technically closer to virtual addressing than bank-swapping).

 

The Mattel-style bank swapping in the ECS (and I think the WSMLB game) uses a single register location at the end of each 4K boundary to implement the classic kind of bank swapping that was common for the era.

 

Note that the LTO supports both kinds of bank-swapping.

 

Hopefully that clears up things rather than muddied the waters.

Link to comment
Share on other sites

The Intellivision Wiki page outlines how "bank swapping" works via the Intellicart and the CC3, which uses a bank of 32 registers to map 2K ranges of the cartridges RAM/ROM addresses into the main CPU's address space (technically closer to virtual addressing than bank-swapping).

 

The Mattel-style bank swapping in the ECS (and I think the WSMLB game) uses a single register location at the end of each 4K boundary to implement the classic kind of bank swapping that was common for the era.

 

Note that the LTO supports both kinds of bank-swapping.

 

Hopefully that clears up things rather than muddied the waters.

 

Aha! Thanks for that, it makes sense. I guess that's what I get for skimming through the wiki without paying too much attention. :)

 

I'll update the post upstream.

 

-dZ.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...