Added color supprt, render only happens every 1/60 of a second and added startup commands

This commit is contained in:
Dreaded_X 2021-01-15 03:22:11 +01:00
parent 6d6a304bd2
commit 399dbef684

View File

@ -1,4 +1,5 @@
#include <bits/stdint-uintn.h> #include <bits/stdint-uintn.h>
#include <chrono>
#include <cstring> #include <cstring>
#include <fstream> #include <fstream>
#include <stdexcept> #include <stdexcept>
@ -20,6 +21,8 @@
#include "SDL_events.h" #include "SDL_events.h"
#include "SDL_keyboard.h" #include "SDL_keyboard.h"
#include "SDL_keycode.h" #include "SDL_keycode.h"
#include "SDL_pixels.h"
#include "SDL_surface.h"
#include "Z/types/base.h" #include "Z/types/base.h"
#include "Z80.h" #include "Z80.h"
@ -28,11 +31,49 @@
#define SD_PAGE_SIZE 512 #define SD_PAGE_SIZE 512
#define CPM_PAGES (SD_PAGE_SIZE/CPM_PAGE_SIZE*SD_PAGES) #define CPM_PAGES (SD_PAGE_SIZE/CPM_PAGE_SIZE*SD_PAGES)
struct Glyph {
uint8_t c = 0x00;
int fg = 0b111;
int bg = 0;
bool reverse = false;
};
struct RGB {
uint8_t r;
uint8_t g;
uint8_t b;
};
std::array<RGB, 17> color_map = {
// Normal colors
RGB{0x00, 0x00, 0x00},
RGB{0xAA, 0x00, 0x00},
RGB{0x00, 0xAA, 0x00},
RGB{0xAA, 0x55, 0x00},
RGB{0x00, 0x00, 0xAA},
RGB{0xAA, 0x00, 0xAA},
RGB{0x00, 0xAA, 0xAA},
RGB{0xAA, 0xAA, 0xAA},
// Bright colors
RGB{0x55, 0x55, 0x55},
RGB{0xFF, 0x55, 0x55},
RGB{0x55, 0xFF, 0x55},
RGB{0xFF, 0xFF, 0x55},
RGB{0x55, 0x55, 0xFF},
RGB{0xFF, 0x55, 0xFF},
RGB{0x55, 0xFF, 0xFF},
RGB{0xFF, 0xFF, 0xFF},
// Cursor
RGB{0xAA, 0xAA, 0x99}
};
template <int W, int H> template <int W, int H>
class Screen { class Screen {
public: public:
// @todo This should handle the input properly (same as fpga) // @todo This should handle the input properly (same as fpga)
void input(char c) { void input(uint8_t c) {
if (escape == 1) { if (escape == 1) {
if (c == '[') { if (c == '[') {
escape = 2; escape = 2;
@ -135,21 +176,27 @@ class Screen {
} }
} }
char get(int x, int y) { Glyph get(int x, int y) const {
return buffer[y*W + x]; Glyph g = buffer[((y+cursor.scroll) % H)*W + x];
// @todo Disable this until we can turn cursor on and off
// if (x == cursor.x && y == cursor.y) {
// g.fg = 0;
// g.bg = 16;
// }
return g;
} }
private: private:
void set_foreground(int color) { void set_foreground(int color) {
current.fg = color;
} }
void set_background(int color) { void set_background(int color) {
current.bg = color;
} }
void reverse(bool enable) { void reverse(bool enable) {
current.reverse = enable;
} }
void clear_screen() { void clear_screen() {
@ -157,8 +204,9 @@ class Screen {
set_foreground(0b111); set_foreground(0b111);
set_background(0); set_background(0);
for (char& c : buffer) { for (Glyph& g : buffer) {
c = 0x00; g = current;
g.c = 0x00;
} }
cursor = {0, 0, 0}; cursor = {0, 0, 0};
@ -174,8 +222,9 @@ class Screen {
} }
} }
void write(int x, int y, char c) { void write(int x, int y, uint8_t c) {
buffer[y*W + x] = c; buffer[((y+cursor.scroll) % H)*W + x] = current;
buffer[((y+cursor.scroll) % H)*W + x].c = c;
} }
void next() { void next() {
@ -207,12 +256,14 @@ class Screen {
} }
std::array<char, W*H> buffer = {0}; std::array<Glyph, W*H> buffer;
int escape = 0; int escape = 0;
int escape_parameter_1 = 0; int escape_parameter_1 = 0;
int escape_parameter_2 = 0; int escape_parameter_2 = 0;
Glyph current = {0, 0b111, 0, false};
struct { struct {
int x = 0; int x = 0;
int y = 0; int y = 0;
@ -252,7 +303,7 @@ class Memory {
ram[address] = value; ram[address] = value;
} }
uint8_t read(uint16_t address) { uint8_t read(uint16_t address) const {
if (cfg == 0 && address < 0x1000) { if (cfg == 0 && address < 0x1000) {
return rom[address]; return rom[address];
} }
@ -322,7 +373,7 @@ class Disk {
} }
bool is_ready() { bool is_ready() const {
return action != Action::NONE; return action != Action::NONE;
} }
@ -337,7 +388,7 @@ class Disk {
template <int W, int H> template <int W, int H>
class Emulator { class Emulator {
public: public:
Emulator(std::filesystem::path rom_name, std::filesystem::path disk_name) : memory(rom_name), disk(disk_name) { Emulator(std::filesystem::path rom_name, std::filesystem::path disk_name, std::string startup = "") : memory(rom_name), disk(disk_name) {
// SDL // SDL
int render_flags = SDL_RENDERER_ACCELERATED; int render_flags = SDL_RENDERER_ACCELERATED;
int window_flags = 0; int window_flags = 0;
@ -362,12 +413,23 @@ class Emulator {
} }
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Loading '../convert-font/font.png'"); SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO, "Loading '../convert-font/font.png'");
font = IMG_LoadTexture(renderer, "../convert-font/font.png"); SDL_Surface* surface = IMG_Load("../convert-font/font.png");
if (!surface) {
throw std::runtime_error("Failed to load font: " + std::string(IMG_GetError()));
}
SDL_SetColorKey(surface, SDL_TRUE, SDL_MapRGB(surface->format, 0, 0, 0));
font = SDL_CreateTextureFromSurface(renderer, surface);
if (!font) { if (!font) {
throw std::runtime_error("Failed to open font: " + std::string(IMG_GetError())); throw std::runtime_error("Failed to create texture from surface: " + std::string(IMG_GetError()));
} }
SDL_FreeSurface(surface);
surface = nullptr;
// Z80 // Z80
z.context = this; z.context = this;
z.read = z_read; z.read = z_read;
@ -379,12 +441,21 @@ class Emulator {
z80_reset(&z); z80_reset(&z);
SDL_StartTextInput(); SDL_StartTextInput();
for (char c : startup) {
input.push(c);
}
} }
~Emulator() { ~Emulator() {
SDL_DestroyTexture(font);
font = nullptr;
SDL_DestroyRenderer(renderer); SDL_DestroyRenderer(renderer);
renderer = nullptr;
SDL_DestroyWindow(window); SDL_DestroyWindow(window);
window = nullptr;
SDL_Quit(); SDL_Quit();
} }
@ -408,7 +479,7 @@ class Emulator {
// Input // Input
case 0x1E: { case 0x1E: {
char c = e->input.front(); uint8_t c = e->input.front();
e->input.pop(); e->input.pop();
return c; return c;
} }
@ -470,8 +541,7 @@ class Emulator {
} }
void update() { void update() {
// Run for 1000 cycles z80_run(&z, 1);
z80_run(&z, 1000);
} }
void draw_screen() { void draw_screen() {
@ -483,21 +553,35 @@ class Emulator {
dest.w = 16; dest.w = 16;
dest.h = 16; dest.h = 16;
SDL_SetTextureColorMod(font, 255, 255, 255);
for (int x = 0; x < W; ++x) { for (int x = 0; x < W; ++x) {
for (int y = 0; y < H; ++y) { for (int y = 0; y < H; ++y) {
char c = screen.get(x, y); const Glyph& g = screen.get(x, y);
src.x = (c % 16) * 16;
src.y = (c / 16) * 16; RGB fg = color_map[g.fg];
RGB bg = color_map[g.bg];
if (g.reverse) {
std::swap(fg, bg);
}
SDL_SetTextureColorMod(font, fg.r, fg.g, fg.b);
dest.x = x * 16; dest.x = x * 16;
dest.y = y * 16; dest.y = y * 16;
SDL_SetRenderDrawColor(renderer, bg.r, bg.g, bg.b, 0xFF);
SDL_RenderFillRect(renderer, &dest);
// Only render if the char is not empty
if (g.c && g.c != ' ') {
src.x = (g.c % 16) * 16;
src.y = (g.c / 16) * 16;
SDL_RenderCopy(renderer, font, &src, &dest); SDL_RenderCopy(renderer, font, &src, &dest);
} }
} }
} }
}
bool handle_events() { bool handle_events() {
SDL_Event event; SDL_Event event;
@ -517,7 +601,7 @@ class Emulator {
break; break;
case SDL_TEXTINPUT: { case SDL_TEXTINPUT: {
char c = event.text.text[0]; uint8_t c = event.text.text[0];
// @todo For some reason we cannot capture ctrl??? // @todo For some reason we cannot capture ctrl???
if (SDL_GetModState() & KMOD_ALT && c == 'c') { if (SDL_GetModState() & KMOD_ALT && c == 'c') {
input.push(0x03); input.push(0x03);
@ -536,7 +620,8 @@ class Emulator {
} }
void prepare() { void prepare() {
SDL_SetRenderDrawColor(renderer, 96, 128, 255, 255); // If we see this color something is going wrong
SDL_SetRenderDrawColor(renderer, 0xFF, 0x00, 0xFF, 0xFF);
SDL_RenderClear(renderer); SDL_RenderClear(renderer);
} }
@ -555,20 +640,29 @@ class Emulator {
Memory memory; Memory memory;
Disk disk; Disk disk;
std::queue<char> input; std::queue<uint8_t> input;
bool ctrl = false; bool ctrl = false;
}; };
int main() { int main(int argc, char* argv[]) {
Emulator<80, 45> e("../../software/monitor/.build/rom_monitor.bin", "../create-disk/disk.img"); std::string startup;
if (argc == 2) {
startup = std::string(argv[1]);
}
Emulator<80, 45> e("../../software/monitor/.build/rom_monitor.bin", "../create-disk/disk.img", startup);
std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now();
while (e.handle_events()) { while (e.handle_events()) {
e.prepare();
e.update(); e.update();
// Make sure we only draw once every 60 seconds
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - begin) >= std::chrono::milliseconds(1000/60)) {
begin = std::chrono::steady_clock::now();
e.prepare();
e.draw_screen(); e.draw_screen();
e.render(); e.render();
} }
}
} }