Category Archives: Color BASIC

Color BASIC Attract Screen – part 6

See also: part 1, part 2, part 3, part 4, unrelated, part 5 and part 6.

War by James Garon

Just as I thought I had reached the conclusion to my epic masterpiece about classic CoCo game startup screens, Robert Gault made this post to the Color Computer mailing list:

Robert Gault robert.gault at att.net
Sat Jul 2 09:56:21 EDT 2022

An author of games for Tandy, James Garon, put some lines in the Basic game WAR which is on colorcomputerarchive.com . The code is in lines 60000 and up which can’t be LISTed with a CoCo3 but can be read with a CoCo2.  The lines in question contain PRINT commands which when listed with a CoCo2, look like the data inside the quotes have been converted into Basic commands. You can also see this if you use imgtool or wimgtool to extract the program from the .dsk image.  These lines generate PMODE graphics for the title screen.

Do anyone have a clue as to how these Basic lines work?

– Robert Gault, via CoColist

The game in question is available in tape or disk format from the Color Computer Archive:

https://colorcomputerarchive.com/search?q=War+%28Tandy%29&ww=1

And, you can even click the “Play Now” button and see it run right in your web browser. Click below and take a look at the title screen:

https://colorcomputerarchive.com/test/xroar-online/?machine=cocous&basic=RUN%22WAR%22%5cr&cart=rsdos&disk0=/unzip%3Ffile%3DDisks/Games/War%20(Tandy).zip/WAR.DSK

War by James Garon (title screen)

This title screen uses the CoCo’s lesser-known screen color, and has those iconic rotating color blocks.

And, it’s in BASIC! Well, almost. It contains an assembly language routine that rotates those colors, and it does it the same way I figured out in this article series. I am just thirty years too late with my solution.

Line 60000

60000 CLS:A$=STRING$(28,32):PRINT"RESTORERESTOREMOTORMOTOR^^SCREENSCREENDRIVEDRIVEDSKI$DSKI$!!!RESTORERESTOREMOTORMOTOR^^SCREENSCREENDRIVEDRIVEDSKI$DSKI$!!!";

As Robert mentioned, there’s some weirdness in the program starting at line 60000. When you LIST it, you get a rather odd output full of BASIC keywords and such:

WAR.BAS line 60000

The first bit looks okay… CLS to clear the screen, then A$ is created to be 28 spaces — CHR$(32). But that PRINT looks a bit … weird.

I’ve seen this trick before, and even mentioned it recently when talking about typing in an un-typable BAISC pogram. The idea is you can create a program that contains something in a string or PRINT statement, like:

PRINT "1234567890"

…and then you alter the bytes between those quotes somehow, such as locating them and using POKE to change them. Let’s figure out an easy way to do this.

First, I’ll start with a print statement that has characters I can look for later, such as the asterisk. I like that because it is ASCII 42. If you don’t know why I like forty-two, you must be new here. This wikipedia page will give you the details…

100 PRINT "**********"

Now we need to alter those bytes and change them to something we couldn’t normally type, such as graphics blocks (characters 128-255). To do this, we can use some code that scans from the start of the BASIC program to the end, and tries to replace the 42s with something else.

This is dangerous, since 42 could easily appear in a program as part of a keyword token or line number or other data. But, I know that a quote is CHR$(34), so I could specifically look for a series of 42s that is between two 34s.

Numbers.

So many numbers.

In Color BASIC, memory locations 25 and 26 contains the start of the BASIC program in memory. Locations 27 and 28 is the end of the program. Code to scan that range of memory looking for a quote byte (34) that has an asterisks byte (42) after it might look like this:

0 ' CODEHACK.bas
10 S=PEEK(25)*256+PEEK(26)
20 E=PEEK(27)*256+PEEK(28)
30 L=S
40 V=PEEK(L)
50 IF V=34 THEN IF PEEK(L+1)=42 THEN 80
60 L=L+1:IF L<E THEN 40
70 END
...

We could then add code to change all the 42s encountered up until the next quote (34).

80 L=L+1:IF PEEK(L)=34 THEN END
90  POKE L,128:GOTO 80
100 PRINT "**********"

Line 80 moves to the next byte and will end if that byte is a quote.

In line 90, it uses POKE to change the character to a 128 — a black block. It continues to do this until it finds the closing quote.

If you load this program and LIST it, it looks like the code shown. But after you RUN, listing it reveals a garbled PRINT statement similar to the WAR line 60000. But, if you run that garbled PRINT statement, you get the output of black blocks:

CoCo code hack!

As you can see, line 100 changes ten asterisks to the keyword FOR ten times. I am guessing that the numeric token for “FOR” is 128.

Color BASIC Unravelled

…and my guess was correct! According to Color BASIC Unravelled, the token for FOR is hex 80 — which is 128. Perfect. So the BASIC “LIST” routine is dump, and tries to detokenize things even if they are surrounded by quotes. Interesting.

At this point, if you were to EDIT that line, it would detokenize it to be…

100 PRINT "FORFORFORFORFORFORFORFORFORFOR"

…and if you saved your edit, you’d now have a line that would print exactly that, no longer ten black blocks for the word FOR ten times as if you’d typed them all in.

This makes these changes uneditable. BUT, once the modification code has been ran, you can delete it, then SAVE/CSAVE the modified program. When it loads back up, it will have those changes.

In the case of WAR line 60000, it’s a PRINT that shows a series of color blocks used for the top of the attract screen.

Here is the garbled output of lines 60000 on in the WAR.BAS program:

60000 CLS:A$=STRING$(28,32):PRINT"RESTORERESTOREMOTORMOTOR^^SCREENSCREENDRIVEDRIVEDSKI$DSKI$!!!RESTORERESTOREMOTORMOTOR^^SCREENSCREENDRIVEDRIVEDSKI$DSKI$!!!";
60005 FORI=1TO8:GOSUB60028:NEXT:FORI=1TO6:GOSUB60028:NEXT
60010                             PRINT"MOTORMOTORRESTORERESTORE!!!DSKI$DSKI$DRIVEDRIVESCREENSCREEN^^MOTORMOTORRESTORERESTORE!!!DSKI$DSKI$DRIVEDRIVESCREENSCREEN^";
60020 POKE1535,175:T$="WAR!":PRINT@99,"A YOUNG PERSON'S CARD GAME";:PRINT@80-LEN(T$)/2,T$;:PRINT@175,"BY";:PRINT@202,"JAMES  GARON";:PRINT@263,"COPYRIGHT (C) 1982";:PRINT@298,"DATASOFT INC.";:PRINT@389,"LICENSED TO TANDY CORP.";:SCREEN0,1
60025 GOSUB60030:I$=INKEY$:FORI=1TO300:FORJ=1TO30:NEXT:IFINKEY$=""THENEXECV:NEXT:RETURNELSERETURN
60028 PRINTSTRING$(2,127+16*(9-I))TAB(30)STRING$(2,127+16*I);:RETURN
60030 A$="RUN!SUBELSE,NEXTENDFORTHENFORDIM/!9"
60060 V=VARPTR(A$):V=PEEK(V+2)*256+PEEK(V+3):RETURN

Line 60005 does two FOR/NEXT loops and both GOSUB 600028, so we’ll look at that next.

Line 60028

60028 PRINTSTRING$(2,127+16*(9-I))TAB(30)STRING$(2,127+16*I);:RETURN

This routine prints solid colored blocks on the left and right side of the screen, based on the value of I.

WAR.BAS line 60028

The two FOR loops are used to fill the entire screen. There are only 8 colors, so you can’t just do one loop from 1 to 14.

Line 60010

60010                             PRINT"MOTORMOTORRESTORERESTORE!!!DSKI$DSKI$DRIVEDRIVESCREENSCREEN^^MOTORMOTORRESTORERESTORE!!!DSKI$DSKI$DRIVEDRIVESCREENSCREEN^";

Look familiar? It’s just like the PRINT in line 60000, though the pattern of the blocks is different, and it is only printing 31. Since the one prints on the bottom of the screen, if it printed all the way to the bottom right position, the screen would scroll up one line.

WAR.BAS line 60010

Line 60020

60020 POKE1535,175:T$="WAR!":PRINT@99,"A YOUNG PERSON'S CARD GAME";:PRINT@80-LEN(T$)/2,T$;:PRINT@175,"BY";:PRINT@202,"JAMES  GARON";:PRINT@263,"COPYRIGHT (C) 1982";:PRINT@298,"DATASOFT INC.";:PRINT@389,"LICENSED TO TANDY CORP.";:SCREEN0,1

More normal code… The POKE 1535,175 is what fills in the bottom right block of the screen, where PRINT did not go to. 175 is a blue block.

After this are just normal PRINT@ statements to put the text on the screen.

At the end, SCREEN 0,1 puts the CoCo in to its alternate screen color of pink/red/orange/whatever color that is.

Line 60025

60025 GOSUB60030:I$=INKEY$:FORI=1TO300:FORJ=1TO30:NEXT:IFINKEY$=""THENEXECV:NEXT:RETURNELSERETURN

This line is normal code, but the use of EXEC tells us some assembly language is being used.

The first thing it does is GOSUB 60030, then it gets any waiting keypress in I$. I don’t see I$ used so I’m unsure of this purpose.

Next it does two FOR/NEXT loops. The first “FOR I” appears to be the number of times to do this routine. The second “FOR J/NEXT” is just a timing delay.

After this is a direct check for any waiting key by using INKEY$ directly. If no key is pressed, “EXEC V” is done… This would execute whatever machine language routine is loaded in to memory at wherever V is set to. But what is V? That must be the GOSUB 60030, which we will discuss after this.

After the NEXT (FOR I) is “RETURN ELSE RETURN”. That way it does a return whether or not the IF INKEY$ is true. Since either way will RETURN, with nothing executed after this line, this might have also worked (extra spaces added for readability):

