Files
z80_monitor/monitor.c
T
2026-10-01 12:44:11 +02:00

306 lines
7.6 KiB
C

#include <stdint.h>
#include "bios.h"
#define LINE_SIZE 80
#define MAX_ARGS 40
#define DEFAULT_DUMP_LENGTH 0x40
#define MAX_DUMP_LENGTH 0x100
static char hex_digit(uint8_t value)
{
value &= 0x0f;
return value < 10 ? (char)('0' + value) : (char)('A' + value - 10);
}
static void print_hex8(uint8_t value)
{
bios_putc(hex_digit(value >> 4));
bios_putc(hex_digit(value));
}
static void print_hex16(uint16_t value)
{
print_hex8((uint8_t)(value >> 8));
print_hex8((uint8_t)value);
}
static uint8_t hex_value(char c, uint8_t *value)
{
if (c >= '0' && c <= '9') {
*value = (uint8_t)(c - '0');
return 1;
}
if (c >= 'a' && c <= 'f') {
*value = (uint8_t)(c - 'a' + 10);
return 1;
}
if (c >= 'A' && c <= 'F') {
*value = (uint8_t)(c - 'A' + 10);
return 1;
}
return 0;
}
static uint8_t parse_hex(const char *text, uint16_t *result)
{
uint16_t value = 0;
uint8_t digit;
uint8_t digits = 0;
if (text[0] == '0' && (text[1] == 'x' || text[1] == 'X')) text += 2;
while (*text) {
if (digits == 4 || !hex_value(*text++, &digit)) return 0;
value = (uint16_t)((value << 4) | digit);
++digits;
}
if (digits == 0) return 0;
*result = value;
return 1;
}
static uint8_t strings_equal(const char *a, const char *b)
{
char ca;
char cb;
do {
ca = *a++;
cb = *b++;
if (ca >= 'A' && ca <= 'Z') ca += 'a' - 'A';
if (cb >= 'A' && cb <= 'Z') cb += 'a' - 'A';
if (ca != cb) return 0;
} while (ca);
return 1;
}
static uint8_t split_line(char *line, char **argv)
{
uint8_t argc = 0;
while (*line) {
while (*line == ' ' || *line == '\t') ++line;
if (!*line) break;
if (argc == MAX_ARGS) return argc;
argv[argc++] = line;
while (*line && *line != ' ' && *line != '\t') ++line;
if (*line) *line++ = 0;
}
return argc;
}
static void print_help(void)
{
bios_puts(
"Commands (hex numbers):\r\n"
" D addr [len] Dump memory\r\n"
" C start end 16-bit checksum, inclusive\r\n"
" W addr byte... Write memory\r\n"
" I port Read I/O\r\n"
" O port byte Write I/O\r\n"
" G addr Execute\r\n"
" B Tiny BASIC\r\n"
" H Help\r\n");
}
static void command_dump(uint8_t argc, char **argv)
{
uint16_t address;
uint16_t length = DEFAULT_DUMP_LENGTH;
uint16_t offset = 0;
if (argc < 2 || argc > 3 || !parse_hex(argv[1], &address) ||
(argc == 3 && !parse_hex(argv[2], &length))) {
bios_puts("Usage: D address [length]\r\n");
return;
}
if (length > MAX_DUMP_LENGTH) {
bios_puts("Length must be 0100 or less\r\n");
return;
}
while (offset < length) {
uint8_t column;
uint8_t row_length = (uint8_t)((length - offset) > 16 ? 16 : (length - offset));
print_hex16((uint16_t)(address + offset));
bios_puts(": ");
for (column = 0; column < row_length; ++column) {
print_hex8(*(volatile uint8_t *)(address + offset + column));
bios_putc(' ');
}
bios_puts("\r\n");
offset += row_length;
}
}
static void command_checksum(uint8_t argc, char **argv)
{
uint16_t address;
uint16_t end;
uint16_t checksum = 0;
if (argc != 3 || !parse_hex(argv[1], &address) ||
!parse_hex(argv[2], &end) || end < address) {
bios_puts("Usage: C start end\r\n");
return;
}
for (;;) {
checksum = (uint16_t)(checksum + *(volatile uint8_t *)address);
if (address == end) break;
++address;
}
print_hex16(checksum);
bios_puts("\r\n");
}
static void command_write(uint8_t argc, char **argv)
{
uint16_t address;
uint16_t value;
uint8_t i;
if (argc < 3 || !parse_hex(argv[1], &address)) {
bios_puts("Usage: W address byte [byte ...]\r\n");
return;
}
for (i = 2; i < argc; ++i) {
if (!parse_hex(argv[i], &value) || value > 0xff) {
bios_puts("Bad byte: ");
bios_puts(argv[i]);
bios_puts("\r\n");
return;
}
*(volatile uint8_t *)address++ = (uint8_t)value;
}
bios_puts("OK\r\n");
}
static void command_in(uint8_t argc, char **argv)
{
uint16_t port;
if (argc != 2 || !parse_hex(argv[1], &port) || port > 0xff) {
bios_puts("Usage: I port\r\n");
return;
}
print_hex8((uint8_t)port);
bios_puts(": ");
print_hex8(io_in((uint8_t)port));
bios_puts("\r\n");
}
static void command_out(uint8_t argc, char **argv)
{
uint16_t port;
uint16_t value;
if (argc != 3 || !parse_hex(argv[1], &port) || port > 0xff ||
!parse_hex(argv[2], &value) || value > 0xff) {
bios_puts("Usage: O port byte\r\n");
return;
}
io_out((uint8_t)port, (uint8_t)value);
bios_puts("OK\r\n");
}
static void execute_address(uint16_t address)
{
bios_puts("Executing at ");
print_hex16(address);
bios_puts("...\r\n");
bios_flush();
jump_to(address);
bios_puts("Returned to monitor\r\n");
}
static void command_go(uint8_t argc, char **argv)
{
uint16_t address;
if (argc != 2 || !parse_hex(argv[1], &address)) {
bios_puts("Usage: G address\r\n");
return;
}
execute_address(address);
}
static void command_basic(uint8_t argc)
{
if (argc != 1) {
bios_puts("Usage: B\r\n");
return;
}
execute_address(0x1000);
}
static void execute_line(char *line)
{
char *argv[MAX_ARGS];
uint8_t argc = split_line(line, argv);
if (!argc) return;
if (strings_equal(argv[0], "h") || strings_equal(argv[0], "help") ||
strings_equal(argv[0], "?")) {
print_help();
} else if (strings_equal(argv[0], "d") || strings_equal(argv[0], "dump")) {
command_dump(argc, argv);
} else if (strings_equal(argv[0], "c") || strings_equal(argv[0], "checksum")) {
command_checksum(argc, argv);
} else if (strings_equal(argv[0], "w") || strings_equal(argv[0], "write")) {
command_write(argc, argv);
} else if (strings_equal(argv[0], "i") || strings_equal(argv[0], "in")) {
command_in(argc, argv);
} else if (strings_equal(argv[0], "o") || strings_equal(argv[0], "out")) {
command_out(argc, argv);
} else if (strings_equal(argv[0], "g") || strings_equal(argv[0], "go")) {
command_go(argc, argv);
} else if (strings_equal(argv[0], "b") || strings_equal(argv[0], "basic")) {
command_basic(argc);
} else {
bios_puts("Unknown command; type HELP\r\n");
}
}
void main(void)
{
char line[LINE_SIZE];
uint8_t length = 0;
uint8_t ignore_lf = 0;
bios_puts("\r\nZ80 monitor - v1\r\nType HELP for commands.\r\n> ");
for (;;) {
char c = bios_getc();
if (c == '\n' && ignore_lf) {
ignore_lf = 0;
continue;
}
ignore_lf = 0;
if (c == '\r' || c == '\n') {
ignore_lf = (c == '\r');
line[length] = 0;
bios_puts("\r\n");
execute_line(line);
length = 0;
bios_puts("> ");
} else if (c == '\b' || c == 0x7f) {
if (length) {
--length;
bios_puts("\b \b");
}
} else if (c == 0x15) {
while (length) {
--length;
bios_puts("\b \b");
}
} else if (c >= ' ' && c <= '~' && length < LINE_SIZE - 1) {
line[length++] = c;
bios_putc(c);
} else if (c >= ' ' && c <= '~') {
bios_putc('\a');
}
}
}