macemu/cxmon/src/mon_cmd.cpp
Ricky Zhang 175fbfde43 Enhance ROM break point feature.
1. Change --break input option format. Too much typing by taking decimal address. Change to hexadecimal input.
2. Allow ROM break point to continue to execution. The original ROM break point just replace instruction in ROM break point address with emul_op M68K_EMUL_BREAK. This just halts emulation right at the break point. The patch is less invasive than the original approach. It allows emulation to continue to run by pressing 'x' to exit from cxmon.
3. Add option --loadbreak which load break point from file before emulation start.

Signed-off-by: Ricky Zhang <rickyzhang@gmail.com>
2017-09-15 21:36:16 -04:00

883 lines
17 KiB
C++

/*
* mon_cmd.cpp - cxmon standard commands
*
* cxmon (C) 1997-2004 Christian Bauer, Marc Hellwig
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "sysdeps.h"
#include <stdlib.h>
#include <assert.h>
#include "mon.h"
#include "mon_cmd.h"
#include "mon_disass.h"
#ifndef VERSION
#define VERSION "3"
#endif
/*
* range_args = [expression] [[COMMA] expression] END
*
* Read start address to "adr", end address to "end_adr".
* "adr" defaults to '.', "end_adr" defaults to '.'+def_range
*
* true: OK, false: Error
*/
static bool range_args(uintptr *adr, uintptr *end_adr, uint32 def_range)
{
*adr = mon_dot_address;
*end_adr = mon_dot_address + def_range;
if (mon_token == T_END)
return true;
else {
if (!mon_expression(adr))
return false;
*end_adr = *adr + def_range;
if (mon_token == T_END)
return true;
else {
if (mon_token == T_COMMA) mon_get_token();
if (!mon_expression(end_adr))
return false;
return mon_token == T_END;
}
}
}
/*
* byte_string = (expression | STRING) {COMMA (expression | STRING)} END
*/
static bool byte_string(uint8 *&str, uintptr &len)
{
uintptr value;
static const int GRANULARITY = 16; // must be a power of 2
str = NULL;
len = 0;
goto start;
for (;;) {
if (mon_token == T_COMMA) {
mon_get_token();
start:
if (mon_token == T_STRING) {
unsigned n = strlen(mon_string);
str = (uint8 *)realloc(str, (len + n - 1 + GRANULARITY) & ~(GRANULARITY - 1));
assert(str != NULL);
memcpy(str + len, mon_string, n);
len += n;
mon_get_token();
} else if (mon_expression(&value)) {
str = (uint8 *)realloc(str, (len + GRANULARITY) & ~(GRANULARITY - 1));
assert(str != NULL);
str[len] = value;
len++;
} else {
if (str)
free(str);
return false;
}
} else if (mon_token == T_END) {
return true;
} else {
mon_error("',' expected");
if (str)
free(str);
return false;
}
}
}
/*
* Convert character to printable character
*/
static inline uint8 char2print(uint8 c)
{
return (c >= 0x20 && c <= 0x7e) ? c : '.';
}
/*
* Show version
* ver
*/
void version(void)
{
fprintf(monout, "cxmon V" VERSION "\n");
}
/*
* Redirect output
* o [file]
*/
void redir_output(void)
{
// Close old file
if (monout != monerr) {
fclose(monout);
monout = monerr;
return;
}
// No argument given?
if (mon_token == T_END)
return;
// Otherwise open file
if (mon_token == T_STRING) {
mon_get_token();
if (mon_token != T_END) {
mon_error("Too many arguments");
return;
}
if (!(monout = fopen(mon_string, "w")))
mon_error("Unable to open file");
} else
mon_error("'\"' around file name expected");
}
/*
* Compute and display expression
* ? expression
*/
void print_expr(void)
{
uintptr val;
if (!mon_expression(&val))
return;
if (mon_token != T_END) {
mon_error("Too many arguments");
return;
}
if (val > 0x7fffffff) {
fprintf(monout, "Hex unsigned: $%08x\n"
"Hex signed : -$%08x\n"
"Dec unsigned: %u\n"
"Dec signed : %d\n", val, -val, val, val);
fprintf(monout, "Char : '%c%c%c%c'\n", char2print(val >> 24), char2print(val >> 16), char2print(val >> 8), char2print(val));
} else {
fprintf(monout, "Hex : $%08x\n"
"Dec : %d\n", val, val);
fprintf(monout, "Char: '%c%c%c%c'\n", char2print(val >> 24), char2print(val >> 16), char2print(val >> 8), char2print(val));
}
}
/*
* Execute shell command
* \ "command"
*/
void shell_command(void)
{
if (mon_token != T_STRING) {
mon_error("'\"' around command expected");
return;
}
mon_get_token();
if (mon_token != T_END) {
mon_error("Too many arguments");
return;
}
system(mon_string);
}
/*
* Memory dump
* m [start [end]]
*/
#define MEMDUMP_BPL 16 // Bytes per line
void memory_dump(void)
{
uintptr adr, end_adr;
uint8 mem[MEMDUMP_BPL + 1];
mem[MEMDUMP_BPL] = 0;
if (!range_args(&adr, &end_adr, 16 * MEMDUMP_BPL - 1)) // 16 lines unless end address specified
return;
while (adr <= end_adr && !mon_aborted()) {
fprintf(monout, "%0*lx:", int(2 * sizeof(adr)), mon_use_real_mem ? adr: adr % mon_mem_size);
for (int i=0; i<MEMDUMP_BPL; i++, adr++) {
if (i % 4 == 0)
fprintf(monout, " %08x", mon_read_word(adr));
mem[i] = char2print(mon_read_byte(adr));
}
fprintf(monout, " '%s'\n", mem);
}
mon_dot_address = adr;
}
/*
* ASCII dump
* i [start [end]]
*/
#define ASCIIDUMP_BPL 64 // Bytes per line
void ascii_dump(void)
{
uintptr adr, end_adr;
uint8 str[ASCIIDUMP_BPL + 1];
str[ASCIIDUMP_BPL] = 0;
if (!range_args(&adr, &end_adr, 16 * ASCIIDUMP_BPL - 1)) // 16 lines unless end address specified
return;
while (adr <= end_adr && !mon_aborted()) {
fprintf(monout, "%0*lx:", int(2 * sizeof(adr)), mon_use_real_mem ? adr : adr % mon_mem_size);
for (int i=0; i<ASCIIDUMP_BPL; i++, adr++)
str[i] = char2print(mon_read_byte(adr));
fprintf(monout, " '%s'\n", str);
}
mon_dot_address = adr;
}
/*
* Binary dump
* b [start [end]]
*/
void binary_dump(void)
{
uintptr adr, end_adr;
uint8 str[9];
str[8] = 0;
if (!range_args(&adr, &end_adr, 7)) // 8 lines unless end address specified
return;
while (adr <= end_adr && !mon_aborted()) {
fprintf(monout, "%0*lx:", int(2 * sizeof(adr)), mon_use_real_mem ? adr : adr % mon_mem_size);
uint8 b = mon_read_byte(adr);
for (int m=0x80, i=0; i<8; m>>=1, i++)
str[i] = (b & m) ? '*' : '.';
fprintf(monout, " '%s'\n", str);
adr++;
}
mon_dot_address = adr;
}
/*
* Add Break Point
*/
void break_point_add(void)
{
uintptr address;
if (mon_token == T_END || !mon_expression(&address)) {
mon_error("Expect break point in hexadecimal.");
return;
}
if (mon_token != T_END) {
mon_error("Too many arguments");
return;
}
mon_add_break_point(address);
}
bool validate_index(uintptr *index_ptr, const BREAK_POINT_SET& break_point_set)
{
if (mon_token == T_END || !mon_expression(index_ptr)) {
mon_error("Expect index number of break point in hexadecimal.\n");
return false;
}
if (mon_token != T_END) {
mon_error("Too many arguments");
return false;
}
if (*index_ptr > break_point_set.size()) {
mon_error("Illegal index number!");
return false;
}
return true;
}
/*
* Remove Break Point
*/
void break_point_remove(void)
{
uintptr index;
if (!validate_index(&index, active_break_points))
return;
if (0 == index) {
active_break_points.clear();
printf("Removed all break points!\n");
return;
}
BREAK_POINT_SET::iterator it = active_break_points.begin();
std::advance(it, index - 1);
// Remove break point
printf("Removed break point %4x at address %08lx\n", index, *it);
active_break_points.erase(it);
}
/*
* Disable Break Point
*/
void break_point_disable(void)
{
uintptr index;
if (!validate_index(&index, active_break_points))
return;
if (0 == index) {
for (BREAK_POINT_SET::iterator it = active_break_points.begin(); it != active_break_points.end(); it++)
disabled_break_points.insert(*it);
active_break_points.clear();
printf("Disabled all break points!\n");
return;
}
BREAK_POINT_SET::iterator it = active_break_points.begin();
std::advance(it, index - 1);
// Add to disable break points
printf("Disabled break point %4x at address %08lx\n", index, *it);
disabled_break_points.insert(*it);
// Remove break point
active_break_points.erase(it);
}
/*
* Enable Break Point
*/
void break_point_enable(void)
{
uintptr index;
if (!validate_index(&index, disabled_break_points))
return;
if (0 == index) {
active_break_points.insert(disabled_break_points.begin(), disabled_break_points.end());
disabled_break_points.clear();
printf("Enabled all break points!\n");
return;
}
BREAK_POINT_SET::iterator it = disabled_break_points.begin();
std::advance(it, index - 1);
// Add to active break points
printf("Disabled break point %4x at address %08lx\n", index, *it);
active_break_points.insert(*it);
// Remove break point
disabled_break_points.erase(it);
}
/*
* List all Active Break Points
*/
void break_point_info(void)
{
if (mon_token != T_END) {
mon_error("Too many arguments");
return;
}
BREAK_POINT_SET::iterator it;
if (!active_break_points.empty()) {
int pos = 1;
printf(STR_ACTIVE_BREAK_POINTS);
for (it = active_break_points.begin(); it != active_break_points.end(); it++)
printf("\tBreak point %4x at address %08lx\n", pos++, *it);
}
if (!disabled_break_points.empty()) {
putchar('\n');
printf(STR_DISABLED_BREAK_POINTS);
int pos = 1;
for (it = disabled_break_points.begin(); it != disabled_break_points.end(); it++)
printf("\tBreak point %4x at address %08lx\n", pos++, *it);
}
}
/*
* Save all Active Break Points to a file
*/
void break_point_save(void)
{
if (mon_token == T_END) {
mon_error("Missing file name");
return;
}
if (mon_token != T_STRING) {
mon_error("'\"' around file name expected");
return;
}
mon_get_token();
if (mon_token != T_END) {
mon_error("Too many arguments");
return;
}
FILE *file;
if (!(file = fopen(mon_string, "w"))) {
mon_error("Unable to create file");
return;
}
BREAK_POINT_SET::iterator it;
fprintf(file, STR_ACTIVE_BREAK_POINTS);
for (it = active_break_points.begin(); it != active_break_points.end(); it++)
fprintf(file, "%x\n", *it);
fprintf(file, STR_DISABLED_BREAK_POINTS);
for (it = disabled_break_points.begin(); it != disabled_break_points.end(); it++)
fprintf(file, "%x\n", *it);
fclose(file);
}
/*
* Load Break Point from a file
*/
void break_point_load(void)
{
if (mon_token == T_END) {
mon_error("Missing file name");
return;
}
if (mon_token != T_STRING) {
mon_error("'\"' around file name expected");
return;
}
mon_get_token();
if (mon_token != T_END) {
mon_error("Too many arguments");
return;
}
// load from file
mon_load_break_point(mon_string);
}
/*
* Disassemble
* d [start [end]]
* d65 [start [end]]
* d68 [start [end]]
* d80 [start [end]]
* d86 [start [end]]
* d8086 [start [end]]
*/
enum CPUType {
CPU_PPC,
CPU_6502,
CPU_680x0,
CPU_Z80,
CPU_80x86_32,
CPU_80x86_16,
CPU_x86_64
};
static void disassemble(CPUType type)
{
uintptr adr, end_adr;
if (!range_args(&adr, &end_adr, 16 * 4 - 1)) // 16 lines unless end address specified
return;
switch (type) {
case CPU_PPC:
while (adr <= end_adr && !mon_aborted()) {
uint32 w = mon_read_word(adr);
fprintf(monout, "%0*lx: %08x\t", int(2 * sizeof(adr)), mon_use_real_mem ? adr : adr % mon_mem_size, w);
disass_ppc(monout, mon_use_real_mem ? adr : adr % mon_mem_size, w);
adr += 4;
}
break;
case CPU_6502:
while (adr <= end_adr && !mon_aborted()) {
uint8 op = mon_read_byte(adr);
uint8 lo = mon_read_byte(adr + 1);
uint8 hi = mon_read_byte(adr + 2);
fprintf(monout, "%0*lx: ", int(2 * sizeof(adr)), mon_use_real_mem ? adr : adr % mon_mem_size);
adr += disass_6502(monout, mon_use_real_mem ? adr : adr % mon_mem_size, op, lo, hi);
}
break;
case CPU_680x0:
while (adr <= end_adr && !mon_aborted()) {
fprintf(monout, "%0*lx: ", int(2 * sizeof(adr)), mon_use_real_mem ? adr : adr % mon_mem_size);
adr += disass_68k(monout, mon_use_real_mem ? adr : adr % mon_mem_size);
}
break;
case CPU_Z80:
while (adr <= end_adr && !mon_aborted()) {
fprintf(monout, "%0*lx: ", int(2 * sizeof(adr)), mon_use_real_mem ? adr : adr % mon_mem_size);
adr += disass_z80(monout, mon_use_real_mem ? adr : adr % mon_mem_size);
}
break;
case CPU_x86_64:
while (adr <= end_adr && !mon_aborted()) {
fprintf(monout, "%0*lx: ", int(2 * sizeof(adr)), mon_use_real_mem ? adr : adr % mon_mem_size);
adr += disass_x86(monout, mon_use_real_mem ? adr : adr % mon_mem_size, 64);
}
break;
case CPU_80x86_32:
while (adr <= end_adr && !mon_aborted()) {
fprintf(monout, "%0*lx: ", int(2 * sizeof(adr)), mon_use_real_mem ? adr : adr % mon_mem_size);
adr += disass_x86(monout, mon_use_real_mem ? adr : adr % mon_mem_size, 32);
}
break;
case CPU_80x86_16:
while (adr <= end_adr && !mon_aborted()) {
fprintf(monout, "%0*lx: ", int(2 * sizeof(adr)), mon_use_real_mem ? adr : adr % mon_mem_size);
adr += disass_x86(monout, mon_use_real_mem ? adr : adr % mon_mem_size, 16);
}
}
mon_dot_address = adr;
}
void disassemble_ppc(void)
{
disassemble(CPU_PPC);
}
void disassemble_6502(void)
{
disassemble(CPU_6502);
}
void disassemble_680x0(void)
{
disassemble(CPU_680x0);
}
void disassemble_z80(void)
{
disassemble(CPU_Z80);
}
void disassemble_80x86_32(void)
{
disassemble(CPU_80x86_32);
}
void disassemble_80x86_16(void)
{
disassemble(CPU_80x86_16);
}
void disassemble_x86_64(void)
{
disassemble(CPU_x86_64);
}
/*
* Modify memory
* : addr bytestring
*/
void modify(void)
{
uintptr adr, len, src_adr = 0;
uint8 *str;
if (!mon_expression(&adr))
return;
if (!byte_string(str, len))
return;
while (src_adr < len)
mon_write_byte(adr++, str[src_adr++]);
mon_dot_address = adr;
free(str);
}
/*
* Fill
* f start end bytestring
*/
void fill(void)
{
uintptr adr, end_adr, len, src_adr = 0;
uint8 *str;
if (!mon_expression(&adr))
return;
if (!mon_expression(&end_adr))
return;
if (!byte_string(str, len))
return;
while (adr <= end_adr)
mon_write_byte(adr++, str[src_adr++ % len]);
free(str);
}
/*
* Transfer memory
* t start end dest
*/
void transfer(void)
{
uintptr adr, end_adr, dest;
int num;
if (!mon_expression(&adr))
return;
if (!mon_expression(&end_adr))
return;
if (!mon_expression(&dest))
return;
if (mon_token != T_END) {
mon_error("Too many arguments");
return;
}
num = end_adr - adr + 1;
if (dest < adr)
for (int i=0; i<num; i++)
mon_write_byte(dest++, mon_read_byte(adr++));
else {
dest += end_adr - adr;
for (int i=0; i<num; i++)
mon_write_byte(dest--, mon_read_byte(end_adr--));
}
}
/*
* Compare
* c start end dest
*/
void compare(void)
{
uintptr adr, end_adr, dest;
int num = 0;
if (!mon_expression(&adr))
return;
if (!mon_expression(&end_adr))
return;
if (!mon_expression(&dest))
return;
if (mon_token != T_END) {
mon_error("Too many arguments");
return;
}
while (adr <= end_adr && !mon_aborted()) {
if (mon_read_byte(adr) != mon_read_byte(dest)) {
fprintf(monout, "%0*lx ", int(2 * sizeof(adr)), mon_use_real_mem ? adr : adr % mon_mem_size);
num++;
if (!(num & 7))
fputc('\n', monout);
}
adr++; dest++;
}
if (num & 7)
fputc('\n', monout);
fprintf(monout, "%d byte(s) different\n", num);
}
/*
* Search for byte string
* h start end bytestring
*/
void hunt(void)
{
uintptr adr, end_adr, len;
uint8 *str;
int num = 0;
if (!mon_expression(&adr))
return;
if (!mon_expression(&end_adr))
return;
if (!byte_string(str, len))
return;
while ((adr+len-1) <= end_adr && !mon_aborted()) {
uint32 i;
for (i=0; i<len; i++)
if (mon_read_byte(adr + i) != str[i])
break;
if (i == len) {
fprintf(monout, "%0*lx ", int(2 * sizeof(adr)), mon_use_real_mem ? adr : adr % mon_mem_size);
num++;
if (num == 1)
mon_dot_address = adr;
if (!(num & 7))
fputc('\n', monout);
}
adr++;
}
free(str);
if (num & 7)
fputc('\n', monout);
fprintf(monout, "Found %d occurrences\n", num);
}
/*
* Load data
* [ start "file"
*/
void load_data(void)
{
uintptr start_adr;
FILE *file;
int fc;
if (!mon_expression(&start_adr))
return;
if (mon_token == T_END) {
mon_error("Missing file name");
return;
}
if (mon_token != T_STRING) {
mon_error("'\"' around file name expected");
return;
}
mon_get_token();
if (mon_token != T_END) {
mon_error("Too many arguments");
return;
}
if (!(file = fopen(mon_string, "rb")))
mon_error("Unable to open file");
else {
uintptr adr = start_adr;
while ((fc = fgetc(file)) != EOF)
mon_write_byte(adr++, fc);
fclose(file);
fprintf(monerr, "%08x bytes read from %0*lx to %0*lx\n", adr - start_adr, int(2 * sizeof(adr)), mon_use_real_mem ? start_adr : start_adr % mon_mem_size, int(2 * sizeof(adr)), mon_use_real_mem ? adr-1 : (adr-1) % mon_mem_size);
mon_dot_address = adr;
}
}
/*
* Save data
* ] start size "file"
*/
void save_data(void)
{
uintptr start_adr, size;
FILE *file;
if (!mon_expression(&start_adr))
return;
if (!mon_expression(&size))
return;
if (mon_token == T_END) {
mon_error("Missing file name");
return;
}
if (mon_token != T_STRING) {
mon_error("'\"' around file name expected");
return;
}
mon_get_token();
if (mon_token != T_END) {
mon_error("Too many arguments");
return;
}
if (!(file = fopen(mon_string, "wb")))
mon_error("Unable to create file");
else {
uintptr adr = start_adr, end_adr = start_adr + size - 1;
while (adr <= end_adr)
fputc(mon_read_byte(adr++), file);
fclose(file);
fprintf(monerr, "%08x bytes written from %0*lx to %0*lx\n", size, int(2 * sizeof(adr)), mon_use_real_mem ? start_adr : start_adr % mon_mem_size, int(2 * sizeof(adr)), mon_use_real_mem ? end_adr : end_adr % mon_mem_size);
}
}