60025 GOSUB 60030:I$=INKEY$:FOR I=1 TO 300:FOR J=1 TO 30:NEXT:IF INKEY$=""THEN EXEC V:NEXT
6026 RETURN

But James Garon seems to know his stuff. Each line number takes up 5 bytes of space. The three keywords “RETURN ELSE RETURN” (without spaces) only takes up four (I’m guessing RETURN is a one byte token and ELSE is a two byte token.)

So indeed, the odd “RETURN ELSE RETURN” is less memory than putting one RETURN on the next line. (In my Benchmarking BASIC articles, I’ve focused on speed versus space, so perhaps I’ll have to do a series on “Making BASIC Smaller” some time…)

This leads us to the GOSUB 60030.

Lines 60030-60060

60030 A$="RUN!SUBELSE,NEXTENDFORTHENFORDIM/!9"
60060 V=VARPTR(A$):V=PEEK(V+2)*256+PEEK(V+3):RETURN

Line 60030 creates a string, but the string seems reminiscent of those PRINT lines seen earlier. Since we don’t see anything using this string, it’s probably not for displaying block graphics characters.

We do see the use of VARPTR(A$) on the next line. VARPTR returns the “variable pointer” for a specified variable. I’ve discussed VARPTR in earlier articles, but the Getting Started with Color BASIC manual describes it as:

VARPTR (var) Returns addresses of pointer to the specified variable.

– Getting Started with Color BASIC

When using VARPTR on a numeric (floating point) variable, it returns the location of the five bytes that make up the number, somewhere in variable space.

But strings are different. Strings live in separate string memory (reserved by using the CLEAR command, with a default of 200 bytes), or they could be contained in the program code itself. See my String Theory series for more on that. With a string, the VARPTR points to five bytes that point to where the string data is contained.

In part 3 of my my DEFUSR series, I describe it as:

The first byte where the string is stored will be the size of that string:

A$=”THIS IS A STRING IN MEMORY”
X = VARPTR(A$)
PRINT “A$ IS LOCATED AT”;X
PRINT “A$ IS”;PEEK(X);”LONG”

I forget what the second byte is used for, but bytes three and four are the actual address of the string character data:

PRINT “STRING DATA IS AT”;PEEK(X+2)*256+PEEK(X+3)

– Interfacing assembly with BASIC via DEFUSR, part 3

This looks familiar, since the VARPTR(A$) in this code then gets the address of the string by PEEKing bytes three and four:

60060 V=VARPTR(A$):V=PEEK(V+2)*256+PEEK(V+3):RETURN

This tells me A$ contains a machine language routine. The GOSUB to this routine returns the address of the bytes between the quotes of the A$=”…” then that location is EXECuted to run whatever the routine does.

To figure this one out is much simpler, since no PEEKing of BASIC code is needed. It’s in a string, so we can just print the ASC() value of each byte in the string:

60030 A$="RUN!SUBELSE,NEXTENDFORTHENFORDIM/!9"
60031 FOR I=1 TO LEN(A$):PRINT ASC(MID$(A$,I,1));:NEXT:END

By doing a RUN 60030 I then get a list of bytes that are inside of that string:

142  3  255  48  1  166  132  44  4  139  16  138  128  167  128  140  6  1  47  241  57

I recognize 57 as the op code for an RTS instruction, so this does look like it’s machine code. And while I could use some 6809 data sheet and look up each of those bytes to figure out what they are, I’d rather have something else do the work for me.

Online 6809 Simulator to the rescue!

At www.6809.uk is an incredible 6809 simulator that I recently wrote about. It lets you paste in assembly code and run it in a debugger, showing the registers, op codes, etc. To get these bytes in to the emulator, I just turned them in to a stream of DATA using the “fcb” command in the simulator’s assembler:

routine fcb 142,3,255,48,1,166,132,44
        fcb 4,139,16,138,128,167,128,140,6
        fcb 1,47,241,57

By pasting that in to the simulator’s “Assembly language input” box and then clicking “Assemble source code“, the code is built and then displays on the left side in the debugger, complete with op codes:

6809.uk

Now I can see the code is:

4000:	8E 03 FF      	LDX #$03FF
4003:	30 01         	LEAX $01,X
4005:	A6 84         	LDA ,X
4007:	2C 04         	BGE $400D
4009:	8B 10         	ADDA #$10
400B:	8A 80         	ORA #$80
400D:	A7 80         	STA ,X+
400F:	8C 06 01      	CMPX #$0601
4012:	2F F1         	BLE $4005
4014:	39            	RTS

Since I did not give any origination address for where this code should go, the simulator used hex 4000. There are two branch instructions that refer to memory locations, so I’ll change those to labels and convert this just to the source code. I’ll even include some comments:

    LDX #$03FF  * X points to 1023, one byte before screen memory
    LEAX $01,X  * Increment X by 1 so it is now 1024
L4005
    LDA ,X      * Load A with the byte pointed to by X
    BGE L400D   * If that byte is not a graphics char, branch to L400d
    ADDA #$10   * Add hex 10 (16) to the character, next color up
    ORA #$80    * Set the high bit (in case value rolled over)
L400D
    STA ,X+     * Store (possibly changed) value back at X and increment X.
    CMPX #$0601 * Compare X to two bytes past end of screen
    BLE L4005   * If X is less than that, branch to L4005
    RTS         * Return

This code scans each byte of the 23 column screen. If the character there has the high bit set, it is a graphics character (128-255 range). It adds 16 to the value, which moves it to the next color (16 possible combinations of a 2×2 character block for 8 colors). It stores the value back to the screen (either the original, or one that has been shifted) and then increments X to the next screen position. If X is less than two bytes past the end of the screen, it goes back and does it again.

Hmm, it seems this routine would actually write to one byte past the end of the screen memory if it contained a value 128-255. Hopefully nothing important is stored there. (Am I right?)

And, for folks more used to BASIC, here is the same code with BASIC-style comments:

    LDX #$03FF  * X=&H3FF (1023, byte before screen)
    LEAX $01,X  * X=X+1
L4005
    LDA ,X      * A=PEEK(X)
    BGE L400D   * IF A<128 GOTO L400D
    ADDA #$10   * A=A+&H10:IF A>&HFF THEN A=A-&HFF
    ORA #$80    * A=A OR &H80
L400D
    STA ,X+     * POKE X,A:X=X+1
    CMPX #$0601 * Compare X to &H601 (two bytes past end)
    BLE L4005   * IF X<=&H601 GOTO L4005
    RTS         * RETURN

My BASIC-style comments don’t exactly match what happens in assembly since. For example, “BGE” means “Branch If Greater Than”, but there was no compare instruction before it. In this case, it’s branching based on what was just loaded in to the A register, and would be compared to 0. That looks odd, but BGE is comparing the value based on it being signed — instead of the byte representing 0-255, it represents -127 to 128, with the high bit set to indicate the value is negative. So, if the high bit is set, it’s a negative value, and the BGE would NOT branch. Fun.

If you run this BASIC program, it will BASICally do the same thing … just much slower:

100 X=&H3FF
110 X=X+1
120 A=PEEK(X)
130 IF A<128 GOTO 160
140 A=A+&H10:IF A>&HFF THEN A=A-&HFF
150 A=A OR &H80
160 POKE A,X:X=X+1
170 'Compare X to &H601
180 IF X<=&H601 GOTO 120
190 RETURN

So that is the magic to how this attract screen works so fast. It uses POKEd PRINT statements to quickly print the top and bottom of the screen, FOR/NEXT loops to print the sides, then this assembly routine to shift the colors and make them rotate around the screen.

And, this code is very similar to the routine I came up with earlier in this series:

start
    ldx #1024   X points to top left of 32-col screen
loop
    lda ,x+     load A with what X points to and inc X
    bpl skip    if not >128, skip
    adda #16    add 16, changing to next color
    ora #$80    make sure high gfx bit is set
    sta -1,x    save at X-1
skip
    cmpx #1536  compare X with last byte of screen
    bne loop    if not there, repeat
    sync        wait for screen sync
    rts         done

My routine starts with X at 1024, then uses BPL instead of BGE. I also increment X after I load the character, and I only update it if it gets modified by storing it 1 before where X is then.

At first, I thought mine was more clever. But I decided to see what LWASM said.

Counting cycles for fun and profit. Or just more speed.

LWASM can be made to display a listing of the compiled program and, optionally, list how many cycles each line will take. And, optionally optionally, keep a running total of cycles between places in the code you mark. (I have another article about how to use this.)

Here is my version, with (cycles) in parens, and running totals in the column next to it.

        start
(3)         ldx #1024
                loop
(5)     5           lda ,x+
(3)     8           bpl skip
(2)     10          adda #16
(2)     12          ora #$80
(5)     17          sta -1,x
                skip
(3)     20          cmpx #1536
(3)     23          bne loop
(4)         rts

My routine uses 23 cycles from “loop” to “bne loop”.

Here is the routine from WAR.BAS:

(3)         LDX #$03FF
(5)         LEAX $01,X
                L4005
(4)     4           LDA ,X 
(3)     7           BGE L400D
(2)     9           ADDA #$10
(2)     11          ORA #$80
                L400D
(5)     16          STA ,X+
(3)     19          CMPX #$0601
(3)     22          BLE L4005
(4)         RTS

This one appears to use one cycle less — 22 — in it’s loop. Nice! Even though I thought it would be worse, once again, James Garon appears to know his stuff.

I think his routine may be a bit larger, and I wondered why he started X one byte before the screen memory and then incremented it. That seems wasteful.

However, William “Lost Wizard” Astle saw exactly why in a reply on the CoCo mailing list:

He uses that sequence instead of the more obvious one to avoid having a NUL byte in the code. A NUL would cause the interpreter to think it’s the end of the line and break things.

