Category Archives: CoCo

Tandy/Radio Shack TRS-80 Color Computer (CoCo)

Benchmarking the CoCo keyboard – part 1

See also: part 1, part 2, part 3, part 4, part 5, part 6, part 7 and more (coming “soon”).

Updates:

  • 2020-12-13 – Fixed a typo in the last BASIC example. Thanks, Johann K.
  • 2020-12-14 – Fixed a typo in a PEEK command. My bad.

Over in the Facebook CoCo group, Jim McClellan posted a tidbit about reading the CoCo keyboard in BASIC:

I self answered a question I had and figured maybe this might be helpful info for other BASIC peeps. The location for whether a key is beng pressed is 65282. It’s either 255 (yes) or 0 (no) By peeking location 135 (135 retains the ASCII value of the key pressed even after releasing the key) and ANDing 65282, you can get a smooth, repeating key.

10 KBD=PEEK(135) AND PEEK(65282)
20 PRINT KBD
30 GOTO 10

Jim McClellan

Using standard BASIC commands, we have INKEY$ available to detect a keypress:

10 A$=INKEY$:IF A$="" THEN 10
20 PRINT "YOU PRESSED ";A$

This works great, and I have mentioned it in earlier articles on this site including one showing a way to use it without the variable by using it as a parameter of INSTR inside an ON GOTO.

But I digress.

INKEY$ only gets you the first press. It will not detect if the key is being held down. This limits its usefulness to one-key selections (without needing to press ENTER) and perhaps turn-by-turn games where you have to press a directional arrow over and over to move from position to position.

Most games want to move as long as you are holding down the arrow key, and Jim was sharing a way to do that easily by using PEEK.

What the PEEK is going on?

I wanted to understand what these PEEKs are doing, so I consulted the Color BASIC Unravelled book:

0146   0087   IKEYIM   RMB   1   *TV INKEY$ RAM IMAGE

Memory location 135 (&H87 hex) contains the last key handled by INKEY$. Except, that must not be technically correct because it works without calling INKEY$. Looking further into the disassembly, I see that it is actually where the BREAK CHECK stores the key it found:

It checks for BREAK and also the PAUSE key (SHIFT+@ on the CoCo keyboard, which now I see returns as &H13 from the “GET A KEYSTROKE ENTRY” routine). Thus, while INKEY$ uses this location…

…it is not involved with setting that location. (Note to self: Explore what KEYIN does.)

Doing a PEEK(135) will return whatever the last pressed key was, as detected by the looping BREAK check code. By itself, you could use this for a game where the player never stops moving, or only stops when you press a “stop moving” key.

GAME: Never Stop Moving using PEEK

0 REM nsm.bas
10 CLS0
20 PRINT STRING$(32,153);
30 FOR A=0 TO 12
40 PRINT CHR$(153);STRING$(30,32);CHR$(153);
50 SOUND 200-A*2,1:NEXT
60 PRINT STRING$(32,153);
70 L=1024+8*32+16:SC=0
80 POKE L,255:SOUND1,1
90 K=PEEK(135)
100 IF K=94 THEN M=-32
110 IF K=10 THEN M=32
120 IF K=8 THEN M=-1
130 IF K=9 THEN M=1
140 POKE L,96:IF PEEK(L+M)=96 THEN L=L+M ELSE 200
150 PRINT @480,SC;:IF K<>0 THEN SC=SC+1
160 GOTO 80
200 SOUND 1,5
210 PRINT @32*7+11,"GAME OVER!";
999 GOTO 999

In this pointless game (though I’ve seen worse for smartphones), you begin moving a red block around the screen using the arrow keys. Once you start moving, it will never stop unless you crash into a wall, ending the game. How high of a score can you make?

GAME: Never Stop Moving using INKEY$

It would have been easier (and more portable) to do this without PEEK and use INKEY$. All we would have needed was a check for no key being pressed, and then continue using the last direction used. Something like this would work:

0 REM nsm2.bas
10 CLS0
20 PRINT STRING$(32,153);
30 FOR A=0 TO 12
40 PRINT CHR$(153);STRING$(30,32);CHR$(153);
50 SOUND 200-A*2,1:NEXT
60 PRINT STRING$(32,153);
70 L=1024+8*32+16:SC=0
80 POKE L,255:SOUND1,1
90 K$=INKEY$:IF K$<>"" THEN K=ASC(K$)
100 IF K=94 THEN M=-32
110 IF K=10 THEN M=32
120 IF K=8 THEN M=-1
130 IF K=9 THEN M=1
140 POKE L,96:IF PEEK(L+M)=96 THEN L=L+M ELSE 200
150 PRINT @480,SC;:IF K<>0 THEN SC=SC+1
160 GOTO 80
200 SOUND 1,5
210 PRINT @32*7+11,"GAME OVER!";
999 GOTO 999

By avoiding CoCo-specific PEEKs, this version may run on an MC-10 or Dragon.

But, using PEEK may be faster since it has less BASIC to churn through than an INKEY$, IF and ASC() conversion. (Note to self: Benchmark PEEK versus INKEY$, assuming I haven’t already done that in an earlier article.)

But what if you wanted the player to move ONLY when the key is being pressed down? INKEY$ cannot do that, and neither can PEEK(135). That’s where the second memory location come in to play:

Decoding the (keyboard) Matrix

The Unraveled book shows that memory location 65282 (&HFF02) is part of a PIA that is hooked to the keyboard column matrix. BASIC uses this to determine which key is being held down:

Two bytes earlier is a similar value for the keyboard rows:

These bits are set or clear based on what key in the keyboard matrix is being held down.

                Color Computer Keyboard Array 
    Pin 1 --- @ --- A --- B --- C --- D --- E --- F --- G 
              |     |     |     |     |     |     |     | 
    Pin 2 --- H --- I --- J --- K --- L --- M --- N --- O 
              |     |     |     |     |     |     |     | 
    Pin 3 nc  |     |     |     |     |     |     |     | 
              |     |     |     |     |     |     |     | 
    Pin 4 --- P --- Q --- R --- S --- T --- U --- V --- W 
              |     |     |     |     |     |     |     | 
    Pin 5 --- X --- Y --- Z -- UP -- DWN - LFT - RGT - SPACE 
              |     |     |     |     |     |     |     | 
    Pin 6 --- 0 -- 1! -- 2" -- 3# -- 4$ -- 5% -- 6& -- 7' 
              |     |     |     |     |     |     |     | 
    Pin 7 -- 8( -- 9) -- :* -- ;+ -- ,< -- -= -- .> -- /? 
              |     |     |     |     |     |     |     | 
    Pin 8 -- ENT - CLR - BRK - ALT - CTL - F1 -- F2 - SHIFT 
              |     |     |     |     |     |     |     | 
    Pin 9 -----     |     |     |     |     |     |     | 
                    |     |     |     |     |     |     | 
    Pin 10 ----------     |     |     |     |     |     | 
                          |     |     |     |     |     | 
    Pin 11 ----------------     |     |     |     |     | 
                                |     |     |     |     | 
    Pin 12 ----------------------     |     |     |     | 
                                      |     |     |     | 
    Pin 13 ----------------------------     |     |     | 
                                            |     |     | 
    Pin 14 ----------------------------------     |     | 
                                                  |     | 
    Pin 15 ----------------------------------------     | 
                                                        | 
    Pin 16 ---------------------------------------------- 

