diff --git a/pat80-emulator/main.c b/pat80-emulator/main.c index 2c80b55..d2d99ba 100644 --- a/pat80-emulator/main.c +++ b/pat80-emulator/main.c @@ -12,6 +12,10 @@ #define ROM_SIZE 0x8000 /* 32 KiB */ #define MEMORY_SIZE 0xFFFF /* 64 KiB */ +#define TERMINAL_WIDTH 60 +#define TERMINAL_HEIGHT 25 +#define SPACING_BETWEEN_WINDOWS 3 +#define INSTRUCTION_WINDOW_HEIGHT 3 typedef struct { void* context; @@ -23,6 +27,8 @@ typedef struct { zusize cycles; zuint8 memory[65536]; Z80 cpu; + WINDOW *terminal_win; + WINDOW *status_win; } Machine; @@ -47,44 +53,59 @@ static zuint8 machine_cpu_in(Machine *self, zuint16 port) { if (decoded <= 0x1F) { // Port 0 (0x00 to 0x1F): terminal // Read char from stin - return getch(); + char c = getch(); + // Intercept emulator commands + switch (c) { + case 27: + // ESC: shutdown emulator + exit(0); // TODO: Shutdown ncurses and emulator cleanly + return 0; + default: + // Deliver keypress to pat80 + return c; + } } if (decoded <= 0x3F) { // Port 1 (0x20 to 0x3F): sound card (sn76489) - printw("\nsound_cmd[IN]: Not supported!\n"); + wprintw(self->status_win, "sound_cmd[IN]: Not supported!\n"); return 0x00; } if (decoded <= 0x5F) { // Port 2 (0x40 to 0x5F) - printw("IO_ERROR_IN: No device at port 2\n"); + wprintw(self->status_win, "IO_ERROR_IN: No device at port 2\n"); return 0x00; } if (decoded <= 0x7F) { // Port 3 (0x60 to 0x7F) - printw("IO_ERROR_IN: No device at port 3\n"); + wprintw(self->status_win, "IO_ERROR_IN: No device at port 3\n"); return 0x00; } if (decoded <= 0x9F) { // Port 4 (0x80 to 0x9F) - printw("IO_ERROR_IN: No device at port 4\n"); + wprintw(self->status_win, "IO_ERROR_IN: No device at port 4\n"); return 0x00; } if (decoded <= 0x5F) { // Port 5 (0xA0 to 0xBF) - printw("IO_ERROR_IN: No device at port 5\n"); + wprintw(self->status_win, "IO_ERROR_IN: No device at port 5\n"); return 0x00; } if (decoded <= 0x5F) { // Port 6 (0xC0 to 0xDF) - printw("IO_ERROR_IN: No device at port 6\n"); + wprintw(self->status_win, "IO_ERROR_IN: No device at port 6\n"); return 0x00; } if (decoded <= 0x5F) { // Port 7 (0xE0 to 0xFF) - printw("IO_ERROR_IN: No device at port 7\n"); + wprintw(self->status_win, "IO_ERROR_IN: No device at port 7\n"); } else { - printw("IO_ERROR_IN: Invalid port address: %#04x\n", port); + wprintw(self->status_win, "IO_ERROR_IN: Invalid port address: %#04x\n", port); } + + // TODO: place this in a refresh cycle between CPU instructions + refresh(); + wrefresh(self->terminal_win); + wrefresh(self->status_win); } @@ -97,33 +118,36 @@ static void machine_cpu_out(Machine *self, zuint16 port, zuint8 value) { int decoded = port & bitmask; if (decoded <= 0x1F) { // Port 0 (0x00 to 0x1F): terminal - attron(A_BOLD); - printw("%c", value); - attroff(A_BOLD); + wprintw(self->terminal_win, "%c", value); } else if (decoded <= 0x3F) { // Port 1 (0x20 to 0x3F): sound card (sn76489) - printw("\nsound_cmd[%#04x]\n", value); + wprintw(self->status_win, "sound_cmd[%#04x]\n", value); } else if (decoded <= 0x5F) { // Port 2 (0x40 to 0x5F) - printw("IO_ERROR_OUT: No device at port 2\n"); + wprintw(self->status_win, "IO_ERROR_OUT: No device at port 2\n"); } else if (decoded <= 0x7F) { // Port 3 (0x60 to 0x7F) - printw("IO_ERROR_OUT: No device at port 3\n"); + wprintw(self->status_win, "IO_ERROR_OUT: No device at port 3\n"); } else if (decoded <= 0x9F) { // Port 4 (0x80 to 0x9F) - printw("IO_ERROR_OUT: No device at port 4\n"); + wprintw(self->status_win, "IO_ERROR_OUT: No device at port 4\n"); } else if (decoded <= 0x5F) { // Port 5 (0xA0 to 0xBF) - printw("IO_ERROR_OUT: No device at port 5\n"); + wprintw(self->status_win, "IO_ERROR_OUT: No device at port 5\n"); } else if (decoded <= 0x5F) { // Port 6 (0xC0 to 0xDF) - printw("IO_ERROR_OUT: No device at port 6\n"); + wprintw(self->status_win, "IO_ERROR_OUT: No device at port 6\n"); } else if (decoded <= 0x5F) { // Port 7 (0xE0 to 0xFF) - printw("IO_ERROR_OUT: No device at port 7\n"); + wprintw(self->status_win, "IO_ERROR_OUT: No device at port 7\n"); } else { - printw("IO_ERROR_OUT: Invalid port address: %#04x\n", port); + wprintw(self->status_win, "IO_ERROR_OUT: Invalid port address: %#04x\n", port); } + + // TODO: place this in a refresh cycle between CPU instructions + refresh(); + wrefresh(self->terminal_win); + wrefresh(self->status_win); } @@ -177,13 +201,36 @@ int main(int argc, char *argv[]) { } char* romFilePath = argv[1]; + + // Init ncurses + initscr(); // Start curses mode + raw(); // Line buffering disabled (get character without waiting for ENTER key) + keypad(stdscr, TRUE); // We get F1, F2 etc.. + noecho(); // Don't echo() while we do getch + start_color(); // Use colors + init_pair(1, COLOR_WHITE, COLOR_BLUE); // Terminal window color + init_pair(2, COLOR_YELLOW, COLOR_BLACK); // Status window color + int x,y; + getmaxyx(stdscr, y,x); + // Setup virtual Pat80 computer Z80 pat80Cpu = {}; Machine pat80 = { /*zusize*/ .cycles = 0, - /*Z80*/ .cpu = pat80Cpu + /*Z80*/ .cpu = pat80Cpu, + .terminal_win = newwin(TERMINAL_HEIGHT, TERMINAL_WIDTH, INSTRUCTION_WINDOW_HEIGHT, 0), + .status_win = newwin(y, x - TERMINAL_WIDTH - SPACING_BETWEEN_WINDOWS, INSTRUCTION_WINDOW_HEIGHT, TERMINAL_WIDTH + SPACING_BETWEEN_WINDOWS) }; + wbkgd(pat80.terminal_win, COLOR_PAIR(1)); // Ncurses: set terminal window color + wbkgd(pat80.status_win, COLOR_PAIR(2)); + scrollok(pat80.terminal_win, TRUE); // Ncurses: Allow scrolling when reached end of window + scrollok(pat80.status_win, TRUE); + attron(A_BOLD); // Print instructions + printw("Emulator commands\n"); + attroff(A_BOLD); + printw("ESC: Exit"); + machine_initialize(&pat80); machine_power(&pat80, Z_TRUE); @@ -197,18 +244,13 @@ int main(int argc, char *argv[]) { fread(&pat80.memory,ROM_SIZE,1,romFile); // load rom from file into memory, up > fclose(romFile); - // Init ncurses - initscr(); // Start curses mode - raw(); // Line buffering disabled (get character without waiting for ENTER key) - keypad(stdscr, TRUE); // We get F1, F2 etc.. - noecho(); // Don't echo() while we do getch - scrollok(stdscr, TRUE); // Allow scrolling when reached end of window - // Start emulated computer machine_reset(&pat80); machine_run(&pat80); // Stop ncurses - endwin(); /* End curses mode */ + delwin(pat80.terminal_win); + delwin(pat80.status_win); + endwin(); return 0; }