– William Astle via CoCo mailing list

It took me a moment, but I think I understand now. If he had done “LDA #$4000”, the byte sequence would have been whatever byte is LDA, followed by $04 and $00. You can’t put a 0 in a string, or BASIC will think that is the end of the string. Any assembly encoded this way must avoid using a zero. This is also the reason he doesn’t compare to the byte past the end of the screen, which is $6000. Though, I expect comparing to 1535 and using “Branch if less than OR equal to” would have worked and avoided the zero.

But James Garon knows his stuff, so I had to see if my way was larger or slower:

(3)         cmpx #1535
(3)         ble loop
        
(3)         CMPX #$0601
(3)         BLE L4005

Well, they look the same speed, and neither generates a zero byte in the machine code. I don’t know why he does it that way.

Any thoughts?

BONUS!

If one were to patch the Color BASIC “UNCRUNCH” routine to show things between quotes, here is what those lines would look like… (And if one did such a patch, I expect they’d be writing a future article about it…)

Conclusion

For some reason, James Garon chose to embed assembly code and graphics characters like this, rather than using DATA statements and building strings or POKEing assembly bytes in to memory somewhere.

It’s cool to see. But unless someone knows James Garon, I guess we don’t know why this method was done.

Other than “because it’s cool,” which is always a good reason to do something when programming.

Until next time…

More CoCo MC6847 VDG chip “draw black” challenge responses.

See also: challenge, responses, and more responses.

Today Sebastian Tepper submitted a solution to the “draw black” challenge. He wrote:

I think this is much faster and avoids unnecessary SETs. Instruction 100 will do the POKE only once per character block.

– Sebastian Tepper, 7/5/2022

The routine he presented (seen in lines 100 and 101) looked like this:

10 CLS
20 FOR A=0 TO 31
30 X=A:Y=A:GOSUB 100
40 NEXT
50 GOTO 50
100 IF POINT(X,Y)<0 THEN POKE 1024+Y*16+X/2,143
101 RESET(X,Y):RETURN

It did see the criteria of the challenge, correctly drawing a diagonal line from (0,0) down to (31,31) on the screen. And, it was fast.

POINT() will return -1 if the location is not a graphics character. On the standard CLS screen, the screen is filled with character 96 — a space. (That’s the value you use to POKE to the screen, but when printing, it would be CHR$(32) instead.) His code would simply figure out which screen character contained the target pixel, and POKE it to 143 before setting the pixel.

So I immediately tried to break it. I wondered what would happen if it was setting two pixels next to each other in the same block. What would RESET do?

I added a few lines to the original test program so it drew the diagonal line in both directions PLUS draw a box (with no overlapping corners). My intent was to make it draw a horizontal line on an even pixel line, and odd pixel line, and the same for verticals. It looks like this (and the original article has been updated):

10 CLS
20 FOR A=0 TO 15

30 X=A:Y=A:GOSUB 100
31 X=15-A:Y=16+A:GOSUB 100

32 X=40+A:Y=7:GOSUB 100
33 X=40+A:Y=24:GOSUB 100

34 X=39:Y=8+A:GOSUB 100
35 X=56:Y=8+A:GOSUB 100

40 NEXT
50 GOTO 50

And this did break Sebastian’s routine… and he immediately fixed it:

100 IF POINT(X,Y)<0 THEN POKE 1024+INT(Y/2)*32+INT(X/2),143
101 RESET(X,Y):RETURN

I haven’t looked at what changed, but I see it calculates the character memory location by dividing Y by two (and making sure it’s an integer with no floating point decimals — so for 15 becomes 7 rather than 7.5), and then adds half of X. (Screen blocks are half as many as the SET/RESET pixels).

And it works. And it works well — all cases are satisfied.

And if that wasn’t enough, some optimizations came next:

And for maximum speed you could change line 100 from:

100 IF POINT(X,Y)<0 THEN POKE 1024+INT(Y/2)*32+INT(X/2),143

to:

100 IFPOINT(X,Y)<.THEN POKE&H400+INT(Y/2)*&H20+INT(X/2),&H8F

To time the difference, I added these extra lines:

15 TIMER=0

and:

45 PRINT TIMER

This lowers execution time from 188 to 163 timer units, i.e., down to 87% of the original time.

– Sebastian Tepper, 7/5/2022

Any time I see TIMER in the mix, I get giddy.

Spaces had been removed, 0 was changed to . (which BASIC will see a much faster-to-parse zero), and integer values were changed to base-16 hex values.

Also, in doing speed tests about the number format I verified that using hexadecimal numbers was more convenient only when the numbers in question have two or more digits.

– Sebastian Tepper, 7/5/2022

Awesome!

Perhaps final improvement could be to change the screen memory location from 1024/&H400 to a variable set to that value, the multiplication value of 32/&h20, as well as the 143/&H8F. Looking up a variable, if there are not too many of them in the list before the ones you’re looking up, can be even faster.

Using the timer value of 163 for our speed to beat, first I moved that extra space just to see if it mattered. No change.

Then I declared three new variables, and used DIM to put them in the order I wanted them (the A in the FOR/NEXT loop initially being the last):

11 DIM S,C,W,A
12 S=1024:W=32:C=143
...
100 IFPOINT(X,Y)<.THENPOKES+INT(Y/2)*W+INT(X/2),C
101 RESET(X,Y):RETURN

No change. I still got 163. So I moved A to the start. A is used more than any other variable, so maybe that will help:

11 DIM A,S,C,W

No change — still 163.

Are there any other optimizations we could try? Let us know in the comments.

Thank you for this contribution, Sebastian. I admire your attention to speed.

Until next time…

Color BASIC Attract Screen – part 5

See also: part 1, part 2, part 3, part 4, unrelated, part 5 and part 6.

In part 4 of this series, Jason Pittman provided several variations of creating the attract screen:

Jason Pittman variation #1

If those four corners bother you, then my attempt will really kick in that OCD when you notice how wonky the colors are moving…

Jason Pittman
10 CLS0:C=143:PRINT@268,"ATTRACT!";
20 FOR ZZ=0TO1STEP0:FORX=0TO15:POKEX+1024,C:POKEX+1040,C:POKE1535-X,C:POKE1519-X,C:POKE1055+(32*X),C:POKE1472-(32*X),C:GOSUB50:NEXT:GOSUB50:NEXT
50 C=C+16:IF C>255 THEN C=143
60 RETURN

Jason Pittman variation #2

Also, another option using the substrings might be to fill the sides by printing two-character strings on the 32nd column so that a character spills over to the first column of the next line:

Jason Pittman
10 CLS 0:C=143:OF=1:CH$=""
20 FOR X=0TO40:CH$=CH$+CHR$(C):GOSUB 90:NEXT
30 FOR ST=0TO1STEP0
40 PRINT@0,MID$(CH$,OF,31):GOSUB 120
50 FORX=31TO480STEP32:PRINT@X,MID$(CH$,OF,2);:GOSUB 120:NEXT
60 PRINT@481,MID$(CH$,OF,30);:GOSUB120
70 NEXT
80 REM ADVANCE COLOR
90 C=C+16:IF C>255 THEN C=143
100 RETURN
110 REM ADVANCE OFFSET
120 OF=OF+2:IF OF>7 THEN OF=OF-8
130 RETURN

Jason Pittman variation #3

One more try at O.C.D-compliant “fast”:

Jason Pittman
10 DIM CL(24):FORX=0TO7:CL(X)=143+(X*16):CL(X+8)=CL(X):CL(X+16)=CL(X):NEXT
20 CLS0:FORXX=0TO1STEP0:FORYY=0TO7:FORZZ=1TO12:READPO,CT,ST,SR:FOR X=SRTOSR+CT-1:PO=PO+ST:POKE PO,CL(X+YY):NEXT:NEXT:RESTORE:NEXT:NEXT
180 REM POSITION,COUNT,STEP,START
190 DATA 1024,8,1,0,1032,8,1,0,1040,8,1,0,1048,6,1,0,1055,8,32,6,1311,6,32,6,1535,8,-1,4,1527,8,-1,4,1519,8,-1,4,1511,6,-1,4,1504,8,-32,2,1248,6,-32,2

The #3 variation using DATA statements is my favorite due to its speed. Great work!

The need for speed: Some assembly required.

It seems clear that even the fastest BASIC tricks presented so far are still not as fast as an attract screen really needs to be. When this happens, assembly code is the solution. There are also at least two C compilers for Color BASIC that I need to explore, since writing stuff in C would be much easier for me than 6809 assembly.

Shortly after part 4, I put out a plea for help with some assembly code that would rotate graphical color blocks on the 32 column screen. William “Lost Wizard” Astle answered that plea, so I’ll present the updated routine in his LWASM 6809 compiler format instead of as an EDTASM+ screen shot in the original article.

* lwasm attract32.asm -fbasic -oattract32.bas --map

    org $3f00

start
    ldx #1024   X points to top left of 32-col screen
loop
    lda ,x+     load A with what X points to and inc X
    bpl skip    if not >128, skip
    adda #16    add 16, changing to next color
    ora #$80    make sure high gfx bit is set
    sta -1,x    save at X-1
skip
    cmpx #1536  compare X with last byte of screen
    bne loop    if not there, repeat
    sync        wait for screen sync
    rts         done

    END

The code will scan all 512 bytes of the 32-column screen, and any byte that has the high bit set (indicating it is a graphics character) will be incremented to the next color. This would allow us to draw our attract screen border one time, then let assembly cycle through the colors.

