Added color supprt, render only happens every 1/60 of a second and added startup commands
This commit is contained in:
parent
6d6a304bd2
commit
399dbef684
164
src/main.cpp
164
src/main.cpp
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user