ABOUT K9B-64

Overview

Welcome to the "World Wide Web" home of the K9B-64. Here is a quick rundown of what is going on here:

  • The K9B-64 is a virtual 8-bit computer modeled after the microcomputers of the 1980s such as the Commodore 64, Apple II and Atari 800.
  • Under the hood, the K9B-64 is a web application with a database, backend and frontend operating more or less as you would expect. The virtual system lives on the server and is accessible from any web browser.
  • First and foremost this is meant to be something fun and relatively easy to use. It doesn't matter if you know anything about assembly or not, feel free to click around and if you break something, it doesn't matter! Just click the "PROG A" or "PROG B" buttons to get back to a working state at any time.
  • One of the fun things about the monitor (Fun Zone) is that you can break stuff and it looks interesting. Try clicking "PROG A", run ("|>"), and then in the RAM grid, find address (the top number of each cell) 00e8 and click on it while the program is running for a display glitch!
  • For now this is just a minimal proof of concept. More features and documentation will be added as I continue to work on it.
  • The demo system available on the Fun Zone page is currently only actually using about 4 KB (4,096 bytes) of RAM! It can address 64 KB, so there is lots of room to grow!

Technical Discussion

This is a Javascript Node/Express API pretending to be a 6502-based 8 bit system and a separate React frontend serving as a kind of monitor, assembler and visualizer for that system. They run almost completely separately although they do have a single shared dependency defining the opcodes and address modes of the K9B assembly language.

This language, as it happens, is nearly identical to 6502 assembly with a couple of exceptions:

  • the mnemonics of certain operations where I added some vowels and underscores for legibility, e.g. "lda" becomes "load_a", "sta" becomes "store_a", and so on
  • Immediate address mode values, previously written in the format "#$12", are now written as "#12"

As such, you can make use of existing 6502 resources. I also hope to add the ability for the system to assemble the shorter original 6502 instructions eventually too.

Registers are displayed on the monitor as numbers, but RAM appears as a grid with addresses, values and opcodes displayed on each cell, and a cell background color determined by the value. The cell of the current instruction address is illuminated, which serves as a handy visualization of what the CPU is up to. You can see what logical paths are being followed by just tracking the moving light.

The CPU API server can take and execute individual instructions entered by the user through a command text area on the frontend. These are delivered to the server as JSON over HTTP from React. The result (success or failure) of each instruction is sent from the server back to the client. The client then pulls the updated registers and RAM data from the server.

Multi-line assembly programs can also be entered through the user command text area on the frontend, which sends them to be assembled and entered into memory serverside. The program can then be run from that server memory with a run command from the monitor.

Once the run button is clicked on the monitor frontend, it will use a WebSocket connection to get updated state from the server at the user specified refresh interval as the program is executed on the Node server.

Alternatively, a program can be run exclusively through HTTP API calls. In step mode, the run is driven by the client issuing HTTP POST requests at an interval to execute and step to the next instruction. The step interval can be controlled by the user in the client.

The Fun Zone React UI sends JSON commands for user interactions as well:

  • Clicking on a RAM cell at address $0123 will send a "jump $0123" command for that memory address. This can be done while a program is running for interesting glitches!
  • If the "paint mode" checkbox is checked, then clicking on RAM will instead issue a "store_a $0123" command, which will write the value of the A register into the selected ram cell. This can also be done live during execution. For instance, in the String Scroller program (PROG B), you can use paint mode to alter the characters stored starting at $d000.

As the program runs, the registers and RAM of the virtual system are copied to a Postgres database every nth instruction.

Since the RAM is persisted in a database, the system can dump ROMs as CSV files.

Those ROMs are currently the only way to save and load programs via the PROG A and PROG B buttons on UI. They are stored on the server side as CSV files. The ability to dump and load custom ROMs from Fun Zone should be coming soon!