But, from my testing, just PEEKing these I/O values does not work, at least not on Color BASIC 1.1. (Keyboard scanning changed a bit in later version of the Color BASIC ROMs.) I am unfamiliar with using the PIA so there may be some other things that have to be done to set it up before you can read it. Still, just PEEKing doesn’t do it for me.

10 PRINT PEEK(65282):GOTO 10

I get 255s over and over, though sometimes it looks like it might be blipping to a different value. To catch the changes, I tried this:

10 P=PEEK(65282)
20 IF P<>LP THEN PRINT P:LP=P
30 GOTO 10

Jim says it is 0 if nothing is pressed, or 255 if any key is being held down. From looking at the matrix, even if this did work, it seems like it would only be 0 if nothing was held down in any column, and 255 if a key was held down in each column. Can this be correct?

So I ask the audience: Why does this work for Jim, but not in the Xroar emulator I am using?

Comments are apprecaited!

To be continued…

Color BASIC “DATA” quirk.

I was in the middle of writing more on my CoCo Base-64 encoding series and stumbled upon some weirdness with the DATA command. Consider this silly program:

0 REM baddata.bas
10 READ A$:IF A$="" THEN END
15 PRINT A$;:GOTO 10
20 DATA ":"
30 DATA HELLO
40 DATA ""

This will print:

:HELLO

I know I could just have done DATA “:HELLO” but stay with me on this..

If you try to combine lines like this:

0 REM baddata.bas
10 READ A$:IF A$="" THEN END
15 PRINT A$;:GOTO 10
20 DATA ":":DATA HELLO
40 DATA ""

…you get this:

Color BASIC DATA quirk.

When I get a moment, I’ll have to look at the ROM disassembly and see what is going on.

Until then…

Compressing BASIC DATA with Base-64 – part 3

See also: part 1 and part 2.

A Faster Base-64

I had planned to end this series with this third part, giving a simple way to turn 8-bit value DATA statements into Base-64 DATA statements. But smarter folks than I have looked at my previous work, so now my plans have changed. We will need an extra part or two… or three.

Today, let’s highlight some comments made to previous installments.

The always thought-provoking MiaM wrote:

I would had written out the 2 exponent values directly as 4, 16 and 64 rather than using INT(2^2).

The decdode-4-“chars”-to-3-bytes parts could use a modified base 64 thingy where you have the first 6 bits of three bytes in a row, and the fourth “char” contains the upper two bits for the previous three “chars”/bytes. Or the other way around, start with the “char” that contains the upper two bits for the following three chars and put that in a numerical variable. Then have a loop that runs three times. Each time first shift the “upper two bits” variable two steps left, i.e. multiply by 4, and then read a “char” and to that char OR the result of the “upper two bit” variable ANDed with 192 (=%110000).

That format would be incompatible with the standardized representation used by BASE 64, but it would indeed be a format with the same density and using the same characters.

Btw you could use the “BASE 90” as a way to slightly compress some data, by just having the values >63 represent two instances of the actual value minus 64. That might not save much, but perhaps worth investigating.

MiaM

Very good point! Eliminating “power of two” calculations and changing them to hard coded values should offer a noticeable speed increase. Pre-calculating values (i.e. writing 4 instead of 2^2) is a good way to save some time, and possibly space too (since “2^2” takes up more memory than “4”).

Normally I would go on a Benchmarking BASIC tangent, but I will save that for later.

I was more intrigued by the concept of making an easier to parse Base-64 format. Since the original goal of this article was to cram as much type-able DATA numbers as possible in to a BASIC program, there is nothing that says it needs to follow the standard Base-64 encoding format. Any format that gets more bits of data in to type-able DATA values would suffice.

This opens up an opportunity to tweak the encode/decode method to be easier to do in BASIC. Mia suggests something like this:

Instead of the standard Base-64 encoding of three 8-bit values into four 6-bit values:

+- Byte 1 --+- Byte 2 --+- Byte 3 --+
| 000000|00 | 0000|0000 | 00|000000 |
| \__A__/\___B___/ \___C___/ \_D__/

We could alter the encoding into a different version:

+- Byte 1 --+- Byte 2 --+- Byte 3 --+
| 00|000000 | 00|000000 | 00|000000 |
| \| \_A__/   \| \_B__/ | |/ \_C__/ |
|  \___________D__________/

The benefit here is that decoding this in BASIC could be done much easier and faster. Rather than all the multiplication/division needed to shift bits and then combine them into bytes, it could be as simple as this a few ANDs and divides. Here’s a rough example of converting three 8-bit (0-255) input values (A, B and C) into four 6-bit (0-63) output values (O1, O2, O3 and O4).

0 REM 6BIT.BAS
1 REM As proposed by MiaM

10 READ A,B,C
15 REM --XXXXXX of A
20 O1=(A AND &H3F)

25 REM --XXXXXX of B
30 O2=(B AND &H3F)

35 REM --XXXXXX of C
40 O3=(C AND &H3F)

45 REM XX------ of A
50 O4=(A AND &HC0)/4

55 REM XX------ of B
60 O4=O4+(B AND &HC0)/16

65 REM XX------ of C
70 O4=O4+(C AND &HC0)/64

75 PRINT "ENCODED:"
80 PRINT A;B;C,O1;O2;O3;O4

85 A=0:B=0:C=0
90 A=O1+INT(O4 AND &H30)*4
100 B=O2+INT(O4 AND &HC)*16
110 C=O3+INT(O4 AND &H3)*64

120 PRINT "DECODED:"
125 PRINT O1;O2;O3;O4,A;B;C

1000 DATA 111,222,123

Running this program displays:

Three 8-bit values converted to four 6-bit values, and back.

The three 8-bit input values (111, 222 and 123) are converted into four 6-bit output values, then those four are turned back into three 8-bit values to verify it worked.

The conversion is very simple, since the output values O1, O2 and O3 are just the right 6-bits of the input values A, B, and C, which can be obtained by using AND to mask off the top two bits:

20 O1=(A AND &H3F)
30 O2=(B AND &H3F)
40 O3=(C AND &H3F)

