PrintStar's Blog
Ramblings of a Fortran Nut
October 26, 2016 by Jeff

Troubleshooting Some Hardware Issues

Today I wanted to set up my Rainbow 100 server, but I ran into some unforeseen issues.  After spending far too long trying to find the “perfect” combination of serial cables, null modem adapters, and gender changers, I decided I better actually turn on the Rainbow I’ll be configuring as a floppy-driven web server.  That’s when I ran into problems.

My web server is a Rainbow 100A variant, meaning it:

  • can’t boot directly from a hard disk (a.k.a. a Winchester disk)
  • supports less RAM
  • has an older BIOS

All these things really don’t matter for this application except, perhaps, the RAM.

After connecting the monitor and powering it up, I was presented with a blank screen and 8 illuminated diagnostic LEDs on the back panel.  It appeared dead.  Pulling the motherboard and removing the memory expansion did not alleviate the symptoms.  Swapping the floppy controller also didn’t help matters.

When Rainbow motherboards aren’t working, I usually move on quickly.  I still have some spares, and trying to diagnose why it isn’t working can be tedious.  I had exactly one spare Rainbow 100A motherboard, and I swapped it into the machine.  The system posted, but the RX50 floppy drive was dead (and one of the “B” drive door pins was missing).  Swapping the floppy drive and exercising it a bit yielded a working system.

Time for some pictures!  The server internals:

The delightfully large amount of space DEC left for cabling the drives to the controllers and power supply to the motherboard can be extremely frustrating:

A closeup of the labeling of a relatively rare Suitable Solutions IDrive, which provides a double-sided double-density IBM-compatible floppy drive on a Rainbow:

After checking out this machine, I’ve slid it back into its tower stand in preparation for its service as a web server.

As a side task, I need to back up the floppy that came with this box.  I’m not sure that the IDrive drivers are generally available anywhere anymore.

  •   •   •   •   •
October 17, 2016 by Jeff

Moving Slowly Ahead

I’ve recently added another file to the repository, GWBLOG.BAS, that represents the beginnings of the actual static site generator.  As I mentioned before, I thought I’d use the GW-BASIC MERGE statement to keep all the modules separate.  It turns out that MERGE doesn’t exactly work as I had expected.  When called programatically, it actually halts execution after the first MERGE statement.  My attempt at some coding cleanliness failed.

I could just continue by copy/pasting everything into a single BASIC source file, but that could have some downsides just from trying to locate things.  On the other hand, every other GW-BASIC program I’ve ever attempted was a single file, including one game that I once printed out on paper, measuring 11 pages of tractor-fed spagetti code.  I’m already feeling some of the stress of having separate files since I have to call subroutines by line number, and I keep forgetting what the damn subroutine line numbers are!

I’ve decided instead to use a batch file to combine all the files into a single BASIC file and launch the subsequent BASIC file.  I actually tried it for the first time today, and I ran into a bug that I had feared: The list of files returned by the directory listing don’t contain a path, whereas TSORT.BAS expects a full, valid path.  It’s a minor speed bump that I should have some time to correct tomorrow.

  •   •   •   •   •
October 1, 2016 by Jeff

A Peek at My Hardware

I’ll be doing almost all my Retrochallenge programming on one of my Digital Rainbow 100 personal computers.  The one I currently have set up is particularly exciting because:

  • It’s mounted in an E.T. stand
  • It’s using an MFM Emulator for its hard disk

The E.T. stand mounting is pictured below:

An E.T. Stand

The E.T. stand had been featured on this blog before, I believe.  It’s a ridiculous piece of hardware that feautres a pedal-driving monitor arm.  Good times!

The disk emulator is currently set up to simulate a 40MB MFM hard disk; using a higher capacity disk on the Rainbow can be troublesome due to DEC’s terrible cable connecting the disk to the controller.  However, the machine is delightfully silent now that some ancient Seagate monstrosity doesn’t have to spin up.  Furthermore, the emulator is a bit faster than an actual hard disk (though not appreciably so).  I had previously taken some pictures of the emulator, so I’ll skip that for now.

Just two days ago, I decided to “modify” the LK201 keyboard attached to my ‘bow.  Some years ago I acquired a clear plastic cover to replace the tan plastic on the keyboard.  The alternate casing was apparently created by DEC as a way to show off the keyboard’s internals at promotional events.  It’s clearly built by them since a mold that fits perfectly would have been expensive to produce.

