mii_emu/nuklear/mii_nuklear.c
Michel Pollet af6ff70155 Split the CPU regulator thread to it's own file
Removed it fron the Nuklear file, as it's actually not doign anything UI
based.
Also cleanup up type names etc

Signed-off-by: Michel Pollet <buserror@gmail.com>
2023-10-28 07:23:06 +01:00

501 lines
16 KiB
C

/*
* mii_nuklear.c
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include "mii.h"
#include "mii_bank.h"
#include "mii_sw.h"
#include "mii_thread.h"
#define NK_INCLUDE_FIXED_TYPES
#define NK_INCLUDE_STANDARD_IO
#define NK_INCLUDE_STANDARD_VARARGS
#define NK_INCLUDE_DEFAULT_ALLOCATOR
#define NK_INCLUDE_VERTEX_BUFFER_OUTPUT
#define NK_INCLUDE_FONT_BAKING
#define NK_INCLUDE_DEFAULT_FONT
#define NK_BUTTON_TRIGGER_ON_RELEASE
#include "nuklear.h"
#include "nuklear_xlib_gl3.h"
//#include "stb_image_write.h"
#include <GL/gl.h>
#include <GL/glx.h>
#define _NK_RGBA(_r,_g,_b,_a) {.r=_r,.g=_g,.b=_b,.a=_a}
static const struct nk_color style[NK_COLOR_COUNT] = {
[NK_COLOR_TEXT] = _NK_RGBA(0, 0, 0, 255),
[NK_COLOR_WINDOW] = _NK_RGBA(175, 175, 175, 255),
[NK_COLOR_HEADER] = _NK_RGBA(175, 175, 175, 255),
[NK_COLOR_BORDER] = _NK_RGBA(0, 0, 0, 255),
[NK_COLOR_BUTTON] = _NK_RGBA(185, 185, 185, 255),
[NK_COLOR_BUTTON_HOVER] = _NK_RGBA(170, 170, 170, 255),
[NK_COLOR_BUTTON_ACTIVE] = _NK_RGBA(160, 160, 160, 255),
[NK_COLOR_TOGGLE] = _NK_RGBA(150, 150, 150, 255),
[NK_COLOR_TOGGLE_HOVER] = _NK_RGBA(120, 120, 120, 255),
[NK_COLOR_TOGGLE_CURSOR] = _NK_RGBA(175, 175, 175, 255),
[NK_COLOR_SELECT] = _NK_RGBA(190, 190, 190, 255),
[NK_COLOR_SELECT_ACTIVE] = _NK_RGBA(175, 175, 175, 255),
[NK_COLOR_SLIDER] = _NK_RGBA(190, 190, 190, 255),
[NK_COLOR_SLIDER_CURSOR] = _NK_RGBA(80, 80, 80, 255),
[NK_COLOR_SLIDER_CURSOR_HOVER] = _NK_RGBA(70, 70, 70, 255),
[NK_COLOR_SLIDER_CURSOR_ACTIVE] = _NK_RGBA(60, 60, 60, 255),
[NK_COLOR_PROPERTY] = _NK_RGBA(175, 175, 175, 255),
[NK_COLOR_EDIT] = _NK_RGBA(150, 150, 150, 255),
[NK_COLOR_EDIT_CURSOR] = _NK_RGBA(0, 0, 0, 255),
[NK_COLOR_COMBO] = _NK_RGBA(175, 175, 175, 255),
[NK_COLOR_CHART] = _NK_RGBA(160, 160, 160, 255),
[NK_COLOR_CHART_COLOR] = _NK_RGBA(45, 45, 45, 255),
[NK_COLOR_CHART_COLOR_HIGHLIGHT] = _NK_RGBA( 255, 0, 0, 255),
[NK_COLOR_SCROLLBAR] = _NK_RGBA(180, 180, 180, 255),
[NK_COLOR_SCROLLBAR_CURSOR] = _NK_RGBA(140, 140, 140, 255),
[NK_COLOR_SCROLLBAR_CURSOR_HOVER] = _NK_RGBA(150, 150, 150, 255),
[NK_COLOR_SCROLLBAR_CURSOR_ACTIVE] = _NK_RGBA(160, 160, 160, 255),
[NK_COLOR_TAB_HEADER] = _NK_RGBA(180, 180, 180, 255),
};
static GLuint screen_texture;
static struct nk_image screen_nk;
extern struct nk_font *nk_main_font;
struct nk_font *nk_mono_font = NULL;
extern const unsigned char mii_proggy_data[];
extern const unsigned int mii_proggy_size;
extern const unsigned char mii_droid_data[];
extern const unsigned int mii_droid_size;
void
mii_nuklear_init(
mii_t *mii,
struct nk_context *ctx)
{
nk_style_from_table(ctx, style);
{
struct nk_font_atlas *atlas;
nk_x11_font_stash_begin(&atlas);
struct nk_font_config cfg = nk_font_config(0);
#if 0
nk_rune ranges[] = {
0x0020, 0x007E, /* Ascii */
0x00A1, 0x00FF, /* Symbols + Umlaute */
0
};
#endif
cfg.range = nk_font_default_glyph_ranges();
cfg.oversample_h = cfg.oversample_v = 1;
cfg.pixel_snap = true;
struct nk_font *nkf = nk_font_atlas_add_from_memory(atlas,
(void*)mii_proggy_data, mii_proggy_size, 20, &cfg);
nk_x11_font_stash_end();
nk_mono_font = nkf;
}
glGenTextures(1, &screen_texture);
glBindTexture(GL_TEXTURE_2D, screen_texture);
glTexImage2D(GL_TEXTURE_2D, 0, 4,
MII_VRAM_WIDTH,
MII_VRAM_HEIGHT, 0, GL_RGBA,
GL_UNSIGNED_BYTE,
mii->video.pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// printf("%s texture created %d\n", __func__, screen_texture);
// display opengl error
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
printf("Error creating texture: %d\n", err);
}
screen_nk = nk_subimage_id(
screen_texture, MII_VRAM_WIDTH, MII_VRAM_HEIGHT,
nk_rect(0, 0, MII_VIDEO_WIDTH, MII_VIDEO_HEIGHT));
/* start the CPU/emulator thread */
mii_thread_start(mii);
}
extern int disk2_debug;
int show_zero_page = 0;
static void
mii_nuklear_handle_input(
mii_t *mii,
struct nk_context *ctx)
{
struct nk_input *in = &ctx->input;
if (in->keyboard.text_len) {
// printf("INPUT %d %02x %s\n",
// in->keyboard.text_len, in->keyboard.text[0], in->keyboard.text);
if (in->keyboard.text_len < 4) {
mii_keypress(mii, in->keyboard.text[0]);
} else if (in->keyboard.text_len > 1) {
uint32_t *raw = ((uint32_t *) in->keyboard.text);
for (int ki = 0; ki < in->keyboard.text_len / 4; ki ++) {
uint32_t key = (raw[ki] >> 16) & 0xffff;
uint8_t down = raw[ki] & 0xff;
printf("KEY %04x %s\n", key, down ? "down" : "up");
if (down) {
if (key == 0xffc9) { // F12
if (nk_input_is_key_down(in, NK_KEY_CTRL)) {
mii_th_signal_t sig = {
.cmd = SIGNAL_RESET,
.data = nk_input_is_key_down(in, NK_KEY_SHIFT)
};
mii_th_fifo_write(mii_thread_get_fifo(mii), sig);
printf("RESET\n");
}
} else if (key == 0xffc8) { // F11
if (nk_input_is_key_down(in, NK_KEY_CTRL)) {
mii_th_signal_t sig = {
.cmd = SIGNAL_STOP,
};
mii_th_fifo_write(mii_thread_get_fifo(mii), sig);
printf("STOP\n");
}
} else if (key == 0xffc7) { // F10
if (nk_input_is_key_down(in, NK_KEY_CTRL)) {
mii_th_signal_t sig = {
.cmd = SIGNAL_STEP,
};
mii_th_fifo_write(mii_thread_get_fifo(mii), sig);
printf("STEP\n");
}
} else if (key == 0xffc6) { // F9
if (nk_input_is_key_down(in, NK_KEY_CTRL)) {
mii_th_signal_t sig = {
.cmd = SIGNAL_RUN,
};
mii_th_fifo_write(mii_thread_get_fifo(mii), sig);
printf("RUN\n");
}
} else if (key == 0xffc2) { // F5
mii->speed = 1.0;
printf("Speed Normal (1MHz)\n");
} else if (key == 0xffc3) { // F6
mii->speed = 4;
printf("Speed Fast (4MHz)\n");
}
}
if (key == 0xffeb || key == 0xffec) { // super left/right
key -= 0xffeb;
mii_bank_t *bank = &mii->bank[MII_BANK_MAIN];
uint8_t old = mii_bank_peek(bank, 0xc061 + key);
mii_bank_poke(bank, 0xc061 + key, down ? 0x80 : 0);
if (!!down != !!old) {
printf("Apple %s %s\n", key ? "Open" : "Close",
down ? "down" : "up");
}
}
}
}
in->keyboard.text_len = 0;
} else {
mii_th_signal_t sig = {.cmd = -1 };
if (nk_input_is_key_pressed(in, NK_KEY_ENTER))
sig.data = 0x0d;
else if (nk_input_is_key_pressed(in, NK_KEY_BACKSPACE))
sig.data = 0x08;
else if (nk_input_is_key_pressed(in, NK_KEY_DEL))
sig.data = 0x7f;
else if (nk_input_is_key_pressed(in, NK_KEY_UP))
sig.data = 'K' - 'A' + 1;
else if (nk_input_is_key_pressed(in, NK_KEY_DOWN))
sig.data = 'J' - 'A' + 1;
else if (nk_input_is_key_pressed(in, NK_KEY_LEFT))
sig.data = 'H' - 'A' + 1;
else if (nk_input_is_key_pressed(in, NK_KEY_RIGHT))
sig.data = 'U' - 'A' + 1;
else if (nk_input_is_key_pressed(in, NK_KEY_ESCAPE))
sig.data = 0x1b;
if (sig.data) {
// mii_th_fifo_write(mii_thread_get_fifo(mii), sig);
// printf("Key %d\n", sig.data);
mii_keypress(mii, sig.data);
}
}
}
void
mii_nuklear(
mii_t *mii,
struct nk_context *ctx)
{
if (mii->video.frame_count != mii->video.frame_drawn) {
mii->video.frame_drawn = mii->video.frame_count;
// update texture with new pixels; we only need 192 lines, the others
// are padding for the power of 2 texture
glBindTexture(GL_TEXTURE_2D, screen_texture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
MII_VRAM_WIDTH,
MII_VIDEO_HEIGHT, GL_RGBA,
GL_UNSIGNED_BYTE,
mii->video.pixels);
}
mii_nuklear_handle_input(mii, ctx);
int height = 720 - 8;
int width = MII_VIDEO_WIDTH * (height / (float)MII_VIDEO_HEIGHT);
int xpos = 1280 / 2 - width / 2;
{
struct nk_style *s = &ctx->style;
nk_style_push_color(ctx, &s->window.background,
nk_rgba(0,0,0, 255));
nk_style_push_style_item(ctx, &s->window.fixed_background,
nk_style_item_color(nk_rgba(0,0,0, 255)));
if (nk_begin(ctx, "Apple //e Enhanced",
nk_rect(xpos, 0, width + 10, height + 20),
NK_WINDOW_NO_SCROLLBAR)) {
nk_layout_row_static(ctx, height, width, 1);
static int was_in = -1;
if (nk_widget_is_hovered(ctx) && mii->mouse.enabled) {
if (was_in != 1) {
was_in = 1;
ctx->input.mouse.grab = 1;
// printf("IN\n");
}
struct nk_rect bounds = nk_widget_bounds(ctx);
// normalize mouse coordinates
double x = ctx->input.mouse.pos.x - bounds.x;
double y = ctx->input.mouse.pos.y - bounds.y;
// get mouse button state
int button = ctx->input.mouse.buttons[NK_BUTTON_LEFT].down;
// clamp coordinates inside bounds
double vw = bounds.w;
double vh = bounds.h;
double mw = mii->mouse.max_x - mii->mouse.min_x;
double mh = mii->mouse.max_y - mii->mouse.min_y;
mii->mouse.x = mii->mouse.min_x + (x * mw / vw) + 0.5;
mii->mouse.y = mii->mouse.min_y + (y * mh / vh) + 0.5;
mii->mouse.button = button;
// printf("Mouse is %d %d\n", (int)mii->mouse.x, (int)mii->mouse.y);
} else {
if (was_in == 1) {
was_in = 0;
ctx->input.mouse.ungrab = 1;
// printf("OUT\n");
}
}
nk_image(ctx, screen_nk);
nk_end(ctx);
nk_style_pop_color(ctx);
nk_style_pop_style_item(ctx);
}
}
struct nk_rect bounds = { .x = 0, .y = 0, .w = xpos, .h = height };
// nk_window_get_bounds(ctx);
bool in = nk_input_is_mouse_hovering_rect(&ctx->input, bounds);
static bool menu_open = false;
if (in || menu_open) {
struct nk_style *s = &ctx->style;
nk_style_push_color(ctx, &s->window.background,
nk_rgba(175, 175, 175, 255));
nk_style_push_style_item(ctx, &s->window.fixed_background,
nk_style_item_color(nk_rgba(175, 175, 175, 255)));
if (nk_begin(ctx, "Left Bar",
nk_rect(0, 0, xpos, height + 20),
NK_WINDOW_NO_SCROLLBAR)) {
#if 0
nk_menubar_begin(ctx);
nk_layout_row_begin(ctx, NK_STATIC, 25, 2);
nk_layout_row_push(ctx, 60);
bool menu = false;
if (nk_menu_begin_label(ctx, "MII", NK_TEXT_LEFT,
nk_vec2(140, 200))) {
static size_t prog = 40;
static float slider = 0.5;
static int check = nk_true;
nk_layout_row_dynamic(ctx, 25, 1);
if (nk_menu_item_label(ctx, "Hide", NK_TEXT_LEFT))
;//show_menu = nk_false;
if (nk_menu_item_label(ctx, "About", NK_TEXT_LEFT))
;//show_app_about = nk_true;
nk_progress(ctx, &prog, 100, NK_MODIFIABLE);
nk_slider_float(ctx, 0.01, &slider, 1.0, 0.05);
nk_checkbox_label(ctx, "Mute", &check);
nk_menu_end(ctx);
menu = true;
}
menu_open = menu;
nk_menubar_end(ctx);
nk_layout_space_end(ctx);
#endif
int rw = xpos - 8;
// nk_layout_row_dynamic(ctx, 4, 1);
// nk_spacing(ctx, 1);
nk_layout_row_static(ctx, 30, rw, 1);
if (nk_button_label(ctx, "C-RESET"))
mii_reset(mii, false);
if (nk_button_label(ctx, "C-OA-RESET"))
mii_reset(mii, true);
// nk_layout_row_dynamic(ctx, 0, 1);
nk_layout_row_begin(ctx, NK_STATIC, 30, 2);
nk_layout_row_push(ctx, 20);
nk_label(ctx, "V:", NK_TEXT_LEFT);
nk_layout_row_push(ctx, rw - 20 - 4);
const char *video_modes[] = {
"Color",
"Green",
"Amber",
};
mii->video.color_mode = nk_combo(ctx,
video_modes, NK_LEN(video_modes),
mii->video.color_mode, 25, nk_vec2(80,200));
nk_layout_space_end(ctx);
nk_layout_row_begin(ctx, NK_STATIC, 30, 1);
nk_layout_row_push(ctx, rw);
const char *speed[] = {
"1 MHz",
"Slow",
"Fast",
};
nk_label(ctx, "Speed:", NK_TEXT_LEFT);
nk_layout_row_push(ctx, 100);
int spi = mii->speed > .95 && mii->speed < 1.05 ? 0 :
mii->speed < 0.9 ? 1 : 2;
spi = nk_combo(ctx,
speed, NK_LEN(speed),
spi, 25, nk_vec2(80,200));
mii->speed = spi == 0 ? 1.0 : spi == 1 ? 0.2 : 4.0;
nk_layout_space_end(ctx);
}
nk_end(ctx);
nk_style_pop_color(ctx);
nk_style_pop_style_item(ctx);
}
if ( 0 && nk_begin(ctx, "Controls",
nk_rect(width, 0, 350, 10 + 192 * 3),
NK_WINDOW_NO_SCROLLBAR)) {
nk_layout_row_static(ctx, 30, 110, 2);
if (nk_button_label(ctx, "C-RESET"))
mii_reset(mii, false);
if (nk_button_label(ctx, "C-OA-RESET"))
mii_reset(mii, true);
#if 0
if (nk_button_label(ctx, "Screenshot")) {
stbi_write_png("screen.png",
MII_VIDEO_WIDTH, MII_VIDEO_HEIGHT, 4, mii->video.pixels,
MII_VRAM_WIDTH * 4);
printf("Screenshot taken\n");
}
#endif
nk_layout_row_dynamic(ctx, 30, 4);
nk_label(ctx, "Speed:", NK_TEXT_CENTERED);
if (nk_option_label(ctx, "Slow", mii->speed < 0.9)) mii->speed = 0.2;
if (nk_option_label(ctx, "1 MHz", mii->speed > .95 && mii->speed < 1.05)) mii->speed = 1.0;
if (nk_option_label(ctx, "Fast", mii->speed > 1.1 && mii->speed < 4.1)) mii->speed = 4.0;
nk_layout_row_dynamic(ctx, 30, 4);
nk_label(ctx, "Video:", NK_TEXT_CENTERED);
if (nk_option_label(ctx, "Color", mii->video.color_mode == MII_VIDEO_COLOR))
mii->video.color_mode = MII_VIDEO_COLOR;
if (nk_option_label(ctx, "Green", mii->video.color_mode == MII_VIDEO_GREEN))
mii->video.color_mode = MII_VIDEO_GREEN;
if (nk_option_label(ctx, "Amber", mii->video.color_mode == MII_VIDEO_AMBER))
mii->video.color_mode = MII_VIDEO_AMBER;
#if 0
nk_layout_row_dynamic(ctx, 20, 1);
nk_style_set_font(ctx, &nk_mono_font->handle);
struct nk_color save = ctx->style.window.background;
ctx->style.window.background = (struct nk_color)_NK_RGBA(0, 0, 0, 255);
struct nk_color fore = (struct nk_color)_NK_RGBA(0, 255, 0, 255);
char label[64];
mii_dasm_t _dasm = {};
mii_dasm_t *dasm = &_dasm;
mii_dasm_init(dasm, mii, 0);
dasm->mii = mii;
// display the last few cycles up to the PC
for (int di = 0; di < 3; di++) {
int pci = (mii_trace.idx + MII_PC_LOG_SIZE - 3 + di) % MII_PC_LOG_SIZE;
dasm->pc = mii_trace.log[pci];
mii_dasm_step(dasm, label, sizeof(label));
if (di == 2)
label[0] = '*';
nk_label_colored(ctx, label, NK_TEXT_LEFT, fore);
}
// and the (potentially) next instruction here
mii_dasm_step(dasm, label, sizeof(label));
nk_label_colored(ctx, label, NK_TEXT_LEFT, fore);
sprintf(label, "A:%02X X:%02X Y:%02X S:%02X",
mii->cpu.A, mii->cpu.X, mii->cpu.Y, mii->cpu.S);
nk_label_colored(ctx, label, NK_TEXT_CENTERED, fore);
char n[] = {'C','Z','I','D','B','V','N'};
label[0] = 0;
sprintf(label, "%04x ", mii->cpu.PC);
for (int i = 0; i < 7; i++)
sprintf(label + strlen(label), "%c%d ", n[i],
mii->cpu.P.P[i]);
nk_label_colored(ctx, label, NK_TEXT_CENTERED, fore);
ctx->style.window.background = save;
nk_style_set_font(ctx, &nk_main_font->handle);
#endif
nk_layout_row_static(ctx, 30, 30, 3);
if (nk_button_symbol(ctx, NK_SYMBOL_RECT_SOLID)) {
mii_th_fifo_write(mii_thread_get_fifo(mii), (mii_th_signal_t){.cmd = SIGNAL_STOP});
}
if (nk_button_symbol(ctx, NK_SYMBOL_PLUS)) {
mii_th_fifo_write(mii_thread_get_fifo(mii), (mii_th_signal_t){.cmd = SIGNAL_STEP});
}
if (nk_button_symbol(ctx, NK_SYMBOL_TRIANGLE_RIGHT)) {
mii_th_fifo_write(mii_thread_get_fifo(mii), (mii_th_signal_t){.cmd = SIGNAL_RUN});
}
nk_layout_row_dynamic(ctx, 20, 3);
{
char label[32];
sprintf(label, "CPU: %.1fMHz", mii->speed_current);
nk_label(ctx, label, NK_TEXT_CENTERED);
}
#if 0
nk_layout_row_dynamic(ctx, 20, 2);
nk_checkbox_label(ctx, "Disk II Debug", &disk2_debug);
nk_checkbox_label(ctx, "Slowmo", &mii_SLOW);
// nk_checkbox_label(ctx, "Zero Page", &show_zero_page);
#endif
nk_end(ctx);
}
if (show_zero_page) {
if (nk_begin(ctx, "Zero Page",
nk_rect(0, 10 + 192 * 3, 600, 10 + 16 * 20),
0 )) {
nk_layout_row_dynamic(ctx, 20, 1);
nk_style_set_font(ctx, &nk_mono_font->handle);
struct nk_color fore = (struct nk_color)_NK_RGBA(0, 255, 0, 255);
uint8_t *zp = mii->bank[0].mem;
char label[128];
for (int ri = 0; ri < (256 / 16); ri++) {
sprintf(label, "%02x: ", ri * 16);
for (int ci = 0; ci < 16; ci++) {
sprintf(label + strlen(label), "%02X ",
zp[ri * 16 + ci]);
}
nk_label_colored(ctx, label, NK_TEXT_LEFT, fore);
}
nk_style_set_font(ctx, &nk_main_font->handle);
nk_end(ctx);
}
}
}