Retrochallenge: The Final Entry

Well. Here it is, the final entry for my Summer Retrochallenge project. I wanted to do more, but as so often happens, real life intervened and I didn’t have nearly as much time to work on it as I’d wanted. C’est la vie!

But I’m still proud of what I accomplished. I have a working ROM monitor, and I’m happy to report that as a final hurrah, I got it fully integrated with Lee Davison’s Enhanced 6502 BASIC.

Integrating with BASIC

I didn’t start this project thinking I’d tackle BASIC integration, but as Retrochallenge drew to a close, it seemed like it might be the best use of my precious time. What I mean by “Integration” here is simply having my ROM monitor code live side by side with BASIC and provide the underlying I/O functionality needed for BASIC to talk to the terminal and get input, plus the ability to take over and run in the foreground when the user wants it.

If you’ve ever used an Apple II, you may recall that it, too, has a built in ROM monitor, and it can be reached by typing CALL -151 into Apple BASIC. Well, when running Enhanced 6502 BASIC with my monitor, you can get the same thing by typing CALL -1239. Once you’re in the ROM monitor, you can quit back to BASIC just by typing Q. Like so:

BASIC Interop

It actually turned out to be fairly simple to get the integration working.

The first thing I had to do was re-organize my memory usage so as not to conflict with EhBASIC’s memory map:

There’s not much room left in Page Zero after EhBASIC gets done with it, but the locations $DE thorugh $E2 are free, so I made use of them. Page Two is similar, so I stuck my variables all the way up at $02A0 and on.

After that, I had to change the implementation of CIN a little bit, because EhBASIC requires that it not block. Instead, it expects this routine to return immediately and set the Carry flag if a character was read, and clear the Carry flag if no character was read. The new routine looks like this:

It’s a little ugly and could easily be optimized at the expense of readability, but hey, it works!

So that’s it. With just those changes, I was pretty much able to integrate the monitor into EhBASIC in just a few hours. As always, all of my code is available on GitHub for your viewing pleasure.

I’d also like to add that this has been a fantastic Summer Retrochallenge. There were so many amazing projects, I can’t even pick a favorite. I’ve really enjoyed reading what everyone else has been up to, and I’m already looking forward to January. Onward and upward, everyone!

State of the Retrochallenge

As I write this, it’s early on the evening of July 25th, and I’m staring next Thursday’s deadline in the face. I haven’t been able to work on my Retrochallenge entry for over a week, and it’s in poor shape.

But am I going to give up? No way. I’m going to go down fighting.

My over-enthusiastic early plans called for me to finish up my 6502 ROM monitor so early that I’d have time to work on cleaning and sorting my RL02 packs. That, needless to say, is not going to happen. Instead, I’m going to concentrate on polishing up and documenting my monitor this weekend. Whatever I have ready to go next week will just have to be good enough. It won’t be as fully-featured as I originally wanted, but at least it’s something, and at least it works.

I have to pick my remaining features pretty carefully now. I want to enhance the Deposit and Examine commands to add syntax that will allow auto-incrementing the address, and then work on tying my monitor into EhBASIC, so I can run it on my home-brew 6502 after Retrochallenge is over.

It’s a race to the finish, now. Expect an update from me on Sunday or Monday. Until then, I’m face down in the code!

Parsing and Tokenizing

I’m fairly happy with my parsing and tokenizing code now. I wanted to give a little breakdown of how it works.

The over-all goal here is to take a command from the user in the form:

C NNNN [(NN)NN [NN NN NN NN NN ... NN]]

where C is a command such as “E” for Examine, “D” for Deposit, etc., and store it in memory, tokenized and converted from ASCII to binary.

I wanted to give the user flexibility. For example, numbers do not need to be zero-padded on entry. You should be able to type E 1F and have the monitor know you mean E 001F, or D 2FF 1A and know that you mean D 02FF 1A.

I wanted whitespace between tokens to be ignored. Typing "E 1FF" should be the same as typing "E    1FF "