Optimization Note: We could save a few bytes by omitting the parenthesis and the space before the &H3F. Due to how the Color BASIC’s parser works, we need the space between the variables (A, B and C) and the keyword “AND”. That space is what tells the tokenizer we want a variable followed by the keyword AND, versus a variable that starts with “AA”:

A=255

PRINT A AND 4
4

PRINT A AND4
4

PRINT AAND4
0

Above, Color BASIC thinks the third example is a variable called “AAND4” which is truncated to just be “AA” since Color BASIC only cares about the first two characters of a variable name:

A=255
AAND4=42

PRINT AAND4
42

PRINT A AND4
4

Oh the fun bugs that must have caused me back in the day!

But I digress…

The fourth byte is built by doing the opposite AND to get only the top two bits of A, then a divided that by 4 to shift them to the right 2 bits (AA—— to —AA—-), then do the same mask to B and divide by 16 to shift them 4 bits to the right (BB—— to ——BB–) and again for C divided by 64 to shift 6 bits to the right (CC—— to ——CC) and then add them together to make the result (–AABBCC).

50 O4=(A AND &HC0)/4
60 O4=O4+(B AND &HC0)/16
70 O4=O4+(C AND &HC0)/64

I think I did a poor job explaining that. But here it is visually:

INPUT (three 8-bit values):

A: aaAAAAAA
B: bbBBBBBB
C: ccCCCCCC

OUTPUT (four 6-bit values):

O1: --AAAAAA
O2: --BBBBBB
O3: --CCCCCC
O4: --aabbcc

Maybe that helps.

Doing it this was can be done faster and with less code, I think. Some benchmarking needs to be done to see if AND is faster than addition for combining the values, and the O4 line can just be made as one thing without the intermediate line numbers and steps:

50 O4=(A AND &HC0)/4+(B AND &HC0)/16+(C AND &HC0)/64

We will want to make the decoder as small as possible, since if we save 100 bytes doing BASE-64 over HEX and the decoder takes more than 100 bytes it defeats the purpose.

Maybe we can figure out this “base 90” concept in a future article, as well.

To be continued…

Compressing BASIC DATA with Base-64 – part 2

See also: part 1

Today we will explore writing a standard base-64 converter in BASIC, and then see if we can make a smaller and faster (and nonstandard) Color-BASIC-specific one.

When we last left off, we were looking at ways to get as much encoded data on to a DATA statement as possible. Instead of using integer numbers (base-10) or hex values (base-16), we began exploring if we could increase the base and use more typeable character to encode the data.

Although it seems we could create a weird base-90 format using every typeable character except for quote (which we’d need to start a DATA line else we couldn’t use comma), the decoder would be much larger and have to do much more work, and we actually wouldn’t benefit since we really need numbers that round to specific numbers of bits:

  • Base-8 (octal) values can be represented by 3-bits (111). (Extended BASIC supports octal when you use &Oxx or just &xx.)
  • Base-16 (hexadecimal) values can be represented as 4-bits (1111). (Extended BASIC supports hexadecimal when you use &Hxx.)
  • Base-32 values would be represented as 5-bits (11111).
  • Base-64 values would be represented as 6-bits (111111).
  • Base-128 values would be represented as 7-bits (111111).

As you can see, a base-90 value isn’t a large enough range to give us an extra bit over base-64. We need to use bases that are nice multiples of the power of 2. Because of this, we’ll ignore a made-up base-90 and look at something a bit more standard, such as base-64 encoding.

Pump up the base

As previously discussed, natively, you can represent a number in a DATA statement as a base-10 value, or a hexadecimal value. Both of these are the value 32:

100 DATA 32,&H20

BASIC will READ them the same way, though hex values are much faster for BASIC to read and parse. Using native hex values like “&H20” is the fastest way to load DATA, but it is also the largest since every value has two extra characters (“&H”) in front.

A recent tip was given by Shaun Bebbington about how you can represent zero just by leaving it out between commas. It saves space, and the parse gets zero from this faster than if you put a zero there:

100 DATA 8,6,7,5,3,,9

But since we are trying to get as much DATA in there as possible, we don’t want to separate numbers by commas. We can pack all the 2-digit hex values together in a string then read that entire string and parse out the individual 2-digit hex values. That is more work, and slower, but gets more data per DATA line. Here are the values 0 to 15 in hex (00 to 0f):

100 DATA 000102030405060708090A0B0C0D0E0F

As previously demonstrated, this is the most efficient way to store HEX values. Even when we pad a low 0-15 value to make it two digits (1 represented by 01), it stills saves space over comma delimited values since no commas are used.

But each hex value is wasting 50% of the bits it takes to represent it. HEX values of 0-15 could be represented by four bits (0000 to 1111). We are storing them as one 8-bit character and thus achieving 50% storage efficiency.

We can do better by using a higher base-x value that can use those wasted bits. We want the highest value we can represent with typeable characters, which is 64 (since the next higher would be 128 and we don’t have a way to type 128 different characters on the CoCo).

Base-64

The standard Base-64 encoding uses the following 64 characters to represent values of 0 to 63:

ABCDEFGHIJKLMNOPQRSTUVWZYZabcdefghijklmnopqrstuvwxyz01234567890+/

Each base-64 character needs 6-bits to be represented (000000-111111).

Representing values that way only wastes 2 bits per character, rather than 4-bits like hex base-16 does:

ASCII HEX Chars.:    ASCII Base-64 Chars.:
      0    15              0    63
     "0"   "F"            "A"   "/"
     /       \            /       \
xxxx0000  xxxx1111   xx000000   xx111111

But, converting to and from base-64 is much trickier. Hex base-16 is as simple as this:

  • Hex F0” -> F is 15 which is 1111 in binary. 0 is 0000 in binary. Thus the first character becomes the left four bits, and the second character becomes the right four bits. Super easy. Barely an inconvenience. Two ASCII bytes represent one byte of data.

But for base-64, we are dealing with 6-bits, and two of those won’t fit into an 8-bit byte. Instead, four base-64 6-bit values are merged together to make a 3-byte 24-bit value.

  • Base-64 “ABCD” (xx000000 xx000001 xx000010 xx000011) -> A is 0 which is 000000 in binary. B is 1 which is 000001 in binary. C is 2 which is 000010 in binary. D is 3 which is 000011 in binary. These values are merged together (removing the unused 2-bits in each one) and stored in 3 bytes as:
+- Byte 1 --+- Byte 2 --+- Byte 3 --+
| 000000|00 | 0001|0000 | 10|000011 |
| \__A__/\___B___/ \___C___/ \_D__/
  • Byte 1 contains 6 bits of base-64 value A and 2 bits of base-64 value B.
  • Byte 2 contains 4 bits of base-64 value B and 4-bits of base-64 value C.
  • Byte 3 contains 2 bits of base-64 value C and 6 bits of base-64 value D.