How it works:

  • The X register is loaded with the address of the top left 32-column screen.
  • The A register is loaded with the byte that X points to, then X is incremented.
  • BPL is used to skip any bytes that do not have the high bit set. This optimization was suggested by William. An 8-bit value can be treated as an unsigned value from 0-255, or as a signed value of -127 to 128. A signed byte uses the high bit to indicate a negative. Thus, a positive number would not have the high bit set (and is therefor not in the 128-255 graphics character range).
  • If the high bit was set, then 16 is added to A.
  • ORA is used to set the high bit, in case it was in the final color range (240-255) and had 16 added to it, turning it in to a non-graphics block. Setting the high bit changes it from 0-16 to 128-143.
  • The modified value is stored back at one byte before where X now points. (This was another William optimization, since originally I was not incrementing X until after the store, using an additional instruction to do that.)
  • Finally, we compare X to see if it has passed the end of screen memory.
  • If it hasn’t, we do it all again.
  • Finally, we have a SYNC instruction, that waits for the next screen interrupt. This is not really necessary, but it prevents flickering of the screen if the routine is being called too fast. (I’m not 100% sure if this should be here, or at the start of the code.)

The LWASM compiler has an option to generate a BASIC program full of DATA statements containing the machine code. You can then type that program in and RUN it to get this routine in memory. The command line to do this is in the first comment of the source code above.

10 READ A,B
20 IF A=-1 THEN 70
30 FOR C = A TO B
40 READ D:POKE C,D
50 NEXT C
60 GOTO 10
70 END
80 DATA 16128,16147,142,4,0,166,128,42,6,139,16,138,128,167,31,140,6,0,38,241,19,57,-1,-1

The program loads at $3f00 (16128), meaning it would only work on a 16K+ system. There is no requirement for that much memory, and it could be loaded anywhere else (even on a 4K system). The machine code itself is only 20 bytes. Since the code was written to be position independent (using relate branch instructions instead of hard-coded jump instructions), you could change where it loads just by altering the first two numbers in the DATA statement (start address, end address).

For instance, on a 4K CoCo, memory is from 0 to 4095. Since the assembly code only uses 20 bytes, one could load it at 4076, and use CLEAR 200,4076 to make sure BASIC doesn’t try to overwrite it. However, I found that the SYNC instruction hangs the 4K CoCo, at least in the emulator I am using, so to run on a 4K system you would have to remove that.

Here is the BASIC program modified for 4K. I added a CLEAR to protect the code from being overwritten by BASIC, changed the start and end addresses in the data statements, and altered the SYNC code to be an RTS (changing SYNC code of 19 to a 57, which I knew was an RTS because it was the last byte of the program in the DATA statements). This means it is wasting a byte, but here it is:

5 CLEAR 200,4076
10 READ A,B
20 IF A=-1 THEN 70
30 FOR C = A TO B
40 READ D:POKE C,D
50 NEXT C
60 GOTO 10
70 END
80 DATA 4076,4095,142,4,0,166,128,42,6,139,16,138,128,167,31,140,6,0,38,241,57,57,-1,-1

Using the code

Lastly, here is an example that uses this routine. I’ll use the BASIC loader for the 32K version, then add Jason’s variation #1 to it, modified by renaming it to start at line 100, and removing the outer infinite FOR Z loop so it only draws once. I’ll then add a GOTO loop that just executes this assembly routine over and over.

5 CLEAR 200,16128
10 READ A,B
20 IF A=-1 THEN 70
30 FOR C = A TO B
40 READ D:POKE C,D
50 NEXT C
60 GOTO 10
70 GOTO 100
80 DATA 16128,16147,142,4,0,166,128,42,6,139,16,138,128,167,31,140,6,0,38,241,19,57,-1,-1
100 CLS0:C=143:PRINT@268,"ATTRACT!";
120 FORX=0TO15:POKEX+1024,C:POKEX+1040,C:POKE1535-X,C:POKE1519-X,C:POKE1055+(32*X),C:POKE1472-(32*X),C:GOSUB150:NEXT:GOSUB150
130 EXEC 16128:GOTO 130
150 C=C+16:IF C>255 THEN C=143
160 RETURN

And there you have it! An attract screen for BASIC that uses assembly so it’s really not a BASIC attract screen at all except for the code that draws it initially using BASIC.

I think that about covers it. And, this routine also looks cool on normal 32-column VDG graphics screens, too, causing the colors to flash as if there is palette switching in use. (You can actually palette switch the 32-column screen colors on a CoCo 3.)

Addendum: WAR by James Garon

On 7/2/2022, Robert Gault posted to the CoCo list a message titled “Special coding in WAR“. He mentioned some embedded data inside this BASIC program. You can download it as a cassette or disk image here:

https://colorcomputerarchive.com/search?q=War+%28Tandy%29&ww=1

You can even go to that link and click “Play Now” to see the game in action.

I found this particularly interesting because this BASIC program starts with one of the classic CoCo attract screens this article series is about. In the program, the author did two tricks: One was to embed graphics characters in a PRINT statement, and the other was to embedded a short assembly language routine in a string that would cycle through the screen colors, just like my approach! I feel my idea has been validated, since it was already used by this game in 1982. See it in action:

https://colorcomputerarchive.com/test/xroar-online/?machine=cocous&basic=RUN%22WAR%22%5cr&cart=rsdos&disk0=/unzip%3Ffile%3DDisks/Games/War%20(Tandy).zip/WAR.DSK

And if you are curious, the code in question starts at line 60000. I did a reply about this on the CoCo mailing list as I dug in to what it is doing. That sounds like it might make a part 6 of this series…

Until next time…

TIL: Color BASIC device numbers and more.

When I moved from my Commodore VIC-20 to a TRS-80 Color Computer, I spent much time going through the “Getting Started with Color BASIC” and “Getting Started with Extended Color BASIC” manuals that came with it.

If you had the original manual revision, you may have been taught things slightly different from later editions. For instance, clearing the screen. The CLS command can be followed by a number to fill the screen with a color.

But the early edition manual demonstrated this using parenthesis, which I’ve never seen done:

CLS(3)

It works. Try it :)

After a moment of thinking about this, I realized it’s just normal math grouping for numbers, like this:

PRINT 3*(5-1)

The parenthesis make sense there. But they sure look odd if you do this:

PRINT (42)

It works. Try it :)

And whoever wrote the manual was using them for CLS too. But why?

Not too long ago, I learned that the BASIC ROMs had other undocumented syntax. For instance, there is a Syntax for the PRINT command where you give a screen position using the @ symbol:

PRINT @96,"HELLO"

But the BASIC ROMs also look for (but do not require) the @ symbol after keywords LINE@, PAINT@, GET@ and PUT@. But I’ve never seen anyone code:

LINE@(0,0)-(255,191),PSET

It works. Try it :)

I expect there are other oddities in there, as well. One I recently discovered, just by trying it, had to do with device numbers.

Device numbers

I was surprised to find that the original Color BASIC manual did not cover all of the commands available. For example, printer listing with LLIST and PRINT#-2, or opening and reading/writing to/from cassettes. I expect later revisions may have added these, and maybe other, missing commands.

Folks using a printer learned that, while PRINT by itself goes to the screen, doing PRINT#-2 makes the output go to the printer.

#-2 is a device number. When accessing a cassette file, device #-1 is used:

OPEN "O",#-1,"TAPEFILE"
PRINT #-1,"THIS GOES TO THE CASSETTE FILE"
CLOSE #-1

Disk Extended Color BASIC added disk device numbers, and allowed opening up to ten files at a time using devices #1 to #10.

OPEN "O",#1,"DISKFILE.TXT"
PRINT #1,"THIS GOES TO A DISK FILE"
CLOSE #1

At some point, I became aware that device #0 was the screen. The use of it was optional, since PRINT by itself assumed screen.

PRINT "THIS GOES TO THE SCREEN"
PRINT #0,"SO DOES THIS"

It works. Try it :)

But, it was useful when creating a program that allowed the user to select output to screen or printer.

10 INPUT "OUTPUT TO S)CREEN OR P)RINTER";A$
20 IF A$="S" THEN DV=0
30 IF A$="P" THEN DV=-2
40 PRINT #DV,"THIS GOES TO SCREEN OR PRINTER"

I recall writing programs that would also allow outputting to a tape or disk file, so if one of those options was chosen, I would make sure to create the output file:

10 INPUT "OUTPUT TO S)CREEN, P)RINTER, T)APE OR D)ISK";A$
20 IF A$="S" THEN DV=0
30 IF A$="P" THEN DV=-2
40 IF A$="T" THEN DV=-1:OPEN "O",#-1,"TAPEFILE"
50 IF A$="D" THEN DV=1:OPEN "O",#1,"DISKFILE.TXT"
60 PRINT #DV,"THIS GOES TO... SOMEWHERE!"
70 IF DV=-1 THEN CLOSE #-1
80 IF DV=1 THEN CLOSE #1

Yuck. But you get the idea.

On a whim, I wondered what happened if you tried to CLOSE #0 (nothing). Or CLOSE #-2 (nothing). Then I wondered what happened if you tried to open those devices:

10 OPEN "O",#0,"SCREEN"
20 PRINT #0,"HELLO, SCREEN"
30 CLOSE #0

It appears BASIC just ignores OPEN/CLOSE to the screen device. And the printer:

10 OPEN "O",#-2,"PRINTER"
20 PRINT #-2,"HELLO, PRINTER"
30 CLOSE #-2

This means my multi-output program could have been made much, much simpler:

10 INPUT "OUTPUT TO S)CREEN, P)RINTER, T)APE OR D)ISK";A$
20 IF A$="S" THEN DV=0
30 IF A$="P" THEN DV=-2
40 IF A$="T" THEN DV=-1
50 IF A$="D" THEN DV=1
60 OPEN "O",#DV,"OUTFILE"
60 PRINT #DV,"THIS GOES TO... SOMEWHERE!"
70 CLOSE #DV

The only extra thing one might want to do is name the file different if going to disk (adding an extension), though you can do that to a tape file without error:

