100 lines
2.5 KiB
C
100 lines
2.5 KiB
C
#include <fcntl.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <termios.h>
|
|
#include <time.h>
|
|
#include <unistd.h>
|
|
|
|
#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;
|
|
} |