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
158
src/main.cpp
158
src/main.cpp
|
@ -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();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user