Author Archives: Allen Huffman

About Allen Huffman

Co-founder of Sub-Etha Software.

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…

MacInvaders09 by Jamie Cho

In 1994, Sub-Etha Software released Invaders09 – a Space Invaders-style game for the CoCO 3.

The game was written in 6809 assembly, and ran under Microware OS-9 Level 2.

Jamie Cho took on the task of porting the game to the MM/1, a “CoCo 4” system that ran OS-9/68000.

Years later, he ported the MM/1 version to run on classic MacOS (on the original 68000 series of processors).

This led to the game being re-ported to the Mac OS for PowerPC, then Intel x86, and finally Apple’s ARM-based M1 series of processors.

Invaders 09 was originally written in 6809 assembly language for the Tandy Color Computer 3 running OS-9 Level 2. I ported it to C on the MM/1 running OS-9 68K sometime around 1995 or so and eventually to the Mac in the early 2000s or so. This means the game has successfully run more or less natively on 5 different platforms – the 6809, 68K, PowerPC, x86 and ARM. This also explains the kind of weird way the bitmaps are drawn to the screen…

– Jamie Cho on his GitHub page.

Downloading MacInvaders09

You can download the source code for the current release from his GitHub:

https://github.com/jamieleecho/MacInvaders09

If you just want to download the binary and play it, he has that available here:

https://github.com/jamieleecho/MacInvaders09/releases/tag/1.0.6

(Note, this leads to the current 1.0.6 build. If that link doesn’t work, check his main page for a later release.)

Running MacInvaders09

After opening the archive file, you will see the “MacInvaders09.app” application. If you try to run it, you will get this warning:

Click OK on that box to dismiss it. Go in to “System Preferences”, then in to the “Security & Privacy” section. It will look like this:

You can then click “Open Anyway” to allow this program to run. You should then see the same warning box, but now you can “Open” the program to run it.

Playing MacInvaders09

The game will open in a tiny size, matching its 1994 CoCo 3 release:

You can go full screen if you actually want to see it on a modern sized monitor:

Differences from the CoCo 3 original

Jamie’s port is more of an update/rewrite than a straight port. Trying to port 6809 assembly to C doesn’t make a lot of sense. Instead, the game graphics were brought over, and likely some of the logic. There are some significant differences:

  1. The laser shots were enhanced to vertical lines instead of just dots. Nice.
  2. There are new sound effects added. Also nice.
  3. The Power level seems to be missing. (I’m not sure if the game increases the number of simultaneous shots you get as levels progress.)
  4. There is no joystick support.
  5. As far as I know, the undocumented “cheat mode” is not implemented, nor is the “zoop mode” which made the game play too fast on the CoCo 3.
  6. The Invader graphics are upside down from the original. You will notice they have some dots at the top of them. In the CoCo 3 original, I looked for a “hit” by seeing is my bullet dot encountered a non-empty screen byte. I put those dots there just to give it a target at the bottom of the invader. I didn’t like the way they looked, but I didn’t know a better way to do it at the time.
  7. The UFO in the original would always drop a bomb the moment it was above the player, forcing the player to NEVER sit still as the UFO passes. In this version, the UFO seems to drop bombs in a more random pattern.
  8. The UFO bombs can be stopped by the shields. In the original, the UFO bomb would go THROUGH the shields, forcing the player to not hide under shields. I always through it was unfair that the player could just sit in one spot and nothing could hurt them there.
  9. The player is allowed to move while shooting. In the original, any time you fired the laser, your movement was stopped. This prevented the player from doing “run and shoot” moves, making it a bit harder since you couldn’t take a pot shot as you scooted across the screen.
  10. The damage taken to the shields is different. In the original, the blocks kind of pushed outwards, with the first hit taking a small dent out of the shield, and another hit in that location making the dent wider. This version takes longer bits out of the shield, matching the longer laser bolts.
  11. I think the Invaders may move down at a slower rate. It seems much easier to clear them all out and have one left that is still very high, but I’d have to play them side by side to really compare that.

I am quite impressed and honored that Jamie has taken time to do these ports. I can’t express how thrilled I was the first time I saw this on an MM/1, and even more so on an Apple Macintosh.

Thank you, Jamie Cho, for your efforts to make defending the Earth a cross-platform activity!

Old posts should be working better now.

I did some updates to this site a year or so back, and found out it broke images in all my older posts. I think I fixed those last week, so if you find any broken images, please drop a comment on that post so I can look at them.

Code listings are still quite busted, and I have to fix them manually — if I can. Some seem to have lost data and I have no idea what was there. But, if you catch any of those that are broken, leave a comment as well and I’ll fix if I can.

Thanks!

Drobo “saves the day”?

I hate it when this happens… It looks like a 4TB drive in my 5-bay Drobo has gone out. Drobo cannot detect it. I have dual-drive redundancy enabled, so two drives can fail and I’d still have my data… Fortunately.

Drobo 5C showing a 4TB drive failure.

Hopefully, I won’t have two drives fail between now and the time my replacement drive arrives. :)

On the plus, drive prices have dropped since I bought these drives in 2019. I’ll begin the process of upgrading drives to 6TB models over coming months, money permitting.

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…

Robert Gault’s EDTASM+for native CoCo 6809/6309 assembling.

Radio Shack introduced the Color Computer in 1980. It came 4K or RAM, and Microsoft Color BASIC in the ROM. It could be expanded to 16K RAM, which allowed adding a second ROM for Extended BASIC. A plug-in disk interface cartridge came later, with it’s own ROM containing Disk BASIC.

I’ve often wondered what Microsoft used to write the CoCo BASIC ROMs in.

EDTASM+

