#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "Z/types/base.h" #include "Z80.h" template class Emulator { public: Emulator(std::filesystem::path rom_name) { // SDL int render_flags = SDL_RENDERER_ACCELERATED; int window_flags = 0; if (SDL_Init(SDL_INIT_VIDEO) < 0) { throw std::runtime_error("Failed to initialize SDL2: " + std::string(SDL_GetError())); } window = SDL_CreateWindow("Z80 Emulator", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, W*16, H*16, window_flags); if (!window) { throw std::runtime_error("Failed to open window: " + std::string(SDL_GetError())); } SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); renderer = SDL_CreateRenderer(window, -1, render_flags); if (!window) { throw std::runtime_error("Failed to create rendere: " + std::string(SDL_GetError())); } if (!IMG_Init(IMG_INIT_PNG)) { throw std::runtime_error("Failed to initialize SDL2_image: " + std::string(SDL_GetError())); } SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Loading '../convert-font/font.png'"); font = IMG_LoadTexture(renderer, "../convert-font/font.png"); if (!font) { throw std::runtime_error("Failed to open font: " + std::string(IMG_GetError())); } // 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; z.in = z_in; z.out = z_out; z.int_data = z_int_data; z80_reset(&z); } ~Emulator() { SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); SDL_Quit(); } static zuint8 z_read(void* context, zuint16 address) { auto e = (Emulator*)context; return e->memory[address]; } static void z_write(void* context, zuint16 address, zuint8 value) { auto e = (Emulator*)context; e->memory[address] = value; } static zuint8 z_in(void* /* context */, zuint16 port) { switch (port & 0xFF) { case 0x03: return 0x01; // char case 0x1E: return 0x00; // has_input case 0x1F: return 0x00; default: SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "IO Read @ 0x%X\n", (port & 0xFF)); return 0x00; } } static void z_out(void* context, zuint16 port, zuint8 value) { auto e = (Emulator*)context; switch (port & 0xFF) { case 0x02: e->input(value); break; default: SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "IO Wrte 0x%X @ 0x%X\n", value, (port & 0xFF)); break; } } static zuint32 z_int_data(void* /* context */) { return 0x00; } void update() { z80_run(&z, 1); } // @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() { SDL_Rect src; src.w = 16; src.h = 16; SDL_Rect dest; dest.w = 16; dest.h = 16; SDL_SetTextureColorMod(font, 255, 255, 255); for (int x = 0; x < W; ++x) { for (int y = 0; y < H; ++y) { char c = buffer[y*W + x]; src.x = (c % 16) * 16; src.y = (c / 16) * 16; dest.x = x * 16; dest.y = y * 16; SDL_RenderCopy(renderer, font, &src, &dest); } } } bool handle_events() { SDL_Event event; while (SDL_PollEvent(&event)) { switch (event.type) { case SDL_QUIT: return false; default: break; } } return true; } void prepare() { SDL_SetRenderDrawColor(renderer, 96, 128, 255, 255); SDL_RenderClear(renderer); } void render() { SDL_RenderPresent(renderer); } private: SDL_Renderer* renderer = nullptr; SDL_Window* window = nullptr; SDL_Texture* font = nullptr; std::array buffer = {0}; struct { int x = 0; int y = 0; } cursor; Z80 z; std::array memory; }; int main() { Emulator<80, 45> e("../../software/monitor/.build/rom_monitor.bin"); while (e.handle_events()) { e.prepare(); e.update(); e.draw_buffer(); e.render(); } }