Category Archives: Retro Computing

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…

CoCo and 16-bits

When dealing with bits in Color BASIC, we have AND, OR and NOT. Unfortunately, we can really only use these on values 15-bits or less. For example, here is a table represent various 8-bit values in the range of 0-255:

Dec    Hex   Binary
-----  ----  --------
    0    00  00000000
    1    01  00000001
    2    02  00000010
    4    04  00000100
    8    08  00001000
   16    10  00010000
   32    20  00100000
   64    40  01000000
  128    80  10000000
  255    FF  11111111

We have no problem using 8-bit values with standard Color BASIC. Here is my routine that will print out the bits of any 8-bit value:

0 REM 8BITS.BAS
10 DIM BT(7):FOR BT=0 TO 7:BT(BT)=2^BT:NEXT
20 INPUT "VALUE     ";Z
30 GOSUB 500:GOTO 20
500 REM SHOW Z AS BINARY
510 FOR BT=7 TO 0 STEP-1
520 IF Z AND BT(BT) THEN PRINT "1"; ELSE PRINT "0";
530 NEXT
540 PRINT Z:RETURN

Here is a program using that routine that will print out a similar table:

0 REM 8BITTABL.BAS
10 DIM BT(7):FOR BT=0 TO 7:BT(BT)=INT(2^BT):NEXT
20 PRINT "DEC    HEX   BINARY"
30 PRINT "-----  ----  --------"
40 FOR I=0 TO 7:Z=INT(2^I)
50 GOSUB 100
60 NEXT
70 Z=255:GOSUB 100
80 END

100 REM PRINT TABLE ENTRY
110 PRINT USING"#####    ";Z;
120 IF Z<&H10 THEN PRINT "0";
130 PRINT HEX$(Z);"  ";
140 GOSUB 500
150 RETURN

500 REM SHOW Z AS BINARY
510 FOR BT=7 TO 0 STEP-1
520 IF Z AND BT(BT) THEN PRINT "1"; ELSE PRINT "0";
530 NEXT
540 PRINT:RETURN

When I started experimenting with bits like this, I tried to modify my routine to work with 16-bit values. It did not work:

0 REM 8BITTABL.BAS - DOES NOT WORK!
10 DIM BT(15):FOR BT=0 TO 15:BT(BT)=INT(2^BT):NEXT
20 PRINT "DEC    HEX   BINARY"
30 PRINT "-----  ----  ----------------"
40 FOR I=0 TO 15:Z=INT(2^I)
50 GOSUB 100
60 NEXT
70 Z=255:GOSUB 100
80 END

100 REM PRINT TABLE ENTRY
110 PRINT USING"#####  ";Z;
120 IF Z<&H10 THEN PRINT "0";
121 IF Z<&H100 THEN PRINT "0";
122 IF Z<&H1000 THEN PRINT "0";
130 PRINT HEX$(Z);"  ";
140 GOSUB 500
150 RETURN

500 REM SHOW Z AS BINARY
510 FOR BT=15 TO 0 STEP-1
520 IF Z AND BT(BT) THEN PRINT "1"; ELSE PRINT "0";
530 NEXT
540 PRINT:RETURN

A bit of investigation revealed that AND could not operate on values greater than 32767 (&H3FFF in hex):

I did not understand why, but I expected it has something to do with integer values being treated as signed values, as if this was an INT16 (−32768 to +32767 range) rather than a UIN16 (0-65535 range).

rflberg to the rescue

I had recently posted a series of YouTube videos discussing bits in Color BASIC. My most recent one showed a program I wrote that demonstrated AND, OR and NOT operations:

The program I demonstrated looked like this:

0 REM ANDOR.BAS
10 DIM BT(7):FOR BT=0 TO 7:BT(BT)=INT(2^BT):NEXT
20 INPUT "VALUE     ";V
30 PRINT "(A/O/N)";
40 A$=INKEY$:IF A$="" THEN 40
50 IF A$="A" THEN M=0:PRINT "AND";:GOTO 90
60 IF A$="O" THEN M=1:PRINT "OR ";:GOTO 90
70 IF A$="N" THEN M=2:PRINT "NOT":GOTO 100
80 SOUND 1,1:GOTO 40
90 INPUT O
100 PRINT:PRINT "    ";:Z=V:GOSUB 500
110 IF M=0 THEN PRINT "AND ";:Z=O:GOSUB 500:Z=V AND O:PRINT "    ";:GOSUB 500
120 IF M=1 THEN PRINT "OR  ";:Z=O:GOSUB 500:Z=V OR O:PRINT "    ";:GOSUB 500
130 IF M=2 THEN PRINT "NOT ";:Z=NOT V:GOSUB 500
140 PRINT:GOTO 20

