#include #ifndef WOZMON #define WOZMON 0 #endif #if WOZMON #define BACKSPACE 0 #define ASCIIDUMP 0 #define CLEARSCREENCMD 0 #define FARCALL 0 #define SHOWTITLE 0 #define BOOTSTRAP 0 #endif // WOZMON #ifndef BACKSPACE #define BACKSPACE 1 #endif #ifndef ASCIIDUMP #define ASCIIDUMP 1 #endif #ifndef CLEARSCREENCMD #define CLEARSCREENCMD 1 #endif #ifndef FARCALL #define FARCALL 1 #endif #ifndef SHOWTITLE #define SHOWTITLE 1 #endif #ifndef BOOTSTRAP #define BOOTSTRAP 1 #endif namespace { constexpr int kDumpSize = 16; uint8_t getc() { register uint8_t c asm ("al"); asm volatile ( "movb $0x00, %%ah\n\t" "int $0x16" : "=r" (c) :: "ah", "cc" ); return c; } void putc(uint8_t c) { asm volatile ( "movb %0, %%al \n\t" "movb $0x0e, %%ah \n\t" "movb $0, %%bh \n\t" "int $0x10 \n\t" :: "r" (c) : "ax", "bh", "cc", "bp" ); } void puts(const char* s) { while (*s) { putc(*s++); } } // arguments on the stack in reverse order extern "C" uint16_t Jump(uint16_t addr, int nargs); asm ( ".section .text \n" "Jump: \n" " push %bx \n" " push %si \n" " push %di \n" " push %bp \n" " push %es \n" " push %ax \n" // addr " movw %sp, %bp \n" " movw %dx, %si \n" // nargs " add %si, %si \n" "j0: \n" " test %si, %si \n" " jz j1 \n" " lea 12(%bp,%si), %di\n" " push %di \n" " sub $2, %si \n" " jmp j0 \n" "j1: \n" " lcall *(%bp) \n" " add $4, %sp \n" " pop %bp \n" " pop %di \n" " pop %si \n" " pop %bx \n" " ret" // return value in ax ); constexpr uint8_t kBackspace = 0x7f; constexpr uint8_t kOtherBackspace = 0x08; uint8_t ReadHexNibble(uint8_t c) { // lowercase only if (c <= '9') { return c - '0'; } return 10 + (c - 'a'); } uint16_t ReadUintN(int n, const char* buf) { uint16_t out = 0; for (int i = 0; i < n; i++) { if (buf[i] == 0) { break; } out <<= 4; out += ReadHexNibble(buf[i]); } return out; } uint8_t ReadUint8(const char* buf) { return ReadUintN(2, buf); } uint16_t ReadUint16(const char* buf) { return ReadUintN(4, buf); } void WriteHexNibble(uint8_t c) { if (c > 9) { putc('a' + c - 10); } else { putc('0' + c); } } void WriteUint8(uint8_t a) { WriteHexNibble(a >> 4); WriteHexNibble(a & 0xf); } void WriteUint16(uint16_t a) { WriteUint8(a >> 8); WriteUint8(a & 0xff); } void __set_es__(uint16_t es) { asm volatile ( "mov %0, %%es" :: "r" (es) : "es" ); } uint8_t __get_es_u8__(uint16_t addr) { register uint16_t ad asm ("di") = addr; uint8_t ret; asm volatile ( "movb %%es:(%1), %0" : "=r" (ret) : "r" (ad) ); return ret; } void __set_es_u8__(uint16_t addr, uint8_t val) { register uint16_t ad asm ("di") = addr; asm volatile ( "movb %0, %%es:(%1)" :: "ri" (val), "rmi" (ad) : "memory" ); } __attribute__((noreturn)) inline static void __basic__() { asm volatile ( "movw $0x40, %ax \n\t" "mov %ax, %ds \n\t" "int $0x18" ); __builtin_unreachable(); } void Dump(uint16_t addr, int count) { for (int i = 0; i < count; i++) { putc(' '); uint8_t d = __get_es_u8__(addr + i); WriteUint8(d); } #if ASCIIDUMP putc(' '); putc(' '); for (int i = 0; i < count; i++) { uint8_t d = __get_es_u8__(addr + i); if (d > 31 && d < 127) { putc(d); } else { putc('.'); } } #endif } void DumpHex(uint16_t addr, uint16_t seg) { addr &= -kDumpSize; putc('['); WriteUint16(seg); putc(':'); __set_es__(seg); WriteUint16(addr); putc(']'); putc(':'); Dump(addr, kDumpSize); putc('\r'); putc('\n'); } void ClearScreen() { asm volatile ( "movw $0x0002, %%ax \n\t" "int $0x10" ::: "ax", "cc" ); } void Status(uint8_t status) { asm volatile ( "xorb %%bh, %%bh \n\t" "movb $0x03, %%ah \n\t" "int $0x10 \n\t" "mov %%dx, %%di \n\t" "movb $0x02, %%ah \n\t" "movw $77, %%dx \n\t" "int $0x10 \n\t" "movb %0, %%al \n\t" "call _ZN12_GLOBAL__N_110WriteUint8Eh \n\t" "movb $0x02, %%ah \n\t" "movw %%di, %%dx \n\t" "int $0x10" :: "rm" (status) : "ax", "bh", "dx", "cx", "di", "cc" ); } bool ParseCommand(const char* buf, uint16_t& cur_addr, uint16_t& cur_seg) { bool dump = true; for (const char* ptr = buf; *ptr;) { if (*ptr == 's') { cur_addr = 0; cur_seg = ReadUint16(ptr+1); ptr += 5; } else if (*ptr == '$') { __basic__(); } else if (*ptr == 'r') { dump = false; #if FARCALL int nargs = 0; ptr++; for (; *ptr;) { if (*ptr == ' ') { ptr++; continue; } uint16_t d = ReadUint16(ptr); asm volatile ("push %0" :: "r" (d)); nargs++; ptr += 4; } __set_es__(cur_seg); uint16_t ret = Jump(cur_addr, nargs); asm volatile ( "shl %0 \n\t" "add %0, %%sp" :: "r"(nargs) ); WriteUint16(ret); putc('\r'); putc('\n'); #else // FARCALL auto jump = reinterpret_cast(cur_addr); jump(); #endif // FARCALL ptr++; #if CLEARSCREENCMD } else if (*ptr == 'l') { dump = false; ClearScreen(); ptr++; #endif } else if (*ptr == 'w') { dump = false; uint16_t addr = cur_addr; ptr += 1; for (; *ptr;) { if (*ptr == ' ') { ptr++; continue; } uint8_t d = ReadUint8(ptr); __set_es__(cur_seg); __set_es_u8__(addr, d); addr++; ptr += 2; } } else { cur_addr = ReadUint16(ptr); ptr += 4; } } return dump; } const char* prompt = "> "; void polmon() { uint16_t cur_addr = 0; uint16_t cur_seg = 0; char inbuf[64]; bool first = true; char* inptr = inbuf; ClearScreen(); #if SHOWTITLE puts("PolMon 0.2\r\n"); #endif // SHOWTITLE puts(prompt); while (1) { uint8_t c = getc(); putc(c); // echo if (c == '\r') { *inptr = 0; if (inbuf[0] == 0) { if (!first) { cur_addr += kDumpSize; } } else { putc('\n'); } if (ParseCommand(inbuf, cur_addr, cur_seg)) { DumpHex(cur_addr, cur_seg); }; first = false; inptr = inbuf; puts(prompt); #if BACKSPACE } else if (c == kBackspace || c == kOtherBackspace) { inptr--; if (inptr < inbuf) { inptr = inbuf; putc(' '); continue; } putc(' '); putc(kOtherBackspace); #endif } else { *inptr = c; inptr++; } } } } // namespace int main() { polmon(); } #if BOOTSTRAP __attribute__((section(".init"), noreturn, used)) void _start() { main(); __builtin_unreachable(); } #endif // BOOTSTRAP