Well that’s a mess. Moving bits around like that is super easy under languages like C, but a bit more work in BASIC.

Encode this!

We will start with encoding a simple ASCII string into base-64 using a web tool:

https://www.base64encode.org

If you go to that link, you can type something in and then encode it into base-64. I typed:

Greetings from Sub-Etha Software! Do you know where your towel is?

And that gets encoded into this:

R3JlZXRpbmdzIGZyb20gU3ViLUV0aGEgU29mdHdhcmUhIERvIHlvdSBrbm93IHdoZXJlIHlvdXIgdG93ZWwgaXM/

Each character represents a 6-bit (0-63) value which we will have to combine into 8-bit values and decode.

An easy way to decode the characters used by base-64 encoding is with a string:

10 Z$="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"

We can use Extended BASIC’s INSTR() function to match a character from the encoded string with a character in that string, and the position it is found in will the the value it represents (well, minus 1, since INSTR returns a base-1 value).

Here is an example that will display the bytes of the encoded string:

0 REM base64-1.bas
10 Z$="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
20 READ A$:PRINT A$
30 FOR A=1 TO LEN(A$)
40 PRINT INSTR(Z$,MID$(A$,A,1))-1;
50 NEXT
1000 REM BASE-64 DATA
1010 DATA R3JlZXRpbmdzIGZyb20gU3ViLUV0aGEgU29mdHdhcmUhIERvIHlvdSBrbm93IHdoZXJlIHlvdXIgdG93ZWwgaXM/

Running that shows me this:

Displaying base-64 values.

If A is 0, then R should be 17, and that is what it prints first. Now we know we can get the values for each character in a base-64 encoded string.

Next we have to turn four 6-bit base-64 values into three bytes (24-bits). I am not sure what a good way to do this is, so I’ll just brute-force it and see how that works out.

First, I know that I need four base-64 values to make my 3 8-bit values, so I’ll modify my loop to skip every four values, and then add an inner loop to process the individual four base-64 values.

Inside that inner loop it will process the next four base-64 6-bit values and convert them into 3 8-bit values.

Here is what I came up with:

0 REM base64.bas
5 POKE65395,0
10 Z$="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
20 READ A$
30 FOR A=1 TO LEN(A$) STEP 4
35 REM GET 4 6-BIT VALUES
40 FOR B=0 TO 3:B(B)=INSTR(Z$,MID$(A$,A+B,1))-1
50 IFB(B)<0 THEN B(B)=0
60 NEXT
65 REM CONVERT TO 3 8-BIT
70 C1=INT(B(0)*INT(2^2)) OR INT(B(1)/INT(2^4))
80 C2=(B(1) AND &HF)*INT(2^4) OR B(2)/INT(2^2)
90 C3=(B(2) AND &H3)*INT(2^6) OR B(3)
100 PRINT CHR$(C1);CHR$(C2);CHR$(C3);
110 NEXT
120 END
1000 REM BASE-64 DATA
1010 DATA R3JlZXRpbmdzIGZyb20gU3ViLUV0aGEgU29mdHdhcmUhIERvIHlvdSBrbm93IHdoZXJlIHlvdXIgdG93ZWwgaXM/

I figured out all the 6-bit to 8-bit stuff (lines 70-90) with alot of trial and error, so I expect there is a faster and easier way to do this. But, then end results is a program that will print out the expected message, albeit really slowly.

A successful, but slow, decode of a base-64 encoded message.

One unexpected problem was with the powers of two — (2^2) and such. They produce rounding errors which caused some bits to be lost. I had to use INT() round them. That took me hours to figure out, but it’s just part of the inaccuracies of floating point values, especially limited ones like a 1970s BASIC used.

PROBLEM: Since the goal here is to put more data in DATA statements, the base-64 decode routine needs to be small. If it is 100 bytes larger than just using HEX, you have to save 100 bytes in DATA before you break even. The routine I give is not small and not fast. It would probably not be useful in the 10 LINE contest I mentioned. Maybe one of you can help improve it.

Now that we have a simple base-64 decoder, the next step will be making an encoder to turn DATA statement values into a base-64 string.

Until next time…

Compressing BASIC DATA with Base-64 – part 1

NOTE: This article was started a year or two ago, so some references may no longer be pertinent.

NOTE 2: It was then updated in April 2020 before finally being published in November, so some references in the updates may no longer be relevant.

There is some kind of “10 line BASIC” contest, and one of the categories allows for assembly language as long as it can be embedded in a typeable BASIC program. I previously discussed embedded assembly in BASIC in my Interfacing BASIC with Assembly series.

One of the examples I gave was a Pac-Man maze demo that used some assembly code to scroll the screen up and down. The loader looked like this:

2000 REM
2001 REM LOAD ASSEMBLY ROUTINE
2002
2010 READ A,B
2020 IF A=-1 THEN 2070
2030 FOR C = A TO B
2040 READ D:POKE C,D
2050 NEXT C
2060 GOTO 2010
2070 RETURN 'END
2080 DATA 16128,16217,189,179,237,90,39,14,90,39,28,90,39,42,90,39,55,204,255,255,32,67,142,4,32,166,132,167,136,224,48,1,140,5,255,47,244,32,47,142,5,223,166,132,167,136,32,48,31,140,4,0,44,244,32,30,142,4,1,166,132,167,31,48,1,140,5,255,47,245,32,14
2090 DATA 142,5,254,166,132,167,1,48,31,140,4,0,44,245,204,0,0,126,180,244,-1,-1

That particular bit of BASIC code was created by the LWASM assembler. It reads assembly language values from DATA statements and POKEs them into memory where they can be executed.

In another series, I discussed various ways to use DATA statements either for the fastest reading/loading or the smallest size.

Today, I’d like to revisit this subject and offer some more ways to compress data into DATA to store even more than before.

Or something like that.

Knowing our limitations

We know the BASIC input buffer is 249 characters long, so we should be able to type in a single digit line number, the keyword “DATA” and 244 more characters.

BASIC allows typing in up to 249 characters.

Above, I have a one digit line number (“0”), then four characters for the keyword (“DATA”) then seven full lines of 32 characters (32*7=224) plus 20 characters on the final line – so 1+4+224+20 is 249. Hey, it works!

Since loading a BASIC program saved in ASCII is the same as typing it in, that limit should also apply for loading a non-tokenized ASCII program. I will be using that method here to load test programs into the XRoar emulator, so I won’t be able to cheat and load a tokenized line that would have exceeded our typing limit.

