/* * mii_mish.c * * Copyright (C) 2023 Michel Pollet * * SPDX-License-Identifier: MIT */ #include #include #include #include #include #include #include "mii.h" #include "mii_bank.h" #include "mii_sw.h" #include "mii_65c02_ops.h" #include "mii_65c02_disasm.h" void _mii_mish_text( void * param, int argc, const char * argv[]) { // load 0x400, calculate the 24 line addresses from the code in video // and show the 40 or 80 chars, depending on col80 mii_t * mii = param; uint16_t a = 0x400; int page2 = mii_read_one(mii, SWPAGE2); // int col80 = mii_read_one(mii, SW80COL); for (int li = 0; li < 24; li++) { int i = li; a = (0x400 + (0x400 * page2)); a += ((i & 0x07) << 7) | ((i >> 3) << 5) | ((i >> 3) << 3); printf("%04x: ", a); for (int ci = 0; ci < 40; ci++) { uint8_t c = (mii_read_one(mii, a++) & 0x3f); printf("%c", c >= 0x20 && c < 0x7f ? c : '.'); } printf("\n"); } } static void _mii_mish_cmd( void * param, int argc, const char * argv[]) { const char * state[] = { "RUNNING", "STOPPED", "STEP" }; mii_t * mii = param; if (!argv[1]) { show_state: printf("mii: %s Target speed: %.3fMHz Current: %.3fMHz\n", state[mii->state], mii->speed, mii->speed_current); mii_dump_run_trace(mii); mii_dump_trace_state(mii); mii_bank_t * main = &mii->bank[MII_BANK_MAIN]; bool text = !!mii_bank_peek(main, SWTEXT); bool page2 = !!mii_bank_peek(main, SWPAGE2); bool col80 = !!mii_bank_peek(main, SW80COL); bool mixed = !!mii_bank_peek(main, SWMIXED); bool hires = !!mii_bank_peek(main, SWHIRES); bool dhires = !!mii_bank_peek(main, SWRDDHIRES); printf("text:%d page2:%d col80:%d mixed:%d hires:%d dhires:%d\n", text, page2, col80, mixed, hires, dhires); return; } if (!strcmp(argv[1], "reset")) { mii_reset(mii, 0); return; } if (!strcmp(argv[1], "mem")) { for (int i = 0; i < 16; i++) { printf("%02x: ", i * 16); for (int j = 0; j < 16; j++) printf("%d:%d ", mii->mem[(i * 16) + j].read, mii->mem[(i * 16) + j].write); printf("\n"); } return; } if (!strcmp(argv[1], "trace")) { mii->trace_cpu = !mii->trace_cpu; printf("trace_cpu %d\n", mii->trace_cpu); return; } if (!strcmp(argv[1], "poke")) { if (argc < 4) { printf("poke: missing argument\n"); return; } uint16_t addr = strtol(argv[2], NULL, 16); uint8_t val = strtol(argv[3], NULL, 16); mii_mem_access(mii, addr, &val, false, true); return; } if (!strcmp(argv[1], "peek")) { if (argc < 3) { printf("peek: missing argument\n"); return; } uint16_t addr = strtol(argv[2], NULL, 16); uint8_t val; mii_mem_access(mii, addr, &val, false, false); printf("%04x: %02x\n", addr, val); return; } if (!strcmp(argv[1], "speed")) { if (argc < 3) { printf("speed: missing argument\n"); return; } float speed = atof(argv[2]); if (speed < 0.1 || speed > 30.0) { printf("speed: speed must be between 0.0 and 30.0\n"); return; } mii->speed = speed; printf("speed: %.3fMHz\n", speed); return; } if (!strcmp(argv[1], "stop")) { mii->state = MII_STOPPED; goto show_state; } if (!strcmp(argv[1], "quit") || !strcmp(argv[1], "exit")) { mii->state = MII_TERMINATE; printf("mii: terminating\n"); return; } printf("mii: unknown command %s\n", argv[1]); } void _mii_mish_bp( void * param, int argc, const char * argv[]) { mii_t * mii = param; if (!argv[1] || !strcmp(argv[1], "list")) { printf("breakpoints: map %04x\n", mii->debug.bp_map); for (int i = 0; i < (int)sizeof(mii->debug.bp_map)*8; i++) { printf("%2d %c %04x %c%c%c size:%2d\n", i, (mii->debug.bp_map & (1 << i)) ? '*' : ' ', mii->debug.bp[i].addr, (mii->debug.bp[i].kind & MII_BP_R) ? 'r' : '-', (mii->debug.bp[i].kind & MII_BP_W) ? 'w' : '-', (mii->debug.bp[i].kind & MII_BP_STICKY) ? 's' : '-', mii->debug.bp[i].size); } return; } const char *p = argv[1]; if (p[0] == '+') { p++; int addr = strtol(p, NULL, 16); int kind = 0; if (strchr(p, 'r')) kind |= MII_BP_R; if (strchr(p, 'w')) kind |= MII_BP_W; if (strchr(p, 's')) kind |= MII_BP_STICKY; if (!kind || kind == MII_BP_STICKY) kind |= MII_BP_R; int size = 1; if (argv[2]) size = strtol(argv[2], NULL, 16); if (!size) size++; for (int i = 0; i < (int)sizeof(mii->debug.bp_map)*8; i++) { if (!(mii->debug.bp_map & (1 << i)) || mii->debug.bp[i].addr == addr) { mii->debug.bp_map |= 1 << i; mii->debug.bp[i].addr = addr; mii->debug.bp[i].kind = kind; mii->debug.bp[i].size = size; printf("breakpoint %d set at %04x size %d\n", i, addr, size); break; } } return; } if (p[0] == '-') { p++; int idx = strtol(p, NULL, 10); if (idx >= 0 && idx < 7) { mii->debug.bp_map &= ~(1 << idx); printf("breakpoint %d cleared\n", idx); } } } static void _mii_mish_il( void * param, int argc, const char * argv[]) { static uint16_t addr = 0x800; if (argv[1]) { addr = strtol(argv[1], NULL, 16); if (addr >= 0xffff) addr = 0xfff0; } mii_t * mii = param; for (int li = 0; li < 20; li++) { uint8_t op[16]; for (int bi = 0; bi < 4; bi++) mii_mem_access(mii, addr + bi, op + bi, false, false); char dis[64]; addr += mii_cpu_disasm_one(op, addr, dis, sizeof(dis), MII_DUMP_DIS_PC | MII_DUMP_DIS_DUMP_HEX); printf("%s\n", dis); } } static void _mii_mish_dm( void * param, int argc, const char * argv[]) { static uint16_t addr = 0x800; if (argv[1]) { addr = strtol(argv[1], NULL, 16); if (addr >= 0xffff) addr = 0xfff0; } mii_t * mii = param; if (!strcmp(argv[0], "dm")) { printf("dm: %04x\n", addr); for (int i = 0; i < 8; i++) { printf("%04x: ", addr); for (int j = 0; j < 16; j++) printf("%02x ", mii_read_one(mii, addr++)); printf("\n"); } return; } if (!strcmp(argv[0], "db")) { printf("%s: %04x: ", argv[0], addr); printf("%02x ", mii_read_one(mii, addr++)); printf("\n"); return; } if (!strcmp(argv[0], "dw") || !strcmp(argv[0], "da")) { printf("%s: %04x: ", argv[0], addr); uint8_t l = mii_read_one(mii, addr++); uint8_t h = mii_read_one(mii, addr++); printf("%02x%02x", h, l); printf("\n"); return; } } static void _mii_mish_step( void * param, int argc, const char * argv[]) { if (argv[0][0] == 's') { mii_t * mii = param; if (argv[1]) { int n = strtol(argv[1], NULL, 10); mii->trace.step_inst = n; } else mii->trace.step_inst = 1; mii->state = MII_STEP; return; } if (argv[0][0] == 'n') { mii_t * mii = param; // read current opcode, find how how many bytes it take, // then put a temporary breakpoint to the next PC. // all of that if this is not a relative branch of course, in // which case we use a normal 'step' behaviour uint8_t op; mii_mem_access(mii, mii->cpu.PC, &op, false, false); if (op == 0x20) { // set a temp breakpoint on reading 3 bytes from PC for (int i = 0; i < (int)sizeof(mii->debug.bp_map) * 8; i++) { if ((mii->debug.bp_map & (1 << i))) continue; mii->debug.bp[i].addr = mii->cpu.PC + 3; mii->debug.bp[i].kind = MII_BP_R; mii->debug.bp[i].size = 1; mii->debug.bp[i].silent = 1; mii->debug.bp_map |= 1 << i; mii->state = MII_RUNNING; return; } printf("no more breakpoints available\n"); } else { mii->trace.step_inst = 1; mii->state = MII_STEP; } return; } if (argv[0][0] == 'c') { mii_t * mii = param; mii->trace.step_inst = 0; mii->state = MII_RUNNING; return; } if (argv[0][0] == 'h') { mii_t * mii = param; mii->trace.step_inst = 0; mii->state = MII_STOPPED; return; } } #include static void _mii_mish_audio( void * param, int argc, const char * argv[]) { mii_t * mii = param; if (argc < 2) { printf("audio: missing argument\n"); return; } if (!strcmp(argv[1], "record")) { if (mii->speaker.debug_fd != -1) { close(mii->speaker.debug_fd); mii->speaker.debug_fd = -1; printf("audio: stop recording\n"); } else { mii->speaker.debug_fd = open("audio.raw", O_WRONLY | O_CREAT | O_TRUNC, 0644); printf("audio: start recording\n"); } } else if (!strcmp(argv[1], "mute")) { if (argv[2] && !strcmp(argv[2], "off")) mii->speaker.muted = false; else if (argv[2] && !strcmp(argv[2], "on")) mii->speaker.muted = true; else if (!argv[2] || (argv[2] && !strcmp(argv[2], "toggle"))) mii->speaker.muted = !mii->speaker.muted; printf("audio: %s\n", mii->speaker.muted ? "muted" : "unmuted"); } else if (!strcmp(argv[1], "volume")) { if (argc < 3) { printf("audio: missing volume\n"); return; } // convert a linear volume from 0 to 10 into a float from 0.0 to 1.0 float vol = atof(argv[2]); if (vol < 0) vol = 0; else if (vol > 10) vol = 10; mii_speaker_volume(&mii->speaker, vol); printf("audio: volume %.3f (amp: %.4f)\n", vol, mii->speaker.vol_multiplier); } else { printf("audio: unknown command %s\n", argv[1]); } } #include "mish.h" MISH_CMD_NAMES(mii, "mii"); MISH_CMD_HELP(mii, "mii: access internals, trace, reset, speed, etc", " : dump current state", " reset : reset the cpu", " t|trace : toggle trace_cpu (WARNING HUGE traces!))", " mem : dump memory and bank map", " poke : poke a value in memory (respect SW)", " peek : peek a value in memory (respect SW)", " speed : set speed in MHz", " stop : stop the cpu", " quit|exit : quit the emulator" ); MII_MISH(mii, _mii_mish_cmd); MISH_CMD_NAMES(bp, "bp"); MISH_CMD_HELP(bp, "mii: breakpoints. 'sticky' means the breakpoint is re-armed after hit", " : dump state", " +[r|w][s] [size]: add at for read/write, sticky", " - : disable (don't clear) breakpoint " ); MII_MISH(bp, _mii_mish_bp); MISH_CMD_NAMES(il, "il"); MISH_CMD_HELP(il, "mii: disassembly", " : list next 20 instructions.", " [addr]: start at address addr" ); MII_MISH(il, _mii_mish_il); MISH_CMD_NAMES(dm, "dm","db","dw","da"); MISH_CMD_HELP(dm, "mii: dump memory, byte, word, address", " |dm : dump 64 bytes.", " db []: dump one byte.", " dw []: dump one word.", " da []: dump one address.", " [addr]: start at address addr" ); MII_MISH(dm, _mii_mish_dm); MISH_CMD_NAMES(step, "s","step","n","next","cont","h","halt"); MISH_CMD_HELP(step, "mii: step instructions", " s|step [num]: step [num, or one] instruction.", " n|next : step one instruction, skip subroutines.", " cont : continue execution." ); MII_MISH(step, _mii_mish_step); MISH_CMD_NAMES(text, "text"); MISH_CMD_HELP(text, "mii: show text page [buggy]", " : that's it" ); MII_MISH(text, _mii_mish_text); MISH_CMD_NAMES(audio, "audio"); MISH_CMD_HELP(audio, "audio: audio control/debug", " record: record/stop debug file.", " mute: mute/unmute audio.", " volume: set volume (0.0 to 1.0)." ); MII_MISH(audio, _mii_mish_audio);