Emulator: separated terminal and status windows
This commit is contained in:
parent
ed3ed7bd03
commit
24158df261
@ -12,6 +12,10 @@
|
|||||||
|
|
||||||
#define ROM_SIZE 0x8000 /* 32 KiB */
|
#define ROM_SIZE 0x8000 /* 32 KiB */
|
||||||
#define MEMORY_SIZE 0xFFFF /* 64 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 {
|
typedef struct {
|
||||||
void* context;
|
void* context;
|
||||||
@ -23,6 +27,8 @@ typedef struct {
|
|||||||
zusize cycles;
|
zusize cycles;
|
||||||
zuint8 memory[65536];
|
zuint8 memory[65536];
|
||||||
Z80 cpu;
|
Z80 cpu;
|
||||||
|
WINDOW *terminal_win;
|
||||||
|
WINDOW *status_win;
|
||||||
} Machine;
|
} Machine;
|
||||||
|
|
||||||
|
|
||||||
@ -47,44 +53,59 @@ static zuint8 machine_cpu_in(Machine *self, zuint16 port) {
|
|||||||
if (decoded <= 0x1F) {
|
if (decoded <= 0x1F) {
|
||||||
// Port 0 (0x00 to 0x1F): terminal
|
// Port 0 (0x00 to 0x1F): terminal
|
||||||
// Read char from stin
|
// 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) {
|
if (decoded <= 0x3F) {
|
||||||
// Port 1 (0x20 to 0x3F): sound card (sn76489)
|
// 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;
|
return 0x00;
|
||||||
}
|
}
|
||||||
if (decoded <= 0x5F) {
|
if (decoded <= 0x5F) {
|
||||||
// Port 2 (0x40 to 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;
|
return 0x00;
|
||||||
}
|
}
|
||||||
if (decoded <= 0x7F) {
|
if (decoded <= 0x7F) {
|
||||||
// Port 3 (0x60 to 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;
|
return 0x00;
|
||||||
}
|
}
|
||||||
if (decoded <= 0x9F) {
|
if (decoded <= 0x9F) {
|
||||||
// Port 4 (0x80 to 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;
|
return 0x00;
|
||||||
}
|
}
|
||||||
if (decoded <= 0x5F) {
|
if (decoded <= 0x5F) {
|
||||||
// Port 5 (0xA0 to 0xBF)
|
// 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;
|
return 0x00;
|
||||||
}
|
}
|
||||||
if (decoded <= 0x5F) {
|
if (decoded <= 0x5F) {
|
||||||
// Port 6 (0xC0 to 0xDF)
|
// 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;
|
return 0x00;
|
||||||
}
|
}
|
||||||
if (decoded <= 0x5F) {
|
if (decoded <= 0x5F) {
|
||||||
// Port 7 (0xE0 to 0xFF)
|
// 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 {
|
} 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;
|
int decoded = port & bitmask;
|
||||||
if (decoded <= 0x1F) {
|
if (decoded <= 0x1F) {
|
||||||
// Port 0 (0x00 to 0x1F): terminal
|
// Port 0 (0x00 to 0x1F): terminal
|
||||||
attron(A_BOLD);
|
wprintw(self->terminal_win, "%c", value);
|
||||||
printw("%c", value);
|
|
||||||
attroff(A_BOLD);
|
|
||||||
} else if (decoded <= 0x3F) {
|
} else if (decoded <= 0x3F) {
|
||||||
// Port 1 (0x20 to 0x3F): sound card (sn76489)
|
// 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) {
|
} else if (decoded <= 0x5F) {
|
||||||
// Port 2 (0x40 to 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) {
|
} else if (decoded <= 0x7F) {
|
||||||
// Port 3 (0x60 to 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) {
|
} else if (decoded <= 0x9F) {
|
||||||
// Port 4 (0x80 to 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) {
|
} else if (decoded <= 0x5F) {
|
||||||
// Port 5 (0xA0 to 0xBF)
|
// 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) {
|
} else if (decoded <= 0x5F) {
|
||||||
// Port 6 (0xC0 to 0xDF)
|
// 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) {
|
} else if (decoded <= 0x5F) {
|
||||||
// Port 7 (0xE0 to 0xFF)
|
// 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 {
|
} 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];
|
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
|
// Setup virtual Pat80 computer
|
||||||
Z80 pat80Cpu = {};
|
Z80 pat80Cpu = {};
|
||||||
Machine pat80 = {
|
Machine pat80 = {
|
||||||
/*zusize*/ .cycles = 0,
|
/*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_initialize(&pat80);
|
||||||
machine_power(&pat80, Z_TRUE);
|
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 >
|
fread(&pat80.memory,ROM_SIZE,1,romFile); // load rom from file into memory, up >
|
||||||
fclose(romFile);
|
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
|
// Start emulated computer
|
||||||
machine_reset(&pat80);
|
machine_reset(&pat80);
|
||||||
machine_run(&pat80);
|
machine_run(&pat80);
|
||||||
|
|
||||||
// Stop ncurses
|
// Stop ncurses
|
||||||
endwin(); /* End curses mode */
|
delwin(pat80.terminal_win);
|
||||||
|
delwin(pat80.status_win);
|
||||||
|
endwin();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user