mirror of
https://github.com/buserror/mii_emu.git
synced 2024-11-22 16:33:01 +00:00
d53b0bceff
It is limited for now, hard coded to mine. Still need some sort of way to specify which button to map to the emulated joytick. Same with the axis, currently default to 0,1 Signed-off-by: Michel Pollet <buserror@gmail.com>
447 lines
11 KiB
C
447 lines
11 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[] = { "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], "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, 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 <math.h>
|
|
|
|
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",
|
|
" <default> : dump current state",
|
|
" 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(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]",
|
|
" <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);
|