Not long after the previous article, someone asked me about making the ROM cartridge loading technique work on a BASIC program written specifically for the CoCo3. Although the issue hadn't occurred to me before that, I immediately suspected that the existing technique would not work in that situation. A little experimentation revealed that to be true...
What's The Deal?
I knew that the CoCo3 had a more complicated boot process than its predecessors. The CoCo3 boot-up involves copying code from ROM to RAM, patching some existing BASIC routines, and adding some new ones designed to take advantage of CoCo3 hardware enhancements. I also knew that to preserve some compatibility with the original CoCo and the CoCo2, ROM cartridge programs were given an environment that looked much more like those earlier machines. In other words, the ROM cartridge environment doesn't get access to the CoCo3 extensions to BASIC. So, a BASIC program written specifically for the CoCo3 won't work with the ROM cartridge environment as our existing technique would try to use it.
Finding The Solution
Understanding the CoCo3 boot process requires some understanding of the actual code in the CoCo3 ROMs. This could be done the hard way, manipulating the CoCo3 registers to expose the actual ROM contents, dumping those contents, then analyzing them with f9dasm or some other disassembler. In this case, however, that work has already been done for us in the Unravelled Series. Referring to Super Extended Basic Unravelled II gives a good disassembly of the code in question and some useful commentary as well.
I had never had cause to look so closely at the CoCo3 boot sequence. I was confused and believed that the Color BASIC and Extended BASIC ROMs in the CoCo3 were essentially untouched from the CoCo and CoCo2 equivalents, and that the CoCo3 ROM got control in a chained fashion similar to how Disk Extended BASIC gets control from Extended Color BASIC. This misunderstanding led to some initial puzzlement until I was able to clear-up the situation in my mind.
In reality, there are some changes to the original Color BASIC and Extended BASIC ROMs that are actually burned into the hardware, as detailed by Super Extended Basic Unravelled II. Among these changes, the startup code is changed to transfer control to the CoCo3 ROM early in the boot process. This code then copies the BASIC ROM contents to RAM and patches the code as required for the CoCo3. After this is done, the patched ROM code (now in RAM) is entered and BASIC initializes itself. Late in this process the cartridge autostart sequence is enabled. When the cartridge autostart is detected, then the CoCo3 re-enables the ROMs and jumps to the code at the start of the cartridge.
The point here is easy to miss -- the CoCo3 has already initialized itself, including copying its patched BASIC code to RAM. But then it purposefully turns-off access to the RAM with the patched code in favor of accessing the ROM with the more-or-less original CoCo2 BASIC. It's no wonder that CoCo3 BASIC programs won't run!
As an experiment, I hacked-together a simple BASIC-in-a-cart program that would run and then exit. From there I verified that by default, I could not access CoCo3-specific BASIC commands (e.g. HSCREEN). But if I executed the POKE to turn-off the ROMs and re-enable the RAM first, I could access the CoCo3 BASIC commands with no problem!
New Loader
So now we must consider modifications to the "loader" program. My first thought was to add the equivalent of the required POKE as an assembly language instruction in the program. But that won't work -- since the loader is executing from ROM, swapping-out the ROM for whatever is in RAM at the same address after the swap will almost certainly cause the CPU to do something weird, foolish, or just wrong. What we really need is a way to execute the POKE after the "loader" has already executed, but before the BASIC program is RUN...
BASADR EQU 7680
ORG 49152
LOADER LDA #85 SET WARM RESET
STA 113
LDD #32960 SET EXTENDED BASIC RESET VECTOR
STD 114
JSR 47452 SET UP PARAMETERS FOR BASIC
LDA #53 RESTORE INTERRUPTS
STA 65283 THAT ARE
LDA #5 DISABLED ON
STA 65315 CARTRIDGE AUTO START
BASIN LDX #BASLOD
LDY #BASADR THIS IS LOAD ADDRESS OF BASIC (S)
STY 25 SAVE IT IN BASIC START
INC 26 CORRECT THE ACTUAL START VALUE
TNSFER CLRB SET END COUNTER TO ZERO
TNSFR2 LDA ,X+ GET FIRST BYTE FROM ROMPAK
STA ,Y+ TRANSFER BYTE TO BASIC RAM
BNE TNSFR2 NON ZERO DATA, KEEP TRANSFERRING
ENDCHK INCB ZERO DATA DETECTED, INCREMENT COUNTER
CMPB #3 IS THERE 3 CONSECUTIVE ZERO'S?
BEQ LODDON IF YES, STOP TRANSFER
LDA ,X+ LOAD NEXT ROMPAK BYTE AFTER ZERO
STA ,Y+ TRANSFER BYTE TO BASIC RAM
BNE TNSFER NON ZERO DATA, RETURN TO MAIN LOOP
BRA ENDCHK ZERO DATA, INC COUNTER, STAY IN LOOP
LODDON STY 27 SAVE END ADDRESS FOR BASIC
STY 29 SAVE VAR START AT END OF TEXT
STY 31 SAVE ARRAY START AT END OF VAR/TEXT
AUTRUN LDX #733 BASIC LINE INPUT BUFFER
LDD #$504f LOAD CHARS "PO"
STD ,x++
LDD #$4b45 LOAD CHARS "KE"
STD ,x++
LDD #$2648 LOAD CHARS "&H"
STD ,x++
LDD #$4646 LOAD CHARS "FF"
STD ,x++
LDD #$4446 LOAD CHARS "DF"
STD ,x++
LDD #$2c30 LOAD CHARS ",0"
STD ,x++
LDA #$3a LOAD CHAR ":"
STA ,x+
LDD #21077 LOAD LETTERS "RU"
STD ,X++
LDD #19968 LOAD "N" AND END
STD ,X++
LDB #17 INDICATE 17 CHARACTERS
CLRA
STA 112 SET CONSOLE IN BUFFER FLAG
LDX #732 POINT TO LINE INPUT BUFFER
JMP 44159 START MAIN BASIC LOOP
BASLOD EQU * BASIC PROGRAM DATA STARTS HERE
FCB $00
END
Wrap-Up
As you can see, the solution is a bit clever -- just add the POKE to the same line of BASIC input that causes the program to RUN! Aside from that, the process is the same as described in the earlier blog post -- enter the BASIC program, save the in-memory image of the program to emulated cassette, extract the file, append it to the assembled "loader" program above, burn it to an EPROM, and use that in a cartridge. I used Robert Gault's PALETTE.BAS program as a test subject for the technique described, and it works perfectly for me.
For now, I'll leave the actual BASIC conversion work as an exercise for the reader. Perhaps in the future I will seek permission to post converted versions of PALETTE.BAS or some other BASIC programs, along with pre-assembled "loader" images both for the CoCo/CoCo2 and the CoCo3. If you want to see that, then for now you'll just have to stay tuned...
Tuesday, August 15, 2017
Friday, August 4, 2017
Color BASIC Program On A Cartridge?
Wow! It's been a while since I posted on this blog. I guess the podcast has been keeping me busy, among other things. Anyway, I'm back for now so let's talk about something cool and retro... :-)
Games On Cartridge
Those of you familiar with my Fahrfall project will recall that I ultimately packaged Fahrfall as a ROM cartridge, one of the available mechanisms for distributing software for the Tandy Color Computer (aka CoCo). I even went so far as to produce silicon molds and to cast cartridge cases made of plastic resin for that project. For me, no other means of distributing software seems as cool and "retro" as using a ROM cartridge, and I am very pleased with the results of that project. Nevertheless, producing resin cast plastic cartridges was not without its problems.
3D-printed plastic projects are 'all the rage' these days, but 3D-printing still has plenty of its own problems: filament is expensive, surfaces usually have visible texture marks, the layered construction often leads to "delamination" and other structural problems, and printing times are so long that bulk production runs become at least impractical. With these issues in mind, some time ago I partnered with Boisy Pitre and Mike Rowen to produce injection-molded cartridge cases for the CoCo. These cases are beautiful, strong, and despite considerable setup costs fairly cheap in large quantities.
Having produced a large number of cartridge cases, my partners and I have explored a variety of ways to market them to the community. My preferred approach is to wrap cases around software releases, and I have done so with Fahrfall and a number of other titles both from myself and from others. To date all of these titles have been written either in assembly language or C, resulting in machine language programs running directly from ROM on the CoCo. But many CoCo authors are skilled in Color BASIC (Extended or otherwise).
BASIC Games Too!
Ultimately, ROM is just another storage medium. So, it should come as no surprise that a BASIC program could be distributed on a ROM cartridge as well. No version of Color BASIC supports loading programs from ROM, so a little trickery is required to make the CoCo aware of the program stored in ROM. So, what has to be done?
Long ago there was a web page called "Paul D's CoCo Pages". This was nearly lost after Yahoo bought GeoCities, but fortunately an archive exists. The page in question describes a technique for loading a BASIC program from a ROM cartridge. Boisy and I had also sought advice from Robert Gault on this topic, and he proposed a technique that was substantially the same. I had some trouble replicating the procedure described on the "Paul D" page, but below I'll summarize what worked for me.
Extraction Procedure
Start with loading the BASIC program on the CoCo. Then you will need to determine the start and end of the RAM used to store the actual BASIC program itself in memory:
Now you will need to save that as a memory image (i.e. not as a normal BASIC program). Here I will presume that one is using an emulator and capturing the CSAVEM to a file called cartprog.cas:
CSAVEM "CARTPROG",S,E,0
Now on the machine running the CoCo emulator, we will use the cecb comand from the Toolshed project to extract the binary image file of the CoCo's memory:
cecb copy -2 -b cartprog.cas,CARTPROG cartprog
Building the ROM Image
The Color BASIC ROM will jump to the beginning of cartridge ROM as part of the machine's initialization. We need to put some machine code there to do the job of loading the BASIC program. The assembly language program I am using for this task is shown below. It is based upon the loader program shown on the original "Paul D" page, but with some minor changes to suit my way of working:
BASADR EQU 7680
ORG 49152
LOADER LDA #85 SET WARM RESET
STA 113
LDD #32960 SET EXTENDED BASIC RESET VECTOR
STD 114
JSR 47452 SET UP PARAMETERS FOR BASIC
LDA #53 RESTORE INTERRUPTS
STA 65283 THAT ARE
LDA #52 DISABLED ON
STA 65315 CARTRIDGE AUTO START
BASIN LDX #BASLOD
LDY #BASADR THIS IS LOAD ADDRESS OF BASIC (S)
STY 25 SAVE IT IN BASIC START
INC 26 CORRECT THE ACTUAL START VALUE
TNSFER CLRB SET END COUNTER TO ZERO
TNSFR2 LDA ,X+ GET FIRST BYTE FROM ROMPAK
STA ,Y+ TRANSFER BYTE TO BASIC RAM
BNE TNSFR2 NON ZERO DATA, KEEP TRANSFERRING
ENDCHK INCB ZERO DATA DETECTED, INCREMENT COUNTER
CMPB #3 IS THERE 3 CONSECUTIVE ZERO'S?
BEQ LODDON IF YES, STOP TRANSFER
LDA ,X+ LOAD NEXT ROMPAK BYTE AFTER ZERO
STA ,Y+ TRANSFER BYTE TO BASIC RAM
BNE TNSFER NON ZERO DATA, RETURN TO MAIN LOOP
BRA ENDCHK ZERO DATA, INC COUNTER, STAY IN LOOP
LODDON STY 27 SAVE END ADDRESS FOR BASIC
STY 29 SAVE VAR START AT END OF TEXT
STY 31 SAVE ARRAY START AT END OF VAR/TEXT
AUTRUN LDX #733 BASIC LINE INPUT BUFFER
LDD #21077 LOAD LETTERS "RU"
STD ,X++
LDD #19968 LOAD "N" AND END
STD ,X++
LDB #4 INDICATE 4 CHARACTERS
CLRA
STA 112 SET CONSOLE IN BUFFER FLAG
LDX #732 POINT TO LINE INPUT BUFFER
JMP 44159 START MAIN BASIC LOOP
BASLOD EQU * BASIC PROGRAM DATA STARTS HERE
FCB $00
END
This program is assembled and a "raw" format image is produced, called "loader"...
lwasm -9 -l -f raw -o loader loader.asm
The BASIC program image "cartprog" is concatenated with "loader" to produce the data for programming into the cartridge ROM:
cat loader cartprog > eprom.dat # DOS/Windows command differs
How Does It Work?
Once the auto-start cartridge is detected, Color BASIC transfers control to the loader program listed above. The "loader" program performs some simple initializations for the Color BASIC interpreter before proceeding to copy the BASIC program to the CoCo's program memory. This includes setting the start address of the BASIC program.
The "loader" program starts reading from the location labeled BASLOD. There it will read the value zero as shown, and it will write it to the location defined by BASADR. After the first zero, "loader" continues with the next location which will correspond to the first value from "cartprog". The sequence of reading and writing byte-by-byte continues until 3 consecutive zeroes are read. This corresponds to the end of the BASIC program. (The original "Paul D" version looks for 4 zeroes, but AFAICT that is incorrect.) At this point, the end address of the BASIC program (and the end addresses for BASIC variables and arrays) is set for the Color BASIC interpreter.
Finally, the "loader" program manipulates the keyboard input buffer used by Color BASIC in order to force a "RUN" command to be executed immediately. Then, control is transferred to the Color BASIC interpreter and the loaded BASIC program is now executed.
So, there it is -- a BASIC program loaded from a ROM cartridge on a Tandy Color Computer. Now Color BASIC programmers can publish their programs on ROM cartridge, with all of the prestige, durability, and collector value that entails. That includes ROM cartridges with extra hardware, like my CoCo Games Master Cartridge. But that is another story -- you'll just have to stay tuned!
Games On Cartridge
Those of you familiar with my Fahrfall project will recall that I ultimately packaged Fahrfall as a ROM cartridge, one of the available mechanisms for distributing software for the Tandy Color Computer (aka CoCo). I even went so far as to produce silicon molds and to cast cartridge cases made of plastic resin for that project. For me, no other means of distributing software seems as cool and "retro" as using a ROM cartridge, and I am very pleased with the results of that project. Nevertheless, producing resin cast plastic cartridges was not without its problems.
3D-printed plastic projects are 'all the rage' these days, but 3D-printing still has plenty of its own problems: filament is expensive, surfaces usually have visible texture marks, the layered construction often leads to "delamination" and other structural problems, and printing times are so long that bulk production runs become at least impractical. With these issues in mind, some time ago I partnered with Boisy Pitre and Mike Rowen to produce injection-molded cartridge cases for the CoCo. These cases are beautiful, strong, and despite considerable setup costs fairly cheap in large quantities.
Having produced a large number of cartridge cases, my partners and I have explored a variety of ways to market them to the community. My preferred approach is to wrap cases around software releases, and I have done so with Fahrfall and a number of other titles both from myself and from others. To date all of these titles have been written either in assembly language or C, resulting in machine language programs running directly from ROM on the CoCo. But many CoCo authors are skilled in Color BASIC (Extended or otherwise).
BASIC Games Too!
Ultimately, ROM is just another storage medium. So, it should come as no surprise that a BASIC program could be distributed on a ROM cartridge as well. No version of Color BASIC supports loading programs from ROM, so a little trickery is required to make the CoCo aware of the program stored in ROM. So, what has to be done?
Long ago there was a web page called "Paul D's CoCo Pages". This was nearly lost after Yahoo bought GeoCities, but fortunately an archive exists. The page in question describes a technique for loading a BASIC program from a ROM cartridge. Boisy and I had also sought advice from Robert Gault on this topic, and he proposed a technique that was substantially the same. I had some trouble replicating the procedure described on the "Paul D" page, but below I'll summarize what worked for me.
Extraction Procedure
Start with loading the BASIC program on the CoCo. Then you will need to determine the start and end of the RAM used to store the actual BASIC program itself in memory:
A=PEEK(25)
B=PEEK(26)
C=PEEK(27)
D=PEEK(28)
S=256*A+B
E=256*C+D-1
Now you will need to save that as a memory image (i.e. not as a normal BASIC program). Here I will presume that one is using an emulator and capturing the CSAVEM to a file called cartprog.cas:
CSAVEM "CARTPROG",S,E,0
Now on the machine running the CoCo emulator, we will use the cecb comand from the Toolshed project to extract the binary image file of the CoCo's memory:
cecb copy -2 -b cartprog.cas,CARTPROG cartprog
Building the ROM Image
The Color BASIC ROM will jump to the beginning of cartridge ROM as part of the machine's initialization. We need to put some machine code there to do the job of loading the BASIC program. The assembly language program I am using for this task is shown below. It is based upon the loader program shown on the original "Paul D" page, but with some minor changes to suit my way of working:
BASADR EQU 7680
ORG 49152
LOADER LDA #85 SET WARM RESET
STA 113
LDD #32960 SET EXTENDED BASIC RESET VECTOR
STD 114
JSR 47452 SET UP PARAMETERS FOR BASIC
LDA #53 RESTORE INTERRUPTS
STA 65283 THAT ARE
LDA #52 DISABLED ON
STA 65315 CARTRIDGE AUTO START
BASIN LDX #BASLOD
LDY #BASADR THIS IS LOAD ADDRESS OF BASIC (S)
STY 25 SAVE IT IN BASIC START
INC 26 CORRECT THE ACTUAL START VALUE
TNSFER CLRB SET END COUNTER TO ZERO
TNSFR2 LDA ,X+ GET FIRST BYTE FROM ROMPAK
STA ,Y+ TRANSFER BYTE TO BASIC RAM
BNE TNSFR2 NON ZERO DATA, KEEP TRANSFERRING
ENDCHK INCB ZERO DATA DETECTED, INCREMENT COUNTER
CMPB #3 IS THERE 3 CONSECUTIVE ZERO'S?
BEQ LODDON IF YES, STOP TRANSFER
LDA ,X+ LOAD NEXT ROMPAK BYTE AFTER ZERO
STA ,Y+ TRANSFER BYTE TO BASIC RAM
BNE TNSFER NON ZERO DATA, RETURN TO MAIN LOOP
BRA ENDCHK ZERO DATA, INC COUNTER, STAY IN LOOP
LODDON STY 27 SAVE END ADDRESS FOR BASIC
STY 29 SAVE VAR START AT END OF TEXT
STY 31 SAVE ARRAY START AT END OF VAR/TEXT
AUTRUN LDX #733 BASIC LINE INPUT BUFFER
LDD #21077 LOAD LETTERS "RU"
STD ,X++
LDD #19968 LOAD "N" AND END
STD ,X++
LDB #4 INDICATE 4 CHARACTERS
CLRA
STA 112 SET CONSOLE IN BUFFER FLAG
LDX #732 POINT TO LINE INPUT BUFFER
JMP 44159 START MAIN BASIC LOOP
BASLOD EQU * BASIC PROGRAM DATA STARTS HERE
FCB $00
END
This program is assembled and a "raw" format image is produced, called "loader"...
lwasm -9 -l -f raw -o loader loader.asm
The BASIC program image "cartprog" is concatenated with "loader" to produce the data for programming into the cartridge ROM:
cat loader cartprog > eprom.dat # DOS/Windows command differs
How Does It Work?
Once the auto-start cartridge is detected, Color BASIC transfers control to the loader program listed above. The "loader" program performs some simple initializations for the Color BASIC interpreter before proceeding to copy the BASIC program to the CoCo's program memory. This includes setting the start address of the BASIC program.
The "loader" program starts reading from the location labeled BASLOD. There it will read the value zero as shown, and it will write it to the location defined by BASADR. After the first zero, "loader" continues with the next location which will correspond to the first value from "cartprog". The sequence of reading and writing byte-by-byte continues until 3 consecutive zeroes are read. This corresponds to the end of the BASIC program. (The original "Paul D" version looks for 4 zeroes, but AFAICT that is incorrect.) At this point, the end address of the BASIC program (and the end addresses for BASIC variables and arrays) is set for the Color BASIC interpreter.
Finally, the "loader" program manipulates the keyboard input buffer used by Color BASIC in order to force a "RUN" command to be executed immediately. Then, control is transferred to the Color BASIC interpreter and the loaded BASIC program is now executed.
So, there it is -- a BASIC program loaded from a ROM cartridge on a Tandy Color Computer. Now Color BASIC programmers can publish their programs on ROM cartridge, with all of the prestige, durability, and collector value that entails. That includes ROM cartridges with extra hardware, like my CoCo Games Master Cartridge. But that is another story -- you'll just have to stay tuned!