Microsoft BASIC
A picture tells the story here:
It’s BASIC! And not just any BASIC– it’s the venerable Microsoft BASIC that I remember fondly from the Apple II, Commodore 64, and other machines of that era. The version shown here is a plain-vanilla build that makes no assumptions about memory size or terminal capabilities. I think it was originally used on the KIM-1 computer.
BASIC took longer than I’d expected to get working, and in fact it still doesn’t work correctly. Any reference to a string variable or a PRINT statement with a string literal will lock up the computer. It works in the simulator, though, and I had it print “HELLO STEVE” infinitely on the real hardware earlier, but then I tweaked a few things and broke it somehow. Hopefully it won’t be too difficult to fix.
The main obstacle to getting MS BASIC running correctly on BMOW was the imperfect emulation of the 6502 instruction set. As I’d mentioned earlier, I never implemented some 6502 instructions, and others I implemented slightly differently than the 6502. I had to write microcode to implement several dozen 6502 instructions that BMOW was missing, which were mostly the ones that used page zero addressing. I also had to harmonize which condition codes are modified by each instruction. That was a slow and tedious process that would have been impossible without the BMOW simulator.
Another source of problems was BMOW’s stack. The 6502 has a 256 byte stack at $0100 in memory. Since it’s only 256 bytes, the stack pointer register is just 8 bits. BMOW has a 24-bit stack pointer that can point anywhere in the 16MB address range. Most of the explicit references to stack memory in the MS BASIC code used a STACK symbolic name, which was easy to change. But there were also some hidden assumptions about the stack location in the address arithmetic of parts of the code. Again, finding those would have been impossible without the simulator.
Of course I had to implement BMOW-specific I/O routines for text. I still don’t have CTRL-C working. And LOAD and SAVE don’t do anything, since there’s no storage medium from which to load or save.
The final challenge was working with two different assemblers. Up until now, all of the assembly code I’ve written has been assembled with an assembler called ACME. I’ve made lots of changes to ACME so that it supports all the instructions and addressing modes that are unique to BMOW. The MS BASIC code I used was written for the ca65 assembler, however. That meant I couldn’t use any BMOW-specific instructions or addressing without resorting to hand-assembled bytes included into the source code. It also meant that I couldn’t easily incorporate my existing keyboard, video, and console libraries into MS BASIC. My eventual solution was assemble the BMOW libraries with ACME, and include them as binary data in the ca65 source files. That worked, but it meant none of the labels or constants defined in the ACME source can be referenced from the ca65 source, which is a pain in the butt.
If I get the problems with strings sorted out, and generic BASIC programs running OK, then I’ll probably look at adding some BMOW-specific keyboard to BASIC for drawing graphics and things.
Read 9 comments and join the conversation9 Comments so far
Leave a reply. For customer support issues, please use the Customer Support link instead of writing comments.
Want some sin-cos-tan-atn? NO! Happy birthday BMOW!!
Ugh, it looks like the problem with strings is not a simple bug at all. There seems to be some kind of hardware glitch that’s overwriting memory, or at least that’s what it looks like. It doesn’t happen on the simulator, so I don’t think it’s a program error, but rather some kind of problem with the hardware.
With the logic analyzer, I was able to see that whatever value is read from address $149F has two bits that are zero when they should be one. This persisted even when I rewrote the code so that a different instruction fell at that address. I thought perhaps I had a faulty RAM, but I wrote a simple memory-test app, and it passed just fine. I tried changing the code to jump around that address completely, but then BASIC just started failing in other ways, so there must be more than one place where this is happening.
Doh! I hate these sorts of issues.
Hopefully (or maybe not) you can rule out wiring since it passes a memory check… Must be something evil about that address, maybe it isn’t being written correctly, hopefully not an interference problem
This is really impressive. One thought about your memory corruption problem: you may care to have a look at the RAM timing. Is the address stable when the RAM write line is low? I’ve had trouble with changing addresses causing writes to the transient addresses. For reliable operation, you should observe the setup and hold times in the datasheet as well.
A good technique is to run a tight loop that exercises a faulty location. Then you can look at the entire address bus with a ‘scope, one pin at a time, without taking all day. Just a thought.
Good lock, and congratulations on the chess and basic!
I don’t think it’s a wiring error, but it is quite possibly a wiring “design problem” that introduces just enough noise somewhere under some circumstance to cause a problem. It could also be a timing problem as Merlin suggested, or some kind of race condition that causes a brief glitch on one of the memory lines when some rare condition exists that happens to be triggered by MS BASIC.
For a glitchy problem, though, the problems seems very consistent. It fails every time in MS BASIC, even when I rewrite the section of code surrounding the problem address. Actually, now that I think of it, that probably just means the actual problem is some other piece of code that happens earlier, that corrupts the address.
My plan is to add code to confirm that the memory address has the correct value when BASIC starts, and then more checks at various points in the code, to try to narrow exactly where it goes wrong. That may be enough to solve the problem, or at least to isolate it to a small piece of code that I can then run repeatedly in a test setup like Merlin proposed.
It’s likely to be a while before I make more progress on this. Between a very busy period at work, Christmas activities, and some health problems, spare time for BMOW is in short supply.
You mentioned that you have a logic analyzer. How about setting it up to trigger on writes to that address? That might give you a clue about what is happening (if it is a software problem). As you’d be able to see the instruction fetches too, and so would have the address of the errant code.
I once had a similar problem. This turned out to be the code assuming it was in ROM. If I remember correctly, some floating point code would, under certain circumstances, calculate a value it would later discard. It achieved this rather neatly by changing the destination address to point to ROM instead of the more normal RAM. This saved a later check to see whether or not the write should take place. Since my code was actually in RAM, it overwrite something critical! It took me ages to find that one…
Good luck!
Merlin
That was the first thing I tried with the logic analyzer. It didn’t show any accesses (reads or writes) to the offending address between the time the program was first loaded and when it later tried to execute the instruction at that address. Yet the value at that address somehow changed during that time.
[…] It even runs Microsoft Basic! […]
[…] You might decide to construct an 8-bit CPU out of ICs and a big mess of wires, and cobble together your own port of Microsoft BASIC. […]