Monday, March 30, 2020

6502 emulator for terminal, Commodore Edition

(Update: Commodore 64, 128, VIC-20, PET, and Commodore 16, Plus/4 aka TED series)

This is a simple 6502 emulator.  And a very simple Commodore emulator.  If you like console windows and command lines, this may be your emulator, especially if you like both 6502 and BASIC.  And if you prefer C#, check out the emulator sources (link at end).



Simple

How simple is it?  It is so simple!  It turns out you can craft a 6502 emulator, hook just the character in/out routines, do a little memory management, feed it Commodore ROMs, and you've got a retro computer in a terminal (Windows Console in this case).  If you implement the 6502 accurate enough, it just works.

Commodore 64 and VIC-20 ROMs don't really care if there is any real hardware I/O available or not.  All memory is zeroed to begin with, and I've treated the I/O areas as RAM so the I/O values can be retrieved.  And it seems to work for booting the system.

The only two kernel JMP table entries patches are CHRIN ($FFCF) and CHROUT ($FFD2).  By intercepting CHRIN completely, the screen editor is bypassed, but by allowing the CHROUT to continue, screen memory is updated simultaneously to the Windows console.  Note that this emulation has lazily not implemented hooks to writes to screen memory, though you are free to implement this as you desire adding code to the SetMemory() method within the C64Emu, Vic20Emu, or other appropriate class.

The goal was to access the BASIC command line.  And using the Windows Console ReadLine(), we now get those history and editing features instead:  Home, End, Del, Backspace, Cursor Up/Down/Left/Right, etc.

If you're itching for some simple BASIC command line, including entering programs line by line, you are all set.  Until you want to SAVE, LOAD, VERIFY.  Sorry, not implemented (yet?  use the Windows clipboard for now).   Full LOAD/SAVE implementation exercise left to the reader.  ;-)   Yet, since it is a faithful 6502 emulator, if you can get your own machine code loaded, you can run that too.

Commodore PET 2001 series, Commodore 16 and Plus/4 (TED) emulators are available too, required some additional changes including RAM banking for the TED series.

Update (2020-04-14) Commodore 64 now includes banking support for RAM/ROM switching.

Update (2020-05-4) Commodore 128 support added including MMU support for banking, etc., yet skips the Z80 support.

Want to work with another system like an Atari 400/800 series, Apple II, BBC Micro, Atari 2600 cartridge, or NES ROM?  I'm not going to stop you.  The 6502 emulation is here to use.  Though the video game systems probably won't work well in a console.  Not sure what the minimal emulation is required to get BASIC working on the others.


Limitations:

  • Only keyboard/console I/O.  No text pokes, no graphics.  Just stdio.  No asynchronous input (GET K$), but INPUT S$ works.  No special Commodore keys, e.g. function keys, cursor keys, color keys, STOP/RESTORE, etc.  (Could improve this.)
  • No keyboard color switching.  No border or border color.  Text and background colors are implemented, but disabled by default because mapping to the standard console colors results in some fairly horrible color combinations.
  • No screen editing (gasp!) Just short and sweet for running C64 BASIC in terminal/console window via 6502 chip emulation in software
  • PETSCII graphic characters supported only as an option if you include petscii on the command line and use a third-party font C64 Pro Mono (note: has own individual license restrictions), otherwise without PETSCII only supports printables CHR$(32) to CHR$(126).  But does support CHR$(147) for clear screen.  Now also supports Home/Left/Right/Up/Down output characters too.
  • No timers.  No interrupts except BRK.  No NMI/RESTORE key.  No STOP key.
  • No loading or saving of files implemented (but Windows clipboard works!)
  • No cartridges.
  • No joysticks.
  • No sprites.
  • No graphics (you know this is a terminal/console window right?)
  • Yikes, that's a lot of no.  Seriously, it is a simple emulator.  Emphasis on simple.
  • Lightly tested.  Bugs are lurking! 


Trace Execution:

It is possible to trace some or all of execution by setting the Emu6502.trace to true, and it will produce debug output (see in your IDE, or capture with DebugView).  Note that this slows down the emulation significantly, but provides very good insight.   Registers and flags are shown to the left of the statement's disassembly, in their states prior to executing the statement.  Currently, referenced memory is not shown, that would be a good enhancement for the future.


Walk a ROM:

Another feature is the ability to walk a ROM method without actually executing anything.  It traverses all subroutines, and both cases of branches (not taking the branch first, then when exhausted, taking the branch).  Each statement is disassembled exactly once.  Note that JMP($00FE) or similar will probably disassemble as BRK, unless you specifically code loading memory (see C64Emu for an example).   Pushing values to the stack (say from a table) and using RTS is not supported, as the walk isn't executing anything or remembering variable assignment, so dynamically pulling values from specific entries in a table is not supported directly (but see C64Emu.Walk() that implements a workaround).

If you initialize enough low memory, and walk all the entry points including addresses from tables, then the disassembly should be a view to the complete code coverage possible (and some impossible branches and paths too).  Note it is recommended to pass the output to sort (shown below) so the code is organized in memory order.  You could effectively get a ROM disassembly (without data tables).  More work could be done to show the data tables and/or unreferenced code/bytes.