If we encode our assembly code as 2-digit hex values, we should have room to type a single digit line number, the keyword “DATA”, and 122 2-digit hex values. We could enter all the hex digits from &H00 to &H79 on a line. This appears to work:

The BASIC input buffer is 249 characters, so that’s the most you can type before pressing ENTER.

As soon as we press enter, BASIC tokenizes the “DATA” keyword. It no longer takes up four bytes. This means even though we typed the full 249 characters, we could EDIT the line and perhaps type a few more characters. Typing “EDIT 0″…

Editing the longest typed line…

…then pressing “X” to extend (go to the end) of the line allows us to add two more digits:

In EDIT mode, there are now two more characters available since DATA has been tokenized.

I am guessing DATA is tokenized into a 2-byte token. However, if you add these two characters, then re-list the line:

BASIC can’t list all the characters.

…we see BASIC does not show the final character. However, if you use the READ command to read that line in to a string, and the PRINT it, you see it’s actually there:

BASIC is storing all the characters, but LIST cannot show them.

The BASIC LIST command has a limit to how much it will display, it seems, and it is one character less than it should be. Bug?

BASIC line packing

It is possible to create BASIC lines that contain much more data than you can type in. They will run fine, but cannot be fully listed. There were several BASIC “crunch” programs available, including one by Carl England that I used often, that did this trick.

However, if we want to stick to how much someone could type in, we need to limit ourselves to that 249 buffer, and not rely on doing the EDIT trick to add more to it.

If that is the case, the most amount of HEX encoded assembly language bytes you can fit on a BASIC line is 122 per single-digit line. The code to read in those lines and POKE them in to memory can easily fit into one line as well, so we could easily fit a 1098 byte assembly language program into a ten line BASIC program.

And, we could even stick a few more bytes on the end of the loader line. Using a simple test program, I figured I could get the loader plus 1167 bytes of assembly code POKEd into memory. It looked like this:

0DATA000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778
1DATA797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1
2DATAF2F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A
3DATA6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3
4DATAE4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C
5DATA5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7C8C9CACBCCCDCECFD0D1D2D3D4D5
6DATAD6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E
7DATA4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9BABBBCBDBEBFC0C1C2C3C4C5C6C7
8DATAC8C9CACBCCCDCECFD0D1D2D3D4D5D6D7D8D9DADBDCDDDEDFE0E1E2E3E4E5E6E7E8E9EAEBECEDEEEFF0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F40
9 L=16128:FORA=0TO9:READA$:FORB=1TOLEN(A$)/2:POKEL,VAL(MID$(A$,B*2,2)):L=L+1:NEXT:NEXT:DATA4142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E
100 BT=0:FORA=0TO9:READA$:FORB=1TOLEN(A$)/2:PRINTMID$(A$,B*2,2);:BT=BT+1:NEXT:NEXT:PRINTBT"BYTES":END

Notice LINE 100. It is not part of the program. I can load this and type “RUN 100” to get it to dump all the data and show me what it would be POKEing into memory, as well as the final byte count. For a contest entry, it would only include lines 0-9. (Using the EXIT/eXtend trick, you could probably get another 9 or 10 bytes total, but they might consider that cheating, and maybe someone could optimize the loader code to make even more room.)

We can do better…

Over on the Facebook group, someone (who was it?) suggested using some alternative encoding to get even more data in to the DATA statement. There are existing forms of doing this with 7-bit characters, using the printable range of characters.

The wikipedia has a page showing many different implementations of binary to text encodings.

Since we need to encode things that can be typed in, this puts some limits on what we can do. Characters 0-32 are control characters, so normal printable text characters start with ASCII 32 (space) and go to ASCII 127.

See https://en.wikipedia.org/wiki/ASCII for the list.

There are some other characters in this range that we cannot type on a CoCo keyboard. Here is the printable CoCo character set:

Printable CoCo characters from ASCII 32 to ASCII 127.

We can type the inverted (lower case) letters “a” through “z”, but we have no way to type the inverted @ symbol, or the inverted left bracket, backslash, right bracket, up arrow or left arrow.

But, just because they are typeable does not mean we could use them all in a DATA statement. Quote, for instance, is okay if it’s in the middle of a string that is read…

10 READ A$:PRINT A$
20 DATA ABC"DEF

…but if the quote started up at the beginning of a string of characters, BASIC would skip it and join everything after it together until it sees another quote or an end of line:

10 READ A$:PRINT A$
20 DATA “ABCDEF

Also, comma is a problem. READ separates data using the comma UNLESS the data is quoted. This would only read the first ABC:

10 READ A$:PRINT A$
20 DATA ABC,DEF

…but this would read “ABC,DEF” as one string:

10 READ A$:PRINT A$
20 DATA “ABC,DEF”

So even though we can type about 91 characters, we can’t use all of them in a DATA statement.

Reduce the bass! (Er, base…)

It seems we could achieve a base-90 (?) format by having each line start with a quote (thus we could include a comma in the DATA), but the decoder would have to be much larger.

So instead of that, we’ll turn the base down a bit and do something a bit easier in the next installment.

Until next time…

VIC-20 knowledge lived on, without me knowing it.

I have posted a number of CoCo BASIC articles that used the example of bouncing a ball around the screen. I have a simple routine I use for this, which involves an X and Y location variable, and movement MX and MY variables that will be positive or negative for which direction the ball is moving.

To my surprise, I found nearly this exact code in the old VIC-20 manual as a bouncing ball example!

This code called it DX and DY (delta), but it’s basically the same code I’ve been using all these years. I had no idea I learned this from the VIC-20 manual back in 1982!

Classic.

CoCo text screen can be changed in BASIC?

This writing is inspired by somethingI just saw in a YouTube video by 8-Bit Show and Tell:

In this video, Robin shows off an Easter egg found in the CoCo 2’s BASIC (the same one also found in the CoCo 1 since it was the same BASIC). He was wondering if there was any way to change the screen color (and I remember seeing someone ask this in the CoCo Facebook group but did not realize it was the Show and Tell guy). Australian programmer Nick Marantes gives him a “POKE 359,0:SCREEN 0,1” command to change the screen color… SCREEN can change the screen color? Surely I must have known this back then, but I had completely forgotten.

Thus, this article.

Nuclear Green CoCo Screen

The “nuclear green” screen of the Radio Shack TRS-80 Color Computer is certainly iconic. Black text on a green 32 column screen.

Disk Extended Color BASIC on the CoCo.

The Motorola MC6487 VDG chip contained a number of graphics modes, but only one text mode: 32×16 characters. The semigraphics modes supported up to 8 colors on the screen at the time, but text mode was limited to two colors: black on green. Well, except it was really a dark green on a light green. Here’s a zoomed in look, showing what color the text is compared to the black border:

CoCo “dark green” text on a nuclear green background with a black border.

