mirror of
https://github.com/kanjitalk755/macemu.git
synced 2024-06-19 19:29:39 +00:00
1324 lines
26 KiB
C++
1324 lines
26 KiB
C++
/*
|
|
* mon.cpp - cxmon main program
|
|
*
|
|
* 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <signal.h>
|
|
#include <ctype.h>
|
|
#include <string>
|
|
#include <map>
|
|
#include <sstream>
|
|
|
|
#if defined(HAVE_READLINE_H)
|
|
extern "C" {
|
|
#include <readline.h>
|
|
}
|
|
#elif defined(HAVE_READLINE_READLINE_H)
|
|
extern "C" {
|
|
#include <readline/readline.h>
|
|
}
|
|
#endif
|
|
|
|
#if defined(HAVE_HISTORY_H)
|
|
extern "C" {
|
|
#include <history.h>
|
|
}
|
|
#elif defined(HAVE_READLINE_HISTORY_H)
|
|
extern "C" {
|
|
#include <readline/history.h>
|
|
}
|
|
#endif
|
|
|
|
#include "mon.h"
|
|
#include "mon_cmd.h"
|
|
#include "mon_lowmem.h"
|
|
|
|
#ifndef VERSION
|
|
#define VERSION "3"
|
|
#endif
|
|
|
|
// Break points
|
|
BREAK_POINT_SET active_break_points;
|
|
BREAK_POINT_SET disabled_break_points;
|
|
|
|
// Buffer we're operating on
|
|
bool mon_use_real_mem = false;
|
|
uint32 mon_mem_size;
|
|
static uint8 *mem;
|
|
|
|
|
|
// Streams for input, output and error messages
|
|
FILE *monin, *monout, *monerr = NULL;
|
|
|
|
// Input line
|
|
static char *input;
|
|
static char *in_ptr;
|
|
char *mon_args_ptr;
|
|
|
|
// Current address, value of '.' in expressions
|
|
uintptr mon_dot_address;
|
|
|
|
// Current value of ':' in expression
|
|
static uint32 colon_value;
|
|
|
|
|
|
// Scanner variables
|
|
enum Token mon_token; // Last token read
|
|
uintptr mon_number; // Contains the number if mon_token==T_NUMBER
|
|
char *mon_string; // Contains the string if mon_token==T_STRING
|
|
char *mon_name; // Contains the variable name if mon_token==T_NAME
|
|
|
|
|
|
// List of installed commands
|
|
struct CmdSpec {
|
|
const char *name; // Name of command
|
|
void (*func)(); // Function that executes this command
|
|
};
|
|
|
|
static CmdSpec *cmds; // Array of CmdSpecs
|
|
static int num_cmds; // Number of installed commands
|
|
static char *cmd_help; // Help text for commands
|
|
|
|
|
|
// List of variables
|
|
typedef std::map<std::string, uintptr> var_map;
|
|
static var_map vars;
|
|
|
|
|
|
// Prototypes
|
|
static void init_abort();
|
|
static void exit_abort();
|
|
|
|
static void read_line(char *prompt); // Scanner
|
|
static char get_char();
|
|
static void put_back(char c);
|
|
static enum Token get_hex_number(uintptr &i);
|
|
static enum Token get_dec_number(uintptr &i);
|
|
static enum Token get_char_number(uintptr &i);
|
|
static enum Token get_string(char *&str);
|
|
static enum Token get_hex_or_name(uintptr &i, char *&name);
|
|
|
|
static bool eor_expr(uintptr *number); // Parser
|
|
static bool and_expr(uintptr *number);
|
|
static bool shift_expr(uintptr *number);
|
|
static bool add_expr(uintptr *number);
|
|
static bool mul_expr(uintptr *number);
|
|
static bool factor(uintptr *number);
|
|
|
|
|
|
/*
|
|
* Add command to mon
|
|
*/
|
|
|
|
void mon_add_command(const char *name, void (*func)(), const char *help_text)
|
|
{
|
|
num_cmds++;
|
|
if (cmds)
|
|
cmds = (CmdSpec *)realloc(cmds, num_cmds * sizeof(CmdSpec));
|
|
else
|
|
cmds = (CmdSpec *)malloc(sizeof(CmdSpec));
|
|
cmds[num_cmds - 1].name = name;
|
|
cmds[num_cmds - 1].func = func;
|
|
if (help_text) {
|
|
if (cmd_help) {
|
|
cmd_help = (char *)realloc(cmd_help, strlen(cmd_help) + strlen(help_text) + 1);
|
|
strcat(cmd_help, help_text);
|
|
} else
|
|
cmd_help = strdup(help_text);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Print error message
|
|
*/
|
|
|
|
void mon_error(const char *s)
|
|
{
|
|
fprintf(monerr == NULL? stdout : monerr, "*** %s\n", s);
|
|
}
|
|
|
|
|
|
/*
|
|
* CTRL-C pressed?
|
|
*/
|
|
|
|
static bool was_aborted;
|
|
static struct sigaction my_sa;
|
|
|
|
#ifdef __BEOS__
|
|
static void handle_abort(int sig, void *arg, vregs *r)
|
|
#else
|
|
static void handle_abort(int sig)
|
|
#endif
|
|
{
|
|
was_aborted = true;
|
|
}
|
|
|
|
static void init_abort()
|
|
{
|
|
was_aborted = false;
|
|
sigemptyset(&my_sa.sa_mask);
|
|
#ifdef __BEOS__
|
|
my_sa.sa_handler = (__signal_func_ptr)handle_abort;
|
|
my_sa.sa_userdata = 0;
|
|
#else
|
|
my_sa.sa_handler = handle_abort;
|
|
#endif
|
|
my_sa.sa_flags = 0;
|
|
sigaction(SIGINT, &my_sa, NULL);
|
|
}
|
|
|
|
static void exit_abort()
|
|
{
|
|
my_sa.sa_handler = SIG_DFL;
|
|
sigaction(SIGINT, &my_sa, NULL);
|
|
}
|
|
|
|
bool mon_aborted()
|
|
{
|
|
bool ret = was_aborted;
|
|
was_aborted = false;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Access to buffer
|
|
*/
|
|
|
|
uint32 (*mon_read_byte)(uintptr adr);
|
|
|
|
uint32 mon_read_byte_buffer(uintptr adr)
|
|
{
|
|
return mem[adr % mon_mem_size];
|
|
}
|
|
|
|
uint32 mon_read_byte_real(uintptr adr)
|
|
{
|
|
return *(uint8 *)adr;
|
|
}
|
|
|
|
void (*mon_write_byte)(uintptr adr, uint32 b);
|
|
|
|
void mon_write_byte_buffer(uintptr adr, uint32 b)
|
|
{
|
|
mem[adr % mon_mem_size] = b;
|
|
}
|
|
|
|
void mon_write_byte_real(uintptr adr, uint32 b)
|
|
{
|
|
*(uint8 *)adr = b;
|
|
}
|
|
|
|
uint32 mon_read_half(uintptr adr)
|
|
{
|
|
return (mon_read_byte(adr) << 8) | mon_read_byte(adr+1);
|
|
}
|
|
|
|
void mon_write_half(uintptr adr, uint32 w)
|
|
{
|
|
mon_write_byte(adr, w >> 8);
|
|
mon_write_byte(adr+1, w);
|
|
}
|
|
|
|
uint32 mon_read_word(uintptr adr)
|
|
{
|
|
return (mon_read_byte(adr) << 24) | (mon_read_byte(adr+1) << 16) | (mon_read_byte(adr+2) << 8) | mon_read_byte(adr+3);
|
|
}
|
|
|
|
void mon_write_word(uintptr adr, uint32 l)
|
|
{
|
|
mon_write_byte(adr, l >> 24);
|
|
mon_write_byte(adr+1, l >> 16);
|
|
mon_write_byte(adr+2, l >> 8);
|
|
mon_write_byte(adr+3, l);
|
|
}
|
|
|
|
|
|
/*
|
|
* Read a line from the keyboard
|
|
*/
|
|
|
|
static void read_line(char *prompt)
|
|
{
|
|
#ifdef HAVE_LIBREADLINE
|
|
if (input)
|
|
free(input);
|
|
input = readline(prompt);
|
|
|
|
if (input) {
|
|
if (*input)
|
|
add_history(input);
|
|
} else {
|
|
// EOF, quit cxmon
|
|
input = (char *)malloc(2);
|
|
input[0] = 'x';
|
|
input[1] = 0;
|
|
fprintf(monout, "x\n");
|
|
}
|
|
|
|
in_ptr = input;
|
|
#else
|
|
static const unsigned INPUT_LENGTH = 256;
|
|
if (!input)
|
|
input = (char *)malloc(INPUT_LENGTH);
|
|
fprintf(monout, prompt);
|
|
fflush(monout);
|
|
fgets(in_ptr = input, INPUT_LENGTH, monin);
|
|
char *s = strchr(input, '\n');
|
|
if (s != NULL)
|
|
*s = 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Read a character from the input line
|
|
*/
|
|
|
|
static char get_char()
|
|
{
|
|
return *in_ptr++;
|
|
}
|
|
|
|
|
|
/*
|
|
* Stuff back a character into the input line
|
|
*/
|
|
|
|
static void put_back(char c)
|
|
{
|
|
*(--in_ptr) = c;
|
|
}
|
|
|
|
|
|
/*
|
|
* Scanner: Get a token from the input line
|
|
*/
|
|
|
|
enum Token mon_get_token()
|
|
{
|
|
char c = get_char();
|
|
|
|
// Skip spaces
|
|
while (isspace(c))
|
|
c = get_char();
|
|
|
|
switch (c) {
|
|
case 0:
|
|
return mon_token = T_END;
|
|
case '(':
|
|
return mon_token = T_LPAREN;
|
|
case ')':
|
|
return mon_token = T_RPAREN;
|
|
case '.':
|
|
return mon_token = T_DOT;
|
|
case ':':
|
|
return mon_token = T_COLON;
|
|
case ',':
|
|
return mon_token = T_COMMA;
|
|
case '+':
|
|
return mon_token = T_PLUS;
|
|
case '-':
|
|
return mon_token = T_MINUS;
|
|
case '*':
|
|
return mon_token = T_MUL;
|
|
case '/':
|
|
return mon_token = T_DIV;
|
|
case '%':
|
|
return mon_token = T_MOD;
|
|
case '&':
|
|
return mon_token = T_AND;
|
|
case '|':
|
|
return mon_token = T_OR;
|
|
case '^':
|
|
return mon_token = T_EOR;
|
|
case '<':
|
|
if (get_char() == '<')
|
|
return mon_token = T_SHIFTL;
|
|
else {
|
|
mon_error("Unrecognized token");
|
|
return mon_token = T_NULL;
|
|
}
|
|
case '>':
|
|
if (get_char() == '>')
|
|
return mon_token = T_SHIFTR;
|
|
else {
|
|
mon_error("Unrecognized token");
|
|
return mon_token = T_NULL;
|
|
}
|
|
case '~':
|
|
return mon_token = T_NOT;
|
|
case '=':
|
|
return mon_token = T_ASSIGN;
|
|
|
|
case '$':
|
|
if ((mon_token = get_hex_number(mon_number)) == T_NULL)
|
|
mon_error("'$' must be followed by hexadecimal number");
|
|
return mon_token;
|
|
case '_':
|
|
if ((mon_token = get_dec_number(mon_number)) == T_NULL)
|
|
mon_error("'_' must be followed by decimal number");
|
|
return mon_token;
|
|
case '\'':
|
|
return mon_token = get_char_number(mon_number);
|
|
case '"':
|
|
return mon_token = get_string(mon_string);
|
|
|
|
default:
|
|
if (isalnum(c)) {
|
|
put_back(c);
|
|
return mon_token = get_hex_or_name(mon_number, mon_name);
|
|
}
|
|
mon_error("Unrecognized token");
|
|
return mon_token = T_NULL;
|
|
}
|
|
}
|
|
|
|
static enum Token get_hex_number(uintptr &i)
|
|
{
|
|
char c = get_char();
|
|
|
|
i = 0;
|
|
if (!isxdigit(c))
|
|
return T_NULL;
|
|
|
|
do {
|
|
c = tolower(c);
|
|
if (c < 'a')
|
|
i = (i << 4) + (c - '0');
|
|
else
|
|
i = (i << 4) + (c - 'a' + 10);
|
|
c = get_char();
|
|
} while (isxdigit(c));
|
|
|
|
if (isalnum(c))
|
|
return T_NULL;
|
|
else {
|
|
put_back(c);
|
|
return T_NUMBER;
|
|
}
|
|
}
|
|
|
|
static enum Token get_dec_number(uintptr &i)
|
|
{
|
|
char c = get_char();
|
|
|
|
i = 0;
|
|
if (!isdigit(c))
|
|
return T_NULL;
|
|
|
|
do {
|
|
i = (i * 10) + (c - '0');
|
|
c = get_char();
|
|
} while (isdigit(c));
|
|
|
|
if (isalnum(c))
|
|
return T_NULL;
|
|
else {
|
|
put_back(c);
|
|
return T_NUMBER;
|
|
}
|
|
}
|
|
|
|
static enum Token get_char_number(uintptr &i)
|
|
{
|
|
char c;
|
|
|
|
i = 0;
|
|
while ((c = get_char()) != 0) {
|
|
if (c == '\'')
|
|
return T_NUMBER;
|
|
i = (i << 8) + (uint8)c;
|
|
}
|
|
|
|
mon_error("Unterminated character constant");
|
|
return T_NULL;
|
|
}
|
|
|
|
static enum Token get_string(char *&str)
|
|
{
|
|
// Remember start of string
|
|
char *old_in_ptr = in_ptr;
|
|
|
|
// Determine string length
|
|
char c;
|
|
unsigned n = 0;
|
|
while ((c = get_char()) != 0) {
|
|
n++;
|
|
if (c == '"')
|
|
break;
|
|
}
|
|
if (c == 0) {
|
|
mon_error("Unterminated string");
|
|
return T_NULL;
|
|
}
|
|
|
|
// Allocate new buffer (n: size needed including terminating 0)
|
|
str = (char *)realloc(str, n);
|
|
|
|
// Copy string to buffer
|
|
char *p = str;
|
|
in_ptr = old_in_ptr;
|
|
while (--n)
|
|
*p++ = get_char();
|
|
*p++ = 0;
|
|
get_char(); // skip closing '"'
|
|
return T_STRING;
|
|
}
|
|
|
|
static enum Token get_hex_or_name(uintptr &i, char *&name)
|
|
{
|
|
// Remember start of token
|
|
char *old_in_ptr = in_ptr;
|
|
|
|
// Try hex number first
|
|
if (get_hex_number(i) == T_NUMBER)
|
|
return T_NUMBER;
|
|
|
|
// Not a hex number, must be a variable name; determine its length
|
|
in_ptr = old_in_ptr;
|
|
char c = get_char();
|
|
unsigned n = 1;
|
|
do {
|
|
n++;
|
|
c = get_char();
|
|
} while (isalnum(c));
|
|
|
|
// Allocate new buffer (n: size needed including terminating 0)
|
|
name = (char *)realloc(name, n);
|
|
|
|
// Copy name to buffer
|
|
in_ptr = old_in_ptr;
|
|
char *p = name;
|
|
while (--n)
|
|
*p++ = get_char();
|
|
*p = 0;
|
|
return T_NAME;
|
|
}
|
|
|
|
|
|
/*
|
|
* expression = eor_expr {OR eor_expr}
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
bool mon_expression(uintptr *number)
|
|
{
|
|
uintptr accu, expr;
|
|
|
|
if (!eor_expr(&accu))
|
|
return false;
|
|
|
|
for (;;)
|
|
switch (mon_token) {
|
|
case T_OR:
|
|
mon_get_token();
|
|
if (!eor_expr(&expr))
|
|
return false;
|
|
accu |= expr;
|
|
break;
|
|
|
|
default:
|
|
*number = accu;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* eor_expr = and_expr {EOR and_expr}
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool eor_expr(uintptr *number)
|
|
{
|
|
uintptr accu, expr;
|
|
|
|
if (!and_expr(&accu))
|
|
return false;
|
|
|
|
for (;;)
|
|
switch (mon_token) {
|
|
case T_EOR:
|
|
mon_get_token();
|
|
if (!and_expr(&expr))
|
|
return false;
|
|
accu ^= expr;
|
|
break;
|
|
|
|
default:
|
|
*number = accu;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* and_expr = shift_expr {AND shift_expr}
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool and_expr(uintptr *number)
|
|
{
|
|
uintptr accu, expr;
|
|
|
|
if (!shift_expr(&accu))
|
|
return false;
|
|
|
|
for (;;)
|
|
switch (mon_token) {
|
|
case T_AND:
|
|
mon_get_token();
|
|
if (!shift_expr(&expr))
|
|
return false;
|
|
accu &= expr;
|
|
break;
|
|
|
|
default:
|
|
*number = accu;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* shift_expr = add_expr {(SHIFTL | SHIFTR) add_expr}
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool shift_expr(uintptr *number)
|
|
{
|
|
uintptr accu, expr;
|
|
|
|
if (!add_expr(&accu))
|
|
return false;
|
|
|
|
for (;;)
|
|
switch (mon_token) {
|
|
case T_SHIFTL:
|
|
mon_get_token();
|
|
if (!add_expr(&expr))
|
|
return false;
|
|
accu <<= expr;
|
|
break;
|
|
|
|
case T_SHIFTR:
|
|
mon_get_token();
|
|
if (!add_expr(&expr))
|
|
return false;
|
|
accu >>= expr;
|
|
break;
|
|
|
|
default:
|
|
*number = accu;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* add_expr = mul_expr {(PLUS | MINUS) mul_expr}
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool add_expr(uintptr *number)
|
|
{
|
|
uintptr accu, expr;
|
|
|
|
if (!mul_expr(&accu))
|
|
return false;
|
|
|
|
for (;;)
|
|
switch (mon_token) {
|
|
case T_PLUS:
|
|
mon_get_token();
|
|
if (!mul_expr(&expr))
|
|
return false;
|
|
accu += expr;
|
|
break;
|
|
|
|
case T_MINUS:
|
|
mon_get_token();
|
|
if (!mul_expr(&expr))
|
|
return false;
|
|
accu -= expr;
|
|
break;
|
|
|
|
default:
|
|
*number = accu;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* mul_expr = factor {(MUL | DIV | MOD) factor}
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool mul_expr(uintptr *number)
|
|
{
|
|
uintptr accu, fact;
|
|
|
|
if (!factor(&accu))
|
|
return false;
|
|
|
|
for (;;)
|
|
switch (mon_token) {
|
|
case T_MUL:
|
|
mon_get_token();
|
|
if (!factor(&fact))
|
|
return false;
|
|
accu *= fact;
|
|
break;
|
|
|
|
case T_DIV:
|
|
mon_get_token();
|
|
if (!factor(&fact))
|
|
return false;
|
|
if (fact == 0) {
|
|
mon_error("Division by 0");
|
|
return false;
|
|
}
|
|
accu /= fact;
|
|
break;
|
|
|
|
case T_MOD:
|
|
mon_get_token();
|
|
if (!factor(&fact))
|
|
return false;
|
|
if (fact == 0) {
|
|
mon_error("Division by 0");
|
|
return false;
|
|
}
|
|
accu %= fact;
|
|
break;
|
|
|
|
default:
|
|
*number = accu;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* factor = NUMBER | NAME | DOT | COLON | (PLUS | MINUS | NOT) factor | LPAREN expression RPAREN
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool factor(uintptr *number)
|
|
{
|
|
switch (mon_token) {
|
|
case T_NUMBER:
|
|
*number = mon_number;
|
|
mon_get_token();
|
|
return true;
|
|
|
|
case T_NAME:{
|
|
var_map::const_iterator v = vars.find(mon_name);
|
|
if (v == vars.end())
|
|
return false;
|
|
else {
|
|
*number = v->second;
|
|
mon_get_token();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
case T_DOT:
|
|
*number = mon_dot_address;
|
|
mon_get_token();
|
|
return true;
|
|
|
|
case T_COLON:
|
|
*number = colon_value;
|
|
mon_get_token();
|
|
return true;
|
|
|
|
case T_PLUS:
|
|
mon_get_token();
|
|
return factor(number);
|
|
|
|
case T_MINUS:
|
|
mon_get_token();
|
|
if (factor(number)) {
|
|
*number = -*number;
|
|
return true;
|
|
} else
|
|
return false;
|
|
|
|
case T_NOT:
|
|
mon_get_token();
|
|
if (factor(number)) {
|
|
*number = ~*number;
|
|
return true;
|
|
} else
|
|
return false;
|
|
|
|
case T_LPAREN:
|
|
mon_get_token();
|
|
if (mon_expression(number))
|
|
if (mon_token == T_RPAREN) {
|
|
mon_get_token();
|
|
return true;
|
|
} else {
|
|
mon_error("Missing ')'");
|
|
return false;
|
|
}
|
|
else {
|
|
mon_error("Error in expression");
|
|
return false;
|
|
}
|
|
|
|
case T_END:
|
|
mon_error("Required argument missing");
|
|
return false;
|
|
|
|
default:
|
|
mon_error("'(' or number expected");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Set/clear/show variables
|
|
* set [var[=value]]
|
|
*/
|
|
|
|
static void set_var()
|
|
{
|
|
if (mon_token == T_END) {
|
|
|
|
// Show all variables
|
|
if (vars.empty())
|
|
fprintf(monout, "No variables defined\n");
|
|
else {
|
|
var_map::const_iterator v = vars.begin(), end = vars.end();
|
|
for (v=vars.begin(); v!=end; ++v)
|
|
fprintf(monout, "%s = %08lx\n", v->first.c_str(), v->second);
|
|
}
|
|
|
|
} else if (mon_token == T_NAME) {
|
|
std::string var_name = mon_name;
|
|
mon_get_token();
|
|
if (mon_token == T_ASSIGN) {
|
|
|
|
// Set variable
|
|
uintptr value;
|
|
mon_get_token();
|
|
if (!mon_expression(&value))
|
|
return;
|
|
if (mon_token != T_END) {
|
|
mon_error("Too many arguments");
|
|
return;
|
|
}
|
|
vars[var_name] = value;
|
|
|
|
} else if (mon_token == T_END) {
|
|
|
|
// Clear variable
|
|
vars.erase(var_name);
|
|
|
|
} else
|
|
mon_error("'=' expected");
|
|
} else
|
|
mon_error("Variable name expected");
|
|
}
|
|
|
|
|
|
/*
|
|
* Clear all variables
|
|
* cv
|
|
*/
|
|
|
|
static void clear_vars()
|
|
{
|
|
vars.clear();
|
|
}
|
|
|
|
|
|
/*
|
|
* Display help
|
|
* h
|
|
*/
|
|
|
|
static void help_or_hunt()
|
|
{
|
|
if (mon_token != T_END) {
|
|
hunt();
|
|
return;
|
|
}
|
|
fprintf(monout, "x Quit mon\n"
|
|
"h This help text\n");
|
|
fprintf(monout, "%s", cmd_help);
|
|
}
|
|
|
|
|
|
/*
|
|
* Display command list
|
|
* ??
|
|
*/
|
|
|
|
static void mon_cmd_list()
|
|
{
|
|
for (int i=0; i<num_cmds; i++)
|
|
fprintf(monout, "%s ", cmds[i].name);
|
|
fprintf(monout, "\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* Reallocate buffer
|
|
* @ [size]
|
|
*/
|
|
|
|
static void reallocate()
|
|
{
|
|
uintptr size;
|
|
|
|
if (mon_use_real_mem) {
|
|
fprintf(monerr, "Cannot reallocate buffer in real mode\n");
|
|
return;
|
|
}
|
|
|
|
if (mon_token == T_END) {
|
|
fprintf(monerr, "Buffer size: %08x bytes\n", mon_mem_size);
|
|
return;
|
|
}
|
|
|
|
if (!mon_expression(&size))
|
|
return;
|
|
if (mon_token != T_END) {
|
|
mon_error("Too many arguments");
|
|
return;
|
|
}
|
|
|
|
if ((mem = (uint8 *)realloc(mem, size)) != NULL)
|
|
fprintf(monerr, "Buffer size: %08x bytes\n", mon_mem_size = size);
|
|
else
|
|
fprintf(monerr, "Unable to reallocate buffer\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* Apply expression to memory
|
|
* y[b|h|w] start end expression
|
|
*/
|
|
|
|
static void apply(int size)
|
|
{
|
|
uintptr adr, end_adr, value;
|
|
char c;
|
|
|
|
if (!mon_expression(&adr))
|
|
return;
|
|
if (!mon_expression(&end_adr))
|
|
return;
|
|
if (!mon_expression(&value))
|
|
return;
|
|
if (mon_token != T_END) {
|
|
mon_error("Too many arguments");
|
|
return;
|
|
}
|
|
|
|
uint32 (*read_func)(uintptr adr);
|
|
void (*write_func)(uintptr adr, uint32 val);
|
|
switch (size) {
|
|
case 1:
|
|
read_func = mon_read_byte;
|
|
write_func = mon_write_byte;
|
|
break;
|
|
case 2:
|
|
read_func = mon_read_half;
|
|
write_func = mon_write_half;
|
|
break;
|
|
case 4:
|
|
read_func = mon_read_word;
|
|
write_func = mon_write_word;
|
|
break;
|
|
default:
|
|
abort();
|
|
break;
|
|
}
|
|
|
|
while (adr<=end_adr) {
|
|
colon_value = read_func(adr);
|
|
mon_dot_address = adr;
|
|
|
|
in_ptr = input;
|
|
while ((c = get_char()) == ' ') ;
|
|
while ((c = get_char()) != ' ') ;
|
|
while ((c = get_char()) == ' ') ;
|
|
put_back(c);
|
|
mon_get_token();
|
|
mon_expression(&value); // Skip start address
|
|
mon_expression(&value); // Skip end address
|
|
mon_expression(&value);
|
|
|
|
write_func(adr, value);
|
|
adr += size;
|
|
}
|
|
|
|
mon_dot_address = adr;
|
|
}
|
|
|
|
static void apply_byte()
|
|
{
|
|
apply(1);
|
|
}
|
|
|
|
static void apply_half()
|
|
{
|
|
apply(2);
|
|
}
|
|
|
|
static void apply_word()
|
|
{
|
|
apply(4);
|
|
}
|
|
|
|
|
|
/*
|
|
* Execute command via system() (for ls, rm, etc.)
|
|
*/
|
|
|
|
static void mon_exec()
|
|
{
|
|
system(input);
|
|
}
|
|
|
|
|
|
/*
|
|
* Change current directory
|
|
*/
|
|
|
|
void mon_change_dir()
|
|
{
|
|
in_ptr = input;
|
|
char c = get_char();
|
|
while (isspace(c))
|
|
c = get_char();
|
|
while (isgraph(c))
|
|
c = get_char();
|
|
while (isspace(c))
|
|
c = get_char();
|
|
put_back(c);
|
|
if (chdir(in_ptr) != 0)
|
|
mon_error("Cannot change directory");
|
|
}
|
|
|
|
|
|
/*
|
|
* Add break point
|
|
*/
|
|
|
|
void mon_add_break_point(uintptr addr)
|
|
{
|
|
BREAK_POINT_SET::iterator it = disabled_break_points.find(addr);
|
|
// Save break point
|
|
if (it == disabled_break_points.end()) {
|
|
active_break_points.insert(addr);
|
|
} else {
|
|
disabled_break_points.erase(it);
|
|
active_break_points.insert(addr);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Load break point from file
|
|
*/
|
|
void mon_load_break_point(const char* file_path)
|
|
{
|
|
FILE *file;
|
|
if (!(file = fopen(file_path, "r"))) {
|
|
mon_error("Unable to create file");
|
|
return;
|
|
}
|
|
|
|
char line_buff[1024];
|
|
bool is_disabled_break_points = false;
|
|
|
|
if (fgets(line_buff, sizeof(line_buff), file) == NULL ||
|
|
strcmp(line_buff, STR_ACTIVE_BREAK_POINTS) != 0) {
|
|
mon_error("Invalid break point file format!");
|
|
fclose(file);
|
|
return;
|
|
}
|
|
|
|
while (fgets(line_buff, sizeof(line_buff), file) != NULL) {
|
|
if (strcmp(line_buff, STR_DISABLED_BREAK_POINTS) == 0) {
|
|
is_disabled_break_points = true;
|
|
continue;
|
|
}
|
|
uintptr address;
|
|
std::stringstream ss;
|
|
ss << std::hex << line_buff;
|
|
ss >> address;
|
|
if (is_disabled_break_points)
|
|
disabled_break_points.insert(address);
|
|
else
|
|
active_break_points.insert(address);
|
|
}
|
|
|
|
fclose(file);
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize mon
|
|
*/
|
|
|
|
void mon_init()
|
|
{
|
|
cmds = NULL;
|
|
num_cmds = 0;
|
|
cmd_help = NULL;
|
|
|
|
mon_add_command("??", mon_cmd_list, "?? Show list of commands\n");
|
|
mon_add_command("ver", version, "ver Show version\n");
|
|
mon_add_command("?", print_expr, "? expression Calculate expression\n");
|
|
mon_add_command("@", reallocate, "@ [size] Reallocate buffer\n");
|
|
mon_add_command("i", ascii_dump, "i [start [end]] ASCII memory dump\n");
|
|
mon_add_command("m", memory_dump, "m [start [end]] Hex/ASCII memory dump\n");
|
|
mon_add_command("b", binary_dump, "b [start [end]] Binary memory dump\n");
|
|
mon_add_command("ba", break_point_add, "ba [address] Add a break point\n");
|
|
mon_add_command("br", break_point_remove, "br [breakpoints#] Remove a break point. If # is 0, remove all break points.\n");
|
|
mon_add_command("bd", break_point_disable, "bd [breakpoints#] Disable a break point. If # is 0, disable all break points.\n");
|
|
mon_add_command("be", break_point_enable, "be [breakpoints#] Enable a break point. If # is 0, enable all break points.\n");
|
|
mon_add_command("bi", break_point_info, "bi List all break points\n");
|
|
mon_add_command("bs", break_point_save, "bs \"file\" Save all break points to a file\n");
|
|
mon_add_command("bl", break_point_load, "bl \"file\" Load break points from a file\n");
|
|
mon_add_command("d", disassemble_ppc, "d [start [end]] Disassemble PowerPC code\n");
|
|
mon_add_command("d65", disassemble_6502, "d65 [start [end]] Disassemble 6502 code\n");
|
|
mon_add_command("d68", disassemble_680x0, "d68 [start [end]] Disassemble 680x0 code\n");
|
|
mon_add_command("d80", disassemble_z80, "d80 [start [end]] Disassemble Z80 code\n");
|
|
mon_add_command("d86", disassemble_80x86_32, "d86 [start [end]] Disassemble 80x86 (32-bit) code\n");
|
|
mon_add_command("d8086", disassemble_80x86_16, "d8086 [start [end]] Disassemble 80x86 (16-bit) code\n");
|
|
mon_add_command("d8664", disassemble_x86_64, "d8664 [start [end]] Disassemble x86-64 code\n");
|
|
mon_add_command(":", modify, ": start string Modify memory\n");
|
|
mon_add_command("f", fill, "f start end string Fill memory\n");
|
|
mon_add_command("y", apply_byte, "y[b|h|w] start end expr Apply expression to memory\n");
|
|
mon_add_command("yb", apply_byte, NULL);
|
|
mon_add_command("yh", apply_half, NULL);
|
|
mon_add_command("yw", apply_word, NULL);
|
|
mon_add_command("t", transfer, "t start end dest Transfer memory\n");
|
|
mon_add_command("c", compare, "c start end dest Compare memory\n");
|
|
mon_add_command("h", help_or_hunt, "h start end string Search for byte string\n");
|
|
mon_add_command("\\", shell_command, "\\ \"command\" Execute shell command\n");
|
|
mon_add_command("ls", mon_exec, "ls [args] List directory contents\n");
|
|
mon_add_command("rm", mon_exec, "rm [args] Remove file(s)\n");
|
|
mon_add_command("cp", mon_exec, "cp [args] Copy file(s)\n");
|
|
mon_add_command("mv", mon_exec, "mv [args] Move file(s)\n");
|
|
mon_add_command("cd", mon_change_dir, "cd directory Change current directory\n");
|
|
mon_add_command("o", redir_output, "o [\"file\"] Redirect output\n");
|
|
mon_add_command("[", load_data, "[ start \"file\" Load data from file\n");
|
|
mon_add_command("]", save_data, "] start size \"file\" Save data to file\n");
|
|
mon_add_command("set", set_var, "set [var[=value]] Set/clear/show variables\n");
|
|
mon_add_command("cv", clear_vars, "cv Clear all variables\n");
|
|
|
|
mon_read_byte = NULL;
|
|
mon_write_byte = NULL;
|
|
|
|
input = NULL;
|
|
mon_string = NULL;
|
|
mon_name = NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Deinitialize mon
|
|
*/
|
|
|
|
void mon_exit()
|
|
{
|
|
if (cmds) {
|
|
free(cmds);
|
|
cmds = NULL;
|
|
}
|
|
num_cmds = 0;
|
|
cmd_help = NULL;
|
|
|
|
if (input) {
|
|
free(input);
|
|
input = NULL;
|
|
}
|
|
if (mon_string) {
|
|
free(mon_string);
|
|
mon_string = NULL;
|
|
}
|
|
if (mon_name) {
|
|
free(mon_name);
|
|
mon_name = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Main function, read-execute loop
|
|
*/
|
|
|
|
void mon(int argc, const char **argv)
|
|
{
|
|
bool done = false, interactive = true;
|
|
|
|
// Setup input/output streams
|
|
monin = stdin;
|
|
monout = stdout;
|
|
monerr = stdout;
|
|
|
|
// Make argc/argv point to the actual arguments
|
|
const char *prg_name = argv[0];
|
|
if (argc)
|
|
argc--; argv++;
|
|
|
|
// Parse arguments
|
|
mon_macos_mode = false;
|
|
mon_use_real_mem = false;
|
|
while (argc > 0) {
|
|
if (strcmp(argv[0], "-h") == 0 || strcmp(argv[0], "--help") == 0) {
|
|
printf("Usage: %s [-m] [-r] [command...]\n", prg_name);
|
|
exit(0);
|
|
} else if (strcmp(argv[0], "-m") == 0)
|
|
mon_macos_mode = true;
|
|
else if (strcmp(argv[0], "-r") == 0)
|
|
mon_use_real_mem = true;
|
|
else
|
|
break;
|
|
argc--; argv++;
|
|
}
|
|
interactive = (argc == 0);
|
|
|
|
// Set up memory access functions if not supplied by the user
|
|
if (mon_read_byte == NULL) {
|
|
if (mon_use_real_mem)
|
|
mon_read_byte = mon_read_byte_real;
|
|
else
|
|
mon_read_byte = mon_read_byte_buffer;
|
|
}
|
|
if (mon_write_byte == NULL) {
|
|
if (mon_use_real_mem)
|
|
mon_write_byte = mon_write_byte_real;
|
|
else
|
|
mon_write_byte = mon_write_byte_buffer;
|
|
}
|
|
|
|
// Allocate buffer
|
|
if (!mon_use_real_mem) {
|
|
mon_mem_size = 0x100000;
|
|
mem = (uint8 *)malloc(mon_mem_size);
|
|
|
|
// Print banner
|
|
if (interactive)
|
|
fprintf(monerr, "\n *** cxmon V" VERSION " by Christian Bauer and Marc Hellwig ***\n"
|
|
" *** Press 'h' for help ***\n\n");
|
|
}
|
|
|
|
// Clear variables
|
|
vars.clear();
|
|
|
|
// In MacOS mode, pull in the lowmem globals as variables
|
|
if (mon_macos_mode) {
|
|
const lowmem_info *l = lowmem;
|
|
while (l->name) {
|
|
vars[l->name] = l->addr;
|
|
l++;
|
|
}
|
|
}
|
|
|
|
init_abort();
|
|
|
|
// Read and parse command line
|
|
char *cmd = NULL;
|
|
while (!done) {
|
|
if (interactive) {
|
|
char prompt[16];
|
|
sprintf(prompt, "[%0*lx]-> ", int(2 * sizeof(mon_dot_address)), mon_dot_address);
|
|
read_line(prompt);
|
|
if (!input) {
|
|
done = true;
|
|
continue;
|
|
}
|
|
} else {
|
|
if (argc == 0) {
|
|
done = true;
|
|
break;
|
|
} else {
|
|
unsigned n = strlen(argv[0]) + 1;
|
|
input = (char *)realloc(input, n);
|
|
strcpy(in_ptr = input, argv[0]);
|
|
argc--;
|
|
argv++;
|
|
}
|
|
}
|
|
|
|
// Skip leading spaces
|
|
char c = get_char();
|
|
while (isspace(c))
|
|
c = get_char();
|
|
put_back(c);
|
|
if (!c)
|
|
continue; // blank line
|
|
|
|
// Read command word
|
|
char *p = in_ptr;
|
|
while (isgraph(c))
|
|
c = get_char();
|
|
put_back(c);
|
|
unsigned n = in_ptr - p;
|
|
cmd = (char *)realloc(cmd, n + 1);
|
|
memcpy(cmd, p, n);
|
|
cmd[n] = 0;
|
|
|
|
// Execute command
|
|
if (strcmp(cmd, "x") == 0) { // Exit
|
|
done = true;
|
|
continue;
|
|
}
|
|
for (int i=0; i<num_cmds; i++) {
|
|
if (strcmp(cmd, cmds[i].name) == 0) {
|
|
mon_get_token();
|
|
cmds[i].func();
|
|
goto cmd_done;
|
|
}
|
|
}
|
|
mon_error("Unknown command");
|
|
cmd_done: ;
|
|
}
|
|
|
|
if (cmd)
|
|
free(cmd);
|
|
|
|
exit_abort();
|
|
|
|
// Free buffer
|
|
if (!mon_use_real_mem)
|
|
free(mem);
|
|
|
|
// Close output file if redirected
|
|
if (monout != monerr)
|
|
fclose(monout);
|
|
}
|