6502 emulator for Commodore 64 ROMs, serial terminal edition for MBED. Recommend terminal echo on, line edit on, caps lock, 115200bps, implicit carriage return on newline, currently non-buffered so don't paste lots of stuff
More details at:
[https://github.com/davervw] [https://techwithdave.davevw.com/2020/03/simple-emu-c64.html]
Revision 9:b4293b01083b, committed 2020-04-17
- Comitter:
- davervw
- Date:
- Fri Apr 17 09:15:50 2020 +0000
- Parent:
- 8:519febdce8db
- Commit message:
- comment updates, and implemented optional #define LOCAL_LOAD // for loading from Mbed filesystem
Changed in this revision
diff -r 519febdce8db -r b4293b01083b emuc64.cpp --- a/emuc64.cpp Wed Apr 15 05:15:07 2020 +0000 +++ b/emuc64.cpp Fri Apr 17 09:15:50 2020 +0000 @@ -36,98 +36,119 @@ // // LIMITATIONS: // Only keyboard/console I/O. No text pokes, no graphics. Just stdio. -// No asynchronous input (GET K$), but INPUT S$ works +// No asynchronous input (GET K$), but INPUT S$ works. No key scan codes. // No keyboard color switching. No border displayed. No border color. +// No background screen color. No reverse colors implemented in this version. // No screen editing (gasp!) Just short and sweet for running C64 BASIC in -// terminal/console window via 6502 chip emulation in software -// No PETSCII graphic characters, only supports printables CHR$(32) to CHR$(126), and CHR$(147) clear screen -// No memory management. Full 64K RAM not accessible via banking despite startup screen. -// Just 44K RAM, 16K ROM, 1K VIC-II color RAM nybbles +// terminal/console window via 6502 chip emulation in software. +// No PETSCII graphic characters, only supports printables CHR$(32) to CHR$(126), +// and CHR$(147) clear screen and HOME/LEFT/RIGHT/UP/DOWN (see cbmconsole.cpp) // No timers. No interrupts except BRK. No NMI/RESTORE key. No STOP key. -// No loading of files implemented. +// IRQ is specifically commented out because breaks terminal I/O. +// Loading of files at startup is optional depending on availability of +// a local file system store (e.g. Mbed MSD) +// Device I/O not implemented. No tape/serial/printer/disk/joystick. +// No VIC II. +// No CIA1/CIA2. +// No sound. Sorry no SID. +// No cartridges. +// Simple means simple. This is a simple emulator using terminal console. // -// $00/$01 (DDR and banking and I/O of 6510 missing), just RAM -// $0000-$9FFF RAM (199=reverse if non-zero, 646=foreground color) -// $A000-$BFFF BASIC ROM (write to RAM underneath, but haven't implemented read/banking) +// MEMORY MAP +// $00 (data direction missing) +// $01 Banking implemented (tape sense/controls missing) +// $0000-$9FFF RAM (upper limit may vary based on MCU SRAM available) +// $A000-$BFFF BASIC ROM +// $A000-$BFFF Banked LORAM (may not be present based on MCU SRAM limits) // $C000-$CFFF RAM -// $D000-$DFFF (missing I/O and character ROM and RAM banks), just zeros except... -// $D021 Background Screen Color -// $D800-$DFFF VIC-II color RAM nybbles (note: haven't implemented RAM banking) -// $E000-$FFFF KERNAL ROM (write to RAM underneath, but haven't implemented read/banking) -// -// Requires user provided Commodore 64 BASIC/KERNAL ROMs (e.g. from VICE) -// as they are not provided, others copyrights may still be in effect. +// $D000-$D7FF (I/O missing, reads as zeros) +// $D800-$DFFF VIC-II color RAM nybbles in I/O space (1K x 4bits) +// $D000-$DFFF Banked RAM (may not be present based on MCU SRAM limits) +// $D000-$DFFF Banked Character ROM +// $E000-$FFFF KERNAL ROM +// $E000-$FFFF Banked HIRAM (may not be present based on MCU SRAM limits) // //////////////////////////////////////////////////////////////////////////////// // ROMs copyright Commodore or their assignees //////////////////////////////////////////////////////////////////////////////// #include <mbed.h> -//#include <LocalFileSystem.h> #include "emu6502.h" #include "cbmconsole.h" +#include "emuc64.h" + +// for limited SRAM MCUs, use a smaller number than 64. 3 <= RAM_SIZE <= 64 +#define RAM_SIZE 16 +//#define RAM_SIZE 64 // global references extern Serial pc; // globals +#ifdef LOCAL_LOAD char* StartupPRG = 0; +#endif // locals -//static int startup_state = 0; -//LocalFileSystem local("local"); +#ifdef LOCAL_LOAD +static int startup_state = 0; +#endif -//static void File_ReadAllBytes(byte* bytes, unsigned int size, const char* filename) -//{ -// int file; -// file = open(filename, O_RDONLY); -// if (file < 0) -// { -// pc.printf("file ""%s"", errno=%d\n", filename, errno); -// exit(1); -// } -// read(file, bytes, size); -// close(file); -//} +#ifdef LOCAL_LOAD +static void File_ReadAllBytes(byte* bytes, unsigned int size, const char* filename) +{ + int file; + file = open(filename, O_RDONLY); + if (file < 0) + { + pc.printf("file ""%s"", errno=%d\n", filename, errno); + exit(1); + } + read(file, bytes, size); + close(file); +} +#endif -//// returns true if BASIC -//static bool LoadPRG(const char* filename) -//{ -// bool result; -// byte lo, hi; -// int file; -// ushort loadaddr; -// -// file = open(filename, O_RDONLY); -// if (file < 0 -// || read(file, &lo, 1) != 1 -// || read(file, &hi, 1) != 1 -// ) -// { -// pc.printf("file ""%s"", errno=%d\n", filename, errno); -// exit(1); -// } -// if (lo == 1) -// { -// loadaddr = (ushort)(GetMemory(43) | (GetMemory(44) << 8)); -// result = true; -// } -// else -// { -// loadaddr = (ushort)(lo | (hi << 8)); -// result = false; -// } -// while (true) -// { -// byte value; -// if (read(file, &value, 1) == 1) -// SetMemory(loadaddr++, value); -// else -// break; -// } -// close(file); -// return result; -//} +#ifdef LOCAL_LOAD +// returns true if BASIC +static bool LoadPRG(const char* filename) +{ + bool result; + byte lo, hi; + int file; + ushort loadaddr; + + file = open(filename, O_RDONLY); + if (file < 0 + || read(file, &lo, 1) != 1 + || read(file, &hi, 1) != 1 + ) + { + pc.printf("file ""%s"", errno=%d\n", filename, errno); + exit(1); + } + if (lo == 1) + { + loadaddr = (ushort)(GetMemory(43) | (GetMemory(44) << 8)); + result = true; + } + else + { + loadaddr = (ushort)(lo | (hi << 8)); + result = false; + } + while (true) + { + byte value; + if (read(file, &value, 1) == 1) + SetMemory(loadaddr++, value); + else + break; + } + close(file); + return result; +} +#endif bool ExecutePatch(void) { @@ -152,75 +173,78 @@ return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc. } -// else if (PC == 0xA474) // READY -// { -// if (StartupPRG != 0 && strlen(StartupPRG) > 0) // User requested program be loaded at startup -// { -// const char* filename = StartupPRG; -// StartupPRG = 0; -// -// if (LoadPRG(filename)) -// { -// //UNNEW that I used in late 1980s, should work well for loang a program too, probably gleaned from BASIC ROM -// //ldy #0 -// //lda #1 -// //sta(43),y -// //iny -// //sta(43),y -// //jsr $a533 ; LINKPRG -// //clc -// //lda $22 -// //adc #2 -// //sta 45 -// //lda $23 -// //adc #0 -// //sta 46 -// //lda #0 -// //jsr $a65e ; CLEAR/CLR -// //jmp $a474 ; READY -// -// // This part shouldn't be necessary as we have loaded, not recovering from NEW, bytes should still be there -// ushort addr = (ushort)(GetMemory(43) | (GetMemory(44) << 8)); -// SetMemory(addr, 1); -// SetMemory((ushort)(addr + 1), 1); -// -// // JSR equivalent -// ushort retaddr = (ushort)(PC - 1); -// Push(HI(retaddr)); -// Push(LO(retaddr)); -// PC = 0xA533; // LINKPRG -// -// startup_state = 1; // should be able to regain control when returns... -// -// return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc. -// } -// } -// else if (startup_state == 1) -// { -// ushort addr = (ushort)(GetMemory(0x22) | (GetMemory(0x23) << 8) + 2); -// SetMemory(45, (byte)addr); -// SetMemory(46, (byte)(addr >> 8)); -// -// // JSR equivalent -// ushort retaddr = (ushort)(PC - 1); -// Push(HI(retaddr)); -// Push(LO(retaddr)); -// PC = 0xA65E; // CLEAR/CLR -// A = 0; -// -// startup_state = 2; // should be able to regain control when returns... -// -// return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc. -// } -// else if (startup_state == 2) -// { -// CBM_Console_Push("RUN\r"); -// PC = 0xA47B; // skip READY message, but still set direct mode, and continue to MAIN -// C = false; -// startup_state = 0; -// return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc. -// } -// } +#ifdef LOCAL_LOAD + else if (PC == 0xA474) // READY + { + if (StartupPRG != 0 && strlen(StartupPRG) > 0) // User requested program be loaded at startup + { + const char* filename = StartupPRG; + StartupPRG = 0; + + if (LoadPRG(filename)) + { + //UNNEW that I used in late 1980s, should work well for loang a program too, probably gleaned from BASIC ROM + //ldy #0 + //lda #1 + //sta(43),y + //iny + //sta(43),y + //jsr $a533 ; LINKPRG + //clc + //lda $22 + //adc #2 + //sta 45 + //lda $23 + //adc #0 + //sta 46 + //lda #0 + //jsr $a65e ; CLEAR/CLR + //jmp $a474 ; READY + + // This part shouldn't be necessary as we have loaded, not recovering from NEW, bytes should still be there + ushort addr = (ushort)(GetMemory(43) | (GetMemory(44) << 8)); + SetMemory(addr, 1); + SetMemory((ushort)(addr + 1), 1); + + // JSR equivalent + ushort retaddr = (ushort)(PC - 1); + Push(HI(retaddr)); + Push(LO(retaddr)); + PC = 0xA533; // LINKPRG + + startup_state = 1; // should be able to regain control when returns... + + return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc. + } + } + else if (startup_state == 1) + { + ushort addr = (ushort)(GetMemory(0x22) | (GetMemory(0x23) << 8) + 2); + SetMemory(45, (byte)addr); + SetMemory(46, (byte)(addr >> 8)); + + // JSR equivalent + ushort retaddr = (ushort)(PC - 1); + Push(HI(retaddr)); + Push(LO(retaddr)); + PC = 0xA65E; // CLEAR/CLR + A = 0; + + startup_state = 2; // should be able to regain control when returns... + + return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc. + } + else if (startup_state == 2) + { + CBM_Console_Push("RUN\r"); + PC = 0xA47B; // skip READY message, but still set direct mode, and continue to MAIN + C = false; + startup_state = 0; + return true; // overriden, and PC changed, so caller should reloop before execution to allow breakpoint/trace/ExecutePatch/etc. + } + } +#endif // LOCAL_LOAD + return false; // execute normally } @@ -873,7 +897,7 @@ '\xF0','\xF0','\xF0','\xF0','\xFF','\xFF','\xFF','\xFF','\xE7','\xE7','\xE7','\x07','\x07','\xFF','\xFF','\xFF','\x0F','\x0F','\x0F','\x0F','\xFF','\xFF','\xFF','\xFF','\x0F','\x0F','\x0F','\x0F','\xF0','\xF0','\xF0','\xF0' }; -static byte ram[24 * 1024]; // MAX: 64 * 1024 if you have the SRAM, allows RAM banking +static byte ram[RAM_SIZE * 1024]; // MAX: 64 * 1024 if you have the SRAM, allows RAM banking static byte color_nybles[1024]; // note ram starts at 0x0000 @@ -888,6 +912,7 @@ void C64_Init(const char* basic_file, const char* chargen_file, const char* kernal_file) { //File_ReadAllBytes(basic_rom, sizeof(basic_rom), basic_file); + //File_ReadAllBytes(char_rom, sizeof(char_rom), chargen_file); //File_ReadAllBytes(kernal_rom, sizeof(kernal_rom), kernal_file); for (int i = 0; i < sizeof(ram); ++i) @@ -943,8 +968,6 @@ ) ) ram[addr] = value; - else if (addr == 0xD021) // background - ; else if (addr >= color_addr && addr < color_addr + sizeof(color_nybles)) color_nybles[addr - color_addr] = value; //else if (addr >= io_addr && addr < io_addr + io.Length)
diff -r 519febdce8db -r b4293b01083b emuc64.h --- a/emuc64.h Wed Apr 15 05:15:07 2020 +0000 +++ b/emuc64.h Fri Apr 17 09:15:50 2020 +0000 @@ -32,6 +32,12 @@ #pragma once +// note: LOCAL_LOAD/StartupPRG requires a file system implementation (SD, Mbed MSD, etc.) +//#define LOCAL_LOAD + extern void C64_Init(const char* basic_file, const char* chargen_file, const char* kernal_file); + +#ifdef LOCAL_LOAD extern char* StartupPRG; +#endif
diff -r 519febdce8db -r b4293b01083b main.cpp --- a/main.cpp Wed Apr 15 05:15:07 2020 +0000 +++ b/main.cpp Fri Apr 17 09:15:50 2020 +0000 @@ -35,17 +35,33 @@ #include "emuc64.h" #include "emu6502.h" +#ifdef LOCAL_LOAD +#include <LocalFileSystem.h> +LocalFileSystem local("local"); +#endif + Serial pc(USBTX, USBRX, 115200); +//Serial pc(p9, p10, 115200); int main(/*int argc, char* argv[]*/) { pc.printf("\n"); - pc.printf("c-simple-emu-cbm version 1.6\n"); + pc.printf("c-simple-emu-cbm version 1.7 for Mbed\n"); pc.printf("Copyright (c) 2020 by David R. Van Wagner\n"); - pc.printf("MIT License\n"); + pc.printf("Open Source - MIT License\n"); pc.printf("github.com/davervw\n"); pc.printf("\n"); - //StartupPRG = "/local/guess2.prg"; + pc.printf("Contains other licensed software\n"); + pc.printf(" ARM MBED OS\n"); + pc.printf("\n"); + pc.printf("Commodore ROMs not licensed\n"); + pc.printf("\n"); + +#ifdef LOCAL_LOAD + // note: requires a file system implementation (SD, Mbed MSD, etc.) + StartupPRG = "/local/startup.prg"; +#endif + C64_Init("/local/basic", "/local/chargen", "/local/kernal"); ResetRun(ExecutePatch); return 0;
diff -r 519febdce8db -r b4293b01083b mbed-os.lib --- a/mbed-os.lib Wed Apr 15 05:15:07 2020 +0000 +++ b/mbed-os.lib Fri Apr 17 09:15:50 2020 +0000 @@ -1,1 +1,1 @@ -https://github.com/ARMmbed/mbed-os/#532654ebb31c7bf79601042a6fa976b85532ef47 +https://github.com/ARMmbed/mbed-os/#565ab149819481224ab43f878c3921b14b11d180