And finally, I wanted to support multiple forms of commands with the same tokenizing code. The Examine command can take either one or two 16-bit addresses as arguments—for example, E 100 1FF should dump all memory contents from 0100 to 01FF. But the Deposit command takes one 16-bit address and between one and sixteen 8-bit values, for example D 1234 1A 2B 3C to deposit three bytes starting at address 1234

So I decided I’d reserve 20 bytes of memory in page 0 to hold the tokenized, converted data.

  • Byte 0 stores the number of arguments entered, not including the command itself
  • Byte 1 stores the command, for example “E” for Examine or “D” for Deposit.
  • Bytes 2 and 3 store the first argument, which is always a 16-bit address value.
  • Bytes 4 and 5 store the second argument, which is sometimes a full 16-bits, and sometimes only 8-bits occupying byte 4.
  • Bytes 6 through 20 are always 8-bit values.

Each implemented command then knows how to use this parsed data to fulfill its operation.

[An aside: Using page zero for this is controversial in my mind — 21 bytes is a lot of space to use, and page zero is precious, so I will move it to page 2 at a later date. But it will work the same, just a change in addressing mode from Zero Page,X to Absolute,X would be required.]

How It’s Implemented

The implementation is fairly straightforward. Here’s a very rough flowchart with some details elided:

flow

In general, the idea is to start at the beginning of IBUF, the input buffer, and scan until the start of a token is found. This location is then stored in TKST. Next, we continue scanning until the end of the token is found. The end is stored in TKND. Once it is, we walk the token backward, one character at a time, converting it from a hexadecimal ASCII representation into a number. Once we’ve reached the start of the token (or we’ve done 4 characters, whichever comes first), we know we’re done with the token. We jump back to TKND and start the process over again.

We do this until either:

  • The buffer is exhausted, or
  • We’ve scanned 17 arguments, total

whichever comes first. At that point, we fall through and start executing whatever command has been decoded.

The code is below.

Examining Memory

Today is kind of a big update, and not all the source code will be presented in-line here in the blog post. As always, you can look at the current source Here at GitHub.

Good news, everyone! After quite a lot of hacking, I can examine memory with my monitor. It’s a very primitive feature, right now: I can only examine one byte at a time, so I can’t dump memory regions yet. But hey, it’s a good start. Let’s dive into the code.

Here’s the entry point, where the CPU jumps after being reset:

The code starts by making sure interrupts are disabled, then clears the Decimal Mode flag and sets the stack pointer to $01FF (it could be anywhere when the processor starts up!). Following that is the IO initialization routine, which is probably pretty familiar by now. We just set up the ACIA for 9600 baud 8-N-1 communication.

Then things start to get more interesting:

Here, we’re clearing the entire contents of Page 2 ($0200 to $02FF) in order to use it for scratch space. I’m using page 2 to store whatever the user types on the command line, which I’m referring to as IBUF (the Input Buffer). I’ve named this entry point HRESET, in case I want to be able to jump to it directly in a future version of the monitor.

Next, we welcome the user and start looping on input, parsing one line at a time.

There’s kind of a lot going on in this code, so let’s take it a little bit at a time.

At the very start, we print out a message (using our STR macro) that welcomes the user. Then, we print a “*” prompt, do some housekeeping, and finally wait for input. It looks like this when it’s running:

Running

EVLOOP is the main Eval Loop that takes a line of input and handles it. The NXTCHR block is the meat of this code. Every time a key is pressed, the character is appended to the IBUF input buffer, located at $0200. We’re using the Y register as a pointer into the buffer.

If the user ever presses the backspace key, there’s a separate routine that handles that. It decrements the Y pointer and echoes the backspace character to the terminal.

And when the user presses ENTER (which generates a Carriage Return/Line Feed combo), we jump immediately to the PARSE parsing code to handle the command.

And this is where I totally cheat:

This is a lot to take in, I know. But basically, it parses the command into two parts: A one-letter command, and a one- to four-letter operand.

The idea is that a user should be able to enter a command like this:

and be able to see the contents of memory address (“examine”) $01FF. I’m cheating because I totally ignore the value of the first letter, the “E” — right now, ONLY the examine command is implemented, you could use any letter in place of E. Ssshhhhh, don’t tell anyone. I’ll fix that later. Really.

But the rest of the code is not cheating. It works by using two pointers, Y and X, to walk the input buffer until the operand is found. When it is, Y will point at the start of the operand. Then we increment the second pointer, X, until it’s pointing at the end of the operand. At that point, we start taking the operand backward, one character at a time, and convert it from hex to binary. The value is stored in page zero in locations OP1H (high byte) and OP1L (low byte). If the input is malformed or isn’t hexadecimal, we abort by printing “?” to the console and then jumping back to the EVLOOP entry point.

When the operand has been decoded, we call another routine, PRADDR, to print the address, followed by a colon and space. Then, finally, we use PRBYT to print the contents of the memory at the desired address. Whew, that’s a lot of code. I haven’t shown the implementation of PRADDR or PRBYT here, but the source, as always, is up on GitHub. Feel free to dig through it if that’s your thing.

When running, it looks like this:

In Use

That’s plenty for one day. I hope to refactor this code a little and clean it up tomorrow, so I can start adding more commands.

Getting Input

I’m moving kind of fast here because I really want to get to the meaty parts of the code, but I want to cover how I’m getting input from the console into the system. As you might expect, it’s all about reading from the ACIA instead of writing to it.

Recall the ACIA’s addresses:

To read in a byte of data, we’ll first need to check the IOST status and see whether bit 3 is a 1. If it is, there’s data to read. On the other hand, if it’s a 0 the ACIA doesn’t have any data for us. We can just loop checking that status. This is a technique called polling, and it’s the same technique we used for writing to the ACIA.

Here’s what the code looks like:

Pretty straightforward. Now we can use this in conjunction with COUT to just read in keyboard input, and echo it back out to the console.

But for my monitor, I want to do something a little bit more. I want to convert all lower-case letters to upper-case, primarily so later on when I’m parsing input, I know everything will be in the same case. Turns out we can do that with a pretty simple AND mask. But we only want to do it if the character is a-z, inclusive, so we’ll need a little branching magic.

Here’s my change:

Now if the character is < 'a' or >= ‘{‘ (which comes right after ‘z’ in ASCII land), we’ll skip the mask. But if it’s between ‘a’ and ‘z’ inclusive, we’ll apply a mask of $5F to it, which will strip off bit 5 and convert lower case to upper case.

That’s it for today. Tomorrow we’ll tackle saving input to memory and parsing it for commands.

Oh, and if you’re in the United States, have a happy and safe Independence Day!

Our First Macro

Last night I got string output working. But what if I want to make string output generic? I want to write a STOUT (STring OUT) subroutine that can take the address of any null-terminated string and print it to the console, but there’s a problem: The 6502 is an 8-bit machine, so passing a 16-bit address as an argument takes some wrangling.

To get around this, I’ve designated two locations in the precious Zero Page (arbitrarily choosing $20 and $21) to store the locations of the low byte and high byte of the string’s address, respectively.

Now I can write my STOUT subroutine. The change here is that I’m going to use the Indirect Indexed addressing mode to look up the characters of the string from the zero-page addresses, instead of the Absolute,X addressing that I used in my last post.

But calling this subroutine is still a little weird. Each time I want to print a string, I need to put the low byte of the string’s address in $20, the high byte in $21, and then call STOUT. It’s just slightly unwieldy, so… enter our first assembler macro!

The ca65 assembler supports putting a series of instructions together into a macro call, sort of like an inline function in C, so that code can be re-used without the overhead of a subroutine call. I’ve created a macro named STR that takes a 16-bit address and prints the string to the console.

Great. Now all I have to do is call STR to print my “HELLO, 6502 WORLD!” string over and over again.

With this slight diversion out of the way, next up will be console input, which I promised to talk about today. But that’s a subject for another post.

Talking to the World

Now that I have a skeleton 6502 assembly project set up and building, it’s time to get going and writing some real code for the ROM monitor. But wait, there’s just one more thing I need to get set up, and that’s an emulator so I can test code easily. Without an emulator, I’d have to flash a new ROM image to an EPROM and put it into a real 6502 computer every time I wanted to run it, and debugging would be very, very hard. Luckily for me, I wrote a 6502 emulator called Symon a couple of years ago! You can download it from GitHub if you want to follow along.

Symon will allow me to run code from a ROM image, single-step through the instructions, and examine the state of the registers and memory for debugging purposes. With that up and running, I can finally get started.

Console IO

The 6502 computer I’m using talks to the world through a 6551 ACIA located at base address $8800. The ACIA has a couple of special registers at the following addresses

Address Name Description
$8800 R/W Read and Write data to and from the console
$8801 STATUS Read the current status of the ACIA
$8802 COMMAND Write set-up commands to the ACIA
$8803 CONTROL Control the ACIA’s baud rate generator

In order to write data out to the console, we first need to check the status register and make sure that bit 4 (“Transmitter data register empty”) is set to a 1. If it is, we just write the character to address $8800 and it will be sent to the console. If it’s a 0, we just wait for it to become a 1. Easy! Here’s what it looks like in assembler:

First, we’ll define some constants:

And then, we’ll write the code:

I’ve named this subroutine COUT. It will be used quite a bit by the ROM monitor.

It also demonstrates one of my favorite features of the ca65 assembler: cheap local labels. Any label starting with an @-sign is only has scope between the nearest two regular labels, so I can re-use the name @loop in other contexts without worry. Very convenient!

Testing It

OK, so let’s test it. First, I’ll change my monitor start-up code to set the baud rate properly, then I’ll print a single “@” to the console, and finally enter an infinite loop.

Now I’ll compile it and test it in the emulator.

ACIA Test 1

Rock on! That worked! I can print a character to the console now.

Going Further

If I can print one character, I can print lots of characters. Let’s make a tiny change to print “@” to the console continuously instead of just one time, by adding a label and changing where the BNE branches to.

Now we get a continuous stream of “@” printed to the console, just as predicted

ACIA Test 2

Wrapping It Up

The final thing I’d like to do for today is get whole strings printing to the console. Sure, it feels good to print a character, but wouldn’t it feel better if we were doing more?

Let’s start by defining a string to print. Let’s just make it a variation on the standard “Hello, world!”

Now, we’ll modify the printing code to use the X register as an offset into the string, indexing into it character by character. Since the string is null-terminated with a 0, we’ll always be able to tell when we’re at the end of the string.

And voilà! It’s working.

acia_test_3

OK, that’s enough for tonight. As always, you can follow along by looking at the project on GitHub as I go.

Tomorrow, I’ll tackle the other direction – reading input from the console.

Baby Steps

Before we get our ROM monitor off the ground, we’ll need to sort out a few things first. The most important decision will be what assembler to use. I’ve decided to go with the CC65 tool chain, because I’m already familiar with it and I don’t have a lot of time to come up to speed with a new assembler. Now, a word of caution: CC65 is no longer being developed, so its fate is uncertain. There are other assemblers out there, and if I were doing this outside of Retrochallenge and had more time, I would probably look into them. Chief among these seem to be Ophis, a 6502 cross assembler written in Python, and XA, a venerable cross assembler with a long history.

Now that I’ve picked my assembler, it’s time to set up the project. I’m going to get things rolling with a very simple skeleton directory. If you want to follow along in real time, the project is hosted here on GitHub

I’ll explain what each of these files is for.

Makefile

Let’s start with the Makefile. This is a pretty bog-standard Makefile that knows how to assemble and link the source code in monitor.asm.

This is pretty self-explanatory. Typing make will use ca65 to assemble the monitor.asm file and output the object file monitor.o. Then, the ld65 linker will link the object file and produce the final ROM image, monitor.rom.

That symon.config file needs further explanation.

symon.config

This file is basically a memory map of the RAM and ROM in the system I’m targeting. For me, this is my home-brew 6502 with 32KB of RAM and 16KB of ROM. The MEMORY section is the full memory map, and the SEGMENTS section spells out where the assembly language segments live in the memory map.

monitor.asm

Finally, there’s the assembly source code itself, in monitor.asm. Right now, it does absolutely nothing:

Notice how each .segment pseudo-op maps to one of the segments from the symon.config file? That tells the linker exactly where to put things in the final ROM image.

Right now, this code really doesn’t do much. On reset, the 6502 will load the address located at $FFFC and $FFFD into the program counter and start running from that address. Here, the destination is the address of the START label, which works out to $FB00. The instruction at that address loads $00 into the accumulator, which sets the zero flag. The very next instruction tells the 6502 to branch back to itself if the zero flag is set — an infinite loop.

That’s it for today. I just wanted to get a project off the ground and give me a framework to start developing in. More tomorrow.

Getting Started

Here it is, Retrochallenge Summer 2014. It’s time to get started. It’s time to write a ROM monitor.

But just what is a ROM monitor? In simple terms, it’s a program that gives the user direct control over the lowest level of a computer. Typical monitors will, at the very least, allow a user to read and write memory contents directly using a very simple command-line interface. Now, when I say “very simple”, I mean primitive. ROM monitors usually don’t have such luxuries as user help or warnings or anything of the sort. No. They allow you to shoot yourself squarely in the foot, using whatever gauge you happen to have on hand.

My goal is to build a full-featured but very simple ROM monitor for the 6502 that offers the following features:

  • Built-in help
  • Simple line editing (backspace, if nothing else)
  • Read and write single memory addresses
  • Read and write memory ranges
  • Show contents of Accumulator and X and Y registers
  • Show contents of the Processor Status register
  • Allows starting execution from an arbitrary address

A secondary goal is to be very clear and well-documented code. It will likely be much larger and take up more ROM space than it needs to be. A superior 6502 programmer may even look at it and wince. But that’s OK. As long as it’s readable and easy to understand, I will be happy. I’m not a ROM hacker, I don’t need to fit my monitor into 128 bytes of code!

By necessity, of course, the monitor will be written in 6502 Assembly. That means my very first step will be picking and setting up a 6502 assembler and development environment. More on that tomorrow.

Retrochallenge 2014

Well here we are again. It’s June of 2014, and I have to come up with a Retrochallenge entry.

Last year I decided to tackle an original hardware design and convert a VT100 keyboard to USB. It was a lot of fun, and a lot harder than I thought it would be. This year, I just haven’t been able to come up with a cohesive and innovative idea for something to do, so instead, I’m going to do two smaller projects.

6502 ROM Monitor

The first thing I’d like to do this year is finally put together a simple 6502 ROM Monitor program for my Home Brew 6502 computer. It runs EhBASIC right now, but there’s no interactive ROM monitor or debugger. For a while I had Steve Wozniak’s ROM monitor running, but I’d like to roll my own.

I don’t think this will be a particularly hard challenge, but I do think it will be fun and educational. It’ll probably also leave me with time for a totally un-related Part II…

Clean and Rehabilitate My RL02 Drives

Part two of my Retrochallenge will be to finally open up, clean out, and rehabilitate the RL02 drives on my PDP-11/23+ computer. It’s been sitting in my garage for years, and those drives need some attention. They both allegedly work, but they’re dirty, they (probably?) still have old crumbly air filters in them, and they probably need some regular maintenance before I turn them on. So I’m going to follow DEC’s care procedures, clean them, clean my packs, and get them up and running again. I think it’ll be fun to document this and make some videos out of it.

And In Closing…

I hope I haven’t bitten off more than I can chew, but I really think that these projects are small enough for me to get them both done in a month. I believe the ROM monitor will be two weekends, and the RL02 drives will be one weekend. We will see! I’ll do them serially, so I’ll try to get at least one of them done.

I’ll be seeing you all soon! July 1st is coming up fast.