OPEN "O",#-1,"TAPE.TXT"

For tape, the filename can be up to 8 characters, with no extension. So the name would be “TAPE.TXT”. But if you had done “OUTPUT.TXT”, the tape name would be “OUTPUT.T” (8 characters). Not pretty, but it works.

Maybe we take care of that with:

10 INPUT "OUTPUT TO S)CREEN, P)RINTER, T)APE OR D)ISK";A$
20 IF A$="S" THEN DV=0
30 IF A$="P" THEN DV=-2
40 IF A$="T" THEN DV=-1
50 IF A$="D" THEN DV=1:EX$=".TXT"
60 OPEN "O",#DV,"OUTFILE"+EX$
60 PRINT #DV,"THIS GOES TO... SOMEWHERE!"
70 CLOSE #DV

That would add an extension (“.TXT”) only for disk files.

Anyway, I thought it was interesting that BASIC allowed OPEN/CLOSE on printer and screen devices, and I expect if I looked at the ROM disassembly, I’d find extra code there that just returns (not doing anything) when those device numbers are used.

Oh, and there is also a device #-3 in Extended Color BASIC, but you can’t do anything with it. That device number seems to be associated with the DLOAD command (which was removed on the CoCo 3) and is just used internally. But that’s the subject of a potential future article…

Until next time…

Installing LWTOOLS on Windows using Cygwin

Updates:

  • 2022-06-30 – Added details about adding Mercurial and retrieving source that way. Also added notes about building Toolshed (currently does not work) and NitrOS9.

A quick tutorial on how to get the Lost Wizard LWTOOLS running under Windows. I have tested this under Windows 11.

To build under Windows, you will need to use a compatibility layer that makes Linux-style code work under Windows. Lost Wizard recommends mingw or Cygwin. This tutorial will use Cygwin.

Step 1: Download Cygwin.

Go to https://www.cygwin.com/ and download the current Windows installer. It is called “setup-x86_64.exe“.

Run the installer:

Click Next, then choose “Install from Internet”:

Click Next, then either use the default install location, or customize if you know what you are doing. Same thing for installing for “All Users” or “Just Me.” In my example, I am just using the defaults:

Click Next, then select where it will download files. I will just use the defaults:

Click Next, and just use the default Internet Connection settings unless you know what you are doing and need to change them:

Click Next, then select one of the download sites. It shouldn’t matter which one you choose, but since I used to read the old Maddox website hosted on xmission.com, I selected that:

Click Next, and it will retrieve a list of available packages. For building LWTOOLS, you need a C compiler and “make” utility. I went with the standard GCC compiler and standard make.

Expand the “All” then “Devel” items.

Locate the entry that says “gcc-core” (or use the Search box) and click on the “Skip” to the right of it. It should change from “Skip” to a version number (currently 11.3.0-1 as I type this).

Locate the entry that says “make” and do the same (currently 4.3-1 as I type this).

If you would like to download LWTOOLS (and other items) source directly rather than having to download “the most recent release”, you will also need to install “mercurial“. This will give you the “hg” command used to retrieve the latest source code for the projects. (And if you are doing all of this, might as well do this, too.)

Click Next, and you will see a list of all the required packages that will be download. (When you select an item, this installer will download any other items that are required for the one you selected.)

Click Next, and the download will begin.

When completed, you can click Next and then choose if you want to add a Cygwin icon on your Desktop and/or in the Start Menu. Since you will need to run Cygwin to build LWTOOLS, you may want one or both.

Click Finish. You now have Cygwin!

Step 2: Download and extract the LWtools source code.

You can either download the source code and build that, OR you can use Mercurial to retrieve the latest version of the source (which currently includes a bug fix that is not in the release archive yet). Plus, it saves all the steps of extracting the gzipped tar file in 2b ;-)

For either way, you will want to run “Cygwin64 Terminal” that is now installed. This will open up a console prompt.

You will need to change directories to where you plan to download the LWTOOLS source code. Cygwin translates the Windows directories like “C:\Users\huffm\Downloads” to a Unix-style directory path like “/cygdrive/c/Users/huffm/Downloads” on my system. If you know how to change directories from a Windows Command Prompt, instead of going to “C:\whatever” you would change backslashes to forward slashes, and start with “/cygdrive/c/whatever”

Use the change directory (“cd”) command and change to where you downloaded the LWTOOLS .gz file.

An easy way to get this path is to type the change directory command in the Cygwin terminal window, followed by a space, (“cd “) and then drag the “Downloads” folder from a Windows Explore window IN to the Cygwin Terminal. It will type out the path to whatever folder you drag and drop there:

Press enter, and you should now be in the directory where you downloaded the LWTOOLS file. The Cygwin prompt will change to show you what directory you are in. Mine looks like this:

huffm@Allen-LT /cygdrive/c/Users/huffm/Downloads
$

2a. Use Mercurial to get the latest source.

From that location, enter this mercurial (“hg”) command:

hg clone http://lwtools.projects.l-w.ca/hg/ lwtools

That will retrieve the latest source and place it in a subdirectory called “lwtools” from here you are. Once complete, proceed to Step 3.

2b. Download and extract the latest release.

OR, if you want to manually download the latest release…

Go to http://www.lwtools.ca/ and download the latest version (currently 4.19 as I type this). Save it to wherever you chose, above.

The download will be a gzipped .tar file, so you will need some tool to extract it. You can find something in the Windows Store, or just use a command line utility from Cygwin. For this tutorial, we will use the command line.

From this terminal, many Linux-style commands are available, including gzip (which we will use to extract the LWTOOLS .tar file) and tar (which we will use to un-tar that file).

Extract the .gzip file by typing “gzip -d” (for decompress) followed by the lwtools filename:

gzip -d lwtools-4.19.tar.gz

This should extract the “lwtools-4.19.tar” file in to that directory. Now un-tar that file by typing:

tar -xf lwtools-4.19.tar

