mii_emu/src/mii_mish.c

613 lines
14 KiB
C

/*
* mii_mish.c
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <unistd.h>
#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[] = {
"INIT", "RUNNING", "STOPPED", "STEP", "TERMINATE"};
mii_t * mii = param;
if (!argv[1]) {
show_state:
printf("mii: %s Target speed: %.3fMHz\n",
state[mii->state], mii->speed);
mii_dump_run_trace(mii);
mii_dump_trace_state(mii);
for (int i = 0; mii_sw_names[i]; i++) {
char buf[32];
sprintf(buf, "%s:%d", mii_sw_names[i],
!!(mii->sw_state & (1 << i)));
printf("%-13.13s%s", buf, !((i+1) % 6) ? "\n" : " ");
}
printf("\n");
printf("RGB Video Mode: %d\n", mii->video.color_mode);
return;
}
if (!strcmp(argv[1], "reset")) {
mii_reset(mii, 0);
return;
}
if (!strcmp(argv[1], "rgb")) {
if (argc < 3) {
printf("rgb: missing argument\n");
return;
}
uint8_t val = strtol(argv[2], NULL, 16);
mii_video_set_mode(mii, val);
return;
}
if (!strcmp(argv[1], "mem")) {
printf("mii: memory map:\n");
for (int i = 0; i < MII_BANK_COUNT; i++)
printf("%0d:%s ", i, mii->bank[i].name);
printf("\n");
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], "analog")) {
printf("analog: %3d %3d %3d %3d\n", mii->analog.v[0].value,
mii->analog.v[1].value, mii->analog.v[2].value,
mii->analog.v[3].value);
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, true, 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, true);
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;
}
if (!strcmp(argv[1], "timers")) {
uint64_t timer = mii->timer.map;
printf("mii: %d cycle timers\n", __builtin_popcountll(timer));
while (timer) {
int i = ffsll(timer) - 1;
timer &= ~(1ull << i);
printf("%2d: %8ld %s\n", i, mii->timer.timers[i].when,
mii->timer.timers[i].name);
}
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_mm(
void * param,
int argc,
const char * argv[])
{
static uint32_t addr = 0x800;
if (argv[1]) {
addr = strtol(argv[1], NULL, 16);
if (addr > 0x00100000)
addr = 0x00fff0;
}
mii_t * mii = param;
int bi = (addr >> 16) & 0xf;
if (bi > MII_BANK_COUNT) {
printf("mii: invalid bank index %d (0:7)\n", bi);
return;
}
addr &= 0xffff;
mii_bank_t * bank = &mii->bank[bi];
// addr -= bank->base;
if (!strcmp(argv[0], "mm")) {
printf("%s %04x\n", bank->name, addr);
for (int i = 0; i < 8; i++) {
printf("%06x: ", addr);
for (int j = 0; j < 16; j++)
printf("%02x ", mii_bank_peek(bank, addr++));
printf("\n");
}
return;
}
if (!strcmp(argv[0], "mb")) {
printf("%s: %04x: ", bank->name, addr);
printf("%02x ", mii_bank_peek(bank, addr++));
printf("\n");
return;
}
if (!strcmp(argv[0], "mw") || !strcmp(argv[0], "ma")) {
printf("%s: %04x: ", bank->name, addr);
uint8_t l = mii_bank_peek(bank, addr++);
uint8_t h = mii_bank_peek(bank, addr++);
printf("%02x%02x", h, l);
printf("\n");
return;
}
}
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_sr(
void * param,
int argc,
const char * argv[])
{
// set register
mii_t * mii = param;
if (argc < 3) {
printf("sr: missing argument <A,X,Y,S,PC,P> <Value>)\n");
return;
}
uint16_t val = strtol(argv[2], NULL, 16);
if (!strcasecmp(argv[1], "A"))
mii->cpu.A = val;
else if (!strcasecmp(argv[1], "X"))
mii->cpu.X = val;
else if (!strcasecmp(argv[1], "Y"))
mii->cpu.Y = val;
else if (!strcasecmp(argv[1], "S"))
mii->cpu.S = val;
else if (!strcasecmp(argv[1], "PC"))
mii->cpu.PC = val;
else if (!strcasecmp(argv[1], "P")) {
MII_SET_P(&mii->cpu, val);
} else {
printf("sr: unknown register %s\n", argv[1]);
return;
}
mii_dump_trace_state(mii);
}
static void
_mii_mish_step(
void * param,
int argc,
const char * argv[])
{
if (argv[0][0] == 's') {
mii_t * mii = param;
if (mii->state != MII_STOPPED) {
printf("mii: can't step/next, not stopped\n");
return;
}
int count = 1;
if (argv[1])
count = strtol(argv[1], NULL, 10);
mii_cpu_step(mii, count);
return;
}
if (argv[0][0] == 'n') {
mii_t * mii = param;
mii_cpu_next(mii);
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 <math.h>
static void
_mii_mish_audio(
void * param,
int argc,
const char * argv[])
{
mii_t * mii = param;
if (argc < 2) {
printf("audio volume: %.3f multiplier:%.3f muted:%d\n",
mii->speaker.volume, mii->speaker.vol_multiplier,
mii->speaker.muted);
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]);
}
}
static void
_mii_mish_bsave(
void * param,
int argc,
const char * argv[])
{
mii_t * mii = param;
if (argc < 4) {
printf("bsave: missing argument <file> <addr> <size>\n");
return;
}
const char * file = argv[1];
uint16_t addr = strtol(argv[2], NULL, 16);
if (!strcmp(argv[0], "bsave")) {
uint16_t size = strtol(argv[3], NULL, 16);
if (addr + size > 0x10000) {
printf("bsave: size too big\n");
return;
}
FILE * f = fopen(file, "wb");
if (!f) {
printf("bsave: can't open %s\n", file);
return;
}
mii_bank_t * bank = &mii->bank[MII_BANK_MAIN];
fwrite(bank->mem + addr, size, 1, f);
fclose(f);
printf("bsave: %s saved %d bytes at %04x\n", file, size, addr);
} else if (!strcmp(argv[0], "bload")) {
FILE * f = fopen(file, "rb");
if (!f) {
printf("bsave: can't open %s\n", file);
return;
}
fseek(f, 0, SEEK_END);
uint16_t size = ftell(f);
fseek(f, 0, SEEK_SET);
if (addr + size > 0x10000) {
printf("bsave: size too big\n");
return;
}
mii_bank_t * bank = &mii->bank[MII_BANK_MAIN];
fread(bank->mem + addr, size, 1, f);
fclose(f);
printf("bsave: %s loaded %d bytes at %04x\n", file, size, addr);
} else {
printf("%s: unknown command\n", argv[0]);
return;
}
}
#include "mish.h"
MISH_CMD_NAMES(mii, "mii");
MISH_CMD_HELP(mii,
"mii: access internals, trace, reset, speed, etc",
" <default> : dump current state",
" rgb <val>: Set RGB mode to <val>(0:color,1:green,2:amber)",
" reset : reset the cpu",
" t|trace : toggle trace_cpu (WARNING HUGE traces!))",
" mem : dump memory and bank map",
" poke <addr> <val> : poke a value in memory (respect SW)",
" peek <addr> : peek a value in memory (respect SW)",
" speed <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",
" <default> : dump state",
" +<addr>[r|w][s] [size]: add at <addr> for read/write, sticky",
" -<index> : disable (don't clear) breakpoint <index>"
);
MII_MISH(bp, _mii_mish_bp);
MISH_CMD_NAMES(il, "il");
MISH_CMD_HELP(il,
"mii: disassembly",
" <default> : 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",
" <default>|dm <addr>: dump 64 bytes.",
" db [<addr>]: dump one byte.",
" dw [<addr>]: dump one word.",
" da [<addr>]: dump one address.",
" [addr]: start at address addr"
);
MII_MISH(dm, _mii_mish_dm);
MISH_CMD_NAMES(mm, "mm","mb","mw","ma");
MISH_CMD_HELP(mm,
"mii: dump memory, byte, word, [raw address]",
" <default>|mm <addr>: dump 64 bytes.",
" mb [<addr>]: dump one byte.",
" mw [<addr>]: dump one word.",
" ma [<addr>]: dump one address.",
" [addr]: 24 bits address addr 00 main, 01 aux"
);
MII_MISH(mm, _mii_mish_mm);
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(sr, "sr");
MISH_CMD_HELP(sr,
"mii: set register",
" <reg> <val>: set register to value",
" <reg>: A, X, Y, S, PC, P"
);
MII_MISH(sr, _mii_mish_sr);
MISH_CMD_NAMES(text, "text");
MISH_CMD_HELP(text,
"mii: show text page [buggy]",
" <default> : 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);
MISH_CMD_NAMES(bsave, "bsave","bload");
MISH_CMD_HELP(bsave,
"bsave/bload: save/load binary data from ram",
" bsave <file> <addr> <size>: save binary data to file.",
" bload <file> <addr>: load binary data from file."
);
MII_MISH(bsave, _mii_mish_bsave);