I guess I sorta remembered this, because there was a way to invert the video to get light text on a dark background, and that background clearly wasn’t black:

Inverted text. Easy to do with a menu setting in the Xroar emulator.

I remember reading an article (probably in Rainbow Magazine) that showed how you could remove the VDG chip, pull up a pin, then plug it back in to the socket and get inverted video. I did that on my CoCo 1 since I thought that looked alot better (but not as good as if it was white on black like an Apple II or TRS-80 Model III).

The Xroar emulator has a menu option to toggle this inverted video.

But there was also a weird “pink” mode that I saw used in some early CoCo games. I recall reading a POKE or something that let you toggle it. You can also do that with the SCREEN command in Extended Color BASIC.

From the manual (page 96):

SCREEN type, color set displays the current graphics or text screen
type is 0 (text screen) or 1 (graphics screen)
color set is 0 or 1
Note: If type or color set is any positive number greater than 1, your computer uses 1.

Although not explained in the manual, you could select two color sets for the text mode. 0 was the normal dark green on nuclear green. 1 is the weird pink mode. But, when BASIC returns to the OK prompt, it resets the mode to 0, so you can’t use this to type things in (without a special POKE to bypass this reset). But, you can enter it in a program and then loop using a GOTO:

Alternate color set for text mode!

As soon as you hit BREAK and return to BASIC, the color resets to the green mode. The inverted mode looks like:

Inverted text color 1.

I don’t recall every using this mode, though I do recall at least trying it out with the POKE.

I do not know how to invert the video in software, but I expect there is a POKE to do that.

Anyway, there you go… alternate color sets for CoCo BASIC that you can access from BASIC using the SCREEN command.

More CoCo items (software, ROM-Paks, MC-10) for sale.

The great purge continues…

My CoCo 1/2/3 ROM-Paks:

https://www.ebay.com/itm/Radio-Shack-TRS-80-Color-Computer-CoCo-ROM-Paks/333745731777?hash=item4db4cbb8c1:g:fpcAAOSwjJFffRgC

Walt Disney cassette software (with boxes and everything):

https://www.ebay.com/itm/Walt-Disney-Radio-Shack-TRS-80-Color-Computer-CoCo-cassette-software-4-titles/333745728555?hash=item4db4cbac2b:g:x0QAAOSwqPBffRU~

Radio Ball (cassette game from Radio Shack):

https://www.ebay.com/itm/Radio-Shack-TRS-80-Color-Computer-Radio-Ball-game-on-cassette-w-packaging/333745757969?hash=item4db4cc1f11:g:tyYAAOSwCthffSKh

My MC-10 setup with memory expansion and Technical Service Manual:

https://www.ebay.com/itm/Radio-Shack-TRS-80-MC-10-computer-memory-expansion-and-software/333745738373?hash=item4db4cbd285:g:KaMAAOSwmZpffRu2

Radio Shack Appliance Light Controllers (Plug ‘n Power aka X-10):

https://www.ebay.com/itm/Radio-Shack-TRS-80-Color-Computer-Appliance-Light-Controller-NIB-X10-Plug-n/333745748407?hash=item4db4cbf9b7:g:wxgAAOSwZu9ffR6k

CoCo 3 “sealed in box” disk software: Where in the World is Carmen San Diego, Microsoft Flight Simulator II, and King’s Quest III:

https://www.ebay.com/itm/CoCo-3-Kings-Quest-III-Carmen-San-Diego-Flight-Simulator-II-NIB/333745755468?hash=item4db4cc154c:g:0bwAAOSwRKRffSGj

CoCo graphics tablet, video digitizer and speech recognition hardware for sale!

The great purge has begun! I have to downsize to about 50% of my current stuff, so it’s time to sell off all the CoCo gear I have that I will no longer be actively using.

First on the list is a Radio Shack X-PAD Graphics Tablet. This is still in the original box, with the original manual and template, as well as the plastic bags that came covering the pak and tablet! It’s as complete as it gets.

https://www.ebay.com/itm/333744740596

Next is the E.A.R.S. speech recognition system that was sold by Speech Systems. Yep, voice recognition on the CoCo! It could recognize 64 voice patterns, if I recall, and used simple BASIC commands to listen and match the voices. It needed a headset that was special so I don’t have a way to test it.

https://www.ebay.com/itm/333744732187

Then there is the Micro Works DS-68B video digitizer! Yep, the one with software that Tim Jenison (later of Newtek and the Video Toaster for Amiga) programmed! It works! But the label has come lose and will need to be “restored” with some glue.

https://www.ebay.com/itm/333744721237

Lastly, the Rulaford Research CoCo MIDI Pak with options — two switches that toggle the ports to THRU and OUT and such. I got this direct from Cecil Houk himself, and used it for years. I no longer have anything with MIDI, so … away it goes.

https://www.ebay.com/itm/333744735920

Please bid and help me raise some much needed money, and downsize at the same time. Thanks!

Bit shifting in BASIC

VIC-20 character blocks, bit shifted.

Recently, I had the urge to be lazy and have the computer do my work for me. I was designing some 8×8 graphics characters for the Commodore VIC-20 and wanted one for each offset position in a 2-character block (see image to the right).

Using modern tools like the Windows-based CBM prg Studio I could have easily edited each one by hand to get the desired result (which is what I did to create that image), but I thought it might be more fun to just design one character and write a program to create the other shifted frames.

I have bit shifted in C programming many times (because it has a bit shift operator) but I don’t recall ever knowingly doing it in BASIC.

Bit shifting basics

Bit shifting is where you take all the bits in a byte (or word, or long, whatever) and move them left or right. Here is an example of shifting the byte value 00111100 to the right:

Before: [ 0 0 1 1 1 1 0 0 ]
After : [ 0 0 0 1 1 1 1 0 ]

If you just want to shift and don’t care about the bit that “falls off the edge,” it is as simple as multiplying or dividing the value by 2.

To demonstrate this, we first need a program that will display a byte as bits.

0 REM bitsslow.bas
10 Z=&H00:GOSUB 500
20 Z=&HF0:GOSUB 500
30 Z=&H0F:GOSUB 500
40 Z=&HFF:GOSUB 500
50 END

500 REM PRINT BITS
510 FOR ZB=7 TO 0 STEP -1
520 IF Z AND 2^ZB THEN PRINT"1"; ELSE PRINT "0";
530 NEXT
540 PRINT:RETURN

The routine at line 500 will print the value in variable Z as bits. It is hard coded to only work with a byte (0-255). When I run that, this is what it displays:

00000000
11110000
00001111
11111111

Those lines correspond to &H00, &HF0, &H0F and &FF. I show them in hex so the bit pattern is easier to see (0=0000, F=1111).

A bit faster…