simple-emu-c64 c64 walk | sort | more
simple-emu-c64 c64 walk FF3D
simple-emu-c64 vic20 walk FFCF FFD2


Development Environment:

Developed in beautiful C# using Microsoft .NET Framework 4 in Microsoft Visual Studio 2017.  Should be portable to other languages without much difficulty (it really is a simple implementation).  And should be able to recompile on Mac or Linux with a minimum of changes if any.  Submit your pull requests if you do something cool that would be generally useful.  Or just fork your own version. 

History

So in the process of my retro binge session the last year or so, I did come across another BASIC for the console.  I remember at least reading it, maybe even trying it out.  But time passed.  And I was recently puzzled on how it actually worked.  Problem was, I couldn't find it, which caused me to be even more puzzled.  I googled and googled, but the keywords are so common, it is hard to sift through the results to find exactly the solution I was searching for.   So I pieced together memories and thoughts of how one would approach a solution.  I wrote down steps.
  1. Use C64 BASIC in existing VICE emulator to test tokenizing, and execution - validate entry points, memory usage, define API subset
  2. Emulate 6502 code on Windows
  3. Graphing techniques - capture code paths, variables used
  4. Convert 6502 machine code to C or other using graphing information
  5. Add optimization techniques
I used Compute!'s Mapping the Commodore 64 and VICE to find and test the code to tokenize a statement: $A579, code for adding/replacing a line of a program: $A49C, and to execute a statement: $A7E4.  And I made notes about more Commodore 64 memory map entries.

I also found an Internet archived copy of the 6502 (65XX) Microprocessor Instant Reference Card (that I probably have filed away somewhere in a box in my garage) and I was itching real bad to implement the emulator.  Now, I've implemented the 6502 emulator before, but if memory serves correctly, it was a long time ago, and the moment I was ready to test it, I kinda gave up on it.  I was ready to return to this challenge.  I needed to get over that hill.  I was determined.  I was obsessed.

And then I happened to search for the original solution on github.  Huh, yeah, I should have thought of that first.  Brain fart!  I found mist64/cbmbasic.   The statically recompiled BASIC console program and loader.  Works great!

But my obsession was in full gear, so I wrote the disassembler Thursday night, March 26, 2020, and wrote the emulator Friday all day, March 27, 2020.  I had a miscommunication with other members of my family -- they thought I was working.  But I was on PTO.   Guess I forgot to tell them.  I was present for the family -- working at the dining room table -- but my mind was full bore into my laptop getting this emulator going.  And sure enough, I was able to post a screen shot to Twitter after midnight, barely into Saturday, March 28, 2020, just over 28 hours start to finish, including some sleep.  I am so lucky to get a successful result after such a short time.

Did it actually work first time?  Definitely not.  I had to fix my implementation of the BIT statement, somehow some opcodes got deleted, had to fix some instruction modes, and continuously fixing the flag assignments.  The challenge was fun.

Put in a bit more polish, separate out the Commodore 64 code from the 6502, and I was able to post the source to Github Saturday night, March 28.

Sunday I was able to test the VIC-20 ROMs, and wow, those work too.  Posted additional changes to formally support those, and have command line arguments checked in Sunday, March 29.

Then checked in backspace support early Monday, March 30 before heading to my real world job of working at home (how convenient!).  Yeah Coronavirus going on and all that.  All the more reason to keep my head low and focus on something else.

Monday night March 30 I created this blog entry.  Okay, time to get more sleep.

Sleep, maybe some, but TED was bothering me.  Turns out it needed memory banking (damn you TED!) so there you go, revised the memory scheme from an array, to a C# class that each machine type can handle those accesses including banking support.  Watch out 128, I'm going to get you next.

Saturday April 4 I was able to fix some I/O issues, and check in TED (C16, Plus/4) and PET 2001 support.

What's next?  I'm toying with replicating the static recompilation, to C would work best for me.  Then I could test on various or select embedded hardware platforms I have in my possession (or did I say obsession?).  

And maybe even use one with an LCD, and modify the emulation to support the screen editor and hi-res graphics support, either hooked or emulated, and support file loading from SD.  

Then probably on to something else newly retro and exiting.  Have fun!

[Update: ported C64 emulator to C, and tested on ARM boards with MBED and Arduino.  Skipped the static recompilation, because easier not to, and keeps all the machine code 6502 compatibility.  No banking support in Arduino/ESP32 version though, so limited to RAM at 0000-9FFF, C000-CFFF, and color nybbles at D800-DFFF; in other words no RAM under I/O or under ROMs, and no character graphics ROM for Arduino yet -- code exists in other branches, so exercise left to user.]


Credits

Inspired by mist64/cbmbasic and RetroShield 6502
Compute!'s Mapping the VIC
VICE - the Versatile Commodore Emulator


External Links:

Open Source!  https://github.com/davervw/simple-emu-c64 (C#, follow links for C version)

No comments:

Post a Comment