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!
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 .
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 via string substitution. I also hope to add the ability for the system to assemble the shorter original 6502 instructions eventually too.
The registers and RAM are persisted in a Postgres database, so all instructions need to take an extra hop to deal with the database. For example, loading a register in the virtual system translates to updating a row in the table representing registers.
The CPU API server can take and execute individual instructions through JSON API calls from React. The result 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.
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, and the current instruction address illuminated. This serves as a handy visualization of what the CPU is up to since you can see what paths it's following by just tracking the moving light.
Programs can be entered through the frontend, sent as JSON to the server, assembled and entered into memory serverside, and then run on the server with a single run command from the client.
Alternatively, a program can be run exclusively through HTTP API calls, driven by the client issuing HTTP POST requests at a user-controlled interval. This is called step mode, and 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.
Since the RAM is stored 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.
As with an actual 6502, 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.
Performance: server-side run: 130 instructions per second, client-driven step mode: 20 IPS due to limitations of repeated fetch calls on client side
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 Express backend server API.
- Express backend recieves POST request with the user command and determines which opcode and address mode are being used per the K9B ASM definitions JSON file.
- 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. It will then update those values in the database as necessary.
- 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 Express server to pull the new register and RAM values from the database 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 Express backend server API.
- Express backend translates command and loads String Scroller program from CSV file into RAM database table.
- 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. The monitor also starts updating RAM at the user-specified "refresh" or "step" interval in ms. Default will have it GETting updates 5 times per second as the program runs from this point onward.
- 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.
- 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.
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 you could get with it nonetheless, but it does impose some major limitations, especially on speed, given the latency for each instruction. I intend to implement a WebSocket interface between the backend and frontend next, which should be better suited to this application.
- I also want to allow people to have their own instances, rather than one shared system as is the current case. Building that out will be another priority.
- Currently the demo programs only use 00 and ff for the "display" RAM. I'd like to experiment with the other 254 available shades.
- 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.