Interrupts are modeled as a specific instruction that can be submitted from the monitor frontend as JSON with a parameter. The parameter value will then be written into a preset location in virtual RAM and an interrupt subroutine will be executed based on the two byte vector defined at address $01fe (currently hardcoded for demo but will be defined.

Setting the I processor status flag will prevent another interrupt from being triggered while the processor is already inside an interrupt service routine. This system does not currently allow for non-maskable-interrupts, though, so any interrupt can be blocked like this.

The CPU uses a simple semaphore to queue instructions that arrive while another is already executing.

Example Instruction Flow

  • User enters a command through the form in the Fun Zone (React frontend), e.g. "load_a #01".
  • React form submit handler turns user command into JSON and POSTs it to the Node backend server API.
  • NODE API recieves POST request with the user command and determines which opcode and address mode are being used per the K9B ASM definitions module.
  • Server CPU handle method takes intermediate instruction (opcode, param, address mode), translates into machine code, and executes it on the virtual CPU. This handler will compute the correct new values based on the current register and ram database tables.
  • If it is the nth instruction (currently 100th) or a break, registers and ram will be stored in the database.
  • Once the handler has successfully executed the instruction, it will return JSON to the frontend with a success message and command number to be displayed to the user.
  • The React frontend recieves the success message in the response from its original POST request and displays it to the user.
  • The React frontend makes a GET call to the Node server to pull the new register and RAM values from the server and updates the monitor display with the results.

Example Program Run Flow

  • User clicks a "PROG A" button to load the String Scroller demo program.
  • React "PROG A" button element triggers a "prog" instruction to be POSTed as JSON to the Node backend server API.
  • Node backend translates command and loads String Scroller program from CSV file into virtual system RAM.
  • Backend returns that program loaded successfully, and frontend monitor then GETs updated memory from backend.
  • Monitor sets program counter to starting position specified in ROM.
  • User clicks Run button (with the symbol "|>") and it sends the command JSON for the "run" instruction.
  • Backend recieves "run" instruction and starts a run loop from the current program counter. It will continue to run on its own from this point until it receives a "break" instruction.
  • For standard program running, a WebSocket connection handles refreshing registers and memory data on frontend at user-specified refresh interval. In step mode polling via HTTP GET requests is used.
  • User clicks pause button "||" on frontend, which sends a JSON command for a "break" instruction. This stops the refresh/step updater as well.
  • Backend receives "break" instruction and stops the current run loop. It will now be dormant until it gets a new external command.
  • Virtual system registers and RAM are dumped to the database on break, ready to be loaded on server restart.

Future Development and Directions

  • Clearly HTTP is not the ideal way to update the monitor for a running computer. I was intrigued to see how far one could get with it, but it does impose some major limitations, especially on speed. The normal run mode now uses a WebSocket connection to refresh the UI, however step mode still uses HTTP for now.
  • Earlier versions having every virtual CPU operation involve the database was an interesting development challenge, and allowed me to establish a baseline for what was possible. As you would expect though, it was a huge drag on performance. The current version performs most operations in server memory instead, and only copies to the database at a set interval (every 100 instructions and on break, currently). This vastly sped up performance while keeping memory backed up in the database for later retrieval.
  • I have been focused on getting a single shared public system working as smoothly as possible, but eventually I want to allow for users to have their own instances, rather than one shared system as is the current case. I am thinking eventually it could be like a shared document accessible only to a group of people you invite.
  • Currently the demo programs only use 00 and ff for the "display" RAM (which is just regular RAM $0000 - $0080 for now). I'd like to experiment with the other 254 available values since this sets the background color of the cell, it would effectively mean more colors.
  • RAM cell React elements could be arranged in interesting ways, flowing around text for example. I'd like to use that in a tutorial section to demonstrate basic operations.
  • Overall, I could see this turning into an interesting educational tool if I'm able to improve performance and flesh it out with fuller functionality.

Inspirations and Resources

I have gotten a lot of enjoyment out of the C64 emulation tool ICU64 that visualizes system memory in real time and allows you to tweak it through a point and click GUI. I thought it would be interesting to try and do that in a web setting. Going forward, I would like to try and get functionality closer to that of ICU64.

I found Easy 6502 very handy for learning 6502 assembly. If you have any curiosity give it a try, it breaks it down really well and provides you with an integrated Javascript 6502 simulator. I would eventually like to build out some tutorials here in the same vein.

This is not meant to be a precise 6502 emulator though, rather something closer to a Fantasy Console like the Pico 8 or TIS-100 that happens to be built on a 6502-esque system .