Jump to content

Recommended Posts

Hi friends.

 

Is there any way to put 3 games made in IntyBASIC in a single cartridge? Putting the codes of the 3 games in a BAS file is not feasible and there is no way to compile it.

Maybe a menu that loads the binaries individually (assembly?), but I have no idea how (or if it is possible) to do it.

Ideas?

 

Thanks.

 

Sergio

Link to comment
https://forums.atariage.com/topic/375110-multiple-games-in-1-cartridge/
Share on other sites

4 minutes ago, Intellivision Brasil said:

Hi friends.

 

Is there any way to put 3 games made in IntyBASIC in a single cartridge? Putting the codes of the 3 games in a BAS file is not feasible and there is no way to compile it.

Maybe a menu that loads the binaries individually (assembly?), but I have no idea how (or if it is possible) to do it.

Ideas?

 

Thanks.

 

Sergio

 

Yes, this is what the TRON Anthology does, among others.  You can accomplish it using bank-switching.  Unfortunately, there is no direct support for this in IntyBASIC, so you will have to manipulate the ROM pages yourself from Assembly Language.

 

I will try to prepare some examples on how to do this, but the theory is that you overlay the ROM for each game by compiling/assembling them into their own page, reserving the default page for the menu.  When the user makes a selection from the menu, your program switches to the appropriate page and invokes the code there.

 

I believe @nanochess did this for the TRON games, so perhaps he could share some insight or even sample code.

 

     -dZ.

  • Like 1

I didn't made the menu for TRON, but the logic should be pretty simple actually. The JLP cartridge boots up in page 0, where the menu should be available.

 

For example, game 1 on page 1, game 2 on page 2, game 3 on page 3.

 

The menu jumps to an unused location, and it switchs in the correct pages for the selected game, and jumps back to $5000.

 

Already as1600 supports banked format, jzintv the emulation, and LTO-Flash the bank switching:

 

; Remember each bank is 4k words (compatible with JLP bank-switching)

ASM ORG $5000:0    ; This is modified in intybasic_prologue.asm

...your menu program....

ASM ORG $5000:1    ; Your first game.

ASM DECLE ...          ; Converted to DECLE (separated segments)

ASM ORG $D000:1    ; If your first game uses $d000

ASM DECLE ...

ASM ORG $5000:2    ; Your second game.

...

ASM ORG $5000:3    ; Your third game.

...

ASM ORG $F000:0    ; A segment not used by any game.
    ; Because the bank-switching is immediate

change_game_1:
    ASM MVII R0,#$5A51
	ASM MVO R0,$5FFF
    ASM MVII R0,#$DA51
	ASM MVO R0,$DFFF
    ASM B $5000

change_game_2:
    ASM MVII R0,#$5A52
	ASM MVO R0,$5FFF
    ASM B $5000

change_game_3:
    ASM MVII R0,#$5A53
	ASM MVO R0,$5FFF
    ASM B $5000

 

  • Thanks 1

I've done some tests, but I'm still stumbling over some small stone...