My bit printing routine is so slow that you can see it print the digits one at a time. This is due to the math that goes on in line 520 for each bit check. To speed things up, we could pre-calculate an array of those powers of two (1, 2, 4, 8, 16, 32, 64, 128) and use that instead:

0 REM bitsfast.bas

5 GOSUB 1000

10 Z=&H00:GOSUB 500
20 Z=&HF0:GOSUB 500
30 Z=&H0F:GOSUB 500
40 Z=&HFF:GOSUB 500
50 END

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

1000 REM INIT BIT ARRAY
1010 FOR ZB=0 TO 7
1020 ZB(ZB)=2^ZB
1030 NEXT
1040 RETURN

Line 5 will GOSUB to our initialization routine which sets up the power-of-two array ZB(). After that, the routine at 500 can be called as it previously was, except now it will print so fast you can’t really see it printing.

Now let’s use this routine to demonstrate bit shifting by multiplying or dividing by 2.

Bit shifting left

0 REM bitshftl.bas
5 GOSUB 1000
10 Z=1 '00000001
20 FOR A=1 TO 8
30 GOSUB 500
35 REM *2 TO SHIFT LEFT
40 Z=Z*2:NEXT
50 END
500 REM PRINT BITS
510 FOR ZB=7 TO 0 STEP -1
520 IF Z AND ZB(ZB) THEN PRINT"1"; ELSE PRINT "0";
530 NEXT
540 PRINT:RETURN
1000 REM INIT BIT ARRAY
1010 FOR ZB=0 TO 7
1020 ZB(ZB)=2^ZB
1030 NEXT
1040 RETURN

In line 10, Z starts out with 1, which is the bit pattern of 00000001. It is then multiplied by 2 in a loop eight times, each time shifting the bit one place to the left. The output looks like this:

00000001
00000010
00000100
00001000
00010000
00100000
01000000
10000000

Bit shifting right

And if we start out with Z being 128 (bit pattern 10000000) and divide by 2, it will shift right:

0 REM bitshftr.bas
5 GOSUB 1000
10 Z=128 '10000000
20 FOR A=1 TO 8
30 GOSUB 500
35 REM /2 TO SHIFT RIGHT
40 Z=Z/2:NEXT
50 END
500 REM PRINT BITS
510 FOR ZB=7 TO 0 STEP -1
520 IF Z AND ZB(ZB) THEN PRINT"1"; ELSE PRINT "0";
530 NEXT
540 PRINT:RETURN
1000 REM INIT BIT ARRAY
1010 FOR ZB=0 TO 7
1020 ZB(ZB)=2^ZB
1030 NEXT
1040 RETURN

This will display:

10000000
01000000
00100000
00010000
00001000
00000100
00000010
00000001

Pretty simple. And if that were all I wanted to do, I’d be done.

But that was not all I wanted to do, so I am not done.

I wanted to shift bits right in one byte and have the bits that shift out end up shifting in to a second byte, like this:

    1st Byte  2nd Byte
1) [00001111][00000000]
2) [00000111][10000000]
3) [00000011][11000000]
4) [00000011][11100000]
5) [00000001][11110000]
6) [00000000][11111000]
...etc...

I spent far too much time coming up with an approach that would get me which bits were going to be shifted off to the right (using AND), and then placing those in a new variable and then shifting those bits to the left to line up where they would have ended up if I had just shifted a 16-bit value…

…then I realized that was silly, because I could just use a 16-bit value in BASIC!

16-bits in BASIC

You may have seen things like this which print out memory locations BASIC is using:

PRINT PEEK(25)*256+PEEK(26)

In Color BASIC, memory locations 25 and 26 contain the 16-bit address for where BASIC memory starts. Since PEEK only works on a single byte, it takes two PEEKs to get the two bytes that make up the 16-byte address, and some math to turn the two 8-bit values into one 16-bit value.

On my virtual CoCo in the Xroar emulator, memory location 25 contains 38 (&H26) and memory location 26 contains 1 (&h01). To turn those two 8-bit values into a 16-bit address, I need to somehow shift the 38/&H28 left eight times into a new 16-bit value, and then add (or OR) in the 1/&H01 value.

In C, one of the ways we’d do this is by using the bit shift operator:

uint16_t Result;
uint8_2  Byte1;
uint8_2  Byte2;
Byte1 = Peek(25); // Made up function to get a memory value.
Byte2 = Peek(26);
Result = (Byte1 << 8) | Byte2; // Shift Byte1 left 8 bits,
                               // OR in Byte2.
// Or...
Result = (Byte1 << 8) + Byte2; // Since the right 8 bits are empty,
                               // adding would have the same result.

…but there are many other ways to do the same thing, some much faster than these two approaches.

Back to BASIC

Shifting left can be done by multiplying the value by 2. To shift once, you might have:

V = V * 2

To shift twice, you could do:

V = V * 2 * 2

…but that looks silly. You’d just multiply by 4. And to bit shift three places (2 * 2 * 2) you could multiply by 8. See the pattern? They are all powers of 2 (2, 4, 8, 16, 32, 64, 128, 256, etc.)

So to shift 8 places left, you multiply by 256. Thus, the C example I gave earlier is represented in BASIC by:

10 B1=PEEK(25)
20 B2=PEEK(26)
30 R=(B1*256)+B2

Tada! When I first saw things like “X=PEEK(Y)*PEEK(Z)” back then, I did not understand why it worked, I just knew it did. And I was also confused at why the CoCo was like that, since on the VIC-20 it would have been “X=PEEK(Y)+256*PEEK(Z)”. See the differences? On the CoCo’s 6809 processor, a 16-bit value is YYZZ, but on the VIC-20’s 6502 processor it is stored the opposite as ZZYY. Thus, on my VIC, you would take the second location and multiply it by 256 then add the first location.

But I digress.

A bit more…

The previous example shows how you are effectively shifting a byte (B1) left 8 places so it ends up at the top 8-bits of a new 16-bit value. Something like that would work very well for shifting an 8-bit value into different positions of two characters (two bytes).

My approach would be this:

  • R will be the result, a value representing 16-bits (two bytes, 0-65535 in range).
  • Since I am shifting right (in this example), my first step is to get the 8-bit source value into the top 8-bits (left most bits) of the R result variable.
  • Next I will shift that entire R variable to the right, and then extract the left and right 8-bits as separate variables which is my output values.

Extract? Well, that will be the reverse of what we just did. In C, you can turn a 16-bit value into two 8-bit values like this:

uint16_t Source;
uint8_2  Byte1;
uint8_2  Byte2;
Source = 0x1234;           // Some 16-bit value.
Byte1 = (Source >> 8);     // Shift left 8-bits to the right.
Byte2 = (Source & 0x00ff); // Mask off top 8-bits.

