From 6d6a304bd23d8c0c4e26705bbf4db5d11f6c3f4b Mon Sep 17 00:00:00 2001 From: Dreaded_X Date: Thu, 14 Jan 2021 22:20:24 +0100 Subject: [PATCH] Added memory map, partial keyboard input and disk support --- src/main.cpp | 434 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 392 insertions(+), 42 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 703ae46..de5b135 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include @@ -14,15 +16,328 @@ #include #include #include -#include +#include "SDL_events.h" +#include "SDL_keyboard.h" +#include "SDL_keycode.h" #include "Z/types/base.h" #include "Z80.h" +#define SD_PAGES 1 +#define CPM_PAGE_SIZE 128 +#define SD_PAGE_SIZE 512 +#define CPM_PAGES (SD_PAGE_SIZE/CPM_PAGE_SIZE*SD_PAGES) + +template +class Screen { + public: + // @todo This should handle the input properly (same as fpga) + void input(char c) { + if (escape == 1) { + if (c == '[') { + escape = 2; + } else { + escape = 0; + } + } else if (escape) { + switch (c) { + // For now we are only going to implement what we actually use + case 'K': + // Assume parameter 0 + clear_eol(); + escape = 0; + break; + + case 'H': + if (escape_parameter_1 == 0) { + escape_parameter_1 = 1; + } + + if (escape_parameter_2 == 0) { + escape_parameter_2 = 1; + } + + cursor.x = escape_parameter_1 - 1; + cursor.y = escape_parameter_2 - 1; + escape = 0; + break; + + case 'm': + if (escape_parameter_1 == 0) { + reverse(0); + set_foreground(0b111); + set_background(0); + } else if (escape_parameter_1 == 7) { + reverse(1); + } else if (escape_parameter_1 >= 30 && escape_parameter_1 <= 37) { + set_foreground(escape_parameter_1 - 30); + } else if (escape_parameter_1 >= 40 && escape_parameter_1 <= 47) { + set_background(escape_parameter_1 - 40); + } else if (escape_parameter_1 >= 90 && escape_parameter_1 <= 97) { + set_foreground(escape_parameter_1 - 90 + 8); + } else if (escape_parameter_1 >= 100 && escape_parameter_1 <= 107) { + set_background(escape_parameter_1 - 100 + 8); + } + escape = 0; + break; + + case 'J': + // Assume parameter 2 + clear_screen(); + escape = 0; + break; + + case '0' ... '9': + escape_parameter_1 *= 10; + escape_parameter_1 += (c - 48); + break; + + case ';': + escape_parameter_2 = escape_parameter_1; + escape_parameter_1 = 0; + break; + + default: + escape = 0; + break; + } + } else { + switch (c) { + case '\n': + cursor.y++; + if (cursor.y >= 45) { + cursor.y--; + cursor.scroll = (cursor.scroll + 1) % 45; + clear_eol(); + } + break; + + case '\r': + cursor.x = 0; + break; + + case 0x08: + previous(); + break; + + case 0x1B: + escape = 1; + escape_parameter_1 = 0; + escape_parameter_2 = 0; + // Handle escape code + break; + + default: + write(cursor.x, cursor.y, c); + next(); + break; + } + } + } + + char get(int x, int y) { + return buffer[y*W + x]; + } + + private: + void set_foreground(int color) { + + } + + void set_background(int color) { + + } + + void reverse(bool enable) { + + } + + void clear_screen() { + reverse(0); + set_foreground(0b111); + set_background(0); + + for (char& c : buffer) { + c = 0x00; + } + + cursor = {0, 0, 0}; + } + + void clear_eol() { + reverse(0); + set_foreground(0b111); + set_background(0); + + for (int i = cursor.x; i < W; ++i) { + write(i, cursor.y, 0x00); + } + } + + void write(int x, int y, char c) { + buffer[y*W + x] = c; + } + + void next() { + cursor.x++; + if (cursor.x >= W) { + cursor.y++; + cursor.x %= W; + } + + if (cursor.y >= H) { + cursor.y--; + cursor.scroll = (cursor.scroll + 1) % H; + clear_eol(); + } + } + + void previous() { + cursor.x--; + + if (cursor.x < 0) { + cursor.y--; + cursor.x %= W; + } + + if (cursor.y < 0) { + cursor.y = 0; + cursor.x = 0; + } + } + + + std::array buffer = {0}; + + int escape = 0; + int escape_parameter_1 = 0; + int escape_parameter_2 = 0; + + struct { + int x = 0; + int y = 0; + int scroll = 0; + } cursor; +}; + +class Memory { + public: + Memory(std::filesystem::path rom_name) { + std::ifstream file(rom_name, std::ios::in | std::ios::binary); + + if (!file.is_open()) { + throw std::runtime_error("Failed to open: " + rom_name.string()); + } + + file.seekg(0, std::ios::end); + size_t size = file.tellg(); + file.seekg(0, std::ios::beg); + + if (size > rom.size()) { + throw std::runtime_error("ROM file is larger than avaiable ram"); + } + + file.read(reinterpret_cast(rom.data()), size); + } + + void config(int i) { + cfg = i; + } + + void write(uint16_t address, uint8_t value) { + if (cfg == 0 && address < 0x1000) { + return; + } + + ram[address] = value; + } + + uint8_t read(uint16_t address) { + if (cfg == 0 && address < 0x1000) { + return rom[address]; + } + return ram[address]; + } + + private: + int cfg = 0; + + // We do not initialize ram as we want it to contain random value + std::array ram; + std::array rom{}; +}; + +enum Action { + NONE = 0x30, + READ = 0x20, + WRITE = 0x30 +}; + +class Disk { + public: + Disk(std::filesystem::path disk_name) : file(disk_name, std::ios::in | std::ios::out | std::ios::binary) { + if (!file.is_open()) { + throw std::runtime_error("Failed to open: " + disk_name.string()); + } + } + + void set_lba(uint8_t value, int part) { + lba_part[part] = value; + } + + void set_action(Action a) { + if (a == Action::READ || a == Action::WRITE) { + int lba = (lba_part[0]) + (lba_part[1] << 8) + (lba_part[2] << 16); + + file.seekg(lba*128, std::ios::beg); + file.seekp(lba*128, std::ios::beg); + + action = a; + counter = 0; + } + } + + void write(uint8_t value) { + if (action == Action::WRITE) { + file.write(reinterpret_cast(&value), 1); + counter++; + + if (counter >= CPM_PAGE_SIZE) { + action = Action::NONE; + } + } + } + + uint8_t read() { + uint8_t value = 0; + if (action == Action::READ) { + file.read(reinterpret_cast(&value), 1); + counter++; + + if (counter >= CPM_PAGE_SIZE) { + action = Action::NONE; + } + } + return value; + } + + + bool is_ready() { + return action != Action::NONE; + } + + private: + std::fstream file; + + Action action = Action::NONE; + int lba_part[3] = {0}; + size_t counter = 0; +}; + template class Emulator { public: - Emulator(std::filesystem::path rom_name) { + Emulator(std::filesystem::path rom_name, std::filesystem::path disk_name) : memory(rom_name), disk(disk_name) { // SDL int render_flags = SDL_RENDERER_ACCELERATED; int window_flags = 0; @@ -54,14 +369,6 @@ class Emulator { } // Z80 - std::ifstream rom_file(rom_name, std::ios::in | std::ios::binary); - - rom_file.seekg(0, std::ios::end); - size_t rom_size = rom_file.tellg(); - rom_file.seekg(0, std::ios::beg); - - rom_file.read(reinterpret_cast(memory.data()), rom_size); - z.context = this; z.read = z_read; z.write = z_write; @@ -70,6 +377,8 @@ class Emulator { z.int_data = z_int_data; z80_reset(&z); + + SDL_StartTextInput(); } ~Emulator() { @@ -82,26 +391,39 @@ class Emulator { static zuint8 z_read(void* context, zuint16 address) { auto e = (Emulator*)context; - return e->memory[address]; + return e->memory.read(address); } static void z_write(void* context, zuint16 address, zuint8 value) { auto e = (Emulator*)context; - e->memory[address] = value; + e->memory.write(address, value); } - static zuint8 z_in(void* /* context */, zuint16 port) { + static zuint8 z_in(void* context, zuint16 port) { + auto e = (Emulator*)context; + switch (port & 0xFF) { case 0x03: return 0x01; - // char - case 0x1E: - return 0x00; + // Input + case 0x1E: { + char c = e->input.front(); + e->input.pop(); + return c; + } - // has_input + // Has input case 0x1F: - return 0x00; + return e->input.size() ? 0x01 : 0x00; + + // Data + case 0x08: + return e->disk.read(); + + // Has data + case 0x0f: + return e->disk.is_ready() ? 0x08 : 0x00; default: SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "IO Read @ 0x%X\n", (port & 0xFF)); @@ -114,8 +436,27 @@ class Emulator { auto e = (Emulator*)context; switch (port & 0xFF) { + case 0x00: + case 0x01: + e->memory.config(port & 0xFF); + break; + case 0x02: - e->input(value); + e->screen.input(value); + break; + + case 0x08: + e->disk.write(value); + break; + + case 0x0b: + case 0x0c: + case 0x0d: + e->disk.set_lba(value, (port & 0xFF) - 0x0b); + break; + + case 0x0f: + e->disk.set_action((Action)value); break; default: @@ -129,21 +470,11 @@ class Emulator { } void update() { - z80_run(&z, 1); + // Run for 1000 cycles + z80_run(&z, 1000); } - // @todo This should handle the input properly (same as fpga) - void input(char c) { - buffer[cursor.y*W + cursor.x] = c; - - cursor.x++; - if (cursor.x > W) { - cursor.x = 0; - cursor.y++; - } - } - - void draw_buffer() { + void draw_screen() { SDL_Rect src; src.w = 16; src.h = 16; @@ -156,7 +487,7 @@ class Emulator { for (int x = 0; x < W; ++x) { for (int y = 0; y < H; ++y) { - char c = buffer[y*W + x]; + char c = screen.get(x, y); src.x = (c % 16) * 16; src.y = (c / 16) * 16; @@ -176,6 +507,26 @@ class Emulator { case SDL_QUIT: return false; + case SDL_KEYDOWN: + if (event.key.keysym.sym == SDLK_RETURN) { + input.push('\r'); + } else if (event.key.keysym.sym == SDLK_BACKSPACE) { + input.push(0x08); + } + + break; + + case SDL_TEXTINPUT: { + char c = event.text.text[0]; + // @todo For some reason we cannot capture ctrl??? + if (SDL_GetModState() & KMOD_ALT && c == 'c') { + input.push(0x03); + } else { + input.push(c); + } + break; + } + default: break; } @@ -198,26 +549,25 @@ class Emulator { SDL_Window* window = nullptr; SDL_Texture* font = nullptr; - std::array buffer = {0}; - - struct { - int x = 0; - int y = 0; - } cursor; + Screen screen; Z80 z; - std::array memory; + Memory memory; + Disk disk; + + std::queue input; + bool ctrl = false; }; int main() { - Emulator<80, 45> e("../../software/monitor/.build/rom_monitor.bin"); + Emulator<80, 45> e("../../software/monitor/.build/rom_monitor.bin", "../create-disk/disk.img"); while (e.handle_events()) { e.prepare(); e.update(); - e.draw_buffer(); + e.draw_screen(); e.render(); }