500 REM SHOW Z AS BINARY
510 FOR BT=7 TO 0 STEP-1
520 IF Z AND BT(BT) THEN PRINT "1"; ELSE PRINT "0";
530 NEXT
540 PRINT:RETURN

In the video I explain how it works, somewhat, but you will notice it works only on 8-bit values. Because I did not know a way to make it work.

However, in the comments, use rflberg left a few comments:

IF you want to see the full bits change the program to this:

10 DIM BT(15):FOR BT=0 TO 15:BT(BT)=2^BT:NEXT
501 IF Z<0 THEN PRINT”1″; ELSE PRINT”0″;
510 FOR BT=14 TO 0 STEP -1

rflberg (via YouTube)

I was intrigued. The modifications did not work for me, but a few additional comments help me understand the intent:

-1 is actually 1111111111111111 and 255 is 0000000011111111. It computes numbers -32768 to 32767. Negative numbers the most significant bit is a 1 and positive numbers is a 0.

-32768 is 1000000000000000 and 32767 is 0111111111111111

rflberg (via YouTube)

I experimented with this for awhile last night, and now I think I understand it. AND, NOT and OR allow you to pass in 0 to 32677 just fine. But, you can also pass in -32768 to -1 as well! It seems to be using the high bit (bit 15) to indicate a negative value. The explanation was to simply use negative values to make AND, NOT and OR see that bit.

The code modification would work if I passed in 0-32767 for the normal 15-bit range then -32768 to 1 to represent the high-bit range. I should be able to modify my routine to do this automatically.

I could use standard bit values for bits 0 to 14 (my BT array values of 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, and 16384, just like in the earlier table), and then have a special case for bit 15 — a value of -32768 — which I would have in the array as BT(15)=-32768.

Then, in the print bit routine I could check to see if the value was greater than 32767, and turn it in to a negative number by subtracting 65536. (i.e., 32767 would be fine, but 32768 would turn in to -32768).

Since I print out the integer value after the bit display, I decided to make a temporary (altered) variable Z2, and retain the user’s intended Z value. This means I could pass in 32768 and it would print 32768, but would be really using -32768.

I ended up with a minor modification to my program, giving me this routine that will display the bits of any 16-bit value (0-65535):

0 REM 16BITS.BAS
1 REM WORKS THANKS TO rflberg
10 DIM BT(15):FOR BT=0 TO 14:BT(BT)=INT(2^BT):NEXT:BT(15)=-32768

20 INPUT "VALUE     ";Z
30 GOSUB 500:GOTO 20
500 REM SHOW Z AS BINARY
505 IF Z>32767 THEN Z2=Z-65536 ELSE Z2=Z
510 FOR BT=15 TO 0 STEP-1
520 IF Z2 AND BT(BT) THEN PRINT "1"; ELSE PRINT "0";
530 NEXT
540 PRINT Z;Z2:RETURN

Using this updated routine, I modified my table printing program to handle 16-bits:

0 REM 8BITTABL.BAS
1 REM WORKS THANKS TO rflberg
10 DIM BT(15):FOR BT=0 TO 14:BT(BT)=INT(2^BT):NEXT:BT(15)=-32768
20 PRINT "DEC    HEX   BINARY"
30 PRINT "-----  ----  ----------------"
40 FOR I=0 TO 15:Z=INT(2^I)
50 GOSUB 100
60 NEXT
70 Z=65535:GOSUB 100
80 END

100 REM PRINT TABLE ENTRY
110 PRINT USING"#####  ";Z;
120 IF Z<&H10 THEN PRINT "0";
121 IF Z<&H100 THEN PRINT "0";
122 IF Z<&H1000 THEN PRINT "0";
130 PRINT HEX$(Z);"  ";
140 GOSUB 500
150 RETURN

500 REM SHOW Z AS BINARY
505 IF Z>32767 THEN Z2=Z-65536 ELSE Z2=Z
510 FOR BT=15 TO 0 STEP-1
520 IF Z2 AND BT(BT) THEN PRINT "1"; ELSE PRINT "0";
530 NEXT
540 PRINT:RETURN

Tada! Thanks for those great YouTube comments, I now have a workaround to doing bit detection on all 16 bits. Thank you very much, rflberg!

Tackling the Logiker Vintage Computing Christmas Challenge 2021 follow-up

