diff options
author | 2022-07-01 22:55:52 +1000 | |
---|---|---|
committer | 2022-07-01 22:55:52 +1000 | |
commit | f04d9d605c2bea16c2b8fc4cd5d32cd7e55839d4 (patch) | |
tree | 92075cdea364273964c882ad2d2500344050a34e /attic/fddebug.cpp | |
parent | 81727a940941deb8bdf873242a2f3d9cfac994c6 (diff) |
Assume any src/<target>/* folder containing C++ source is a project. Only 'shared' is special-cased.
Diffstat (limited to 'attic/fddebug.cpp')
-rw-r--r-- | attic/fddebug.cpp | 2439 |
1 files changed, 2439 insertions, 0 deletions
diff --git a/attic/fddebug.cpp b/attic/fddebug.cpp new file mode 100644 index 00000000000..74deb2af167 --- /dev/null +++ b/attic/fddebug.cpp @@ -0,0 +1,2439 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles + +#include <functional> + +#include "emu.h" +#include "machine/fddebug.h" + +void fd1094_init_debugging(running_machine &machine, const char *cpureg, const char *keyreg, const char *statreg, void (*changed)(running_machine &)) +{ +} + +#if 0 + +/*************************************************************************** + + fddebug.c + + FD1094 decryption helper routines. + +**************************************************************************** + + When searching for new keys, here are some common sequences in the + System 16B games that are useful. + + IRQ4 handler entry points: + + common sequence 1: + MOVE SR,(A7) 40D7 + MOVE.B #$23,(A7) 1EBC 0023 + MOVEM.L D0-D7/A0-A6,-(A7) 48E7 FFFE + + common sequence 2: + MOVEM.L D0-D7/A0-A6,-(A7) 48E7 FFFE + + common sequence 3: + BRA.W <previous sequence> 6000 xxxx + + IRQ4 handler exit points: + + common sequence (often appears twice nearby): + MOVE (A7)+,D0-D7/A0-A6 4CDF 7FFF + RTE 4E73 + + Entry points: + + common sequence 1: + LEA <stack>.L,A7 4FF9 xxxx xxxx + MOVE #$2700,SR 46FC 2700 + CMPI.L #$00xxffff,D0 0C80 00xx FFFF + MOVEQ #0,D0 + MOVE.L D0,D1 2200 + MOVE.L D0,D2 2400 + MOVE.L D0,D3 2600 + MOVE.L D0,D4 2800 + MOVE.L D0,D5 2A00 + MOVE.L D0,D6 2C00 + MOVE.L D0,D7 2E00 + + common sequence 2: + LEA <stack>.W,A7 4FF8 xxxx + MOVE #$2700,SR 46FC 2700 + CMPI.L #$00xxffff,D0 0C80 00xx FFFF + MOVEQ #0,D0 + MOVE.L D0,D1 2200 + MOVE.L D0,D2 2400 + MOVE.L D0,D3 2600 + MOVE.L D0,D4 2800 + MOVE.L D0,D5 2A00 + MOVE.L D0,D6 2C00 + MOVE.L D0,D7 2E00 + + common sequence 3: + LEA <stack>.W,A7 4FF8 xxxx + MOVE #$2700,SR 46FC 2700 + MOVEQ #0,D0 + MOVE.L D0,D1 2200 + MOVE.L D0,D2 2400 + MOVE.L D0,D3 2600 + MOVE.L D0,D4 2800 + MOVE.L D0,D5 2A00 + MOVE.L D0,D6 2C00 + MOVE.L D0,D7 2E00 + + common sequence 4: + BRA.W <previous sequence> 6000 xxxx + +**************************************************************************** + + These constraints worked for finding exctleag's seed: + + fdcset 0410,4ff9 + fdcset 0412,0000 + fdcset 0414,0000 + fdcset 0416,46fc + fdcset 0418,2700 + fdcset 041a,0c80 + fdcset 041c,0000,ff00 + fdcset 041e,ffff + + //fdcset 0f9e,40d7,ffff,irq + fdcset 0fa0,1ebc,ffff,irq + fdcset 0fa2,0023,ffff,irq + //fdcset 0fa4,48e7,ffff,irq + fdcset 0fa6,fffe,ffff,irq + fdcset 0fa8,13f8,ffff,irq + fdcset 0fac,00c4,ffff,irq + fdcset 0fae,0001,ffff,irq + + //fdcset 1060,4cdf,ffff,irq + fdcset 1062,7fff,ffff,irq + //fdcset 1064,4e73,ffff,irq + //fdcset 1070,4cdf,ffff,irq + fdcset 1072,7fff,ffff,irq + //fdcset 1074,4e73,ffff,irq + +**************************************************************************** + + Add something like this to debug_view_memory::write + + // hack for FD1094 editing + #ifdef FD1094_HACK + if (source.m_base == machine().root_device().memregion("user2")) + { + extern void fd1094_regenerate_key(running_machine &machine); + fd1094_regenerate_key(machine()); + } + #endif + +***************************************************************************/ + +#include "emu.h" +#include "machine/fd1094.h" +#include "cpu/m68000/m68000.h" + +#include "debug/debugcmd.h" +#include "debug/debugcon.h" +#include "debug/debugcpu.h" +#include "debug/debugvw.h" +#include "machine/fddebug.h" + + + +/*************************************************************************** + CONSTANTS +***************************************************************************/ + +#define KEY_SIZE 8192 +#define MAX_CONSTRAINTS 100 +#define MAX_SEARCH_DEPTH 10000 + +/* status byte breakdown */ +#define STATE_MASK 0xff00 +#define HIBITS_MASK 0x00c0 +#define SEARCH_MASK 0x0020 +#define STATUS_MASK 0x001f + +/* possible status values */ +#define STATUS_UNVISITED 0x00 +#define STATUS_LOCKED 0x01 +#define STATUS_NOCHANGE 0x02 +#define STATUS_GUESS 0x03 + +/* sizes for the opcode table */ +#define SIZE_BYTE 1 /* single byte */ +#define SIZE_WORD 2 /* single word */ +#define SIZE_LONG 3 /* single long */ +#define SIZE_BIT 4 /* single byte, limited to bit sizes (0-7) */ +#define SIZE_MASK 7 + +/* operand sizes */ +#define OF_SIZEMASK (SIZE_MASK << 0) +#define OF_BYTE (SIZE_BYTE << 0) /* byte size operation */ +#define OF_WORD (SIZE_WORD << 0) /* word size operation */ +#define OF_LONG (SIZE_LONG << 0) /* long size operation */ + +/* immediate sizes */ +#define OF_ISIZEMASK (SIZE_MASK << 3) +#define OF_IMMB (SIZE_BYTE << 3) /* immediate byte follows */ +#define OF_IMMW (SIZE_WORD << 3) /* immediate word follows */ +#define OF_IMML (SIZE_LONG << 3) /* immediate long follows */ +#define OF_IMMBIT (SIZE_BIT << 3) /* immediate byte follows */ + +/* other opcode flags */ +#define OF_EASRC 0x00000040 /* standard EA is source */ +#define OF_EADST 0x00000080 /* standard EA is destination */ +#define OF_EADREG 0x00000100 /* EA with data register is allowed */ +#define OF_EAAREG 0x00000200 /* EA with address register is allowed */ +#define OF_EAA 0x00000400 /* EA with (An) is allowed */ +#define OF_EAPLUS 0x00000800 /* EA with (An)+ is allowed */ +#define OF_EAMINUS 0x00001000 /* EA with -(An) is allowed */ +#define OF_EADISP 0x00002000 /* EA with (D,An) displacement is allowed */ +#define OF_EAABS 0x00004000 /* EA with absolute (both word and long) is allowed */ +#define OF_EAIMM 0x00008000 /* EA with immediate is allowed */ +#define OF_EAPCR 0x00010000 /* EA with PC-relative addressing is allowed */ +#define OF_RARE 0x00080000 /* opcode is not commonly used */ +#define OF_BRANCH 0x00100000 /* opcode represents a branch */ +#define OF_JMP 0x00200000 /* opcode represents a jmp/jsr */ +#define OF_MOVE 0x00400000 /* opcode has MOVE semantics */ +#define OF_LENMASK 0xf0000000 /* opcode length mask */ +#define OF_INVALID 0xffffffff /* invalid opcode */ + + + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +/* a single possible instruction decoding */ +struct fd1094_possibility +{ + offs_t basepc; /* starting PC of the possibility */ + int length; /* number of words */ + uint8_t instrbuffer[10]; /* instruction data for disassembler */ + uint8_t keybuffer[10]; /* array of key values to produce the instruction data */ + uint8_t iffy; /* is this an iffy possibility? */ + char dasm[256]; /* disassembly */ +}; + +/* an entry in the opcode table */ +struct optable_entry +{ + uint32_t flags; /* per-opcode flags */ + const char * string; /* identifying string */ +}; + + + +/*************************************************************************** + GLOBAL VARIABLES +***************************************************************************/ + +/* array of PCs not to stop at */ +static uint8_t * ignorepc; +static uint8_t ignore_all; + +/* array of information about each opcode */ +static std::unique_ptr<optable_entry[]> optable; + +/* buffer for undoing operations */ +static uint8_t * undobuff; + +/* array of possible instruction decodings */ +static fd1094_possibility posslist[4*4*4*4*4]; +static int posscount; + +/* array of possible seeds */ +static uint32_t * possible_seed; + +/* array of constraints */ +static fd1094_constraint constraints[MAX_CONSTRAINTS]; +static int constcount; + +/* stack of search addresses */ +static uint32_t searchstack[MAX_SEARCH_DEPTH]; +static int searchsp; + +/* current key generation parameters */ +static uint32_t fd1094_global; +static uint32_t fd1094_seed; +static uint8_t keydirty; + +/* pointers to our data */ +static uint16_t * coderegion; +static uint32_t coderegion_words; +static uint8_t * keyregion; +static uint16_t * keystatus; +static uint32_t keystatus_words; + +/* key changed callback */ +static void (*key_changed)(running_machine &); + + + +/*************************************************************************** + FUNCTION PROTOTYPES +***************************************************************************/ + +static void set_default_key_params(running_machine &machine); +static void load_overlay_file(running_machine &machine); +static void save_overlay_file(running_machine &machine); + +static int instruction_hook(device_t &device, offs_t curpc); + +static void execute_fdsave(running_machine &machine, int ref, int params, const char **param); +static void execute_fdoutput(running_machine &machine, int ref, int params, const char **param); +static void execute_fdseed(running_machine &machine, int ref, int params, const char **param); +static void execute_fdlockguess(running_machine &machine, int ref, int params, const char **param); +static void execute_fdeliminate(running_machine &machine, int ref, int params, const char **param); +static void execute_fdunlock(running_machine &machine, int ref, int params, const char **param); +static void execute_fdignore(running_machine &machine, int ref, int params, const char **param); +static void execute_fdundo(running_machine &machine, int ref, int params, const char **param); +static void execute_fdstatus(running_machine &machine, int ref, int params, const char **param); +static void execute_fdstate(running_machine &machine, int ref, int params, const char **param); +static void execute_fdpc(running_machine &machine, int ref, int params, const char **param); +static void execute_fdsearch(running_machine &machine, int ref, int params, const char **param); +static void execute_fddasm(running_machine &machine, int ref, int params, const char **param); +static void execute_fdcset(running_machine &machine, int ref, int params, const char **param); +static void execute_fdclist(running_machine &machine, int ref, int params, const char **param); +static void execute_fdcsearch(running_machine &machine, int ref, int params, const char **param); + +static fd1094_possibility *try_all_possibilities(address_space &space, int basepc, int offset, int length, uint8_t *instrbuffer, uint8_t *keybuffer, fd1094_possibility *possdata); +static void tag_possibility(running_machine &machine, fd1094_possibility *possdata, uint8_t status); + +static void perform_constrained_search(running_machine &machine); +static uint32_t find_global_key_matches(uint32_t startwith, uint16_t *output); +static int find_constraint_sequence(uint32_t global, int quick); +static int does_key_work_for_constraints(const uint16_t *base, uint8_t *key); +static uint32_t reconstruct_base_seed(int keybaseaddr, uint32_t startseed); + +static void build_optable(running_machine &machine); +static int validate_ea(address_space &space, uint32_t pc, uint8_t modereg, const uint8_t *parambase, uint32_t flags); +static int validate_opcode(address_space &space, uint32_t pc, const uint8_t *opdata, int maxwords); + + + + +/*************************************************************************** + INLINE FUNCTIONS +***************************************************************************/ + +/*----------------------------------------------- + addr_to_keyaddr - given an address, + return the address in the key that will be + used to decrypt it +-----------------------------------------------*/ + +static inline int addr_to_keyaddr(offs_t address) +{ + /* for address xx0000-xx0006 (but only if >= 000008), use key xx2000-xx2006 */ + if ((address & 0x0ffc) == 0 && address >= 4) + return (address & 0x1fff) | 0x1000; + else + return address & 0x1fff; +} + + +/*----------------------------------------------- + mask_for_keyaddr - given a key address, + return a mask indicating which bits should + always be 1 +-----------------------------------------------*/ + +static inline uint8_t mask_for_keyaddr(offs_t address) +{ + /* the first half of the key always has bit 0x80 set; the second half 0x40 */ + /* however, the values at 0000-0003 and 1000-1003 don't follow this rule */ + if ((address & 0x0ffc) == 0) + return 0x00; + else if ((address & 0x1000) == 0) + return 0x80; + else + return 0x40; +} + + +/*----------------------------------------------- + advance_seed - advance the PRNG seed by + the specified number of steps +-----------------------------------------------*/ + +static inline uint32_t advance_seed(uint32_t seed, int count) +{ + /* iterate over the seed for 'count' reps */ + while (count--) + { + seed = seed * 0x29; + seed += seed << 16; + } + return seed; +} + + +/*----------------------------------------------- + key_value_from_seed - extract the key value + from a seed and apply the given mask +-----------------------------------------------*/ + +static inline uint8_t key_value_from_seed(uint32_t seed, uint8_t mask) +{ + /* put bits 16-21 of the seed in the low 6 bits and OR with the mask */ + return ((~seed >> 16) & 0x3f) | mask; +} + + +/*----------------------------------------------- + generate_key_bytes - generate a sequence of + consecutive key bytes, starting with the + given seed +-----------------------------------------------*/ + +static inline void generate_key_bytes(uint8_t *dest, uint32_t keyoffs, uint32_t count, uint32_t seed) +{ + int bytenum; + + /* generate 'count' bytes of a key */ + for (bytenum = 0; bytenum < count; bytenum++) + { + uint32_t keyaddr = (keyoffs + bytenum) & 0x1fff; + uint8_t mask = mask_for_keyaddr(keyaddr); + + /* advance the seed first, then store the derived value */ + seed = advance_seed(seed, 1); + dest[keyaddr] = key_value_from_seed(seed, mask); + } +} + + +/*----------------------------------------------- + get_opcode_length - return the length of + an opcode based on the opcode +-----------------------------------------------*/ + +static inline uint8_t get_opcode_length(uint16_t opcode) +{ + /* return the length from the table */ + return optable[opcode].flags >> 28; +} + + +/*----------------------------------------------- + set_constraint - set the values of a + constraint +-----------------------------------------------*/ + +static inline void set_constraint(fd1094_constraint *constraint, uint32_t pc, uint16_t state, uint16_t value, uint16_t mask) +{ + constraint->pc = pc; + constraint->state = state; + constraint->value = value & mask; + constraint->mask = mask; +} + +/*----------------------------------------------- + print_possibilities - print possibilities + for a given address +-----------------------------------------------*/ + +static inline void print_possibilities(running_machine &machine) +{ + machine.debugger().console().printf("Possibilities @ %06X:\n", posslist[0].basepc); + for (int i = 0; i < posscount; i++) + machine.debugger().console().printf(" %c%2x: %s\n", posslist[i].iffy ? ' ' : '*', i, posslist[i].dasm); +} + + +/*----------------------------------------------- + pc_is_valid - is a given PC value valid? + 0=no, 1=yes, 2=unlikely +-----------------------------------------------*/ + +static inline int pc_is_valid(address_space &space, uint32_t pc, uint32_t flags) +{ + /* if we're odd or out of range, fail */ + if ((pc & 1) == 1) + return 0; + if (pc & 0xff000000) + return 0; + if (space.direct().read_ptr(pc) == nullptr) + return 0; + return 1; +} + + +/*----------------------------------------------- + addr_is_valid - is a given address value + valid? 0=no, 1=yes, 2=unlikely +-----------------------------------------------*/ + +static inline int addr_is_valid(address_space &space, uint32_t addr, uint32_t flags) +{ + /* if this a JMP, the address is a PC */ + if (flags & OF_JMP) + return pc_is_valid(space, addr, flags); + + /* if we're odd or out of range, fail */ + if ((flags & OF_SIZEMASK) != OF_BYTE && (addr & 1) == 1) + return 0; + if ((addr & 0xff000000) != 0 && (addr & 0xff000000) != 0xff000000) + return 0; + + /* if we're invalid, fail */ + if (strcmp(const_cast<address_space &>(space)->get_handler_string(read_or_write::READ, addr), "segaic16_memory_mapper_lsb_r") == 0) + return 2; + + return 1; +} + + + +/*************************************************************************** + CORE IMPLEMENTATION +***************************************************************************/ + +/*----------------------------------------------- + fd1094_init_debugging - set up debugging +-----------------------------------------------*/ + +void fd1094_init_debugging(running_machine &machine, const char *cpureg, const char *keyreg, const char *statreg, void (*changed)(running_machine &)) +{ + /* set the key changed callback */ + key_changed = changed; + + /* set up the regions */ + coderegion = (uint16_t *)machine.root_device().memregion(cpureg)->base(); + coderegion_words = machine.root_device().memregion(cpureg)->bytes() / 2; + keyregion = (uint8_t *)machine.root_device().memregion(keyreg)->base(); + keystatus = (uint16_t *)machine.root_device().memregion(statreg)->base(); + keystatus_words = machine.root_device().memregion(statreg)->bytes() / 2; + assert(coderegion_words == keystatus_words); + + /* allocate memory for the ignore table */ + ignorepc = make_unique_clear<uint8_t>(1 << 23); + + /* allocate memory for the undo buffer */ + undobuff = std::make_unique<uint8_t[]>(keystatus_words * 2); + memcpy(undobuff, keystatus, keystatus_words * 2); + + /* allocate memory for the possible seeds array */ + possible_seed = std::make_unique<uint32_t[]>(65536); + + /* build the opcode table */ + build_optable(machine); + + /* set up default constraints */ + constcount = 0; + set_constraint(&constraints[constcount++], 0x000000, FD1094_STATE_RESET, 0x0000, 0xffff); + set_constraint(&constraints[constcount++], 0x000002, FD1094_STATE_RESET, 0x0000, 0xffff); + set_constraint(&constraints[constcount++], 0x000004, FD1094_STATE_RESET, 0x0000, 0xffff); + set_constraint(&constraints[constcount++], 0x000006, FD1094_STATE_RESET, 0x0000, 0xc001); + + /* determine the key parameters */ + set_default_key_params(machine); + + /* read the key overlay file */ + load_overlay_file(machine); + + /* add some commands */ + using namespace std::placeholder; + machine.debugger().console().register_command("fdsave", CMDFLAG_NONE, 0, 0, 0, std::bind(&execute_fdsave, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdoutput", CMDFLAG_NONE, 0, 1, 1, std::bind(&execute_fdoutput, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdseed", CMDFLAG_NONE, 0, 2, 2, std::bind(&execute_fdseed, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdguess", CMDFLAG_NONE, STATUS_GUESS, 1, 1, std::bind(&execute_fdlockguess, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdlock", CMDFLAG_NONE, STATUS_LOCKED, 1, 1, std::bind(&execute_fdlockguess, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdeliminate", CMDFLAG_NONE, 0, 1, 10, std::bind(&execute_fdeliminate, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdunlock", CMDFLAG_NONE, 0, 1, 1, std::bind(&execute_fdunlock, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdignore", CMDFLAG_NONE, 0, 0, 1, std::bind(&execute_fdignore, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdundo", CMDFLAG_NONE, 0, 0, 0, std::bind(&execute_fdundo, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdstatus", CMDFLAG_NONE, 0, 0, 0, std::bind(&execute_fdstatus, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdstate", CMDFLAG_NONE, 0, 0, 1, std::bind(&execute_fdstate, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdpc", CMDFLAG_NONE, 0, 0, 1, std::bind(&execute_fdpc, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdsearch", CMDFLAG_NONE, 0, 0, 0, std::bind(&execute_fdsearch, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fddasm", CMDFLAG_NONE, 0, 1, 1, std::bind(&execute_fddasm, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdcset", CMDFLAG_NONE, 0, 2, 4, std::bind(&execute_fdcset, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdclist", CMDFLAG_NONE, 0, 0, 0, std::bind(&execute_fdclist, std::ref(machine), _1, _2, _3)); + machine.debugger().console().register_command("fdcsearch", CMDFLAG_NONE, 0, 0, 0, std::bind(&execute_fdcsearch, std::ref(machine), _1, _2, _3)); + + /* set up the instruction hook */ + machine.device("maincpu")->debug()->set_instruction_hook(instruction_hook); + + /* regenerate the key */ + if (keydirty) + fd1094_regenerate_key(machine); +} + + +/*----------------------------------------------- + set_default_key_params - based on the game + name, set some defaults +-----------------------------------------------*/ + +static void set_default_key_params(running_machine &machine) +{ + static const struct + { + const char * gamename; + uint32_t global; + uint32_t seed; + } default_keys[] = + { + { "altbeastj1", 0xFCAFF9F9, 0x177AC6 }, + { "bullet", 0x12A8F9EC, 0x1B1FC3 }, + }; + int keynum; + + /* look for a matching game and set the key appropriately */ + for (keynum = 0; keynum < std::size(default_keys); keynum++) + if (strcmp(machine.system().name, default_keys[keynum].gamename) == 0) + { + fd1094_global = default_keys[keynum].global; + fd1094_seed = default_keys[keynum].seed; + keydirty = true; + break; + } +} + + +/*----------------------------------------------- + load_overlay_file - load the key overlay + file +-----------------------------------------------*/ + +static void load_overlay_file(running_machine &machine) +{ + int pcaddr; + + /* determine the filename and open the file */ + emu_file file(OPEN_FLAG_READ); + osd_file::error filerr = file.open(machine.system().name, ".kov"); + if (filerr == osd_file::error::NONE) + { + file.read(keystatus, keystatus_words * 2); + + /* convert from big-endian */ + for (pcaddr = 0; pcaddr < keystatus_words; pcaddr++) + keystatus[pcaddr] = big_endianize_int16(keystatus[pcaddr]) & ~SEARCH_MASK; + } + + /* mark the key dirty */ + keydirty = true; +} + + +/*----------------------------------------------- + save_overlay_file - save the key overlay + file +-----------------------------------------------*/ + +static void save_overlay_file(running_machine &machine) +{ + int pcaddr; + + /* determin the filename and open the file */ + emu_file file(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE); + osd_file::error filerr = file.open(machine.system().name, ".kov"); + if (filerr == osd_file::error::NONE) + { + /* convert to big-endian */ + for (pcaddr = 0; pcaddr < keystatus_words; pcaddr++) + keystatus[pcaddr] = big_endianize_int16(keystatus[pcaddr]); + + /* write the data */ + file.write(keystatus, keystatus_words * 2); + + /* convert from big-endian */ + for (pcaddr = 0; pcaddr < keystatus_words; pcaddr++) + keystatus[pcaddr] = big_endianize_int16(keystatus[pcaddr]); + } +} + + +/*----------------------------------------------- + fd1094_regenerate_key - regenerate the key + based on the raw parameters and the overlay + data +-----------------------------------------------*/ + +void fd1094_regenerate_key(running_machine &machine) +{ + int reps = keystatus_words / KEY_SIZE; + int keyaddr, repnum; + + /* store the global key in the first 4 bytes */ + keyregion[0] = fd1094_global >> 24; + keyregion[1] = fd1094_global >> 16; + keyregion[2] = fd1094_global >> 8; + keyregion[3] = fd1094_global >> 0; + + /* then generate the remaining 8188 bytes */ + generate_key_bytes(keyregion, 4, 8192 - 4, fd1094_seed); + + /* apply the overlay */ + for (keyaddr = 4; keyaddr < KEY_SIZE; keyaddr++) + { + keyregion[keyaddr] |= keystatus[keyaddr] & HIBITS_MASK; + + /* if we're locked, propogate that info to all our reps */ + if ((keystatus[keyaddr] & STATUS_MASK) == STATUS_LOCKED) + for (repnum = 1; repnum < reps; repnum++) + { + keystatus[repnum * KEY_SIZE + keyaddr] = (keystatus[repnum * KEY_SIZE + keyaddr] & ~STATUS_MASK) | STATUS_LOCKED; + if ((keyaddr & 0x1ffc) == 0x1000) + keystatus[repnum * KEY_SIZE + keyaddr - 0x1000] = (keystatus[repnum * KEY_SIZE + keyaddr - 0x1000] & ~STATUS_MASK) | STATUS_LOCKED; + } + } + + /* update the key with the current fd1094 manager */ + if (key_changed != nullptr) + (*key_changed)(machine); + + /* force all memory and disassembly views to update */ + machine.debug_view().update_all(DVT_MEMORY); + machine.debug_view().update_all(DVT_DISASSEMBLY); + + /* reset keydirty */ + keydirty = false; +} + + +/*----------------------------------------------- + instruction_hook - per-instruction hook +-----------------------------------------------*/ + +static int instruction_hook(device_t &device, offs_t curpc) +{ + int curfdstate = fd1094_set_state(keyregion, -1); + uint8_t instrbuffer[10], keybuffer[5]; + int i, keystat; + + /* quick exit if we're ignoring */ + if (ignore_all || ignorepc[curpc/2]) + return 0; + + /* quick exit if we're already locked */ + keystat = keystatus[curpc/2] & STATUS_MASK; + keystatus[curpc/2] = (keystatus[curpc/2] & ~STATE_MASK) | (curfdstate << 8); + if (keystat == STATUS_LOCKED || keystat == STATUS_NOCHANGE) + { + uint16_t opcode = fd1094_decode(curpc/2, coderegion[curpc/2], keyregion, 0); + int length = get_opcode_length(opcode); + for (i = 1; i < length; i++) + { + keystat = keystatus[curpc/2 + i] & STATUS_MASK; + if (keystat != STATUS_LOCKED && keystat != STATUS_NOCHANGE) + break; + } + if (i == length) + { + for (i = 1; i < length; i++) + keystatus[curpc/2 + i] = (keystatus[curpc/2 + i] & ~STATE_MASK) | (curfdstate << 8); + return 0; + } + } + + /* try all possible decodings at the current pc */ + posscount = try_all_possibilities(device.memory().space(AS_PROGRAM), curpc, 0, 0, instrbuffer, keybuffer, posslist) - posslist; + if (keydirty) + fd1094_regenerate_key(device.machine()); + + /* if we only ended up with one possibility, mark that one as good */ + if (posscount == 1) + { + tag_possibility(device.machine(), &posslist[0], STATUS_LOCKED); + fd1094_regenerate_key(device.machine()); + return 0; + } + + /* print possibilities and break */ + print_possibilities(device.machine()); + return 1; +} + + +/*----------------------------------------------- + execute_fdsave - handle the 'fdsave' command +-----------------------------------------------*/ + +static void execute_fdsave(running_machine &machine, int ref, int params, const char **param) +{ + save_overlay_file(machine); + machine.debugger().console().printf("File saved\n"); +} + + +/*----------------------------------------------- + execute_fdoutput - output the current key + to a file +-----------------------------------------------*/ + +static void execute_fdoutput(running_machine &machine, int ref, int params, const char **param) +{ + /* make sure we're up-to-date */ + if (keydirty) + fd1094_regenerate_key(machine); + + /* determin the filename and open the file */ + emu_file file(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE); + osd_file::error filerr = file.open(param[0]); + if (filerr == osd_file::error::NONE) + file.write(keyregion, KEY_SIZE); + + machine.debugger().console().printf("File '%s' saved\n", param[0]); +} + + +/*----------------------------------------------- + execute_fdseed - handle the 'fdseed' command +-----------------------------------------------*/ + +static void execute_fdseed(running_machine &machine, int ref, int params, const char **param) +{ + uint64_t num1, num2; + + /* extract the parameters */ + if (!machine.debugger().commands().validate_number_parameter(param[0], &num1)) + return; + if (!machine.debugger().commands().validate_number_parameter(param[1], &num2)) + return; + + /* set the global and seed, and then regenerate the key */ + fd1094_global = num1; + fd1094_seed = num2; + + /* clear out our buffer */ + memset(keystatus, 0, keystatus_words * sizeof(keystatus[0])); + + /* regenerate the key and reset the 68000 */ + fd1094_regenerate_key(machine); +} + + +/*----------------------------------------------- + execute_fdlockguess - handle the 'fdlock' + and 'fdguess' commands +-----------------------------------------------*/ + +static void execute_fdlockguess(running_machine &machine, int ref, int params, const char **param) +{ + uint64_t num1; + + /* extract the parameter */ + if (!machine.debugger().commands().validate_number_parameter(param[0], &num1)) + return; + + /* make sure it is within range of our recent possibilities */ + if (num1 >= posscount) + { + machine.debugger().console().printf("Possibility of out range (%x max)\n", posscount); + return; + } + + /* create an undo buffer */ + memcpy(undobuff, keystatus, keystatus_words * 2); + + /* tag this possibility as indicated by the ref parameter, and then regenerate the key */ + tag_possibility(machine, &posslist[num1], ref); + fd1094_regenerate_key(machine); +} + + +/*----------------------------------------------- + execute_fdeliminate - handle the + 'fdeliminate' command +-----------------------------------------------*/ + +static void execute_fdeliminate(running_machine &machine, int ref, int params, const char **param) +{ + int pnum, posssrc, possdst; + int plist[10]; + + /* extract parameters */ + for (pnum = 0; pnum < params; pnum++) + { + uint64_t num1; + + /* extract the parameters */ + if (!machine.debugger().commands().validate_number_parameter(param[pnum], &num1)) + return; + + /* make sure it is within range of our recent possibilities */ + if (num1 >= posscount) + { + machine.debugger().console().printf("Possibility %x of out range (%x max)\n", (int)num1, posscount); + return; + } + + /* set the entry */ + plist[pnum] = num1; + } + + /* loop over parameters */ + for (posssrc = possdst = 0; posssrc < posscount; posssrc++) + { + /* is the current pnum in our list to delete? */ + for (pnum = 0; pnum < params; pnum++) + if (plist[pnum] == posssrc) + break; + + /* if not, copy to the dest */ + if (pnum == params) + posslist[possdst++] = posslist[posssrc]; + } + + /* set the final count */ + posscount = possdst; + + /* reprint the possibilities */ + print_possibilities(machine); +} + + +/*----------------------------------------------- + execute_fdunlock - handle the 'fdunlock' + command +-----------------------------------------------*/ + +static void execute_fdunlock(running_machine &machine, int ref, int params, const char **param) +{ + device_t *cpu = machine.debugger().console().get_visible_cpu(); + + /* support 0 or 1 parameters */ + uint64_t offset; + if (params != 1 || !machine.debugger().commands().validate_number_parameter(param[0], &offset)) + offset = cpu->state().pc(); + int keyaddr = addr_to_keyaddr(offset / 2); + + /* toggle the ignore PC status */ + machine.debugger().console().printf("Unlocking PC %06X\n", (int)offset); + + /* iterate over all reps and unlock them */ + const int reps = keystatus_words / KEY_SIZE; + for (int repnum = 0; repnum < reps; repnum++) + { + uint16_t *dest = &keystatus[repnum * KEY_SIZE + keyaddr]; + if ((*dest & STATUS_MASK) == STATUS_LOCKED) + *dest &= ~STATUS_MASK & ~HIBITS_MASK; + + /* unlock the duplicate key bytes as well */ + if ((keyaddr & 0x1ffc) == 0x1000) + { + dest = &keystatus[repnum * KEY_SIZE + keyaddr - 0x1000]; + if ((*dest & STATUS_MASK) == STATUS_LOCKED) + *dest &= ~STATUS_MASK & ~HIBITS_MASK; + } + } +} + + +/*----------------------------------------------- + execute_fdignore - handle the 'fdignore' + command +-----------------------------------------------*/ + +static void execute_fdignore(running_machine &machine, int ref, int params, const char **param) +{ + device_t *cpu = machine.debugger().console().get_visible_cpu(); + + /* support 0 or 1 parameters */ + if (params == 1 && strcmp(param[0], "all") == 0) + { + ignore_all = true; + machine.debugger().console().printf("Ignoring all unknown opcodes\n"); + return; + } + + uint64_t offset; + if (params != 1 || !machine.debugger().commands().validate_number_parameter(param[0], &offset)) + offset = cpu->state().pc(); + offset /= 2; + + /* toggle the ignore PC status */ + ignorepc[offset] = !ignorepc[offset]; + if (ignorepc[offset]) + machine.debugger().console().printf("Ignoring address %06X\n", (int)offset * 2); + else + machine.debugger().console().printf("No longer ignoring address %06X\n", (int)offset * 2); + + /* if no parameter given, implicitly run as well */ + if (params == 0) + machine.debugger().console().get_visible_cpu()->debug()->go(); +} + + +/*----------------------------------------------- + execute_fdundo - handle the 'fdundo' + command +-----------------------------------------------*/ + +static void execute_fdundo(running_machine &machine, int ref, int params, const char **param) +{ + /* copy the undobuffer back and regenerate the key */ + memcpy(keystatus, undobuff, keystatus_words * 2); + fd1094_regenerate_key(machine); + machine.debugger().console().printf("Undid last change\n"); +} + + +/*----------------------------------------------- + execute_fdstatus - handle the 'fdstatus' + command +-----------------------------------------------*/ + +static void execute_fdstatus(running_machine &machine, int ref, int params, const char **param) +{ + int numreps = keystatus_words / KEY_SIZE; + int locked = 4, nomatter = 0, guesses = 0; + int keyaddr; + + /* count how many locked keys we have */ + for (keyaddr = 4; keyaddr < KEY_SIZE; keyaddr++) + { + int count[STATUS_MASK + 1] = { 0 }; + int repnum; + + for (repnum = 0; repnum < numreps; repnum++) + count[keystatus[repnum * KEY_SIZE + keyaddr] & STATUS_MASK]++; + if (count[STATUS_LOCKED] > 0) + locked++; + else if (count[STATUS_GUESS] > 0) + guesses++; + else + nomatter++; + } + machine.debugger().console().printf("%4d/%4d keys locked (%d%%)\n", locked, KEY_SIZE, locked * 100 / KEY_SIZE); + machine.debugger().console().printf("%4d/%4d keys guessed (%d%%)\n", guesses, KEY_SIZE, guesses * 100 / KEY_SIZE); + machine.debugger().console().printf("%4d/%4d keys don't matter (%d%%)\n", nomatter, KEY_SIZE, nomatter * 100 / KEY_SIZE); +} + + +/*----------------------------------------------- + execute_fdstate - handle the 'fdstate' + command +-----------------------------------------------*/ + +static void execute_fdstate(running_machine &machine, int ref, int params, const char **param) +{ + uint64_t newstate; + + /* set the new state if we got a parameter */ + if (params > 0) + { + if (!machine.debugger().commands().validate_number_parameter(param[0], &newstate)) + return; + fd1094_set_state(keyregion, newstate); + fd1094_regenerate_key(machine); + machine.debug_view().update_all(DVT_MEMORY); + machine.debug_view().update_all(DVT_DISASSEMBLY); + } + + /* 0 parameters displays the current state */ + machine.debugger().console().printf("FD1094 state = %X\n", fd1094_set_state(keyregion, -1)); +} + + +/*----------------------------------------------- + execute_fdpc - handle the 'fdpc' + command +-----------------------------------------------*/ + +static void execute_fdpc(running_machine &machine, int ref, int params, const char **param) +{ + device_t *cpu = machine.debugger().console().get_visible_cpu(); + + /* support 0 or 1 parameters */ + uint64_t newpc = 0; + if (!machine.debugger().commands().validate_number_parameter(param[0], &newpc)) + newpc = cpu->state().pc(); + + /* set the new PC */ + cpu->state().set_pc(newpc); + + /* recompute around that */ + instruction_hook(*cpu, newpc); +} + + +/*----------------------------------------------- + execute_fdsearch - handle the 'fdsearch' + command +-----------------------------------------------*/ + +static void execute_fdsearch(running_machine &machine, int ref, int params, const char **param) +{ + address_space &space = machine->debugger().console().get_visible_cpu()->memory().space(AS_PROGRAM); + int pc = space.device().state().pc(); + int length, first = true; + uint8_t instrdata[2]; + uint16_t decoded; + + /* if we don't match, reset the stack */ + if (searchsp == 0 || searchstack[searchsp-1] != pc) + { + int pcaddr; + machine.debugger().console().printf("Starting new search at PC=%06X\n", pc); + searchsp = 0; + for (pcaddr = 0; pcaddr < coderegion_words; pcaddr++) + keystatus[pcaddr] &= ~SEARCH_MASK; + } + else + { + machine.debugger().console().printf("Resuming search at PC=%06X\n", pc); + searchsp--; + } + + /* loop while we don't need to break */ + while (1) + { + int newpc; + + /* for each PC after the first, do some extra work */ + if (!first) + { + /* if we've hit this PC already, stop and back off */ + while ((keystatus[pc/2] & SEARCH_MASK) != 0 && searchsp > 0) + pc = searchstack[--searchsp]; + if ((keystatus[pc/2] & SEARCH_MASK) != 0) + { + machine.debugger().console().printf("Search stack exhausted\n"); + break; + } + + /* set this as our current PC and run the instruction hook */ + space.device().state().set_pc(pc); + if (instruction_hook(space.device(), pc)) + break; + } + keystatus[pc/2] |= SEARCH_MASK; + first = false; + + /* decode the first word */ + decoded = fd1094_decode(pc/2, coderegion[pc/2], keyregion, 0); + instrdata[0] = decoded >> 8; + instrdata[1] = decoded; + + /* get the opcode */ + length = validate_opcode(space, pc, instrdata, 1); + if (length < 0) + length = -length; + if (length == 0) + { + machine.debugger().console().printf("Invalid opcode; unable to advance\n"); + break; + } + + /* advance to the new PC */ + newpc = pc + length * 2; + + /* handle branches */ + if (optable[decoded].flags & OF_BRANCH) + { + int deltapc = (int8_t)decoded; + int targetpc; + + /* extract the delta PC */ + if ((optable[decoded].flags & OF_ISIZEMASK) == OF_IMMW) + deltapc = (int16_t)fd1094_decode((pc+2)/2, coderegion[(pc+2)/2], keyregion, 0); + else if ((optable[decoded].flags & OF_ISIZEMASK) == OF_IMML) + deltapc = (int32_t)(fd1094_decode((pc+2)/2, coderegion[(pc+2)/2], keyregion, 0) << 16) + fd1094_decode((pc+4)/2, coderegion[(pc+4)/2], keyregion, 0); + + /* for everything but unconditional branches, push the target on the stack; else just go there */ + targetpc = (pc + 2 + deltapc) & 0xffffff; + if ((decoded & 0xff00) != 0x6000) + searchstack[searchsp++] = targetpc; + else + newpc = targetpc; + } + + /* handle jumps */ + if (optable[decoded].flags & OF_JMP) + { + int targetpc; + + /* if we're not an absolute address, skip it */ + if ((decoded & 0x3e) != 0x38) + continue; + + /* determine the target PC */ + if ((decoded & 0x3f) == 0x38) + targetpc = (int16_t)fd1094_decode((pc+2)/2, coderegion[(pc+2)/2], keyregion, 0); + else + targetpc = (int32_t)(fd1094_decode((pc+2)/2, coderegion[(pc+2)/2], keyregion, 0) << 16) + fd1094_decode((pc+4)/2, coderegion[(pc+4)/2], keyregion, 0); + + /* for jsr's, add a stack entry to explore the destination; else just go there */ + if ((decoded & 0xffc0) == 0x4e80) + searchstack[searchsp++] = targetpc; + else + newpc = targetpc; + } + + /* if we hit RTS/RTE, stop here */ + if (decoded == 0x4e73 || decoded == 0x4e75) + continue; + + /* set the new PC */ + pc = newpc; + } + + /* push the current PC on the stack */ + searchstack[searchsp++] = pc; +} + + +/*----------------------------------------------- + execute_fddasm - handle the 'fddasm' + command +-----------------------------------------------*/ + +static void execute_fddasm(running_machine &machine, int ref, int params, const char **param) +{ + address_space &space = machine->debugger().console().get_visible_cpu()->memory().space(AS_PROGRAM); + int origstate = fd1094_set_state(keyregion, -1); + const char *filename; + int skipped = false; + uint32_t pcaddr; + + /* extract the parameters */ + filename = param[0]; + + /* open the file */ + emu_file file(OPEN_FLAG_WRITE | OPEN_FLAG_CREATE); + osd_file::error filerr = file.open(filename); + if (filerr != osd_file::error::NONE) + { + machine.debugger().console().printf("Unable to create file '%s'\n", filename); + return; + } + + /* now do the disassembly */ + for (pcaddr = 0; pcaddr < coderegion_words; ) + { + uint8_t instrbuffer[10]; + int unknowns = false; + int length, pcoffs; + char disasm[256]; + uint16_t decoded; + int pnum; + + /* if we haven't visited this word, go to the next */ + if ((keystatus[pcaddr] & STATE_MASK) == 0) + { + pcaddr++; + skipped = true; + continue; + } + + /* get the opcode */ + fd1094_set_state(keyregion, FD1094_STATE_RESET | (keystatus[pcaddr] >> 8)); + decoded = fd1094_decode(pcaddr, coderegion[pcaddr], keyregion, 0); + length = optable[decoded].flags >> 28; + if (optable[decoded].flags == OF_INVALID) + length = 1; + + /* decode the remaining words */ + instrbuffer[0] = decoded >> 8; + instrbuffer[1] = decoded; + for (pcoffs = 1; pcoffs < length; pcoffs++) + { + if ((keystatus[pcaddr + pcoffs] & STATUS_MASK) == STATUS_UNVISITED) + { + pcaddr++; + skipped = true; + continue; + } + decoded = fd1094_decode(pcaddr + pcoffs, coderegion[pcaddr + pcoffs], keyregion, 0); + instrbuffer[pcoffs*2+0] = decoded >> 8; + instrbuffer[pcoffs*2+1] = decoded; + } + + /* disassemble the instruction */ + m68k_disassemble_raw(disasm, pcaddr * 2, instrbuffer, instrbuffer, M68K_CPU_TYPE_68000); + + /* print the line */ + if (skipped) + file.printf("\n"); + skipped = false; + file.printf(" %02X %06X:", keystatus[pcaddr] >> 8, pcaddr * 2); + for (pcoffs = 0; pcoffs < 5; pcoffs++) + { + if (pcoffs < length) + { + static const char statchar[] = "? =?"; + int keystat = keystatus[pcaddr + pcoffs] & STATUS_MASK; + if (keystat != STATUS_LOCKED && keystat != STATUS_NOCHANGE) + unknowns = true; + file.printf(" %02X%02X%c", instrbuffer[pcoffs*2+0], instrbuffer[pcoffs*2+1], statchar[keystat]); + } + else + file.printf(" "); + } + file.printf("%s\n", disasm); + + /* if we have unknowns, display them as well */ + if (unknowns > 0) + { + uint8_t keybuffer[5]; + int posscount = try_all_possibilities(space, pcaddr * 2, 0, 0, instrbuffer, keybuffer, posslist) - posslist; + for (pnum = 0; pnum < posscount; pnum++) + if (strcmp(disasm, posslist[pnum].dasm) != 0) + { + file.printf(" :"); + for (pcoffs = 0; pcoffs < 5; pcoffs++) + if (pcoffs < posslist[pnum].length) + file.printf(" %02X%02X ", posslist[pnum].instrbuffer[pcoffs*2+0], posslist[pnum].instrbuffer[pcoffs*2+1]); + else + file.printf(" "); + file.printf("%s\n", posslist[pnum].dasm); + } + } + + /* advance */ + pcaddr += length; + } + + /* close the file */ + fd1094_set_state(keyregion, origstate); +} + + +/*----------------------------------------------- + execute_fdcset - handle the 'fdcset' + command +-----------------------------------------------*/ + +static void execute_fdcset(running_machine &machine, int ref, int params, const char **param) +{ + uint64_t pc, value, mask = 0xffff, state = FD1094_STATE_RESET; + int cnum; + + /* extract the parameters */ + if (!machine.debugger().commands().validate_number_parameter(param[0], &pc)) + return; + if (!machine.debugger().commands().validate_number_parameter(param[1], &value)) + return; + if (params >= 3 && !machine.debugger().commands().validate_number_parameter(param[2], &mask)) + return; + if (params >= 4) + { + if (strcmp(param[3], "irq") == 0) + state = FD1094_STATE_IRQ; + else if (!machine.debugger().commands().validate_number_parameter(param[3], &state)) + return; + } + + /* validate parameters */ + if ((pc & 1) != 0 || pc > 0xffffff) + { + machine.debugger().console().printf("Invalid PC specified (%08X)\n", (uint32_t)pc); + return; + } + + /* look for a match and remove any matching constraints */ + for (cnum = 0; cnum < constcount; cnum++) + { + /* insert ahead of later constraints */ + if (constraints[cnum].pc > pc) + { + memmove(&constraints[cnum + 1], &constraints[cnum], (constcount - cnum) * sizeof(constraints[0])); + break; + } + + /* replace matching constraints */ + else if (constraints[cnum].pc == pc) + break; + } + + /* set the new constraint and increase the count */ + if (cnum >= constcount || constraints[cnum].pc != pc) + constcount++; + set_constraint(&constraints[cnum], pc, state, value, mask); + + /* explain what we did */ + machine.debugger().console().printf("Set new constraint at PC=%06X, state=%03X: decrypted & %04X == %04X\n", + (int)pc, (int)state, (int)mask, (int)value); +} + + +/*----------------------------------------------- + execute_fdclist - handle the 'fdclist' + command +-----------------------------------------------*/ + +static void execute_fdclist(running_machine &machine, int ref, int params, const char **param) +{ + int cnum; + + /* loop over constraints and print them */ + for (cnum = 0; cnum < constcount; cnum++) + { + fd1094_constraint *constraint = &constraints[cnum]; + machine.debugger().console().printf(" PC=%06X, state=%03X: decrypted & %04X == %04X\n", + constraint->pc, constraint->state, constraint->mask, constraint->value); + } +} + + +/*----------------------------------------------- + execute_fdcsearch - handle the 'fdcsearch' + command +-----------------------------------------------*/ + +static void execute_fdcsearch(running_machine &machine, int ref, int params, const char **param) +{ +// machine.debugger().console().printf("Searching for possible global keys....\n"); + perform_constrained_search(machine); +} + + +/*----------------------------------------------- + try_all_possibilities - recursively try + all possible values of the high bits of the + key at the given address for the specified + length +-----------------------------------------------*/ + +static fd1094_possibility *try_all_possibilities(address_space &space, int basepc, int offset, int length, uint8_t *instrbuffer, uint8_t *keybuffer, fd1094_possibility *possdata) +{ + uint8_t keymask, keystat; + uint16_t possvalue[4]; + uint8_t posskey[4]; + int numposs = 0; + int decoded; + int keyaddr; + int pcaddr; + int hibit; + int i; + + /* get the key address and mask */ + pcaddr = basepc/2 + offset; + keyaddr = addr_to_keyaddr(pcaddr); + keymask = mask_for_keyaddr(keyaddr); + keystat = keystatus[pcaddr] & STATUS_MASK; + + /* if the status is 1 (locked) or 2 (doesn't matter), just take the current value */ + if (keystat == STATUS_LOCKED || keystat == STATUS_NOCHANGE) + { + posskey[numposs] = keyregion[keyaddr]; + possvalue[numposs++] = fd1094_decode(pcaddr, coderegion[pcaddr], keyregion, 0); + } + + /* otherwise, iterate over high bits */ + else + { + /* remember the original key and iterate over high bits */ + uint8_t origkey = keyregion[keyaddr]; + for (hibit = 0x00; hibit < 0x100; hibit += 0x40) + if ((hibit & keymask) == keymask) + { + /* set the key and decode this word */ + keyregion[keyaddr] = (origkey & ~HIBITS_MASK) | hibit; + decoded = fd1094_decode(pcaddr, coderegion[pcaddr], keyregion, 0); + + /* see if we already got that value */ + for (i = 0; i < numposs; i++) + if ((uint16_t)decoded == possvalue[i]) + break; + + /* if not, add it to the list */ + if (i == numposs) + { + posskey[numposs] = keyregion[keyaddr]; + possvalue[numposs++] = decoded; + } + } + + /* restore the original key */ + keyregion[keyaddr] = origkey; + + /* if there was only one possibility, then mark it as "doesn't matter" */ + if (numposs == 1) + { + keystatus[pcaddr] = (keystatus[pcaddr] & ~STATUS_MASK) | STATUS_NOCHANGE; + keydirty = true; + } + } + + /* now iterate over our possible values */ + for (i = 0; i < numposs; i++) + { + /* set the instruction buffer */ + instrbuffer[offset*2 + 0] = possvalue[i] >> 8; + instrbuffer[offset*2 + 1] = possvalue[i]; + keybuffer[offset] = posskey[i]; + + /* if our length is 0, we need to do a quick dasm to see how long our length is */ + if (offset == 0) + { + /* first make sure we are a valid instruction */ + if ((possvalue[i] & 0xf000) == 0xa000 || (possvalue[i] & 0xf000) == 0xf000) + continue; + length = validate_opcode(space, basepc, instrbuffer, 1); + if (length == 0) + continue; + if (length < 0) + length = -length; + } + + /* if we're not at our target length, recursively call ourselves */ + if (offset < length - 1) + possdata = try_all_possibilities(space, basepc, offset + 1, length, instrbuffer, keybuffer, possdata); + + /* otherwise, output what we have */ + else + { + int tlen, inoffs; + + /* do the disassembly, and make sure we don't get an invalid result */ + m68k_disassemble_raw(possdata->dasm, basepc, instrbuffer, instrbuffer, M68K_CPU_TYPE_68000); + + /* validate the opcode */ + tlen = validate_opcode(space, basepc, instrbuffer, length); + if (tlen == 0) + { + printf("Eliminated: %s [", possdata->dasm); + for (inoffs = 0; inoffs < length; inoffs++) + printf("%04X ", (instrbuffer[inoffs*2+0] << 8) | instrbuffer[inoffs*2+1]); + printf("]\n"); + continue; + } + + /* copy the rest of the data and increment the pointer */ + possdata->basepc = basepc; + possdata->length = (tlen < 0) ? -tlen : tlen; + possdata->iffy = (tlen < 0); + memcpy(possdata->instrbuffer, instrbuffer, sizeof(possdata->instrbuffer)); + memcpy(possdata->keybuffer, keybuffer, sizeof(possdata->keybuffer)); + possdata++; + } + } + + return possdata; +} + + +/*----------------------------------------------- + tag_possibility - tag a given possibility + with the specified status +-----------------------------------------------*/ + +static void tag_possibility(running_machine &machine, fd1094_possibility *possdata, uint8_t status) +{ + int curfdstate = fd1094_set_state(keyregion, -1); + int nomatter = 0, locked = 0, guessed = 0; + int reps = keystatus_words / KEY_SIZE; + uint8_t newstat[5]; + int pcoffs; + + /* determine the new status for each word */ + for (pcoffs = 0; pcoffs < possdata->length; pcoffs++) + { + int pnum; + + /* default to setting the requested status */ + newstat[pcoffs] = status; + + /* see if the current word was the same across all possibilities */ + for (pnum = 0; pnum < posscount; pnum++) + if (posslist[pnum].instrbuffer[pcoffs*2+0] != possdata->instrbuffer[pcoffs*2+0] || + posslist[pnum].instrbuffer[pcoffs*2+1] != possdata->instrbuffer[pcoffs*2+1]) + break; + + /* if so, lock, don't guess */ + if (pnum == posscount) + newstat[pcoffs] = STATUS_LOCKED; + } + + /* iterate over words in the opcode */ + for (pcoffs = 0; pcoffs < possdata->length; pcoffs++) + { + int pcaddr = possdata->basepc/2 + pcoffs; + int keyaddr = addr_to_keyaddr(pcaddr); + int keystat = keystatus[pcaddr] & STATUS_MASK; + int repnum; + + /* if the status doesn't match and isn't "no change", then set the status */ + if (keystat != STATUS_NOCHANGE) + { + keystatus[keyaddr] = (keystatus[keyaddr] & ~HIBITS_MASK) | (possdata->keybuffer[pcoffs] & HIBITS_MASK); + keystatus[pcaddr] = (keystatus[pcaddr] & ~STATE_MASK & ~STATUS_MASK) | (curfdstate << 8) | newstat[pcoffs]; + keydirty = true; + } + else + keystatus[pcaddr] = (keystatus[pcaddr] & ~STATE_MASK) | (curfdstate << 8); + + /* if we're now locked, propogate across all reps */ + keystat = keystatus[pcaddr] & STATUS_MASK; + if (keystat == STATUS_LOCKED) + for (repnum = 0; repnum < reps; repnum++) + { + keystatus[repnum * KEY_SIZE + keyaddr] = (keystatus[repnum * KEY_SIZE + keyaddr] & ~STATUS_MASK) | STATUS_LOCKED; + if ((keyaddr & 0x1ffc) == 0x1000) + keystatus[repnum * KEY_SIZE + keyaddr - 0x1000] = (keystatus[repnum * KEY_SIZE + keyaddr - 0x1000] & ~STATUS_MASK) | STATUS_LOCKED; + } + + /* update the final key status */ + if (keystat == STATUS_LOCKED) + locked++; + else if (keystat == STATUS_GUESS) + guessed++; + else if (keystat == STATUS_NOCHANGE) + nomatter++; + } + + machine.debugger().console().printf("PC=%06X: locked %d, guessed %d, nochange %d\n", possdata->basepc, locked, guessed, nomatter); +} + + +/*----------------------------------------------- + perform_constrained_search - look for + the next global key that will match the + given sequence/mask pair +-----------------------------------------------*/ + +static void perform_constrained_search(running_machine &machine) +{ + uint32_t global; + + /* ensure our first 4 constraints are what we expect */ + assert(constraints[0].pc == 0x000000); + assert(constraints[1].pc == 0x000002); + assert(constraints[2].pc == 0x000004); + assert(constraints[3].pc == 0x000006); + + /* start with a 0 global key and brute force from there */ + global = 0; + + /* loop until we run out of possibilities */ + while (1) + { + uint16_t output[4]; + int numseeds; + + /* look for the next global key match */ + global = find_global_key_matches(global + 1, output); + if (global == 0) + break; +// machine.debugger().console().printf("Checking global key %08X (PC=%06X)....\n", global, (output[2] << 16) | output[3]); + + /* use the IRQ handler to find more possibilities */ + numseeds = find_constraint_sequence(global, false); + if (numseeds > 0) + { + int i; + for (i = 0; i < numseeds; i++) + machine.debugger().console().printf(" Possible: global=%08X seed=%06X pc=%04X\n", global, possible_seed[i], output[3]); + } + } +} + + +/*----------------------------------------------- + find_global_key_matches - look for + the next global key that will match the + given sequence/mask pair +-----------------------------------------------*/ + +static uint32_t find_global_key_matches(uint32_t startwith, uint16_t *output) +{ + int key0, key1, key2, key3; + uint8_t key[4]; + + /* iterate over the first key byte, allowing all possible values */ + for (key0 = (startwith >> 24) & 0xff; key0 < 256; key0++) + { + /* set the key and reset the fd1094 */ + key[0] = key0; + startwith &= 0x00ffffff; + fd1094_set_state(key, FD1094_STATE_RESET); + + /* if we match, iterate over the second key byte */ + output[0] = fd1094_decode(0x000000, coderegion[0], key, true); + if ((output[0] & constraints[0].mask) == constraints[0].value) + + /* iterate over the second key byte, limiting the scope to known valid keys */ + for (key1 = (startwith >> 16) & 0xff; key1 < 256; key1++) + if ((key1 & 0xf8) == 0xa8 || (key1 & 0xf8) == 0xf8) + { + /* set the key and reset the fd1094 */ + key[1] = key1; + startwith &= 0x0000ffff; + fd1094_set_state(key, FD1094_STATE_RESET); + + /* if we match, iterate over the third key byte */ + output[1] = fd1094_decode(0x000001, coderegion[1], key, true); + if ((output[1] & constraints[1].mask) == constraints[1].value) + + /* iterate over the third key byte, limiting the scope to known valid keys */ + for (key2 = (startwith >> 8) & 0xff; key2 < 256; key2++) + if ((key2 & 0xc0) == 0xc0) + { + /* set the key and reset the fd1094 */ + key[2] = key2; + startwith &= 0x000000ff; + fd1094_set_state(key, FD1094_STATE_RESET); + + /* if we match, iterate over the fourth key byte */ + output[2] = fd1094_decode(0x000002, coderegion[2], key, true); + if ((output[2] & constraints[2].mask) == constraints[2].value) + + /* iterate over the fourth key byte, limiting the scope to known valid keys */ + for (key3 = (startwith >> 0) & 0xff; key3 < 256; key3++) + if ((key3 & 0xc0) == 0xc0) + { + /* set the key and reset the fd1094 */ + key[3] = key3; + startwith = 0; + fd1094_set_state(key, FD1094_STATE_RESET); + + /* if we match, return the value */ + output[3] = fd1094_decode(0x000003, coderegion[3], key, true); + if ((output[3] & constraints[3].mask) == constraints[3].value) + return (key0 << 24) | (key1 << 16) | (key2 << 8) | key3; + } + } + } + } + return 0; +} + + +/*----------------------------------------------- + find_constraint_sequence - look for a + sequence of decoded words at the given + address, and optionally verify that there + are valid PRNG keys that could generate the + results +-----------------------------------------------*/ + +static int find_constraint_sequence(uint32_t global, int quick) +{ + const fd1094_constraint *minkeyaddr = &constraints[4]; + const fd1094_constraint *maxkeyaddr = &constraints[4]; + const fd1094_constraint *curr; + int keyvalue, keyaddr, keysneeded; + int seedcount = 0; + uint16_t decrypted; + uint8_t key[8192]; + uint8_t keymask; + offs_t pcaddr; + + /* if we don't have any extra constraints, we're good */ + if (constcount <= 4) + return -1; + + /* set the global key */ + key[0] = global >> 24; + key[1] = global >> 16; + key[2] = global >> 8; + key[3] = global >> 0; + fd1094_set_state(key, -1); + + /* first see if it is even possible, regardless of PRNG */ + for (curr = &constraints[4]; curr < &constraints[constcount]; curr++) + { + /* get the key address and value for this offset */ + pcaddr = curr->pc / 2; + keyaddr = addr_to_keyaddr(pcaddr); + keymask = mask_for_keyaddr(keyaddr); + + /* track the minumum and maximum key addresses, but only for interesting combinations */ + if ((coderegion[pcaddr] & 0xe000) != 0x0000) + { + if (keyaddr < addr_to_keyaddr(minkeyaddr->pc / 2)) + minkeyaddr = curr; + if (keyaddr > addr_to_keyaddr(maxkeyaddr->pc / 2)) + maxkeyaddr = curr; + } + + /* set the state */ + fd1094_set_state(key, curr->state); + + /* brute force search this byte */ + for (keyvalue = 0; keyvalue < 256; keyvalue++) + if ((keyvalue & keymask) == keymask) + { + /* see if this works */ + key[keyaddr] = keyvalue; + decrypted = fd1094_decode(pcaddr, coderegion[pcaddr], key, false); + + /* if we got a match, stop; we're done */ + if ((decrypted & curr->mask) == curr->value) + break; + } + + /* if we failed, we're done */ + if (keyvalue == 256) + return 0; + } + + /* if we're quick, that's all the checking we do */ + if (quick) + return -1; + + /* determine how many keys we need to cover our whole range */ + keysneeded = addr_to_keyaddr(maxkeyaddr->pc / 2) + 1 - addr_to_keyaddr(minkeyaddr->pc / 2); + + /* now do the more thorough search */ + pcaddr = minkeyaddr->pc / 2; + keyaddr = addr_to_keyaddr(pcaddr); + keymask = mask_for_keyaddr(keyaddr); + + /* set the state */ + fd1094_set_state(key, minkeyaddr->state); + + /* brute force search the first byte key of the key */ + for (keyvalue = 0; keyvalue < 256; keyvalue++) + if ((keyvalue & keymask) == keymask) + { + /* see if this works */ + key[keyaddr] = keyvalue; + decrypted = fd1094_decode(pcaddr, coderegion[pcaddr], key, false); + + /* if we got a match, then iterate over all possible PRNG sequences starting with this */ + if ((decrypted & minkeyaddr->mask) == minkeyaddr->value) + { + uint32_t seedlow; + +// machine.debugger().console().printf("Global %08X ... Looking for keys that generate a keyvalue of %02X at %04X\n", +// global, keyvalue, keyaddr); + + /* iterate over seed possibilities */ + for (seedlow = 0; seedlow < (1 << 16); seedlow++) + { + /* start with the known upper bits together with the 16 guessed lower bits */ + uint32_t seedstart = (~keyvalue << 16) | seedlow; + + /* generate data starting with this seed into the key */ + generate_key_bytes(key, keyaddr + 1, keysneeded - 1, seedstart); + + /* if the whole thing matched, record the match */ + if (does_key_work_for_constraints(coderegion, key)) + { + seedstart = reconstruct_base_seed(keyaddr, seedstart); + if ((seedstart & 0x3fffff) != 0) + possible_seed[seedcount++] = seedstart; + } + } + } + } + + return seedcount; +} + + +/*----------------------------------------------- + does_key_work_for_constraints - return true + if the given key might work for a given set + of constraints +-----------------------------------------------*/ + +static int does_key_work_for_constraints(const uint16_t *base, uint8_t *key) +{ + const fd1094_constraint *curr; + uint16_t decrypted; + + /* iterate over the sequence */ + for (curr = &constraints[4]; curr < &constraints[constcount]; curr++) + { + offs_t pcaddr = curr->pc / 2; + int keyaddr = addr_to_keyaddr(pcaddr); + uint8_t keymask = mask_for_keyaddr(keyaddr); + int hibits; + + /* set the state */ + fd1094_set_state(key, curr->state); + + /* iterate over high bits (1 per byte) */ + for (hibits = 0; hibits < 0x100; hibits += 0x40) + if ((hibits & keymask) == keymask) + { + /* update the key bits */ + key[keyaddr] = (key[keyaddr] & ~0xc0) | hibits; + + /* decrypt using this key; stop if we get a match */ + decrypted = fd1094_decode(pcaddr, base[pcaddr], key, false); + if ((decrypted & curr->mask) == curr->value) + break; + } + + /* if we failed to match, we're done */ + if (hibits >= 0x100) + return false; + } + + /* got a match on all entries */ + return true; +} + + +/*----------------------------------------------- + reconstruct_base_seed - given the seed + value at a particular key address, return + the seed that would be used to generate the + first key value (at offset 4) +-----------------------------------------------*/ + +static uint32_t reconstruct_base_seed(int keybaseaddr, uint32_t startseed) +{ + uint32_t seed = startseed; + uint32_t window[8192]; + int index = 0; + + /* keep generating, starting from the start seed until we re-generate the start seed */ + /* note that some sequences are smaller than the window, so we also have to ensure */ + /* that we generate at least one full window's worth of data */ + do + { + seed = seed * 0x29; + seed += seed << 16; + window[index++ % std::size(window)] = seed; + } while (((startseed ^ seed) & 0x3fffff) != 0 || index < std::size(window)); + + /* when we break, we have overshot */ + index--; + + /* back up to where we would have been at address 3 */ + index -= keybaseaddr - 3; + if (index < 0) + index += std::size(window); + + /* return the value from the window at that location */ + return window[index % std::size(window)] & 0x3fffff; +} + + +/*----------------------------------------------- + Table of opcode parameters +-----------------------------------------------*/ + +#define ENTRY(a,b,c,d) { #a, #b, c, d }, + +static const struct +{ + const char * bitstring; + const char * eastring; + uint32_t flags; + const char * instring; +} instr_table[] = +{ + ENTRY(1100...100000..., ........., OF_BYTE | OF_RARE, "ABCD Dn,Dm") + ENTRY(1100...100001..., ........., OF_BYTE | OF_RARE, "ABCD -(An),-(Am)") + ENTRY(1101...000......, d.A+-DBIP, OF_BYTE | OF_EASRC, "ADD.B <ea>,Dn") + ENTRY(1101...001......, daA+-DBIP, OF_WORD | OF_EASRC, "ADD.W <ea>,Dn") + ENTRY(1101...010......, daA+-DBIP, OF_LONG | OF_EASRC, "ADD.L <ea>,Dn") + ENTRY(1101...011......, daA+-DBIP, OF_WORD | OF_EASRC, "ADDA.W <ea>,An") + ENTRY(1101...100......, ..A+-DB.., OF_BYTE | OF_EADST, "ADD.B Dn,<ea>") + ENTRY(1101...101......, ..A+-DB.., OF_WORD | OF_EADST, "ADD.W Dn,<ea>") + ENTRY(1101...110......, ..A+-DB.., OF_LONG | OF_EADST, "ADD.L Dn,<ea>") + ENTRY(1101...111......, daA+-DBIP, OF_LONG | OF_EASRC, "ADDA.L <ea>,An") + ENTRY(0000011000......, d.A+-DB.., OF_BYTE | OF_EADST | OF_IMMB, "ADDI.B #x,<ea>") + ENTRY(0000011001......, d.A+-DB.., OF_WORD | OF_EADST | OF_IMMW, "ADDI.W #x,<ea>") + ENTRY(0000011010......, d.A+-DB.., OF_LONG | OF_EADST | OF_IMML, "ADDI.L #x,<ea>") + ENTRY(0101...000......, d.A+-DB.., OF_BYTE | OF_EADST, "ADDQ.B #x,<ea>") + ENTRY(0101...001......, daA+-DB.., OF_WORD | OF_EADST, "ADDQ.W #x,<ea>") + ENTRY(0101...010......, daA+-DB.., OF_LONG | OF_EADST, "ADDQ.L #x,<ea>") + ENTRY(1101...10000...., ........., OF_BYTE | OF_RARE, "ADDX.B") + ENTRY(1101...10100...., ........., OF_WORD | OF_RARE, "ADDX.W") + ENTRY(1101...11000...., ........., OF_LONG | OF_RARE, "ADDX.L") + ENTRY(1100...000......, d.A+-DBIP, OF_BYTE | OF_EASRC, "AND.B <ea>,Dn") + ENTRY(1100...001......, d.A+-DBIP, OF_WORD | OF_EASRC, "AND.W <ea>,Dn") + ENTRY(1100...010......, d.A+-DBIP, OF_LONG | OF_EASRC, "AND.L <ea>,Dn") + ENTRY(1100...100......, ..A+-DB.., OF_BYTE | OF_EADST, "AND.B Dn,<ea>") + ENTRY(1100...101......, ..A+-DB.., OF_WORD | OF_EADST, "AND.W Dn,<ea>") + ENTRY(1100...110......, ..A+-DB.., OF_LONG | OF_EADST, "AND.L Dn,<ea>") + ENTRY(0000001000111100, ........., OF_BYTE | OF_IMMB | OF_RARE, "ANDI #x,CCR") + ENTRY(0000001000......, d.A+-DB.., OF_BYTE | OF_EADST | OF_IMMB, "ANDI.B #x,<ea>") + ENTRY(0000001001......, d.A+-DB.., OF_WORD | OF_EADST | OF_IMMW, "ANDI.W #x,<ea>") + ENTRY(0000001010......, d.A+-DB.., OF_LONG | OF_EADST | OF_IMML, "ANDI.L #x,<ea>") + ENTRY(1110....00.00..., ........., OF_BYTE, "ASL/ASR.B") + ENTRY(1110....01.00..., ........., OF_WORD, "ASL/ASR.W") + ENTRY(1110....10.00..., ........., OF_LONG, "ASL/ASR.L") + ENTRY(1110000.11......, ..A+-DB.., OF_WORD | OF_EADST, "ASL/ASR.W <ea>") + ENTRY(0110000000000000, ........., OF_WORD | OF_IMMW | OF_BRANCH, "BRA.W <dst>") + ENTRY(01100000.......0, ........., OF_BYTE | OF_BRANCH, "BRA.B <dst>") + ENTRY(0110000100000000, ........., OF_WORD | OF_IMMW | OF_BRANCH, "BSR.W <dst>") + ENTRY(01100001.......0, ........., OF_BYTE | OF_BRANCH, "BSR.B <dst>") + ENTRY(0110....00000000, ........., OF_WORD | OF_IMMW | OF_BRANCH, "Bcc.W <dst>") + ENTRY(0110...........0, ........., OF_BYTE | OF_BRANCH, "Bcc.B <dst>") + ENTRY(0000...101......, d.A+-DB.., OF_BYTE | OF_EADST, "BCHG Dn,<ea>") + ENTRY(0000100001......, d.A+-DB.., OF_BYTE | OF_EADST | OF_IMMBIT, "BCHG #x,<ea>") + ENTRY(0000...110......, d.A+-DB.., OF_BYTE | OF_EADST, "BCLR Dn,<ea>") + ENTRY(0000100010......, d.A+-DB.., OF_BYTE | OF_EADST | OF_IMMBIT, "BCLR #x,<ea>") + ENTRY(0000...111......, d.A+-DB.., OF_BYTE | OF_EADST, "BSET Dn,<ea>") + ENTRY(0000100011......, d.A+-DB.., OF_BYTE | OF_EADST | OF_IMMBIT, "BSET #x,<ea>") + ENTRY(0000...100......, d.A+-DBIP, OF_BYTE | OF_EADST, "BTST Dn,<ea>") + ENTRY(0000100000......, d.A+-DB.P, OF_BYTE | OF_EADST | OF_IMMBIT, "BTST #x,<ea>") + ENTRY(0100...110......, d.A+-DBIP, OF_WORD | OF_EADST | OF_RARE, "CHK.W <ea>,Dn") + ENTRY(0100001000......, d.A+-DB.., OF_BYTE | OF_EADST, "CLR.B <ea>") + ENTRY(0100001001......, d.A+-DB.., OF_WORD | OF_EADST, "CLR.W <ea>") + ENTRY(0100001010......, d.A+-DB.., OF_LONG | OF_EADST, "CLR.L <ea>") + ENTRY(1011...000......, d.A+-DBIP, OF_BYTE | OF_EASRC, "CMP.B <ea>,Dn") + ENTRY(1011...001......, daA+-DBIP, OF_WORD | OF_EASRC, "CMP.W <ea>,Dn") + ENTRY(1011...010......, daA+-DBIP, OF_LONG | OF_EASRC, "CMP.L <ea>,Dn") + ENTRY(1011...011......, daA+-DBIP, OF_WORD | OF_EASRC, "CMPA.W <ea>,Dn") + ENTRY(1011...111......, daA+-DBIP, OF_LONG | OF_EASRC, "CMPA.L <ea>,Dn") + ENTRY(0000110000......, d.A+-DB.., OF_BYTE | OF_EASRC | OF_IMMB, "CMPI.B #x,<ea>") + ENTRY(0000110001......, d.A+-DB.., OF_WORD | OF_EASRC | OF_IMMW, "CMPI.W #x,<ea>") + ENTRY(0000110010......, d.A+-DB.., OF_LONG | OF_EASRC | OF_IMML, "CMPI.L #x,<ea>") + ENTRY(1011...100001..., ........., OF_BYTE | OF_RARE, "CMPM.B") + ENTRY(1011...101001..., ........., OF_WORD | OF_RARE, "CMPM.W") + ENTRY(1011...110001..., ........., OF_LONG | OF_RARE, "CMPM.L") + ENTRY(0101....11001..., ........., OF_WORD | OF_IMMW | OF_BRANCH, "DBcc.W <dst>") + ENTRY(1000...111......, d.A+-DBIP, OF_WORD | OF_EASRC, "DIVS.W <ea>,Dn") + ENTRY(1000...011......, d.A+-DBIP, OF_WORD | OF_EASRC, "DIVU.W <ea>,Dn") + ENTRY(1011...100......, d.A+-DB.., OF_BYTE | OF_EADST, "EOR.B Dn,<ea>") + ENTRY(1011...101......, d.A+-DB.., OF_WORD | OF_EADST, "EOR.W Dn,<ea>") + ENTRY(1011...110......, d.A+-DB.., OF_LONG | OF_EADST, "EOR.L Dn,<ea>") + ENTRY(0000101000111100, ........., OF_BYTE | OF_IMMB | OF_RARE, "EORI #x,CCR") + ENTRY(0000101000......, d.A+-DB.., OF_BYTE | OF_EADST | OF_IMMB, "EORI.B #x,<ea>") + ENTRY(0000101001......, d.A+-DB.., OF_WORD | OF_EADST | OF_IMMW, "EORI.W #x,<ea>") + ENTRY(0000101010......, d.A+-DB.., OF_LONG | OF_EADST | OF_IMML, "EORI.L #x,<ea>") + ENTRY(1100...101000..., ........., OF_LONG, "EXG Dn,Dn") + ENTRY(1100...101001..., ........., OF_LONG, "EXG An,An") + ENTRY(1100...110001..., ........., OF_LONG, "EXG Dn,An") + ENTRY(0100100010000..., ........., OF_WORD, "EXT.W Dn") + ENTRY(0100100011000..., ........., OF_WORD, "EXT.L Dn") + ENTRY(0100111011......, ..A..DB.P, OF_WORD | OF_EASRC | OF_JMP, "JMP <ea>") + ENTRY(0100111010......, ..A..DB.P, OF_WORD | OF_EASRC | OF_JMP, "JSR <ea>") + ENTRY(0100...111......, ..A..DB.P, OF_BYTE | OF_EASRC, "LEA <ea>,An") + ENTRY(0100111001010..., ........., OF_WORD | OF_IMMW | OF_RARE, "LINK An,#x") + ENTRY(1110....00.01..., ........., OF_BYTE, "LSL/LSR.B Dn") + ENTRY(1110....01.01..., ........., OF_WORD, "LSL/LSR.W Dn") + ENTRY(1110....10.01..., ........., OF_LONG, "LSL/LSR.L Dn") + ENTRY(1110001.11......, ..A+-DB.., OF_WORD | OF_EADST, "LSL/LSR.W <ea>") + ENTRY(0001............, d.A+-DBIP, OF_BYTE | OF_EASRC | OF_MOVE, "MOVE.B <ea>,<ea>") + ENTRY(0011............, daA+-DBIP, OF_WORD | OF_EASRC | OF_MOVE, "MOVE.W <ea>,<ea>") + ENTRY(0010............, daA+-DBIP, OF_LONG | OF_EASRC | OF_MOVE, "MOVE.L <ea>,<ea>") + ENTRY(0011...001......, daA+-DBIP, OF_WORD | OF_EASRC, "MOVEA.W <ea>,An") + ENTRY(0010...001......, daA+-DBIP, OF_LONG | OF_EASRC, "MOVEA.L <ea>,An") + ENTRY(0100010011......, d.A+-DBIP, OF_WORD | OF_EASRC | OF_RARE, "MOVE <ea>,CCR") + ENTRY(0100000011......, d.A+-DB.., OF_WORD | OF_EADST | OF_RARE, "MOVE SR,<ea>") + ENTRY(0100100010......, ..A.-DB.., OF_WORD | OF_EADST | OF_IMMW, "MOVEM.W <regs>,<ea>") + ENTRY(0100100011......, ..A.-DB.., OF_LONG | OF_EADST | OF_IMMW, "MOVEM.L <regs>,<ea>") + ENTRY(0100110010......, ..A+.DB.P, OF_WORD | OF_EASRC | OF_IMMW, "MOVEM.W <ea>,<regs>") + ENTRY(0100110011......, ..A+.DB.P, OF_LONG | OF_EASRC | OF_IMMW, "MOVEM.L <ea>,<regs>") + ENTRY(0000...100001..., ........., OF_WORD | OF_IMMW | OF_RARE, "MOVEP.W (d16,Ay),Dn") + ENTRY(0000...101001..., ........., OF_LONG | OF_IMMW | OF_RARE, "MOVEP.L (d16,Ay),Dn") + ENTRY(0000...110001..., ........., OF_WORD | OF_IMMW | OF_RARE, "MOVEP.W Dn,(d16,Ay)") + ENTRY(0000...111001..., ........., OF_LONG | OF_IMMW | OF_RARE, "MOVEP.L Dn,(d16,Ay)") + ENTRY(0111...0........, ........., OF_LONG, "MOVEQ #x,Dn") + ENTRY(1100...111......, d.A+-DBIP, OF_WORD | OF_EASRC, "MULS.W <ea>,Dn") + ENTRY(1100...011......, d.A+-DBIP, OF_WORD | OF_EASRC, "MULU.W <ea>,Dn") + ENTRY(0100100000......, d.A+-DB.., OF_BYTE | OF_EADST | OF_RARE, "NBCD <ea>") + ENTRY(0100010000......, d.A+-DB.., OF_BYTE | OF_EADST, "NEG.B <ea>") + ENTRY(0100010001......, d.A+-DB.., OF_WORD | OF_EADST, "NEG.W <ea>") + ENTRY(0100010010......, d.A+-DB.., OF_LONG | OF_EADST, "NEG.L <ea>") + ENTRY(0100000000......, d.A+-DB.., OF_BYTE | OF_EADST | OF_RARE, "NEGX.B <ea>") + ENTRY(0100000001......, d.A+-DB.., OF_WORD | OF_EADST | OF_RARE, "NEGX.W <ea>") + ENTRY(0100000010......, d.A+-DB.., OF_LONG | OF_EADST | OF_RARE, "NEGX.L <ea>") + ENTRY(0100111001110001, ........., 0, "NOP") + ENTRY(0100011000......, d.A+-DB.., OF_BYTE | OF_EADST, "NOT.B <ea>") + ENTRY(0100011001......, d.A+-DB.., OF_WORD | OF_EADST, "NOT.W <ea>") + ENTRY(0100011010......, d.A+-DB.., OF_LONG | OF_EADST, "NOT.L <ea>") + ENTRY(1000...000......, d.A+-DBIP, OF_BYTE | OF_EASRC, "OR.B <ea>,Dn") + ENTRY(1000...001......, d.A+-DBIP, OF_WORD | OF_EASRC, "OR.W <ea>,Dn") + ENTRY(1000...010......, d.A+-DBIP, OF_LONG | OF_EASRC, "OR.L <ea>,Dn") + ENTRY(1000...100......, ..A+-DB.., OF_BYTE | OF_EADST, "OR.B Dn,<ea>") + ENTRY(1000...101......, ..A+-DB.., OF_WORD | OF_EADST, "OR.W Dn,<ea>") + ENTRY(1000...110......, ..A+-DB.., OF_LONG | OF_EADST, "OR.L Dn,<ea>") + ENTRY(0000000000111100, ........., OF_BYTE | OF_IMMB | OF_RARE, "ORI #x,CCR") + ENTRY(0000000000......, d.A+-DB.., OF_BYTE | OF_EADST | OF_IMMB, "ORI.B #x,<ea>") + ENTRY(0000000001......, d.A+-DB.., OF_WORD | OF_EADST | OF_IMMW, "ORI.W #x,<ea>") + ENTRY(0000000010......, d.A+-DB.., OF_LONG | OF_EADST | OF_IMML, "ORI.L #x,<ea>") + ENTRY(0100100001......, ..A..DB.P, OF_BYTE | OF_EADST | OF_RARE, "PEA <ea>") + ENTRY(1110....00.11..., ........., OF_BYTE, "ROL/ROR.B Dn") + ENTRY(1110....01.11..., ........., OF_WORD, "ROL/ROR.W Dn") + ENTRY(1110....10.11..., ........., OF_LONG, "ROL/ROR.L Dn") + ENTRY(1110011.11......, ..A+-DB.., OF_WORD | OF_EADST, "ROL/ROR.W <ea>") + ENTRY(1110....00.10..., ........., OF_BYTE | OF_RARE, "ROXL/ROXR.B Dn") + ENTRY(1110....01.10..., ........., OF_WORD | OF_RARE, "ROXL/ROXR.W Dn") + ENTRY(1110....10.10..., ........., OF_LONG | OF_RARE, "ROXL/ROXR.L Dn") + ENTRY(1110010.11......, ..A+-DB.., OF_WORD | OF_EADST | OF_RARE, "ROXL/ROXR.W <ea>") + ENTRY(0100111001110111, ........., OF_RARE, "RTR") + ENTRY(0100111001110101, ........., OF_RARE, "RTS") + ENTRY(1000...100000..., ........., OF_BYTE | OF_RARE, "SBCD Dn,Dm") + ENTRY(1000...100001..., ........., OF_BYTE | OF_RARE, "SBCD -(An),-(Am)") + ENTRY(0101....11......, d.A+-DB.., OF_BYTE | OF_EADST | OF_RARE, "Scc <ea>") + ENTRY(1001...000......, d.A+-DBIP, OF_BYTE | OF_EASRC, "SUB.B <ea>,Dn") + ENTRY(1001...001......, daA+-DBIP, OF_WORD | OF_EASRC, "SUB.W <ea>,Dn") + ENTRY(1001...010......, daA+-DBIP, OF_LONG | OF_EASRC, "SUB.L <ea>,Dn") + ENTRY(1001...011......, daA+-DBIP, OF_WORD | OF_EASRC, "SUBA.W <ea>,An") + ENTRY(1001...100......, ..A+-DB.., OF_BYTE | OF_EADST, "SUB.B Dn,<ea>") + ENTRY(1001...101......, ..A+-DB.., OF_WORD | OF_EADST, "SUB.W Dn,<ea>") + ENTRY(1001...110......, ..A+-DB.., OF_LONG | OF_EADST, "SUB.L Dn,<ea>") + ENTRY(1001...111......, daA+-DBIP, OF_LONG | OF_EASRC, "SUBA.L <ea>,An") + ENTRY(0000010000......, d.A+-DB.., OF_BYTE | OF_EADST | OF_IMMB, "SUBI.B #x,<ea>") + ENTRY(0000010001......, d.A+-DB.., OF_WORD | OF_EADST | OF_IMMW, "SUBI.W #x,<ea>") + ENTRY(0000010010......, d.A+-DB.., OF_LONG | OF_EADST | OF_IMML, "SUBI.L #x,<ea>") + ENTRY(0101...100......, d.A+-DB.., OF_BYTE | OF_EADST, "SUBQ.B #x,<ea>") + ENTRY(0101...101......, daA+-DB.., OF_WORD | OF_EADST, "SUBQ.W #x,<ea>") + ENTRY(0101...110......, daA+-DB.., OF_LONG | OF_EADST, "SUBQ.L #x,<ea>") + ENTRY(1001...10000...., ........., OF_BYTE | OF_RARE, "SUBX.B") + ENTRY(1001...10100...., ........., OF_WORD | OF_RARE, "SUBX.W") + ENTRY(1001...11000...., ........., OF_LONG | OF_RARE, "SUBX.L") + ENTRY(0100100001000..., ........., OF_LONG | OF_RARE, "SWAP Dn") + ENTRY(0100101011......, d.A+-DB.., OF_BYTE | OF_EASRC | OF_RARE, "TAS <ea>") + ENTRY(010011100100...., ........., OF_RARE, "TRAP #x") + ENTRY(0100111001110110, ........., OF_RARE, "TRAPV") + ENTRY(0100101000......, d.A+-DB.., OF_BYTE | OF_EASRC, "TST.B <ea>") + ENTRY(0100101001......, d.A+-DB.., OF_WORD | OF_EASRC, "TST.W <ea>") + ENTRY(0100101010......, d.A+-DB.., OF_LONG | OF_EASRC, "TST.L <ea>") + ENTRY(0100111001011..., ........., OF_RARE, "UNLK") + ENTRY(0000001001111100, ........., OF_WORD | OF_IMMW | OF_RARE, "ANDI #x,SR") + ENTRY(0000101001111100, ........., OF_WORD | OF_IMMW | OF_RARE, "EORI #x,SR") + ENTRY(0100000011......, d.A+-DB.., OF_WORD | OF_EADST | OF_RARE, "MOVE SR,<ea>") + ENTRY(0100011011......, d.A+-DBIP, OF_WORD | OF_EASRC | OF_RARE, "MOVE <ea>,SR") + ENTRY(010011100110...., ........., OF_LONG | OF_RARE, "MOVE USP") + ENTRY(0000000001111100, ........., OF_WORD | OF_IMMW | OF_RARE, "ORI #x,SR") + ENTRY(0100111001110000, ........., OF_RARE, "RESET") + ENTRY(0100111001110011, ........., OF_RARE, "RTE") + ENTRY(0100111001110010, ........., OF_WORD | OF_IMMW | OF_RARE, "STOP #x") +}; + + +/*----------------------------------------------- + build_optable - build up the opcode table +-----------------------------------------------*/ + +static void build_optable(running_machine &machine) +{ + int opnum, inum; + + /* allocate and initialize the opcode table */ + optable = std::make_unique<optable_entry[]>(65536); + for (opnum = 0; opnum < 65536; opnum++) + { + optable[opnum].flags = OF_INVALID; + optable[opnum].string = nullptr; + } + + /* now iterate over entries in our intruction table */ + for (inum = 0; inum < std::size(instr_table); inum++) + { + const char *bitstring = instr_table[inum].bitstring; + const char *eastring = instr_table[inum].eastring; + const char *instring = instr_table[inum].instring; + uint32_t flags = instr_table[inum].flags; + uint8_t ea_allowed[64], ea2_allowed[64]; + int bitnum, step, eanum, ea2num; + uint16_t mask = 0, value = 0; + + /* build up the mask and value from the bitstring */ + for (bitnum = 0; bitnum < 16; bitnum++) + { + assert(bitstring[bitnum] == '0' || bitstring[bitnum] == '1' || bitstring[bitnum] == '.'); + mask <<= 1; + value <<= 1; + if (bitstring[bitnum] != '.') + { + mask |= 1; + value |= (bitstring[bitnum] == '1'); + } + } + + /* if we have an EA, fill in the EA bits */ + memset(ea_allowed, 0, sizeof(ea_allowed)); + if (flags & (OF_EASRC | OF_EADST)) + { + assert((mask & 0x003f) == 0); + assert(eastring[0] == 'd' || eastring[0] == '.'); + if (eastring[0] == 'd') memset(&ea_allowed[0x00], 1, 8); + assert(eastring[1] == 'a' || eastring[1] == '.'); + if (eastring[1] == 'a') memset(&ea_allowed[0x08], 1, 8); + assert(eastring[2] == 'A' || eastring[2] == '.'); + if (eastring[2] == 'A') memset(&ea_allowed[0x10], 1, 8); + assert(eastring[3] == '+' || eastring[3] == '.'); + if (eastring[3] == '+') memset(&ea_allowed[0x18], 1, 8); + assert(eastring[4] == '-' || eastring[4] == '.'); + if (eastring[4] == '-') memset(&ea_allowed[0x20], 1, 8); + assert(eastring[5] == 'D' || eastring[5] == '.'); + if (eastring[5] == 'D') memset(&ea_allowed[0x28], 1, 16); + assert(eastring[6] == 'B' || eastring[6] == '.'); + if (eastring[6] == 'B') memset(&ea_allowed[0x38], 1, 2); + assert(eastring[7] == 'I' || eastring[7] == '.'); + if (eastring[7] == 'I') ea_allowed[0x3c] = 1; + assert(eastring[8] == 'P' || eastring[8] == '.'); + if (eastring[8] == 'P') memset(&ea_allowed[0x3a], 1, 2); + step = 0x40; + } + else + { + assert(strcmp(eastring, ".........") == 0); + ea_allowed[0] = 1; + step = 1; + } + + /* if we're a move instruction, fill in the EA2 bits */ + memset(ea2_allowed, 0, sizeof(ea2_allowed)); + if (flags & OF_MOVE) + { + assert((mask & 0x0fc0) == 0); + memset(&ea2_allowed[0x00], 1, 8); + memset(&ea2_allowed[0x10], 1, 42); + step = 0x1000; + } + else + ea2_allowed[0] = 1; + + /* iterate over allowed EAs and fill in the opcode entries */ + for (ea2num = 0; ea2num < 64; ea2num++) + if (ea2_allowed[ea2num]) + for (eanum = 0; eanum < 64; eanum++) + if (ea_allowed[eanum]) + { + uint16_t eabits = ((ea2num & 0x38) << 3) | ((ea2num & 0x07) << 9) | eanum; + + /* iterate over opcode entries */ + for (opnum = 0; opnum <= mask; opnum += step) + if ((opnum & mask) == value) + { + int length = 1; + + /* skip if we've already populated */ + if (optable[opnum | eabits].flags != OF_INVALID) + continue; + + /* determine the length of the opcode */ + if (flags & OF_ISIZEMASK) + length += ((flags & OF_ISIZEMASK) == OF_IMML) ? 2 : 1; + if ((eanum >= 0x28 && eanum <= 0x38) || eanum == 0x3a || eanum == 0x3b) + length += 1; + else if (eanum == 0x39) + length += 2; + else if (eanum == 0x3c) + length += ((flags & OF_SIZEMASK) == OF_LONG) ? 2 : 1; + if ((ea2num >= 0x28 && ea2num <= 0x38) || ea2num == 0x3a || ea2num == 0x3b) + length += 1; + else if (ea2num == 0x39) + length += 2; + else if (ea2num == 0x3c) + length += ((flags & OF_SIZEMASK) == OF_LONG) ? 2 : 1; + + /* make sure we match the disassembler */ + #ifdef MAME_DEBUG + { + char dummybuffer[40]; + uint8_t instrbuffer[10]; + instrbuffer[0] = (opnum | eabits) >> 8; + instrbuffer[1] = (opnum | eabits); + dummybuffer[0] = 0; + assert(length == (m68k_disassemble_raw(dummybuffer, 0, instrbuffer, instrbuffer, M68K_CPU_TYPE_68000) & 0xff) / 2); + } + #endif + + /* set the value of the entry in the table */ + optable[opnum | eabits].flags = flags | (length << 28); + optable[opnum | eabits].string = instring; + } + } + } +} + + +/*----------------------------------------------- + validate_ea - determine whether an EA is + valid or not, and return the length +-----------------------------------------------*/ + +static int validate_ea(address_space &space, uint32_t pc, uint8_t modereg, const uint8_t *parambase, uint32_t flags) +{ + uint32_t addr; + int valid; + + /* switch off of the mode */ + switch ((modereg >> 3) & 7) + { + case 0: /* Dn -- always good */ + case 1: /* An -- always good */ + case 2: /* (An) -- always good */ + case 3: /* (An)+ -- always good */ + case 4: /* -(An) -- always good */ + return 0; + + case 5: /* (d16,An) -- always good, but odd displacements are a warning for word/long */ + if ((flags & OF_SIZEMASK) != OF_BYTE && (parambase[1] & 1) == 1) + return -1; + return 1; + + case 6: /* (d8,An,Xn) -- always good, but odd displacements are a warning for word/long */ + /* also look for invalid extension words */ + if ((parambase[0] & 7) != 0) + return 1000; + if ((flags & OF_SIZEMASK) != OF_BYTE && (parambase[1] & 1) == 1) + return -1; + return 1; + + case 7: + switch (modereg & 7) + { + case 0: /* (xxx).W -- make sure it is not odd for word/long */ + addr = (int16_t)((parambase[0] << 8) | parambase[1]); + valid = addr_is_valid(space, addr & 0xffffff, flags); + return (valid == 0) ? 1000 : (valid == 2) ? -1 : 1; + + case 1: /* (xxx).L -- make sure it is not odd for word/long, and make sure upper byte of addr is 0 */ + valid = addr_is_valid(space, (parambase[0] << 24) | (parambase[1] << 16) | (parambase[2] << 8) | parambase[3], flags); + return (valid == 0) ? 1000 : (valid == 2) ? -2 : 2; + + case 2: /* (d16,PC) -- make sure it is not odd for word/long */ + valid = addr_is_valid(space, pc + (int16_t)((parambase[0] << 8) | parambase[1]), flags); + return (valid == 0) ? 1000 : (valid == 2) ? -1 : 1; + + case 3: /* (d8,PC,Xn) -- odd displacements are a warning for word/long */ + if ((parambase[0] & 7) != 0) + return 1000; + if ((flags & OF_SIZEMASK) != OF_BYTE && (parambase[1] & 1) == 1) + return -1; + return 1; + + case 4: /* immediate -- check high byte if byte-sized */ + if ((flags & OF_SIZEMASK) == OF_BYTE && parambase[0] != 0) + return 1000; + return ((flags & OF_SIZEMASK) == SIZE_LONG) ? 2 : 1; + } + break; + } + + /* should never get here */ + assert(false); + return 0; +} + + +/*----------------------------------------------- + validate_opcode - validate an opcode up to + the length specified +-----------------------------------------------*/ + +static int validate_opcode(address_space &space, uint32_t pc, const uint8_t *opdata, int maxwords) +{ + uint32_t immvalue = 0; + int iffy = false; + int offset = 0; + uint16_t opcode; + uint32_t flags; + int oplength; + + assert(maxwords >= 1); + + /* extract the opcode and look it up in our table */ + opcode = (opdata[offset*2+0] << 8) | opdata[offset*2+1]; + flags = optable[opcode].flags; + oplength = flags >> 28; + + /* weed out invalid opcodes immediately */ + offset++; + if (flags == OF_INVALID) + return 0; + iffy = ((flags & OF_RARE) != 0); + + /* if we're done, or if we don't have enough words, stop now */ + if (offset == oplength || maxwords < oplength) + return iffy ? -oplength : oplength; + + /* if the opcode has an immediate, process that */ + if (flags & OF_ISIZEMASK) + { + int neededwords = ((flags & OF_ISIZEMASK) == OF_IMML) ? 2 : 1; + + /* extract the immediate value */ + immvalue = (opdata[offset*2+0] << 8) | opdata[offset*2+1]; + if ((flags & OF_ISIZEMASK) == OF_IMML) + immvalue = (immvalue << 16) | (opdata[offset*2+2] << 8) | opdata[offset*2+3]; + + /* if it's a byte immediate, ensure the upper bits are 0 (except for -1) */ + if ((flags & OF_ISIZEMASK) == OF_IMMB && immvalue > 0xff && immvalue != 0xffff) + return 0; + + /* if it's a bit immediate, ensure all but the lower 3 bits are 0 */ + if ((flags & OF_ISIZEMASK) == OF_IMMBIT) + { + /* registers can do up to 32 bits */ + if ((opcode & 0x3f) < 8) + { + if (immvalue > 31) + return 0; + } + + /* memory operands can do up to 8 bits */ + else + { + if (immvalue > 7) + return 0; + } + } + + /* advance past the immedate */ + offset += neededwords; + } + + /* if we're a branch, validate the immediate value */ + if (flags & OF_BRANCH) + { + int valid; + + /* first adjust the PC based on the size of the branch */ + pc += 2; + if ((flags & OF_SIZEMASK) == OF_BYTE) + pc += (int8_t)opcode; + else if ((flags & OF_SIZEMASK) == OF_WORD) + pc += (int16_t)immvalue; + else + pc += immvalue; + + /* if we're odd or out of range, fail */ + valid = pc_is_valid(space, pc, flags); + if (valid == 0) + return 0; + if (valid == 2) + iffy = true; + } + + /* process the EA, if present */ + if (flags & (OF_EASRC | OF_EADST)) + { + int modereg = opcode & 0x003f; + int ealen = validate_ea(space, pc + offset*2, modereg, &opdata[offset*2], flags); + + /* if the ea was invalid, forward that result */ + if (ealen == 1000) + return 0; + + /* if the ea was iffy, indicate that */ + if (ealen < 0) + { + ealen = -ealen; + iffy = true; + } + + /* advance past the ea */ + offset += ealen; + } + + /* process the move EA, if present */ + if (flags & OF_MOVE) + { + int modereg = ((opcode & 0x01c0) >> 3) | ((opcode & 0x0e00) >> 9); + int ealen = validate_ea(space, pc + offset*2, modereg, &opdata[offset*2], flags); + + /* if the ea was invalid, forward that result */ + if (ealen == 1000) + return 0; + + /* if the ea was iffy, indicate that */ + if (ealen < 0) + { + ealen = -ealen; + iffy = true; + } + + /* advance past the ea */ + offset += ealen; + } + + /* at this point we should be at the end */ + assert(offset == oplength); + return iffy ? -oplength : oplength; +} + +#endif |