From a5e30a7b286eec756c3192b3661068d291a8790c Mon Sep 17 00:00:00 2001 From: Kilian Hofmann Date: Sun, 27 Sep 2026 04:48:39 +0200 Subject: [PATCH] Fix BASIC Fix cycle waiting Fix warnings --- .vscode/settings.json | 7 ++++ CMakelists.txt | 6 +++ inc/diag.h | 28 ++++++++++++++ inc/io.h | 2 +- inc/memoryMap.h | 4 +- inc/ram.h | 6 +-- inc/rom.h | 2 +- msbasic/defines_kilian.s | 2 +- msbasic/init.s | 9 ++++- src/cpu.c | 6 +-- src/main.c | 79 +++++++++++++++++++++------------------- src/memoryMap.c | 7 +++- src/ram.c | 4 +- src/rom.c | 13 +++---- 14 files changed, 114 insertions(+), 61 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 inc/diag.h diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f3caafe --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "cSpell.words": [ + "msbasic", + "Wmissing", + "Wunused" + ] +} \ No newline at end of file diff --git a/CMakelists.txt b/CMakelists.txt index 3bc0253..3f48401 100644 --- a/CMakelists.txt +++ b/CMakelists.txt @@ -10,12 +10,18 @@ enable_language(C) add_executable(${CMAKE_PROJECT_NAME}) +target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE _POSIX_C_SOURCE=200809L) + if(MSVC) target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE /W4) else() target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE -Wall -Wextra -Wpedantic) endif() +target_compile_options(${CMAKE_PROJECT_NAME} PRIVATE + -include ${CMAKE_CURRENT_SOURCE_DIR}/inc/diag.h +) + target_sources(${CMAKE_PROJECT_NAME} PRIVATE "src/main.c" "src/ram.c" diff --git a/inc/diag.h b/inc/diag.h new file mode 100644 index 0000000..fe6f636 --- /dev/null +++ b/inc/diag.h @@ -0,0 +1,28 @@ +#define DIAG_STRINGIFY_(x) #x +#define DIAG_STRINGIFY(x) DIAG_STRINGIFY_(x) + +#define DIAG_DO_PRAGMA(x) _Pragma(DIAG_STRINGIFY(x)) + +#if defined(__clang__) + +#define DIAG_PUSH() DIAG_DO_PRAGMA(clang diagnostic push) + +#define DIAG_POP() DIAG_DO_PRAGMA(clang diagnostic pop) + +#define DIAG_IGNORE(w) DIAG_DO_PRAGMA(clang diagnostic ignored w) + +#elif defined(__GNUC__) + +#define DIAG_PUSH() DIAG_DO_PRAGMA(GCC diagnostic push) + +#define DIAG_POP() DIAG_DO_PRAGMA(GCC diagnostic pop) + +#define DIAG_IGNORE(w) DIAG_DO_PRAGMA(GCC diagnostic ignored w) + +#else + +#define DIAG_PUSH() +#define DIAG_POP() +#define DIAG_IGNORE(w) + +#endif \ No newline at end of file diff --git a/inc/io.h b/inc/io.h index c48496d..2b54519 100644 --- a/inc/io.h +++ b/inc/io.h @@ -3,7 +3,7 @@ #include -#define IO_SIZE (1 * 1024) // 1KiB +#define IO_SIZE (1 * 1024) // 1KiB #define IO_BASE (47 * 1024) // 47KiB RAM, hard coded, 0xBC00 #define SCREEN_ADDRESS 0 diff --git a/inc/memoryMap.h b/inc/memoryMap.h index 7df0a4a..9834edd 100644 --- a/inc/memoryMap.h +++ b/inc/memoryMap.h @@ -1,12 +1,12 @@ #ifndef MEMORY_MAP_H #define MEMORY_MAP_H +#include + #include "io.h" #include "ram.h" #include "rom.h" -#include - uint8_t readMemory(uint16_t address, uint8_t *ram, uint8_t *rom); void writeMemory(uint16_t address, uint8_t value, uint8_t *ram); diff --git a/inc/ram.h b/inc/ram.h index 784e540..7ea8ba3 100644 --- a/inc/ram.h +++ b/inc/ram.h @@ -1,10 +1,10 @@ #ifndef RAM_H #define RAM_H -#include "stdint.h" +#include -#define RAM_SIZE (64 * 1024) // 55KiB -#define RAM_BASE (0x00) // 0 +#define RAM_SIZE (64 * 1024) // 64KiB, read/write to IO is ignored, ROM can be banked +#define RAM_BASE (0x00) // 0 int createRAM(uint8_t **ram); void destroyRAM(uint8_t *ram); diff --git a/inc/rom.h b/inc/rom.h index 6f2e499..bcf57cf 100644 --- a/inc/rom.h +++ b/inc/rom.h @@ -3,7 +3,7 @@ #include -#define ROM_SIZE (16 * 1024) // 16KiB +#define ROM_SIZE (16 * 1024) // 16KiB #define ROM_BASE (48 * 1024) // 47KiB RAM + 1KiB IO, hard coded extern uint8_t rom[ROM_SIZE]; diff --git a/msbasic/defines_kilian.s b/msbasic/defines_kilian.s index 09c88b3..393f60f 100644 --- a/msbasic/defines_kilian.s +++ b/msbasic/defines_kilian.s @@ -22,6 +22,6 @@ USR := GORESTART ; XXX ; constants SPACE_FOR_GOSUB := $3E STACK_TOP := $FA -WIDTH := 40 +WIDTH := 80 WIDTH2 := 30 RAMSTART2 := $0400 diff --git a/msbasic/init.s b/msbasic/init.s index f1de786..7fc1a9a 100644 --- a/msbasic/init.s +++ b/msbasic/init.s @@ -200,6 +200,11 @@ L4098: lda #$01 sta INPUTBUFFER-3 sta INPUTBUFFER-4 + .endif + .ifdef CONFIG_INPUTBUFFER_0200 + lda #$01 + sta INPUTBUFFER-3 + sta INPUTBUFFER-4 .endif ldx #TEMPST stx TEMPPT @@ -226,7 +231,9 @@ L4098: .ifndef AIM65 .ifndef SYM1 cmp #$41 - beq PR_WRITTEN_BY + bne SKIPPED + jmp PR_WRITTEN_BY +SKIPPED: .endif .endif tay diff --git a/src/cpu.c b/src/cpu.c index 54bbced..1dbdae8 100644 --- a/src/cpu.c +++ b/src/cpu.c @@ -58,10 +58,10 @@ static void doSBC(CPU *cpu, uint8_t value) { } void initCPU(CPU *cpu) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wmissing-field-initializers" + DIAG_PUSH(); + DIAG_IGNORE("-Wmissing-field-initializers"); *cpu = (CPU){0x0000, 0xFF}; -#pragma clang diagnostic pop + DIAG_POP(); } void resetCPU(CPU *cpu, uint8_t *rom) { diff --git a/src/main.c b/src/main.c index 08ba508..f8a0ea8 100644 --- a/src/main.c +++ b/src/main.c @@ -1,18 +1,23 @@ -#include "cpu.h" -#include "ram.h" -#include "rom.h" - #include +#include #include #include #include #include #include -#define MACHINE_SPEED_HZ 1e6 // 1 MHz; +#include "cpu.h" +#include "ram.h" +#include "rom.h" -static struct termios orig_termios; -static struct timespec sleepTime; +#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); } @@ -36,11 +41,13 @@ void set_nonblocking_input(void) { } int main(void) { + uint8_t *ram = NULL; + CPU cpu; + printf("6502PC Emulator with MS Basic\n"); printf("CTRL + G to exit\n"); - uint8_t *ram = NULL; - CPU cpu; + clock_gettime(CLOCK_MONOTONIC, &emulationStart); set_nonblocking_input(); @@ -51,44 +58,40 @@ int main(void) { exit(EXIT_FAILURE); } - /* DEBUG */ - - // FILE *fp = fopen("/Users/Kili2/Documents/Projekte/6502pc/6502_65C02_functional_tests/6502_functional_test.bin", "rb"); - // FILE *fp = fopen("/Users/Kili2/Documents/Projekte/6502pc/6502_65C02_functional_tests/6502_decimal_test.bin", "rb"); - // FILE *fp = fopen("/mnt/c/Users/llego/source/repos/6502PC/6502_65C02_functional_tests/6502_functional_test.bin", "rb"); - // FILE *fp = fopen("/mnt/c/Users/llego/source/repos/6502PC/6502_65C02_functional_tests/6502_decimal_test.bin", "rb"); - /* - if (!fp) { - perror("fopen"); - exit(EXIT_FAILURE); - } - - fread(ram, 1, RAM_SIZE, fp); - if (ferror(fp)) { - perror("fread"); - fclose(fp); - exit(EXIT_FAILURE); - } - - fclose(fp); - */ - - /* DEBUG END*/ - initCPU(&cpu); resetCPU(&cpu, rom); while (1) { - uint8_t sleep = runCycle(&cpu, ram, rom); - if (sleep == 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); } - sleepTime.tv_sec = 0; - sleepTime.tv_nsec = sleep * (1000 / MACHINE_SPEED_HZ); - nanosleep(&sleepTime, NULL); + + // 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); diff --git a/src/memoryMap.c b/src/memoryMap.c index a056d42..0e81465 100644 --- a/src/memoryMap.c +++ b/src/memoryMap.c @@ -1,13 +1,16 @@ #include "memoryMap.h" uint8_t readMemory(uint16_t address, uint8_t *ram, uint8_t *rom) { - if ((address < RAM_SIZE && address < IO_BASE) || address >= ROM_BASE) { + // RAM/ROM + if (address < IO_BASE || address >= ROM_BASE) { if (address >= ROM_BASE && ram[IO_BASE + ROM_BANKED_OUT] == 0) { return rom[address - ROM_BASE]; } return ram[address]; - } else { + } + // I/O + else { uint16_t _address = address - IO_BASE; switch (_address) { case KEYBOARD_ADDRESS: { diff --git a/src/ram.c b/src/ram.c index cdd4e93..d609dbb 100644 --- a/src/ram.c +++ b/src/ram.c @@ -1,9 +1,9 @@ #include "ram.h" -#include "io.h" - #include +#include "io.h" + int createRAM(uint8_t **ram) { *ram = (uint8_t *)malloc(RAM_SIZE); diff --git a/src/rom.c b/src/rom.c index 79a529a..1aace06 100644 --- a/src/rom.c +++ b/src/rom.c @@ -1,20 +1,23 @@ #include "rom.h" -#include "io.h" - #include +#include "io.h" + uint8_t rom[ROM_SIZE] = {0}; void initRom(uint8_t *rom) { - FILE *fp = fopen("/mnt/c/Users/llego/source/repos/6502PC/msbasic/tmp/kilian.bin", "rb"); + FILE *fp = fopen("msbasic/tmp/kilian.bin", "rb"); if (!fp) { perror("fopen"); return; } + DIAG_PUSH(); + DIAG_IGNORE("-Wunused-result"); fread(rom, 1, ROM_SIZE, fp); + DIAG_POP(); if (ferror(fp)) { perror("fread"); fclose(fp); @@ -22,8 +25,4 @@ void initRom(uint8_t *rom) { } fclose(fp); - - /* Reset Vector, BASIC at DF02 */ - //rom[0xFFFC - ROM_BASE] = 0x02; // Lower byte base of ROM - //rom[0xFFFD - ROM_BASE] = 0xDF; // Upper byte of base of ROM } \ No newline at end of file