That will finally leave you with a directory called “lwtools-4.19” (or whatever version you downloaded.

Step 3: Build and install LWTOOLS

Change directories in to the “lwtools-4.19” directory (or, if you downloaded with Mercurial, in to “lwtools” or whatever you called it):

cd lwtools-4.19

Once you are there, all you need to do is type “make” to begin the build process.

Once complete (it may take awhile), the binaries have been built, but they aren’t located where Cygwin can run them yet. To copy them over in to the proper location, type “make install“:

You now have some new command line programs available from within Cygwin. To verify that they worked, you can try typing them to see if they bring up their usage display. Try typing:

lwasm --usage

If you get back a “Usage:” message, you should now be ready to use LWTOOLS to compile 6809 assembly language for the CoCo.

4. Other things you may want to install

Toolshed is a series of commands for copying files from your PC in to disk image files used by emulators or things like the CoCoSDC.

NOTE: Currently, this will not work. Some rules have changed in the compiler and it will error out. There are about 12 places in the source that can easily be fixed to make it build, but I’m going to wait and see if the Toolshed maintainers have a solution.

hg clone http://hg.code.sf.net/p/toolshed/code toolshed
cd toolshed
make -C build/unix install
cd ..

NitrOS9 is a 6809 (or 6309) operating system based on Microware OS-9/6809.

hg clone http://hg.code.sf.net/p/nitros9/code nitros9
cd nitros9
make dsk

BONUS: Building a simple program.

As a simple test, use a text or code editor to create the following “helloworld.asm” file. You will need to know where you save this file, since you will be typing that on the command line to build it. On my system, I have all my .asm files in a directory, and I just “cd” to that directory from the Cygwin terminal.

* helloasm.asm

    org $3f00

    ldx #message
loop:
    lda ,x+
    beq done
    jsr [$a002]
    bra loop
done:
    rts

message fcc "HELLO WORLD!"
    fcb 13
    fcb 0

This simple program will display the message “HELLO WORLD!”. It does this by using the Color BASIC “CHROUT” ROM call. This code starts by loading X with the address of a text message that is a series of characters, followed by a 13 (carriage return) and a 0 to mark the end of the message. The main loop loads the A register with whatever is at X, and if it is zero it ends. Otherwise, it calls the CHROUT routine indirectly by jumping to the location stored at $a002 in the ROM. It will repeat this until it gets to the 0 at the end of the message.

LWTOOLS can build .bin files that can be transferred to a CoCo (or emulator) on a disk image (using other tools), and then you can LOADM that file and EXEC it:

lwasm helloasm.asm -fdecb -ohelloasm.bin

Above, that takes the input file “helloasm.asm” and compiles it in format “decb” (a .bin binary) and calls the output file “helloasm.bin”. (You’d probably want all uppercase for filenames on the CoCo.) That should give a LOADM-able file to try.

But, a nifty feature of LWTOOLS is the ability to generate a BASIC program that loads the assembly language. Use the format “basic” and make the output file a “.bas” instead:

lwasm helloasm.asm -fbasic -ohelloasm.bas

That will create a text file called “helloasm.bas”:

10 READ A,B
20 IF A=-1 THEN 70
30 FOR C = A TO B
40 READ D:POKE C,D
50 NEXT C
60 GOTO 10
70 END
80 DATA 16128,16155,142,63,14,166,128,39,6,173,159,160,2,32,246,57,72,69,76,76,79,32,87,79,82,76,68,33,13,0,-1,-1

I like to use the XRoar emulator, since it lets me load a text file as if it was a cassette file. You can run XRoar, then use Ctrl-L (or File->Load) then select the “helloasm.bas” file. After that is done, typing “CLOAD” in XRoar will load this text file as if it was coming from tape!

Then you can “RUN” the program and load your assembly in to memory. For this example, the address of $3f00 was specified in the source codes “org” address (16128 in decimal) so that is where the code would load. After the “RUN”, you should be able to type “EXEC &H3f00” (or EXEC 16128 if not using Extended Color BASIC) and see the program run:

Have fun!

Until next time…

6809 request: smallest 64K test?

Okay, 6809 folks… In my 64K TRS-80 CoCo memory test article, I used an assembly language program of unknown origin to copy the CoCo’s ROM in to RAM. The “test” part is POKEing a byte in to ROM space and seeing if it now changes (since, on a 64K system, it would be running out of RAM).

The BASIC “OK” prompt is changed to read “OY” (after placing the 64K CoCo in to all-RAM mode).

That code looks like it was built for speed, moving 6 bytes at a time by using three 16-bit registers (X, Y and U). As a refresher, here is that code:

start:
    PSHS CC
    ORCC #$50
    LDY #$8000
loop1:    
    STA $FFDE
    LDD ,Y
    LDX $02,Y
    LDU $04,Y
    STA $FFDF
    STD ,Y++
    STX ,Y++
    STU ,Y++
loop2:    
    CMPY #$FEFC
    BCS loop1
    CMPY #$FF00
    BCC done
    STA $FFDE
    LDD ,Y
    STA $FFDF
    STD ,Y++
    BRA loop2
done:    
    PULS CC
    RTS

I presented this routine as a BASIC loader program so one could easily type it in rather than needing an assembler and typing in assembly source code to compile.

For folks patient enough to type in a whole CoCo screen full of hexadecimal DATA statements, it works fine. But I thought it might be finer to present an even smaller program with less DATA statements.

The program size can almost be cut in half by eliminating the first loop that copies the 6 bytes at a time. Instead, I came up with something like this:

start:
    PSHS CC     Save CC
    ORCC #$50   Mask interrupts
    
    LDX #$8000  Start of ROM
loop:    
    STA $FFDE   Enable ROM
    LDD ,X      Load D with whatever is at X
    STA $FFDF   Disable ROM
    STD ,X++    Store D at X and increment X
    CMPX #$FF00 Is X past end of ROM?
    BNE loop    If not, repeat

    PULS CC     Restore CC
    RTS         Return

The original “need for speed” version compiled to 53 bytes. This new version compiles to 25 bytes. That would make it much easier to type in, like this:

0 REM 64K ROM TO RAM (25)
10 FOR L=16128 TO 16152
20 READ V:POKE L,V
30 NEXT
40 EXEC 16128:POKE 44015,89
80 DATA 52,1,26,80,142,128,0
90 DATA 183,255,222,236,132
100 DATA 183,255,223,237,129
110 DATA 140,255,0,38,241,53
120 DATA 1,57

Is there anything I can do to save a few bytes in that ROM to RAM routine? Please share comments and suggestions.

Is there an easier way to detect 64K? (Hey, that rhymes!)

If the goal is to just test for the existence of 64K, all we really need to do is put the machine in RAM mode and try to modify a byte in the upper 32K. If it can be modified, 64K is there. Maybe there is an even smaller way just to do that?

In 64K, you have memory locations 0-65535 available ($0000-$FFFF). But, the last 255 bytes ($FF00-$FFFF) are used for I/O, and as far as I know, the RAM there cannot be accessed. (Is this correct?) That would mean the last byte of usable RAM on a 64K CoCo would be at $FF00-1 ($FEFF). If that is correct, all we need to do is switch to RAM mode, store a byte at $FEFF and then read it back and see if it is what we put there. If it is, 64K exists.

There is a ROM routine that will output whatever character is loaded in the A register. We could use that to print out a message if 64K exists. Since the goal is to make this as small as possible, the message could simply be ‘Y’. My first attempt was something like this:

start:
    PSHS CC     Save CC
    ORCC #$50   Mask interrupts.
    
    STA $FFDF   Disable ROM
    LDA #'Y     Load A with 'Y'
    STA $FEFF   Store A in last RAM byte
    CLRA        Clear A
    LDA $FEFF   Load A with last RAM byte
    CMPA #'Y    Compare to 'Y'
    BEQ done    If Y, done.
    LDA #'N     Else, load A with 'N'
done:
    STA $FFDE   Enable ROM
    PULS CC     Restore CC
    JSR [$A002] Output byte in A to console.

    RTS         Return

I disable the ROM (going in to “all RAM mode”), load A with a ‘Y’ character, then store it at $FEFF. I then clear A, then load A with whatever is at $FEFF. I compare A with ‘Y’ and if it is, I branch to the end where it will re-enable ROM, restore the CC register, then jump to the ROM routine that outputs whatever is in A. If it had no equaled ‘Y’, it would have not branched to ‘done’ and would instead load A with ‘N’, then complete, outputting ‘N’.

Running that should print out Y or N, depending on if 64K is detected.

And it works! But it is larger than the 64K ROM TO RAM code, taking up 32 bytes. I suppose there could be less typing since now the “ROM to RAM” program wouldn’t need to POKE the ‘OK’ prompt to show the user if it can be changed, so this probably is better.

0 REM 64KTEST1.BAS (32)
10 FOR L=16128 TO 16159
20 READ V:POKE L,V
30 NEXT
40 EXEC 16128
50 DATA 52,1,26,80,183,255,223
60 DATA 134,89,183,254,255,79
70 DATA 182,254,255,129,89,39
80 DATA 2,134,78,183,255,222
90 DATA 53,1,173,159,160,2,57

However … If we don’t want to be as user-friendly (printing a ‘Y’ or ‘N’), maybe some bytes could be saved by just setting a byte on the 32-column screen to indicate the result. I tried this:

start:
    PSHS CC     Save CC
    ORCC #$50   Mask interrupts.
    
    STA $FFDF   Disable ROM
    CLR $FEFF
    DEC $FEFF
    LDA $FEFF
done:
    STA $FFDE   Enable ROM
    PULS CC     Restore CC
    STA $0400

    RTS         Return

For this routine, I save CC and disable interrupts, then disable ROM. I then clear memory location $FEFF, and then try to Decrement whatever is there. If the CLR worked, it should be 0, and a DEC would turn it in to 255. To see what happened, I load A with whatever is at $FEFF, re-enable ROM, restore CC and then store whatever I loaded in to A to the top left of the 32 column screen. If 64K is present, an orange graphics block (255) should appear in the top left of the screen. If not, whatever value in ROM at $FEFF will be stored that. On my CoCo, that is a 0, so I should see an inverted ‘@’ sign appear on a non-64K system. This isn’t perfect, since if ROM just happened to contain 255 at that location, this test would not work.

This is 25 bytes of code. Still no savings, but the BASIC program could be smaller, which is the end goal:

0 REM 64KTEST2.BAS (25)
10 FOR L=16128 TO 16152
20 READ V:POKE L,V
30 NEXT
40 EXEC 16128
50 DATA 52,1,26,80,183,255,223
60 DATA 127,254,255,122,254
70 DATA 255,182,254,255,183
80 DATA 255,222,53,1,183,4,0,57

Line 40 should really be “CLS:PRINT:EXEC 16128”, otherwise the user would have to make sure they weren’t at the end of the screen when they ran it, since the next output would scroll and overwrite whatever the program POKEd to the top left of the screen.

Is this good enough?

Do you have better ideas?

Please share your thoughts in the comments.

Until next time…

Typing in an un-typable BASIC program…

In a recent article here, I explored the Logiker Vintage Computing Christmas Challenge 2021. The goal was to create a BASIC program that displayed a test Christmas tree:

The discussion continued in a follow-up article I posted a bit later. Check out those two postings, as well as the comments, for the full discussion. In the end, I used a suggested approach of encoding the length of each line (the number of characters to print as a centered string) in ASCII, so an ‘A’ represented one, ‘B’ would be two, and so on. To convert ASCII to the values needed, the program would do a subtract 64:

0 FORC=1TO14:W=ASC(MID$("ACEGCGKOEKQWCC",C))-64:PRINTTAB(16-W/2)STRING$(W,42):NEXT

On May 30, Sebastian T posted a new comment to that article with a suggestion that would reduce this program’s 64-byte size to 61 by getting rid of the “-64” bytes:

It would be nice to get rid of the “-64” part after the ASC() function, as this would save another 3 bytes, reducing the program size from 64 to 61 bytes.

All you have to do is to replace the characters inside the string with lower ASCII values just as needed, like &H01, &H03, &H05 and so on.

– Sebastian T. (5/30/2022)

Indeed, if instead of having “A”, “B”, “C”, etc. you could put in the raw bytes of 0, 1 and 2, that subtraction would not be needed. It is possible to alter BASIC programs to do this, but they create programs that are impossible for a user to type in from a listing. (I experimented with such a trick when I was working on a BASIC Pac-Man program.)

Or so I thought. Sebastian continued with a very clever approach/solution to typing in an un-typeable program:

You cannot use the keyboard to edit the string in this way, but I did write some self-modifying code that replaced the characters directly in program memory. After this, you can erase the extra code and you are left with a fully functional version that is 61 bytes long.

– Sebastian T. (5/30/2022)

What an interesting approach! Provide a fully typeable BASIC program which contains self-modifying code. Run the routine that self-modifies, then delete the self-modifying routine and save out the final program. Here is Sabastian’s example:

0 FORC=1TO14:W=ASC(MID$(“ACEGCGKOEKQWCC”,C)):PRINTTAB(16-W/2)STRING$(W,42):NEXT
10 ‘
20 ‘FIND FIRST STRING IN THE BASIC PROGRAM MEMORY AND HACK IT!
30 S=PEEK(25)256+PEEK(26) ‘START OF BASIC CODE IN MEMORY
40 E=PEEK(27)256+PEEK(28) ‘END OF BASIC CODE IN MEMORY
50 FOR A=S TO E
60 IF F THEN READ V : IF V<0 THEN END ELSE POKE A,V
70 IF PEEK(A)=34 THEN F=-1 ‘TOGGLE FLAG UPON REACHING THE FIRST QUOTATION MARK
80 NEXT A
90 DATA 1,3,5,7,3,7,11,15,5,11,17,23,3,3,-1

Sebastian concludes:

Run it once, then do a DEL 10-90. Run it again to verify it still fulfills the original task, and it does!

Some comments:

1) All tests done in XROAR 1.0.1 emulating a COCO 1 with 32k RAM.

