/* * SAM.h - Simple Assembler and Monitor With Integrated System Explorer * * Frodo (C) 1994-1997,2002-2004 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(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(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= ' ') && (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= ' ') && (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; iASCII */ 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; iExtConfig = 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<