mirror of
https://github.com/Oibaf66/frodo-wii.git
synced 2024-11-26 21:44:22 +01:00
2193 lines
48 KiB
C++
2193 lines
48 KiB
C++
/*
|
|
* SAM.h - Simple Assembler and Monitor With Integrated System Explorer
|
|
*
|
|
* Frodo (C) 1994-1997,2002-2009 Christian Bauer
|
|
*
|
|
* 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 "SAM.h"
|
|
#include "C64.h"
|
|
#include "CPUC64.h"
|
|
#include "CPU1541.h"
|
|
#include "VIC.h"
|
|
#include "SID.h"
|
|
#include "CIA.h"
|
|
|
|
|
|
// Pointers to chips
|
|
static MOS6510 *TheCPU;
|
|
static MOS6502_1541 *TheCPU1541;
|
|
static MOS6569 *TheVIC;
|
|
static MOS6581 *TheSID;
|
|
static MOS6526_1 *TheCIA1;
|
|
static MOS6526_2 *TheCIA2;
|
|
|
|
// 6510/6502 registers
|
|
static MOS6510State R64;
|
|
static MOS6502State R1541;
|
|
|
|
static bool access_1541; // false: accessing C64, true: accessing 1541
|
|
|
|
// Access to 6510/6502 address space
|
|
static inline uint8 SAMReadByte(uint16 adr)
|
|
{
|
|
if (access_1541)
|
|
return TheCPU1541->ExtReadByte(adr);
|
|
else
|
|
return TheCPU->ExtReadByte(adr);
|
|
}
|
|
|
|
static inline void SAMWriteByte(uint16 adr, uint8 byte)
|
|
{
|
|
if (access_1541)
|
|
TheCPU1541->ExtWriteByte(adr, byte);
|
|
else
|
|
TheCPU->ExtWriteByte(adr, byte);
|
|
}
|
|
|
|
|
|
// Streams for input, output and error messages
|
|
static FILE *fin, *fout, *ferr;
|
|
|
|
// Input line
|
|
#define INPUT_LENGTH 80
|
|
static char input[INPUT_LENGTH];
|
|
static char *in_ptr;
|
|
|
|
static uint16 address, end_address;
|
|
|
|
|
|
// Input tokens
|
|
enum Token {
|
|
T_NULL, // Invalid token
|
|
T_END, // End of line
|
|
T_NUMBER, // Hexadecimal number
|
|
T_STRING, // String enclosed in ""
|
|
T_LPAREN, // '('
|
|
T_RPAREN, // ')'
|
|
T_ADD, // '+'
|
|
T_SUB, // '-'
|
|
T_MUL, // '*'
|
|
T_DIV, // '/'
|
|
T_COMMA, // ','
|
|
T_IMMED, // '#'
|
|
T_X, // 'x'
|
|
T_Y, // 'y'
|
|
T_PC, // 'pc'
|
|
T_SP, // 'sp'
|
|
|
|
T_A, // 'a' (get_reg_token() only)
|
|
T_DR, // 'dr' (get_reg_token() only)
|
|
T_PR // 'pr' (get_reg_token() only)
|
|
};
|
|
|
|
static enum Token the_token; // Last token read
|
|
static uint16 the_number; // Contains the number if the_token==T_NUMBER
|
|
static char the_string[INPUT_LENGTH]; // Contains the string if the_token==T_STRING
|
|
|
|
|
|
// Addressing modes
|
|
enum {
|
|
A_IMPL,
|
|
A_ACCU, // A
|
|
A_IMM, // #zz
|
|
A_REL, // Branches
|
|
A_ZERO, // zz
|
|
A_ZEROX, // zz,x
|
|
A_ZEROY, // zz,y
|
|
A_ABS, // zzzz
|
|
A_ABSX, // zzzz,x
|
|
A_ABSY, // zzzz,y
|
|
A_IND, // (zzzz)
|
|
A_INDX, // (zz,x)
|
|
A_INDY // (zz),y
|
|
};
|
|
|
|
// Mnemonics
|
|
enum {
|
|
M_ADC, M_AND, M_ASL, M_BCC, M_BCS, M_BEQ, M_BIT, M_BMI, M_BNE, M_BPL,
|
|
M_BRK, M_BVC, M_BVS, M_CLC, M_CLD, M_CLI, M_CLV, M_CMP, M_CPX, M_CPY,
|
|
M_DEC, M_DEX, M_DEY, M_EOR, M_INC, M_INX, M_INY, M_JMP, M_JSR, M_LDA,
|
|
M_LDX, M_LDY, M_LSR, M_NOP, M_ORA, M_PHA, M_PHP, M_PLA, M_PLP, M_ROL,
|
|
M_ROR, M_RTI, M_RTS, M_SBC, M_SEC, M_SED, M_SEI, M_STA, M_STX, M_STY,
|
|
M_TAX, M_TAY, M_TSX, M_TXA, M_TXS, M_TYA,
|
|
|
|
M_ILLEGAL, // Undocumented opcodes start here
|
|
|
|
M_IANC, M_IANE, M_IARR, M_IASR, M_IDCP, M_IISB, M_IJAM, M_INOP, M_ILAS,
|
|
M_ILAX, M_ILXA, M_IRLA, M_IRRA, M_ISAX, M_ISBC, M_ISBX, M_ISHA, M_ISHS,
|
|
M_ISHX, M_ISHY, M_ISLO, M_ISRE,
|
|
|
|
M_MAXIMUM // Highest element
|
|
};
|
|
|
|
// Mnemonic for each opcode
|
|
static const char mnemonic[256] = {
|
|
M_BRK , M_ORA , M_IJAM, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO, // 00
|
|
M_PHP , M_ORA , M_ASL , M_IANC, M_INOP, M_ORA, M_ASL , M_ISLO,
|
|
M_BPL , M_ORA , M_IJAM, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO, // 10
|
|
M_CLC , M_ORA , M_INOP, M_ISLO, M_INOP, M_ORA, M_ASL , M_ISLO,
|
|
M_JSR , M_AND , M_IJAM, M_IRLA, M_BIT , M_AND, M_ROL , M_IRLA, // 20
|
|
M_PLP , M_AND , M_ROL , M_IANC, M_BIT , M_AND, M_ROL , M_IRLA,
|
|
M_BMI , M_AND , M_IJAM, M_IRLA, M_INOP, M_AND, M_ROL , M_IRLA, // 30
|
|
M_SEC , M_AND , M_INOP, M_IRLA, M_INOP, M_AND, M_ROL , M_IRLA,
|
|
M_RTI , M_EOR , M_IJAM, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE, // 40
|
|
M_PHA , M_EOR , M_LSR , M_IASR, M_JMP , M_EOR, M_LSR , M_ISRE,
|
|
M_BVC , M_EOR , M_IJAM, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE, // 50
|
|
M_CLI , M_EOR , M_INOP, M_ISRE, M_INOP, M_EOR, M_LSR , M_ISRE,
|
|
M_RTS , M_ADC , M_IJAM, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA, // 60
|
|
M_PLA , M_ADC , M_ROR , M_IARR, M_JMP , M_ADC, M_ROR , M_IRRA,
|
|
M_BVS , M_ADC , M_IJAM, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA, // 70
|
|
M_SEI , M_ADC , M_INOP, M_IRRA, M_INOP, M_ADC, M_ROR , M_IRRA,
|
|
M_INOP, M_STA , M_INOP, M_ISAX, M_STY , M_STA, M_STX , M_ISAX, // 80
|
|
M_DEY , M_INOP, M_TXA , M_IANE, M_STY , M_STA, M_STX , M_ISAX,
|
|
M_BCC , M_STA , M_IJAM, M_ISHA, M_STY , M_STA, M_STX , M_ISAX, // 90
|
|
M_TYA , M_STA , M_TXS , M_ISHS, M_ISHY, M_STA, M_ISHX, M_ISHA,
|
|
M_LDY , M_LDA , M_LDX , M_ILAX, M_LDY , M_LDA, M_LDX , M_ILAX, // a0
|
|
M_TAY , M_LDA , M_TAX , M_ILXA, M_LDY , M_LDA, M_LDX , M_ILAX,
|
|
M_BCS , M_LDA , M_IJAM, M_ILAX, M_LDY , M_LDA, M_LDX , M_ILAX, // b0
|
|
M_CLV , M_LDA , M_TSX , M_ILAS, M_LDY , M_LDA, M_LDX , M_ILAX,
|
|
M_CPY , M_CMP , M_INOP, M_IDCP, M_CPY , M_CMP, M_DEC , M_IDCP, // c0
|
|
M_INY , M_CMP , M_DEX , M_ISBX, M_CPY , M_CMP, M_DEC , M_IDCP,
|
|
M_BNE , M_CMP , M_IJAM, M_IDCP, M_INOP, M_CMP, M_DEC , M_IDCP, // d0
|
|
M_CLD , M_CMP , M_INOP, M_IDCP, M_INOP, M_CMP, M_DEC , M_IDCP,
|
|
M_CPX , M_SBC , M_INOP, M_IISB, M_CPX , M_SBC, M_INC , M_IISB, // e0
|
|
M_INX , M_SBC , M_NOP , M_ISBC, M_CPX , M_SBC, M_INC , M_IISB,
|
|
M_BEQ , M_SBC , M_IJAM, M_IISB, M_INOP, M_SBC, M_INC , M_IISB, // f0
|
|
M_SED , M_SBC , M_INOP, M_IISB, M_INOP, M_SBC, M_INC , M_IISB
|
|
};
|
|
|
|
// Addressing mode for each opcode
|
|
static const char adr_mode[256] = {
|
|
A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 00
|
|
A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS,
|
|
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 10
|
|
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX,
|
|
A_ABS , A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 20
|
|
A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS,
|
|
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 30
|
|
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX,
|
|
A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 40
|
|
A_IMPL, A_IMM , A_ACCU, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS,
|
|
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 50
|
|
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX,
|
|
A_IMPL, A_INDX, A_IMPL, A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 60
|
|
A_IMPL, A_IMM , A_ACCU, A_IMM , A_IND , A_ABS , A_ABS , A_ABS,
|
|
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // 70
|
|
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX,
|
|
A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // 80
|
|
A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS,
|
|
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROY, A_ZEROY, // 90
|
|
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSY , A_ABSY,
|
|
A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // a0
|
|
A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS,
|
|
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROY, A_ZEROY, // b0
|
|
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSY , A_ABSY,
|
|
A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // c0
|
|
A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS,
|
|
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // d0
|
|
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX,
|
|
A_IMM , A_INDX, A_IMM , A_INDX, A_ZERO , A_ZERO , A_ZERO , A_ZERO, // e0
|
|
A_IMPL, A_IMM , A_IMPL, A_IMM , A_ABS , A_ABS , A_ABS , A_ABS,
|
|
A_REL , A_INDY, A_IMPL, A_INDY, A_ZEROX, A_ZEROX, A_ZEROX, A_ZEROX, // f0
|
|
A_IMPL, A_ABSY, A_IMPL, A_ABSY, A_ABSX , A_ABSX , A_ABSX , A_ABSX
|
|
};
|
|
|
|
// Chars for each mnemonic
|
|
static const char mnem_1[] = "aaabbbbbbbbbbcccccccdddeiiijjllllnopppprrrrssssssstttttt?aaaadijnlllrrsssssssss";
|
|
static const char mnem_2[] = "dnscceimnprvvllllmppeeeonnnmsdddsorhhlloottbeeetttaasxxy?nnrscsaoaaxlrabbhhhhlr";
|
|
static const char mnem_3[] = "cdlcsqtielkcscdivpxycxyrcxypraxyrpaapaplrisccdiaxyxyxasa?cerrpbmpsxaaaxcxasxyoe";
|
|
|
|
// Instruction length for each addressing mode
|
|
static const char adr_length[] = {1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 2, 2};
|
|
|
|
|
|
// Prototypes
|
|
static void error(const char *s);
|
|
static void handle_abort(...);
|
|
static void init_abort(void);
|
|
static void exit_abort(void);
|
|
static bool aborted(void);
|
|
|
|
static void read_line(void); // Scanner
|
|
static char get_char(void);
|
|
static void put_back(char c);
|
|
static enum Token get_token(void);
|
|
static enum Token get_reg_token(void);
|
|
static uint16 get_number(void);
|
|
static enum Token get_string(char *str);
|
|
|
|
static bool expression(uint16 *number); // Parser
|
|
static bool term(uint16 *number);
|
|
static bool factor(uint16 *number);
|
|
static bool address_args(void);
|
|
static bool range_args(int def_range);
|
|
static bool instr_args(uint16 *number, char *mode);
|
|
|
|
static void help(void); // Routines for commands
|
|
static void registers(void);
|
|
static void display_registers(void);
|
|
static void memory_dump(void);
|
|
static void ascii_dump(void);
|
|
static char conv_from_64(char c);
|
|
static void screen_dump(void);
|
|
static char conv_from_scode(char c);
|
|
static void binary_dump(void);
|
|
static void sprite_dump(void);
|
|
static void byte_to_bin(uint8 byte, char *str);
|
|
static void disassemble(void);
|
|
static int disass_line(uint16 adr, uint8 op, uint8 lo, uint8 hi);
|
|
static void assemble(void);
|
|
static char find_mnemonic(char op1, char op2, char op3);
|
|
static bool find_opcode(char mnem, char mode, uint8 *opcode);
|
|
static void mem_config(void);
|
|
static void fill(void);
|
|
static void compare(void);
|
|
static void transfer(void);
|
|
static void modify(void);
|
|
static void print_expr(void);
|
|
static void redir_output(void);
|
|
static void int_vectors(void);
|
|
static void view_state(void);
|
|
static void view_cia_state(void);
|
|
static void dump_cia_ints(uint8 i);
|
|
static void view_sid_state(void);
|
|
static void dump_sid_waveform(uint8 wave);
|
|
static void view_vic_state(void);
|
|
static void dump_spr_flags(uint8 f);
|
|
static void dump_vic_ints(uint8 i);
|
|
static void view_1541_state(void);
|
|
static void dump_via_ints(uint8 i);
|
|
static void load_data(void);
|
|
static void save_data(void);
|
|
|
|
|
|
/*
|
|
* Open and handle SAM
|
|
*/
|
|
|
|
void SAM(C64 *the_c64)
|
|
{
|
|
bool done = false;
|
|
char c;
|
|
|
|
TheCPU = the_c64->TheCPU;
|
|
TheCPU1541 = the_c64->TheCPU1541;
|
|
TheVIC = the_c64->TheVIC;
|
|
TheSID = the_c64->TheSID;
|
|
TheCIA1 = the_c64->TheCIA1;
|
|
TheCIA2 = the_c64->TheCIA2;
|
|
|
|
// Get CPU registers and current memory configuration
|
|
TheCPU->GetState(&R64);
|
|
TheCPU->ExtConfig = (~R64.ddr | R64.pr) & 7;
|
|
TheCPU1541->GetState(&R1541);
|
|
|
|
#ifdef __riscos__
|
|
Wimp_CommandWindow((int)"SAM");
|
|
#endif
|
|
|
|
#ifdef AMIGA
|
|
if (!(fin = fout = ferr = fopen("CON:0/0/640/480/SAM", "w+")))
|
|
return;
|
|
#else
|
|
fin = stdin;
|
|
fout = stdout;
|
|
ferr = stdout;
|
|
#endif
|
|
|
|
access_1541 = false;
|
|
address = R64.pc;
|
|
|
|
fprintf(ferr, "\n *** SAM - Simple Assembler and Monitor ***\n *** Press 'h' for help ***\n\n");
|
|
init_abort();
|
|
display_registers();
|
|
|
|
while (!done) {
|
|
if (access_1541)
|
|
fprintf(ferr, "1541> ");
|
|
else
|
|
fprintf(ferr, "C64> ");
|
|
fflush(ferr);
|
|
read_line();
|
|
while ((c = get_char()) == ' ') ;
|
|
|
|
switch (c) {
|
|
case 'a': // Assemble
|
|
get_token();
|
|
assemble();
|
|
break;
|
|
|
|
case 'b': // Binary dump
|
|
get_token();
|
|
binary_dump();
|
|
break;
|
|
|
|
case 'c': // Compare
|
|
get_token();
|
|
compare();
|
|
break;
|
|
|
|
case 'd': // Disassemble
|
|
get_token();
|
|
disassemble();
|
|
break;
|
|
|
|
case 'e': // Interrupt vectors
|
|
int_vectors();
|
|
break;
|
|
|
|
case 'f': // Fill
|
|
get_token();
|
|
fill();
|
|
break;
|
|
|
|
case 'h': // Help
|
|
help();
|
|
break;
|
|
|
|
case 'i': // ASCII dump
|
|
get_token();
|
|
ascii_dump();
|
|
break;
|
|
|
|
case 'k': // Memory configuration
|
|
get_token();
|
|
mem_config();
|
|
break;
|
|
|
|
case 'l': // Load data
|
|
get_token();
|
|
load_data();
|
|
break;
|
|
|
|
case 'm': // Memory dump
|
|
get_token();
|
|
memory_dump();
|
|
break;
|
|
|
|
case 'n': // Screen code dump
|
|
get_token();
|
|
screen_dump();
|
|
break;
|
|
|
|
case 'o': // Redirect output
|
|
get_token();
|
|
redir_output();
|
|
break;
|
|
|
|
case 'p': // Sprite dump
|
|
get_token();
|
|
sprite_dump();
|
|
break;
|
|
|
|
case 'r': // Registers
|
|
get_reg_token();
|
|
registers();
|
|
break;
|
|
|
|
case 's': // Save data
|
|
get_token();
|
|
save_data();
|
|
break;
|
|
|
|
case 't': // Transfer
|
|
get_token();
|
|
transfer();
|
|
break;
|
|
|
|
case 'v': // View machine state
|
|
view_state();
|
|
break;
|
|
|
|
case 'x': // Exit
|
|
done = true;
|
|
break;
|
|
|
|
case ':': // Change memory
|
|
get_token();
|
|
modify();
|
|
break;
|
|
|
|
case '1': // Switch to 1541 mode
|
|
access_1541 = true;
|
|
break;
|
|
|
|
case '6': // Switch to C64 mode
|
|
access_1541 = false;
|
|
break;
|
|
|
|
case '?': // Compute expression
|
|
get_token();
|
|
print_expr();
|
|
break;
|
|
|
|
case '\n': // Blank line
|
|
break;
|
|
|
|
default: // Unknown command
|
|
error("Unknown command");
|
|
break;
|
|
}
|
|
}
|
|
|
|
exit_abort();
|
|
|
|
#ifdef AMIGA
|
|
fclose(fin);
|
|
#endif
|
|
if (fout != ferr)
|
|
fclose(fout);
|
|
|
|
#ifdef __riscos__
|
|
Wimp_CommandWindow(-1);
|
|
#endif
|
|
|
|
// Set CPU registers
|
|
TheCPU->SetState(&R64);
|
|
TheCPU1541->SetState(&R1541);
|
|
}
|
|
|
|
|
|
/*
|
|
* Print error message
|
|
*/
|
|
|
|
static void error(const char *s)
|
|
{
|
|
fprintf(ferr, "*** %s\n", s);
|
|
}
|
|
|
|
|
|
/*
|
|
* CTRL-C pressed?
|
|
*/
|
|
|
|
static bool WasAborted;
|
|
|
|
#ifdef HAVE_SIGACTION
|
|
struct sigaction my_sa;
|
|
#endif
|
|
|
|
static void handle_abort(...)
|
|
{
|
|
WasAborted = true;
|
|
#if !defined(HAVE_SIGACTION) && defined(HAVE_SIGNAL)
|
|
#ifdef __riscos__
|
|
signal(SIGINT, (Handler*) handle_abort);
|
|
#else
|
|
signal(SIGINT, (sighandler_t) handle_abort);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static void init_abort(void)
|
|
{
|
|
WasAborted = false;
|
|
#if defined(HAVE_SIGACTION)
|
|
my_sa.sa_handler = (void (*)(int))handle_abort;
|
|
my_sa.sa_flags = 0;
|
|
sigemptyset(&my_sa.sa_mask);
|
|
sigaction(SIGINT, &my_sa, NULL);
|
|
#elif defined(HAVE_SIGNAL)
|
|
#ifdef __riscos__
|
|
signal(SIGINT, (Handler*) handle_abort);
|
|
#else
|
|
signal(SIGINT, (sighandler_t) handle_abort);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
static void exit_abort(void)
|
|
{
|
|
#if defined(HAVE_SIGACTION)
|
|
my_sa.sa_handler = SIG_DFL;
|
|
sigaction(SIGINT, &my_sa, NULL);
|
|
#elif defined(HAVE_SIGNAL)
|
|
signal(SIGINT, SIG_DFL);
|
|
#endif
|
|
}
|
|
|
|
static bool aborted(void)
|
|
{
|
|
bool ret = WasAborted;
|
|
|
|
WasAborted = false;
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Read a line from the keyboard
|
|
*/
|
|
|
|
static void read_line(void)
|
|
{
|
|
#ifdef __riscos__
|
|
OS_ReadLine(in_ptr = input, INPUT_LENGTH, 0, 255, 0);
|
|
#else
|
|
fgets(in_ptr = input, INPUT_LENGTH, fin);
|
|
#endif
|
|
}
|
|
|
|
|
|
/*
|
|
* Read a character from the input line
|
|
*/
|
|
|
|
static char get_char(void)
|
|
{
|
|
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
|
|
*/
|
|
|
|
static enum Token get_token(void)
|
|
{
|
|
char c;
|
|
|
|
// Skip spaces
|
|
while ((c = get_char()) == ' ') ;
|
|
|
|
switch (c) {
|
|
case '\n':
|
|
return the_token = T_END;
|
|
case '(':
|
|
return the_token = T_LPAREN;
|
|
case ')':
|
|
return the_token = T_RPAREN;
|
|
case '+':
|
|
return the_token = T_ADD;
|
|
case '-':
|
|
return the_token = T_SUB;
|
|
case '*':
|
|
return the_token = T_MUL;
|
|
case '/':
|
|
return the_token = T_DIV;
|
|
case ',':
|
|
return the_token = T_COMMA;
|
|
case '#':
|
|
return the_token = T_IMMED;
|
|
case 'x':
|
|
return the_token = T_X;
|
|
case 'y':
|
|
return the_token = T_Y;
|
|
case 'p':
|
|
if (get_char() == 'c')
|
|
return the_token = T_PC;
|
|
else {
|
|
error("Unrecognized token");
|
|
return the_token = T_NULL;
|
|
}
|
|
case 's':
|
|
if (get_char() == 'p')
|
|
return the_token = T_SP;
|
|
else {
|
|
error("Unrecognized token");
|
|
return the_token = T_NULL;
|
|
}
|
|
case '0': case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
|
|
put_back(c);
|
|
the_number = get_number();
|
|
return the_token = T_NUMBER;
|
|
case '"':
|
|
return the_token = get_string(the_string);
|
|
default:
|
|
error("Unrecognized token");
|
|
return the_token = T_NULL;
|
|
}
|
|
}
|
|
|
|
static enum Token get_reg_token(void)
|
|
{
|
|
char c;
|
|
|
|
// Skip spaces
|
|
while ((c = get_char()) == ' ') ;
|
|
|
|
switch (c) {
|
|
case '\n':
|
|
return the_token = T_END;
|
|
case 'a':
|
|
return the_token = T_A;
|
|
case 'd':
|
|
if (get_char() == 'r')
|
|
return the_token = T_DR;
|
|
else {
|
|
error("Unrecognized token");
|
|
return the_token = T_NULL;
|
|
}
|
|
case 'p':
|
|
if ((c = get_char()) == 'c')
|
|
return the_token = T_PC;
|
|
else if (c == 'r')
|
|
return the_token = T_PR;
|
|
else {
|
|
error("Unrecognized token");
|
|
return the_token = T_NULL;
|
|
}
|
|
case 's':
|
|
if (get_char() == 'p')
|
|
return the_token = T_SP;
|
|
else {
|
|
error("Unrecognized token");
|
|
return the_token = T_NULL;
|
|
}
|
|
case 'x':
|
|
return the_token = T_X;
|
|
case 'y':
|
|
return the_token = T_Y;
|
|
default:
|
|
error("Unrecognized token");
|
|
return the_token = T_NULL;
|
|
}
|
|
}
|
|
|
|
static uint16 get_number(void)
|
|
{
|
|
char c;
|
|
uint16 i = 0;
|
|
|
|
while (((c = get_char()) >= '0') && (c <= '9') || (c >= 'a') && (c <= 'f'))
|
|
if (c < 'a')
|
|
i = (i << 4) + (c - '0');
|
|
else
|
|
i = (i << 4) + (c - 'a' + 10);
|
|
|
|
put_back(c);
|
|
return i;
|
|
}
|
|
|
|
static enum Token get_string(char *str)
|
|
{
|
|
char c;
|
|
|
|
while ((c = get_char()) != '\n') {
|
|
if (c == '"') {
|
|
*str = 0;
|
|
return T_STRING;
|
|
}
|
|
*str++ = c;
|
|
}
|
|
|
|
error("Unterminated string");
|
|
return T_NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* expression = term {(ADD | SUB) term}
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool expression(uint16 *number)
|
|
{
|
|
uint16 accu, trm;
|
|
|
|
if (!term(&accu))
|
|
return false;
|
|
|
|
for (;;)
|
|
switch (the_token) {
|
|
case T_ADD:
|
|
get_token();
|
|
if (!term(&trm))
|
|
return false;
|
|
accu += trm;
|
|
break;
|
|
|
|
case T_SUB:
|
|
get_token();
|
|
if (!term(&trm))
|
|
return false;
|
|
accu -= trm;
|
|
break;
|
|
|
|
default:
|
|
*number = accu;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* term = factor {(MUL | DIV) factor}
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool term(uint16 *number)
|
|
{
|
|
uint16 accu, fact;
|
|
|
|
if (!factor(&accu))
|
|
return false;
|
|
|
|
for (;;)
|
|
switch (the_token) {
|
|
case T_MUL:
|
|
get_token();
|
|
if (!factor(&fact))
|
|
return false;
|
|
accu *= fact;
|
|
break;
|
|
|
|
case T_DIV:
|
|
get_token();
|
|
if (!factor(&fact))
|
|
return false;
|
|
if (fact == 0) {
|
|
error("Division by 0");
|
|
return false;
|
|
}
|
|
accu /= fact;
|
|
break;
|
|
|
|
default:
|
|
*number = accu;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* factor = NUMBER | PC | SP | LPAREN expression RPAREN
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool factor(uint16 *number)
|
|
{
|
|
switch (the_token) {
|
|
case T_NUMBER:
|
|
*number = the_number;
|
|
get_token();
|
|
return true;
|
|
|
|
case T_PC:
|
|
get_token();
|
|
*number = access_1541 ? R1541.pc : R64.pc;
|
|
return true;
|
|
|
|
case T_SP:
|
|
get_token();
|
|
*number = access_1541 ? R1541.sp : R64.sp;
|
|
return true;
|
|
|
|
case T_LPAREN:
|
|
get_token();
|
|
if (expression(number))
|
|
if (the_token == T_RPAREN) {
|
|
get_token();
|
|
return true;
|
|
} else {
|
|
error("Missing ')'");
|
|
return false;
|
|
}
|
|
else {
|
|
error("Error in expression");
|
|
return false;
|
|
}
|
|
|
|
case T_END:
|
|
error("Required argument missing");
|
|
return false;
|
|
|
|
default:
|
|
error("'pc', 'sp', '(' or number expected");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* address_args = [expression] END
|
|
*
|
|
* Read start to "address"
|
|
*
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool address_args(void)
|
|
{
|
|
if (the_token == T_END)
|
|
return true;
|
|
else {
|
|
if (!expression(&address))
|
|
return false;
|
|
return the_token == T_END;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* range_args = [expression] [[COMMA] expression] END
|
|
*
|
|
* Read start address to "address", end address to "end_address"
|
|
*
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool range_args(int def_range)
|
|
{
|
|
end_address = address + def_range;
|
|
|
|
if (the_token == T_END)
|
|
return true;
|
|
else {
|
|
if (!expression(&address))
|
|
return false;
|
|
end_address = address + def_range;
|
|
if (the_token == T_END)
|
|
return true;
|
|
else {
|
|
if (the_token == T_COMMA) get_token();
|
|
if (!expression(&end_address))
|
|
return false;
|
|
return the_token == T_END;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* instr_args = END
|
|
* | IMMED NUMBER END
|
|
* | NUMBER [COMMA (X | Y)] END
|
|
* | LPAREN NUMBER (RPAREN [COMMA Y] | COMMA X RPAREN) END
|
|
*
|
|
* Read arguments of a 6510 instruction, determine address and addressing mode
|
|
*
|
|
* true: OK, false: Error
|
|
*/
|
|
|
|
static bool instr_args(uint16 *number, char *mode)
|
|
{
|
|
switch (the_token) {
|
|
|
|
case T_END:
|
|
*mode = A_IMPL;
|
|
return true;
|
|
|
|
case T_IMMED:
|
|
get_token();
|
|
if (the_token == T_NUMBER) {
|
|
*number = the_number;
|
|
*mode = A_IMM;
|
|
get_token();
|
|
return the_token == T_END;
|
|
} else {
|
|
error("Number expected");
|
|
return false;
|
|
}
|
|
|
|
case T_NUMBER:
|
|
*number = the_number;
|
|
get_token();
|
|
switch (the_token) {
|
|
|
|
case T_END:
|
|
if (*number < 0x100)
|
|
*mode = A_ZERO;
|
|
else
|
|
*mode = A_ABS;
|
|
return true;
|
|
|
|
case T_COMMA:
|
|
get_token();
|
|
switch (the_token) {
|
|
|
|
case T_X:
|
|
get_token();
|
|
if (*number < 0x100)
|
|
*mode = A_ZEROX;
|
|
else
|
|
*mode = A_ABSX;
|
|
return the_token == T_END;
|
|
|
|
case T_Y:
|
|
get_token();
|
|
if (*number < 0x100)
|
|
*mode = A_ZEROY;
|
|
else
|
|
*mode = A_ABSY;
|
|
return the_token == T_END;
|
|
|
|
default:
|
|
error("Illegal index register");
|
|
return false;
|
|
}
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
case T_LPAREN:
|
|
get_token();
|
|
if (the_token == T_NUMBER) {
|
|
*number = the_number;
|
|
get_token();
|
|
switch (the_token) {
|
|
|
|
case T_RPAREN:
|
|
get_token();
|
|
switch (the_token) {
|
|
|
|
case T_END:
|
|
*mode = A_IND;
|
|
return true;
|
|
|
|
case T_COMMA:
|
|
get_token();
|
|
if (the_token == T_Y) {
|
|
*mode = A_INDY;
|
|
get_token();
|
|
return the_token == T_END;
|
|
} else {
|
|
error("Only 'y' index register allowed");
|
|
return false;
|
|
}
|
|
|
|
default:
|
|
error("Illegal characters after ')'");
|
|
return false;
|
|
}
|
|
|
|
case T_COMMA:
|
|
get_token();
|
|
if (the_token == T_X) {
|
|
get_token();
|
|
if (the_token == T_RPAREN) {
|
|
*mode = A_INDX;
|
|
get_token();
|
|
return the_token == T_END;
|
|
} else {
|
|
error("')' expected");
|
|
return false;
|
|
}
|
|
} else {
|
|
error("Only 'x' index register allowed");
|
|
return false;
|
|
}
|
|
|
|
default:
|
|
error("')' or ',' expected");
|
|
return false;
|
|
}
|
|
} else {
|
|
error("Number expected");
|
|
return false;
|
|
}
|
|
|
|
default:
|
|
error("'(', '#' or number expected");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Display help
|
|
* h
|
|
*/
|
|
|
|
static void help(void)
|
|
{
|
|
fprintf(fout, "a [start] Assemble\n"
|
|
"b [start] [end] Binary dump\n"
|
|
"c start end dest Compare memory\n"
|
|
"d [start] [end] Disassemble\n"
|
|
"e Show interrupt vectors\n"
|
|
"f start end byte Fill memory\n"
|
|
"i [start] [end] ASCII/PETSCII dump\n"
|
|
"k [config] Show/set C64 memory configuration\n"
|
|
"l start \"file\" Load data\n"
|
|
"m [start] [end] Memory dump\n"
|
|
"n [start] [end] Screen code dump\n"
|
|
"o [\"file\"] Redirect output\n"
|
|
"p [start] [end] Sprite dump\n"
|
|
"r [reg value] Show/set CPU registers\n"
|
|
"s start end \"file\" Save data\n"
|
|
"t start end dest Transfer memory\n"
|
|
"vc1 View CIA 1 state\n"
|
|
"vc2 View CIA 2 state\n"
|
|
"vf View 1541 state\n"
|
|
"vs View SID state\n"
|
|
"vv View VIC state\n"
|
|
"x Return to Frodo\n"
|
|
": addr {byte} Modify memory\n"
|
|
"1541 Switch to 1541\n"
|
|
"64 Switch to C64\n"
|
|
"? expression Calculate expression\n");
|
|
}
|
|
|
|
|
|
/*
|
|
* Display/change 6510 registers
|
|
* r [reg value]
|
|
*/
|
|
|
|
static void registers(void)
|
|
{
|
|
enum Token the_reg;
|
|
uint16 value;
|
|
|
|
if (the_token != T_END)
|
|
switch (the_reg = the_token) {
|
|
case T_A:
|
|
case T_X:
|
|
case T_Y:
|
|
case T_PC:
|
|
case T_SP:
|
|
case T_DR:
|
|
case T_PR:
|
|
get_token();
|
|
if (!expression(&value))
|
|
return;
|
|
|
|
switch (the_reg) {
|
|
case T_A:
|
|
if (access_1541)
|
|
R1541.a = value;
|
|
else
|
|
R64.a = value;
|
|
break;
|
|
case T_X:
|
|
if (access_1541)
|
|
R1541.x = value;
|
|
else
|
|
R64.x = value;
|
|
break;
|
|
case T_Y:
|
|
if (access_1541)
|
|
R1541.y = value;
|
|
else
|
|
R64.y = value;
|
|
break;
|
|
case T_PC:
|
|
if (access_1541)
|
|
R1541.pc = value;
|
|
else
|
|
R64.pc = value;
|
|
break;
|
|
case T_SP:
|
|
if (access_1541)
|
|
R1541.sp = (value & 0xff) | 0x0100;
|
|
else
|
|
R64.sp = (value & 0xff) | 0x0100;
|
|
break;
|
|
case T_DR:
|
|
if (!access_1541)
|
|
R64.ddr = value;
|
|
break;
|
|
case T_PR:
|
|
if (!access_1541)
|
|
R64.pr = value;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
display_registers();
|
|
}
|
|
|
|
static void display_registers(void)
|
|
{
|
|
if (access_1541) {
|
|
fprintf(fout, " PC A X Y SP NVDIZC Instruction\n");
|
|
fprintf(fout, "%04lx %02lx %02lx %02lx %04lx %c%c%c%c%c%c ",
|
|
R1541.pc, R1541.a, R1541.x, R1541.y, R1541.sp,
|
|
R1541.p & 0x80 ? '1' : '0', R1541.p & 0x40 ? '1' : '0', R1541.p & 0x08 ? '1' : '0',
|
|
R1541.p & 0x04 ? '1' : '0', R1541.p & 0x02 ? '1' : '0', R1541.p & 0x01 ? '1' : '0');
|
|
disass_line(R1541.pc, SAMReadByte(R1541.pc), SAMReadByte(R1541.pc+1), SAMReadByte(R1541.pc+2));
|
|
} else {
|
|
fprintf(fout, " PC A X Y SP DR PR NVDIZC Instruction\n");
|
|
fprintf(fout, "%04lx %02lx %02lx %02lx %04lx %02lx %02lx %c%c%c%c%c%c ",
|
|
R64.pc, R64.a, R64.x, R64.y, R64.sp, R64.ddr, R64.pr,
|
|
R64.p & 0x80 ? '1' : '0', R64.p & 0x40 ? '1' : '0', R64.p & 0x08 ? '1' : '0',
|
|
R64.p & 0x04 ? '1' : '0', R64.p & 0x02 ? '1' : '0', R64.p & 0x01 ? '1' : '0');
|
|
disass_line(R64.pc, SAMReadByte(R64.pc), SAMReadByte(R64.pc+1), SAMReadByte(R64.pc+2));
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Memory dump
|
|
* m [start] [end]
|
|
*/
|
|
|
|
#define MEMDUMP_BPL 16 // Bytes per line
|
|
|
|
static void memory_dump(void)
|
|
{
|
|
bool done = false;
|
|
short i;
|
|
uint8 mem[MEMDUMP_BPL + 2];
|
|
uint8 byte;
|
|
|
|
mem[MEMDUMP_BPL] = 0;
|
|
|
|
if (!range_args(16 * MEMDUMP_BPL - 1)) // 16 lines unless end address specified
|
|
return;
|
|
|
|
do {
|
|
fprintf(fout, "%04lx:", address);
|
|
for (i=0; i<MEMDUMP_BPL; i++, address++) {
|
|
if (address == end_address) done = true;
|
|
|
|
fprintf(fout, " %02lx", byte = SAMReadByte(address));
|
|
if ((byte >= ' ') && (byte <= '~'))
|
|
mem[i] = conv_from_64(byte);
|
|
else
|
|
mem[i] = '.';
|
|
}
|
|
fprintf(fout, " '%s'\n", mem);
|
|
} while (!done && !aborted());
|
|
}
|
|
|
|
|
|
/*
|
|
* ASCII dump
|
|
* i [start] [end]
|
|
*/
|
|
|
|
#define ASCIIDUMP_BPL 64 // Bytes per line
|
|
|
|
static void ascii_dump(void)
|
|
{
|
|
bool done = false;
|
|
short i;
|
|
uint8 mem[ASCIIDUMP_BPL + 2];
|
|
uint8 byte;
|
|
|
|
mem[ASCIIDUMP_BPL] = 0;
|
|
|
|
if (!range_args(16 * ASCIIDUMP_BPL - 1)) // 16 lines unless end address specified
|
|
return;
|
|
|
|
do {
|
|
fprintf(fout, "%04lx:", address);
|
|
for (i=0; i<ASCIIDUMP_BPL; i++, address++) {
|
|
if (address == end_address) done = true;
|
|
|
|
byte = SAMReadByte(address);
|
|
if ((byte >= ' ') && (byte <= '~'))
|
|
mem[i] = conv_from_64(byte);
|
|
else
|
|
mem[i] = '.';
|
|
}
|
|
fprintf(fout, " '%s'\n", mem);
|
|
} while (!done && !aborted());
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert PETSCII->ASCII
|
|
*/
|
|
|
|
static char conv_from_64(char c)
|
|
{
|
|
if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
|
|
return c ^ 0x20;
|
|
else
|
|
return c;
|
|
}
|
|
|
|
|
|
/*
|
|
* Screen code dump
|
|
* n [start] [end]
|
|
*/
|
|
|
|
#define SCRDUMP_BPL 64 // Bytes per line
|
|
|
|
static void screen_dump(void)
|
|
{
|
|
bool done = false;
|
|
short i;
|
|
uint8 mem[SCRDUMP_BPL + 2];
|
|
uint8 byte;
|
|
|
|
mem[SCRDUMP_BPL] = 0;
|
|
|
|
if (!range_args(16 * SCRDUMP_BPL - 1)) // 16 Zeilen unless end address specified
|
|
return;
|
|
|
|
do {
|
|
fprintf(fout, "%04lx:", address);
|
|
for (i=0; i<SCRDUMP_BPL; i++, address++) {
|
|
if (address == end_address) done = true;
|
|
|
|
byte = SAMReadByte(address);
|
|
if (byte < 90)
|
|
mem[i] = conv_from_scode(byte);
|
|
else
|
|
mem[i] = '.';
|
|
}
|
|
fprintf(fout, " '%s'\n", mem);
|
|
} while (!done && !aborted());
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert screen code->ASCII
|
|
*/
|
|
|
|
static char conv_from_scode(char c)
|
|
{
|
|
c &= 0x7f;
|
|
|
|
if (c <= 31)
|
|
return c + 64;
|
|
else
|
|
if (c >= 64)
|
|
return c + 32;
|
|
else
|
|
return c;
|
|
}
|
|
|
|
|
|
/*
|
|
* Binary dump
|
|
* b [start] [end]
|
|
*/
|
|
|
|
static void binary_dump(void)
|
|
{
|
|
bool done = false;
|
|
char bin[10];
|
|
|
|
bin[8] = 0;
|
|
|
|
if (!range_args(7)) // 8 lines unless end address specified
|
|
return;
|
|
|
|
do {
|
|
if (address == end_address) done = true;
|
|
|
|
byte_to_bin(SAMReadByte(address), bin);
|
|
fprintf(fout, "%04lx: %s\n", address++, bin);
|
|
} while (!done && !aborted());
|
|
}
|
|
|
|
|
|
/*
|
|
* Sprite data dump
|
|
* p [start] [end]
|
|
*/
|
|
|
|
static void sprite_dump(void)
|
|
{
|
|
bool done = false;
|
|
short i;
|
|
char bin[10];
|
|
|
|
bin[8] = 0;
|
|
|
|
if (!range_args(21 * 3 - 1)) // 21 lines unless end address specified
|
|
return;
|
|
|
|
do {
|
|
fprintf(fout, "%04lx: ", address);
|
|
for (i=0; i<3; i++, address++) {
|
|
if (address == end_address) done = true;
|
|
|
|
byte_to_bin(SAMReadByte(address), bin);
|
|
fprintf(fout, "%s", bin);
|
|
}
|
|
fputc('\n', fout);
|
|
} while (!done && !aborted());
|
|
}
|
|
|
|
|
|
/*
|
|
* Convert byte to binary representation
|
|
*/
|
|
|
|
static void byte_to_bin(uint8 byte, char *str)
|
|
{
|
|
short i;
|
|
|
|
for (i=0; i<8; i++, byte<<=1)
|
|
if (byte & 0x80)
|
|
str[i] = '#';
|
|
else
|
|
str[i] = '.';
|
|
}
|
|
|
|
|
|
/*
|
|
* Disassemble
|
|
* d [start] [end]
|
|
*/
|
|
|
|
static void disassemble(void)
|
|
{
|
|
bool done = false;
|
|
short i;
|
|
uint8 op[3];
|
|
uint16 adr;
|
|
|
|
if (!range_args(31)) // 32 bytes unless end address specified
|
|
return;
|
|
|
|
do {
|
|
fprintf(fout, "%04lx:", adr = address);
|
|
for (i=0; i<3; i++, adr++) {
|
|
if (adr == end_address) done = true;
|
|
op[i] = SAMReadByte(adr);
|
|
}
|
|
address += disass_line(address, op[0], op[1], op[2]);
|
|
} while (!done && !aborted());
|
|
}
|
|
|
|
|
|
/*
|
|
* Disassemble one instruction, return length
|
|
*/
|
|
|
|
static int disass_line(uint16 adr, uint8 op, uint8 lo, uint8 hi)
|
|
{
|
|
char mode = adr_mode[op], mnem = mnemonic[op];
|
|
|
|
// Display instruction bytes in hex
|
|
switch (adr_length[mode]) {
|
|
case 1:
|
|
fprintf(fout, " %02lx ", op);
|
|
break;
|
|
|
|
case 2:
|
|
fprintf(fout, " %02lx %02lx ", op, lo);
|
|
break;
|
|
|
|
case 3:
|
|
fprintf(fout, " %02lx %02lx %02lx ", op, lo, hi);
|
|
break;
|
|
}
|
|
|
|
// Tag undocumented opcodes with an asterisk
|
|
if (mnem > M_ILLEGAL)
|
|
fputc('*', fout);
|
|
else
|
|
fputc(' ', fout);
|
|
|
|
// Print mnemonic
|
|
fprintf(fout, "%c%c%c ", mnem_1[mnem], mnem_2[mnem], mnem_3[mnem]);
|
|
|
|
// Pring argument
|
|
switch (mode) {
|
|
case A_IMPL:
|
|
break;
|
|
|
|
case A_ACCU:
|
|
fprintf(fout, "a");
|
|
break;
|
|
|
|
case A_IMM:
|
|
fprintf(fout, "#%02lx", lo);
|
|
break;
|
|
|
|
case A_REL:
|
|
fprintf(fout, "%04lx", ((adr + 2) + (int8)lo) & 0xffff);
|
|
break;
|
|
|
|
case A_ZERO:
|
|
fprintf(fout, "%02lx", lo);
|
|
break;
|
|
|
|
case A_ZEROX:
|
|
fprintf(fout, "%02lx,x", lo);
|
|
break;
|
|
|
|
case A_ZEROY:
|
|
fprintf(fout, "%02lx,y", lo);
|
|
break;
|
|
|
|
case A_ABS:
|
|
fprintf(fout, "%04lx", (hi << 8) | lo);
|
|
break;
|
|
|
|
case A_ABSX:
|
|
fprintf(fout, "%04lx,x", (hi << 8) | lo);
|
|
break;
|
|
|
|
case A_ABSY:
|
|
fprintf(fout, "%04lx,y", (hi << 8) | lo);
|
|
break;
|
|
|
|
case A_IND:
|
|
fprintf(fout, "(%04lx)", (hi << 8) | lo);
|
|
break;
|
|
|
|
case A_INDX:
|
|
fprintf(fout, "(%02lx,x)", lo);
|
|
break;
|
|
|
|
case A_INDY:
|
|
fprintf(fout, "(%02lx),y", lo);
|
|
break;
|
|
}
|
|
|
|
fputc('\n', fout);
|
|
return adr_length[mode];
|
|
}
|
|
|
|
|
|
/*
|
|
* Assemble
|
|
* a [start]
|
|
*/
|
|
|
|
static void assemble(void)
|
|
{
|
|
bool done = false;
|
|
char c1, c2, c3;
|
|
char mnem, mode;
|
|
uint8 opcode;
|
|
uint16 arg;
|
|
int16 rel;
|
|
|
|
// Read parameters
|
|
if (!address_args())
|
|
return;
|
|
|
|
do {
|
|
fprintf(fout, "%04lx> ", address);
|
|
fflush(ferr);
|
|
read_line();
|
|
|
|
c1 = get_char();
|
|
c2 = get_char();
|
|
c3 = get_char();
|
|
|
|
if (c1 != '\n') {
|
|
|
|
if ((mnem = find_mnemonic(c1, c2, c3)) != M_ILLEGAL) {
|
|
|
|
get_token();
|
|
if (instr_args(&arg, &mode)) {
|
|
|
|
// Convert A_IMPL -> A_ACCU if necessary
|
|
if ((mode == A_IMPL) && find_opcode(mnem, A_ACCU, &opcode))
|
|
mode = A_ACCU;
|
|
|
|
// Handle relative addressing seperately
|
|
if (((mode == A_ABS) || (mode == A_ZERO)) && find_opcode(mnem, A_REL, &opcode)) {
|
|
mode = A_REL;
|
|
rel = arg - (address + 2) & 0xffff;
|
|
if ((rel < -128) || (rel > 127)) {
|
|
error("Branch too long");
|
|
continue;
|
|
} else
|
|
arg = rel & 0xff;
|
|
}
|
|
|
|
if (find_opcode(mnem, mode, &opcode)) {
|
|
|
|
// Print disassembled line
|
|
fprintf(fout, "\v%04lx:", address);
|
|
disass_line(address, opcode, arg & 0xff, arg >> 8);
|
|
|
|
switch (adr_length[mode]) {
|
|
case 1:
|
|
SAMWriteByte(address++, opcode);
|
|
break;
|
|
|
|
case 2:
|
|
SAMWriteByte(address++, opcode);
|
|
SAMWriteByte(address++, arg);
|
|
break;
|
|
|
|
case 3:
|
|
SAMWriteByte(address++, opcode);
|
|
SAMWriteByte(address++, arg & 0xff);
|
|
SAMWriteByte(address++, arg >> 8);
|
|
break;
|
|
|
|
default:
|
|
error("Internal error");
|
|
break;
|
|
}
|
|
|
|
} else
|
|
error("Addressing mode not supported by instruction");
|
|
|
|
} else
|
|
error("Unrecognized addressing mode");
|
|
|
|
} else
|
|
error("Unknown instruction");
|
|
|
|
} else // Input is terminated with a blank line
|
|
done = true;
|
|
} while (!done);
|
|
}
|
|
|
|
|
|
/*
|
|
* Find mnemonic code to three letters
|
|
* M_ILLEGAL: No matching mnemonic found
|
|
*/
|
|
|
|
static char find_mnemonic(char op1, char op2, char op3)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<M_MAXIMUM; i++)
|
|
if ((mnem_1[i] == op1) && (mnem_2[i] == op2) && (mnem_3[i] == op3))
|
|
return i;
|
|
|
|
return M_ILLEGAL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Determine opcode of an instruction given mnemonic and addressing mode
|
|
* true: OK, false: Mnemonic can't have specified addressing mode
|
|
*/
|
|
|
|
static bool find_opcode(char mnem, char mode, uint8 *opcode)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i<256; i++)
|
|
if ((mnemonic[i] == mnem) && (adr_mode[i] == mode)) {
|
|
*opcode = i;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
* Show/set memory configuration
|
|
* k [config]
|
|
*/
|
|
|
|
static void mem_config(void)
|
|
{
|
|
uint16 con;
|
|
|
|
if (the_token != T_END)
|
|
if (!expression(&con))
|
|
return;
|
|
else
|
|
TheCPU->ExtConfig = con;
|
|
else
|
|
con = TheCPU->ExtConfig;
|
|
|
|
fprintf(fout, "Configuration: %ld\n", con & 7);
|
|
fprintf(fout, "A000-BFFF: %s\n", (con & 3) == 3 ? "Basic" : "RAM");
|
|
fprintf(fout, "D000-DFFF: %s\n", (con & 3) ? ((con & 4) ? "I/O" : "Char") : "RAM");
|
|
fprintf(fout, "E000-FFFF: %s\n", (con & 2) ? "Kernal" : "RAM");
|
|
}
|
|
|
|
|
|
/*
|
|
* Fill
|
|
* f start end byte
|
|
*/
|
|
|
|
static void fill(void)
|
|
{
|
|
bool done = false;
|
|
uint16 adr, end_adr, value;
|
|
|
|
if (!expression(&adr))
|
|
return;
|
|
if (!expression(&end_adr))
|
|
return;
|
|
if (!expression(&value))
|
|
return;
|
|
|
|
do {
|
|
if (adr == end_adr) done = true;
|
|
|
|
SAMWriteByte(adr++, value);
|
|
} while (!done);
|
|
}
|
|
|
|
|
|
/*
|
|
* Compare
|
|
* c start end dest
|
|
*/
|
|
|
|
static void compare(void)
|
|
{
|
|
bool done = false;
|
|
uint16 adr, end_adr, dest;
|
|
int num = 0;
|
|
|
|
if (!expression(&adr))
|
|
return;
|
|
if (!expression(&end_adr))
|
|
return;
|
|
if (!expression(&dest))
|
|
return;
|
|
|
|
do {
|
|
if (adr == end_adr) done = true;
|
|
|
|
if (SAMReadByte(adr) != SAMReadByte(dest)) {
|
|
fprintf(fout, "%04lx ", adr);
|
|
num++;
|
|
if (!(num & 7))
|
|
fputc('\n', fout);
|
|
}
|
|
adr++; dest++;
|
|
} while (!done && !aborted());
|
|
|
|
if (num & 7)
|
|
fputc('\n', fout);
|
|
fprintf(fout, "%ld byte(s) different\n", num);
|
|
}
|
|
|
|
|
|
/*
|
|
* Transfer memory
|
|
* t start end dest
|
|
*/
|
|
|
|
static void transfer(void)
|
|
{
|
|
bool done = false;
|
|
uint16 adr, end_adr, dest;
|
|
|
|
if (!expression(&adr))
|
|
return;
|
|
if (!expression(&end_adr))
|
|
return;
|
|
if (!expression(&dest))
|
|
return;
|
|
|
|
if (dest < adr)
|
|
do {
|
|
if (adr == end_adr) done = true;
|
|
SAMWriteByte(dest++, SAMReadByte(adr++));
|
|
} while (!done);
|
|
else {
|
|
dest += end_adr - adr;
|
|
do {
|
|
if (adr == end_adr) done = true;
|
|
SAMWriteByte(dest--, SAMReadByte(end_adr--));
|
|
} while (!done);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Change memory
|
|
* : addr {byte}
|
|
*/
|
|
|
|
static void modify(void)
|
|
{
|
|
uint16 adr, val;
|
|
|
|
if (!expression(&adr))
|
|
return;
|
|
|
|
while (the_token != T_END)
|
|
if (expression(&val))
|
|
SAMWriteByte(adr++, val);
|
|
else
|
|
return;
|
|
}
|
|
|
|
|
|
/*
|
|
* Compute and display expression
|
|
* ? expression
|
|
*/
|
|
|
|
static void print_expr(void)
|
|
{
|
|
uint16 val;
|
|
|
|
if (!expression(&val))
|
|
return;
|
|
|
|
fprintf(fout, "Hex: %04lx\nDec: %lu\n", val, val);
|
|
}
|
|
|
|
|
|
/*
|
|
* Redirect output
|
|
* o [file]
|
|
*/
|
|
|
|
static void redir_output(void)
|
|
{
|
|
// Close old file
|
|
if (fout != ferr) {
|
|
fclose(fout);
|
|
fout = ferr;
|
|
return;
|
|
}
|
|
|
|
// No argument given?
|
|
if (the_token == T_END)
|
|
return;
|
|
|
|
// Otherwise open file
|
|
if (the_token == T_STRING) {
|
|
if (!(fout = fopen(the_string, "w")))
|
|
error("Unable to open file");
|
|
} else
|
|
error("'\"' around file name expected");
|
|
}
|
|
|
|
|
|
/*
|
|
* Display interrupt vectors
|
|
*/
|
|
|
|
static void int_vectors(void)
|
|
{
|
|
fprintf(fout, " IRQ BRK NMI\n");
|
|
fprintf(fout, "%d : %04lx %04lx %04lx\n",
|
|
access_1541 ? 6502 : 6510,
|
|
SAMReadByte(0xffff) << 8 | SAMReadByte(0xfffe),
|
|
SAMReadByte(0xffff) << 8 | SAMReadByte(0xfffe),
|
|
SAMReadByte(0xfffb) << 8 | SAMReadByte(0xfffa));
|
|
|
|
if (!access_1541 && TheCPU->ExtConfig & 2)
|
|
fprintf(fout, "Kernal: %04lx %04lx %04lx\n",
|
|
SAMReadByte(0x0315) << 8 | SAMReadByte(0x0314),
|
|
SAMReadByte(0x0317) << 8 | SAMReadByte(0x0316),
|
|
SAMReadByte(0x0319) << 8 | SAMReadByte(0x0318));
|
|
}
|
|
|
|
|
|
/*
|
|
* Display state of custom chips
|
|
*/
|
|
|
|
static void view_state(void)
|
|
{
|
|
switch (get_char()) {
|
|
case 'c': // CIA
|
|
view_cia_state();
|
|
break;
|
|
|
|
case 's': // SID
|
|
view_sid_state();
|
|
break;
|
|
|
|
case 'v': // VIC
|
|
view_vic_state();
|
|
break;
|
|
|
|
case 'f': // Floppy
|
|
view_1541_state();
|
|
break;
|
|
|
|
default:
|
|
error("Unknown command");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void view_cia_state(void)
|
|
{
|
|
MOS6526State cs;
|
|
|
|
switch (get_char()) {
|
|
case '1':
|
|
TheCIA1->GetState(&cs);
|
|
break;
|
|
case '2':
|
|
TheCIA2->GetState(&cs);
|
|
break;
|
|
default:
|
|
error("Unknown command");
|
|
return;
|
|
}
|
|
|
|
fprintf(fout, "Timer A : %s\n", cs.cra & 1 ? "On" : "Off");
|
|
fprintf(fout, " Counter : %04lx Latch: %04lx\n", (cs.ta_hi << 8) | cs.ta_lo, cs.latcha);
|
|
fprintf(fout, " Run mode: %s\n", cs.cra & 8 ? "One-shot" : "Continuous");
|
|
fprintf(fout, " Input : %s\n", cs.cra & 0x20 ? "CNT" : "Phi2");
|
|
fprintf(fout, " Output : ");
|
|
if (cs.cra & 2)
|
|
if (cs.cra & 4)
|
|
fprintf(fout, "PB6 Toggle\n\n");
|
|
else
|
|
fprintf(fout, "PB6 Pulse\n\n");
|
|
else
|
|
fprintf(fout, "None\n\n");
|
|
|
|
fprintf(fout, "Timer B : %s\n", cs.crb & 1 ? "On" : "Off");
|
|
fprintf(fout, " Counter : %04lx Latch: %04lx\n", (cs.tb_hi << 8) | cs.tb_lo, cs.latchb);
|
|
fprintf(fout, " Run mode: %s\n", cs.crb & 8 ? "One-shot" : "Continuous");
|
|
fprintf(fout, " Input : ");
|
|
if (cs.crb & 0x40)
|
|
if (cs.crb & 0x20)
|
|
fprintf(fout, "Timer A underflow (CNT high)\n");
|
|
else
|
|
fprintf(fout, "Timer A underflow\n");
|
|
else
|
|
if (cs.crb & 0x20)
|
|
fprintf(fout, "CNT\n");
|
|
else
|
|
fprintf(fout, "Phi2\n");
|
|
fprintf(fout, " Output : ");
|
|
if (cs.crb & 2)
|
|
if (cs.crb & 4)
|
|
fprintf(fout, "PB7 Toggle\n\n");
|
|
else
|
|
fprintf(fout, "PB7 Pulse\n\n");
|
|
else
|
|
fprintf(fout, "None\n\n");
|
|
|
|
fprintf(fout, "TOD : %lx%lx:%lx%lx:%lx%lx.%lx %s\n",
|
|
(cs.tod_hr >> 4) & 1, cs.tod_hr & 0x0f,
|
|
(cs.tod_min >> 4) & 7, cs.tod_min & 0x0f,
|
|
(cs.tod_sec >> 4) & 7, cs.tod_sec & 0x0f,
|
|
cs.tod_10ths & 0x0f, cs.tod_hr & 0x80 ? "PM" : "AM");
|
|
fprintf(fout, "Alarm : %lx%lx:%lx%lx:%lx%lx.%lx %s\n",
|
|
(cs.alm_hr >> 4) & 1, cs.alm_hr & 0x0f,
|
|
(cs.alm_min >> 4) & 7, cs.alm_min & 0x0f,
|
|
(cs.alm_sec >> 4) & 7, cs.alm_sec & 0x0f,
|
|
cs.alm_10ths & 0x0f, cs.alm_hr & 0x80 ? "PM" : "AM");
|
|
fprintf(fout, "TOD input : %s\n", cs.cra & 0x80 ? "50Hz" : "60Hz");
|
|
fprintf(fout, "Write to : %s registers\n\n", cs.crb & 0x80 ? "Alarm" : "TOD");
|
|
|
|
fprintf(fout, "Serial data : %02lx\n", cs.sdr);
|
|
fprintf(fout, "Serial mode : %s\n\n", cs.cra & 0x40 ? "Output" : "Input");
|
|
|
|
fprintf(fout, "Pending int.: ");
|
|
dump_cia_ints(cs.int_data);
|
|
fprintf(fout, "Enabled int.: ");
|
|
dump_cia_ints(cs.int_mask);
|
|
}
|
|
|
|
static void dump_cia_ints(uint8 i)
|
|
{
|
|
if (i & 0x1f) {
|
|
if (i & 1) fprintf(fout, "TA ");
|
|
if (i & 2) fprintf(fout, "TB ");
|
|
if (i & 4) fprintf(fout, "Alarm ");
|
|
if (i & 8) fprintf(fout, "Serial ");
|
|
if (i & 0x10) fprintf(fout, "Flag");
|
|
} else
|
|
fprintf(fout, "None");
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
static void view_sid_state(void)
|
|
{
|
|
MOS6581State ss;
|
|
|
|
TheSID->GetState(&ss);
|
|
|
|
fprintf(fout, "Voice 1\n");
|
|
fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_1 << 8) | ss.freq_lo_1);
|
|
fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_1 & 0x0f) << 8) | ss.pw_lo_1);
|
|
fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_1 >> 4, ss.AD_1 & 0x0f, ss.SR_1 >> 4, ss.SR_1 & 0x0f);
|
|
fprintf(fout, " Waveform : ");
|
|
dump_sid_waveform(ss.ctrl_1);
|
|
fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_1 & 0x01 ? "On " : "Off", ss.ctrl_1 & 0x04 ? "On" : "Off");
|
|
fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_1 & 0x08 ? "On " : "Off", ss.ctrl_1 & 0x02 ? "On" : "Off");
|
|
fprintf(fout, " Filter : %s\n", ss.res_filt & 0x01 ? "On" : "Off");
|
|
|
|
fprintf(fout, "\nVoice 2\n");
|
|
fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_2 << 8) | ss.freq_lo_2);
|
|
fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_2 & 0x0f) << 8) | ss.pw_lo_2);
|
|
fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_2 >> 4, ss.AD_2 & 0x0f, ss.SR_2 >> 4, ss.SR_2 & 0x0f);
|
|
fprintf(fout, " Waveform : ");
|
|
dump_sid_waveform(ss.ctrl_2);
|
|
fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_2 & 0x01 ? "On " : "Off", ss.ctrl_2 & 0x04 ? "On" : "Off");
|
|
fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_2 & 0x08 ? "On " : "Off", ss.ctrl_2 & 0x02 ? "On" : "Off");
|
|
fprintf(fout, " Filter : %s\n", ss.res_filt & 0x02 ? "On" : "Off");
|
|
|
|
fprintf(fout, "\nVoice 3\n");
|
|
fprintf(fout, " Frequency : %04lx\n", (ss.freq_hi_3 << 8) | ss.freq_lo_3);
|
|
fprintf(fout, " Pulse Width: %04lx\n", ((ss.pw_hi_3 & 0x0f) << 8) | ss.pw_lo_3);
|
|
fprintf(fout, " Env. (ADSR): %lx %lx %lx %lx\n", ss.AD_3 >> 4, ss.AD_3 & 0x0f, ss.SR_3 >> 4, ss.SR_3 & 0x0f);
|
|
fprintf(fout, " Waveform : ");
|
|
dump_sid_waveform(ss.ctrl_3);
|
|
fprintf(fout, " Gate : %s Ring mod.: %s\n", ss.ctrl_3 & 0x01 ? "On " : "Off", ss.ctrl_3 & 0x04 ? "On" : "Off");
|
|
fprintf(fout, " Test bit : %s Synchron.: %s\n", ss.ctrl_3 & 0x08 ? "On " : "Off", ss.ctrl_3 & 0x02 ? "On" : "Off");
|
|
fprintf(fout, " Filter : %s Mute : %s\n", ss.res_filt & 0x04 ? "On" : "Off", ss.mode_vol & 0x80 ? "Yes" : "No");
|
|
|
|
fprintf(fout, "\nFilters/Volume\n");
|
|
fprintf(fout, " Frequency: %04lx\n", (ss.fc_hi << 3) | (ss.fc_lo & 0x07));
|
|
fprintf(fout, " Resonance: %lx\n", ss.res_filt >> 4);
|
|
fprintf(fout, " Mode : ");
|
|
if (ss.mode_vol & 0x70) {
|
|
if (ss.mode_vol & 0x10) fprintf(fout, "Low-pass ");
|
|
if (ss.mode_vol & 0x20) fprintf(fout, "Band-pass ");
|
|
if (ss.mode_vol & 0x40) fprintf(fout, "High-pass");
|
|
} else
|
|
fprintf(fout, "None");
|
|
fprintf(fout, "\n Volume : %lx\n", ss.mode_vol & 0x0f);
|
|
}
|
|
|
|
static void dump_sid_waveform(uint8 wave)
|
|
{
|
|
if (wave & 0xf0) {
|
|
if (wave & 0x10) fprintf(fout, "Triangle ");
|
|
if (wave & 0x20) fprintf(fout, "Sawtooth ");
|
|
if (wave & 0x40) fprintf(fout, "Rectangle ");
|
|
if (wave & 0x80) fprintf(fout, "Noise");
|
|
} else
|
|
fprintf(fout, "None");
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
static void view_vic_state(void)
|
|
{
|
|
MOS6569State vs;
|
|
short i;
|
|
|
|
TheVIC->GetState(&vs);
|
|
|
|
fprintf(fout, "Raster line : %04lx\n", vs.raster | ((vs.ctrl1 & 0x80) << 1));
|
|
fprintf(fout, "IRQ raster line : %04lx\n\n", vs.irq_raster);
|
|
|
|
fprintf(fout, "X scroll : %ld\n", vs.ctrl2 & 7);
|
|
fprintf(fout, "Y scroll : %ld\n", vs.ctrl1 & 7);
|
|
fprintf(fout, "Horizontal border : %ld columns\n", vs.ctrl2 & 8 ? 40 : 38);
|
|
fprintf(fout, "Vertical border : %ld rows\n\n", vs.ctrl1 & 8 ? 25 : 24);
|
|
|
|
fprintf(fout, "Display mode : ");
|
|
switch (((vs.ctrl1 >> 4) & 6) | ((vs.ctrl2 >> 4) & 1)) {
|
|
case 0:
|
|
fprintf(fout, "Standard text\n");
|
|
break;
|
|
case 1:
|
|
fprintf(fout, "Multicolor text\n");
|
|
break;
|
|
case 2:
|
|
fprintf(fout, "Standard bitmap\n");
|
|
break;
|
|
case 3:
|
|
fprintf(fout, "Multicolor bitmap\n");
|
|
break;
|
|
case 4:
|
|
fprintf(fout, "ECM text\n");
|
|
break;
|
|
case 5:
|
|
fprintf(fout, "Invalid text (ECM+MCM)\n");
|
|
break;
|
|
case 6:
|
|
fprintf(fout, "Invalid bitmap (ECM+BMM)\n");
|
|
break;
|
|
case 7:
|
|
fprintf(fout, "Invalid bitmap (ECM+BMM+ECM)\n");
|
|
break;
|
|
}
|
|
fprintf(fout, "Sequencer state : %s\n", vs.display_state ? "Display" : "Idle");
|
|
fprintf(fout, "Bad line state : %s\n", vs.bad_line ? "Yes" : "No");
|
|
fprintf(fout, "Bad lines enabled : %s\n", vs.bad_line_enable ? "Yes" : "No");
|
|
fprintf(fout, "Video counter : %04lx\n", vs.vc);
|
|
fprintf(fout, "Video counter base: %04lx\n", vs.vc_base);
|
|
fprintf(fout, "Row counter : %ld\n\n", vs.rc);
|
|
|
|
fprintf(fout, "VIC bank : %04lx-%04lx\n", vs.bank_base, vs.bank_base + 0x3fff);
|
|
fprintf(fout, "Video matrix base : %04lx\n", vs.matrix_base);
|
|
fprintf(fout, "Character base : %04lx\n", vs.char_base);
|
|
fprintf(fout, "Bitmap base : %04lx\n\n", vs.bitmap_base);
|
|
|
|
fprintf(fout, " Spr.0 Spr.1 Spr.2 Spr.3 Spr.4 Spr.5 Spr.6 Spr.7\n");
|
|
fprintf(fout, "Enabled: "); dump_spr_flags(vs.me);
|
|
fprintf(fout, "Data : %04lx %04lx %04lx %04lx %04lx %04lx %04lx %04lx\n",
|
|
vs.sprite_base[0], vs.sprite_base[1], vs.sprite_base[2], vs.sprite_base[3],
|
|
vs.sprite_base[4], vs.sprite_base[5], vs.sprite_base[6], vs.sprite_base[7]);
|
|
fprintf(fout, "MC : %02lx %02lx %02lx %02lx %02lx %02lx %02lx %02lx\n",
|
|
vs.mc[0], vs.mc[1], vs.mc[2], vs.mc[3], vs.mc[4], vs.mc[5], vs.mc[6], vs.mc[7]);
|
|
|
|
fprintf(fout, "Mode : ");
|
|
for (i=0; i<8; i++)
|
|
if (vs.mmc & (1<<i))
|
|
fprintf(fout, "Multi ");
|
|
else
|
|
fprintf(fout, "Std. ");
|
|
|
|
fprintf(fout, "\nX-Exp. : "); dump_spr_flags(vs.mxe);
|
|
fprintf(fout, "Y-Exp. : "); dump_spr_flags(vs.mye);
|
|
|
|
fprintf(fout, "Prio. : ");
|
|
for (i=0; i<8; i++)
|
|
if (vs.mdp & (1<<i))
|
|
fprintf(fout, "Back ");
|
|
else
|
|
fprintf(fout, "Fore ");
|
|
|
|
fprintf(fout, "\nSS Coll: "); dump_spr_flags(vs.mm);
|
|
fprintf(fout, "SD Coll: "); dump_spr_flags(vs.md);
|
|
|
|
fprintf(fout, "\nPending interrupts: ");
|
|
dump_vic_ints(vs.irq_flag);
|
|
fprintf(fout, "Enabled interrupts: ");
|
|
dump_vic_ints(vs.irq_mask);
|
|
}
|
|
|
|
static void dump_spr_flags(uint8 f)
|
|
{
|
|
short i;
|
|
|
|
for (i=0; i<8; i++)
|
|
if (f & (1<<i))
|
|
fprintf(fout, "Yes ");
|
|
else
|
|
fprintf(fout, "No ");
|
|
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
static void dump_vic_ints(uint8 i)
|
|
{
|
|
if (i & 0x1f) {
|
|
if (i & 1) fprintf(fout, "Raster ");
|
|
if (i & 2) fprintf(fout, "Spr-Data ");
|
|
if (i & 4) fprintf(fout, "Spr-Spr ");
|
|
if (i & 8) fprintf(fout, "Lightpen");
|
|
} else
|
|
fprintf(fout, "None");
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
static void view_1541_state(void)
|
|
{
|
|
fprintf(fout, "VIA 1:\n");
|
|
fprintf(fout, " Timer 1 Counter: %04x Latch: %04x\n", R1541.via1_t1c, R1541.via1_t1l);
|
|
fprintf(fout, " Timer 2 Counter: %04x Latch: %04x\n", R1541.via1_t2c, R1541.via1_t2l);
|
|
fprintf(fout, " ACR: %02x\n", R1541.via1_acr);
|
|
fprintf(fout, " PCR: %02x\n", R1541.via1_pcr);
|
|
fprintf(fout, " Pending interrupts: ");
|
|
dump_via_ints(R1541.via1_ifr);
|
|
fprintf(fout, " Enabled interrupts: ");
|
|
dump_via_ints(R1541.via1_ier);
|
|
|
|
fprintf(fout, "\nVIA 2:\n");
|
|
fprintf(fout, " Timer 1 Counter: %04x Latch: %04x\n", R1541.via2_t1c, R1541.via2_t1l);
|
|
fprintf(fout, " Timer 2 Counter: %04x Latch: %04x\n", R1541.via2_t2c, R1541.via2_t2l);
|
|
fprintf(fout, " ACR: %02x\n", R1541.via2_acr);
|
|
fprintf(fout, " PCR: %02x\n", R1541.via2_pcr);
|
|
fprintf(fout, " Pending interrupts: ");
|
|
dump_via_ints(R1541.via2_ifr);
|
|
fprintf(fout, " Enabled interrupts: ");
|
|
dump_via_ints(R1541.via2_ier);
|
|
}
|
|
|
|
static void dump_via_ints(uint8 i)
|
|
{
|
|
if (i & 0x7f) {
|
|
if (i & 0x40) fprintf(fout, "T1 ");
|
|
if (i & 0x20) fprintf(fout, "T2 ");
|
|
if (i & 2) fprintf(fout, "CA1 ");
|
|
if (i & 1) fprintf(fout, "CA2 ");
|
|
if (i & 0x10) fprintf(fout, "CB1 ");
|
|
if (i & 8) fprintf(fout, "CB2 ");
|
|
if (i & 4) fprintf(fout, "Serial ");
|
|
} else
|
|
fprintf(fout, "None");
|
|
fputc('\n', fout);
|
|
}
|
|
|
|
|
|
/*
|
|
* Load data
|
|
* l start "file"
|
|
*/
|
|
|
|
static void load_data(void)
|
|
{
|
|
uint16 adr;
|
|
FILE *file;
|
|
int fc;
|
|
|
|
if (!expression(&adr))
|
|
return;
|
|
if (the_token == T_END) {
|
|
error("Missing file name");
|
|
return;
|
|
}
|
|
if (the_token != T_STRING) {
|
|
error("'\"' around file name expected");
|
|
return;
|
|
}
|
|
|
|
if (!(file = fopen(the_string, "rb")))
|
|
error("Unable to open file");
|
|
else {
|
|
while ((fc = fgetc(file)) != EOF)
|
|
SAMWriteByte(adr++, fc);
|
|
fclose(file);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Save data
|
|
* s start end "file"
|
|
*/
|
|
|
|
static void save_data(void)
|
|
{
|
|
bool done = false;
|
|
uint16 adr, end_adr;
|
|
FILE *file;
|
|
|
|
if (!expression(&adr))
|
|
return;
|
|
if (!expression(&end_adr))
|
|
return;
|
|
if (the_token == T_END) {
|
|
error("Missing file name");
|
|
return;
|
|
}
|
|
if (the_token != T_STRING) {
|
|
error("'\"' around file name expected");
|
|
return;
|
|
}
|
|
|
|
if (!(file = fopen(the_string, "wb")))
|
|
error("Unable to create file");
|
|
else {
|
|
do {
|
|
if (adr == end_adr) done = true;
|
|
|
|
fputc(SAMReadByte(adr++), file);
|
|
} while (!done);
|
|
fclose(file);
|
|
}
|
|
}
|