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 <chrono>
#include <cstring>
#include <fstream>
#include <stdexcept>
@ -20,6 +21,8 @@
#include "SDL_events.h"
#include "SDL_keyboard.h"
#include "SDL_keycode.h"
#include "SDL_pixels.h"
#include "SDL_surface.h"
#include "Z/types/base.h"
#include "Z80.h"
@ -28,11 +31,49 @@
#define SD_PAGE_SIZE 512
#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>
class Screen {
public:
// @todo This should handle the input properly (same as fpga)
void input(char c) {
void input(uint8_t c) {
if (escape == 1) {
if (c == '[') {
escape = 2;
@ -135,21 +176,27 @@ class Screen {
}
}
char get(int x, int y) {
return buffer[y*W + x];
Glyph get(int x, int y) const {
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:
void set_foreground(int color) {
current.fg = color;
}
void set_background(int color) {
current.bg = color;
}
void reverse(bool enable) {
current.reverse = enable;
}
void clear_screen() {
@ -157,8 +204,9 @@ class Screen {
set_foreground(0b111);
set_background(0);
for (char& c : buffer) {
c = 0x00;
for (Glyph& g : buffer) {
g = current;
g.c = 0x00;
}
cursor = {0, 0, 0};
@ -174,8 +222,9 @@ class Screen {
}
}
void write(int x, int y, char c) {
buffer[y*W + x] = c;
void write(int x, int y, uint8_t c) {
buffer[((y+cursor.scroll) % H)*W + x] = current;
buffer[((y+cursor.scroll) % H)*W + x].c = c;
}
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_parameter_1 = 0;
int escape_parameter_2 = 0;
Glyph current = {0, 0b111, 0, false};
struct {
int x = 0;
int y = 0;
@ -252,7 +303,7 @@ class Memory {
ram[address] = value;
}
uint8_t read(uint16_t address) {
uint8_t read(uint16_t address) const {
if (cfg == 0 && address < 0x1000) {
return rom[address];
}
@ -322,7 +373,7 @@ class Disk {
}
bool is_ready() {
bool is_ready() const {
return action != Action::NONE;
}
@ -337,7 +388,7 @@ class Disk {
template <int W, int H>
class Emulator {
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
int render_flags = SDL_RENDERER_ACCELERATED;
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'");
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) {
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
z.context = this;
z.read = z_read;
@ -379,12 +441,21 @@ class Emulator {
z80_reset(&z);
SDL_StartTextInput();
for (char c : startup) {
input.push(c);
}
}
~Emulator() {
SDL_DestroyTexture(font);
font = nullptr;
SDL_DestroyRenderer(renderer);
renderer = nullptr;
SDL_DestroyWindow(window);
window = nullptr;
SDL_Quit();
}
@ -408,7 +479,7 @@ class Emulator {
// Input
case 0x1E: {
char c = e->input.front();
uint8_t c = e->input.front();
e->input.pop();
return c;
}
@ -470,8 +541,7 @@ class Emulator {
}
void update() {
// Run for 1000 cycles
z80_run(&z, 1000);
z80_run(&z, 1);
}
void draw_screen() {
@ -483,18 +553,32 @@ class Emulator {
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 = screen.get(x, y);
src.x = (c % 16) * 16;
src.y = (c / 16) * 16;
const Glyph& g = screen.get(x, y);
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.y = y * 16;
SDL_RenderCopy(renderer, font, &src, &dest);
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);
}
}
}
}
@ -517,7 +601,7 @@ class Emulator {
break;
case SDL_TEXTINPUT: {
char c = event.text.text[0];
uint8_t c = event.text.text[0];
// @todo For some reason we cannot capture ctrl???
if (SDL_GetModState() & KMOD_ALT && c == 'c') {
input.push(0x03);
@ -536,7 +620,8 @@ class Emulator {
}
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);
}
@ -555,20 +640,29 @@ class Emulator {
Memory memory;
Disk disk;
std::queue<char> input;
std::queue<uint8_t> input;
bool ctrl = false;
};
int main() {
Emulator<80, 45> e("../../software/monitor/.build/rom_monitor.bin", "../create-disk/disk.img");
int main(int argc, char* argv[]) {
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()) {
e.prepare();
e.update();
e.draw_screen();
e.render();
// 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.render();
}
}
}