…though there are many other (faster/better ways to do this). But, we could do this in BASIC the same way.

If shifting left is multiplying by 256, then shifting right must be dividing by 256.

10 R=&H1234
20 B1=(R/256)
30 B2=(R AND &HFF)

IMPORTANT NOTE: As I got to a later point in this article, I discovered a huge problem with this approach. If you didn’t already know it (I didn’t), I’ll explain it in just a bit… When I write, I’m usually learning as I type, and I decided to keep this stream of consciousness intact.

That is the first time I’ve ever done it that way in BASIC (duplicating the way C does it). The way I’ve commonly seen it done in BASIC is this:

10 R=&H1234
20 B1=INT(R/256)
30 B2=R-(B1*256)

Here is a simple test program:

0 REM split1.bas
10 S1=&H12
20 S2=&H34
30 R=S1*256+S2
40 PRINT HEX$(S1)" "HEX$(S2)" -> "HEX$(R)
50 D1=INT(R/256)
60 D2=R-(D1*256)
70 PRINT HEX$(R)" -> "HEX$(D1)" "HEX$(D2)
Using BASIC to split 16-bit values, or combine two 8-bit values.

Benchmark digression

Have I been doing it wrong since the 1980s? Is the C-style way (using AND) any faster than the BASIC way (using INT)? Let’s try the C-style way using my trusty benchmark program:

0 REM split2.bas
5 DIM TE,TM,B,A,TT
6 R=&H1234
10 FORA=0TO3:TIMER=0:TM=TIMER
20 FORB=0TO1000
30 IF Z=42 THEN 100
40 B1=R/256
50 B2=R AND &HFF
70 NEXT
80 TE=TIMER-TM:PRINTA,TE
90 TT=TT+TE:NEXT:PRINTTT/A:END

This shows 1143. (Of course it could be made faster using hex and removing spaces and such, but this is just for comparison with the second version.)

And now let’s try it the traditional BASIC:

0 REM split3.bas
5 DIM TE,TM,B,A,TT
6 R=&H1234
10 FORA=0TO3:TIMER=0:TM=TIMER
20 FORB=0TO1000
30 IF Z=42 THEN 100
40 B1=INT(R/256)
50 B2=R-B1*256
70 NEXT
80 TE=TIMER-TM:PRINTA,TE
90 TT=TT+TE:NEXT:PRINTTT/A:END

This version reports 1498! Well I’ll be darned… INT and a divide and a subtract and a multiply is slow than a divide and an AND. Well, once I write it out like that, I guess it makes sense.

I suppose applying some things I learned in C to BASIC is paying off.

But I digress…

Are we there yet?

So getting back to the original task, I should be able to combine 8-bit values and split 16-bit values and shift them any way I want.

First let me modify the bit printing routine to handle 16-bit values.

IMPORTANT NOTE: Here’s the bit I was talking about earlier…

I thought I could just change the 0 to 7 into 0 to 15 to cover all 16 bits, but that resulted in an ?FC ERROR (function call). A bit of testing revealed that AND only works on values from 0 to 32767 (&H7FFF). That is the maximum value of a 16-bit SIGNED integer, which uses 15-bits for the number and 1-bit for the sign (+ or -).

Yipes! So I could only easily show 15-bits, and I really want that extra bit.

Note to Self: Look into the Color BASIC Unravelled book and see why this is.

So, it looks like I’ll just have to modify my 8-bit print routine to print each byte of a 16-bit value separately. Good thing we learned how to do that earlier in this article!

16-bit bit printing

Here is how I had to modify the routine:

0 REM bits16.bas

5 GOSUB 1000

10 Z=&H0000:GOSUB 500
20 Z=&HFF00:GOSUB 500
30 Z=&H00FF:GOSUB 500
40 Z=&HFFFF:GOSUB 500
50 END

500 REM PRINT BITS
501 ZZ=INT(Z/256):GOSUB 510
502 ZZ=Z-ZZ*256:GOSUB 510
503 PRINT:RETURN
510 FOR ZB=7 TO 0 STEP -1
520 IF ZZ AND ZB(ZB) THEN PRINT"1"; ELSE PRINT "0";
530 NEXT
540 RETURN

1000 REM INIT BIT ARRAY
1010 FOR ZB=0 TO 7
1020 ZB(ZB)=2^ZB
1030 NEXT
1040 RETURN

Whew! That works, and prints the following:

0000000000000000
1111111100000000
0000000011111111
1111111111111111

Okay, so I did not discover any amazing speedup (at least if we want to use values larger than 32767), but I did get it to work.

I saved the best bit for last…

Now we can try a little test to see if we can start with an 8-bit value, shift it to the left side of a 16-bit value, the start shifting one bit at a time right, then splitting up the resulting two bytes at each step. I will use a test byte of &HFF (11111111).

0 REM shift16.bas

5 GOSUB 1000

10 Z=&HFF  '11111111
15 Z=Z*256 '1111111100000000
20 FOR A=1 TO 8
25 REM SPLIT INTO TWO BYTES
26 B1=INT(Z/256)
27 B2=Z-(B1*256)
28 PRINT HEX$(B1)TAB(3)HEX$(B2)TAB(5)" = ";
30 GOSUB 500
35 REM /2 TO SHIFT RIGHT
40 Z=Z/2:NEXT
50 END

500 REM PRINT BITS
501 ZZ=INT(Z/256):GOSUB 510
502 ZZ=Z-ZZ*256:GOSUB 510
503 PRINT:RETURN
510 FOR ZB=7 TO 0 STEP -1
520 IF ZZ AND ZB(ZB) THEN PRINT"1"; ELSE PRINT "0";
530 NEXT
540 RETURN

1000 REM INIT BIT ARRAY
1010 FOR ZB=0 TO 7
1020 ZB(ZB)=2^ZB
1030 NEXT
1040 RETURN

And it displays:

Color BASIC shifting a 16-bit value and splitting it up into two 8-bit values.

Success!

I could now incorporate that routine into my code, and take the data for one 8×8 character (like the solid circle I started this article with) and end up with 16 8×8 characters representing each position it could be within a 2×1 block of characters.

2010 DATA 60,126,255,255,255,255,126,60

In conclusion…

Wow. That was a really long way to go to show multiplying by 256 and dividing by 2, wasn’t it? But maybe there are some useful routines in these examples.

And now it’s my turn to ask you:

  • Do you know a better/faster way to print 16-bit values in binary?
  • Do you know a faster way to combine two 8-bit values into a 16-bit value, or split a 16-bit value into two 8-bit values?
  • Is there a better/faster way to to bit shifting than multiplying or dividing by powers of 2?

Let me know in the comments, and maybe there will be a part 2 on this topic.

Until next time…