Previously I discussed several approaches to printing out the Christmas tree pattern for this challenge.

The method that produced the smallest Color BASIC program looked like this:

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

That uses 66 byte of BASIC program space (though will use more RAM for strings as it runs).

It appears there are still optimizations to make! In the comments. Stewart Orchard pointed out this one:

Not a big gain but two bytes can be saved by removing the third argument from MID$().

The two argument version of MID$() returns the remainder of the string starting from the specified position, and ASC() returns its result based on the first character of its argument.

– Stewart Orchard

In Color BASIC, MID$ can accept three parameters:

Color Computer 3 BASIC Quick Reference Guide, page 11

If you had as string of 10 characters, and wanted to print the third character, you could do it like this:

A$="ABCDEFGHIJ"
OK
PRINT MID$(A$,3,1)
C

Stewart pointed out that if you left off the final parameter, it returns the rest of the string:

A$="ABCDEFGHIJ"
OK
PRINT MID$(A$,3,1)
CDEFGHIJ

Now that I look at it, it appears MID$ with two parameters is sort of like an inverted RIGHT$. MID$ would give you all the characters starting at the one you specify, and RIGHT$ gives you the number of ending characters you specify.

PRINT MID$(A$,3)
CDEFGHIJ

PRINT RIGHT$(A$,3)
HIJ

I don’t recall using MID$ like this, but I have simulated the same behavior using RIGHT$ like:

PRINT RIGHT$(A$,LEN(A$)-3+1)
CDEFGHIJ

PRINT MID$(A$,3)
CDEFGHIJ

I am a bit embarrassed to admit I think I even did something like this:

PRINT MID$(A$,3,LEN(A$)-3)
CDEFGHIJ

…but let’s not speak of that.

And if MID$ can work like RIGHT$, it can also work like LEFT$:

PRINT LEFT$(A$,3)
ABC

PRINT MID$(A$,1,3)
ABC

But I digress…

If MID$(A$,3,1) gives you one character starting as location 3, and MID$(A$,3) gives you all the characters starting at position 3, how does that work? Stewart explained that ASC will still work if you pass it a string since it only works on the first character of the string. It is even documented that way:

Thus, ASC(“HELLO”) produces the same result as ASC(“H”) — the ASCII value of letter H.

With that in mind, we can remove “,1” from the program and reduce it by two bytes:

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

64 bytes!

Thank you, Stewart, for leaving that comment.

Until next time…

Tackling the Logiker Vintage Computing Christmas Challenge 2021

Another programming challenge seems to be occupying folks this Christmas season:

http://logiker.com/Vintage-Computing-Christmas-Challenge-2021

The challenge is simple: Write a program that prints out a centered text Christmas tree using the same layout of their example program. I thought it would be fun to work through several methods to do this.

PRINT

We start with their simple example, which displays the centered tree using PRINT statements. Here is their example with spaces altered so it centers on the CoCo’s 32 column screen:

100 PRINT "               *
110 PRINT "              ***
120 PRINT "             *****
130 PRINT "            *******
140 PRINT "              ***
150 PRINT "            *******
160 PRINT "          ***********
170 PRINT "        ***************
180 PRINT "             *****
190 PRINT "          ***********
200 PRINT "       *****************
210 PRINT "    ***********************
220 PRINT "              ***
230 PRINT "              ***

386 bytes.

You will see one memory optimization they did was leaving off the ending quote. BASIC will stop parsing a quoted string if it reaches the end of a line, so the ending quote is only necessary if more statements follow the PRINT.

I do not know if the space between PRINT and the quote is required for the system they wrote this example, but on the CoCo we could remove it and save 23 bytes of program space right there.

Speaking of spaces, each space takes up a character. BASIC has a command that will jump forward a certain amount of spaces — TAB. It works like this:

PRINT TAB(5);"FIVE SPACES OVER"