2) I removed the “-64” before modifying the string, but you can also do it afterwards, even though the EDIT function will behave a bit funny while skipping over the non-printable characters in the string constant, but it works. I tested both ways.

3) If one of the characters had been 0 or 34 (quotation mark) this would have not worked. But this was not the case, so I got away with this. I’m not sure if there are other forbidden characters to consider, if somebody knows about this please comment.

4) In order to make sure this was still a valid program, I CSAVE’d it in a virtual cassette file, did a cold restart and reloaded the saved program. It works!

5) If you LIST the program, you’ll see a supposedly empty string inside the MID$() function, however it is not empty! I wrote a small memory monitor to verify this.

6) RENUM function won’t work now, though.

In summary, this trickery allows reduction of the final program size from 64 to 61 bytes.

Best regards!

– Sebastian T. (5/30/2022)

What an interesting approach. One can now type in a longer BASIC program, run it, DELete some lines, then end up with a smaller final program.

For BASIC programmers who used modified code for things like this, I suppose they were doing something like this. There would be a master program that could be edited and worked on, with routines at the end to modify strings and such, and then after the modification was ran, a second copy would be saved that could not be easily edited later.

And now we have a two-step way of creating a 61 byte version of that program.

Nicely done, Sebastian!

Until next challenge…

Stranger Things 2 and BASIC and Color BASIC

Updates:

  • 4/26/2022 – Added note about IF/ENDIF via comments. Added link to YouTube video of the segment.
  • 4/27/2022 – Updated screen shots with images taken from the YouTube video (linked in this posting).

With the season four of Stranger Things coming to Netflix soon, I have been re-watching the first three seasons.

Season one is set in 1983, and one of the characters (Joyce Byers, played by Winona Ryder) is shown working at a local general store. We see that there is a Radio Shack next door to it. We also see Radio Shack walkie talkies featured in the episodes.

In Season two we meet Joyce’s boyfriend, Bob Newby (played by Sean Astin). He works at that next-door Radio Shack. There is even one scene that shows him at work, though the environment is unlike any Radio Shack I ever saw.

In season two episode eight (“The Mind Flayer”), there is a need for them to restart a computer system. Radio Shack Bob says someone needs to know BASIC to do this. (Oh, really, writers?) This leads to a scene where Bob gets the system going by … typing in a BASIC program.

Here is the clip that someone posted to YouTube. (It starts around the 15:57 mark of the full episode.)

https://youtu.be/2bRAvxSSzEU

Here is a screen shot of the code:

Stranger Things S2E8 – Around 15:57 timecode.

It is some form of BASIC I am unfamiliar with. It allows defining variables as types (INTEGER in this case) and also appears to support functions with parameters similar to C. I am unsure if this is just Hollywood hookum or if there was actually a BASIC like this that existed in 1984 when Season 2 is set.

Here is a close-up of the code taken from the YouTube video (and apologies for the camera photo of a TV screen — Netflix blocks taking screen shots in their apps).

Stranger Things S2E8 – Around 15:57 timecode. (enhanced)

Update: Did you notice the end of line 70? You can see the lettering of the last word on TOP of the frame of the monitor. I never noticed that when watching it in full speed. The screen was probably added later as a special overlay effect and they didn’t take time to crop it in fully. But I digress…

Either the programmer used a bunch of memory typing in all the spaces to make it look nice, or this version of BASIC has pretty-output like Microware BASIC09 does.

I typed it in, trying to replicate the spacing…

10 DIM FourDigitPassword:INTEGER
20 FOR i = 0 TO 9
30     FOR j = 0 TO 9
40          FOR k = 0 to 9
50                FOR l = 0 TO 9
60                      FourDigitPassword = getFourDigits (i,j,k,l)
70                      IF checkPasswordMatch(FourDigitPassword) = TRUE THEN
80                              GOTO 140
90                      END
100                NEXT l
110          NEXT k
120     NEXT j
130 NEXT i
140 PRINT FourDigitPassword
150 END

Looking at this, I can now say it was a programmer adding the spaces. They do not follow a consistent tab value. Line 30-40 tabs in 5 spaces, then line 40-50 tabs in 6. My OCD is unhappy.

Line 10 creates a variable called FourDigitPassword. This tells us that this BASIC allows mixed case variable names. It also either support long variable names, or is like Microsoft Color BASIC where you can type them in, but the interpreter only honors the first two characters (or however many this version of BASIC supports).

This variable is declared as an INTEGER, whatever data type that is. Since an integer is a number, this four digit password must be like a PIN, consisting of numbers only. Since 0000 to 9999 is to big to fit in a byte (0-255), we’ll say INTEGER is a 16-bit value (0-65535).

Update: The Microsoft BASIC I am used to treat every numeric variable as a floating point value. I now recall that Commodore 64 had a way to declare a variable as an integer (non floating point). Perhaps that is what this is? (Though, they should have also declared I, J, K and L as INTEGERs too since none of this uses floating point values…)

Lines 30-50 are four FOR/NEXT loops using variables i, j, k and l. that cycle through 0-9 for each variable, representing each of the four digits of the password. Note that these variables are not declared in a DIM statement. Perhaps the default variable data type is a BYTE unless you otherwise specify?

In line 60, a function “getFourDigits” is called with each of the four variable values, returning something to variable “FourDigitPassword.” This looks like it would take four values like 1, 2, 3 and 4 and return them as an integer of 1234. So far, so good.

Line 70 is where things get strange. It calls a function “checkPasswordMatch” passing it this newly created integer value. If the function returns TRUE, it is intended to GOTO line 140 and print out the valid password, then end. However, since the GOTO is on a line starting with a new line number, I expect it would at the end of line 70 since nothing is after the THEN.

Let’s assume this weird BASIC will just continue parsing for more tokens on the next line, treating it as the “THEN” clause. If the compare was not valid, though, what would it do? Skip the next line? This is problematic.

Line 90 has an END, which would be problematic in BASIC. At this point, after the first unsuccessful check, this code would stop running.