AS1600 indicated "syntax error" in the 1st line of "change_game_x" (ASM MVII R0,#$5A51). My knowledge of Assembly is about 1%, but I looked for something similar in the LST file and understood that I should invert the values (is this right?).
I changed the Prologue to "ORG $5000:0" and I was able to compile successfully in IntyBASIC and AS1600.
When I run it, the menu screen appears, but when I press 1 or 2, the screen simply closes.
Am I forgetting something? (maybe this line that I didn't understand: ASM DECLE ... ; Converted to DECLE).
Another question: what is the relationship between $5000 and $5A51 or $D000 and $DA51? Is there any logic or calculation to get these values?

 

This is my test code:

 

INCLUDE "constants.bas"
CLS: MODE 1
PRINT AT 0 COLOR 7,"MENU"

Menu:
    IF CONT=KEYPAD_1 THEN GOTO change_game_1
    IF CONT=KEYPAD_2 THEN GOTO change_game_2
GOTO Menu


game_1:
    ASM ORG $5000:1
    CLS: MODE 1: WAIT
    PRINT "1"
    DO: LOOP WHILE a=0


game_2:
    ASM ORG $5000:2
    CLS: MODE 1: WAIT
    PRINT "2"
    DO: LOOP WHILE a=0


ASM ORG $F000:0

change_game_1:
    ASM MVII #$5A51,R0
    ASM MVO R0,$5FFF
    ASM B $5000

change_game_2:
    ASM MVII #$5A52,R0
    ASM MVO R0,$5FFF
    ASM B $5000

 

14 hours ago, Intellivision Brasil said:

I've done some tests, but I'm still stumbling over some small stone...


AS1600 indicated "syntax error" in the 1st line of "change_game_x" (ASM MVII R0,#$5A51). My knowledge of Assembly is about 1%, but I looked for something similar in the LST file and understood that I should invert the values (is this right?).
I changed the Prologue to "ORG $5000:0" and I was able to compile successfully in IntyBASIC and AS1600.
When I run it, the menu screen appears, but when I press 1 or 2, the screen simply closes.
Am I forgetting something? (maybe this line that I didn't understand: ASM DECLE ... ; Converted to DECLE).
Another question: what is the relationship between $5000 and $5A51 or $D000 and $DA51? Is there any logic or calculation to get these values?

 

This is my test code:

 

INCLUDE "constants.bas"
CLS: MODE 1
PRINT AT 0 COLOR 7,"MENU"

Menu:
    IF CONT=KEYPAD_1 THEN GOTO change_game_1
    IF CONT=KEYPAD_2 THEN GOTO change_game_2
GOTO Menu


game_1:
    ASM ORG $5000:1
    CLS: MODE 1: WAIT
    PRINT "1"
    DO: LOOP WHILE a=0


game_2:
    ASM ORG $5000:2
    CLS: MODE 1: WAIT
    PRINT "2"
    DO: LOOP WHILE a=0


ASM ORG $F000:0

change_game_1:
    ASM MVII #$5A51,R0
    ASM MVO R0,$5FFF
    ASM B $5000

change_game_2:
    ASM MVII #$5A52,R0
    ASM MVO R0,$5FFF
    ASM B $5000

 

I think your inversion is correct. The first five in $5A51 indicates which 4k segment to bankswitch. The $xxx1 tells it to bankswitch with bank 1. It's always $xA5x, not sure why.

 

Is the issue here that each Intybasic game being bankswitched into $5xxx require its own Intybasic_prologue? Maybe try compiling and assembling each game and menu separately. Then carefully splice the object code together.

  • Like 1

I would put the individual game code in a higher bank, like the $C200, not in $5000 which is the default one.  That's where the cartridge header resides, so I do not know what shenanigans could happen ...

 

    dZ.

  • Like 1
16 hours ago, Intellivision Brasil said:

I've done some tests, but I'm still stumbling over some small stone...


AS1600 indicated "syntax error" in the 1st line of "change_game_x" (ASM MVII R0,#$5A51). My knowledge of Assembly is about 1%, but I looked for something similar in the LST file and understood that I should invert the values (is this right?).
I changed the Prologue to "ORG $5000:0" and I was able to compile successfully in IntyBASIC and AS1600.
When I run it, the menu screen appears, but when I press 1 or 2, the screen simply closes.
Am I forgetting something? (maybe this line that I didn't understand: ASM DECLE ... ; Converted to DECLE).
Another question: what is the relationship between $5000 and $5A51 or $D000 and $DA51? Is there any logic or calculation to get these values?

 

This is my test code:

 

INCLUDE "constants.bas"
CLS: MODE 1
PRINT AT 0 COLOR 7,"MENU"

Menu:
    IF CONT=KEYPAD_1 THEN GOTO change_game_1
    IF CONT=KEYPAD_2 THEN GOTO change_game_2
GOTO Menu


game_1:
    ASM ORG $5000:1
    CLS: MODE 1: WAIT
    PRINT "1"
    DO: LOOP WHILE a=0


game_2:
    ASM ORG $5000:2
    CLS: MODE 1: WAIT
    PRINT "2"
    DO: LOOP WHILE a=0


ASM ORG $F000:0

change_game_1:
    ASM MVII #$5A51,R0
    ASM MVO R0,$5FFF
    ASM B $5000

change_game_2:
    ASM MVII #$5A52,R0
    ASM MVO R0,$5FFF
    ASM B $5000

 

 

With this code, I get the following error when assembling:
 

				CALL CLRSCR
asm/foo.asm:3474: ERROR - Mixture of paged and unpaged ROM in same 4K range

 

That's probably because the prologue sets the ROM segment to be $5000 "unpaged" (i.e., without the ":0"), so your code at the top of the program is assembled there.  Then you start paging.

 

I switched to using "$C200" just to test and I got to assemble.  I will test the rest of the ROM program now and will report later.

 

      -dZ.

  • Like 1

UPDATE:  It works on my end with the above modifications.

 

The updated code I'm using is:

INCLUDE "constants.bas"
CLS: MODE 1
PRINT AT 0 COLOR 7,"MENU"

Menu:
    IF CONT=KEYPAD_1 THEN GOTO change_game_1
    IF CONT=KEYPAD_2 THEN GOTO change_game_2
GOTO Menu


game_1:
    ASM ORG $C200:1
    CLS: MODE 1: WAIT
    PRINT "1"
    DO: LOOP WHILE a=0


game_2:
    ASM ORG $C200:2
    CLS: MODE 1: WAIT
    PRINT "2"
    DO: LOOP WHILE a=0


ASM ORG $F000:0

change_game_1:
    ASM MVII #$CA51,R0
    ASM MVO R0,$CFFF
    ASM B $C200

change_game_2:
    ASM MVII #$CA52,R0
    ASM MVO R0,$CFFF
    ASM B $C200

 

Notice that the only change is that I am using a different segment ($C200) for the paged code.

 

I believe my previous assessment was correct:  the cartridge ROM header and the IntyBASIC preamble are assembled by default in the default un-paged segment ($5000).  When you try to switch pages later it, it causes a conflict with paged and un-paged segments.

 

Moreover, once you switch into a paged block of $5000, all that IntyBASIC stuff in the preamble is no longer accessible, so weird stuff may happen.  This includes the definition of the core block-copy assembly routines:

  • CLRSCR
  • FILLZERO
  • MEMSET

When your paged program calls CLS (which invokes the assembly CLRSCR procedure), it goes into the weeds.

 

    -dZ.

  • Like 2

So, what is the lesson to be learned?  Below is a bit of background analysis to guide anybody else trying to do the same thing.

 

 

 IntyBASIC Program Structure:

An IntyBASIC program is composed of three core pieces:

  • Prologue
  • Main
  • Epilogue

The compiler takes care of assembling the entire thing, but the crucial point to consider is that your program and all its code, libraries, and data (represented above by "Main") is injected in the middle of the ultimate ROM binary.

 

Typically you do not need to care about this at all, except when you want to switch ROM segments, paged or unpaged.

 

 

Intellivision ROM Segments:

Due to various historical reasons, the Intellivision memory map is splintered into different non-contiguous segments in the address space.  The address space available for programs to use freely depends on the peripherals attached to the console (e.g., for obvious reasons, when the ECS is used, the address space reserved by its ROM is not available to the program).

 

The most common memory map used for home-brewed games typically consists of 5 ROM segments, defined as follows:

  SEGMENT     RANGE           SIZE
     0        $5000 - $6FFF :  8K
     1        $A000 - $BFFF :  8K
     2        $C040 - $FFFF : 16K
     3        $2000 - $2FFF :  4K
     4        $7000 - $7FFF :  4K
     5        $4800 - $4FFF :  2K

 

This map accounts for hardware idiosyncrasies when some peripherals like the ECS or Intellivoice are present.  It is the "standard" memory map originally defined by Joe Zbiciak (@intvnut) in his "CART.MAC" macro library, which formed the basis of many Intellivision games before the advent of IntyBASIC.  Nonetheless, it is still a very useful map to use, and results in a total possible address space of 42K words without paging.

 

By default (and this goes all the way to the original design of the Intellivision hardware) all program code and data resides in Segment #0, at $5000, which affords a program size of 8K words.  If the program overflows this address space, it will clash with internal code or data used by the hardware, and likely result in a program crash.

 

In such cases, you can then split your program code and data to spread it across different segments in order to fit.  This is done by instructing the assembler to relocate its program counter (i.e., the location at which the next code will be assembled) using the ORG directive:

; My Assembly Program
  
    ; Switch to segment #2
    ORG  $C040

    ; Code continues ...

 

Note that this is completely different from bank-switching or paging ROM -- we are still operating within a single flat layout, we are just spreading our program code into different blocks across the full memory space.

 

 

ROM Segments In IntyBASIC:

The IntyBASIC compiler follows the standard Mattel model of constructing a program, dictated by the EXEC:  It instructs the assembler to place its code and data in the default ROM Segment #0 ($5000).  What this means in practice is that when you build your IntyBASIC program, by default, the entire thing is assembled within Segment #0 -- the prologue, main, and epilogue.

 

Unfortunately, IntyBASIC does not directly support ROM segments within the language, so left to its own devices, your entire program -- including the IntyBASIC runtime code, over which you have no control -- cannot be larger than 8K words.

 

Notwithstanding, you can still use the ORG directive to manually switch segments -- you will just need to prepend it with the "ASM" directive:

' My IntyBASIC Program
  
    ' Switch to segment #2
    ASM ORG  $C040

    ' Code continues ...

 

However, be forewarned that this relocates everything that follows into the new segment -- not only your program, but the epilogue code and data as well.  This may seem inconsequential at first, but it becomes important when you want to use multiple segments or paged ROM with bank-switching.

 

Below is a simple illustration of how a program may be allocated in memory, by default, and when switching segments.

 

Default program:

  • Segment #0 ($5000)
    • prologue
    • main
    • epilogue

 

Segmented program:

  • Segment #0 ($5000)
    • prologue
  • Segment #2 ($C040)
    • main
    • epilogue

 

The key thing to remember is that your program does not exist in isolation; it is actually wrapped by the code that serves as the IntyBASIC runtime.

 

My recommendation is to place all IntyBASIC code in their own segments, and use the rest for your code and data.  This affords you full control over the usage of the other segments, because you are in control of your own code and data.

 

NOTE:  Ideally, you would place the epilogue and prologue in the same segment (#0), but there is no easy way to track the current address position of the assembler from IntyBASIC so that you can have it continue assembling where it left off.  It can be done from assembly language, but that is out of scope for this document.

 

For example:

' My IntyBASIC Program

    ' Place the entire program in Segment #2
    ASM ORG  $C040

    ' ...
    ' Code & Data
    ' ...

    ' Switch to Segment #5 (2K)
    ASM ORG  $4800

' End of program

 

The above would result in a program layout like this:

  • Segment #0 ($5000)
    • prologue
  • Segment #2 ($C040)
    • main
  • Segment #5 ($4800)
    • epilogue

 

That takes the prologue and epilogue out of the way, so that your program can maximize the space available in all the other segments.

 

 

Intellivision Bank-Switching:

The Intellivision memory map can be further expanded by overlaying each ROM segment in banks, or pages, of 4K words each.  Each 4K block can be overlaid with up to 15 pages over the default, for a total of 16 pages.  The assembler supports paging by extending the ORG directive as such:

; My Assembly Program
  
    ; Switch to segment #2, page #1
    ORG  $C040:1

    ; Code continues ...

 

You can select a page from 0 to 15 ($0 .. $F).  The above instructs the assembler to assemble all the following code in page #1 of segment #0 ($C040).  That means that it coexists with code in all other pages.

 

You can think of paged ROM as a deck of cards:  they all exist at the same time, stacked up into a pile, but you can only see the one on top.  If you want to read another card, you just switch to that one by bringing it to the top, moving the original out of the way, and so on.  They are all still there, all the time, but only one is "active" at any particular time.

 

By default, when your program executes, all address segments are activated on page #0.  If you do not use bank-switching at all, then this is all the space there is, and your program does not care about any other pages.  If you use bank-switching and you want to access code or data in a different page, you must instruct the CPU to switch the segment of interest to the appropriate page.

 

The code to switch pages is:

   MVII #<seg>A5<page>, R0
   MVO  R0, <seg>FFF

 

In the above, "<seg>" represents the 4K boundary or base address of the segment, "<page>" represents the page number.  So, for segment #0, page #2 ($5000:2), the code would be:

    MVII #$5A52, R0
    MVO  R0, $5FFF

 

Likewise, for segment #2, page #1, the code would be:

    MVII #$CA52, R0
    MVO  R0, $CFFF

 

A more generalized way to do this -- and actually the way I handle it in my own P-Machinery framework, is like this:

' <base-address>:  The base address of a 4K ROM segment
' <page-number>:   The page number, from $0 to $F

segment_base = <base-address> And $F000
page         = $0A50 Or <page-number>

bank_addrs   = segment_base Or page
target_addrs = segment_base Or $0FFF

  Poke target_addrs, bank_addrs

 

In my framework, I encapsulate the entire thing in a macro, so that I can just indicate which ROM Segment and Page I want, and it will generate the code automatically.  Below is an example user function that does this in IntyBASIC:

' <seg> is the base address of the segment
' <page> is the page number (0 to 15)
DEF FN SwitchRomPage(seg, page) = Poke ((seg And $F000) Or $0FFF), ((seg And $F000) Or $0A50 Or page)


Finally, be aware that bank-switching requires additional metadata to instruct the cartridge firmware how to allocate the layered code across the memory space.  The "ROM" binary format does not support this, so you will have to use "BIN+CFG" format instead.

 

 

Bank-Switching In IntyBASIC:

There are two important points to keep in mind when using bank-switching in IntyBASIC:

  • The compiler does not set up any pages, assuming a single, flat layout.
  • The prologue is assembled before your program code, and the epilogue afterwards.

What this means in practice is that it does not matter how you switch your pages and segments throughout your program source, the default prologue will always be assembled in the default unpaged segment #0.

 

In an assembly language program this is not a problem, since your source code contains the entirety of the program.  In an IntyBASIC program, by contrast, you have no control over the prologue.  (Well, you could manually hack the prologue file, but I am assuming here a stock installation of IntyBASIC or the SDK.)

 

The assembler will not allow you to mix unpaged (flat) and paged (layered) addresses within the same segment.  Consequently, this makes segment #0 unavailable for bank-switching.

 

The other thing to consider when using bank-switching in your IntyBASIC program is that your code is trailed by the IntyBASIC epilogue.  Because the epilogue encapsulates the core of the IntyBASIC runtime, you need to ensure it is accessible to every part of your program, on every page.  The easiest way to do this is to ensure the epilogue is assembled in its own segment.

 

Below is an example that layers three blocks of code in Segment #2, over pages #0, #1, and #2.  Note that at the end, we instruct the assembler to place anything that follows (i.e., the epilogue) into Segment #1 ($A000).

' My IntyBASIC Program
' NOTE:  The compiler put the prologue in segment #0, unpaged

    ' Switch to segment #2, page #0
    ASM ORG  $C040:0

    ; Code continues ...

    ' Switch to segment #2, page #1
    ASM ORG  $C040:1

    ; Code continues ...

    ' Switch to segment #2, page #2
    ASM ORG  $C040:2

    ; Code continues ...

    ' Segregate the epilogue in its own 2K segment
    ASM ORG  $4800

' End of program

 

The above would result in the following ROM layout:

 

Bank-switched program:

  • Segment #0 ($5000 unpaged)
    • prologue
  • Segment #2, page #0 ($C040:0)
    • main a
  • Segment #2, page #1 ($C040:1)
    • main b
  • Segment #2, page #2 ($C040:2)
    • main c
  • Segment #1 ($4800 unpaged)
    • epilogue

 

By segregating the prologue and epilogue code into their own unpaged segments, you can guarantee that they will be visible from any part of your program, irrespective of which bank you are executing at any point.

 

I hope the above proves useful to someone.  Feedback is always welcome.

 

      -dZ.

 

Edited by DZ-Jay
Fixed typos and grammatical error. Also fixed the POKE statement in user function, which had arguments reversed.
  • Like 1
1 minute ago, Intellivision Brasil said:

Awesome!

I think these tips should be part of a new book "Advancing the advanced game programming for Intellivision".

 

LOL!

 

1 minute ago, Intellivision Brasil said:

Thanks a lot, dZ!

 

Sergio

 

It's my pleasure, Sergio.  I also posted it in its own topic here:

 

    -dZ.

  • Like 1

Well, I played around with the code a bit and added one more segment to just 1 game, but the screen closes.

I did several tests with different segments and pages, but I couldn't identify the problem.

 

 

INCLUDE "constants.bas"
CLS: MODE 1
PRINT AT 0 COLOR 7,"MENU"

Menu:
    IF CONT=KEYPAD_1 THEN GOTO change_game_1
GOTO Menu

game_1:
    ASM ORG $2100:1
    CLS: MODE 1: WAIT
    PRINT "1"
    ASM ORG $7100:1
    PRINT " 1B"
    DO: LOOP WHILE a=0

ASM ORG $F000:0

change_game_1:
    ASM MVII #$2A51,R0
    ASM MVO R0,$2FFF
    ASM MVII #$7A51,R0
    ASM MVO R0,$7FFF
    ASM B $2100

 

The "ASM ORG $2100:1 line" should be before the game_1 label, otherwise the game_1 label is in the old position.

 

Then after the PRINT "1" you should put a stop, because you are changing the address with "ASM ORG $7100:1" but you didn't stop execution, so it will ride into the sunset (but the jzintv message is something about weeds).

 

  • Thanks 1
13 hours ago, nanochess said:

The "ASM ORG $2100:1 line" should be before the game_1 label, otherwise the game_1 label is in the old position.

 

OMG! I can't believe I missed that.  I wonder why it was working on my end?

 

13 hours ago, nanochess said:

Then after the PRINT "1" you should put a stop, because you are changing the address with "ASM ORG $7100:1" but you didn't stop execution, so it will ride into the sunset (but the jzintv message is something about weeds).

 

 

Yes, the infamous "CPU in the weeds" message.  The emulator gives this message when the CPU jumps to a non-executable location.  The other one you could get is "CPU halted," which means that the CPU reached an address that had an invalid op-code, like for example, if it jumped to a data table instead of a procedure.

 

Both of them mean exactly that the machine has crashed in an irrecoverable way and you must reset the console.

 

     -dZ.

  • Like 2

Thanks again.


I did some more tests, including using 2 areas in the same game, and I got a satisfactory result.

I also used the function suggested by dZ, but I noticed a difference in the behavior of the commands in the 2 situations.
When I used (in ChangeGame1 and ChangeGame2) the ASSEMBLY commands directly, the menu correctly loaded the Game1 and Game2 screens (on pages 1 and 2 of area $2100). However, when I replace the ASSEMBLY commands with the function, the Game1 and Game2 routines only work when I manually change the page to Zero in the "ASM ORG" command (it seems that, even if I inform page 1 or 2 in the function call, the ASM B $2100 command always searches for page zero of that area).
In this case, should I use a command other than "B" or would it be necessary to include another parameter in this command?

 

(I include 2 PRINT at the bottom of the screen just to make sure that the function defined the values correctly - 2A51 and 2A52 respectively).

 

INCLUDE "constants.bas"
CLS: MODE 1

DEF FN ChangeORG(seg,page) = POKE((seg AND $F000) OR $0A50 OR page),((seg AND $F000) OR $0FFF)

PRINT AT 0 COLOR 6,"MENU"
PRINT AT 40 COLOR 7,"1 - GAME 1"
PRINT AT 60 COLOR 7,"2 - GAME 2"
PRINT AT 200,<>($2100 AND $F000) OR $0A50 OR 1
PRINT AT 220,<>($2100 AND $F000) OR $0A50 OR 2

Menu:
    IF CONT=KEYPAD_1 THEN GOTO ChangeGame1
    IF CONT=KEYPAD_2 THEN GOTO ChangeGame2
GOTO Menu

    
ASM ORG $2100:1
Game1:
    CLS: MODE 1: WAIT
    PRINT AT 0,"GAME 1"
    DO: LOOP WHILE a=0


ASM ORG $2100:2
Game2:
    CLS: MODE 1: WAIT
    PRINT AT 0,"GAME 2"
    DO: LOOP WHILE a=0


ASM ORG $F000:0

ChangeGame1:
    ASM MVII #$2A51,R0
    ASM MVO R0,$2FFF
    'ChangeORG($2100,1)
    ASM B $2100

ChangeGame2:
    ASM MVII #$2A52,R0
    ASM MVO R0,$2FFF
    'ChangeORG($2100,2)
    ASM B $2100

 

2 hours ago, Intellivision Brasil said:

Thanks again.


I did some more tests, including using 2 areas in the same game, and I got a satisfactory result.

I also used the function suggested by dZ, but I noticed a difference in the behavior of the commands in the 2 situations.
When I used (in ChangeGame1 and ChangeGame2) the ASSEMBLY commands directly, the menu correctly loaded the Game1 and Game2 screens (on pages 1 and 2 of area $2100). However, when I replace the ASSEMBLY commands with the function, the Game1 and Game2 routines only work when I manually change the page to Zero in the "ASM ORG" command (it seems that, even if I inform page 1 or 2 in the function call, the ASM B $2100 command always searches for page zero of that area).
In this case, should I use a command other than "B" or would it be necessary to include another parameter in this command?

 

(I include 2 PRINT at the bottom of the screen just to make sure that the function defined the values correctly - 2A51 and 2A52 respectively).

 

INCLUDE "constants.bas"
CLS: MODE 1

DEF FN ChangeORG(seg,page) = POKE((seg AND $F000) OR $0A50 OR page),((seg AND $F000) OR $0FFF)

PRINT AT 0 COLOR 6,"MENU"
PRINT AT 40 COLOR 7,"1 - GAME 1"
PRINT AT 60 COLOR 7,"2 - GAME 2"
PRINT AT 200,<>($2100 AND $F000) OR $0A50 OR 1
PRINT AT 220,<>($2100 AND $F000) OR $0A50 OR 2

Menu:
    IF CONT=KEYPAD_1 THEN GOTO ChangeGame1
    IF CONT=KEYPAD_2 THEN GOTO ChangeGame2
GOTO Menu

    
ASM ORG $2100:1
Game1:
    CLS: MODE 1: WAIT
    PRINT AT 0,"GAME 1"
    DO: LOOP WHILE a=0


ASM ORG $2100:2
Game2:
    CLS: MODE 1: WAIT
    PRINT AT 0,"GAME 2"
    DO: LOOP WHILE a=0


ASM ORG $F000:0

ChangeGame1:
    ASM MVII #$2A51,R0
    ASM MVO R0,$2FFF
    'ChangeORG($2100,1)
    ASM B $2100

ChangeGame2:
    ASM MVII #$2A52,R0
    ASM MVO R0,$2FFF
    'ChangeORG($2100,2)
    ASM B $2100

 


OMG!  I put it reverse, sorry about that.  I just realized.  It should be:

Poke ((seg And $F000) Or $0FFF), ((seg And $F000) Or $0A50 Or page)

 

I got my "POKE" arguments backwards.  :dunce:

 

DOH!

 

     -dZ.

 

P.S.  I've updated my earlier post with the correct information.  Sorry for that.

  • Thanks 1

Here I am again.


The test code worked very well, but... when I replaced it with the codes of 2 "real" games, some problems appeared.
I thought that using pages would make IntyBASIC "understand" that each game would be loaded separately, but I received dozens of error messages during compilation, such as "More than one ON FRAME GOSUB" or "8-bits variables exceeds available space".
I don't think that renaming all the variables of the second game to make them the same as those of the first game is the solution (there is a high chance of ruining the code), not to mention unifying "ON FRAME", which would be unfeasible because it would mix up the 2 games.
Is there any specific parameter to avoid these errors in IntyBASIC? (tests with "--jlp" and "FLASH" did not change the compilation result).

 

Thanks.

8 minutes ago, Intellivision Brasil said:

Here I am again.


The test code worked very well, but... when I replaced it with the codes of 2 "real" games, some problems appeared.
I thought that using pages would make IntyBASIC "understand" that each game would be loaded separately, but I received dozens of error messages during compilation, such as "More than one ON FRAME GOSUB" or "8-bits variables exceeds available space".

 

 

Oh, yes.  That's a problem.  Unfortunately, IntyBASIC does not have a concept of "multiple games."  It can support bank-switching, but all that gives you is extra overlapping ROM space.  The set of variables and other IntyBASIC resources are still the same for the entire program.

 

You will need to combine the individually compiled assembly of each one.  This is not trivial, but is possible.

 

8 minutes ago, Intellivision Brasil said:

I don't think that renaming all the variables of the second game to make them the same as those of the first game is the solution (there is a high chance of ruining the code), not to mention unifying "ON FRAME", which would be unfeasible because it would mix up the 2 games.

 

No that would not be sufficient.

 

8 minutes ago, Intellivision Brasil said:

Is there any specific parameter to avoid these errors in IntyBASIC? (tests with "--jlp" and "FLASH" did not change the compilation result).

 

Thanks.

 

I do not think you can do it that way.  The way that others have done it is to compile each game individually, then combine their generated assembly files them into a single one (either by including them or just copy+paste), with the "ORG" directives to store each one on its own bank.

 

The menu that invokes them all can be done in IntyBASIC as the "host" program.

 

     -dZ.

  • Like 1

Thank you all, friends!

I'm sure everything I've learned here will be very useful in future games (some plans for 2025...).


I had a question about how the game starts. I understood that, since IntyBASIC's PROLOGUE is loaded in the $5000 segment, I just need to insert my code to "follow the trail" in the same segment.

But why did I get these results?

 

PRINT "A"

It works.

 

PRINT "A"
ASM ORG $2100
PRINT "B"

It doesn't work (green screen).

 

DO
LOOP UNTIL FRAME>100

ASM ORG $2100
PRINT "A"

It doesn't work (screen closes).

 

I mean, I think in all 3 examples, my code starts at the $5000 segment, but why is the result different when I simply try to use another segment?

 

39 minutes ago, Intellivision Brasil said:

Thank you all, friends!

I'm sure everything I've learned here will be very useful in future games (some plans for 2025...).


I had a question about how the game starts. I understood that, since IntyBASIC's PROLOGUE is loaded in the $5000 segment, I just need to insert my code to "follow the trail" in the same segment.

But why did I get these results?

 

PRINT "A"

It works.

 

PRINT "A"
ASM ORG $2100
PRINT "B"

It doesn't work (green screen).

 

 

Remember that the ROM segments are not contiguous -- that is to say that they exist in different areas of the memory.  When the CPU is executing your code, line by line, how does it know that right after printing "A" -- which is in some address upwards from $5000 -- it needs to jump to address $2100 to continue its execution flow?

 

It won't know, of course, so it will continue with whatever information it finds in the address that directly follows in memory.

 

So, if your code assembled, say, at address $5500, and the end of your PRINT function call is located at, say, address $5600, then the CPU will look to execute whatever exists in address $5601, and I do mean whatever -- if what follows is not an instruction, be it some data table or perhaps even undefined random bits, the CPU will dutifully try to execute it, and -- BOOM! -- you just crashed your console.

 

39 minutes ago, Intellivision Brasil said:
DO
LOOP UNTIL FRAME>100

ASM ORG $2100
PRINT "A"

It doesn't work (screen closes).

 

I mean, I think in all 3 examples, my code starts at the $5000 segment, but why is the result different when I simply try to use another segment?

 


As suggested above, you need to clearly indicate to the CPU the path of flow you expect from it, otherwise, it just continues executing whatever it finds as it moves through the address space in numerical order.

 

Think about something like the following:

PRINT "A"

Print_B: PROCEDURE
   PRINT "B"
   RETURN
END


What do you think will happen?  After printing "A," the CPU will continue with what it finds next, which happens to be a procedure, but it has no concept of this at all -- to the CPU, that's just instructions to print "B" and then return to caller.  But wait!  There is no caller!!!  That is true, so your program just jumped into the weeds and probably crashed.

 

You have to think in similar ways when you are using additional segments:  If you use the other segments just for procedures or data tables, and keep all your actual program in a loop within the main $5000 segment; then you will be fine, because the tables are read by reference from your code by their labels, as mere date in memory; and the procedures will be invoked via a GOSUB statement, so the CPU will jump to the appropriate address and the return to whence it came.

 

What you do not want to do is split your code flow, either your main loop, a procedure, or some other atomic block of code, into multiple segments.  That being said, if you ever feel the need to do so, then the way to do it would be via a GOTO to either the target address, or a label pointing to it

PRINT "A" ASM
GOTO Continue

ORG $2100
Continue:
PRINT "B"


However, I hope the descriptions above make it clear just how incredibly weird that looks. ;)

 

   dZ.

  • Like 3

No worries.  Here is a bit more context.

 

Consider that the memory map is one huge block, organized into 16-bit words, each one with an address.  The addresses are arranged in numerical order, starting at $0000 and ending at $FFFF.  That entire range is fragmented into individual blocks reserved for hardware devices, the EXEC ROM, peripherals, etc.  

 

All that means is that the memory available to store a program is scattered across many separate blocks all over the map.  Back in the Mattel days, this wasn't a problem; the $5000 - $6FFF range was 8K and that was the maximum that cartridges supported, and a 4K or 8K program could fit comfortably in there without spilling over.

 

Our home brewed programs are larger now, so we have to find other blocks of memory to store the overflow.  That is where the additional segments come in.  Those blocks are just the memory addresses that Mattel intended to use for expansion, but never got around to using, so they are free for us now. :)
 

    dZ.

  • Like 1

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...