There’s a brief look at where I’ll be working!  I’ll have another post on the software I’m working with on the Rainbow itself next.

      •   •   •   •   •
    September 30, 2016 by Jeff

    Retrochallenge 2016/10

    I’ve decided to try another Retrochallenge!  This October, I plan to attempt migrating my blogging activities, however sparse, to a DEC Rainbow 100 permanently.  The blog will be generated by a static site generator written in GW-BASIC and served using uIP/FOSSIL, originally ported during the 2009 Retrochallenge Winter Warm Up.

    Using WordPress has become somewhat unpleasant.  Yes, I’m currently typing into a WordPress blog, but it isn’t a fun experience.  Lately, for work, I’ve transitioned a number of websites to static sites generated via Cactus.  I think moving to a static generator for my blog could be delightful.

    Over the past few weeks, I’ve been writing some GW-BASIC subroutines to support this endeavor.  A nice deadline, though, should really get things moving.

    I plan on providing some delightful details of the hardware involved of getting a DEC Rainbow 100 permanently serving pages. I also plan on completing the static blog generator in GW-BASIC, and hopefully someone else might actually find it useful or, at the very least, amusing.  With a DEC Rainbow serving a blog, I think I might perhaps become a bit more prolific.

      •   •   •   •   •
    July 23, 2014 by Jeff

    The Unfamiliar Land of 8088 Interrupts

    It’s been just less than two weeks since my last Retrochallenge update.  I started off strong with posting, but I fizzled fast once I hit the real work.  So have I dropped out?  Not exactly…

    After perusing the piles of Rainbow documentation I have available in a desperate search for how to interact with the Z80 CPU through hardware, I started writing some test code.  At first it was simply a naive attempt to trigger the Z80 to execute anything at all.  Without any context, I was signaling the Z80 to start running by pushing code 0×21 out through I/O port 0 on the 8088 CPU.  This exercise was entirely uninteresting because, after doing so, the 8088 continues on about its own business.  It doesn’t automatically stop and wait for its sister CPU to finish up.

    What should happen under these circumstances is that the Z80 should trigger an interrupt (0×47 to be exact) on the 8088 to notify its friend that it has completed the requested work.  In order to perform this work, I wrote a slightly more elaborate program.  Now it would load some Z80 machine code into the lower 64KB of Rainbow RAM (but above the 2KB mark), attempt to configure the Z80′s start address, and request that the Z80 execute the code.  The unclear part of the above is how exactly to specify the start address to the Z80.  To move things along, I decided to hand-type the MS-DOS BIOS’s execz80 routine.

    The above, once again, really didn’t do much at all since the code didn’t handle any Z80 interrupts yet.  I did send the code off to the Z80, request some work, and halt the 8088, which did work.  What this program showed was that some miscellaneous interrupt was triggering the 8088 to come back to life.  I hoped it would be interrupt 0×47 from the Z80, but I wasn’t sure.  It was time to start writing an appropriate interrupt handler.

    The problem with all these attempts, of course, is that I really don’t know how to work with interrupts.  Sure, in the past, I’ve triggered interrupts to access screen or disk I/O, but I’ve never responded to an interrupt.  The other problem with writing something “new” of this vintage is the documentation.  I have a complete Microsoft C and Macro Assembler documentation set, but nowhere in these binders do they explain exactly how one might author an interrupt handler.  Sure, they provide functions to hook your handlers into the OS, but there’s no implementation examples.  Online searches are generally useless as well because they either focus on modern 32-bit (or higher) machines or they don’t turn up in results.

    I did find a promising introductory paper that gave an example of an interrupt handler responding to system clock ticks.  I decided to work with that code, and I wrote a simple handler that would allow my main program to proceed if it were ever triggered.  I attached the handler to the Z80 interrupt, and, lo and behold, I received the dreaded Message 16 – Interrupts Off message on the Rainbow, returning me to the boot menu. Anyone familiar with the Rainbow know that this message generally means, “something went wrong, so here’s an error that doesn’t actually help you figure out where the problem is.”

    While it was an error, it was something, at least.  For a few more days, I toyed with this same program, trying to get it to do something.  After continual failure, I decided to see if I could write a simple program to trigger and catch a generic, unused interrupt.  This program was met with varying success.  It didn’t always trigger Message 16, though, but it didn’t proceed either.  Basically, the program would trigger an interrupt, then continually check a variable to see if it changed.  The interrupt handler would change that variable when the interrupt was caught, allowing the program to exit.  Almost always, it simply sat there locked up.

    Yesterday it occurred to me that perhaps the handler wasn’t changing the variable properly.  Looking at the code, I made one simple change.  I had declared my status variable that allowed my program to exit as a global:

    static int returned;

    I had been compiling my program as a “small” memory model (don’t you all love x86 chips!).  In this circumstance, though, the variable “returned” existed only in the data segment in which my program ran.  The interrupt handler, on the other hand, does not run in this same segment.  I made one simple change:

    static int far returned;

    Surprise!  My program suddenly worked!  x86 segmented memory strikes again!

    I stopped yesterday after that small victory.  I’ll try again today (hopefully) to catch Z80 interrupts.


      •   •   •   •   •
    July 10, 2014 by Jeff

    Switching to Microsoft

    After a couple of days away from my Retrochallenge, I decided to dive back into trying to get that damn Z80 CPU to listen to me from MS-DOS.  A few days back, I actually stopped in frustration when I realized that I would definitely need to do some x86 assembly language programming to get things moving forwards.

    I had posted a scanned page from the Rainbow 100′s CP/M technical documentation that seemed to outline how to pass a message to the Z80 properly:

    The page outlined the values for the registers of the 8088 when trying to pass arbitrary code to the Z80.  To be honest, I have some serious doubts about the above scheme working when in MS-DOS; I can’t seem to see where anything similar is done in the MS-DOS BIOS code to trigger the Z80 to perform disk access.  There is a good chance that the CP/M BIOS is performing some extra work behind the scenes after the above call is made.  However, the method above is easy enough to try.

    I foolishly assumed, though, that I would be able to first set the values of the 8088 registers and then signal the 8088 output port from C.  This idea is, of course, nonsense because C is too “high-level” to hope that any particular register would persist between being set and my calling a C outport() function.  I needed to use assembly, which annoyed me enough to stop for a bit.

    Yesterday I decided to dive back in.  However, I did run into some speed bumps.  Normally on the Rainbow I work with Turbo C as opposed to Microsoft C.  This preference is based on:

    • Turbo C’s MAKE utility actually works (unlike Microsoft’s lobotomized MAKE)
    • Turbo C’s directory layout is considerably simpler

    For an assembler, things became a bit trickier.  I do also have Turbo Assembler, but, when I ran it yesterday, I found that it did not work on the Rainbow at all.  It wouldn’t even run under Code Blue.  And why is doesn’t it work?

    Borland is mildly famous in DEC Rainbow circles (is that a thing?) for being complete jerks.  Borland produced a number of compilers and applications for the Rainbow as native programs, both under MS-DOS and CP/M.  However, when Turbo C 2.0 was released, things went bad.  Rainbow users who bough the software were horrified to find that this “MS-DOS compatible” compiler didn’t run at all, while previous versions ran just fine.  Borland was completely silent on the issue, too.

    One enterprising Rainbow user was able to determine what was actually happening.  Internally, the Borland compilers were unexpectedly using MS-DOS interrupt 0×18.  This interrupt is relatively harmless on an IBM PC, but it is used for communications ports on a Rainbow.  There was no reason for Borland to choose 0×18 in the first place; it is entirely arbitrary, which makes Borland’s choice so frustrating.  This helpful Rainbow user, though, was able to generate a set of patches for the Turbo C executables, the compiler and preprocessor specifically, that changed their internal interrupt calls to use 0×60 instead.  The same user went on to generate patches for a number of other Borland tools as they continued to use interrupt 0×18 all over their development tools, including Turbo Assembler..

    My copy of Turbo C is obviously already patched.  However, I never did take the time to patch Turbo Assembler, apparently.  Additionally, I have no hard-copy documentation for any Borland tools.  The unpatched assembler and the lack of immediately available documentation convinced me that it was time to move on to Microsoft’s tooling.

    I spent yesterday transferring Microsoft’s Macro Assembler and C Optimizing Compiler onto my Rainbow 190.  All these packages do add up to a considerable amount of disk space relative to its 20MB disk.  However, my 190 is not one of my long-held Rainbows, so it started the Retrochallenge with a relatively empty disk, only containing my essentials:

    • MS-DOS 3.10b
    • Borland Turbo C 2.0
    • BinkleyTerm 2.40
    • Kermit
    • GW-BASIC

    The transfer of MASM to the Rainbow was simple and fast.  Although the archive was about 680KB in size, it goes relatively fast transferring it from a GNU/Linux server via ZModem at 19200 bps.  On the other hand, my stripped-down MSC 5.1 distribution, weighing in at about 900KB, was sitting on another Rainbow with a notoriously (in my house) slow hard disk.  I couldn’t transfer between the two Rainbows using ZModem because the receiving Rainbow would time out prior to the “slow hard disk” Rainbow even accessing the file to transfer.

    Whenever serial transfers become problematic, it’s time to pull out old, slow, and reliable Kermit.  If ZModem were a sports car, Kermit would be a car that won’t start and is being pushed manually up a steep hill. In contrast, though, Kermit provides drastically more reliable error checking than ZModem to the point of overkill.  Back in the BBS days, I rarely had any issues with ZModem, but occasionally the speed of disks can become an issue, requiring a slower transfer rate.  I started the transfer and left the two Rainbows to talk it out.

    In summary, I now have a fully function Microsoft C and MASM environment all set up and functioning.  Hopefully I can get on with my work.

      •   •   •   •   •
    July 3, 2014 by Jeff

    Finding the Z80

    I started today’s Retrochallenge work by trying to hunt down exactly how to trigger the Z80 to execute code.  Unsurprisingly, the Rainbow’s MS-DOS technical documentation doesn’t actually provide an answer to this question directly.  Because the Rainbow’s MS-DOS BIOS only employs the Z80 for disk access, there would be no “practical” reason to do so otherwise.

    The interrupt tables also suggest this inability to access the Z80.  The only interrupts available that appear to do anything similar are actually “Interrupt from the Z80A,” rather than asking the Z80 to do work.  So where to go now?

    Digital’s Technical Documentation  for the Rainbow is an odd collection of massive binders containing a plethora of both useless, high-level information (“Available Peripherals: Keyboard…”) and low-level gems (the video memory’s linked-list layout and scrolling via changing pointers alone).  It also, oddly enough, contains the complete Microsoft Macro Assembler source code for its MS-DOS BIOS.  That’s right, Digital provided every last line of source code for their BIOS.

    If we want access to the Z80, we just need to look up the RX50 disk driver source code in the BIOS.  Sure enough, when we look closely through the code, we come across the routine “execz80” in the assembly:

    I’m not any sort of assembly language expert, but I can usually stumble through the code.  It appears, though, that simply pushing some data OUT to port 0 (the value of intz80) on the 8088, causing the Z80 to begin execution of code.  There is obviously some work to be done setting up what exactly the Z80 should be doing, but it appears that the process is rather straightforward.  The 8088 then sits in a waiting loop checking to see if the Z80 is done.

    I’d rather like to avoid writing assembly language code for this challenge with the exception, possibly, of some Z80 assembly.  Peeking through Turbo C and Microsoft C headers, I ran across “outport” and “outp” respectively (MSVC still supports outp…).  These should allow me to fire off requests to the Z80 from C hopefully, but we’ll have to see.

    As an aside, I’ll try to post any images to my SDF Gallery account for all to see and enjoy.  Some Twitter pictures might take a few days to show up, but I’ll do my best to consolidate everything.

      •   •   •   •   •
    July 2, 2014 by Jeff

    Retrochallenge 2014: More Rainbow Goodness

    This summer I’ve once again decided to waste some time participate in another Retrochallenge!  I do love entering these contests because it gives me an excuse to spend some quality time with my favorite machines.  Unsurprisingly, I’ve decided once again to focus on my Rainbow 100 (or 190 depending on the day of the week).

    My project will focus on executing arbitrary Z80 code on the Rainbow from within MS-DOS.  For those unfamiliar with these strange beasts, the Digital Equipment Corporation Rainbow 100 personal computer attempted to bridge the gap between 8- and 16-bit worlds by providing what is effectively two closely-knit machines in one box.  The Rainbow contains two separate data buses, one controlled by an 8-bit Zilog Z80, and another controlled by a quasi-16-bit Intel (or AMD…) 8088.  The two buses communicated using 62KB of shared RAM (that’s not a typo) and shared interrupt calls.  Each data bus contained a set of peripherals.  The Z80′s bus contained:

    • Floppy controller

    And the 8088′s bus contained:

    • Video controller
    • Serial controllers (RS232, serial printer, and serial keyboard ports)
    • Graphics controller (if installed)
    • Hard disk controller (if installed)
    • Extended Communications Option (if installed

    It’s clear that the Z80 wasn’t given all that much responsibility in the system.  However, the Rainbow had reasonably good floppy disk access speed thanks to the Z80.

    The Rainbow also featured a version of CP/M called CP/M-86/80 that had the fascinating ability to detect if the executable was a Z80 or 8088 program and execute it on the proper CPU.  This feature allowed the Rainbow to run either legacy CP/M-80 8-bit code or newer CP/M-86 16-bit code.

    One interesting detail about the design is that, outside of floppy access, the CPUs’ control over shared memory was relatively fine-grained.  There were systems built in to make sure that only the 8088 or Z80 was accessing the shared RAM at any given time, but each could go about running programs “simultaneously” as long as this conflict didn’t occur.  The manuals, though, do refer to the CPUs as “master” and “slave,” explaining that the Z80 and 8088 didn’t normally (or possibly ever) go about their business simultaneously.  It would appear that one would normally wait while the other would do work.

    Anyways, the Rainbow’s CP/M BIOS provides a pretty easy method for communicating with the Z80 via 8088 interrupt 39.  From that interrupt, one can actually tell the Z80 to begin executing code at a specific memory address.  Doing this from MS-DOS, however, is a bit less documented.

    Of course MS-DOS does speak with the Z80 because it requires floppy disk access.  There is some communication going on between the two.  The real question is whether arbitrary code can be executed, not just floppy disk requests.

    Why would anyone want to execute Z80 code from MS-DOS?  Firstly, I’m not a huge fan of CP/M simply because it doesn’t support subdirectories.  When you’re working on a hard disk, CP/M gets old fast.

    Secondly, most software I enjoy on the Rainbow runs under MS-DOS anyway.  I am a nut for my favorite editor, SEDT, which might be the only editor I ever bothered learning all the hotkeys for.  I still prefer Select as a word processor over most any others (although Select is available under CP/M as well, I believe).  BinkleyTerm is possibly the best terminal emulator ever created even if it was primarily designed to be a BBS front-end mailer.

    Finally, I’d really love to see code executed on the Z80 and 8088 in a truly simultaneous fashion.  Be able to execute code on both CPUs at the same time would have wild consequences, including drastically improving performance in some situations.  It would also be an odd multiprocessing use, at least in todays terms, just because the CPUs are different architectures entirely.

    Will it be successful?  We’ll have to see…

      •   •   •   •   •
    January 31, 2014 by Jeff

    Quietly Closing a Retrochallenge

    This month has been quite trying, not in the Retrochallenge sense, but with both work and personal issues.  Nevertheless, I did meet my Retrochallenge goal of actually writing some delightful GW-BASIC source code.  After a long departure into writing a completely unusable text-mode game, I was able to build a workable game using GW-BASIC and the Rainbow 100′s Graphics Option.

    My little game is rather lame.  It is simply called Bad Robots.  The point of the game is to shoot robots that chase you.  There is nothing more to it.  I’m pretty jazzed that I was able to get a high score table working alright.  Here’s some screenshots:

    I’m quite proud of two minor aspects of the game.  First, the three sprites need to be drawn and copied into BASIC arrays right off the bat.  This is done on the title screen.  The game first displays the text you see in the first screenshot.  However, after the text is displayed, it proceeds to draw a small happy face (the player in this game) in the upper right corner, then abruptly delete it.  Similar subtle drawing operations are then done for the robot and the bullet.  After the three sprites are captured, the “Press any key…”  operation is actually handled.

    Second, I like that the sort routine for the high scores list actually works.  It took some thinking on my part to figure out how best to insert a new high score in the proper position, but I figured it out eventually.  It isn’t hard, it’s just a bit more complicated when dealing with FOR  loops, GOTOs, and arrays rather than stacks.  I’m very pleased that it is handled in-place (no secondary array or anything) and it’s written out when appropriate.

    This silly  little game is written in GW-BASIC, so it should work  on a PC as well.   The only things that would need to change are some assumptions about the screen resolution (the Rainbow has slightly higher resolution than a CGA card).  Additionally, GW-BASIC is “portable,” so this program should run in QBASIC as well.  If you do have QBASIC, everything should  work fine if you simply change the screen mode to something very hi-res  (it’ll look a little wierd, that’s all).

    The game is kinda fun to play.  The enemy robots get progressively faster as you kill more of them.  There is little to no safety checking code for creating enemy robots, so you could very well have  one appear directly on top of you, causing you to lose instantly.  It’s a minor “bug.”  The bullets are infuriatingly slow, but that’s how I wanted them to behave.

    All in all, I’d call this Retrochallenge  a success!  A game was actually  finished in GW-BASIC, and I wrote a cool version control tool along the way (which I used the whole time, by the way).  There is currently a bug in Prism, though, that causes the diff  operation to output garbage on the Rainbow.  It works on DOSBox, so I probably did something bad with a memory pointer somewhere.

    The products of this challenge are here:

    Now  that it’s over, I do wish I could have done more for this Retrochallenge.  However, just because January is over doesn’t mean I can’t continue to hack on the ‘bow!

      •   •   •   •   •
    January 25, 2014 by Jeff

    Graphics Are Faster!?!

    I’ve been truly ignoring my Retrochallenge responsibilities this month, so I sat down to spend some time with my Rainbow 100 today.  Before calling it quits completely (I don’t feel like writing Berzerk in C…), I thought I’d try moving a character around the screen using GW-BASIC’s graphics commands.

    I had originally dismissed this idea for two very good reasons.  First, because the Rainbow was historically known for rapid text routines (faster than the PC even), I was sure they’d be fast enough.  Second, the Rainbow’s graphics routines tend to be slow.

    When I last reported on my text-only zombie/Berzerk game, I said that the GW-BASIC interpreter, even with some optimizations, was way too slow to handle it.  The Rainbow would have no problem with it, but the interpreter and all it’s collision checking and key capturing just couldn’t do it.

    On a whim, I decided to implement basically the same code, along with some “lessons learned” like easing off on the GOSUB calls, using GW-BASIC’s GET and PUT commands.  This pair can be used to pull off a sprite-like experience using purely BASIC code.  These commands work wonders on the IBM PC architecture, where graphics memory is just mapped into the system memory address space.  However, the Rainbow has an entirely different architecture.  It’s graphics chip, an NEC 7220, sits off the system’s 8088 data bus (the Rainbow actually has two separate data buses, each corresponding to its 8088 and z80 CPUs, but let’s ignore that detail for now) with it’s own memory space.  Commands have to be used to copy data from the system memory to graphics memory in a somewhat kludgy way.  In addition, copying out of video memory is just as cumbersome.

    The downside of this memory architecture is that it becomes difficult to store sprites off-screen in a manner that they can be copied to display memory with any sort of reasonable speed.  Furthermore, there are maximum sizes of bits that can be copied based on the time available between screen redraws performed by the 7220 graphics display controller.  It makes graphics programming on the Rainbow… unpleasant.

    I originally dismissed any use of graphics routines in GW-BASIC on the Rainbow as possibly far too slow due to this architecture.  Furthermore, all that work would need to be done in the confines of the interpreter.

    For my sample program, I switched the Rainbow to low-resolution mode (384×220) and printed the letter “X” to the screen.  Next, I dimensioned an array and retrieved my “X” graphically:

    20 DIM XSPR(4+((7*16*20)/8)*20)
    30 GET (0,0)-(20,20),XSPR

    That glorious dimensioning formula was suggested by the GW-BASIC manual.  The 16 represents the “bits-per-pixel” (which is wrong above, actually – it should be 4…), the first 20 represents the width, and the second 20 represents the height.  Simple, right?  The second statement copies the visible picture into the dimensioned array.  The next step is to erase this initial drawing since we no longer need it.  The easiest way is to use the PUT command in the same way we’ll use it animating:

    40 PUT (0,0),XSPR,XOR

    The key element in the PUT command is the XOR action verb.  This instructs the graphics code to perform an exclusive-or while drawing the data in the array.  If the picture is already at that location, it is effectively erased.

    I then wrote some simple code to allow my control of the “X” on the screen, although this time I stuck to GW-BASIC’s INKEY$ functionality rather than the ON KEY(n) GOSUB … constructs.  The INKEY$ function simply returns the last key pressed as a string or an empty string if nothing was pressed.  I had a sneaking suspicion that the complicated key capture routines I had used before may have been contributing to speed problems.  The only catch is that I now have to use WASD for direction control rather than the arrow keys.

    When I debugged and ran the code, I was surprised by how quickly everything worked.  There didn’t seem to be any problem with speed.  The next test was to add one enemy and have it chase me around.  After throwing together the necessary code, including creating another “sprite, ” I was shocked to see that everything ran even faster than the equivalent text code!

    I now suspect that the LOCATE statement in GW-BASIC is the main culprit.  There simply isn’t any reason for the graphics version to be faster.

    Next, I want to get the graphics version up to the point where I had the unresponsive text version.  I should have some hacking time tomorrow to give it a shot.


      •   •   •   •   •