#include #include #include #include #include #include #include #include "cpu.h" #include "ram.h" #include "rom.h" #define MACHINE_SPEED_HZ 1000000ULL // 1 MHz; #define SYNC_CYCLES 100 struct termios orig_termios; struct timespec emulationStart; uint64_t totalCycles = 0; uint64_t lastSyncCycles = 0; void restore_terminal(void) { tcsetattr(STDIN_FILENO, TCSANOW, &orig_termios); } void set_nonblocking_input(void) { struct termios raw; tcgetattr(STDIN_FILENO, &orig_termios); atexit(restore_terminal); raw = orig_termios; raw.c_lflag &= ~(ICANON | ECHO); // disable line buffering, echo /* Ctrl+C becomes ordinary input byte 0x03 */ raw.c_cc[VINTR] = _POSIX_VDISABLE; raw.c_cc[VMIN] = 0; // don't block waiting for input raw.c_cc[VTIME] = 0; tcsetattr(STDIN_FILENO, TCSANOW, &raw); // make stdin reads non-blocking too int flags = fcntl(STDIN_FILENO, F_GETFL, 0); fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); } int main(void) { uint8_t *ram = NULL; CPU cpu; printf("6502PC Emulator with MS Basic\n"); printf("CTRL + G to exit\n"); clock_gettime(CLOCK_MONOTONIC, &emulationStart); set_nonblocking_input(); initRom(rom); if (createRAM(&ram) != 0) { printf("Could not create RAM\n"); exit(EXIT_FAILURE); } initCPU(&cpu); resetCPU(&cpu, rom); while (1) { uint8_t cycles = runCycle(&cpu, ram, rom); totalCycles += cycles; // No 1 cycle instruction exists if (cycles == 1) { printf("Illegal opcode $%02X at $%04X " "A=$%02X X=$%02X Y=$%02X SP=$%02X\n", ram[cpu.ProgrammeCounter - 1], cpu.ProgrammeCounter - 1, cpu.Accumulator, cpu.X, cpu.Y, cpu.StackPointer); exit(EXIT_FAILURE); } // Sleep if batch is full if (totalCycles - lastSyncCycles >= SYNC_CYCLES) { struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); uint64_t elapsed_ns = (uint64_t)(now.tv_sec - emulationStart.tv_sec) * 1000000000ULL + (uint64_t)(now.tv_nsec - emulationStart.tv_nsec); uint64_t target_ns = totalCycles * 1000000000ULL / MACHINE_SPEED_HZ; if (target_ns > elapsed_ns) { uint64_t wait_ns = target_ns - elapsed_ns; struct timespec wait = {.tv_sec = wait_ns / 1000000000ULL, .tv_nsec = wait_ns % 1000000000ULL}; nanosleep(&wait, NULL); } lastSyncCycles = totalCycles; } } destroyRAM(ram); return 0; }