I have posted the source code to my 1994 CoCo 3 OS-9 Space Invaders-style game to GitHub:
Current version is 1.04, from 2015.
Maybe one day I’ll do a 1.05. I never did get around to adding High Scores or a Demo Mode.
I have posted the source code to my 1994 CoCo 3 OS-9 Space Invaders-style game to GitHub:
Current version is 1.04, from 2015.
Maybe one day I’ll do a 1.05. I never did get around to adding High Scores or a Demo Mode.
Radio Shack introduced the Color Computer in 1980. It came 4K or RAM, and Microsoft Color BASIC in the ROM. It could be expanded to 16K RAM, which allowed adding a second ROM for Extended BASIC. A plug-in disk interface cartridge came later, with it’s own ROM containing Disk BASIC.
I’ve often wondered what Microsoft used to write the CoCo BASIC ROMs in.
Around 1982, Radio Shack released the EDTASM+ ROM-Pak for the Color Computer. It was a 6809 assembler for machine language, as well as a debugger. It could load and save files (source code and final binaries) to cassette tape.
There was also Disk EDTASM+, which added some extra features — though the most important one was probably that it could load and save to a disk drive, making that process far faster.
Someone put up a nice EDTAM+ information page on my CoCoPedia.com website.
Since Microsoft created EDTASM, I suspect it may have been (or was at least based on) the tool they used for writing the Color Computer ROMs.
If you want to see it in action, head over to the JS Mocha CoCo emulator where you will find it available from a menu:
The EDTASM+ ROM-Pak and Bill Barden’s Color Computer Assembly Language Programming book where how I learned 6809 assembly. I later used Disk EDTASM+.
While the CoCo 1 and 2 were basically the same machine, just with redesigned circuit boards and enclosures, the 1986 CoCo 3 was quite different. It could operate in a double speed more, and provided up to 80 columns of text versus the original CoCo’s 32 columns. It also came with 128K — double what the CoCo 1/2 could handle — and could be expanded to 512K (though third party folks figured out how to do 1 an 2 megabyte upgrades).
Unfortunately, Radio Shack never released an update to the EDTASM+ ROM-Pak or disk software. It was still limited to the small memory and screen size of the original 1980 CoCo hardware.
Folks came up with various patches. I had one that patched my Disk EDTASM+ to run in 80 columns on the CoCo 3, in double speed more (faster assembling!) while setting the disk drive step rate to 6ms. It was a much nicer experience coding with long lines!
After this I moved on to OS-9, and used the Microware assemblers (asm and rma) from OS-9 Level 1 and Level 2. I am not sure I touched EDTASM+ again until I played with it on JS Mocha, decades later.
Hitcachi made a clone of the 6809. This replacement chip had some undocumented features such as more registers and more op codes. EDTASM+ couldn’t help with that, but there were some OS-9 compilers that were updated to support it.
That’s when folks like Robert Gault came to our rescue with enhancements for the original EDTASM+. Robert added support for the 6309, and many new features — including CoCo 3 compatibility.
His EDTASM+ looks like this on a CoCo 3 in 80 column mode:
If you notice the copyright date, you’ll see he has continued to update and support it. Today he offers it in a variety of versions that run on the original CoCo 1/2, a CoCo 3, certain emulators, RGB-DOS support (for hard drive users), CoCoSDC (the modern SD card floppy replacement) as well as supporting things like DriveWire.
You can pick up your own copy for $35 as follows:
EDTASM6309 $35 Robert Gault 832 N.Renaud Grosse Pointe Woods, MI 48236 USA e-mail: firstname.lastname@example.org
There are a number of new features added. Here is the list provided in the README.txt file:
CHANGES TO EDTASM (Tandy Version) 1) Tape is no longer supported; code has been removed. 2) Buffer size increased to over 42K bytes. 3) Directory obtainable from both Editor and ZBUG; V command. 4) Multiple FCB and FDB data per line. 5) FCS supported. 6) SET command now works properly. 7) Screen colors remain as set from Basic before starting EDTASM. 8) Symbol table printed in five names per line on Coco3. 9) On assembly with /NL option, actual errors are printed. 10) Warning error on long branch where short is possible. 11) ZBUG now defaults to numeric instead of symbolic mode. 12) RGB DOS users now have support for drive numbers higher than 3. 13) Hitachi 6309 opcodes are now supported for both assembly and disassembly including latest discoveries. 14) HD6309 detection is included and if present incorporates a ZBUG error trap for illegal opcodes and enables monitoring and changing the E,F,V registers from ZBUG. 15) Coco 3 users can now safely exit to Basic and use their RESET button from EDTASM. 16) Keyboard now has auto repeat keys when keys are held down. 17) Lower case is now supported for commands, opcodes, options, and symbols. Take care when loading or saving files or using symbols, ex. NAME does not equal name, \.A not= \.a, etc. 18) Local names are now supported. Format is A@-Z@ and a@-z@ for 52 local symbols. New sets of locals are started after each blank line in the source code. Local symbols do not appear in or clutter symbol table. 19) Local symbols can only be accessed from ZBUG in expanded form: ex. A@00023 not A@. 20) Now reads source code files that don't have line numbers. Writes normal source files with line numbers ( W filename ) or without line numbers ( W# filename ). 21) Macro parameters now function correctly from INCLUDEd files. 22) While in the Editor, the U key will backup one screen in your source file. 23) DOS.BAS can be used to program the F1 and F2 keys on a Coco3. See below. 24) Coco3 WIDTH80 now uses 28 lines of text. Coco 1&2 versions do require 64K RAM, the Coco 3 version will work with 128K of RAM. You can assemble 6309 code even if your Coco has a 6809 cpu.
It also adds some new commands:
V - obtains a directory from either Editor or ZBUG modes. U - scrolls backwards through source code. FCS - is used exactly like FCC but automatically add $80 to the last character in the string. FCB, FDB - for multiple entries per line entries should be seperated by a comma. Make sure that the comment field for that line DOES NOT CONTAIN ANY COMMAS or an error will result.
If you are wanting to do some CoCo assembly language programming, I highly recommend you sending $35 to Robert and pick up a copy of his version. EDTASM+ is tricky to learn, and his updates make it a bit less tricky.
And tell him Allen sent ya.
Until next time…
Awhile back, the Internet led me to a wondrous thing: an online 6809 emulator, complete with compiler, debugger, and text/graphical output!
This website, “designed and coded” by Gwilym Thomas, is amazing. If has a spot where you can enter 6809 assembly source code, then you can compile and run it!
It even has a few sample programs you can select and try out.
While it runs, you see the registers update, as well as a source-level debugger showing what op codes are currently executing. You can set break points, and memory watch points, too.
It also provides text output in the form of the MC6847 VDG chip (used by the CoCo, and a few other systems). The graphics mode is different VDG. While it supports some similar resolutions, it also adds a 16-color display.
The screen memory is mapped to $400 (1024) just like the CoCo, so you can run stuff like this:
start ldx #1024 loop inc ,x+ cmpx #1536 bne loop bra start
If you past that in to the Assembly language input window and then click Assemble source code, you will see the text characters in the Text screen preview window cycling through values. Neat!
The graphics screen starts just past the text screen at $600 (1536). I think that might be where it started on a non-Disk Extended Color BASIC system. (See my article about memory on the CoCo for more details.)
The documentation notes this about the modes:
The graphics screen is a memory-mapped display of 6144 bytes of RAM beginning at address $0600. There are 3 graphics colour modes, in which either 1, 2, or 4 bits represent a single pixel in 2, 4, or 16 colours respectively. Addresses increase left to right, top to bottom as for the text screen.
Columns and rows are zero-base with (0, 0) at the (left, top). Sequences of bits (1, 2, or 4) from high to low represent pixels from left to right. The 2 colour mode has 256 pixels by 192, the 4 colour 128 by 192, each line being 32 bytes. The 16 colour mode has 128 pixels by 96, each line being 64 bytes.
Example: in 4 colour (2 bit) mode pixel (93, 38) would be in byte $0600+(3832)+trunc (93/4), because there are 4 pixels in a byte. The colour value (0..3) would be stored in bits 5 & 4, ie. shifted left ((4-1)2)-((93 mod 4)*2 times).http://6809.uk/doc/doku.php?id=interactive_6809_emulator
Changing screen modes is NOT done via simulated VDG registers. Instead, it has code that looks like this:
ldd #$0204 ; select 4 colour graphics mode swi3
I have not been able to find details on what values represent what mode. Also, the documentation says there is keyboard input:
Click the text screen panel then start typing for the emulator to receive keyboard input. Remember that (due to limitations of the emulated hardware) when lower case characters are printed to the screen they will appear in inverse video.http://6809.uk/doc/doku.php?id=interactive_6809_emulator
I have not figured out how this works, yet.
As far as the 6809 assembler goes, it does not parse all of the extensions that the LWTOOLS’ lwasm assembler supports, so I have been modifying my projects to be compatible with the emulator’s assembler. This has let me, with minor changes for things like ROM calls, test and debug my code in a way that is impossible on actual hardware.
Here is the documentation:
If you create anything interesting in it, please let everyone know in the comments.
In an Internet full of so much garbage, it’s wonderful to find such a gem.
Until next time…
A quick tutorial on how to get the Lost Wizard LWTOOLS running under Windows. I have tested this under Windows 11.
To build under Windows, you will need to use a compatibility layer that makes Linux-style code work under Windows. Lost Wizard recommends mingw or Cygwin. This tutorial will use Cygwin.
Run the installer:
Click Next, then choose “Install from Internet”:
Click Next, then either use the default install location, or customize if you know what you are doing. Same thing for installing for “All Users” or “Just Me.” In my example, I am just using the defaults:
Click Next, then select where it will download files. I will just use the defaults:
Click Next, and just use the default Internet Connection settings unless you know what you are doing and need to change them:
Click Next, then select one of the download sites. It shouldn’t matter which one you choose, but since I used to read the old Maddox website hosted on xmission.com, I selected that:
Click Next, and it will retrieve a list of available packages. For building LWTOOLS, you need a C compiler and “make” utility. I went with the standard GCC compiler and standard make.
Expand the “All” then “Devel” items.
Locate the entry that says “gcc-core” (or use the Search box) and click on the “Skip” to the right of it. It should change from “Skip” to a version number (currently 11.3.0-1 as I type this).
Locate the entry that says “make” and do the same (currently 4.3-1 as I type this).
If you would like to download LWTOOLS (and other items) source directly rather than having to download “the most recent release”, you will also need to install “mercurial“. This will give you the “hg” command used to retrieve the latest source code for the projects. (And if you are doing all of this, might as well do this, too.)
Click Next, and you will see a list of all the required packages that will be download. (When you select an item, this installer will download any other items that are required for the one you selected.)
Click Next, and the download will begin.
When completed, you can click Next and then choose if you want to add a Cygwin icon on your Desktop and/or in the Start Menu. Since you will need to run Cygwin to build LWTOOLS, you may want one or both.
Click Finish. You now have Cygwin!
You can either download the source code and build that, OR you can use Mercurial to retrieve the latest version of the source (which currently includes a bug fix that is not in the release archive yet). Plus, it saves all the steps of extracting the gzipped tar file in 2b ;-)
For either way, you will want to run “Cygwin64 Terminal” that is now installed. This will open up a console prompt.
You will need to change directories to where you plan to download the LWTOOLS source code. Cygwin translates the Windows directories like “C:\Users\huffm\Downloads” to a Unix-style directory path like “/cygdrive/c/Users/huffm/Downloads” on my system. If you know how to change directories from a Windows Command Prompt, instead of going to “C:\whatever” you would change backslashes to forward slashes, and start with “/cygdrive/c/whatever”
Use the change directory (“cd”) command and change to where you downloaded the LWTOOLS .gz file.
An easy way to get this path is to type the change directory command in the Cygwin terminal window, followed by a space, (“cd “) and then drag the “Downloads” folder from a Windows Explore window IN to the Cygwin Terminal. It will type out the path to whatever folder you drag and drop there:
Press enter, and you should now be in the directory where you downloaded the LWTOOLS file. The Cygwin prompt will change to show you what directory you are in. Mine looks like this:
huffm@Allen-LT /cygdrive/c/Users/huffm/Downloads $
From that location, enter this mercurial (“hg”) command:
hg clone http://lwtools.projects.l-w.ca/hg/ lwtools
That will retrieve the latest source and place it in a subdirectory called “lwtools” from here you are. Once complete, proceed to Step 3.
OR, if you want to manually download the latest release…
Go to http://www.lwtools.ca/ and download the latest version (currently 4.19 as I type this). Save it to wherever you chose, above.
The download will be a gzipped .tar file, so you will need some tool to extract it. You can find something in the Windows Store, or just use a command line utility from Cygwin. For this tutorial, we will use the command line.
From this terminal, many Linux-style commands are available, including gzip (which we will use to extract the LWTOOLS .tar file) and tar (which we will use to un-tar that file).
Extract the .gzip file by typing “gzip -d” (for decompress) followed by the lwtools filename:
gzip -d lwtools-4.19.tar.gz
This should extract the “lwtools-4.19.tar” file in to that directory. Now un-tar that file by typing:
tar -xf lwtools-4.19.tar
That will finally leave you with a directory called “lwtools-4.19” (or whatever version you downloaded.
Change directories in to the “lwtools-4.19” directory (or, if you downloaded with Mercurial, in to “lwtools” or whatever you called it):
Once you are there, all you need to do is type “make” to begin the build process.
Once complete (it may take awhile), the binaries have been built, but they aren’t located where Cygwin can run them yet. To copy them over in to the proper location, type “make install“:
You now have some new command line programs available from within Cygwin. To verify that they worked, you can try typing them to see if they bring up their usage display. Try typing:
If you get back a “Usage:” message, you should now be ready to use LWTOOLS to compile 6809 assembly language for the CoCo.
Toolshed is a series of commands for copying files from your PC in to disk image files used by emulators or things like the CoCoSDC.
NOTE: Currently, this will not work. Some rules have changed in the compiler and it will error out. There are about 12 places in the source that can easily be fixed to make it build, but I’m going to wait and see if the Toolshed maintainers have a solution.
hg clone http://hg.code.sf.net/p/toolshed/code toolshed cd toolshed make -C build/unix install cd ..
NitrOS9 is a 6809 (or 6309) operating system based on Microware OS-9/6809.
hg clone http://hg.code.sf.net/p/nitros9/code nitros9 cd nitros9 make dsk
As a simple test, use a text or code editor to create the following “helloworld.asm” file. You will need to know where you save this file, since you will be typing that on the command line to build it. On my system, I have all my .asm files in a directory, and I just “cd” to that directory from the Cygwin terminal.
* helloasm.asm org $3f00 ldx #message loop: lda ,x+ beq done jsr [$a002] bra loop done: rts message fcc "HELLO WORLD!" fcb 13 fcb 0
This simple program will display the message “HELLO WORLD!”. It does this by using the Color BASIC “CHROUT” ROM call. This code starts by loading X with the address of a text message that is a series of characters, followed by a 13 (carriage return) and a 0 to mark the end of the message. The main loop loads the A register with whatever is at X, and if it is zero it ends. Otherwise, it calls the CHROUT routine indirectly by jumping to the location stored at $a002 in the ROM. It will repeat this until it gets to the 0 at the end of the message.
LWTOOLS can build .bin files that can be transferred to a CoCo (or emulator) on a disk image (using other tools), and then you can LOADM that file and EXEC it:
lwasm helloasm.asm -fdecb -ohelloasm.bin
Above, that takes the input file “helloasm.asm” and compiles it in format “decb” (a .bin binary) and calls the output file “helloasm.bin”. (You’d probably want all uppercase for filenames on the CoCo.) That should give a LOADM-able file to try.
But, a nifty feature of LWTOOLS is the ability to generate a BASIC program that loads the assembly language. Use the format “basic” and make the output file a “.bas” instead:
lwasm helloasm.asm -fbasic -ohelloasm.bas
That will create a text file called “helloasm.bas”:
10 READ A,B 20 IF A=-1 THEN 70 30 FOR C = A TO B 40 READ D:POKE C,D 50 NEXT C 60 GOTO 10 70 END 80 DATA 16128,16155,142,63,14,166,128,39,6,173,159,160,2,32,246,57,72,69,76,76,79,32,87,79,82,76,68,33,13,0,-1,-1
I like to use the XRoar emulator, since it lets me load a text file as if it was a cassette file. You can run XRoar, then use Ctrl-L (or File->Load) then select the “helloasm.bas” file. After that is done, typing “CLOAD” in XRoar will load this text file as if it was coming from tape!
Then you can “RUN” the program and load your assembly in to memory. For this example, the address of $3f00 was specified in the source codes “org” address (16128 in decimal) so that is where the code would load. After the “RUN”, you should be able to type “EXEC &H3f00” (or EXEC 16128 if not using Extended Color BASIC) and see the program run:
Until next time…
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).
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.
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…
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:
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
Run it once, then do a DEL 10-90. Run it again to verify it still fulfills the original task, and it does!
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…
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.)
Here is a screen shot of the code:
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).
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.
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”):
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!
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!
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:
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:
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.
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:
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).
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):
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):
From wikipedia, here is what ATASCII looks like:
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.
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 computer 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…
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).
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:NEXTrflberg (via YouTube)
501 IF Z<0 THEN PRINT”1″; ELSE PRINT”0″;
510 FOR BT=14 TO 0 STEP -1
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.rflberg (via YouTube)
-32768 is 1000000000000000 and 32767 is 0111111111111111
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!