Update: As noted in comments to this article by William A. and Lee, there were BASIC variants that used IF and ENDIF. If we treat that END to be intended as ENDIF, this code makes sense (but would still be a typo that would stop the code from running as presented

Conclusion: This program cannot work.

Line 100-130 are the NEXTs for the FOR loops.

Assuming functions work the way they appear and do what I assume they are meant to do, this appears to be a brute for password cracker, trying every combination of 0000 to 9999.

Make BASIC less strange…

Let’s make a real version! Here is my edit for Microsoft Color BASIC:

0 REM STRANGE.BAS
10 DIM PW
15 TP=1234
20 FOR I = 0 TO 9
30 FOR J = 0 TO 9
40 FOR K = 0 TO 9
50 FOR L = 0 TO 9
60 PW=I*1000+J*100+K*10+L
70 IF PW=TP THEN GOTO 140
100 NEXT L
110 NEXT K
120 NEXT J
130 NEXT I
140 PRINT PW
150 END

I had to change all the variables to UPPERCASE, and then shortened “FourDigitPassword” to just PW. I could have called it FOURDIGITPASSWORD since BASIC would still honor the first two letters (FO), but I find that a bad practice since it can lead to hard to track down errors latter (say, if you later used a variable called FOREVER or anything else that started with FO, thinking it was a unique variable while BASIC thought it was the same as FOURDIGITPASSWORD).

Since I do not have functions, I decided to just make a target password variable (TP) that will be the password to try to guess. I added this in LINE 15.

Line 20-50 are the same as the original program, just without the tabs (since my CoCo’s screen is only 32 characters wide and it would look messy).

In line 60, instead of calling a function that creates FourDigitPassword (PW) from four separate variables, I just build it myself. I multiply each digit out to turn 1,2,3,4 in to 1*1000 + 2*100 + 3*10 +4 (which is 1234).

Line 70 just compares the generated PW variable to the target TP variable. Again, no functions, so I just do it manually. I moved the “GOTO 140” to the end of that line (but it didn’t actually need the GOTO keyword after THEN).

I removed line 80 and 90 (since 80 is now at the end of 70).

Lines 100-150 are the same as the original program, except using uppercase and shorter variable names. Here is what it looks like, perfectly fitting a 32×16 CoCo screen. (I’ll even use the INVERSE VIDEO mode in tribute to Stranger Thing’s “upside down”):

Stranger Things Color BASIC

If I run this, it will grind away for quite some time before finally cracking the password and printing “1234”…

It would take far longer if the target password had been 9999, but hey, it works!

Make BASIC less slow…

There are some simple ways this could be sped up, such as combining lines, and removing the variables after each NEXT (so BASIC doesn’t have to look them up each time). And, the use of a variable for each of the four digits and creating an integer seems a bit pointless, since the function that checks for a match just does so with a single integer value. This whole thing could be turned in to…

0 REM STRANGE.BAS
10 DIM PW
15 TP=1234
20 FOR I = 0 TO 9999
70 IF PW=TP THEN GOTO 140
130 NEXT I
140 PRINT PW
150 END

Since I cannot resist a BASIC benchmark opportunity, I set the target password to 9999 and ran the first version. I cleared the timer (TIMER=0) at the start and printed it out just after it prints the result. The first version shows 12609.

Then I did the same with the second version, and it shows 277. (And that could be made a bit faster by removing spaces and combining lines — down to 263 in a quick test I did.

Poor Bob could have saved alot of time with that trick. It might have even saved his life ;-) (Do I need to give a spoiler warning for a show that aired in 2017? If so, spoiler warning!)

Until next time, stay strange!

BASIC and Google’s 25 horses interview question

When I loaded YouTube recently, one of the suggested videos was entitled “How To Solve Google’s 25-Horses Interview Question” by MindYourDecisions. The video cover image contained the text:

“What is the best way to find the 3 fastest horses? You can race 5 horses at a time, but you do not have a watch.”

– MindYourDecisions on YouTube

I did not watch the video since I thought this might be a fun exercise in BASIC. Instead, I fired up the excellent XRoar emulator and began writing a simple program that raced horses.

I started with an array big enough to hold the speed of 25 horses:

DIM H(24)

In Color BASIC, arrays are base-0, so that represents H(0) to H(24).

Next I initialized the array with a unique speed value by simply going through the loop and assigning each horse a speed of 0 to 24:

FOR I=0 TO 24:H(I)=I:NEXT

My next step was to randomize the entries, so I looked back on an earlier article I posted about Random BASIC shuffling. I implemented the suggested from James Jones to swap values in this array:

FOR I=0 TO 24
IF I/5=INT(I/5) THEN PRINT
J=I+INT(RND(25-I)-1)
T=H(I)
H(I)=H(J)
H(J)=T
PRINT H(I);
NEXT

Now I had an array of 25 horse speeds — H(0) to H(24) — that contains a random selection of values 0 (slowest) to 24 (fastest).

If you wanted to just find the fastest horse, you could simply scan the array and remember the fastest entry you found. At the end of the scan, you know the fastest horse. Something like this:

FH=-1:FS=-1
FOR I=0 TO 24
IF H(I)>FS THEN FH=I:FS=H(I)
NEXT
PRINT "FASTEST HORSE IS";FH

…but since this question requires racing no more than five horses at a time, I had to split that up in to code that would run five races of five horses, then a sixth race that raced the winners of each of the five races.

This is not the solution to the question, but it was a fun exercise. Here is the messy program I came up with. It will first print out the speeds of all 25 horses (five per line, matching how they will be raced) and then run the five races and final race of the winners:

0 ' HORSES1.BAS
1 '
2 ' 25 horses
3 ' Race up to 5 at a time
4 ' Find the fastest horse
5 '
10 ' H(x) - horse speed
15 DIM H(24)
20 '
21 ' Initialize each speed
22 '
25 FOR I=0 TO 24:H(I)=I:NEXT
30 '
31 ' Randomize
32 '
35 FOR I=0 TO 24
40 IF I/5=INT(I/5) THEN PRINT
45 J=I+INT(RND(25-I)-1)
50 T=H(I)
55 H(I)=H(J)
60 H(J)=T
65 PRINT H(I);
70 NEXT
75 PRINT:PRINT "RACE!"
100 '
101 ' Find fastest horse
102 '
105 DIM FH(4)
110 '
111 ' Race five sets of five
112 ' FH(x) - fastest horse
113 ' FS(x) - and its speed
114 '
115 FOR R=0 TO 4
120 FH(R)=-1:FS=-1
125 PRINT R;"-";
130 FOR I=R*5 TO R*5+4
135 PRINT I;
140 IF H(I)>FS THEN FH(R)=I:FS=H(I)
145 NEXT
150 PRINT "=";FH(R)
155 NEXT
160 '
161 ' Race the five winners
162 '
165 FH=-1:FS=-1
170 PRINT " F -";
175 FOR I=0 TO 4
180 PRINT FH(I);
185 IF H(FH(I))>FS THEN FH=FH(I):FS=H(FH)
190 NEXT
195 PRINT "=";FH
200 PRINT "WINNER IS HORSE";FH
500 END

And the result (also messy) looks like this:

Color BASIC program to find the fastest of 25 horses, when racing no more than five at a time.

But this isn’t the solution we are looking for.

The question was what is the fastest way to find the fastest three horses. I found the fastest by running six races. I expect the solution is simple, but I do not know it.

I thought I’d share this here and see if anyone else wants to work on it.

Any takers?

My 1987 CoCo ASCII to Atari ATASCII converter

It seems like only yesterday that you had a dozens of choices in what computer you could buy. Most were not compatible with the others — and we liked it that way. Software companies, however, probably didn’t. To reach the largest market, they had to write their program multiple times for different systems. My Radio Shack Color Computer, sadly, did not get many of these official ports.

Side note: For a nice list of some of the official ports we actually did get, see this listing by Curtis Boyle.

Many things we take for granted today — such as sending text from one computer to another (via e-mail, text, etc.) — were not as simple back then. Not all systems used an industry standard character set, meaning the numeric code that represented a character on one system, might represent a different character on another.

The CoCo used ASCII – “American Standard Code for Information Interchange.” The defined numeric values represented the following characters in this clear and easy to understand chart:

ASCII table from https://en.wikipedia.org/wiki/ASCII

If that chart doesn’t help, you are not alone. Just know that characters 0 to 127 were all defined to represented a standard set of letters, numbers, punctuation, symbols and control codes (such as 8 being BACKSPACE, 13 being CARRIAGE RETURN, etc.).

System like the Atari 400/800 and Commodore PET/VIC-20/64/etc. included non-ASCII graphical symbols in their character set. Each of these systems came up with their own standard — PETSCII from Commodore (which originated on the Commodore PET in 1977), and ATASCII from Atari (which originated on the Atari 400/800 in 1979).

Before WWW there was BBS

One of the first things I ever did with a computer was use one with a modem to dial other computers over a land line telephone. (Kid’s, ask your parents…) Folks would run Bulletin Board System software that let others call their computer and post messages for other folks to read who called in later.

This presented a problem. If the character sets were different between computers, how could an ASCII CoCo user dial in to an Atari ATASCII system or a Commodore PETSCII system?

To solve this problem, some BBS programs on the non-ASCII computers would first ask you if you wanted ASCII or ATASCII (or PETSCII or whatever). For ASCII users, the BBS would then translate the character codes.

Not all systems did this, of course. There were plenty of Commodore-only and Atari-only systems that made use of the extended character set to draw fancy menus and screens that ASCII computers couldn’t view.

However, the modem “terminal programs” that non-ASCII systems ran usually had an ASCII translation mode built in. Thus, a Commodore or Atari user could call any ASCII BBS. While I am sure they existed, I never did see a terminal program for my ASCII CoCo that let it call an ATASCII or PETSCII-only system. (TwilightTerm by SockMaster is similar, allowing a CoCo 3 to view the IBM PC ANSI character set and colors.)

When I lived in Lufkin, Texas, one of the local BBSes was running on an Atari 800 (via BBS Express software) and allowed ASCII systems to call in. This was how I first learned about the differences in ATASCII versus ASCII.

Here is what ASCII characters 32-127 looked like on the CoCo 1 and 2 (characters 0-31 are control codes and such):

Radio Shack Color Computer 32-column text screen ASCII.

And here is the same set of characters on a CoCo 3 40-column screen with row and column numbers (since I had more screen room):

Tandy Color Computer 3 40-column text screen ASCII.

From wikipedia, here is what ATASCII looks like:

ATASCII from https://en.wikipedia.org/wiki/ATASCII

I think this table is much easier to read that the ASCII one, as long as you know hexadecimal.

Starting at character 32 (0x20) is a space, followed by special characters and the alphabet. Although there are some symbol differences (like the ^ on CoCo being a diamond on the Atari), the main letters, numbers and symbols are the same.

But, if I were to write up a text file and send it to the Atari BBS so they could post it, it would not work. ASCII uses 13 (CR, carriage return) as a line ending, but ATASCII uses 155 (ATASCII CR). If I translated line endings, and avoided using things like ^, brackets (or curly braces), etc., I could then have a text file the Atari BBS could use.

The amazing ASCII to ATASCII Convert program!

So I wrote a simple ASCII to ATASCII converter:

0 REM ASCII TO ATASCII CONVERT
1 REM BY ALLEN HUFFMAN
2 REM (09/02/87)
3 REM
5 CLEAR1000
10 CLS:PRINT@3,"ASCII TO ATASCII CONVERTER":PRINT@40,"BY ALLEN HUFFMAN":PRINTSTRING$(32,131)
15 PRINT@96,"ASCII FILE TO CONVERT:":LINEINPUT">";F1$:IFF1A$=""THEN15
20 PRINT@192,"NAME OF NEW FILE:":LINEINPUT">";F2$:IFF2$=""THEN15
25 PRINT@289,"CONVERTING ASCII TO ATASCII...":OPEN"I",#1,F1$:OPEN"O",#2,F2$
30 LINEINPUT#1,A$:PRINT@320,A$:PRINT#2,A$+CHR$(155);:IFEOF(1)=0THEN30
35 PRINT#2,CHR$(26);:UNLOAD
40 PRINT@422,"CONVERSION COMPLETE!":END

In line 15, it asks for an INPUT filename (F1$).

In line 20, it asks for an OUTPUT file name (F2$).

In line 25, it opens the first file as input (“I”) and the second file for output (“O”).

In line 30, it loops reading a raw line from the first file, displaying it on the screen (so the user can see what is going on), then writes the text out to the output file with a CHR$(155) at the end and NO ASCII carriage return (by using the semicolon). If end-of-file is not reached, it goes back to 30 to process the next line.

In line 35, it writes out a final CHR$(26) (control-Z) to the ATASCII output file — but I do not recall why. It then uses the UNLOAD command to close any open files.

I had to look up UNLOAD, as I had forgotten this existed. The description reads:

“Closes any open files on the disk in the drive you specify. If you do not specify a drive number, the com­puter uses Drive 0 (or the drive you specified in the DRIVE command).”

Disk Extended BASIC manual

Not much to it, but it worked and it let me write bulletins and such that I could upload to the Atari BBS.

I thought I would share this code in case any CoCo user out there needs to upload some text files to an Atari BBS.

Until next time…