From my test, it appears TAB takes up one byte(for the “TAB(” portion, then a byte for each digit, then a byte for the closing parenthesis. If tabbing a single digit of spaces, it takes three bytes of program space. Thus, any time we are spacing over more than 3 spaces, it would save memory to use TAB. Also, the semicolon between TAB and the item being printed is optional, so we can leave that out as well.

For the tree example centered on a 32 column screen, every line starts with at least 4 spaces, so using TAB should save memory. Let’s try this:

100 PRINT TAB(15)"*
110 PRINT TAB(14)"***
120 PRINT TAB(13)"*****
130 PRINT TAB(12)"*******
140 PRINT TAB(14)"***
150 PRINT TAB(12)"*******
160 PRINT TAB(10)"***********
170 PRINT TAB(8)"***************
180 PRINT TAB(13)"*****
190 PRINT TAB(10)"***********
200 PRINT TAB(7)"*****************
210 PRINT TAB(4)"***********************
220 PRINT TAB(14)"***
230 PRINT TAB(14)"***

279 bytes.

That looks icky, but produces the same result with less code space.

Pattern Matching

If we take a look at the tree design, we see it has a pattern to how each of the three seconds is printed:

           *            1
          ***           3
         *****          5
        *******         7
          ***           3
        *******         7
      ***********       11
    ***************     15
         *****          5
      ***********       11
   *****************    17
*********************** 23
          ***           3
          ***           3

It is drawing three sections, with the first expanding on each side by 1 (adding two asterisks to each row), then the next section expanded each side by 2 (adding four asterisks to each row), then the final section expands each side by 3 (adding six asterisks to each row).

There is probably a simple way to math it. But first…

Centering

Since one of the requirements is centering the tree on the screen, let’s look at how we center a string. To do this, we simply subtract the length of the string we want to center from the width of the string then divide the result by two:

A$="CENTER THIS"
PRINT TAB((32-LEN(A$))/2);A$

Or, to eliminate some parenthesis, we can start with half the width of the screen (the center of the screen) and subtract half the length of the string:

A$="CENTER THIS"
PRINT TAB(16-LEN(A$)/2);A$

With that out of the way, now we need to figure out how to draw the three triangle shapes of the tree, each time getting wider.

Numbers and patterns.

I decided to lookat the number sequences of each line length for each section of the tree (skipping the trunk lines).

Section 1: 1  3  5  7
Section 2: 3  7 11 15
Section 3: 5 11 17 23

I could easily create the first pattern by doing a FOR/NEXT loop with a step of 2:

FOR I=1 TO 7 STEP 2:PRINT I:NEXT

And then I could do 3 to 15 with a STEP 4, and 5 to 23 with a STEP 6.

FOR I=1 TO 7 STEP 2:PRINT I:NEXT
FOR I=3 TO 15 STEP 4:PRINT I:NEXT
FOR I=5 TO 23 STEP 6:PRINT I:NEXT

This would generate the number sequence I need, and then I could simply print a centered string of that many asterisks, and manually print the tree trunk at the end.

10 FOR I=1 TO 7 STEP 2:GOSUB 50:NEXT
20 FOR I=3 TO 15 STEP 4:GOSUB 50:NEXT
30 FOR I=5 TO 23 STEP 6:GOSUB 50:NEXT
40 I=3:GOSUB 50:GOSUB 50:END
50 PRINT TAB(16-I/2)STRING$(I,42):RETURN

127 bytes.

That’s much better! Though, I saved a bit more by combining lines, and I could save even more by removing spaces and combining lines further:

10 FORI=1TO7STEP2:GOSUB50:NEXT:FORI=3TO15STEP4:GOSUB50:NEXT:FORI=5TO23STEP6:GOSUB50:NEXT:I=3:GOSUB50:GOSUB50:END
50 PRINTTAB(16-I/2)STRING$(I,42):RETURN

(Compressed down to 94 bytes.)

Now we’re getting somewhere! But ignoring the compressing, can we do better than 127 using better math?

Math. Why did there have to be math?

I am slow and bad at math, but I am convinced there is a math solution to this, as well.

Let’s look at how the numbers for START value and STEP value work. I know there are going to be three sections of the tree, so it seems logical I’d start with something like this:

10 FOR I=1 TO 3
20 PRINT I*2-1
30 NEXT

That would print out 1, 3 and 5, which is the start width of each section of the tree. I could have also done the FOR loop using 0 to 2 and added 1 to get the same result:

10 FOR I=0 TO 2
20 PRINT I*2+1
30 NEXT

Maybe one will be more useful than the other depending on what comes next…

Once we know how wide a section starts, we need to know how much the length increases for each line.

Section 1 increases by 2 each row, section 2 increases by 4 each row, and section 3 increases by 6 each row. That is a pattern of 2, 4, 6. It looks like I could get those values by taking the section (1 to 3) and multiplying it by 2. This will print 2, 4, 6:

10 FOR J=1 TO 3
20 PRINT J*2
30 NEXT

I am bad with math, as I mentioned, but I was able to work out that taking a value of 1 to 4 (for each layer of a section) and multiply that by the section (1 to 3), and subtracting one, I could get this:

10 FOR I=1 TO 3
20 FOR J=1 TO 4
30 PRINT J*I*2-1
60 NEXT
70 NEXT

The math looks like this:

 J * I       * 2       - 1
-------   -------   -------
(1 * 1) = (1 * 2) = (2 - 1) = 1
(2 * 1) = (2 * 2) = (4 - 1) = 3
(3 * 1) = (3 * 2) = (6 - 1) = 5
(4 * 1) = (4 * 2) = (8 - 1) = 7

(1 * 2) = (2 * 2) = (4 - 1) = 3
(2 * 2) = (4 * 2) = (8 - 1) = 7
(3 * 2) = (6 * 2) = (12 - 1) = 11
(4 * 2) = (8 * 2) = (16 - 1) = 15

(1 * 3) = (3 * 2) = (6 - 1) = 5
(2 * 3) = (6 * 2) = (12 - 1) = 11
(3 * 3) = (9 * 2) = (18 - 1) = 17
(4 * 3) = (12 * 2) = (24 - 1) = 23

…and that gets our number sequence we want:

Section 1: 1  3  5  7
Section 2: 3  7 11 15
Section 3: 5 11 17 23

To print the row of asterisks, I use the STRING$ function. It takes the number of characters to print, then the ASCII value of the character. The asterisk is ASCII character 42 so it I wanted to print ten of them I would do:

PRINT STRING$(10,42)

I can now print the layers of the tree like this:

10 FOR I=1 TO 3
20 FOR J=1 TO 4
30 W=J*I*2-1
50 PRINT TAB(16-W/2);STRING$(W,42)
60 NEXT
70 NEXT

But that still doesn’t handle the “trunk” at the bottom of the tree.

Since the trunk does not follow any pattern, I will simply treat it like an extra section, and increase the section loop (FOR I) by one, and add an IF that says if we are on that layer, the width will be 3. Then, since I don’t want this section to be four layers thick, I can use a second IF after the one that checks for the fourth section to know when to exit (checking for J to be larger than 2):

10 FOR I=1 TO 4
20 FOR J=1 TO 4
30 W=J*I*2-1
40 IF I>3 THEN W=3:IF J>2 THEN END
50 PRINT TAB(16-W/2);STRING$(W,42)
60 NEXT
70 NEXT

162 bytes.

Ah, well, being clever seems to have increase beyond the 127 bytes of just using three FOR/NEXT loops. I suspect it’s the overhead of the way I am trying to print the trunk section. Indeed, without it (like 40, above, and adjusting the I loop to 3) makes it 125.

Sometimes being clever doesn’t help. Even without trying to be clever about the trunk section, the “W=J*I*2-1” stuff takes up as much space as just doing another FOR/NEXT and a GOSUB to print the line.

DATA

We could just have a table to line lengths, and do it like this:

10 READ W
20 IF W=0 THEN END
30 PRINT TAB(16-W/2);STRING$(W,42):GOTO 10
40 DATA 1,3,5,7,3,7,11,16,5,9,13,17,3,3,

98 bytes.

Not bad at all! And that is before compressing it to fewer lines. Here’s a 79 byte attempt, removing spaces, removing the semicolon, altering the logic just a bit to be on LINE 0 (so you can just say GOTO) and changing the IF so there is no END token:

0 READW:IFW>0THENPRINTTAB(16-W/2)STRING$(W,42):GOTO:DATA1,3,5,7,3,7,11,16,5,9,13,17,3,3,

Another “DATA” technique which I have seen Jim Gerrie use is to simply encode the line lengths as characters in a string, and use that as data. For example, ASCII “A” (character 65) will represent 1, “B” will represent 2, and so on. Make a string of the appropriate characters and retrieve them using MID$() and get the ASCII value using ASC() then subtract 64 from that value and you have something like this.

10 FOR C=1 TO 14
20 W=ASC(MID$("ACEGCGKOEKQWCC",C,1))-64
30 PRINT TAB(16-W/2);STRING$(W,42)
40 NEXT

97 bytes.

If we compress the lines together, we can shrink it even further:

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

66 bytes!

Is this as small as it gets? (Technically, this uses more memory, since string space has to be used to create temporary strings for MID$(), but we are only looking at program size and not memory usage.)

What else can we try? Leave a comment.

Until next time…

CoCo 3 and 256 colors

Apparently, if you use a composite monitor or TV set on a CoCo 3, you can just put bytes on the 640x192x4 color video mode and they create artifact color. You can get over 200 artifact colors that way with no special tricks other than a palette translation to know what byte value is what color.