#include #include "boot.h" #include "circ_buf.h" #define UART_CTC_CARD_BASE 0x20 #define CTC_BASE (UART_CTC_CARD_BASE + 0x00) #define CTC_CHANNEL_0 (CTC_BASE + 0) #define CTC_CHANNEL_3 (CTC_BASE + 3) #define UART_BASE (UART_CTC_CARD_BASE + 0x04) #define UART_CONTROL (UART_BASE + 0) #define UART_DATA (UART_BASE + 1) #define LINE_SIZE 80 #define MAX_ARGS 40 #define DEFAULT_DUMP_LENGTH 0x40 #define MAX_DUMP_LENGTH 0x100 volatile uint8_t uart_tx_busy; volatile circular_buffer_t rx_buf; volatile circular_buffer_t tx_buf; static void uart_kick(void) { char c; disable_interrupts(); if (!uart_tx_busy && buffer_dequeue(&tx_buf, &c)) { uart_tx_busy = 1; io_out(UART_DATA, c); io_out(UART_CONTROL, 0b10110101); } enable_interrupts(); } static void uart_putc(char c) { uint8_t queued; do { disable_interrupts(); queued = buffer_enqueue(&tx_buf, c); enable_interrupts(); if (!queued) uart_kick(); } while (!queued); uart_kick(); } /* Stable monitor ABI implementations, reached through the jump table at C050. */ void monitor_uart_putc(char c) { uart_putc(c); } uint8_t monitor_uart_getc(void) { char c; uint8_t received; disable_interrupts(); received = buffer_dequeue(&rx_buf, &c); enable_interrupts(); return received ? (uint8_t)c : 0; } static void uart_puts(const char *s) { while (*s) uart_putc(*s++); } static char uart_getc(void) { char c; uint8_t received; do { disable_interrupts(); received = buffer_dequeue(&rx_buf, &c); enable_interrupts(); } while (!received); return c; } static void uart_flush(void) { while (uart_tx_busy || !buffer_is_empty(&tx_buf)) uart_kick(); } void uart_interrupt(void) { uint8_t status = io_in(UART_CONTROL); if ((status & 0x21) != 0) { char c = io_in(UART_DATA); if ((status & 0x01) != 0) buffer_enqueue(&rx_buf, c); } if ((status & 0x02) != 0 && uart_tx_busy) { char c; if (buffer_dequeue(&tx_buf, &c)) { io_out(UART_DATA, c); } else { io_out(UART_CONTROL, 0b10010101); uart_tx_busy = 0; } } } void ctc_interrupt(void) { } 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) { uart_putc(hex_digit(value >> 4)); uart_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) { uart_puts( "Commands (hexadecimal numbers):\r\n" " D|DUMP address [length] Dump memory (default 0040, max 0100)\r\n" " W|WRITE address byte ... Write one or more memory bytes\r\n" " I|IN port Read an I/O port\r\n" " O|OUT port byte Write an I/O port\r\n" " G|GO address Execute code; RET returns here\r\n" " B|BASIC Start Tiny BASIC at D000\r\n" " H|HELP|? Show this 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))) { uart_puts("Usage: D address [length]\r\n"); return; } if (length > MAX_DUMP_LENGTH) { uart_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)); uart_puts(": "); for (column = 0; column < row_length; ++column) { print_hex8(*(volatile uint8_t *)(address + offset + column)); uart_putc(' '); } uart_puts("\r\n"); offset += row_length; } } 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)) { uart_puts("Usage: W address byte [byte ...]\r\n"); return; } for (i = 2; i < argc; ++i) { if (!parse_hex(argv[i], &value) || value > 0xff) { uart_puts("Bad byte: "); uart_puts(argv[i]); uart_puts("\r\n"); return; } *(volatile uint8_t *)address++ = (uint8_t)value; } uart_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) { uart_puts("Usage: I port\r\n"); return; } print_hex8((uint8_t)port); uart_puts(": "); print_hex8(io_in((uint8_t)port)); uart_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) { uart_puts("Usage: O port byte\r\n"); return; } io_out((uint8_t)port, (uint8_t)value); uart_puts("OK\r\n"); } static void execute_address(uint16_t address) { uart_puts("Executing at "); print_hex16(address); uart_puts("...\r\n"); uart_flush(); jump_to(address); uart_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)) { uart_puts("Usage: G address\r\n"); return; } execute_address(address); } static void command_basic(uint8_t argc) { if (argc != 1) { uart_puts("Usage: B\r\n"); return; } execute_address(0xd000); } 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], "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 { uart_puts("Unknown command; type HELP\r\n"); } } void main(void) { char line[LINE_SIZE]; uint8_t length = 0; uint8_t ignore_lf = 0; buffer_init(&rx_buf); buffer_init(&tx_buf); io_out(CTC_CHANNEL_0, 0x57); io_out(CTC_CHANNEL_0, 13); io_out(CTC_CHANNEL_3, 0b11001111); io_out(CTC_CHANNEL_3, 1); io_out(CTC_CHANNEL_0, 0x00); io_out(CTC_CHANNEL_0, 0x40); uart_tx_busy = 0; io_out(UART_CONTROL, 0b00000011); io_out(UART_CONTROL, 0b10010101); enable_interrupts(); uart_puts("\r\nZ80 monitor - v1\r\nType HELP for commands.\r\n> "); for (;;) { char c = uart_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; uart_puts("\r\n"); execute_line(line); length = 0; uart_puts("> "); } else if (c == '\b' || c == 0x7f) { if (length) { --length; uart_puts("\b \b"); } } else if (c == 0x15) { while (length) { --length; uart_puts("\b \b"); } } else if (c >= ' ' && c <= '~' && length < LINE_SIZE - 1) { line[length++] = c; uart_putc(c); } else if (c >= ' ' && c <= '~') { uart_putc('\a'); } } }