Around 1982, Radio Shack released the EDTASM+ ROM-Pak for the Color Computer. It was a 6809 assembler for machine language, as well as a debugger. It could load and save files (source code and final binaries) to cassette tape.

There was also Disk EDTASM+, which added some extra features — though the most important one was probably that it could load and save to a disk drive, making that process far faster.

Someone put up a nice EDTAM+ information page on my CoCoPedia.com website.

Since Microsoft created EDTASM, I suspect it may have been (or was at least based on) the tool they used for writing the Color Computer ROMs.

If you want to see it in action, head over to the JS Mocha CoCo emulator where you will find it available from a menu:

http://www.haplessgenius.com/mocha/

The EDTASM+ ROM-Pak and Bill Barden’s Color Computer Assembly Language Programming book where how I learned 6809 assembly. I later used Disk EDTASM+.

EDTASM++?

While the CoCo 1 and 2 were basically the same machine, just with redesigned circuit boards and enclosures, the 1986 CoCo 3 was quite different. It could operate in a double speed more, and provided up to 80 columns of text versus the original CoCo’s 32 columns. It also came with 128K — double what the CoCo 1/2 could handle — and could be expanded to 512K (though third party folks figured out how to do 1 an 2 megabyte upgrades).

Unfortunately, Radio Shack never released an update to the EDTASM+ ROM-Pak or disk software. It was still limited to the small memory and screen size of the original 1980 CoCo hardware.

Folks came up with various patches. I had one that patched my Disk EDTASM+ to run in 80 columns on the CoCo 3, in double speed more (faster assembling!) while setting the disk drive step rate to 6ms. It was a much nicer experience coding with long lines!

After this I moved on to OS-9, and used the Microware assemblers (asm and rma) from OS-9 Level 1 and Level 2. I am not sure I touched EDTASM+ again until I played with it on JS Mocha, decades later.

Hitatchi 6309

Hitcachi made a clone of the 6809. This replacement chip had some undocumented features such as more registers and more op codes. EDTASM+ couldn’t help with that, but there were some OS-9 compilers that were updated to support it.

That’s when folks like Robert Gault came to our rescue with enhancements for the original EDTASM+. Robert added support for the 6309, and many new features — including CoCo 3 compatibility.

His EDTASM+ looks like this on a CoCo 3 in 80 column mode:

Robert Gault’s EDTASM+ update.

If you notice the copyright date, you’ll see he has continued to update and support it. Today he offers it in a variety of versions that run on the original CoCo 1/2, a CoCo 3, certain emulators, RGB-DOS support (for hard drive users), CoCoSDC (the modern SD card floppy replacement) as well as supporting things like DriveWire.

You can pick up your own copy for $35 as follows:

EDTASM6309 $35
Robert Gault
832 N.Renaud
Grosse Pointe Woods, MI 48236
USA
e-mail:	robert.gault@att.net

There are a number of new features added. Here is the list provided in the README.txt file:

CHANGES TO EDTASM (Tandy Version)
1) Tape is no longer supported; code has been removed.
2) Buffer size increased to over 42K bytes.
3) Directory obtainable from both Editor and ZBUG; V command.
4) Multiple FCB and FDB data per line.
5) FCS supported.
6) SET command now works properly.
7) Screen colors remain as set from Basic before starting EDTASM.
8) Symbol table printed in five names per line on Coco3.
9) On assembly with /NL option, actual errors are printed.
10) Warning error on long branch where short is possible.
11) ZBUG now defaults to numeric instead of symbolic mode.
12) RGB DOS users now have support for drive numbers higher than 3.
13) Hitachi 6309 opcodes are now supported for both assembly and disassembly
including latest discoveries.
14) HD6309 detection is included and if present incorporates a ZBUG error trap
for illegal opcodes and enables monitoring and changing the E,F,V registers
from ZBUG.
15) Coco 3 users can now safely exit to Basic and use their RESET button from
EDTASM.
16) Keyboard now has auto repeat keys when keys are held down.
17) Lower case is now supported for commands, opcodes, options, and symbols.
Take care when loading or saving files or using symbols, ex. NAME does not
equal name, \.A not= \.a, etc.
18) Local names are now supported. Format is A@-Z@ and a@-z@ for 52 local
symbols. New sets of locals are
started after each blank line in the source code. Local
symbols do not appear in or clutter symbol table.
19) Local symbols can only be accessed from ZBUG in expanded form:
ex. A@00023  not A@.
20) Now reads source code files that don't have line numbers. Writes normal
source files with line numbers ( W filename ) or without line numbers
( W# filename ).
21) Macro parameters now function correctly from INCLUDEd files.
22) While in the Editor, the U key will backup one screen in your source file.
23) DOS.BAS can be used to program the F1 and F2 keys on a Coco3. See below.
24) Coco3 WIDTH80 now uses 28 lines of text.

Coco 1&2 versions do require 64K RAM, the Coco 3 version will work with 128K
of RAM. You can assemble 6309 code even if your Coco has a 6809 cpu.

It also adds some new commands:

V - obtains a directory from either Editor or ZBUG modes.
U - scrolls backwards through source code.
FCS - is used exactly like FCC but automatically add $80 to the last character
in the string.
FCB, FDB - for multiple entries per line entries should be seperated by a
comma. Make sure that the comment field for that line DOES NOT CONTAIN ANY
COMMAS or an error will result.
New ‘V’ directory command in Robert Gault’s EDTASM+ update.

If you are wanting to do some CoCo assembly language programming, I highly recommend you sending $35 to Robert and pick up a copy of his version. EDTASM+ is tricky to learn, and his updates make it a bit less tricky.

And tell him Allen sent ya.

Until next time…