/** ** Supermodel ** A Sega Model 3 Arcade Emulator. ** Copyright 2011 Bart Trzynadlowski, Nik Henson ** ** This file is part of Supermodel. ** ** Supermodel 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 3 of the License, or (at your option) ** any later version. ** ** Supermodel 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 Supermodel. If not, see . **/ /* * Turbo68K: Motorola 680X0 emulator * Copyright 2000-2002 Bart Trzynadlowski, see "README.TXT" for terms of use */ /* * Make68K: This program emits Turbo68K. Please see README.TXT for details. * * Note: If you get a link error involving pow(), remember to link in the math * library. Try using "-lm". * * Note: I am not certain about dummy reads. The manual says they occur with * some instructions on the MC68000 and MC68008 only. Does this include * the MC68EC000? I'm assuming it does. Also, is a dummy read made for * all Scc instructions, or just ST? I'm assuming all of them do it. * * Note: Do not use the profiling feature (#define PROFILE.) It will add * useless junk to the emulator. * * Note: Future plans: I intend on removing as much of the Save/RestoreReg() * junk from the API functions as possible. PUSH/POPs should work much * better. * * Additional developers' notes pertaining to all aspects of Turbo68K can be * found at the very end of this file. */ #include #include #include #include #include #include "turbo68k.h" #define VERSION "0.6" #define SIZEOF_CONTEXT "(context_end - context_start)" #define SIZEOF_FETCHREGION "12" #define SIZEOF_DATAREGION "16" #define OFFSET_DATA_BASE "0" #define OFFSET_DATA_LIMIT "4" #define OFFSET_DATA_PTR "8" #define OFFSET_DATA_HANDLER "12" #define OFFSET_FETCH_BASE "0" #define OFFSET_FETCH_LIMIT "4" #define OFFSET_FETCH_PTR "8" #define WRITEPCTOMEM(dest) { \ e("mov dword ["dest"], esi\n");\ e("sub dword ["dest"], ebp\n");\ } #define WRITEPCTOREG(dest) { \ e("mov "dest", esi\n"); \ e("sub "dest", ebp\n"); \ } #define EMULATING() e("or byte [status], 1\n"); #define STOP_EMULATING() e("and byte [status], 0xfe\n"); #define STOP_CPU() e("or byte [status], 2\n"); #define UNSTOP_CPU() e("and byte [status], 0xfd\n"); #define INTERRUPT_PROCESSING() e("or byte [status], 4\n"); #define INTERRUPT_DONE() e("and byte [status], 0xfb\n"); #define STOP_RUNNING() { \ e("mov [remaining], ecx\n"); \ WRITEPCTOMEM("pc"); \ } #define A7 "[a+7*4]" #define SP "[__sp]" #define SR "[sr]" #define UNKNOWN_SIZE 0 #define BYTE_SIZE 1 #define WORD_SIZE 2 #define LONG_SIZE 3 unsigned short decoded[65536]; /* compressed jump table data */ char id[17] = { '\0' }; /* prepended to identifiers */ FILE *fp; /* output file */ int mpu = 68000; /* 68000 by default */ unsigned addr_bits = 24; /* 24-bit by default */ unsigned addr_mask = 0xffffff; int illegal = 1; /* illegal instruction traps */ int dummyread = 1; /* dummy reads (68000 only) */ int skip = 0; /* CPU (idle loop) skipping */ int brafetch = 0; /* branch fetch ptr updating */ int pcfetch = 0; /* special PC-relative fetching */ int multiaddr = 1; /* supervisor and user spaces */ int memmap_type = 0; /* memory map type */ int call_convention = 0; /* calling convention */ int debug = 0; /* call debug function at every instruction */ int mmx = 0; /* MMX support -- don't use! */ unsigned num_handlers = 0; /* # of inst. handlers emitted */ #ifdef PROFILE char *prof[512]; int prof_i = 0; #endif /***************************************************************************** * General Emitters */ void e(char *input, ...) { va_list arg; char text[256]; va_start(arg, input); vsprintf(text, input, arg); va_end(arg); fprintf(fp, text); } void EmitLabel(char *label) { e("%s:\n", label); } void Align(int i) { e("times ($$-$) & %d nop\n", i - 1); } void CacheAlign() { Align(32); } void EmitGlobalLabel(char *label) { e("global %s%s, _%s%s, %s%s_\n", id, label, id, label, id, label); e("%s%s:\n_%s%s:\n%s%s_:\n", id, label, id, label, id, label); } /***************************************************************************** * Utility Functions */ /* * SizeOfContext(): Returns the size of the context if needed by the emitter. * This should be used instead of C's sizeof(). */ int SizeOfContext() { switch (mpu) { case 68010: return sizeof(struct TURBO68K_CONTEXT_68010); } return sizeof(struct TURBO68K_CONTEXT_68000); } /* * NameOfContext(): Returns the name of the context based on the MPU type, * but NOT with the identifier in front of it. */ static char *NameOfContext() { static char name[16 + 6 + 1]; sprintf(name, "turbo68kcontext_%d", mpu); return name; } /* * NOTE: Only pass 32-bit or 8-bit (XL, not XH regs) to the SaveReg/SaveRegTo, * RestoreReg/RestoreRegTo functions. Don't ever pass 16-bit regs! */ /* * SaveReg/RestoreReg: Save/restore registers to/from a specific save slot * area. */ void SaveReg(char *area, char *reg) { if (mmx) { if (!strcmp(area, "memhandler")) { /* memhandler always saves full reg */ if (!strcmp(reg, "eax")) { e("movd mm0, eax\n"); return; } if (!strcmp(reg, "ebx")) { e("movd mm1, ebx\n"); return; } if (!strcmp(reg, "ebp")) { e("movd mm2, ebp\n"); return; } } else if (!strcmp(area, "run")) { /* we have to check if its a full or byte reg */ if (!strcmp(reg, "edx") || !strcmp(reg, "dl")) { e("movd mm3, edx\n"); return; } if (!strcmp(reg, "esi")) { e("movd mm4, esi\n"); return; } if (!strcmp(reg, "edi")) { e("movd mm5, edi\n"); return; } } } e("mov [%s_", area); if (reg[1] == 'l') /* ?l byte reg */ { switch (reg[0]) { case 'a': e("eax"); break; case 'b': e("ebx"); break; case 'c': e("ecx"); break; case 'd': e("edx"); break; } } else e("%s", reg); e("], %s\n", reg); } void RestoreReg(char *area, char *reg) { if (mmx) { if (!strcmp(area, "memhandler")) { /* memhandler always restores full reg */ if (!strcmp(reg, "eax")) { e("movd eax, mm0\n"); return; } if (!strcmp(reg, "ebx")) { e("movd ebx, mm1\n"); return; } if (!strcmp(reg, "ebp")) { e("movd ebp, mm2\n"); return; } } else if (!strcmp(area, "run")) { /* we have to check if its a full or byte reg */ if (!strcmp(reg, "edx") || !strcmp(reg, "dl")) { e("movd edx, mm3\n"); return; } if (!strcmp(reg, "esi")) { e("movd esi, mm4\n"); return; } if (!strcmp(reg, "edi")) { e("movd edi, mm5\n"); return; } } } e("mov %s, [%s_", reg, area); if (reg[1] == 'l') /* ?l byte reg */ { switch (reg[0]) { case 'a': e("eax"); break; case 'b': e("ebx"); break; case 'c': e("ecx"); break; case 'd': e("edx"); break; } } else e("%s", reg); e("]\n"); } /* * SaveRegTo/RestoreRegTo: Saves/restore a register to a different register * than indicated by the name of the save slot. */ void SaveRegTo(char *area, char *d_reg, char *s_reg) { if (mmx) { /* currently, memhandlers don't need this, only run_ does. */ if (!strcmp(area, "run")) { /* test to make sure dest is MMX-able */ if (!strcmp(d_reg, "edx") || !strcmp(d_reg, "esi") || !strcmp(d_reg, "edi")) { e("movd "); if (!strcmp(d_reg, "edx")) e("mm3, "); else if (!strcmp(d_reg, "esi")) e("mm4, "); else if (!strcmp(d_reg, "edi")) e("mm5, "); if (s_reg[1] == 'l') /* ?l byte reg */ { switch (s_reg[0]) { case 'a': e("eax\n"); break; case 'b': e("ebx\n"); break; case 'c': e("ecx\n"); break; case 'd': e("edx\n"); break; } } else e("%s\n", s_reg); return; } } } e("mov [%s_%s], %s\n", area, d_reg, s_reg); } void RestoreRegTo(char *area, char *s_reg, char *d_reg) { if (mmx) { /* currently, memhandlers don't need this, only run_ does. */ if (!strcmp(area, "run")) { /* test to make sure src is MMX-able */ if (!strcmp(s_reg, "edx") || !strcmp(s_reg, "esi") || !strcmp(s_reg, "edi")) { e("movd "); if (d_reg[1] == 'l') /* ?l byte reg */ { switch (d_reg[0]) { case 'a': e("eax, "); break; case 'b': e("ebx, "); break; case 'c': e("ecx, "); break; case 'd': e("edx, "); break; } } else e("%s, ", d_reg); if (!strcmp(s_reg, "edx")) e("mm3\n"); else if (!strcmp(s_reg, "esi")) e("mm4\n"); else if (!strcmp(s_reg, "edi")) e("mm5\n"); return; } } } e("mov %s, [%s_%s]\n", d_reg, area, s_reg); } void AddrClip(char *reg) { /* if it's 32-bit we don't need this */ if (addr_mask != 0xffffffff) e("and %s, 0x%08X\n", reg, addr_mask); } void RideIntoTheDangerZone() /* where EBP is modified and must be saved */ { e("push ebp\n"); } void RideOutOfTheDangerZone() { e("pop ebp\n"); } void GetArg(char *dest_reg, int num_arg, int stack_offs) { char *reg[2] = { "eax", "edx" }; /* currently, only 2 args supported */ if (num_arg > 1) /* error! */ { fprintf(stderr, "Make68K: Internal Error: GetArg() called with num_arg = %d. Please contact Bart Trzynadlowski!\n", num_arg); if (call_convention) return; } if (!call_convention) e("mov %s, [esp+%d]\n", dest_reg, stack_offs); else { if ((!strcmp(dest_reg, "eax") && !num_arg) || (!strcmp(dest_reg, "edx") && num_arg == 1)) ; /* EAX,EAX or EDX,EDX */ else e("mov %s, %s\n", dest_reg, reg[num_arg]); } } void GetArgFromTheDangerZone(char *dest_reg, int num) { char *reg[2] = { "eax", "edx" }; /* no more than 2 args usually */ if (!call_convention) /* stack-based */ e("mov %s, [esp+8+(%d*4)]\n", dest_reg, num); else /* register-based */ { if ((!strcmp(dest_reg, "eax") && !num) || (!strcmp(dest_reg, "edx") && num == 1)) ; /* EAX,EAX or EDX,EDX */ else e("mov %s, %s\n", dest_reg, reg[num]); } } void GetArgNoStringsAttached(char *dest_reg, int num) { char *reg[2] = { "eax", "edx" }; /* no more than 2 args usually */ if (!call_convention) e("mov %s, [esp+4+(%d*4)]\n", dest_reg, num); else { if ((!strcmp(dest_reg, "eax") && !num) || (!strcmp(dest_reg, "edx") && num == 1)) ; /* EAX,EAX or EDX,EDX */ else e("mov %s, %s\n", dest_reg, reg[num]); } } void ReadLong() { if (memmap_type == 0 || memmap_type == 1) e("call ReadLong\n"); else if (memmap_type == 2) e("call dword [read_long]\n"); } void ReadByte() { if (memmap_type == 0 || memmap_type == 1) e("call ReadByte\n"); else if (memmap_type == 2) e("call dword [read_byte]\n"); } void ReadWord() { if (memmap_type == 0 || memmap_type == 1) e("call ReadWord\n"); else if (memmap_type == 2) e("call dword [read_word]\n"); } void WriteLong() { if (memmap_type == 0 || memmap_type == 1) e("call WriteLong\n"); else if (memmap_type == 2) e("call dword [write_long]\n"); } void WriteByte() { if (memmap_type == 0 || memmap_type == 1) e("call WriteByte\n"); else if (memmap_type == 2) e("call dword [write_byte]\n"); } void WriteWord() { if (memmap_type == 0 || memmap_type == 1) e("call WriteWord\n"); else if (memmap_type == 2) e("call dword [write_word]\n"); } void ReadByteSX() { fprintf(stderr, "Make68K: Internal Error: Sign-extended byte read handler required. Please contact Bart Trzynadlowski!\n"); exit(1); } void ReadWordSX() { if (memmap_type == 0 || memmap_type == 1) e("call ReadWordSX\n"); else if (memmap_type == 2) { e("call dword [read_word]\n"); e("movsx edx, dx\n"); } } /* * LoadFromEA() and MOVEM() use the ReadXXXPC() functions. */ void ReadLongPC() { e("call ReadLongPC\n"); } void ReadBytePC() { e("call ReadBytePC\n"); } void ReadWordPC() { e("call ReadWordPC\n"); } void ReadByteSXPC() { fprintf(stderr, "Make68K: Internal Error: Sign-extended byte read handler required. Please contact Bart Trzynadlowski!\n"); exit(1); e("call ReadBytePC\n"); e("movsx edx, dl\n"); } void ReadWordSXPC() { e("call ReadWordPC\n"); e("movsx edx, dx\n"); } void UpdateFetchPtr() /* scan the fetch area */ { int inst = 0; /* * In: ESI = address * Out: ESI = PC + ptr * EBP = ptr (base) * EDX = trashed */ /* * Note: If address bus is NOT 32-bits, the following applies. * * The unused bits of the PC MUST be preserved. Therefore, those unused * bits are subtracted from the base pointer so that when we add a PC * WITH unused bits, it won't go out of range, because it will be far * below range to begin with. * * The reasoning here is that if we subtract the unused bits from the base * pointer, the unused bits in the normalized PC pointer (ESI) will cancel * out. */ if (addr_mask != 0xffffffff) SaveReg("fetch", "esi"); AddrClip("esi"); e("mov edx, [fetch]\n"); e(".find_fetch_loop%d:\n", inst); e("add edx, byte "SIZEOF_FETCHREGION"\n"); e("cmp dword [edx], byte -1\n"); /* limit=-1? end. */ e("je near fetch_error\n"); e("cmp esi, [edx]\n"); /* offset 0: base. above it? */ e("jb .find_fetch_loop%d\n", inst); /* nope, not this region */ e("cmp esi, [edx+"OFFSET_FETCH_LIMIT"]\n"); /* limit. below it? */ e("ja .find_fetch_loop%d\n", inst); /* nope, not this region */ e("mov ebp, [edx+"OFFSET_FETCH_PTR"]\n"); /* ebp=base ptr */ if (addr_mask != 0xffffffff) /* to handle those nasty unused bits! */ { e("mov edx, [fetch_esi]\n"); e("mov esi, edx\n"); /* this is the ACTUAL PC we saved earlier */ e("and edx, 0x%08X\n", ~addr_mask);/* we only want the unused bits */ e("sub ebp, edx\n"); } e("add esi, ebp\n"); /* +pc=pc ptr.. */ } void LoadCCR() /* CCR->AX ... trashes AX and DX */ { e("mov dh, [sr]\n"); /* DH:***XNZVC */ e("mov ah, dh\n"); /* AH:***XNZVC */ e("shl ah, byte 4\n"); /* AH:NZVC0000 */ e("test dh, byte 2\n"); /* V flag? */ e("setnz al\n"); /* AL:0000000V */ e("test dh, 0x10\n"); /* X flag? */ e("setnz byte [x]\n"); /* X:0000000000000000000000000000000X */ e("and dh, byte 1\n"); /* DH:0000000C */ e("or ah, dh\n"); /* AH:NZ**000C */ } void SaveCCR() /* AX->CCR... trashes AX and DX */ { e("and eax, 0xc101\n"); /* EAX:0000000000000000NZ00000C0000000V */ e("mov dh, ah\n"); /* DH:NZ00000C */ e("shr dh, byte 4\n"); /* DH:0000NZ00 */ e("shl al, byte 1\n"); /* AL:000000V0 */ e("or dh, al\n"); /* DH:0000NZV0 */ e("shr ah, byte 1\n"); /* CF:C */ e("adc dh, 0\n"); /* DH:0000NZVC */ e("mov ah, [x]\n"); /* AH:0000000X */ e("shl ah, byte 4\n"); /* AH:000X0000 */ e("or dh, ah\n"); /* DH:000XNZVC */ e("mov [sr], dh\n"); } void InstBegin(unsigned short op, char *mnem, unsigned num) { #ifdef PROFILE int i; #endif Align(4); if (num == 1) e("I%04X: ; %s\t\t0x%04X\n", op, mnem, op); else e("I%04X: ; %s\t\t0x%04X-0x%04X\n", op, mnem, op, op+(num-1)); if (debug) { // Debugging code char noDebugLabel[16]; char noPCChgLabel[16]; sprintf(noDebugLabel, ".no_debug%04X", op); sprintf(noPCChgLabel, ".no_pcchg%04X", op); e("mov ebx, [Debug]\n"); /* Get debug handler */ e("test ebx, ebx\n"); /* If no handler set, skip debugging */ e("jz short %s\n", noDebugLabel); e("push eax\n"); /* Note: __cdecl calling convention used to call handler */ e("push edi\n"); e("push ebp\n"); WRITEPCTOMEM("pc"); /* Save esi */ e("mov [remaining], ecx\n"); e("push edi\n"); /* Pass instruction opcode to handler */ e("sub esi, ebp\n"); /* Convert PC to base offset */ e("sub esi, byte 2\n"); /* Adjust back to beginning of instruction */ e("push esi\n"); /* Pass result to handler */ e("call ebx\n"); /* Call handler */ e("add esp, byte 8\n"); e("mov esi, [pc]\n"); /* Restore esi (this could have been changed) */ e("mov ecx, [remaining]\n"); /* This could have been changed */ e("pop ebp\n"); e("pop edi\n"); e("add esi, ebp\n"); /* Convert esi to pointer (base+pc) */ e("test eax, eax\n"); /* Check if debug handler changed PC */ e("jz short %s\n", noPCChgLabel); e("pop eax\n"); /* If so, move onto instruction at new PC */ e("mov di, [esi]\n"); e("add esi, byte 2\n"); /* Point to word after next opcode */ e("jmp dword [jmptab+edi*4]\n"); EmitLabel(noPCChgLabel); e("pop eax\n"); EmitLabel(noDebugLabel); } decoded[op] = num; num_handlers++; #ifdef PROFILE for (i = 0; i < strlen(mnem); i++) /* remove spaces */ { if (mnem[i] == ' ') mnem[i] = '_'; } e("inc dword [prof%s]\n", mnem); for (i = 0; prof[i] != NULL && i < 512; i++) { if (!strcmp(prof[i], mnem)) /* already have one of these... */ return; } prof[prof_i] = calloc(strlen(mnem) + 1, 1); strcpy(prof[prof_i], mnem); prof_i++; #endif } void InstEnd() { e("mov di, [esi]\n"); e("add esi, byte 2\n"); /* point to word after next opcode */ e("jmp dword [jmptab+edi*4]\n"); } /***************************************************************************** * Address Space Management */ /* * Input: Nothing * Notes: Assumes that run_ecx, run_edi, and run_esi are available for use. */ void SetSupervisorAddressSpace() { if (!multiaddr) return; e("push ecx\n"); e("cld\n"); /* direction: forward */ e("push esi\n"); e("push edi\n"); e("mov ecx, 8\n"); /* 8 dword-size pointers to copy */ e("mov edi, fetch\n"); e("mov esi, super_fetch\n"); e("rep movsd\n"); e("pop edi\n"); e("pop esi\n"); e("pop ecx\n"); } void SetUserAddressSpace() { if (!multiaddr) return; e("push ecx\n"); e("cld\n"); /* direction: forward */ e("push esi\n"); e("push edi\n"); e("mov ecx, 8\n"); /* 8 dword-size pointers to copy */ e("mov edi, fetch\n"); e("mov esi, user_fetch\n"); e("rep movsd\n"); e("pop edi\n"); e("pop esi\n"); e("pop ecx\n"); } /***************************************************************************** * Timing */ /* TimingEA: calculates amount of clocks taken by a given EA mode */ unsigned TimingEA(unsigned ea, int size) { /* EA format = MMMRRR (mode/register) */ switch ((ea >> 3) & 7) /* mode */ { case 0: case 1: return 0; break; case 2: case 3: switch (size) { case BYTE_SIZE: case WORD_SIZE: return 4; break; case LONG_SIZE: return 8; break; } break; case 4: switch (size) { case BYTE_SIZE: case WORD_SIZE: return 6; break; case LONG_SIZE: return 10; break; } break; case 5: switch (size) { case BYTE_SIZE: case WORD_SIZE: return 8; break; case LONG_SIZE: return 12; break; } break; case 6: switch (size) { case BYTE_SIZE: case WORD_SIZE: return 10; break; case LONG_SIZE: return 14; break; } break; case 7: /* misc... */ switch (ea & 0x7) { case 0: /* (xxx).W */ switch (size) { case BYTE_SIZE: case WORD_SIZE: return 8; break; case LONG_SIZE: return 12; break; } break; case 1: /* (xxx).L */ switch (size) { case BYTE_SIZE: case WORD_SIZE: return 12; break; case LONG_SIZE: return 16; break; } break; case 4: /* # */ switch (size) { case BYTE_SIZE: case WORD_SIZE: return 4; break; case LONG_SIZE: return 8; break; } break; case 2: /* (d16,pc) */ switch (size) { case BYTE_SIZE: case WORD_SIZE: return 8; break; case LONG_SIZE: return 12; break; } break; case 3: /* (d8,pc,Xn) */ switch (size) { case BYTE_SIZE: case WORD_SIZE: return 10; break; case LONG_SIZE: return 14; break; } break; } } return 0; } void EmitTiming(unsigned cycles) { if (cycles) { if (cycles == 1) e("dec ecx\n"); else if (!(cycles & 0x80)) e("sub ecx, byte %d\n", cycles); else e("sub ecx, %d\n", cycles); e("js near Turbo68KRun_done\n"); /* below 0, finished */ } } /***************************************************************************** * Effective Address Code */ int CheckEA(unsigned ea, char *valid) /* EA = MMMRRR (mode/reg) */ { /* * valid[] string is a mask for valid addressing modes as they appear * in the 68K manual, in order, 1 = okay, 0 = not allowed */ if (((ea >> 3) & 7) != 7) /* mode 7 is handled separately */ { if (valid[(ea >> 3) & 7] != '1') return 0; /* not allowed! */ else return 1; } else /* handle mode 7 here */ { /* first check if the register is invalid */ if ((ea & 7) >= 5) return 0; /* no such thing! */ else { switch (ea & 7) { case 0: case 1: if (valid[7+(ea & 7)] != '1') return 0; else return 1; break; case 4: if (valid[7+2] != '1') return 0; else return 1; break; case 2: if (valid[7+3] != '1') return 0; else return 1; break; case 3: if (valid[7+4] != '1') return 0; else return 1; break; } } } return 0; } int NumDecodedEA(unsigned ea) /* how many handlers can this feed? */ { /* * Turbo68K does not generate an instruction handler for every possible * opcode, instead, it generates one for every possible EA mode of a given * instruction. This means if reg==0, it returns the # to pass to InstEnd * (which indicates how many opcodes this will cover for) Otherwise, this * function returns 0 and the instruction handler should not be emitted. */ if (((ea >> 3) & 7) <= 6 && (ea & 7) != 0) return 0; else if (((ea >> 3) & 7) <= 6 && (ea & 7) == 0) return 8; /* 8 regs */ else if (((ea >> 3) & 7) == 7 && (ea & 7) <= 4) return 1; /* 1 reg */ return 0; } /* * Effective Address calculations * * Note: When loading words, usually the whole dword is loaded. When storing, * only the word part is stored */ /* * Returns: Data in EDX, address preserved in EBX * Used: EBX, EDX, EDI (unless reading from register) * Notes: ESI is assumed to point at word after opcode * Assumes EDI contains opcode and that it has EA at the very end * This function is only to be used when EA mode fields are located * at bits 0-5. */ void LoadFromEA(unsigned ea, int size, int sign_extend) { switch ((ea >> 3) & 7) { case 0: /* Dn */ case 1: /* An */ { char r = ((ea >> 3) & 7) ? 'a' : 'd'; e("and edi, byte 7\n"); /* get reg number, EDI contains opcode */ if (size == LONG_SIZE) e("mov edx, [%c+edi*4]\n", r); else { if (size == BYTE_SIZE) { if (!sign_extend) e("mov dl, [%c+edi*4]\n", r); else e("movsx edx, byte [%c+edi*4]\n", r); } else /* word size */ { if (!sign_extend) e("mov edx, [%c+edi*4]\n", r); else e("movsx edx, word [%c+edi*4]\n", r); } } } break; case 2: /* (An) */ case 3: /* (An)+ */ case 4: /* -(An) */ case 5: /* (d16,An) */ e("and edi, byte 7\n"); /* reg */ if (((ea >> 3) & 7) == 4) /* pre-dec */ { if (size == BYTE_SIZE) { e("cmp edi, byte 7\n"); /* A7 bytes */ e("cmc\n"); /* switch carry */ e("sbb dword [a+edi*4], byte 1\n"); } else if (size == WORD_SIZE) e("sub dword [a+edi*4], byte 2\n"); else if (size == LONG_SIZE) e("sub dword [a+edi*4], byte 4\n"); } if (((ea >> 3) & 7) == 5) /* (d16,An) */ { e("movsx ebx, word [esi]\n"); e("add ebx, [a+edi*4]\n"); e("add esi, byte 2\n"); /* past d16, next instruction */ } else e("mov ebx, [a+edi*4]\n"); if (((ea >> 3) & 7) == 3) /* post-inc */ { if (size == BYTE_SIZE) { e("cmp edi, byte 7\n"); /* A7 bytes */ e("cmc\n"); /* switch carry */ e("adc dword [a+edi*4], byte 1\n"); } else if (size == WORD_SIZE) e("add dword [a+edi*4], byte 2\n"); else if (size == LONG_SIZE) e("add dword [a+edi*4], byte 4\n"); } switch (size) { case BYTE_SIZE: if (sign_extend) ReadByteSX(); else ReadByte(); break; case WORD_SIZE: if (sign_extend) ReadWordSX(); else ReadWord(); break; case LONG_SIZE: ReadLong(); break; } break; case 6: /* (d8,An,Xn) */ e("and edi, byte 7\n"); /* An reg # */ e("xor ebx, ebx\n"); /* so the high part is clear */ e("mov bx, [esi]\n"); /* fetch extension word */ e("movsx edx, bl\n"); /* sign-extended index */ e("add esi, byte 2\n"); /* point at next word */ e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ /* the shr sets CF if Long reg */ e("mov ebx, [d+ebx*4]\n"); e("jc short .ll\n"); /* CF=1? index reg is LONG */ e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ EmitLabel(".ll"); e("add ebx, edx\n"); /* disp+index_reg */ e("add ebx, [a+edi*4]\n"); switch (size) { case BYTE_SIZE: if (sign_extend) ReadByteSX(); else ReadByte(); break; case WORD_SIZE: if (sign_extend) ReadWordSX(); else ReadWord(); break; case LONG_SIZE: ReadLong(); break; } break; case 0x7: switch (ea & 7) { case 0: /* (xxx).W */ case 1: /* (xxx).L */ if ((ea & 7) == 0) /* word */ { e("movsx ebx, word [esi]\n"); e("add esi, byte 2\n"); } else /* long */ { e("mov ebx, [esi]\n"); e("ror ebx, byte 16\n"); /* word-swap */ e("add esi, byte 4\n"); } switch (size) { case BYTE_SIZE: if (sign_extend) ReadByteSX(); else ReadByte(); break; case WORD_SIZE: if (sign_extend) ReadWordSX(); else ReadWord(); break; case LONG_SIZE: ReadLong(); break; } break; case 4: /* # */ switch (size) { case BYTE_SIZE: if (!sign_extend) e("mov dl, [esi]\n"); /* get low byte of ext. word */ else e("movsx edx, byte [esi]\n"); e("add esi, byte 2\n"); break; case WORD_SIZE: if (!sign_extend) e("mov edx, [esi]\n"); /* bx = word, ebx=????:word */ else e("movsx edx, word [esi]\n"); e("add esi, byte 2\n"); break; case LONG_SIZE: e("mov edx, [esi]\n"); /* get long-word */ e("add esi, byte 4\n"); e("ror edx, byte 16\n"); /* word swap to work... */ break; } break; case 2: /* (d16,PC) */ /* * The PC is assumed to be pointing at the extension word, which * is exactly how the emulator works */ e("movsx ebx, word [esi]\n"); e("add ebx, esi\n"); e("sub ebx, ebp\n"); e("add esi, byte 2\n"); switch (size) { case BYTE_SIZE: if (sign_extend) pcfetch ? ReadByteSXPC() : ReadByteSX(); else pcfetch ? ReadBytePC() : ReadByte(); break; case WORD_SIZE: if (sign_extend) pcfetch ? ReadWordSXPC() : ReadWordSX(); else pcfetch ? ReadWordPC() : ReadWord(); break; case LONG_SIZE: pcfetch ? ReadLongPC() : ReadLong(); break; } break; case 3: /* (d8,PC,Xn) */ e("xor ebx, ebx\n"); /* so the high part is clear */ e("mov bx, [esi]\n"); /* fetch extension word */ e("movsx edx, bl\n"); /* sign-extended index */ e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ /* the shr sets CF if Long reg */ e("mov ebx, [d+ebx*4]\n"); e("jc short .ll\n"); /* CF=1? index reg is LONG */ e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ EmitLabel(".ll"); e("add ebx, edx\n"); /* disp+index_reg */ e("add ebx, esi\n"); /* add the PC */ e("sub ebx, ebp\n"); e("add esi, byte 2\n"); /* point at next word */ switch (size) { case BYTE_SIZE: if (sign_extend) pcfetch ? ReadByteSXPC() : ReadByteSX(); else pcfetch ? ReadBytePC() : ReadByte(); break; case WORD_SIZE: if (sign_extend) pcfetch ? ReadWordSXPC() : ReadWordSX(); else pcfetch ? ReadWordPC() : ReadWord(); break; case LONG_SIZE: pcfetch ? ReadLongPC() : ReadLong(); break; } break; } break; default: printf("Error!\a\n"); break; } } /* * Expects: Data in EDX, address in EBX * Notes: Destroys EBX, make sure to preserve the address if you need it. */ void StoreToEA(unsigned ea, int size, int same) { /* the "same" parameter is ignored. it no longer is needed */ switch ((ea >> 3) & 7) { case 0: /* Dn */ case 1: /* An */ { char r = ((ea >> 3) & 7) ? 'a' : 'd'; if (size == LONG_SIZE) e("mov [%c+%d*4], edx\n", r, ea & 7); else if (size == BYTE_SIZE) e("mov [%c+%d*4], dl\n", r, ea & 7); else if (size == WORD_SIZE) e("mov [%c+%d*4], dx\n", r, ea & 7); } break; case 2: /* (An) */ case 3: /* (An)+ */ case 4: /* -(An) */ case 5: /* (d16,An) */ if (((ea >> 3) & 7) == 4) /* pre-dec */ { if (size == BYTE_SIZE) { if ((ea & 7) == 7) /* A7 kept word aligned */ e("sub dword [a+%d*4], byte 2\n", ea & 7); else e("dec dword [a+%d*4]\n", ea & 7); } else if (size == WORD_SIZE) e("sub dword [a+%d*4], byte 2\n", ea & 7); else if (size == LONG_SIZE) e("sub dword [a+%d*4], byte 4\n", ea & 7); } if (((ea >> 3) & 7) == 5) /* (d16,An) */ { e("movsx ebx, word [esi]\n"); e("add ebx, [a+%d*4]\n", ea & 7); e("add esi, byte 2\n"); /* past d16, next instruction */ } else e("mov ebx, [a+%d*4]\n", ea & 7); if (((ea >> 3) & 7) == 3) /* post-inc */ { if (size == BYTE_SIZE) { if ((ea & 7) == 7) /* A7 kept word aligned */ e("add dword [a+%d*4], byte 2\n", ea & 7); else e("inc dword [a+%d*4]\n", ea & 7); } else if (size == WORD_SIZE) e("add dword [a+%d*4], byte 2\n", ea & 7); else if (size == LONG_SIZE) e("add dword [a+%d*4], byte 4\n", ea & 7); } switch (size) { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } break; case 6: /* (d8,An,Xn) */ e("xor ebx, ebx\n"); /* so the high part is clear */ e("mov bx, [esi]\n"); /* fetch extension word */ e("movsx edi, bl\n"); /* sign-extended index */ e("add esi, byte 2\n"); /* point at next word */ e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ /* the shr sets CF if Long reg */ e("mov ebx, [d+ebx*4]\n"); e("jc short .ls\n"); /* CF=1? index reg is LONG */ e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ EmitLabel(".ls"); e("add ebx, edi\n"); /* disp+index_reg */ e("add ebx, [a+%d*4]\n", ea & 7); switch (size) /* WriteXXX() clears EDI */ { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } break; case 0x7: switch (ea & 7) { case 0: /* (xxx).W */ case 1: /* (xxx).L */ if ((ea & 7) == 0) /* word */ { e("movsx ebx, word [esi]\n"); e("add esi, byte 2\n"); } else /* long */ { e("mov ebx, [esi]\n"); e("ror ebx, byte 16\n"); /* word-swap */ e("add esi, byte 4\n"); } switch (size) { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } break; case 4: /* # */ /* can't store to #data! */ printf("Error!\a\n"); break; case 2: /* (d16,PC) */ /* * The PC is assumed to be pointing at the extension word, which * is exactly how the emulator works */ e("movsx ebx, word [esi]\n"); e("add ebx, esi\n"); e("sub ebx, ebp\n"); e("add esi, byte 2\n"); switch (size) { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } break; case 3: /* (d8,PC,Xn) */ e("xor ebx, ebx\n"); /* so the high part is clear */ e("mov bx, [esi]\n"); /* fetch extension word */ e("movsx edi, bl\n"); /* sign-extended index */ e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ /* the shr sets CF if Long reg */ e("mov ebx, [d+ebx*4]\n"); e("jc short .ls\n"); /* CF=1? index reg is LONG */ e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ EmitLabel(".ls"); e("add ebx, edi\n"); /* disp+index_reg */ e("add ebx, esi\n"); /* add the PC */ e("sub ebx, ebp\n"); e("add esi, byte 2\n"); /* point at next word */ switch (size) /* WriteXXX() clears EDI */ { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } break; } break; default: break; } } /* * This is the same as StoreToEA() except that it uses EDI&7 to find the * register. It is assumed that EA is bits 0-5 of the opcode. This only works * in special situations like CLR and Scc. */ void StoreToEAUsingEDI(unsigned ea, int size, int same) { /* the "same" parameter is ignored. it no longer is needed */ switch ((ea >> 3) & 7) { case 0: /* Dn */ case 1: /* An */ { char r = ((ea >> 3) & 7) ? 'a' : 'd'; e("and edi, byte 7\n"); if (size == LONG_SIZE) e("mov [%c+edi*4], edx\n", r); else if (size == BYTE_SIZE) e("mov [%c+edi*4], dl\n", r); else if (size == WORD_SIZE) e("mov [%c+edi*4], dx\n", r); } break; case 2: /* (An) */ case 3: /* (An)+ */ case 4: /* -(An) */ case 5: /* (d16,An) */ e("and edi, byte 7\n"); if (((ea >> 3) & 7) == 4) /* pre-dec */ { if (size == BYTE_SIZE) { e("cmp edi, byte 7\n"); /* A7 bytes */ e("cmc\n"); /* switch carry */ e("sbb dword [a+edi*4], byte 1\n"); } else if (size == WORD_SIZE) e("sub dword [a+edi*4], byte 2\n"); else if (size == LONG_SIZE) e("sub dword [a+edi*4], byte 4\n"); } if (((ea >> 3) & 7) == 5) /* (d16,An) */ { e("movsx ebx, word [esi]\n"); e("add ebx, [a+edi*4]\n"); e("add esi, byte 2\n"); /* past d16, next instruction */ } else e("mov ebx, [a+edi*4]\n"); if (((ea >> 3) & 7) == 3) /* post-inc */ { if (size == BYTE_SIZE) { e("cmp edi, byte 7\n"); /* A7 bytes */ e("cmc\n"); /* switch carry */ e("adc dword [a+edi*4], byte 1\n"); } else if (size == WORD_SIZE) e("add dword [a+edi*4], byte 2\n"); else if (size == LONG_SIZE) e("add dword [a+edi*4], byte 4\n"); } switch (size) { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } break; case 6: /* (d8,An,Xn) */ e("and edi, byte 7\n"); /* An reg # */ SaveReg("memhandler", "edi"); /* no memhandlers using this now... */ e("xor ebx, ebx\n"); /* so the high part is clear */ e("mov bx, [esi]\n"); /* fetch extension word */ e("movsx edi, bl\n"); /* sign-extended index */ e("add esi, byte 2\n"); /* point at next word */ e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ /* the shr sets CF if Long reg */ e("mov ebx, [d+ebx*4]\n"); e("jc short .ll\n"); /* CF=1? index reg is LONG */ e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ EmitLabel(".ll"); e("add ebx, edi\n"); /* disp+index_reg */ RestoreReg("memhandler", "edi"); e("add ebx, [a+edi*4]\n"); switch (size) /* WriteXXX() clears EDI */ { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } break; case 0x7: switch (ea & 7) { case 0: /* (xxx).W */ case 1: /* (xxx).L */ if ((ea & 7) == 0) /* word */ { e("movsx ebx, word [esi]\n"); e("add esi, byte 2\n"); } else /* long */ { e("mov ebx, [esi]\n"); e("ror ebx, byte 16\n"); /* word-swap */ e("add esi, byte 4\n"); } switch (size) { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } break; case 4: /* # */ /* can't store to #data! */ break; case 2: /* (d16,PC) */ /* * The PC is assumed to be pointing at the extension word, which * is exactly how the emulator works */ e("movsx ebx, word [esi]\n"); e("add ebx, esi\n"); e("sub ebx, ebp\n"); e("add esi, byte 2\n"); switch (size) { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } break; case 3: /* (d8,PC,Xn) */ e("xor ebx, ebx\n"); /* so the high part is clear */ e("mov bx, [esi]\n"); /* fetch extension word */ e("movsx edi, bl\n"); /* sign-extended index */ e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ /* the shr sets CF if Long reg */ e("mov ebx, [d+ebx*4]\n"); e("jc short .ls\n"); /* CF=1? index reg is LONG */ e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ EmitLabel(".ls"); e("add ebx, edi\n"); /* disp+index_reg */ e("add ebx, esi\n"); /* add the PC */ e("sub ebx, ebp\n"); e("add esi, byte 2\n"); /* point at next word */ switch (size) /* WriteXXX() clears EDI */ { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } break; } break; default: printf("Error!\a\n"); break; } } /* * Returns: Address in EBX * Used: EBX, EDX * Notes: ESI is assumed to point at word after opcode * Assumes EDI contains opcode and that it has EA at the very end * This function is only to be used when EA mode fields are located * at bits 0-5. */ void LoadControlEA(unsigned ea) { switch ((ea >> 3) & 7) { case 2: /* (An) */ case 3: /* (An)+ (just load what's in (An) -- MOVEM needs this) */ case 4: /* -(An) (just load what's in (An) -- MOVEM needs this) */ case 5: /* (d16,An) */ e("and edi, byte 7\n"); /* reg */ if (((ea >> 3) & 7) == 5) /* (d16,An) */ { e("movsx ebx, word [esi]\n"); e("add ebx, [a+edi*4]\n"); e("add esi, byte 2\n"); /* past d16, next instruction */ } else e("mov ebx, [a+edi*4]\n"); break; case 6: /* (d8,An,Xn) */ e("and edi, byte 7\n"); /* edi=An # */ e("xor ebx, ebx\n"); /* so the high part is clear */ e("mov bx, [esi]\n"); /* fetch extension word */ e("movsx edx, bl\n"); /* sign-extended index */ e("add esi, byte 2\n"); /* point at next word */ e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ /* the shr sets CF if Long reg */ e("mov ebx, [d+ebx*4]\n"); e("jc short .ll\n"); /* CF=1? index reg is LONG */ e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ EmitLabel(".ll"); e("add ebx, edx\n"); /* disp+index_reg */ e("add ebx, [a+edi*4]\n"); break; case 0x7: switch (ea & 7) { case 0: /* (xxx).W */ case 1: /* (xxx).L */ if ((ea & 7) == 0) /* word */ { e("movsx ebx, word [esi]\n"); e("add esi, byte 2\n"); } else /* long */ { e("mov ebx, [esi]\n"); e("ror ebx, byte 16\n"); /* word-swap */ e("add esi, byte 4\n"); } break; case 2: /* (d16,PC) */ /* * The PC is assumed to be pointing at the extension word, which * is exactly how the emulator works */ e("movsx ebx, word [esi]\n"); e("add ebx, esi\n"); e("sub ebx, ebp\n"); e("add esi, byte 2\n"); break; case 3: /* (d8,PC,Xn) */ e("xor ebx, ebx\n"); /* so the high part is clear */ e("mov bx, [esi]\n"); /* fetch extension word */ e("movsx edx, bl\n"); /* sign-extended index */ e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */ /* the shr sets CF if Long reg */ e("mov ebx, [d+ebx*4]\n"); e("jc short .ll\n"); /* CF=1? index reg is LONG */ e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */ EmitLabel(".ll"); e("add ebx, edx\n"); /* disp+index_reg */ e("add ebx, esi\n"); /* add the PC */ e("sub ebx, ebp\n"); e("add esi, byte 2\n"); /* point at next word */ break; } break; default: printf("Warning: LoadControlEA() defaulted\n"); break; } } /***************************************************************************** * Instruction Handlers */ int RTD(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); e("mov ebx, [a+7*4]\n"); ReadLong(); e("xor ebx, ebx\n"); e("add dword [a+7*4], byte 4\n"); e("mov bx, [esi]\n"); e("add dword [a+7*4], ebx\n"); /* SP+4+disp */ e("mov esi, edx\n"); UpdateFetchPtr(); EmitTiming(base_timing); InstEnd(); return 1; } int MOVEC(unsigned short op, char *mnem, unsigned base_timing) { /* * Timing for an illegal control register is not handled, control is * simply passed to the illegal instruction exception handler, which uses * different timing. */ InstBegin(op, mnem, 2); e("test byte [sr+1], 0x20\n"); /* in supervisor? */ e("jz near exception_privilege_violation\n"); e("xor ebx, ebx\n"); e("xor edx, edx\n"); e("mov dx, [esi]\n"); /* fetch data word */ e("mov ebx, edx\n"); e("shr ebx, byte 12\n"); /* EBX=general register */ e("shr edi, byte 1\n"); /* dr field */ e("jc short .to_control\n"); /* * Test for control register. The control register value is placed in * EDX. At the end, EDX is moved to [d+ebx*4]. */ e("and edx, 0xfff\n"); e("jnz short .from_dfc\n"); e("mov dl, [fc]\n"); /* SFC */ e("jmp short .end_from\n"); EmitLabel(".from_dfc"); e("cmp edx, byte 1\n"); e("jne short .from_usp\n"); e("mov dl, [fc+1]\n"); /* DFC */ e("jmp short .end_from\n"); EmitLabel(".from_usp"); e("cmp edx, 0x800\n"); e("jne short .from_vbr\n"); e("mov edx, [__sp]\n"); /* we're in supervisor, USP is in __sp */ e("jmp short .end_from\n"); EmitLabel(".from_vbr"); e("cmp edx, 0x801\n"); e("jne near exception_illegal_instruction\n"); /* invalid control reg */ e("mov edx, [vbr]\n"); /* VBR */ EmitLabel(".end_from"); e("mov [d+ebx*4], edx\n"); /* move to general reg */ e("add esi, byte 2\n"); /* remember, we fetched a word */ EmitTiming(12); e("jmp short .end\n"); /* * Test for control register. General reg is written to control reg. */ EmitLabel(".to_control"); e("mov ebx, [d+ebx*4]\n"); /* general reg */ e("and edx, 0xfff\n"); e("jnz short .to_dfc\n"); e("and bl, 3\n"); e("mov [fc], bl\n"); /* SFC */ e("jmp short .end_to\n"); EmitLabel(".to_dfc"); e("cmp edx, byte 1\n"); e("jne short .to_usp\n"); e("and bl, 3\n"); e("mov [fc+1], bl\n"); /* DFC */ e("jmp short .end_to\n"); EmitLabel(".to_usp"); e("cmp edx, 0x800\n"); e("jne short .to_vbr\n"); e("mov [__sp], ebx\n"); /* we're in supervisor, USP is in __sp */ e("jmp short .end_to\n"); EmitLabel(".to_vbr"); e("cmp edx, 0x801\n"); e("jne near exception_illegal_instruction\n"); /* invalid control reg */ e("mov [vbr], ebx\n"); /* VBR */ EmitLabel(".end_to"); e("add esi, byte 2\n"); /* remember, we fetched a word */ EmitTiming(10); /* * End */ EmitLabel(".end"); InstEnd(); return 2; } int BKPT(unsigned short op, char *mnem, unsigned base_timing) { /* * NOTE: Timing isn't done here, the exception processing code will do it. * The exception code doesn't use the proper timing for a BKPT, but it's * good enough ;) */ InstBegin(op, mnem, 8); e("and edi, byte 7\n"); /* vector # */ e("mov ebx, [Bkpt]\n"); /* BKPT handler */ e("test ebx, ebx\n"); /* if no handler, don't do anything */ e("jz short .no_handler\n"); e("push eax\n"); e("push edi\n"); e("push ebp\n"); WRITEPCTOMEM("pc"); /* saves esi */ e("mov [remaining], ecx\n"); e("push edi\n"); /* vector # */ e("call ebx\n"); e("add esp, byte 4\n"); e("mov esi, [pc]\n"); e("mov ecx, [remaining]\n"); /* this could have been changed */ e("pop ebp\n"); e("pop edi\n"); e("pop eax\n"); e("add esi, ebp\n"); /* base+pc=pc pointer */ EmitLabel(".no_handler"); e("jmp near exception_illegal_instruction\n"); return 8; } int MOVEfromCCR(unsigned short op, char *mnem, unsigned base_timing) { unsigned ea = op & 0x3f, t = 4; if (!CheckEA(ea, "101111111000")) return 0; if (!NumDecodedEA(ea)) return 0; InstBegin(op, mnem, NumDecodedEA(ea)); e("xor edx, edx\n"); /* CCR->(sign extend)->word */ e("mov dl, [sr]\n"); StoreToEA(ea, WORD_SIZE, 0); if ((ea >> 3) != 0) t += 4; /* mem */ EmitTiming(t + TimingEA(ea, WORD_SIZE)); /* technically, word sized */ InstEnd(); return 1; } int TRAPV(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); e("test al, al\n"); /* do only if V */ e("jz near .no_trap\n"); e("mov edx, 0x2000\n"); /* what the SR will be */ e("xor edx, [sr]\n"); /* see if S bit is different */ e("test edx, 0x2000\n"); e("jz .no_sp_magic\n"); /* nope, don't swap SPs */ e("mov edx, [__sp]\n"); e("xchg [a+7*4], edx\n"); e("mov [__sp], edx\n"); SetSupervisorAddressSpace(); EmitLabel(".no_sp_magic"); e("mov ebx, [a+7*4]\n"); /* SP */ /* * If 68010, push format info */ if (mpu == 68010) { e("sub ebx, byte 2\n");/* SP-2 */ e("mov edx, 7*4\n"); /* TRAPV vector | 0x00000000 */ e("push ebx\n"); WriteWord(); e("pop ebx\n"); } e("mov edx, esi\n"); e("sub edx, ebp\n"); /* PC->EDX */ e("sub ebx, byte 4\n"); /* SP-4 */ e("push ebx\n"); WriteLong(); e("pop ebx\n"); e("mov edx, [sr]\n"); /* get SR */ e("sub ebx, byte 2\n"); /* SP-2 */ e("push ebx\n"); WriteWord(); /* save SR to stack */ e("pop ebx\n"); e("or dh, 0x20\n"); /* set supervisor for exception */ e("and edx, 0xa71f\n"); /* clear unwanted bits */ e("xchg [sr], edx\n"); /* set new SR, get old SR->EDX */ e("mov [a+7*4], ebx\n"); /* write back SP */ e("mov ebx, 7*4\n"); /* TRAPV vector */ if (mpu == 68010) e("add ebx, [vbr]\n"); ReadLong(); /* get PC */ e("mov esi, edx\n"); /* set new PC */ UpdateFetchPtr(); EmitTiming(34+4); /* TRAPV=4, exception=34 */ InstEnd(); EmitLabel(".no_trap"); e("xor edi, edi\n"); EmitTiming(4); InstEnd(); return 1; } int STOP(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); e("test byte [sr+1], 0x20\n"); /* in supervisor? */ e("jz near exception_privilege_violation\n"); e("mov edx, [esi]\n"); e("add esi, byte 2\n"); e("and edx, 0xa71f\n"); /* mask out unimplemented bits */ e("mov [sr], dx\n"); LoadCCR(); STOP_CPU(); EmitTiming(base_timing); e("mov dword [remaining], 0\n"); /* stop executing */ e("jmp near Turbo68KRun_done\n"); return 1; } int RESET(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); e("test byte [sr+1], 0x20\n"); /* in supervisor? */ e("jz near exception_privilege_violation\n"); e("mov ebx, [Reset]\n"); /* Reset handler */ e("test ebx, ebx\n"); /* if no handler, don't do anything */ e("jz short .no_reset\n"); e("push eax\n"); e("push ebp\n"); WRITEPCTOMEM("pc"); /* saves esi */ e("mov [remaining], ecx\n"); e("call ebx\n"); e("mov esi, [pc]\n"); e("mov ecx, [remaining]\n"); /* this could have been changed */ e("pop ebp\n"); e("pop eax\n"); e("add esi, ebp\n"); /* base+pc=pc pointer */ e("xor edi, edi\n"); /* keep clear for fetch */ EmitLabel(".no_reset"); EmitTiming(base_timing); InstEnd(); return 1; } int TAS(unsigned short op, char *mnem, unsigned base_timing) { unsigned ea = op & 0x3f; if (!CheckEA(ea, "101111111000")) return 0; if (!NumDecodedEA(ea)) return 0; InstBegin(op, mnem, NumDecodedEA(ea)); LoadFromEA(ea, BYTE_SIZE, 0); e("test dl, dl\n"); /* test... */ e("lahf\n"); e("xor al, al\n"); e("or dl, 0x80\n"); /* ...and set bit 7 */ if ((ea >> 3) == 0) { e("mov [d+edi*4], dl\n"); EmitTiming(4); } else { /* * NOTE: * ----- * * You may wish to uncomment the WriteByte() below when emulating the Sega * Genesis if you wish to get Gargoyles running. The TAS instruction is used * to synchronize multiple 68Ks by grabbing the bus, but on the Genesis, the * VDP will not release the bus resulting in data not being written. */ WriteByte(); EmitTiming(14 + TimingEA(ea, BYTE_SIZE)); } InstEnd(); return 1; } int NBCD(unsigned short op, char *mnem, unsigned base_timing) { unsigned ea = op & 0x3f, t; /* * Notes About Undefined Flags: * ---------------------------- * * N flag = Set the same as the MSB of the result * V flag = If MSB changes from 1 to 0 when result is adjusted for BCD, * the V flag is set, otherwise it is cleared. */ if (!CheckEA(ea, "101111111000")) return 0; if (!NumDecodedEA(ea)) return 0; InstBegin(op, mnem, NumDecodedEA(ea)); if ((ea >> 3) == 0) /* Dn */ { e("and edi, byte 7\n"); e("xor al, al\n"); /* we use AL so we can use DAS */ e("shr byte [x], 1\n"); /* X->CF */ e("sbb al, [d+edi*4]\n"); e("mov bh, al\n"); /* save unadjusted result in BH */ e("das\n"); /* BCD! */ e("mov bl, ah\n"); /* store old flags */ e("lahf\n"); e("setc byte [x]\n"); e("jnz short .clr\n"); /* Z clear */ e("and bl, byte 0x40\n"); /* isolate old Z */ e("and ah, byte 0x3f\n"); /* kill new Z */ e("or ah, bl\n"); /* in w/ old Z */ EmitLabel(".clr"); e("test al, al\n"); /* N flag */ e("sets dh\n"); e("ror dh, 1\n"); /* into MSB of DH */ e("or ah, dh\n"); /* -> N flag */ e("mov [d+edi*4], al\n"); /* * V flag calculation: Put unadjusted result's MSB into bit 1, and the * adjusted result's MSB into bit 0. If the result is 2, the V flag * is set. */ e("shr bh, byte 6\n"); e("shr al, byte 7\n"); e("and bh, 2\n"); e("or bh, al\n"); e("xor al, al\n"); e("cmp bh, 2\n"); e("jne short .no_v\n"); e("mov al, 1\n"); /* move 1 into V flag */ EmitLabel(".no_v"); t = 6; } else /* memory */ { LoadFromEA(ea, BYTE_SIZE, 0); e("xor al, al\n"); e("shr byte [x], 1\n"); /* X->CF */ e("sbb al, dl\n"); e("mov dh, al\n"); /* DH now has unadjusted result */ e("mov dl, ah\n"); e("das\n"); /* BCD! */ e("lahf\n"); e("setc byte [x]\n"); e("jnz short .clr\n"); e("and dl, byte 0x40\n"); e("and ah, byte 0x3f\n"); e("or ah, dl\n"); EmitLabel(".clr"); e("test al, al\n"); /* N flag */ e("sets dl\n"); e("ror dl, 1\n"); /* into MSB of DL */ e("or ah, dl\n"); /* -> N flag */ e("mov dl, al\n"); /* move result into DL to write it */ /* * V flag calculation: Put unadjusted result's MSB into bit 1, and the * adjusted result's MSB into bit 0. If the result is 2, the V flag * is set. */ e("shr dh, byte 6\n"); e("shr al, byte 7\n"); e("and dh, 2\n"); e("or dh, al\n"); e("xor al, al\n"); e("cmp dh, 2\n"); e("jne short .no_v\n"); e("mov al, 1\n"); /* move 1 into V flag */ EmitLabel(".no_v"); /* WriteXXX will clear EDI for us */ WriteByte(); t = 8 + TimingEA(ea, BYTE_SIZE); } EmitTiming(t); InstEnd(); return 1; } int CHK(unsigned short op, char *mnem, unsigned base_timing) { unsigned reg = (op >> 9) & 7, size, ea = op & 0x3f; /* * Notes About Undefined Flags: * ---------------------------- * * Z flag = Set if register operand is 0 (second operand), cleared * otherwise * V flag = Always cleared (?) * C flag = Always cleared (?) */ if (!CheckEA(ea, "101111111111")) return 0; if (!NumDecodedEA(ea)) return 0; switch ((op >> 7) & 3) { case 3: size = WORD_SIZE; break; default: return 0; } InstBegin(op, mnem, NumDecodedEA(ea)); /* * We have to emit the EA timing first because it must be accounted for * whether or not a trap occurs. DIVU and DIVS work like this too. */ if (TimingEA(ea, size)) e("sub ecx, byte %d\n", TimingEA(ea, size)); LoadFromEA(ea, size, 0); e("xor al, al\n"); /* V should be cleared */ e("test word [d+%d*4], 0xffff\n", reg); /* If Dn == 0 Then Z=1 */ e("setz ah\n"); /* this also clears N and C */ e("shl ah, 6\n"); /* put Z in proper position */ e("test byte [d+%d*4+1], 0x80\n", reg); /* If Dn < 0 Then TRAP */ e("jnz near .trap_dn\n"); e("cmp [d+%d*4], dx\n", reg); /* If Dn > Source Then TRAP */ e("jg near .trap_ea\n"); EmitTiming(base_timing); InstEnd(); EmitLabel(".trap_dn"); /* set N to 1 because Dn<0 test failed */ e("or ah, 0x80\n"); e("jmp short .trap\n"); EmitLabel(".trap_ea"); /* clear N because Dn>Source test failed */ e("and ah, 0x7f\n"); EmitLabel(".trap"); e("jmp near exception_chk\n"); return 1; } int MOVEP(unsigned short op, char *mnem, unsigned base_timing) { unsigned dx = (op >> 9) & 7, opmode = (op >> 6) & 7, t; if (opmode != 4 && opmode != 5 && opmode != 6 && opmode != 7) return 0; InstBegin(op, mnem, 8); e("and edi, byte 7\n"); e("movsx ebx, word [esi]\n"); /* (d16,An) */ e("add ebx, [a+edi*4]\n"); e("add esi, byte 2\n"); switch (opmode & 2) { case 2: /* reg->mem */ if (opmode & 1) /* long */ { t = 24; e("push eax\n"); /* we will be using EAX to save EBX */ e("mov edx, [d+%d*4]\n", dx); e("mov eax, ebx\n"); /* save EBX */ e("rol edx, byte 8\n");/* write high order */ WriteByte(); e("add eax, byte 2\n"); e("rol edx, byte 8\n");/* write mid-upper */ e("mov ebx, eax\n"); WriteByte(); e("add eax, byte 2\n"); e("rol edx, byte 8\n");/* write mid-lower */ e("mov ebx, eax\n"); WriteByte(); e("add eax, byte 2\n"); e("rol edx, byte 8\n");/* write lower order */ e("mov ebx, eax\n"); WriteByte(); e("pop eax\n"); } else /* word */ { t = 16; e("push eax\n"); /* we will be using EAX to save EBX */ e("mov dx, [d+%d*4]\n", dx); e("mov eax, ebx\n"); /* save EBX */ e("ror edx, byte 8\n");/* write high order */ WriteByte(); e("add eax, byte 2\n"); e("rol edx, byte 8\n");/* write low order */ e("mov ebx, eax\n"); WriteByte(); e("pop eax\n"); } break; case 0: /* mem->reg */ if (opmode & 1) /* long */ { t = 24; ReadByte(); /* read high order */ e("mov [d+%d*4+3], dl\n", dx); /* store high order in Dn */ e("add ebx, byte 2\n"); ReadByte(); /* mid-upper order */ e("mov [d+%d*4+2], dl\n", dx); /* store mid-upper order */ e("add ebx, byte 2\n"); ReadByte(); /* mid-lower order */ e("mov [d+%d*4+1], dl\n", dx); /* store mid-lower order */ e("add ebx, byte 2\n"); ReadByte(); /* low order */ e("mov [d+%d*4+0], dl\n", dx); /* store low order */ } else /* word */ { t = 16; ReadByte(); /* read high order */ e("mov [d+%d*4+1], dl\n", dx); /* store high order in Dn */ e("add ebx, byte 2\n"); ReadByte(); /* read low order */ e("mov [d+%d*4+0], dl\n", dx); /* store low order */ } break; } EmitTiming(t); InstEnd(); return 1; } int CMPM(unsigned short op, char *mnem, unsigned base_timing) { unsigned size, ax = (op >> 9) & 7; switch ((op >> 6) & 3) { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } InstBegin(op, mnem, 8); e("and edi, byte 7\n"); e("mov ebx, [a+edi*4]\n"); /* get Ay */ if (size == BYTE_SIZE) /* Ay */ { e("cmp edi, byte 7\n"); /* A7 bytes */ e("cmc\n"); /* switch carry */ e("adc dword [a+edi*4], byte 1\n"); } else if (size == WORD_SIZE) e("add dword [a+edi*4], byte 2\n"); else if (size == LONG_SIZE) e("add dword [a+edi*4], byte 4\n"); switch (size) { case BYTE_SIZE: ReadByte(); break; case WORD_SIZE: ReadWord(); break; case LONG_SIZE: ReadLong(); break; } SaveReg("run", "edx"); /* save Ay */ e("mov ebx, [a+%d*4]\n", ax);/* get Ax */ if (size == BYTE_SIZE) /* Ax */ { if (ax == 7) e("add dword [a+7*4], byte 2\n"); else e("inc dword [a+%d*4]\n", ax); } else if (size == WORD_SIZE) e("add dword [a+%d*4], byte 2\n", ax); else /* LONG */ e("add dword [a+%d*4], byte 4\n", ax); switch (size) { case BYTE_SIZE: ReadByte(); break; case WORD_SIZE: ReadWord(); break; case LONG_SIZE: ReadLong(); break; } switch (size) /* compare */ { case BYTE_SIZE: RestoreRegTo("run", "edx", "bl"); /* [run_edx]->bl */ e("cmp dl, bl\n"); break; case WORD_SIZE: RestoreRegTo("run", "edx", "ebx"); /* [run_edx]->ebx */ e("cmp dx, bx\n"); break; case LONG_SIZE: RestoreRegTo("run", "edx", "ebx"); /* [run_edx]->ebx */ e("cmp edx, ebx\n"); break; } e("lahf\n"); e("seto al\n"); if (size != LONG_SIZE) /* byte, word */ EmitTiming(12); else /* long */ EmitTiming(20); InstEnd(); return 1; } int TRAP(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 16); e("and edi, byte 0xf\n"); /* lower 4 bits=vector */ SaveReg("run", "edi"); e("mov edx, 0x2000\n"); /* what the SR will be */ e("xor edx, [sr]\n"); /* see if S bit is different */ e("test edx, 0x2000\n"); e("jz .no_sp_magic\n"); /* nope, don't swap SPs */ e("mov edx, [__sp]\n"); e("xchg [a+7*4], edx\n"); e("mov [__sp], edx\n"); SetSupervisorAddressSpace(); EmitLabel(".no_sp_magic"); e("mov ebx, [a+7*4]\n"); /* SP */ /* * If 68010, push format info */ if (mpu == 68010) { e("sub ebx, byte 2\n");/* SP-2 */ RestoreReg("run", "edi"); /* vector */ e("mov edx, edi\n"); SaveReg("run", "edi"); e("add edx, byte 32\n"); e("shl edx, byte 2\n"); /* TRAP vector | 0x00000000 */ SaveReg("run", "ebx"); WriteWord(); RestoreReg("run", "ebx"); } e("mov edx, esi\n"); e("sub edx, ebp\n"); /* PC->EDX */ e("sub ebx, byte 4\n"); /* SP-4 */ e("push ebx\n"); WriteLong(); e("pop ebx\n"); e("mov edx, [sr]\n"); /* get SR */ e("sub ebx, byte 2\n"); /* SP-2 */ e("push ebx\n"); SaveReg("run", "ebx"); WriteWord(); /* save SR to stack */ e("pop ebx\n"); e("or dh, 0x20\n"); /* set supervisor for exception */ e("and edx, 0xa71f\n"); /* clear unwanted bits */ e("xchg [sr], edx\n"); /* set new SR, get old SR->EDX */ e("mov [a+7*4], ebx\n"); /* write back SP */ RestoreReg("run", "edi"); e("mov ebx, edi\n"); e("add ebx, byte 32\n"); /* vector+32=trap vector */ e("shl ebx, 2\n"); if (mpu == 68010) e("add ebx, [vbr]\n"); ReadLong(); /* get PC */ e("mov esi, edx\n"); /* set new PC */ UpdateFetchPtr(); e("xor edi, edi\n"); EmitTiming(base_timing); InstEnd(); return 1; } int EORItoSR(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); e("test byte [sr+1], 0x20\n"); /* in supervisor? */ e("jz near exception_privilege_violation\n"); SaveCCR(); /* save flags to SR */ e("mov edx, [esi]\n"); e("and edx, 0xa71f\n"); /* mask out unwanted bits */ e("xor [sr], dx\n"); e("add esi, byte 2\n"); LoadCCR(); /* get flags back */ e("test byte [sr+1], 0x20\n"); /* if we changed to User, swap SPs */ e("jnz short .in_s\n"); e("mov ebx, [a+7*4]\n"); e("mov edx, [__sp]\n"); e("mov [a+7*4], edx\n"); e("mov [__sp], ebx\n"); SetUserAddressSpace(); /* map in user address space */ EmitLabel(".in_s"); EmitTiming(base_timing); InstEnd(); return 1; } int EORItoCCR(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); SaveCCR(); e("mov dl, [esi]\n"); e("and dl, 0x1f\n"); e("xor [sr], dl\n"); LoadCCR(); e("add esi, byte 2\n"); EmitTiming(base_timing); InstEnd(); return 1; } int RTR(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); e("mov ebx, [a+7*4]\n"); /* (SP)->CCR */ ReadWord(); e("and dl, 0x1f\n"); e("mov [sr], dl\n"); e("add dword [a+7*4], byte 2\n"); /* SP+2->SP */ LoadCCR(); e("mov ebx, [a+7*4]\n"); /* (SP)->PC */ ReadLong(); e("add dword [a+7*4], byte 4\n"); /* SP+4->SP */ e("mov esi, edx\n"); UpdateFetchPtr(); EmitTiming(base_timing); InstEnd(); return 1; } int SUBX(unsigned short op, char *mnem, unsigned base_timing) { unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1, size; switch ((op >> 6) & 3) { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } InstBegin(op, mnem, 8); e("and edi, byte 7\n"); if (!rm) /* Dy,Dx */ { if (size == BYTE_SIZE) /* EBX,BX,BL=destination */ e("mov bl, [d+%d*4]\n", rx); else e("mov ebx, [d+%d*4]\n", rx); if (size == BYTE_SIZE) /* EDX,DX,DL=source */ e("mov dl, [d+edi*4]\n"); else e("mov edx, [d+edi*4]\n"); e("shr byte [x], 1\n"); /* X->CF */ switch (size) { case BYTE_SIZE: e("sbb bl, dl\n"); break; case WORD_SIZE: e("sbb bx, dx\n"); break; case LONG_SIZE: e("sbb ebx, edx\n"); break; } e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */ e("lahf\n"); e("setc byte [x]\n"); e("seto al\n"); e("jnz short .z\n"); /* if non-zero, cleared */ e("and dl, 0x40\n"); /* otherwise, unchanged */ e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */ e("or ah, dl\n"); /* OR in the old, unchanged Z flag */ EmitLabel(".z"); switch (size) /* store results */ { case BYTE_SIZE: e("mov [d+%d*4], bl\n", rx); break; case WORD_SIZE: e("mov [d+%d*4], bx\n", rx); break; case LONG_SIZE: e("mov [d+%d*4], ebx\n", rx); break; } } else /* -(Ay),-(Ax) */ { if (size == BYTE_SIZE) { e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */ e("cmc\n"); e("sbb dword [a+edi*4], byte 1\n"); if (rx != 7) e("dec dword [a+%d*4]\n", rx); else e("sub dword [a+7*4], byte 2\n"); } else if (size == WORD_SIZE) { e("sub dword [a+edi*4], byte 2\n"); e("sub dword [a+%d*4], byte 2\n", rx); } else if (size == LONG_SIZE) { e("sub dword [a+edi*4], byte 4\n"); e("sub dword [a+%d*4], byte 4\n", rx); } e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */ switch (size) { case BYTE_SIZE: ReadByte(); break; case WORD_SIZE: ReadWord(); break; case LONG_SIZE: ReadLong(); break; } SaveReg("run", "edx"); /* save data obtained */ e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */ switch (size) { case BYTE_SIZE: ReadByte(); break; case WORD_SIZE: ReadWord(); break; case LONG_SIZE: ReadLong(); break; } SaveReg("run", "ebx"); e("mov ebx, edx\n"); /* EBX,BX,BL=destination */ RestoreReg("run", "edx"); /* EDX,DX,DL=source */ e("shr byte [x], 1\n"); /* X->CF */ switch (size) { case BYTE_SIZE: e("sbb bl, dl\n"); break; case WORD_SIZE: e("sbb bx, dx\n"); break; case LONG_SIZE: e("sbb ebx, edx\n"); break; } e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */ e("lahf\n"); e("setc byte [x]\n"); e("seto al\n"); e("jnz short .z\n"); /* if non-zero, cleared */ e("and dl, 0x40\n"); /* otherwise, unchanged */ e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */ e("or ah, dl\n"); /* OR in the old, unchanged Z flag */ EmitLabel(".z"); e("mov edx, ebx\n"); /* store result */ RestoreReg("run", "ebx"); switch (size) { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } } if (!rm) { if (size == LONG_SIZE) EmitTiming(8); else EmitTiming(4); } else { if (size == LONG_SIZE) EmitTiming(30); else EmitTiming(18); } InstEnd(); return 1; } int ADDX(unsigned short op, char *mnem, unsigned base_timing) { unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1, size; switch ((op >> 6) & 3) { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } InstBegin(op, mnem, 8); e("and edi, byte 7\n"); if (!rm) /* Dy,Dx */ { if (size == BYTE_SIZE) /* EBX,BX,BL=destination */ e("mov bl, [d+%d*4]\n", rx); else e("mov ebx, [d+%d*4]\n", rx); if (size == BYTE_SIZE) /* EDX,DX,DL=source */ e("mov dl, [d+edi*4]\n"); else e("mov edx, [d+edi*4]\n"); e("shr byte [x], 1\n"); /* X->CF */ switch (size) { case BYTE_SIZE: e("adc bl, dl\n"); break; case WORD_SIZE: e("adc bx, dx\n"); break; case LONG_SIZE: e("adc ebx, edx\n"); break; } e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */ e("lahf\n"); e("setc byte [x]\n"); e("seto al\n"); e("jnz short .z\n"); /* if non-zero, cleared */ e("and dl, 0x40\n"); /* otherwise, unchanged */ e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */ e("or ah, dl\n"); /* OR in the old, unchanged Z flag */ EmitLabel(".z"); switch (size) /* store results */ { case BYTE_SIZE: e("mov [d+%d*4], bl\n", rx); break; case WORD_SIZE: e("mov [d+%d*4], bx\n", rx); break; case LONG_SIZE: e("mov [d+%d*4], ebx\n", rx); break; } } else /* -(Ay),-(Ax) */ { if (size == BYTE_SIZE) { e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */ e("cmc\n"); e("sbb dword [a+edi*4], byte 1\n"); if (rx != 7) e("dec dword [a+%d*4]\n", rx); else e("sub dword [a+7*4], byte 2\n"); } else if (size == WORD_SIZE) { e("sub dword [a+edi*4], byte 2\n"); e("sub dword [a+%d*4], byte 2\n", rx); } else if (size == LONG_SIZE) { e("sub dword [a+edi*4], byte 4\n"); e("sub dword [a+%d*4], byte 4\n", rx); } e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */ switch (size) { case BYTE_SIZE: ReadByte(); break; case WORD_SIZE: ReadWord(); break; case LONG_SIZE: ReadLong(); break; } SaveReg("run", "edx"); /* save data obtained */ e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */ switch (size) { case BYTE_SIZE: ReadByte(); break; case WORD_SIZE: ReadWord(); break; case LONG_SIZE: ReadLong(); break; } SaveReg("run", "ebx"); e("mov ebx, edx\n"); /* EBX,BX,BL=destination */ RestoreReg("run", "edx"); /* EDX,DX,DL=source */ e("shr byte [x], 1\n"); /* X->CF */ switch (size) { case BYTE_SIZE: e("adc bl, dl\n"); break; case WORD_SIZE: e("adc bx, dx\n"); break; case LONG_SIZE: e("adc ebx, edx\n"); break; } e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */ e("lahf\n"); e("setc byte [x]\n"); e("seto al\n"); e("jnz short .z\n"); /* if non-zero, cleared */ e("and dl, 0x40\n"); /* otherwise, unchanged */ e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */ e("or ah, dl\n"); /* OR in the old, unchanged Z flag */ EmitLabel(".z"); e("mov edx, ebx\n"); /* store result */ RestoreReg("run", "ebx"); switch (size) { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } } if (!rm) { if (size == LONG_SIZE) EmitTiming(8); else EmitTiming(4); } else { if (size == LONG_SIZE) EmitTiming(30); else EmitTiming(18); } InstEnd(); return 1; } int SBCD(unsigned short op, char *mnem, unsigned base_timing) { /* * Notes About Undefined Flags: * ---------------------------- * * N flag = Set the same as the MSB of the result * V flag = If MSB changes from 1 to 0 when result is adjusted for BCD, * the V flag is set, otherwise it is cleared. */ unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1; InstBegin(op, mnem, 8); e("and edi, byte 7\n"); if (!rm) /* Dy,Dx */ { e("mov al, [d+%d*4]\n", rx); /* AL=destination */ e("mov bl, [d+edi*4]\n"); /* BL=source */ e("shr byte [x], 1\n"); /* X->CF */ e("sbb al, bl\n"); /* subtract */ e("mov bh, al\n"); /* save the unadjusted result in BH */ e("das\n"); /* adjust the packed BCD result */ e("setc dl\n"); e("setc byte [x]\n"); e("jz short .unchanged_z\n"); e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */ EmitLabel(".unchanged_z"); e("and ah, 0x7e\n"); e("or ah, dl\n"); /* set C */ e("test al, al\n"); /* N flag */ e("sets dl\n"); e("ror dl, 1\n"); /* N flag -> MSB of DL */ e("or ah, dl\n"); /* into N flag */ e("mov [d+%d*4], al\n", rx); /* store result */ /* * V flag calculation: Put unadjusted result's MSB into bit 1, and the * adjusted result's MSB into bit 0. If the result is 2, the V flag * is set. */ e("shr bh, byte 6\n"); e("shr al, byte 7\n"); e("and bh, 2\n"); e("or bh, al\n"); e("cmp bh, 2\n"); e("sete al\n"); /* 1 -> V */ } else /* -(Ay),-(Ax) */ { e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */ e("cmc\n"); e("sbb dword [a+edi*4], byte 1\n"); if (rx != 7) e("dec dword [a+%d*4]\n", rx); else e("sub dword [a+7*4], byte 2\n"); e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */ ReadByte(); SaveReg("run", "dl"); /* save byte obtained */ e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */ ReadByte(); e("mov al, dl\n"); /* AL=destination */ RestoreReg("run", "dl"); /* DL=source */ e("shr byte [x], 1\n"); /* X->CF */ e("sbb al, dl\n"); /* subtract */ e("mov dh, al\n"); /* save unadjusted result in DH */ e("das\n"); /* adjust the packed BCD result */ e("setc dl\n"); e("setc byte [x]\n"); e("jz short .unchanged_z\n"); e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */ EmitLabel(".unchanged_z"); e("and ah, 0x7e\n"); e("or ah, dl\n"); /* set C */ e("test al, al\n"); /* N flag */ e("sets dl\n"); e("ror dl, 1\n"); /* N flag -> MSB of DL */ e("or ah, dl\n"); /* into N flag */ e("mov dl, al\n"); /* prepare to write result */ /* * V flag calculation: Put unadjusted result's MSB into bit 1, and the * adjusted result's MSB into bit 0. If the result is 2, the V flag * is set. */ e("shr dh, byte 6\n"); e("shr al, byte 7\n"); e("and dh, 2\n"); e("or dh, al\n"); e("cmp dh, 2\n"); e("sete al\n"); /* 1->V */ WriteByte(); } if (!rm) EmitTiming(6); else EmitTiming(18); InstEnd(); return 1; } int ABCD(unsigned short op, char *mnem, unsigned base_timing) { unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1; /* * Notes About Undefined Flags: * ---------------------------- * * N flag = Set the same as the MSB of the result * V flag = If MSB changes from 0 to 1 when result is adjusted for BCD, * the V flag is set, otherwise it is cleared. */ InstBegin(op, mnem, 8); e("and edi, byte 7\n"); if (!rm) /* Dy,Dx */ { e("mov al, [d+%d*4]\n", rx); /* AL=destination */ e("mov bl, [d+edi*4]\n"); /* BL=source */ e("shr byte [x], 1\n"); /* X->CF */ e("adc al, bl\n"); /* add with extend */ e("mov bh, al\n"); /* save unadjusted result in BH */ e("daa\n"); /* adjust the packed BCD result */ e("setc dl\n"); e("setc byte [x]\n"); e("jz short .unchanged_z\n"); e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */ EmitLabel(".unchanged_z"); e("and ah, 0x7e\n"); e("or ah, dl\n"); /* set C */ e("test al, al\n"); /* N flag */ e("sets dl\n"); e("ror dl, 1\n"); /* N flag -> MSB of DL */ e("or ah, dl\n"); /* into N flag */ e("mov [d+%d*4], al\n", rx); /* store result */ /* * V flag calculation: Put unadjusted result's MSB into bit 1, and the * adjusted result's MSB into bit 0. If the result is 01, the V flag * is set. */ e("shr bh, byte 6\n"); e("shr al, byte 7\n"); e("and bh, 2\n"); e("or bh, al\n"); e("cmp bh, 1\n"); e("sete al\n"); /* move 1 into V flag */ } else /* -(Ay),-(Ax) */ { e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */ e("cmc\n"); e("sbb dword [a+edi*4], byte 1\n"); if (rx != 7) e("dec dword [a+%d*4]\n", rx); else e("sub dword [a+7*4], byte 2\n"); e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */ ReadByte(); SaveReg("run", "dl"); /* save byte obtained */ e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */ ReadByte(); e("mov al, dl\n"); /* AL=destination */ RestoreReg("run", "dl"); /* DL=source */ e("shr byte [x], 1\n"); /* X->CF */ e("adc al, dl\n"); /* add */ e("mov dh, al\n"); /* save unadjusted result in DH */ e("daa\n"); /* adjust the packed BCD result */ e("setc dl\n"); e("setc byte [x]\n"); e("jz short .unchanged_z\n"); e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */ EmitLabel(".unchanged_z"); e("and ah, 0x7e\n"); e("or ah, dl\n"); /* set C */ e("test al, al\n"); /* N flag */ e("sets dl\n"); e("ror dl, 1\n"); /* N flag -> MSB of DL */ e("or ah, dl\n"); /* into N flag */ e("mov dl, al\n"); /* store result */ /* * V flag calculation: Put unadjusted result's MSB into bit 1, and the * adjusted result's MSB into bit 0. If the result is 1, the V flag * is set. */ e("shr dh, byte 6\n"); e("shr al, byte 7\n"); e("and dh, 2\n"); e("or dh, al\n"); e("cmp dh, 1\n"); e("sete al\n"); /* 1 -> V */ WriteByte(); } if (!rm) EmitTiming(6); else EmitTiming(18); InstEnd(); return 1; } int ORItoCCR(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); SaveCCR(); e("mov dl, [esi]\n"); /* get data */ e("and dl, 0x1f\n"); /* mask out unwanted bits */ e("or [sr], dl\n"); e("add esi, byte 2\n"); LoadCCR(); EmitTiming(base_timing); InstEnd(); e("db 66, 65, 82, 84, 33, 1\n"); return 1; } int Scc(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned ea = op & 0x3f, cond = (op >> 8) & 0xf; char mnem[4]; char *mnem_t[] = { "ST", "SF", "SHI", "SLS", "SCC", "SCS", "SNE", "SEQ", "SVC", "SVS", "SPL", "SMI", "SGE", "SLT", "SGT", "SLE" }; if (!CheckEA(ea, "101111111000")) return 0; if (!NumDecodedEA(ea)) return 0; strcpy(mnem, mnem_t[cond]); InstBegin(op, mnem, NumDecodedEA(ea)); switch (cond) { /* EAX:0000000000000000 NZ00 000C 0000 000V */ case 0: /* T: Always true */ if ((ea >> 3) == 0) /* Dn */ { e("and edi, byte 7\n"); e("mov byte [d+edi*4], 0xff\n"); EmitTiming(6); /* reg: byte, true */ } else { if (mpu == 68000 && dummyread && (ea >> 3)) { /* * If the processor type is 68000, and the EA mode is not * register, we can emulate dummy reads! */ LoadFromEA(ea, BYTE_SIZE, 0); e("mov dl, 0xff\n"); WriteByte(); } else { e("mov dl, 0xff\n"); StoreToEAUsingEDI(ea, BYTE_SIZE, 0); } EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, true */ } InstEnd(); return 1; break; case 1: /* F: Always false */ if ((ea >> 3) == 0) /* Dn */ { e("and edi, byte 7\n"); e("mov byte [d+edi*4], 0\n"); EmitTiming(4); /* reg: byte, false */ } else { if (mpu == 68000 && dummyread && (ea >> 3)) { /* * If the processor type is 68000, and the EA mode is not * register, we can emulate dummy reads! */ LoadFromEA(ea, BYTE_SIZE, 0); e("xor dl, dl\n"); WriteByte(); } else { e("xor dl, dl\n"); StoreToEAUsingEDI(ea, BYTE_SIZE, 0); } EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, false */ } InstEnd(); return 1; break; case 2: /* HI: !C & !Z */ e("test ah, byte 0x41\n"); e("jz short do%d\n", op); break; case 3: /* LS: C | Z */ e("test ah, byte 0x41\n"); e("jnz short do%d\n", op); break; case 4: /* CC: !C */ e("test ah, byte 1\n"); e("jz short do%d\n", op); break; case 5: /* CS: C */ e("test ah, byte 1\n"); e("jnz short do%d\n", op); break; case 6: /* NE: !Z */ e("test ah, byte 0x40\n"); e("jz short do%d\n", op); break; case 7: /* EQ: Z */ e("test ah, byte 0x40\n"); e("jnz short do%d\n", op); break; case 8: /* VC: !V */ e("test al, byte 1\n"); e("jz short do%d\n", op); break; case 9: /* VS: V */ e("test al, byte 1\n"); e("jnz short do%d\n", op); break; case 0xa: /* PL: !N */ e("test ah, byte 0x80\n"); e("jz short do%d\n", op); break; case 0xb: /* MI: N */ e("test ah, byte 0x80\n"); e("jnz short do%d\n", op); break; case 0xc: /* GE: N & V | !N & !V (N&V || !N&!V) */ e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpe short do%d\n", op); break; case 0xd: /* LT: N & !V | !N & V */ e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpo short do%d\n", op); EmitLabel(".dont_do"); break; case 0xe: /* GT: N & V & !Z | !N & !V & !Z */ /* if Z, then it is GE and we don't take this branch */ e("test ah, byte 0x40\n"); e("jnz short .dont_do\n"); e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpe short do%d\n", op); EmitLabel(".dont_do"); break; case 0xf: /* LE: Z | N & !V | !N & V */ e("test ah, 0x40\n"); e("jnz short do%d\n", op); e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpo short do%d\n", op); break; } if ((ea >> 3) == 0) /* Dn */ { e("and edi, byte 7\n"); e("mov byte [d+edi*4], 0\n"); EmitTiming(4); /* reg: byte, false */ } else { e("false%d:\n", op); /* this prevents redefinitions of labels in the EA code */ if (mpu == 68000 && dummyread && (ea >> 3)) { /* * If the processor type is 68000, and the EA mode is not * register, we can emulate dummy reads! */ LoadFromEA(ea, BYTE_SIZE, 0); e("xor dl, dl\n"); WriteByte(); } else { e("xor dl, dl\n"); StoreToEAUsingEDI(ea, BYTE_SIZE, 0); } EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, false */ } InstEnd(); Align(4); e("do%d:\n", op); if ((ea >> 3) == 0) /* Dn */ { e("and edi, byte 7\n"); e("mov byte [d+edi*4], 0xff\n"); EmitTiming(6); /* reg: byte, true */ } else { e("true%d:\n", op); /* this prevents redefinitions of labels in the EA code */ if (mpu == 68000 && dummyread && (ea >> 3)) { /* * If the processor type is 68000, and the EA mode is not * register, we can emulate dummy reads! */ LoadFromEA(ea, BYTE_SIZE, 0); e("mov dl, 0xff\n"); WriteByte(); } else { e("mov dl, 0xff\n"); StoreToEAUsingEDI(ea, BYTE_SIZE, 0); } EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, true */ } InstEnd(); return 1; } int EXG(unsigned short op, char *mnem, unsigned base_timing) { unsigned rx = (op >> 9) & 7, opmode = (op >> 3) & 0x1f; if (opmode != 0x8 && opmode != 0x9 && opmode != 0x11) return 0; InstBegin(op, mnem, 8); e("and edi, byte 7\n"); switch (opmode) { case 8: /* Dx,Dy */ e("mov ebx, [d+%d*4]\n", rx); e("mov edx, [d+edi*4]\n"); e("mov [d+%d*4], edx\n", rx); e("mov [d+edi*4], ebx\n"); break; case 9: /* Ax,Ay */ e("mov ebx, [a+%d*4]\n", rx); e("mov edx, [a+edi*4]\n"); e("mov [a+%d*4], edx\n", rx); e("mov [a+edi*4], ebx\n"); break; case 0x11: /* Dx,Ay */ e("mov ebx, [d+%d*4]\n", rx); e("mov edx, [a+edi*4]\n"); e("mov [d+%d*4], edx\n", rx); e("mov [a+edi*4], ebx\n"); break; } EmitTiming(base_timing); InstEnd(); return 1; } int MULS(unsigned short op, char *mnem, unsigned base_timing) { unsigned ea = op & 0x3f, dn = (op >> 9) & 7; if (!CheckEA(ea, "101111111111")) return 0; if (!NumDecodedEA(ea)) return 0; InstBegin(op, mnem, NumDecodedEA(ea)); LoadFromEA(ea, WORD_SIZE, 0); e("mov ax, [d+%d*4]\n", dn); e("imul dx\n"); /* ax*dx->dx:ax */ e("shl edx, byte 16\n"); /* EDX=32-bit result */ e("mov dx, ax\n"); e("mov [d+%d*4], edx\n", dn); e("test edx, edx\n"); /* C is always cleared */ e("lahf\n"); e("xor al, al\n"); /* V=1 only occurs on 32*32 (not on 68000) */ EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); InstEnd(); return 1; } int MULU(unsigned short op, char *mnem, unsigned base_timing) { unsigned ea = op & 0x3f, dn = (op >> 9) & 7; if (!CheckEA(ea, "101111111111")) return 0; if (!NumDecodedEA(ea)) return 0; InstBegin(op, mnem, NumDecodedEA(ea)); LoadFromEA(ea, WORD_SIZE, 0); e("mov ax, [d+%d*4]\n", dn); e("mul dx\n"); /* ax*dx->dx:ax */ e("shl edx, byte 16\n"); /* EDX=32-bit result */ e("mov dx, ax\n"); e("mov [d+%d*4], edx\n", dn); e("test edx, edx\n"); /* C is always cleared */ e("lahf\n"); e("xor al, al\n"); /* V=1 only occurs on 32*32 (not on 68000) */ EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); InstEnd(); return 1; } int DIVS(unsigned short op, char *mnem, unsigned base_timing) { unsigned ea = op & 0x3f, dn = (op >> 9) & 7; if (!CheckEA(ea, "101111111111")) return 0; if (!NumDecodedEA(ea)) return 0; /* See the note in DIVU() for info on how overflow is detected */ InstBegin(op, mnem, NumDecodedEA(ea)); if (TimingEA(ea, WORD_SIZE)) e("sub ecx, byte %d\n", TimingEA(ea, WORD_SIZE)); /* do 1st.. */ LoadFromEA(ea, WORD_SIZE, 0); e("test dx, dx\n"); e("jz .divide_by_zero\n"); e("movsx ebx, dx\n"); /* divisor->EBX (sign extend to work) */ SaveReg("run", "eax"); e("mov eax, [d+%d*4]\n", dn); /* dividend */ e("cdq\n"); /* EAX->sign extend->EDX:EAX */ e("idiv ebx\n"); e("cmp eax, -32768\n"); e("jl .overflow\n"); e("cmp eax, 32767\n"); e("jg .overflow\n"); e("test ax, ax\n"); /* test for flags */ e("mov [d+(%d*4)+2], dx\n", dn); /* store back to Dn */ e("mov [d+(%d*4)+0], ax\n", dn); e("lahf\n"); /* the mov's did not affect the flags... */ e("xor al, al\n"); /* C cleared by TEST */ EmitTiming(base_timing); InstEnd(); EmitLabel(".overflow"); RestoreReg("run", "eax"); e("mov eax, 1\n"); /* clear C and set overflow */ EmitTiming(base_timing); InstEnd(); EmitLabel(".divide_by_zero"); e("xor eax, eax\n"); /* clear C */ e("jmp near exception_divide_by_zero\n"); return 1; } int DIVU(unsigned short op, char *mnem, unsigned base_timing) { /* * Overflow Detection: * * Overflow detection is actually pretty simple. The 68000 DIVU and DIVS * instructions divide 32-bit operands by 16-bit operands. What I've done * is zero extend the operands to 64-bit and 32-bit and then divide. If * the result is larger than 0xFFFF (16 bits), the overflow condition is * set. * * See the end of this function for an alternate method. */ unsigned ea = op & 0x3f, dn = (op >> 9) & 7; if (!CheckEA(ea, "101111111111")) return 0; if (!NumDecodedEA(ea)) return 0; InstBegin(op, mnem, NumDecodedEA(ea)); LoadFromEA(ea, WORD_SIZE, 0); if (TimingEA(ea, WORD_SIZE)) e("sub ecx, byte %d\n", TimingEA(ea, WORD_SIZE)); /* do 1st.. */ e("test dx, dx\n"); e("jz .divide_by_zero\n"); e("mov ebx, edx\n"); e("and ebx, 0xffff\n"); /* divisor->EBX */ SaveReg("run", "eax"); e("mov eax, [d+%d*4]\n", dn); /* dividend */ e("xor edx, edx\n"); /* EDX:EAX=dividend */ e("div ebx\n"); e("cmp eax, 0xffff\n"); e("jg .overflow\n"); e("test ax, ax\n"); /* test for flags */ e("mov [d+(%d*4)+2], dx\n", dn); /* store back to Dn */ e("mov [d+(%d*4)+0], ax\n", dn); e("lahf\n"); /* the mov's did not affect the flags... */ e("xor al, al\n"); /* C cleared by TEST */ EmitTiming(base_timing); InstEnd(); EmitLabel(".overflow"); RestoreReg("run", "eax"); e("mov eax, 1\n"); /* clear C and set overflow */ EmitTiming(base_timing); InstEnd(); EmitLabel(".divide_by_zero"); e("xor eax, eax\n"); /* clear C */ e("jmp near exception_divide_by_zero\n"); return 1; /* * * An alternate method to detecting overflows is shown below. It doesn't * seem to be as acurrate as the method that is actually used, but perhaps * I implemented it wrong or misunderstood it. It broke Comix Zone, Hard * Drivin', and Race Drivin' (well, caused graphical glitches actually.) * * Kuwanger in #programmers (DALnet, of course) came up with a cool idea * on how to detect overflow before actually dividing. He used an example * of AH:AL/BL ... in the emulator, it is DX:AX/BX. The dividend, DX:AX * is stored entirely in EDX, and it is shifted right by the bit number of * the highest 1 bit in BX, the divisor. If the high 16-bits of EDX (which * would be DX in DX:AX) are not clear, then an overflow would occur. * * Here's Kuwanger's classic lecture: * * Maybe I should use a smaller example. * Lets use, ah:al/bl * ... * trzy: You could just convert one to a bit value. * trzy: Ie, hb(bl).. * And then shift the other one ah:al * ... * Though hb(bl) would have to return 0, that time. * then ah:all>>hb(bl) * err, ah:al, rather * Ie, you shift by the most prominent bit. * And if ah is non-zero.. * Well, then the number won't fit. * ... * Lets try an extreme case. * 65535/255 * 65535>>7 * ... * That obviously doesn't fit. :) * * Hip-hip-HURRAY! */ } int NOT(unsigned short op, char *mnem, unsigned base_timing) { int size; unsigned ea = op & 0x3f, t; if (!CheckEA(ea, "101111111000")) return 0; if (!NumDecodedEA(ea)) return 0; switch ((op >> 6) & 3) { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } InstBegin(op, mnem, NumDecodedEA(ea)); if ((ea >> 3) == 0) /* Dn */ { e("and edi, byte 7\n"); if (size == BYTE_SIZE) e("mov dl, [d+edi*4]\n"); else e("mov edx, [d+edi*4]\n"); switch (size) { case BYTE_SIZE: e("not dl\n"); e("test dl, dl\n"); break; case WORD_SIZE: e("not dx\n"); e("test dx, dx\n"); break; case LONG_SIZE: e("not edx\n"); e("test edx, edx\n"); break; } e("lahf\n"); /* C cleared */ e("xor al, al\n"); /* V cleared */ switch (size) { case BYTE_SIZE: e("mov [d+edi*4], dl\n"); break; case WORD_SIZE: e("mov [d+edi*4], dx\n"); break; case LONG_SIZE: e("mov [d+edi*4], edx\n"); break; } switch (size) { case BYTE_SIZE: case WORD_SIZE: t = 4; break; case LONG_SIZE: t = 6; break; } } else /* mem */ { LoadFromEA(ea, size, 0); switch (size) { case BYTE_SIZE: e("not dl\n"); e("test dl, dl\n"); break; case WORD_SIZE: e("not dx\n"); e("test dx, dx\n"); break; case LONG_SIZE: e("not edx\n"); e("test edx, edx\n"); break; } e("lahf\n"); /* C cleared */ e("xor al, al\n"); /* V cleared */ switch (size) { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } switch (size) { case BYTE_SIZE: case WORD_SIZE: t = 8; break; case LONG_SIZE: t = 12; break; } t += TimingEA(ea, size); } EmitTiming(t); InstEnd(); return 1; } int RTE(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); e("test byte [sr+1], 0x20\n"); /* must be in supervisor mode */ e("jz near exception_privilege_violation\n"); e("mov ebx, [a+7*4]\n"); SaveReg("run", "ebx"); ReadWord(); /* get SR */ RestoreReg("run", "ebx"); e("and edx, 0xa71f\n"); e("mov [sr], edx\n"); LoadCCR(); e("add ebx, byte 2\n"); SaveReg("run", "ebx"); ReadLong(); /* get PC */ RestoreReg("run", "ebx"); e("add ebx, byte 4\n"); /* * If 68010, we have to fetch the format word and check it. If invalid, * we branch to stackframe_error. The PC points at the word AFTER the * faulty RTE. */ if (mpu == 68010) { SaveReg("run", "edx"); /* EDX has the PC! */ SaveReg("run", "ebx"); ReadWord(); RestoreReg("run", "ebx"); e("add ebx, byte 2\n"); e("test dh, 0xf0\n"); /* format should only be 0x0000 */ e("jnz near stackframe_error\n"); RestoreReg("run", "edx"); } e("mov [a+7*4], ebx\n"); e("mov esi, edx\n"); UpdateFetchPtr(); e("test byte [sr+1], 0x20\n"); /* if no longer in supervisor, SP->SSP, USP->SP */ e("jnz short .in_s\n"); e("mov ebx, [a+7*4]\n"); e("mov edx, [__sp]\n"); e("mov [a+7*4], edx\n"); e("mov [__sp], ebx\n"); SetUserAddressSpace(); /* map in user address space */ EmitLabel(".in_s"); EmitTiming(base_timing); InstEnd(); return 1; } int ANDItoCCR(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); SaveCCR(); e("mov dl, [esi]\n"); /* get data */ e("and dl, 0x1f\n"); /* mask out unwanted bits */ e("and [sr], dl\n"); e("add esi, byte 2\n"); LoadCCR(); EmitTiming(base_timing); InstEnd(); return 1; } int CMPA(unsigned short op, char *mnem, unsigned base_timing) { unsigned opmode = (op >> 6) & 7, t; if (!CheckEA(op & 0x3f, "111111111111")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; if (opmode != 3 && opmode != 7) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); if (opmode == 3) LoadFromEA(op & 0x3f, WORD_SIZE, 1); else LoadFromEA(op & 0x3f, LONG_SIZE, 0); e("cmp [a+%d*4], edx\n", (op >> 9) & 7); e("lahf\n"); e("seto al\n"); if (opmode == 3) /* word size */ t = 6 + TimingEA(op & 0x3f, WORD_SIZE); else /* long */ t = 6 + TimingEA(op & 0x3f, LONG_SIZE); EmitTiming(t); InstEnd(); return 1; } int PEA(unsigned short op, char *mnem, unsigned base_timing) { unsigned t; if (!CheckEA(op & 0x3f, "001001111011")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); LoadControlEA(op & 0x3f); e("sub dword [a+7*4], byte 4\n"); /* SP-4->SP */ e("mov edx, ebx\n"); e("mov ebx, [a+7*4]\n"); WriteLong(); /* ->(SP) */ switch ((op >> 3) & 7) /* timing based on control addressing mode */ { case 2: t = 12; break; /* (An) */ case 5: t = 16; break; /* (d16,An) */ case 6: t = 20; break; /* (d8,An,Xn) */ case 7: switch (op & 7) { case 0: t = 16; break; /* (xxx).W */ case 1: t = 20; break; /* (xxx).L */ case 2: t = 16; break; /* (d16,pc) */ case 3: t = 20; break; /* (d8,pc,Xn) */ } break; } EmitTiming(t); InstEnd(); return 1; } int SWAP(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 8); e("and edi, byte 7\n"); e("mov edx, [d+edi*4]\n"); e("rol edx, byte 16\n"); /* swap words */ e("test edx, edx\n"); /* clears CF, will set N and Z */ e("lahf\n"); e("xor al, al\n"); /* clear V */ e("mov [d+edi*4], edx\n"); EmitTiming(base_timing); InstEnd(); return 1; } int BSET(unsigned short op, char *mnem, unsigned base_timing) { unsigned t; if (((op >> 6) & 7) != 7 && ((op >> 6) & 7) != 3) return 0; if (op & 0x100) /* dynamic */ { if (!CheckEA(op & 0x3f, "101111111000")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); if (((op >> 3) & 7) == 0) /* Dn is long */ { t = 8; e("and edi, byte 7\n"); e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ e("and ebx, byte 31\n"); /* same as modulo 32 */ e("bts dword [d+edi*4], ebx\n"); /* bit->CF, set */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ } else /* mem is byte */ { t = 8; LoadFromEA(op & 0x3f, BYTE_SIZE, 0); e("mov edi, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ e("and edi, byte 7\n"); /* same as modulo 8 */ e("bts edx, edi\n"); /* bit->CF, set */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ WriteByte(); /* clears EDI for us */ } } else if (!(op & 0x100)) /* static */ { if (!CheckEA(op & 0x3f, "101111111000")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); if (((op >> 3) & 7) == 0) /* Dn is long */ { t = 12; e("and edi, byte 7\n"); /* isolate Dn reg # */ e("mov ebx, [esi]\n"); /* get bit # */ e("and ebx, byte 31\n"); /* same as modulo 32 */ e("add esi, byte 2\n"); e("bts dword [d+edi*4], ebx\n"); /* bit->CF, set */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ } else /* mem is byte */ { t = 12; SaveRegTo("run", "edi", "esi"); /* save to EDI spot */ e("add esi, byte 2\n"); LoadFromEA(op & 0x3f, BYTE_SIZE, 0); RestoreReg("run", "edi"); e("mov edi, [edi]\n"); e("and edi, byte 7\n"); /* same as modulo 8 */ e("bts edx, edi\n"); /* bit->CF, set */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ WriteByte(); /* write back, EDI is cleared */ } } if (((op & 0x3f) & 7) == 0) /* Dn is long */ EmitTiming(t); else /* mem is byte */ EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE)); InstEnd(); return 1; } int BCLR(unsigned short op, char *mnem, unsigned base_timing) { unsigned t; if (((op >> 6) & 7) != 6 && ((op >> 6) & 7) != 2) return 0; if (op & 0x100) /* dynamic */ { if (!CheckEA(op & 0x3f, "101111111000")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); if (((op >> 3) & 7) == 0) /* Dn is long */ { t = 10; e("and edi, byte 7\n"); e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ e("and ebx, byte 31\n"); /* same as modulo 32 */ e("btr dword [d+edi*4], ebx\n"); /* bit->CF, clear */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ } else /* mem is byte */ { t = 8; LoadFromEA(op & 0x3f, BYTE_SIZE, 0); e("mov edi, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ e("and edi, byte 7\n"); /* same as modulo 8 */ e("btr edx, edi\n"); /* bit->CF, clear */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ WriteByte(); /* clears EDI for us */ } } else if (!(op & 0x100)) /* static */ { if (!CheckEA(op & 0x3f, "101111111000")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); if (((op >> 3) & 7) == 0) /* Dn is long */ { t = 14; e("and edi, byte 7\n"); /* isolate Dn reg # */ e("mov ebx, [esi]\n"); /* get bit # */ e("and ebx, byte 31\n"); /* same as modulo 32 */ e("add esi, byte 2\n"); e("btr dword [d+edi*4], ebx\n"); /* bit->CF, clear */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ } else /* mem is byte */ { t = 12; SaveRegTo("run", "edi", "esi"); /* save to EDI spot */ e("add esi, byte 2\n"); LoadFromEA(op & 0x3f, BYTE_SIZE, 0); RestoreReg("run", "edi"); e("mov edi, [edi]\n"); e("and edi, byte 7\n"); /* same as modulo 8 */ e("btr edx, edi\n"); /* bit->CF, clear */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ WriteByte(); /* write back, EDI is cleared */ } } if (((op & 0x3f) & 7) == 0) /* Dn is long */ EmitTiming(t); else /* mem is byte */ EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE)); InstEnd(); return 1; } int BCHG(unsigned short op, char *mnem, unsigned base_timing) { unsigned t; if (((op >> 6) & 7) != 5 && ((op >> 6) & 7) != 1) return 0; if (op & 0x100) /* dynamic */ { if (!CheckEA(op & 0x3f, "101111111000")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); if (((op >> 3) & 7) == 0) /* Dn is long */ { t = 8; e("and edi, byte 7\n"); e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ e("and ebx, byte 31\n"); /* same as modulo 32 */ e("btc dword [d+edi*4], ebx\n"); /* bit->CF, change */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ } else /* mem is byte */ { t = 8; LoadFromEA(op & 0x3f, BYTE_SIZE, 0); e("mov edi, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ e("and edi, byte 7\n"); /* same as modulo 8 */ e("btc edx, edi\n"); /* bit->CF, change */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ WriteByte(); /* clears EDI for us */ } } else if (!(op & 0x100)) /* static */ { if (!CheckEA(op & 0x3f, "101111111000")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); if (((op >> 3) & 7) == 0) /* Dn is long */ { t = 12; e("and edi, byte 7\n"); /* isolate Dn reg # */ e("mov ebx, [esi]\n"); /* get bit # */ e("and ebx, byte 31\n"); /* same as modulo 32 */ e("add esi, byte 2\n"); e("btc dword [d+edi*4], ebx\n"); /* bit->CF, change */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ } else /* mem is byte */ { t = 12; SaveRegTo("run", "edi", "esi"); /* save to EDI spot */ e("add esi, byte 2\n"); LoadFromEA(op & 0x3f, BYTE_SIZE, 0); RestoreReg("run", "edi"); e("mov edi, [edi]\n"); e("and edi, byte 7\n"); /* same as modulo 8 */ e("btc edx, edi\n"); /* bit->CF, change */ e("setnc dh\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl dh, 6\n"); /* put C in Z position */ e("or ah, dh\n"); /* set the new Z */ WriteByte(); /* write back, EDI is cleared */ } } if (((op & 0x3f) & 7) == 0) /* Dn is long */ EmitTiming(t); else /* mem is byte */ EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE)); InstEnd(); return 1; } int ANDItoSR(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); e("test byte [sr+1], 0x20\n"); /* in supervisor? */ e("jz near exception_privilege_violation\n"); SaveCCR(); /* save flags to SR */ e("mov edx, [esi]\n"); e("and edx, 0xa71f\n"); /* mask out unwanted bits */ e("and [sr], dx\n"); e("add esi, byte 2\n"); LoadCCR(); /* get flags back */ e("test byte [sr+1], 0x20\n"); /* if we changed to User, swap SPs */ e("jnz short .in_s\n"); e("mov ebx, [a+7*4]\n"); e("mov edx, [__sp]\n"); e("mov [a+7*4], edx\n"); e("mov [__sp], ebx\n"); SetUserAddressSpace(); /* map in user address space */ EmitLabel(".in_s"); EmitTiming(base_timing); InstEnd(); return 1; } int UNLK(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 8); e("and edi, byte 7\n"); e("mov ebx, [a+edi*4]\n"); /* An->SP */ SaveReg("run", "ebx"); /* to avoid addrclip() */ SaveReg("run", "edi"); ReadLong(); /* (SP)->An */ RestoreReg("run", "edi"); RestoreReg("run", "ebx"); e("mov [a+edi*4], edx\n"); e("add ebx, byte 4\n"); e("mov [a+7*4], ebx\n"); /* SP+4->SP */ EmitTiming(base_timing); InstEnd(); return 1; } int LINK(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 8); e("and edi, byte 7\n"); e("mov ebx, [a+7*4]\n"); /* EBX=SP */ e("sub ebx, byte 4\n"); e("mov edx, [a+edi*4]\n"); SaveReg("run", "ebx"); SaveReg("run", "edi"); WriteLong(); /* An->(SP) */ RestoreReg("run", "edi"); RestoreReg("run", "ebx"); e("mov [a+edi*4], ebx\n"); /* SP->An */ e("movsx edx, word [esi]\n"); e("add ebx, edx\n"); /* dn + SP->SP */ e("mov [a+7*4], ebx\n"); e("add esi, byte 2\n"); EmitTiming(base_timing); InstEnd(); return 1; } int ROxMem(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned ea = op & 0x3f, dr = (op >> 8) & 1; char mnem[4]; char *mnem_t[] = { "ROR", "ROL" }; if (!CheckEA(ea, "001111111000")) return 0; if (!NumDecodedEA(ea)) return 0; strcpy(mnem, mnem_t[(op >> 8) & 1]); InstBegin(op, mnem, NumDecodedEA(ea)); LoadFromEA(ea, WORD_SIZE, 0); if (dr) /* left */ e("rol dx, byte 1\n"); else e("ror dx, byte 1\n"); e("setc al\n"); /* AL=CF */ e("test dx, dx\n"); /* CF is cleared */ e("lahf\n"); e("or ah, al\n"); /* put in CF */ e("xor al, al\n"); /* V is always cleared */ WriteWord(); /* write back to mem */ EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); InstEnd(); return 1; } int ROxReg(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned size, t, opr_i, count; char *opr[] = { "ror", "rol" }; char mnem[4]; char *mnem_t[] = { "ROR", "ROL" }; switch ((op >> 6) & 3) { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } opr_i = (op >> 8) & 1; strcpy(mnem, mnem_t[opr_i]); count = (op >> 9) & 7; if (!count) count = 8; InstBegin(op, mnem, 8); /* 8 regs */ e("and edi, byte 7\n"); if ((op >> 5) & 1) /* reg contains count */ { e("mov ebx, ecx\n"); /* save ECX */ e("mov ecx, [d+%d*4]\n", (op >> 9) & 7); e("and ecx, byte 0x3f\n"); /* modulo 64 */ e("mov edx, [d+edi*4]\n"); switch (size) { case BYTE_SIZE: e("%s dl, cl\n", opr[opr_i]); e("setc al\n"); e("test dl, dl\n"); break; case WORD_SIZE: e("%s dx, cl\n", opr[opr_i]); e("setc al\n"); e("test dx, dx\n"); break; case LONG_SIZE: e("%s edx, cl\n", opr[opr_i]); e("setc al\n"); e("test edx, edx\n"); break; } e("lahf\n"); e("test cl, cl\n"); /* if shift count of 0, X is unaffected */ e("jnz short .not_zero\n"); e("xor al, al\n"); /* clear CF, count was 0 */ EmitLabel(".not_zero"); e("or ah, al\n"); /* put CF in with rest of flags */ e("xor al, al\n"); /* V always cleared */ switch (size) /* write back results */ { case BYTE_SIZE: e("mov [d+edi*4], dl\n", opr[opr_i]); break; case WORD_SIZE: e("mov [d+edi*4], dx\n", opr[opr_i]); break; case LONG_SIZE: e("mov [d+edi*4], edx\n", opr[opr_i]); break; } /* timing */ switch (size) { case BYTE_SIZE: case WORD_SIZE: t = 6; break; case LONG_SIZE: t = 8; break; } e("shl cl, byte 1\n"); /* 2n */ e("add cl, byte %d\n", t); e("neg ecx\n"); e("add ecx, ebx\n"); e("js near Turbo68KRun_done\n"); } else /* immediate count */ { e("mov edx, [d+edi*4]\n"); switch (size) { case BYTE_SIZE: e("%s dl, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dl, dl\n"); break; case WORD_SIZE: e("%s dx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dx, dx\n"); break; case LONG_SIZE: e("%s edx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test edx, edx\n"); break; } e("lahf\n"); e("or ah, al\n"); /* set CF */ e("xor al, al\n"); /* V always cleared */ switch (size) /* write back results */ { case BYTE_SIZE: e("mov [d+edi*4], dl\n"); break; case WORD_SIZE: e("mov [d+edi*4], dx\n"); break; case LONG_SIZE: e("mov [d+edi*4], edx\n"); break; } switch (size) { case BYTE_SIZE: case WORD_SIZE: t = 6; break; case LONG_SIZE: t = 8; break; } EmitTiming(t + count * 2); } InstEnd(); return 1; } int EXT(unsigned short op, char *mnem, unsigned base_timing) { unsigned opmode = (op >> 6) & 7; if (opmode != 2 && opmode != 3) return 0; InstBegin(op, mnem, 8); e("and edi, byte 7\n"); e("mov edx, [d+edi*4]\n"); if (opmode == 2) /* byte -> word */ { e("movsx dx, dl\n"); e("mov [d+edi*4], dx\n"); e("test dx, dx\n"); } else if (opmode == 3) /* word -> long */ { e("movsx edx, dx\n"); e("mov [d+edi*4], edx\n"); e("test edx, edx\n"); } else if (opmode == 7) /* byte -> long */ { /* * Note: This code is never executed. EXTB is present on 68020 and * higher processors. Turbo68K was only intended to emulate the * 68000. This code was an accident, but it will be kept here in case * in the future it is needed. */ e("movsx edx, dl\n"); e("mov [d+edi*4], edx\n"); e("test edx, edx\n"); } e("lahf\n"); /* CF is already cleared by TEST */ e("xor al, al\n"); /* V always cleared */ EmitTiming(base_timing); InstEnd(); return 1; } int ROXxMem(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned ea = op & 0x3f, dr = (op >> 8) & 1; char mnem[5]; char *mnem_t[] = { "ROXR", "ROXL" }; if (!CheckEA(ea, "001111111000")) return 0; if (!NumDecodedEA(ea)) return 0; strcpy(mnem, mnem_t[(op >> 8) & 1]); InstBegin(op, mnem, NumDecodedEA(ea)); LoadFromEA(ea, WORD_SIZE, 0); e("shr byte [x], 1\n"); if (dr) /* left */ e("rcl dx, byte 1\n"); else e("rcr dx, byte 1\n"); e("setc al\n"); /* AL=CF */ e("setc byte [x]\n"); e("test dx, dx\n"); /* CF is cleared */ e("lahf\n"); e("or ah, al\n"); /* put in CF */ e("xor al, al\n"); WriteWord(); /* write back to mem */ EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); InstEnd(); return 1; } int ROXxReg(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned size, t, opr_i, count; char *opr[] = { "rcr", "rcl" }; char mnem[5]; char *mnem_t[] = { "ROXR", "ROXL" }; switch ((op >> 6) & 3) { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } opr_i = (op >> 8) & 1; strcpy(mnem, mnem_t[opr_i]); count = (op >> 9) & 7; if (!count) count = 8; InstBegin(op, mnem, 8); /* 8 regs */ e("and edi, byte 7\n"); if ((op >> 5) & 1) /* reg contains count */ { e("mov ebx, ecx\n"); /* save ECX */ e("mov ecx, [d+%d*4]\n", (op >> 9) & 7); e("and ecx, byte 0x3f\n"); /* modulo 64 */ e("mov dl, [x]\n"); e("shr dl, byte 1\n"); /* X->CF so we can use RCL/RCR */ e("mov edx, [d+edi*4]\n"); switch (size) { case BYTE_SIZE: e("%s dl, cl\n", opr[opr_i]); e("setc al\n"); e("test dl, dl\n"); break; case WORD_SIZE: e("%s dx, cl\n", opr[opr_i]); e("setc al\n"); e("test dx, dx\n"); break; case LONG_SIZE: e("%s edx, cl\n", opr[opr_i]); e("setc al\n"); e("test edx, edx\n"); break; } e("lahf\n"); e("test cl, cl\n"); /* if shift count of 0, X is unaffected */ e("jz short .zero_count\n"); e("or ah, al\n"); /* set CF */ e("test ah, 1\n"); /* CF */ e("setnz byte [x]\n"); e("jmp short .skip\n"); EmitLabel(".zero_count"); e("mov al, [x]\n"); /* X->C */ e("and al, byte 1\n"); e("and ah, 0xfe\n"); /* get rid of old C before putting new C in */ e("or ah, al\n"); EmitLabel(".skip"); e("xor al, al\n"); /* V always cleared */ switch (size) /* write back results */ { case BYTE_SIZE: e("mov [d+edi*4], dl\n", opr[opr_i]); break; case WORD_SIZE: e("mov [d+edi*4], dx\n", opr[opr_i]); break; case LONG_SIZE: e("mov [d+edi*4], edx\n", opr[opr_i]); break; } /* timing */ switch (size) { case BYTE_SIZE: case WORD_SIZE: t = 6; break; case LONG_SIZE: t = 8; break; } e("shl cl, byte 1\n"); /* 2n */ e("add cl, byte %d\n", t); e("neg ecx\n"); e("add ecx, ebx\n"); e("js near Turbo68KRun_done\n"); } else /* immediate count */ { e("mov dl, [x]\n"); e("shr dl, byte 1\n"); /* X->CF so we can use RCL/RCR */ e("mov edx, [d+edi*4]\n"); switch (size) { case BYTE_SIZE: e("%s dl, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dl, dl\n"); break; case WORD_SIZE: e("%s dx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dx, dx\n"); break; case LONG_SIZE: e("%s edx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test edx, edx\n"); break; } e("lahf\n"); e("or ah, al\n"); /* set CF */ e("test ah, 1\n"); /* CF */ e("setnz byte [x]\n"); e("xor al, al\n"); /* V always cleared */ switch (size) /* write back results */ { case BYTE_SIZE: e("mov [d+edi*4], dl\n", opr[opr_i]); break; case WORD_SIZE: e("mov [d+edi*4], dx\n", opr[opr_i]); break; case LONG_SIZE: e("mov [d+edi*4], edx\n", opr[opr_i]); break; } switch (size) { case BYTE_SIZE: case WORD_SIZE: t = 6; break; case LONG_SIZE: t = 8; break; } EmitTiming(t + count * 2); } InstEnd(); return 1; } int MOVEtoCCR(unsigned short op, char *mnem, unsigned base_timing) { unsigned ea = op & 0x3f; if (!CheckEA(ea, "101111111111")) return 0; if (!NumDecodedEA(ea)) return 0; InstBegin(op, mnem, NumDecodedEA(ea)); LoadFromEA(ea, WORD_SIZE, 0); e("mov [sr], dl\n"); LoadCCR(); /* update flags */ EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); /* technically, word sized */ InstEnd(); return 1; } int LSxMem(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned ea = op & 0x3f, dr = (op >> 8) & 1; char mnem[4]; char *mnem_t[] = { "LSR", "LSL" }; if (!CheckEA(ea, "001111111000")) return 0; if (!NumDecodedEA(ea)) return 0; strcpy(mnem, mnem_t[(op >> 8) & 1]); InstBegin(op, mnem, NumDecodedEA(ea)); LoadFromEA(ea, WORD_SIZE, 0); if (dr) /* left */ e("shl dx, byte 1\n"); else e("shr dx, byte 1\n"); e("lahf\n"); e("setc byte [x]\n"); e("xor al, al\n"); WriteWord(); /* write back to mem */ EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); InstEnd(); return 1; } int LSxReg(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned size, t, opr_i, count; char *opr[] = { "shr", "shl" }; char mnem[4]; char *mnem_t[] = { "LSR", "LSL" }; switch ((op >> 6) & 3) { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } opr_i = (op >> 8) & 1; strcpy(mnem, mnem_t[opr_i]); count = (op >> 9) & 7; if (!count) count = 8; InstBegin(op, mnem, 8); /* 8 regs */ e("and edi, byte 7\n"); if ((op >> 5) & 1) /* reg contains count */ { e("mov ebx, ecx\n"); /* save ECX */ e("mov ecx, [d+%d*4]\n", (op >> 9) & 7); e("and ecx, byte 0x3f\n"); /* modulo 64 */ e("mov edx, [d+edi*4]\n"); /* get register... */ switch (size) { case BYTE_SIZE: e("%s dl, cl\n", opr[opr_i]); e("setc al\n"); /* CF -> AL */ e("test dl, dl\n"); e("mov [d+edi*4], dl\n"); /* MOV does not affect flags */ break; case WORD_SIZE: e("%s dx, cl\n", opr[opr_i]); e("setc al\n"); e("test dx, dx\n"); e("mov [d+edi*4], dx\n"); break; case LONG_SIZE: e("%s edx, cl\n", opr[opr_i]); e("setc al\n"); e("test edx, edx\n"); e("mov [d+edi*4], edx\n"); break; } e("lahf\n"); e("test cl, cl\n"); /* if zero count... */ e("jz short .zero\n"); e("or ah, al\n"); /* new CF... the first test cleared old C */ e("test al, 1\n"); /* C->X */ e("setnz byte [x]\n"); EmitLabel(".zero"); e("xor al, al\n"); /* V cleared */ /* timing */ switch (size) { case BYTE_SIZE: case WORD_SIZE: t = 6; break; case LONG_SIZE: t = 8; break; } e("shl cl, byte 1\n"); /* 2n */ e("add cl, byte %d\n", t); e("neg ecx\n"); e("add ecx, ebx\n"); e("js near Turbo68KRun_done\n"); } else /* immediate count */ { e("%s ", opr[opr_i]); switch (size) { case BYTE_SIZE: e("byte "); break; case WORD_SIZE: e("word "); break; case LONG_SIZE: e("dword "); break; } e("[d+edi*4], %d\n", count); e("lahf\n"); e("setc byte [x]\n"); e("xor al, al\n"); switch (size) { case BYTE_SIZE: case WORD_SIZE: t = 6; break; case LONG_SIZE: t = 8; break; } EmitTiming(t + count * 2); } InstEnd(); return 1; } int NOP(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); EmitTiming(base_timing); InstEnd(); return 1; } int CLR(unsigned short op, char *mnem, unsigned base_timing) { unsigned ea = op & 0x3f, size, t; if (!CheckEA(ea, "101111111000")) return 0; if (!NumDecodedEA(ea)) return 0; switch ((op >> 6) & 3) { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } InstBegin(op, mnem, NumDecodedEA(ea)); e("mov eax, 0x4000\n"); /* Z always set, everything else cleared */ if (mpu == 68000 && dummyread && (ea >> 3)) { /* * Emulate the dummy read for memory addressing modes, if necessary. * We only do it for memory modes, because it really can't make any * difference if the destination is a register! */ LoadFromEA(ea, size, 0); /* do the dummy read */ e("xor edx, edx\n"); /* now, write a 0 */ switch (size) /* write */ { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } } else { /* * This method is quicker, since dummy reads aren't needed */ e("xor edx, edx\n"); StoreToEAUsingEDI(ea, size, 0); } switch (size) { case BYTE_SIZE: case WORD_SIZE: if ((ea >> 3) == 0) t = 4; /* reg */ else t = 8 + TimingEA(ea, size); /* mem */ break; case LONG_SIZE: if ((ea >> 3) == 0) t = 6; /* reg */ else t = 12 + TimingEA(ea, size); /* mem */ break; } EmitTiming(t); InstEnd(); return 1; } int ORItoSR(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); e("test byte [sr+1], 0x20\n"); /* in supervisor? */ e("jz near exception_privilege_violation\n"); SaveCCR(); /* save flags to SR */ e("mov edx, [esi]\n"); e("and edx, 0xa71f\n"); /* mask out unwanted bits */ e("or [sr], dx\n"); e("add esi, byte 2\n"); LoadCCR(); /* get flags back */ EmitTiming(base_timing); InstEnd(); return 1; } int MOVEfromSR(unsigned short op, char *mnem, unsigned base_timing) { unsigned ea = op & 0x3f; if (!CheckEA(ea, "101111111000")) return 0; InstBegin(op, mnem, 1); /* * This instruction is not privileged for 68000 and 68008 processors. */ if (mpu == 68010) { e("test byte [sr+1], 0x20\n"); /* in supervisor? */ e("jz near exception_privilege_violation\n"); } SaveCCR(); /* save flags to SR */ if (mpu == 68000 && dummyread && (ea >> 3)) { /* * When dummy reads are enabled, and the EA mode is not register, we * perform a dummy read */ LoadFromEA(ea, WORD_SIZE, 0); e("mov edx, [sr]\n"); WriteWord(); } else { e("mov edx, [sr]\n"); /* get SR */ StoreToEAUsingEDI(ea, WORD_SIZE, 0); } LoadCCR(); /* get back SR */ if ((ea >> 3) == 0) EmitTiming(6); else EmitTiming(8 + TimingEA(ea, WORD_SIZE)); InstEnd(); return 1; } int ArithmeticA(unsigned short op, char *mnem, unsigned base_timing) { int size = UNKNOWN_SIZE, t; if (!CheckEA(op & 0x3f, "111111111111")) /* check EA mode */ return 0; if (!NumDecodedEA(op & 0x3f)) /* check if decoded this EA */ return 0; switch ((op >> 6) & 7) /* set allowed sizes */ { case 3: size = WORD_SIZE; break; case 7: size = LONG_SIZE; break; default: return 0; } if (((op >> 12) & 0xf) != 0xd && ((op >> 12) & 0xf) != 9) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); LoadFromEA(op & 0x3f, size, 1); /* load */ if (((op >> 12) & 0xf) == 0xd) /* ADDA */ e("add [a+%d*4], edx\n", (op >> 9) & 7); /* store */ else /* SUBA */ e("sub [a+%d*4], edx\n", (op >> 9) & 7); switch (size) { case WORD_SIZE: t = 8; break; case LONG_SIZE: t = 6; if (((op & 0x3f) >> 3) == 0) t += 2; /* Dn */ else if (((op & 0x3f) >> 3) == 1) t += 2; /* An */ else if (((op & 0x3f) >> 3) == 7 && (op & 7) == 4) t += 2; /* # */ break; } EmitTiming(TimingEA(op & 0x3f, size) + t); InstEnd(); return 1; } int ASxMem(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned ea = op & 0x3f, dr = (op >> 8) & 1; char mnem[4]; char *mnem_t[] = { "ASR", "ASL" }; if (!CheckEA(ea, "001111111000")) return 0; if (!NumDecodedEA(ea)) return 0; strcpy(mnem, mnem_t[(op >> 8) & 1]); InstBegin(op, mnem, NumDecodedEA(ea)); LoadFromEA(ea, WORD_SIZE, 0); if (dr) /* left */ e("shl dx, byte 1\n"); else e("sar dx, byte 1\n"); e("lahf\n"); e("setc byte [x]\n"); if (dr) /* only for left shifts, right shifts don't change the MSB */ { e("sets al\n"); /* AL=current MSB */ e("adc al, byte 0\n"); /* CF contains old MSB */ e("and al, 1\n"); /* preserve only the flag bit */ } WriteWord(); /* write back to mem */ EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); InstEnd(); return 1; } int ASxReg(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned i, size, t = 0, opr_i, count; char mnem[4]; char *mnem_t[] = { "ASR", "ASL" }; switch ((op >> 6) & 3) { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } opr_i = (op >> 8) & 1; strcpy(mnem, mnem_t[opr_i]); count = (op >> 9) & 7; if (!count) count = 8; InstBegin(op, mnem, 8); /* 8 regs */ e("and edi, byte 7\n"); if ((op >> 5) & 1) /* reg contains count */ { if (opr_i) /* ASL */ { e("xor al, al\n"); SaveReg("run", "edi"); /* save EDI */ e("mov edx, [d+edi*4]\n"); /* get reg to shift */ e("mov edi, [d+%d*4]\n", (op >> 9) & 7); e("and edi, byte 0x3f\n"); /* modulo 64 */ e("jz short .zero_shift\n"); EmitLabel(".loop"); e("shl "); switch (size) { case BYTE_SIZE: e("dl, "); break; case WORD_SIZE: e("dx, "); break; case LONG_SIZE: e("edx, "); break; } e("1\n"); e("setc bl\n"); /* old MSB */ e("sets bh\n"); /* new MSB */ e("xor bh, bl\n"); /* detect change */ e("or al, bh\n"); /* reflect change in V */ e("sub ecx, byte 2\n"); /* each rotation=2 cycles */ e("dec edi\n"); e("jnz short .loop\n"); RestoreReg("run", "edi"); switch (size) /* store reg, get flags */ { case BYTE_SIZE: e("mov [d+edi*4], dl\n"); e("test dl, dl\n"); break; case WORD_SIZE: e("mov [d+edi*4], dx\n"); e("test dx, dx\n"); break; case LONG_SIZE: e("mov [d+edi*4], edx\n"); e("test edx, edx\n"); break; } e("lahf\n"); e("or ah, bl\n"); /* CF */ e("test bl, bl\n"); e("setnz byte [x]\n"); /* CF==X */ e("jmp short .end\n"); EmitLabel(".zero_shift"); switch (size) { case BYTE_SIZE: e("test dl, dl\n"); break; case WORD_SIZE: e("test dx, dx\n"); break; case LONG_SIZE: e("test edx, edx\n"); break; } e("lahf\n"); e("xor al, al\n"); EmitLabel(".end"); switch (size) { case BYTE_SIZE: case WORD_SIZE: t = 6; break; case LONG_SIZE: t = 8; break; } EmitTiming(t); } else /* ASR */ { e("mov ebx, ecx\n"); /* save ECX */ e("mov ecx, [d+%d*4]\n", (op >> 9) & 7); e("and ecx, byte 0x3f\n"); /* modulo 64 */ switch (size) { case BYTE_SIZE: e("mov dl, [d+edi*4]\n"); e("sar dl, cl\n"); e("setc bl\n"); e("test dl, dl\n"); break; case WORD_SIZE: e("mov edx, [d+edi*4]\n"); e("sar dx, cl\n"); e("setc bl\n"); e("test dx, dx\n"); break; case LONG_SIZE: e("mov edx, [d+edi*4]\n"); e("sar edx, cl\n"); e("setc bl\n"); /* get CF from SHL/SHR */ e("test edx, edx\n");/* this trashes the CF */ break; } e("lahf\n"); e("test cl, cl\n"); /* if shift count of 0, X is unaffected, C=0 */ e("jz short .no_x\n"); e("or ah, bl\n"); /* CF! */ e("test ah, 1\n"); /* CF */ e("setnz byte [x]\n"); EmitLabel(".no_x"); switch (size) /* write back and detect MSB change */ { case BYTE_SIZE: e("mov [d+edi*4], dl\n"); break; case WORD_SIZE: e("mov [d+edi*4], dx\n"); break; case LONG_SIZE: e("mov [d+edi*4], edx\n"); break; } e("xor al, al\n"); /* V=0 because MSB never changes w/ ASR */ /* timing */ switch (size) { case BYTE_SIZE: case WORD_SIZE: t = 6; break; case LONG_SIZE: t = 8; break; } e("shl cl, byte 1\n"); /* 2n */ e("add cl, byte %d\n", t); e("neg ecx\n"); e("add ecx, ebx\n"); e("js near Turbo68KRun_done\n"); } } else /* immediate count */ { if (!opr_i) /* ASR */ { e("sar "); switch (size) { case BYTE_SIZE: e("byte "); break; case WORD_SIZE: e("word "); break; case LONG_SIZE: e("dword "); break; } e("[d+edi*4], %d\n", count); e("lahf\n"); e("setc byte [x]\n"); e("xor al, al\n"); t = count * 2; /* 2 cycles per shift */ } else /* ASL */ { e("xor al, al\n"); /* clear V */ switch (size) { case BYTE_SIZE: e("mov dl, [d+edi*4]\n"); break; case WORD_SIZE: e("mov dx, [d+edi*4]\n"); break; case LONG_SIZE: e("mov edx, [d+edi*4]\n"); break; } for (i = 1; i <= count; i++) { e("shl "); switch (size) { case BYTE_SIZE: e("dl, "); break; case WORD_SIZE: e("dx, "); break; case LONG_SIZE: e("edx, "); break; } e("1\n"); if (i == count) { e("lahf\n"); e("setc byte [x]\n"); } e("setc bl\n"); /* old MSB */ e("sets bh\n"); /* new MSB */ e("xor bh, bl\n"); /* detect change */ e("or al, bh\n"); /* reflect change in V */ e("sub ecx, byte 2\n"); /* each rotation=2 cycles */ } switch (size) /* write results back to Dn */ { case BYTE_SIZE: e("mov [d+edi*4], dl\n"); break; case WORD_SIZE: e("mov [d+edi*4], dx\n"); break; case LONG_SIZE: e("mov [d+edi*4], edx\n"); break; } } switch (size) { case BYTE_SIZE: case WORD_SIZE: t += 6; break; case LONG_SIZE: t += 8; break; } EmitTiming(t); } InstEnd(); return 1; } int ArithmeticQ(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned ea = op & 0x3f, size, t, data, opr_i; char mnem[5]; char *mnem_t[] = { "ADDQ", "SUBQ" }; char *opr[] = { "add", "sub" }; if (!CheckEA(ea, "111111111000")) return 0; t = base_timing; switch ((op >> 6) & 3) { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; t += 4; break; default: return 0; } if ((ea >> 3) != 0 && (ea >> 3) != 1) t += 4; /* mem */ if (size == BYTE_SIZE && (ea >> 3) == 1) /* byte An not allowed */ return 0; if (!NumDecodedEA(ea)) return 0; data = (op >> 9) & 7; if (!data) data = 8; strcpy(mnem, mnem_t[(op >> 8) & 1]); opr_i = (op >> 8) & 1; InstBegin(op, mnem, NumDecodedEA(ea)); if ((ea >> 3) == 0) /* Dn */ { e("and edi, byte 7\n"); switch (size) { case BYTE_SIZE: e("%s byte [d+edi*4], %d\n", opr[opr_i], data); break; case WORD_SIZE: e("%s word [d+edi*4], %d\n", opr[opr_i], data); break; case LONG_SIZE: e("%s dword [d+edi*4], byte %d\n", opr[opr_i], data); break; } e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); } else if ((ea >> 3) == 1) /* An */ { /* * Manual says the entire address register is used despite the size * of the operation. */ e("and edi, byte 7\n"); e("%s dword [a+edi*4], byte %d\n", opr[opr_i], data); } else /* memory */ { LoadFromEA(ea, size, 0); switch (size) { case BYTE_SIZE: e("%s dl, byte %d\n", opr[opr_i], data); break; case WORD_SIZE: e("%s dx, %d\n", opr[opr_i], data); break; case LONG_SIZE: e("%s edx, byte %d\n", opr[opr_i], data); break; } e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); switch (size) { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } } EmitTiming(t + TimingEA(ea, size)); InstEnd(); return 1; } int NEGx(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned ea = op & 0x3f, size, t; char mnem[5]; char *mnem_t[] = { "NEGX", "NEG" }; if (!CheckEA(ea, "101111111000")) return 0; if (!NumDecodedEA(ea)) return 0; switch ((op >> 6) & 3) { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } strcpy(mnem, mnem_t[(op >> 10) & 1]); InstBegin(op, mnem, NumDecodedEA(ea)); if ((ea >> 3) == 0) /* Dn */ { e("and edi, byte 7\n"); if (!(op & 0x0400)) /* NEGX */ { switch (size) { case BYTE_SIZE: e("xor dl, dl\n"); break; case WORD_SIZE: case LONG_SIZE: e("xor edx, edx\n"); break; } e("shr byte [x], 1\n"); /* X->CF */ switch (size) { case BYTE_SIZE: e("sbb dl, [d+edi*4]\n"); e("mov bl, ah\n"); /* store old flags */ e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); e("jnz short .clr\n"); /* Z clear */ e("and bl, byte 0x40\n"); /* isolate old Z */ e("and ah, byte 0xbf\n"); /* kill new Z */ e("or ah, bl\n"); /* in w/ old Z */ EmitLabel(".clr"); e("mov [d+edi*4], dl\n"); t = 4; break; case WORD_SIZE: e("sbb dx, [d+edi*4]\n"); e("mov bl, ah\n"); e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); e("jnz short .clr\n"); e("and bl, byte 0x40\n"); e("and ah, byte 0xbf\n"); e("or ah, bl\n"); EmitLabel(".clr"); e("mov [d+edi*4], dx\n"); t = 4; break; case LONG_SIZE: e("sbb edx, [d+edi*4]\n"); e("mov bl, ah\n"); e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); e("jnz short .clr\n"); e("and bl, byte 0x40\n"); e("and ah, byte 0xbf\n"); e("or ah, bl\n"); EmitLabel(".clr"); e("mov [d+edi*4], edx\n"); t = 6; break; } } else /* NEG */ { switch (size) { case BYTE_SIZE: e("neg byte [d+edi*4]\n"); e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); t = 4; break; case WORD_SIZE: e("neg word [d+edi*4]\n"); e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); t = 4; break; case LONG_SIZE: e("neg dword [d+edi*4]\n"); e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); t = 6; break; } } } else /* memory */ { if (!(op & 0x0400)) /* NEGX */ { LoadFromEA(ea, size, 0); e("mov edi, ebx\n"); /* save address where data came from */ switch (size) { case BYTE_SIZE: e("xor bl, bl\n"); break; case WORD_SIZE: case LONG_SIZE: e("xor ebx, ebx\n"); break; } e("shr byte [x], 1\n"); /* X->CF */ switch (size) { case BYTE_SIZE: e("sbb bl, dl\n"); e("mov dl, ah\n"); e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); e("jnz short .clr\n"); e("and dl, byte 0x40\n"); e("and ah, byte 0xbf\n"); e("or ah, dl\n"); EmitLabel(".clr"); break; case WORD_SIZE: e("sbb bx, dx\n"); e("mov dl, ah\n"); e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); e("jnz short .clr\n"); e("and dl, byte 0x40\n"); e("and ah, byte 0xbf\n"); e("or ah, dl\n"); EmitLabel(".clr"); break; case LONG_SIZE: e("sbb ebx, edx\n"); e("mov dl, ah\n"); e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); e("jnz short .clr\n"); e("and dl, byte 0x40\n"); e("and ah, byte 0xbf\n"); e("or ah, dl\n"); EmitLabel(".clr"); break; } e("mov edx, ebx\n"); e("mov ebx, edi\n"); /* WriteXXX will clear EDI for us */ switch (size) { case BYTE_SIZE: WriteByte(); t = 8; break; case WORD_SIZE: WriteWord(); t = 8; break; case LONG_SIZE: WriteLong(); t = 12; break; } t += TimingEA(ea, size); } else /* NEG */ { LoadFromEA(ea, size, 0); switch (size) { case BYTE_SIZE: e("neg dl\n"); e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); WriteByte(); t = 8; break; case WORD_SIZE: e("neg dx\n"); e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); WriteWord(); t = 8; break; case LONG_SIZE: e("neg edx\n"); e("lahf\n"); e("seto al\n"); e("setc byte [x]\n"); WriteLong(); t = 12; break; } t += TimingEA(ea, size); } } EmitTiming(t); InstEnd(); return 1; } int RTS(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 1); e("mov ebx, [a+7*4]\n"); ReadLong(); e("add dword [a+7*4], byte 4\n"); e("mov esi, edx\n"); UpdateFetchPtr(); EmitTiming(base_timing); InstEnd(); return 1; } int MOVEtoSR(unsigned short op, char *mnem, unsigned base_timing) { unsigned ea = op & 0x3f; if (!CheckEA(ea, "101111111111")) return 0; if (!NumDecodedEA(ea)) return 0; InstBegin(op, mnem, NumDecodedEA(ea)); LoadFromEA(ea, WORD_SIZE, 0); e("test byte [sr+1], 0x20\n"); /* in supervisor? */ e("jz near exception_privilege_violation\n"); e("test dh, 0x20\n"); /* if still in supervisor... */ e("jnz .still_s\n"); /* ...no changes need to be made */ e("mov ebx, [__sp]\n"); /* swap SSP and USP */ e("xchg [a+7*4], ebx\n"); e("mov [__sp], ebx\n"); SetUserAddressSpace(); /* map in user address space */ EmitLabel(".still_s"); e("and edx, 0xa71f\n"); /* mask out unwanted bits */ e("mov [sr], edx\n"); /* load new SR */ LoadCCR(); /* reload flags we changed in CCR */ EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); InstEnd(); return 1; } int DBcc(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned cond = (op >> 8) & 0xf; char mnem[5]; char *mnem_t[] = { "DBT", "DBF", "DBHI", "DBLS", "DBCC", "DBCS", "DBNE", "DBEQ", "DBVC", "DBVS", "DBPL", "DBMI", "DBGE", "DBLT", "DBGT", "DBLE" }; strcpy(mnem, mnem_t[cond]); /* set mnemonic */ InstBegin(op, mnem, 8); switch (cond) { /* EAX:0000000000000000 NZ00 000C 0000 000V */ case 0: /* always true */ e("add esi, byte 2\n"); EmitTiming(10); InstEnd(); return 1; case 1: /* DBF/DBRA: no condition to test, just loop */ break; case 2: /* HI: !C & !Z */ e("test ah, byte 0x41\n"); e("jz short .do\n"); break; case 3: /* LS: C | Z */ e("test ah, byte 0x41\n"); e("jnz short .do\n"); break; case 4: /* CC: !C */ e("test ah, byte 1\n"); e("jz short .do\n"); break; case 5: /* CS: C */ e("test ah, byte 1\n"); e("jnz short .do\n"); break; case 6: /* NE: !Z */ e("test ah, byte 0x40\n"); e("jz short .do\n"); break; case 7: /* EQ: Z */ e("test ah, byte 0x40\n"); e("jnz short .do\n"); break; case 8: /* VC: !V */ e("test al, byte 1\n"); e("jz short .do\n"); break; case 9: /* VS: V */ e("test al, byte 1\n"); e("jnz short .do\n"); break; case 0xa: /* PL: !N */ e("test ah, byte 0x80\n"); e("jz short .do\n"); break; case 0xb: /* MI: N */ e("test ah, byte 0x80\n"); e("jnz short .do\n"); break; case 0xc: /* GE: N & V | !N & !V (N&V || !N&!V) */ e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpe short .do\n"); break; case 0xd: /* LT: N & !V | !N & V */ e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpo short .do\n"); EmitLabel(".dont_do"); break; case 0xe: /* GT: N & V & !Z | !N & !V & !Z */ /* if Z, then it is GE and we don't take this branch */ e("test ah, byte 0x40\n"); e("jnz short .dont_do\n"); e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpe short .do\n"); EmitLabel(".dont_do"); break; case 0xf: /* LE: Z | N & !V | !N & V */ e("test ah, 0x40\n"); e("jnz short .do\n"); e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpo short .do\n"); break; } e("and edi, byte 7\n");/* condition not met, decrement and branch */ if (cond == 1 && skip) /* idle loop skipping w/ DBRA */ e("mov edx, esi\n"); e("sub word [d+edi*4], 1\n"); e("jc short .do_loop_expired\n"); /* loop expired... */ e("movsx ebx, word [esi]\n"); e("add esi, ebx\n"); if (brafetch) { if (skip) /* skip uses EDX, we must save it */ SaveReg("run", "edx"); e("sub esi, ebp\n"); /* ESI=denormalized PC */ UpdateFetchPtr(); if (skip) RestoreReg("run", "edx"); } if (cond == 1 && skip) { e("sub edx, byte 2\n"); /* to get address of instruction */ e("cmp edx, esi\n"); e("je short .skip\n"); /* the same: this is an idle loop */ } EmitTiming(10); InstEnd(); Align(4); EmitLabel(".do"); /* condition was met, go on to next instruction */ e("add esi, byte 2\n"); EmitTiming(12); InstEnd(); Align(4); EmitLabel(".do_loop_expired"); /* condition false, but loop expired */ e("add esi, byte 2\n"); EmitTiming(14); InstEnd(); if (cond == 1 && skip) { /* * This code makes it look as if the loop was executed and expired. */ EmitLabel(".skip"); e("add edx, byte 4\n"); e("mov esi, edx\n"); /* EDX contained PC for DBRA */ e("mov edx, [d+edi*4]\n"); /* get counter loop */ e("and edx, 0xffff\n"); e("inc edx\n"); /* we subtracted 1 a little ways above */ e("mov word [d+edi*4], 0xffff\n"); e("mov ebx, edx\n"); e("shl edx, byte 3\n"); /* (i << 3) + (i << 2) = i*10 */ e("shl ebx, byte 2\n"); e("sub ecx, edx\n"); e("sub ecx, ebx\n"); e("js near Turbo68KRun_done\n"); InstEnd(); } return 1; } int MOVEM(unsigned short op, char *mnem, unsigned base_timing) { /* * If a non-post/pre-inc/decrement mode is used, the registers are * transferred to the address, and that address is inc/decremented but the * change is NOT reflected in the address register. * * If the mode is pre-dec or post-inc, it IS reflected. First, load up the * value of the register, and then write it back and the end, but don't * modify it while performing the operation, use a temp. X86 register * instead. * * NOTE: The reg->mem mode should be fine, I've tested it -- but there * might be some bugs. * * NOTE (April 8, 2001): Pete Dabbs just emailed me to report a problem * with MOVEM mem->reg. Basically, code like this wasn't working properly: * * MOVEM.L (A7)+,A7 * * What should happen there is the data from (A7) is read. Then, 4 is added * to A7, and finally, the data is written to A7. What Turbo68K was doing * wrong was it was writing the data from (A7) to A7 and THEN incrementing * A7. Not sure if the fix is correct, but I hope it is... I didn't change * anything in MOVEM reg->mem... */ int dr = (op >> 10) & 1, size = UNKNOWN_SIZE; unsigned ea = op & 0x3f; switch ((op >> 6) & 1) { case 0: size = WORD_SIZE; break; case 1: size = LONG_SIZE; break; default: return 0; } if (!NumDecodedEA(ea)) return 0; if (!dr) /* reg->mem */ { if (!CheckEA(ea, "001011111000")) return 0; } else /* mem->reg */ if (!CheckEA(ea, "001101111011")) return 0; InstBegin(op, mnem, NumDecodedEA(ea)); if (!dr) /* reg->mem */ { SaveReg("run", "eax"); /* save flags */ e("mov eax, [esi]\n"); /* AX=register list mask */ e("add esi, byte 2\n"); LoadControlEA(ea); /* EBX=store address */ SaveReg("run", "esi"); if ((ea >> 3) == 4) /* write back if (An)+,-(An) */ SaveReg("run", "edi"); /* EDI may contain reg # */ if ((ea >> 3) == 4) /* -(An) has reg mask reversed */ e("mov esi, d+15*4\n"); /* start from top and decrement */ else e("mov esi, d\n"); EmitLabel(".loop"); if (size == WORD_SIZE) e("sub ecx, byte 4\n"); else e("sub ecx, byte 8\n"); e("shr eax, byte 1\n"); /* mask bit->CF */ e("jnc short .skip\n"); /* not 1? skip this reg */ if ((ea >> 3) == 4) /* -(An) */ { if (size == WORD_SIZE) e("sub ebx, byte 2\n"); /* predec */ else e("sub ebx, byte 4\n"); } e("mov edx, [esi]\n"); /* get reg to store... */ SaveReg("run", "ebx"); if (size == WORD_SIZE) WriteWord(); else WriteLong(); /* get mem */ RestoreReg("run", "ebx"); if (!((ea >> 3) == 4)) /* not predec, increment */ { if (size == WORD_SIZE) e("add ebx, byte 2\n"); else e("add ebx, byte 4\n"); } EmitLabel(".skip"); if ((ea >> 3) == 4) /* -(An) */ { e("sub esi, byte 4\n"); /* next reg down */ e("cmp esi, d-4\n"); /* done? */ e("jne short .loop\n"); /* not done, keep looping */ } else { e("add esi, byte 4\n"); /* next reg up */ e("cmp esi, d+16*4\n"); /* done? */ e("jne short .loop\n"); /* not done, keep looping */ } if ((ea >> 3) == 4) /* write back if -(An) */ { RestoreReg("run", "edi"); e("mov [a+edi*4], ebx\n"); } RestoreReg("run", "esi"); RestoreReg("run", "eax"); switch (ea >> 3) /* timing for address mode */ { case 2: base_timing = 8; break; /* (An) */ case 4: base_timing = 8; break; /* -(An) */ case 5: base_timing = 12; break; /* (d16,An) */ case 6: base_timing = 14; break; /* (d8,An,Xn) */ case 7: switch (ea & 7) { case 0: base_timing = 12; break; /* (xxx).W */ case 1: base_timing = 16; break; /* (xxx).L */ } break; } } else /* mem->reg */ { SaveReg("run", "eax"); /* save flags */ e("mov eax, [esi]\n"); /* AX=register list mask */ e("add esi, byte 2\n"); LoadControlEA(ea); /* EBX=load address */ SaveReg("run", "esi"); if ((ea >> 3) == 3) /* write back if (An)+,-(An) */ SaveReg("run", "edi"); /* EDI may contain reg # */ e("mov esi, d\n"); /* start from bottom and increment */ EmitLabel(".loop"); if (size == WORD_SIZE) e("sub ecx, byte 4\n"); else e("sub ecx, byte 8\n"); e("shr eax, byte 1\n"); /* mask bit->CF */ e("jnc short .skip\n"); /* not 1? skip this reg */ /* if word, sign extend to long */ if (size == WORD_SIZE) { if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3)) pcfetch ? ReadWordSXPC() : ReadWordSX(); /* PC-relative is special */ else ReadWordSX(); } else { if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3)) pcfetch ? ReadLongPC() : ReadLong(); else ReadLong(); } if (size == WORD_SIZE) e("add ebx, byte 2\n"); else e("add ebx, byte 4\n"); if ((ea >> 3) == 3) { RestoreReg("run", "edi"); e("mov [a+edi*4], ebx\n"); SaveReg("run", "edi"); } e("mov [esi], edx\n"); /* store to reg */ EmitLabel(".skip"); e("add esi, byte 4\n"); /* next reg up */ e("cmp esi, d+16*4\n"); /* done? */ e("jne short .loop\n"); /* not done, keep looping */ if ((ea >> 3) == 3) /* just in case SaveReg()/RestoreReg() use stack */ RestoreReg("run", "edi"); RestoreReg("run", "esi"); RestoreReg("run", "eax"); switch (ea >> 3) /* timing for address mode */ { case 2: base_timing = 12; break; /* (An) */ case 3: base_timing = 12; break; /* (An)+ */ case 5: base_timing = 16; break; /* (d16,An) */ case 6: base_timing = 18; break; /* (d8,An,Xn) */ case 7: switch (ea & 7) { case 0: base_timing = 16; break; /* (xxx).W */ case 1: base_timing = 20; break; /* (xxx).L */ case 2: base_timing = 16; break; /* (d16,PC) */ case 3: base_timing = 18; break; /* (d16,PC,Xn */ } break; } } EmitTiming(base_timing); InstEnd(); return 1; } /* In case I screwed up MOVEM(), I have a back-up right here ;) */ int OldMOVEM(unsigned short op, char *mnem, unsigned base_timing) { /* * If a non-post/pre-inc/decrement mode is used, the registers are * transferred to the address, and that address is inc/decremented but the * change is NOT reflected in the address register. * * If the mode is pre-dec or post-inc, it IS reflected. First, load up the * value of the register, and then write it back and the end, but don't * modify it while performing the operation, use a temp. X86 register * instead. * * NOTE: The reg->mem mode should be fine, I've tested it -- but there * might be some bugs. */ int dr = (op >> 10) & 1, size = UNKNOWN_SIZE; unsigned ea = op & 0x3f; switch ((op >> 6) & 1) { case 0: size = WORD_SIZE; break; case 1: size = LONG_SIZE; break; default: return 0; } if (!NumDecodedEA(ea)) return 0; if (!dr) /* reg->mem */ { if (!CheckEA(ea, "001011111000")) return 0; } else /* mem->reg */ if (!CheckEA(ea, "001101111011")) return 0; InstBegin(op, mnem, NumDecodedEA(ea)); if (!dr) /* reg->mem */ { SaveReg("run", "eax"); /* save flags */ e("mov eax, [esi]\n"); /* AX=register list mask */ e("add esi, byte 2\n"); LoadControlEA(ea); /* EBX=store address */ SaveReg("run", "esi"); if ((ea >> 3) == 4) /* write back if (An)+,-(An) */ SaveReg("run", "edi"); /* EDI may contain reg # */ if ((ea >> 3) == 4) /* -(An) has reg mask reversed */ e("mov esi, d+15*4\n"); /* start from top and decrement */ else e("mov esi, d\n"); EmitLabel(".loop"); if (size == WORD_SIZE) e("sub ecx, byte 4\n"); else e("sub ecx, byte 8\n"); e("shr eax, byte 1\n"); /* mask bit->CF */ e("jnc short .skip\n"); /* not 1? skip this reg */ if ((ea >> 3) == 4) /* -(An) */ { if (size == WORD_SIZE) e("sub ebx, byte 2\n"); /* predec */ else e("sub ebx, byte 4\n"); } e("mov edx, [esi]\n"); /* get reg to store... */ SaveReg("run", "ebx"); if (size == WORD_SIZE) WriteWord(); else WriteLong(); /* get mem */ RestoreReg("run", "ebx"); if (!((ea >> 3) == 4)) /* not predec, increment */ { if (size == WORD_SIZE) e("add ebx, byte 2\n"); else e("add ebx, byte 4\n"); } EmitLabel(".skip"); if ((ea >> 3) == 4) /* -(An) */ { e("sub esi, byte 4\n"); /* next reg down */ e("cmp esi, d-4\n"); /* done? */ e("jne short .loop\n"); /* not done, keep looping */ } else { e("add esi, byte 4\n"); /* next reg up */ e("cmp esi, d+16*4\n"); /* done? */ e("jne short .loop\n"); /* not done, keep looping */ } if ((ea >> 3) == 4) /* write back if -(An) */ { RestoreReg("run", "edi"); e("mov [a+edi*4], ebx\n"); } RestoreReg("run", "esi"); RestoreReg("run", "eax"); switch (ea >> 3) /* timing for address mode */ { case 2: base_timing = 8; break; /* (An) */ case 4: base_timing = 8; break; /* -(An) */ case 5: base_timing = 12; break; /* (d16,An) */ case 6: base_timing = 14; break; /* (d8,An,Xn) */ case 7: switch (ea & 7) { case 0: base_timing = 12; break; /* (xxx).W */ case 1: base_timing = 16; break; /* (xxx).L */ } break; } } else /* mem->reg */ { SaveReg("run", "eax"); /* save flags */ e("mov eax, [esi]\n"); /* AX=register list mask */ e("add esi, byte 2\n"); LoadControlEA(ea); /* EBX=load address */ SaveReg("run", "esi"); if ((ea >> 3) == 3) /* write back if (An)+,-(An) */ SaveReg("run", "edi"); /* EDI may contain reg # */ e("mov esi, d\n"); /* start from bottom and increment */ EmitLabel(".loop"); if (size == WORD_SIZE) e("sub ecx, byte 4\n"); else e("sub ecx, byte 8\n"); e("shr eax, byte 1\n"); /* mask bit->CF */ e("jnc short .skip\n"); /* not 1? skip this reg */ /* if word, sign extend to long */ if (size == WORD_SIZE) { if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3)) pcfetch ? ReadWordSXPC() : ReadWordSX(); /* PC-relative is special */ else ReadWordSX(); } else { if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3)) pcfetch ? ReadLongPC() : ReadLong(); else ReadLong(); } e("mov [esi], edx\n"); /* store to reg */ if (size == WORD_SIZE) e("add ebx, byte 2\n"); else e("add ebx, byte 4\n"); EmitLabel(".skip"); e("add esi, byte 4\n"); /* next reg up */ e("cmp esi, d+16*4\n"); /* done? */ e("jne short .loop\n"); /* not done, keep looping */ if ((ea >> 3) == 3) /* write back if (An)+ */ { RestoreReg("run", "edi"); e("mov [a+edi*4], ebx\n"); } RestoreReg("run", "esi"); RestoreReg("run", "eax"); switch (ea >> 3) /* timing for address mode */ { case 2: base_timing = 12; break; /* (An) */ case 3: base_timing = 12; break; /* (An)+ */ case 5: base_timing = 16; break; /* (d16,An) */ case 6: base_timing = 18; break; /* (d8,An,Xn) */ case 7: switch (ea & 7) { case 0: base_timing = 16; break; /* (xxx).W */ case 1: base_timing = 20; break; /* (xxx).L */ case 2: base_timing = 16; break; /* (d16,PC) */ case 3: base_timing = 18; break; /* (d16,PC,Xn */ } break; } } EmitTiming(base_timing); InstEnd(); return 1; } int MOVEUSP(unsigned short op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 8); /* handles all 8 possible An regs */ e("and edi, byte 7\n"); e("test byte [sr+1], 0x20\n"); e("jz near exception_privilege_violation\n"); if (op & 8) /* USP->An */ { e("mov edx, [__sp]\n"); e("mov [a+edi*4], edx\n"); } else /* An->USP */ { e("mov edx, [a+edi*4]\n"); e("mov [__sp], edx\n"); } EmitTiming(base_timing); InstEnd(); return 1; } int Jxx(unsigned short op, char *mnem, unsigned base_timing) { unsigned t; if (!CheckEA(op & 0x3f, "001001111011")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); LoadControlEA(op & 0x3f); if ((op & 0xffc0) == 0x4e80) /* JSR */ { e("mov edx, esi\n"); /* EDX=PC to save to stack */ e("sub edx, ebp\n"); SaveReg("run", "edx"); } e("mov esi, ebx\n"); UpdateFetchPtr(); if ((op & 0xffc0) == 0x4e80) /* JSR stuff again... */ { RestoreReg("run", "edx"); /* remember? EDX contains PC to save */ e("sub dword [a+7*4], byte 4\n"); /* SP - 4 -> SP */ e("mov ebx, [a+7*4]\n"); WriteLong(); /* PC -> (SP) */ } switch ((op >> 3) & 7) /* timing based on control addressing mode */ { case 2: t = 8; break; /* (An) */ case 5: t = 10; break; /* (d16,An) */ case 6: t = 14; break; /* (d8,An,Xn) */ case 7: switch (op & 7) { case 0: t = 10; break; /* (xxx).W */ case 1: t = 12; break; /* (xxx).L */ case 2: t = 10; break; /* (d16,pc) */ case 3: t = 14; break; /* (d8,pc,Xn) */ } break; } if ((op & 0xffc0) == 0x4e80) t += 8; /* JSR */ EmitTiming(t); InstEnd(); return 1; } int LEA(unsigned short op, char *mnem, unsigned base_timing) { unsigned t; if (!CheckEA(op & 0x3f, "001001111011")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); LoadControlEA(op & 0x3f); e("mov [a+%d*4], ebx\n", (op >> 9) & 7); switch ((op >> 3) & 7) /* timing based on control addressing mode */ { case 2: t = 4; break; /* (An) */ case 5: t = 8; break; /* (d16,An) */ case 6: t = 12; break; /* (d8,An,Xn) */ case 7: switch (op & 7) { case 0: t = 8; break; /* (xxx).W */ case 1: t = 12; break; /* (xxx).L */ case 2: t = 8; break; /* (d16,pc) */ case 3: t = 12; break; /* (d8,pc,Xn) */ } break; } EmitTiming(t); InstEnd(); return 1; } int BTST(unsigned short op, char *mnem, unsigned base_timing) { unsigned t; if (((op >> 6) & 7) != 4 && ((op >> 6) & 7) != 0) return 0; if (op & 0x100) /* dynamic */ { if (!CheckEA(op & 0x3f, "101111111111")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); if (((op >> 3) & 7) == 0) /* Dn is long */ { t = 6; e("and edi, byte 7\n"); e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ e("and ebx, byte 31\n"); /* same as modulo 32 */ e("bt dword [d+edi*4], ebx\n"); /* bit->CF */ } else /* mem is byte */ { t = 4; LoadFromEA(op & 0x3f, BYTE_SIZE, 0); e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */ e("and ebx, byte 7\n"); /* same as modulo 8 */ e("bt edx, ebx\n"); /* bit->CF */ } } else if (!(op & 0x100)) /* static */ { if (!CheckEA(op & 0x3f, "101111111011")) return 0; if (!NumDecodedEA(op & 0x3f)) return 0; InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); if (((op >> 3) & 7) == 0) /* Dn is long */ { t = 10; e("and edi, byte 7\n"); /* isolate Dn reg # */ e("mov ebx, [esi]\n"); /* get bit # */ e("and ebx, byte 31\n"); /* same as modulo 32 */ e("add esi, byte 2\n"); e("bt dword [d+edi*4], ebx\n"); /* bit->CF */ } else /* mem is byte */ { t = 8; e("mov ebx, [esi]\n"); /* get bit # */ e("and ebx, byte 7\n"); /* same as modulo 8 */ e("add esi, byte 2\n"); SaveReg("run", "ebx"); LoadFromEA(op & 0x3f, BYTE_SIZE, 0); RestoreReg("run", "ebx"); e("bt edx, ebx\n"); /* bit->CF */ } } e("setnc bl\n"); /* zero status of C->DH */ e("and ah, 0xbf\n"); /* get rid of old Z */ e("shl bl, 6\n"); /* put C in Z position */ e("or ah, bl\n"); /* set the new Z */ if (((op & 0x3f) & 7) == 0) /* Dn is long */ EmitTiming(t); else /* mem is byte */ EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE)); InstEnd(); return 1; } int TST(unsigned short op, char *mnem, unsigned base_timing) { int size; switch ((op >> 6) & 3) /* set valid sizes */ { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } if (!NumDecodedEA(op & 0x3f)) /* check if decoded this EA */ return 0; if (!CheckEA(op & 0x3f, "101111111000")) return 0; /* no An,#dat,(d16,pc),(d8,pc,Xn) for 68000 */ InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); if (((op & 0x3f) >> 3) == 0) /* Dn shortcut */ { e("and edi, byte 7\n"); e("mov edx, [d+edi*4]\n"); if (size == BYTE_SIZE) e("test dl, dl\n"); else if (size == WORD_SIZE) e("test dx, dx\n"); else if (size == LONG_SIZE) e("test edx, edx\n"); } else { LoadFromEA(op & 0x3f, size, 0); if (size == BYTE_SIZE) e("test dl, dl\n"); else if (size == WORD_SIZE) e("test dx, dx\n"); else if (size == LONG_SIZE) e("test edx, edx\n"); } e("lahf\n"); e("xor al, al\n"); EmitTiming(base_timing + TimingEA(op & 0x3f, size)); InstEnd(); return 1; } int Bxx(unsigned short op, char *mnem, unsigned base_timing) { if ((op & 0xff) != 0 && (op & 0xff) != 1) return 0; switch (op & 0xff) { case 0: InstBegin(op, mnem, 1); break; case 1: InstBegin(op, mnem, 255); break; break; } switch (op & 0xff) { case 0: /* 16-bit displacement */ if ((op & 0xff00) == 0x6100) { e("mov edx, 2\n"); e("add edx, esi\n"); } e("movsx ebx, word [esi]\n"); e("add esi, ebx\n"); break; case 1: /* 8-bit displacement -- XX01-XXFF */ if ((op & 0xff00) == 0x6100) e("mov edx, esi\n"); e("and edi, 0xff\n"); e("mov ebx, edi\n"); e("movsx ebx, bl\n"); e("add esi, ebx\n"); break; } if (brafetch) { e("sub esi, ebp\n"); /* ESI=denormalized PC */ if ((op & 0xff00) == 0x6100) /* if BSR, save EDX */ SaveReg("run", "edx"); UpdateFetchPtr(); if ((op & 0xff00) == 0x6100) RestoreReg("run", "edx"); } if ((op & 0xff00) == 0x6100) /* BSR */ { e("sub dword [a+7*4], byte 4\n"); /* SP - 4 -> SP */ e("sub edx, ebp\n"); /* EDX=PC of next instruction */ e("mov ebx, [a+7*4]\n"); WriteLong(); /* PC -> (SP) */ } EmitTiming(base_timing); InstEnd(); return 1; } int Bcc(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned cond = (op >> 8) & 0xf; char mnem[4]; char *mnem_t[] = { NULL, NULL, "BHI", "BLS", "BCC", "BCS", "BNE", "BEQ", "BVC", "BVS", "BPL", "BMI", "BGE", "BLT", "BGT", "BLE" }; if (!cond || cond == 1) return 0; /* these not available for Bcc */ strcpy(mnem, mnem_t[cond]); /* set mnemonic */ switch (op & 0xff) { case 0: /* 16-bit displacement */ InstBegin(op, mnem, 1); break; case 1: /* 8-bit displacement -- XX01-XXFE */ InstBegin(op, mnem, 255); break; default: return 0; } switch (cond) { /* EAX:0000000000000000 NZ00 000C 0000 000V */ case 2: /* HI: !C & !Z */ e("test ah, byte 0x41\n"); e("jz short .do\n"); break; case 3: /* LS: C | Z */ e("test ah, byte 0x41\n"); e("jnz short .do\n"); break; case 4: /* CC: !C */ e("test ah, byte 1\n"); e("jz short .do\n"); break; case 5: /* CS: C */ e("test ah, byte 1\n"); e("jnz short .do\n"); break; case 6: /* NE: !Z */ e("test ah, byte 0x40\n"); e("jz short .do\n"); break; case 7: /* EQ: Z */ e("test ah, byte 0x40\n"); e("jnz short .do\n"); break; case 8: /* VC: !V */ e("test al, byte 1\n"); e("jz short .do\n"); break; case 9: /* VS: V */ e("test al, byte 1\n"); e("jnz short .do\n"); break; case 0xa: /* PL: !N */ e("test ah, byte 0x80\n"); e("jz short .do\n"); break; case 0xb: /* MI: N */ e("test ah, byte 0x80\n"); e("jnz short .do\n"); break; case 0xc: /* GE: N & V | !N & !V (N&V || !N&!V) */ e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpe short .do\n"); break; case 0xd: /* LT: N & !V | !N & V */ e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpo short .do\n"); EmitLabel(".dont_do"); break; case 0xe: /* GT: N & V & !Z | !N & !V & !Z */ /* if Z, then it is GE and we don't take this branch */ e("test ah, byte 0x40\n"); e("jnz short .dont_do\n"); e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpe short .do\n"); EmitLabel(".dont_do"); break; case 0xf: /* LE: Z | N & !V | !N & V */ e("test ah, 0x40\n"); e("jnz short .do\n"); e("mov bl, al\n"); e("mov bh, ah\n"); e("and bh, 0xfe\n"); e("or bl, bh\n"); e("test bl, byte 0x81\n"); e("jpo short .do\n"); break; } /* branch not taken */ switch (op & 0xff) /* look at this to figure out timing and increment */ { case 0: /* 16-bit */ e("add esi, byte 2\n"); EmitTiming(12); break; case 1: /* 8-bit */ EmitTiming(8); break; } InstEnd(); Align(4); EmitLabel(".do"); switch (op & 0xff) { case 0: /* 16-bit */ e("movsx ebx, word [esi]\n"); e("add esi, ebx\n"); break; case 1: /* 8-bit */ e("and edi, 0xff\n"); e("mov ebx, edi\n"); e("movsx ebx, bl\n"); e("add esi, ebx\n"); break; } if (brafetch) { e("sub esi, ebp\n"); /* ESI=denormalized PC */ UpdateFetchPtr(); } EmitTiming(10); InstEnd(); return 1; } int Arithmetic(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned t, ea = op & 0x3f, opr_code = (op >> 12), opmode = (op >> 6) & 7; int size = UNKNOWN_SIZE; char mnem[4]; char opr[4], *size_s[] = { "byte", "word", "dword" }, *edx[] = { "dl", "dx", "edx" }; if (opmode & 4) /* EA == destination */ { switch (opr_code) { case 8: /* OR */ case 9: /* SUB */ case 0xc: /* AND */ case 0xd: if (!CheckEA(ea, "001111111000")) return 0; /* ADD */ break; case 0xb: if (!CheckEA(ea, "101111111000")) return 0; /* EOR */ break; default: return 0; } } else /* EA == source */ { switch (opr_code) { case 0xd: /* ADD */ case 0xb: /* CMP */ case 9: if (!CheckEA(ea, "111111111111")) return 0; /* SUB */ if ((ea >> 3) == 1 && (opmode & 3) == 0) return 0; /* byte access to An not allowed! */ break; case 8: /* OR */ case 0xc: if (!CheckEA(ea, "101111111111")) return 0; /* AND */ break; default: return 0; } } if (!NumDecodedEA(ea)) /* check if decoded this EA */ return 0; switch (opmode & 3) /* set allowed sizes */ { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } switch (opr_code) /* timing */ { case 0xd: /* ADD */ case 9: /* SUB */ if (size == BYTE_SIZE || size == WORD_SIZE) { if ((ea >> 3) == 1) t = 8; else if ((ea >> 3) == 0) t = 4; else t = 8; } else { if ((ea >> 3) == 1) t = 6; else if ((ea >> 3) == 0) t = 6; else t = 12; if ((ea >> 3) == 0 || (ea >> 3) == 1 || ea == 0x3c) t += 2; /* if reg direct or immediate, timing of 6 is increased to 8 */ } break; case 0xb: if (!(opmode & 4)) /* CMP */ { if (size == BYTE_SIZE || size == WORD_SIZE) { if ((ea >> 3) == 1) t = 6; else if ((ea >> 3) == 0) t = 4; } else { if ((ea >> 3) == 1) t = 6; else if ((ea >> 3) == 0) t = 6; } } else /* EOR */ { if (size == BYTE_SIZE || size == WORD_SIZE) { if ((ea >> 3) == 0) t = 4; else t = 8; } else { if ((ea >> 3) == 0) t = 8; else t = 12; } break; } case 0xc: /* AND */ if (size == BYTE_SIZE || size == WORD_SIZE) { if ((ea >> 3) == 0) t = 4; else t = 8; } else { if ((ea >> 3) == 0) t = 6; else t = 12; if ((ea >> 3) == 0 || (ea >> 3) == 1 || ea == 0x3c) t += 2; /* if reg direct or immediate, timing of 6 is increased to 8 */ } break; case 0x8: /* OR */ if (size == BYTE_SIZE || size == WORD_SIZE) { if ((ea >> 3) == 0) t = 4; else t = 8; } else { if ((ea >> 3) == 0) t = 6; else t = 12; if ((ea >> 3) == 0 || (ea >> 3) == 1 || ea == 0x3c) t += 2; /* if reg direct or immediate, timing of 6 is increased to 8 */ } break; } /* set mnem and opr */ switch (opr_code) { case 0xd: strcpy(mnem, "ADD"); strcpy(opr, "add"); break; case 0xc: strcpy(mnem, "AND"); strcpy(opr, "and"); break; case 0xb: if (opmode & 4) { strcpy(mnem, "EOR"); strcpy(opr, "xor"); } else { strcpy(mnem, "CMP"); strcpy(opr, "cmp"); } break; case 8: strcpy(mnem, "OR"); strcpy(opr, "or"); break; case 9: strcpy(mnem, "SUB"); strcpy(opr, "sub"); break; default: return 0; } InstBegin(op, mnem, NumDecodedEA(ea)); if (opmode & 4) /* op Dn,ea */ { if ((ea >> 3) == 0 || (ea >> 3) == 1) /* Dn, An shortcut */ { char r[2] = { 'd', 'a' }; int i = ea >> 3; e("and edi, byte 7\n"); e("mov edx, [d+(%d*4)]\n", (op >> 9) & 7); e("%s %s [%c+(edi*4)], %s\n", opr, size_s[opmode & 3], r[i], edx[opmode & 3]); e("lahf\n"); switch ((op >> 12) & 0xf) /* flags calculations */ { case 0xc: e("xor al, al\n"); break; /* AND */ case 0xd: e("setc byte [x]\n"); e("seto al\n"); break; /* ADD */ case 0xb: if (opmode & 4) e("xor al, al\n"); /* EOR */ else e("seto al\n"); /* CMP */ break; case 8: e("xor al, al\n"); break; /* OR */ case 9: e("setc byte [x]\n"); e("seto al\n"); break; /* SUB */ } } else { LoadFromEA(ea, size, 0); e("%s %s, [d+(%d*4)]\n", opr, edx[opmode & 3], (op >> 9) & 7); e("lahf\n"); switch ((op >> 12) & 0xf) /* flags calculations */ { case 0xc: e("xor al, al\n"); break; /* AND */ case 0xd: e("setc byte [x]\n"); e("seto al\n"); break; /* ADD */ case 0xb: if (opmode & 4) e("xor al, al\n"); /* EOR */ else e("seto al\n"); /* CMP */ break; case 8: e("xor al, al\n"); break; /* OR */ case 9: e("setc byte [x]\n"); e("seto al\n"); break; /* SUB */ } if(!(((op >> 12) & 0xf) == 0xb && !(opmode & 4))) // don't write if CMP { switch (size) { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } } else { e("xor edi, edi\n"); // the write functions normally handle this } } } else /* op ea,Dn */ { LoadFromEA(ea, size, 0); e("%s [d+(%d*4)], %s\n", opr, (op >> 9) & 7, edx[opmode & 3]); e("lahf\n"); switch ((op >> 12) & 0xf) /* flags calculations */ { case 0xc: e("xor al, al\n"); break; /* AND */ case 0xd: e("setc byte [x]\n"); e("seto al\n"); break; /* ADD */ case 0xb: if (opmode & 4) e("xor al, al\n"); /* EOR */ else e("seto al\n"); /* CMP */ break; case 8: e("xor al, al\n"); break; /* OR */ case 9: e("setc byte [x]\n"); e("seto al\n"); break; /* SUB */ } } EmitTiming(t); InstEnd(); return 1; } int ArithmeticI(unsigned short op, char *mnem_unused, unsigned base_timing) { unsigned t = base_timing, ea = op & 0x3f; /* base_timing = 8 */ int size = UNKNOWN_SIZE; char mnem[5]; char opr[5], *size_s[] = { "byte", "word", "dword" }, *edx[] = { "dl", "dx", "edx" }; if (!CheckEA(ea, "101111111000")) return 0; if (!NumDecodedEA(ea)) /* check if decoded this EA */ return 0; switch ((op >> 6) & 3) /* set allowed sizes */ { case 0: size = BYTE_SIZE; break; case 1: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } switch ((op >> 8) & 0xf) /* set operation, mnem */ { case 2: strcpy(mnem, "ANDI"); strcpy(opr, "and"); break; case 6: strcpy(mnem, "ADDI"); strcpy(opr, "add"); break; case 0xc: strcpy(mnem, "CMPI"); strcpy(opr, "cmp"); break; case 0xa: strcpy(mnem, "EORI"); strcpy(opr, "xor"); break; case 0: strcpy(mnem, "ORI"); strcpy(opr, "or"); break; case 4: strcpy(mnem, "SUBI"); strcpy(opr, "sub"); break; default: return 0; } /* all but CMPI,ANDI take 8+8 cycles for long sizes */ if (((op >> 8) & 0xf) != 0xc && ((op >> 8) & 0xf) != 2 && size == LONG_SIZE) t += 8; else if (((op >> 8) & 0xf) == 0xc) t += 6; InstBegin(op, mnem, NumDecodedEA(ea)); if (((ea >> 3) & 7) == 0) /* Dn is treated differently */ { e("and edi, byte 7\n");/* get reg # */ e("mov edx, [esi]\n"); /* get imm data */ if (size == LONG_SIZE) e("ror edx, byte 16\n"); /* prep. the long-word */ e("%s %s [d+edi*4], %s\n", opr, size_s[(op >> 6) & 3], edx[(op >> 6) & 3]); e("lahf\n"); switch ((op >> 8) & 0xf) /* flags calculations */ { case 2: e("xor al, al\n"); break; /* ANDI */ case 6: e("setc byte [x]\n"); e("seto al\n"); break; /* ADDI */ case 0xc: e("seto al\n"); break; /* CMPI */ case 0xa: e("xor al, al\n"); break; /* EORI */ case 0: e("xor al, al\n"); break; /* ORI */ case 4: e("setc byte [x]\n"); e("seto al\n"); break; /* SUBI */ } if (size != LONG_SIZE) e("add esi, byte 2\n"); else e("add esi, byte 4\n"); } else { SaveReg("run", "esi"); /* address of immediate data */ switch (size) /* make ESI point to EA data */ { case BYTE_SIZE: case WORD_SIZE: e("add esi, byte 2\n"); break; case LONG_SIZE: e("add esi, byte 4\n"); break; } LoadFromEA(ea, size, 0); RestoreRegTo("run", "esi", "edi"); /* EDI=address of immediate data */ /* if byte or word, perform operation directly from memory */ if (size == BYTE_SIZE || size == WORD_SIZE) { e("%s %s, [edi]\n", opr, edx[(op >> 6) & 3]); e("lahf\n"); switch ((op >> 8) & 0xf) /* flags calculations */ { case 2: e("xor al, al\n"); break; /* ANDI */ case 6: e("setc byte [x]\n"); e("seto al\n"); break; /* ADDI */ case 0xc: e("seto al\n"); break; /* CMPI */ case 0xa: e("xor al, al\n"); break; /* EORI */ case 0: e("xor al, al\n"); break; /* ORI */ case 4: e("setc byte [x]\n"); e("seto al\n"); break; /* SUBI */ } } else /* long */ { e("mov edi, [edi]\n"); e("ror edi, byte 16\n"); /* word-swap long-words... */ e("%s %s, edi\n", opr, edx[(op >> 6) & 3]); e("lahf\n"); switch ((op >> 8) & 0xf) /* flags calculations */ { case 2: e("xor al, al\n"); break; /* ANDI */ case 6: e("setc byte [x]\n"); e("seto al\n"); break; /* ADDI */ case 0xc: e("seto al\n"); break; /* CMPI */ case 0xa: e("xor al, al\n"); break; /* EORI */ case 0: e("xor al, al\n"); break; /* ORI */ case 4: e("setc byte [x]\n"); e("seto al\n"); break; /* SUBI */ } } if(((op >> 8) & 0x0F) != 0x0C) { switch (size) /* write back to mem */ { case BYTE_SIZE: WriteByte(); break; case WORD_SIZE: WriteWord(); break; case LONG_SIZE: WriteLong(); break; } } else { e("xor edi, edi\n"); } } EmitTiming(t + TimingEA(ea, size)); InstEnd(); return 1; } int MOVE(unsigned short op, char *mnem, unsigned base_timing) { unsigned src = op & 0x3f, dest = (op >> 6) & 0x3f; int size = UNKNOWN_SIZE; dest = ((dest & 0x7) << 3) | ((dest >> 3) & 0x7); if (!CheckEA(src, "111111111111") || !CheckEA(dest, "101111111000")) return 0; if (!NumDecodedEA(src)) /* check if decoded this source EA */ return 0; switch ((op >> 12) & 3) /* set allowed sizes */ { case 1: size = BYTE_SIZE; break; case 3: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } if (size == BYTE_SIZE && ((src >> 3) & 7) == 1) return 0; /* no An byte accesses allowed */ InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); LoadFromEA(src, size, 0); switch (size) { case BYTE_SIZE: e("test dl, dl\n"); break; case WORD_SIZE: e("test dx, dx\n"); break; case LONG_SIZE: e("test edx, edx\n"); break; } e("lahf\n"); e("xor al, al\n"); /* V=always cleared */ if (src == dest) StoreToEA(dest, size, 1); else StoreToEA(dest, size, 0); EmitTiming(base_timing + TimingEA(src, size) + TimingEA(dest, size)); InstEnd(); return 1; } int MOVEQ(unsigned op, char *mnem, unsigned base_timing) { InstBegin(op, mnem, 256); /* 256 possible immediate data combinations */ e("mov ebx, edi\n"); /* opcode is in edi */ e("movsx ebx, bl\n"); /* sign extend */ e("mov [d+%d*4], ebx\n", (op >> 9) & 7); e("test bl, bl\n"); /* set N,Z,C appropriately */ e("lahf\n"); /* get changes */ e("xor al, al\n"); /* V is cleared */ EmitTiming(base_timing); InstEnd(); return 1; } int MOVEA(unsigned short op, char *mnem, unsigned base_timing) { int size = UNKNOWN_SIZE; if (!CheckEA(op & 0x3f, "111111111111")) /* check EA mode */ return 0; if (!NumDecodedEA(op & 0x3f)) /* check if decoded this EA */ return 0; switch ((op >> 12) & 3) /* set allowed sizes */ { case 3: size = WORD_SIZE; break; case 2: size = LONG_SIZE; break; default: return 0; } InstBegin(op, mnem, NumDecodedEA(op & 0x3f)); LoadFromEA(op & 0x3f, size, 1); /* load */ e("mov [a+%d*4], edx\n", (op >> 9) & 7); /* store */ EmitTiming(TimingEA(op & 0x3f, size) + base_timing); InstEnd(); return 1; } /***************************************************************************** * Instruction Emission */ void EmitInstructions() { unsigned i, j, k, l, n; /* TST */ n = 0; for (i = 0; i <= 2; i++) for (j = 0; j <= 0x3f; j++) n += TST((i << 6) + j + 0x4a00, "TST", 4); printf("TST:\t\t%d\n", n); /* Bcc */ n = 0; for (i = 0; i <= 0xf; i++) for (j = 0; j < 0xff; j++) n += Bcc((i << 8) + j + 0x6000, "B??", 0); printf("Bcc:\t\t%d\n", n); /* MOVE */ n = 0; for (i = 1; i <= 3; i++) /* size */ for (j = 0; j <= 0x3f; j++) /* dest. EA */ for (k = 0; k <= 0x3f; k++) /* source EA */ n += MOVE((i << 12) + (j << 6) + k + 0x0000, "MOVE", 4); printf("MOVE:\t\t%d\n", n); /* ADD, AND, CMP, EOR, OR, SUB -- "Arithmetic" */ n = 0; for (i = 8; i <= 0xd; i++) /* operation */ for (j = 0; j <= 7; j++) /* Dn */ for (k = 0; k <= 6; k++) /* opmode */ for (l = 0; l <= 0x3f; l++) /* EA */ n += Arithmetic((i << 12) + (j << 9) + (k << 6) + l, "????", 0); printf("ADD, AND, CMP, EOR, OR, SUB: %d\n", n); /* ADDI, ANDI, CMPI, EORI, ORI, SUBI -- "ArithmeticI" */ n = 0; for (i = 0; i <= 0xc; i++) /* operation */ for (j = 0; j <= 2; j++) /* size */ for (k = 0; k <= 0x3f; k++) /* EA */ n += ArithmeticI((i << 8) + (j << 6) + k + 0x0000, "????I", 8); printf("ADDI, ANDI, CMPI, EORI, ORI, SUBI: %d\n", n); /* DBcc */ n = 0; for (i = 0; i <= 0xf; i++) /* condition */ n += DBcc((i << 8) + 0x50c8, "DB??", 0); printf("DBcc:\t\t%d\n", n); /* BTST */ n = 0; for (i = 0; i <= 7; i++) /* register */ for (j = 0; j <= 0x3f; j++) /* EA */ n += BTST((i << 9) + j + 0x0100, "BTST", 0); for (i = 0; i <= 0x3f; i++) /* EA */ n += BTST(i + 0x0800, "BTST", 0); printf("BTST:\t\t%d\n", n); /* ADDQ, SUBQ -- "ArithmeticQ" */ n = 0; for (i = 0; i <= 7; i++) /* data */ for (j = 0; j <= 1; j++) /* ADDQ/SUBQ */ for (k = 0; k <= 2; k++) /* size */ for (l = 0; l <= 0x3f; l++) /* EA */ n += ArithmeticQ((i << 9) + (j << 8) + (k << 6) + l + 0x5000, "???Q", 4); printf("ADDQ, SUBQ:\t%d\n", n); /* BRA */ n = 0; for (i = 0; i <= 0xff; i++) n += Bxx(i + 0x6000, "BRA", 10); printf("BRA:\t\t%d\n", n); /* LEA */ n = 0; for (i = 0; i <= 7; i++) /* register */ for (j = 0; j <= 0x3f; j++) /* EA */ n += LEA((i << 9) + 0x41c0 + j, "LEA", 0); printf("LEA:\t\t%d\n", n); /* RTS */ RTS(0x4e75, "RTS", 16); printf("RTS:\t\t1\n"); /* MOVEA */ n = 0; for (i = 2; i <= 3; i++) /* size */ for (j = 0; j <= 7; j++) /* dest. reg */ for (k = 0; k <= 0x3f; k++) /* source EA */ n += MOVEA((i << 12) + (j << 9) + k + 0x0040, "MOVEA", 4); printf("MOVEA:\t\t%d\n", n); /* ADDA */ n = 0; for (i = 3; i <= 7; i++) /* size */ for (j = 0; j <= 7; j++) /* dest. reg */ for (k = 0; k <= 0x3f; k++) /* source EA */ n += ArithmeticA((i << 6) + (j << 9) + k + 0xd000, "ADDA", 4); printf("ADDA:\t\t%d\n", n); /* MOVEQ */ n = 0; for (i = 0; i < 8; i++) n += MOVEQ((i << 9) + 0x7000, "MOVEQ", 4); printf("MOVEQ:\t\t%d\n", n); /* JSR */ n = 0; for (i = 0; i <= 0x3f; i++) /* register */ n += Jxx(i + 0x4e80, "JSR", 0); printf("JSR:\t\t%d\n", n); /* LSL, LSR */ n = 0; for (i = 0; i <= 7; i++) /* count/register */ for (j = 0; j <= 1; j++) /* direction */ for (k = 0; k <= 2; k++) /* size */ for (l = 0; l <= 1; l++) /* immediate/reg */ n += LSxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe008, "LS?", 0); for (i = 0; i <= 1; i++) /* direction */ for (j = 0; j <= 0x3f; j++) /* EA */ n += LSxMem((i << 8) + j + 0xe2c0, "LS?", 8); printf("LSL, LSR:\t%d\n", n); /* SWAP */ SWAP(0x4840, "SWAP", 4); printf("SWAP:\t\t1\n"); /* BSR */ n = 0; for (i = 0; i <= 0xff; i++) n += Bxx(i + 0x6100, "BSR", 18); printf("BSR:\t\t%d\n", n); /* EXT */ n = 0; for (i = 2; i <= 7; i++) n += EXT((i << 6) + 0x4800, "EXT", 4); printf("EXT:\t\t%d\n", n); /* CLR */ n = 0; for (i = 0; i <= 2; i++) /* size */ for (j = 0; j <= 0x3f; j++) /* EA */ n += CLR((i << 6) + j + 0x4200, "CLR", 0); printf("CLR:\t\t%d\n", n); /* MOVEM */ n = 0; for (i = 0; i <= 1; i++) /* direction */ for (j = 0; j <= 1; j++) /* size */ for (k = 0; k <= 0x3f; k++) /* EA */ n += MOVEM((i << 10) + (j << 6) + k + 0x4880, "MOVEM", 0); printf("MOVEM:\t\t%d\n", n); /* MOVE to SR */ n = 0; for (i = 0; i <= 0x3f; i++) n += MOVEtoSR(i + 0x46c0, "MOVE to SR", 12); printf("MOVE to SR:\t%d\n", n); /* NOP */ NOP(0x4e71, "NOP", 4); printf("NOP:\t\t1\n"); /* JMP */ n = 0; for (i = 0; i <= 0x3f; i++) /* register */ n += Jxx(i + 0x4ec0, "JMP", 0); printf("JMP:\t\t%d\n", n); /* CMPA */ n = 0; for (i = 0; i <= 7; i++) /* register */ for (j = 3; j <= 7; j++) /* opmode */ for (k = 0; k <= 0x3f; k++) /* EA */ n += CMPA((i << 9) + (j << 6) + k + 0xb000, "CMPA", 0); printf("CMPA:\t\t%d\n", n); /* ASL, ASR */ n = 0; for (i = 0; i <= 7; i++) /* count/reg */ for (j = 0; j <= 1; j++) /* direction */ for (k = 0; k <= 2; k++) /* size */ for (l = 0; l <= 1; l++) /* immediate/reg */ n += ASxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe000, "AS?", 0); for (i = 0; i <= 1; i++) /* direction */ for (j = 0; j <= 0x3f; j++) /* EA */ n += ASxMem((i << 8) + j + 0xe0c0, "AS?", 8); printf("ASL, ASR:\t%d\n", n); /* MULS */ n = 0; for (i = 0; i <= 7; i++) /* register */ for (j = 0; j <= 0x3f; j++) /* EA */ n += MULS((i << 9) + j + 0xc1c0, "MULS", 70); printf("MULS:\t\t%d\n", n); /* RTE */ RTE(0x4e73, "RTE", 20); printf("RTE:\t\t1\n"); /* NEG, NEGX */ n = 0; for (i = 0; i <= 1; i++) /* NEG/NEGX */ for (j = 0; j <= 2; j++) /* size */ for (k = 0; k <= 0x3f; k++) /* EA */ n += NEGx((i << 10) + (j << 6) + k + 0x4000, "NEG?", 0); printf("NEG, NEGX:\t%d\n", n); /* ROL, ROR */ n = 0; for (i = 0; i <= 7; i++) /* count/register */ for (j = 0; j <= 1; j++) /* direction */ for (k = 0; k <= 2; k++) /* size */ for (l = 0; l <= 1; l++) /* immediate/reg */ n += ROxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe018, "RO?", 0); for (i = 0; i <= 1; i++) /* direction */ for (j = 0; j <= 0x3f; j++) /* EA */ n += ROxMem((i << 8) + j + 0xe6c0, "RO?", 8); printf("ROL, ROR:\t%d\n", n); /* BCLR */ n = 0; for (i = 0; i <= 7; i++) /* register */ for (j = 0; j <= 0x3f; j++) /* EA */ n += BCLR((i << 9) + j + 0x0180, "BCLR", 0); for (i = 0; i <= 0x3f; i++) /* EA */ n += BCLR(i + 0x0880, "BCLR", 0); printf("BCLR:\t\t%d\n", n); /* NOT */ n = 0; for (i = 0; i <= 2; i++) for (j = 0; j <= 0x3f; j++) n += NOT((i << 6) + j + 0x4600, "NOT", 0); printf("NOT:\t\t%d\n", n); /* MOVE USP */ n = 0; for (i = 0; i <= 1; i++) n += MOVEUSP((i << 3) + 0x4e60, "MOVE USP", 4); printf("MOVE USP:\t%d\n", n); /* ROXL, ROXR */ n = 0; for (i = 0; i <= 7; i++) /* count/register */ for (j = 0; j <= 1; j++) /* direction */ for (k = 0; k <= 2; k++) /* size */ for (l = 0; l <= 1; l++) /* immediate/reg */ n += ROXxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe010, "ROX?", 0); for (i = 0; i <= 1; i++) /* direction */ for (j = 0; j <= 0x3f; j++) /* EA */ n += ROXxMem((i << 8) + j + 0xe4c0, "ROX?", 8); printf("ROXL, ROXR:\t%d\n", n); /* BSET */ n = 0; for (i = 0; i <= 7; i++) /* register */ for (j = 0; j <= 0x3f; j++) /* EA */ n += BSET((i << 9) + j + 0x01c0, "BSET", 0); for (i = 0; i <= 0x3f; i++) /* EA */ n += BSET(i + 0x08c0, "BSET", 0); printf("BSET:\t\t%d\n", n); /* MOVE from SR */ /* * The 68000 and 68008 "MOVE from SR" is not privileged. But for the 68010 * and higher, it is. */ n = 0; for (i = 0; i <= 0x3f; i++) n += MOVEfromSR(i + 0x40c0, "MOVE from SR", 0); printf("MOVE from SR:\t%d\n", n); /* SUBA */ n = 0; for (i = 3; i <= 7; i++) /* size */ for (j = 0; j <= 7; j++) /* dest. reg */ for (k = 0; k <= 0x3f; k++) /* source EA */ n += ArithmeticA((i << 6) + (j << 9) + k + 0x9000, "SUBA", 4); printf("SUBA:\t\t%d\n", n); /* DIVS */ n = 0; for (i = 0; i <= 7; i++) /* register */ for (j = 0; j <= 0x3f; j++) /* EA */ n += DIVS((i << 9) + j + 0x81c0, "DIVS", 158); printf("DIVS:\t\t%d\n", n); /* MULU */ n = 0; for (i = 0; i <= 7; i++) /* register */ for (j = 0; j <= 0x3f; j++) /* EA */ n += MULU((i << 9) + j + 0xc0c0, "MULU", 70); printf("MULU:\t\t%d\n", n); /* ADDX */ n = 0; for (i = 0; i <= 7; i++) /* Rx */ for (j = 0; j <= 2; j++) /* size */ for (k = 0; k <= 1; k++) /* R/M */ n += ADDX((i << 9) + (j << 6) + (k << 3) + 0xd100, "ADDX", 0); printf("ADDX:\t\t%d\n", n); /* Scc */ n = 0; for (i = 0; i <= 0xf; i++) /* condition */ for (j = 0; j <= 0x3f; j++) /* EA */ n += Scc((i << 8) + j + 0x50c0, "S??", 0); printf("Scc:\t\t%d\n", n); /* ORI to SR */ ORItoSR(0x007c, "ORI to SR", 20); printf("ORI to SR:\t1\n"); /* MOVE to CCR */ n = 0; for (i = 0; i <= 0x3f; i++) n += MOVEtoCCR(i + 0x44c0, "MOVE to CCR", 12); printf("MOVE to CCR:\t%d\n", n); /* EXG */ n = 0; for (i = 0; i <= 7; i++) /* Rx */ for (j = 0; j <= 0x11; j++) /* opmode */ n += EXG((i << 9) + (j << 3) + 0xc100, "EXG", 6); printf("EXG:\t\t%d\n", n); /* UNLK */ UNLK(0x4e58, "UNLK", 12); printf("UNLK:\t\t1\n"); /* LINK */ LINK(0x4e50, "LINK", 16); /* word */ printf("LINK:\t\t1\n"); /* DIVU */ n = 0; for (i = 0; i <= 7; i++) /* register */ for (j = 0; j <= 0x3f; j++) /* EA */ n += DIVU((i << 9) + j + 0x80c0, "DIVU", 144); printf("DIVU:\t\t%d\n", n); /* ANDI to CCR */ ANDItoCCR(0x023c, "ANDI to CCR", 20); printf("ANDI to CCR:\t1\n"); /* PEA */ n = 0; for (i = 0; i <= 0x3f; i++) n += PEA(i + 0x4840, "PEA", 0); printf("PEA:\t\t%d\n", n); /* ANDI to SR */ ANDItoSR(0x027c, "ANDI to SR", 20); printf("ANDI to SR:\t%d\n", n); /* BCHG */ n = 0; for (i = 0; i <= 7; i++) /* register */ for (j = 0; j <= 0x3f; j++) /* EA */ n += BCHG((i << 9) + j + 0x0140, "BCHG", 0); for (i = 0; i <= 0x3f; i++) /* EA */ n += BCHG(i + 0x0840, "BCHG", 0); printf("BCHG:\t\t%d\n", n); /* ORI to CCR */ ORItoCCR(0x003c, "ORI to CCR", 20); printf("ORI to CCR:\t1\n"); /* RTR */ RTR(0x4e77, "RTR", 20); printf("RTR:\t\t1\n"); /* TRAP */ TRAP(0x4e40, "TRAP", 34); printf("TRAP:\t\t1\n"); /* SBCD */ n = 0; for (i = 0; i <= 7; i++) /* Rx */ for (j = 0; j <= 1; j++) /* R/M */ n += SBCD((i << 9) + (j << 3) + 0x8100, "SBCD", 0); printf("SBCD:\t\t%d\n", n); /* SUBX */ n = 0; for (i = 0; i <= 7; i++) /* Rx */ for (j = 0; j <= 2; j++) /* size */ for (k = 0; k <= 1; k++) /* R/M */ n += SUBX((i << 9) + (j << 6) + (k << 3) + 0x9100, "SUBX", 0); printf("SUBX:\t\t%d\n", n); /* ABCD */ n = 0; for (i = 0; i <= 7; i++) /* Rx */ for (j = 0; j <= 1; j++) /* R/M */ n += ABCD((i << 9) + (j << 3) + 0xc100, "ABCD", 0); printf("ABCD:\t\t%d\n", n); /* CMPM */ n = 0; for (i = 0; i <= 7; i++) /* Ax */ for (j = 0; j <= 2; j++) /* size */ n += CMPM((i << 9) + (j << 6) + 0xb108, "CMPM", 0); printf("CMPM:\t\t%d\n", n); /* MOVEP */ n = 0; for (i = 0; i <= 7; i++) /* Dx */ for (j = 0; j <= 7; j++) /* opmode */ n += MOVEP((i << 9) + (j << 6) + 0x0008, "MOVEP", 0); printf("MOVEP:\t\t%d\n", n); /* * The following have no profile data associated with them, as far as my * tests have shown. */ /* BKPT */ if (mpu == 68010) { n = BKPT(0x4848, "BKPT", 45); printf("BKPT:\t\t%d\n", n); } /* CHK */ n = 0; for (i = 0; i <= 7; i++) /* register */ for (j = 0; j <= 3; j++) /* size */ for (k = 0; k <= 0x3f; k++) /* EA */ n += CHK((i << 9) + (j << 7) + k + 0x4000, "CHK", 10); printf("CHK:\t\t%d\n", n); /* EORI to CCR */ EORItoCCR(0x0a3c, "EORI to CCR", 20); printf("EORI to CCR:\t1\n"); /* EORI to SR */ EORItoSR(0x0a7c, "EORI to SR", 20); printf("EORI to SR:\t1\n"); /* ILLEGAL */ InstBegin(0x4afc, "ILLEGAL", 1); e("jmp near exception_illegal_instruction\n"); printf("ILLEGAL:\t1\n"); /* MOVEC */ if (mpu == 68010) { n = MOVEC(0x4e7a, "MOVEC", 0); printf("MOVEC:\t\t%d\n", n); } /* MOVE from CCR -- 68010, 68020, 68030, 68040, CPU32 */ if (mpu == 68010) { n = 0; for (i = 0; i <= 0x3f; i++) n += MOVEfromCCR(i + 0x42c0, "MOVE from CCR", 0); printf("MOVE from CCR:\t%d\n", n); } /* NBCD */ n = 0; for (i = 0; i <= 0x3f; i++) /* EA */ n += NBCD(i + 0x4800, "NBCD", 0); printf("NBCD:\t\t%d\n", n); /* RESET */ RESET(0x4e70, "RESET", 132); printf("RESET:\t\t1\n"); /* RTD */ if (mpu == 68010) { RTD(0x4e74, "RTD", 16); printf("RTD:\t\t1\n"); } /* STOP */ STOP(0x4e72, "STOP", 4); printf("STOP:\t\t1\n"); /* TAS */ n = 0; for (i = 0; i <= 0x3f; i++) n += TAS(i + 0x4ac0, "TAS", 0); printf("TAS:\t\t%d\n", n); /* TRAPV */ TRAPV(0x4e76, "TRAPV", 0); printf("TRAPV:\t\t1\n"); if (illegal) /* emulate Line 1010 and Line 1111 exceptions */ { /* Line 1010 */ InstBegin(0xa000, "Line 1010 Emulator", 4096); e("jmp near exception_line_1010_emulator\n"); /* Line 1111 */ InstBegin(0xf000, "Line 1111 Emulator", 4096); e("jmp near exception_line_1111_emulator\n"); num_handlers -= 2; /* don't count these as instruction handlers */ } /* I_Invalid: Invalid instruction */ EmitLabel("I_Invalid"); if (illegal) /* emulate illegal instruction exceptions */ e("jmp near exception_illegal_instruction\n"); else { e("sub esi, byte 2\n"); /* back up to the bad instruction */ e("jmp invalid_error\n"); /* invalid instruction error */ } } void EmitExceptions() { /* * exception_chk: * exception_divide_by_zero: * Assume the PC points to the instruction AFTER the instruction which * caused the trap. */ Align(4); EmitLabel("exception_chk"); e("push dword 6*4\n"); /* CHK exception vector */ e("jmp short do_exception\n"); Align(4); EmitLabel("exception_divide_by_zero"); e("push dword 5*4\n"); /* division by zero exception vector */ e("jmp short do_exception\n"); /* * exception_privilege_violation: * exception_illegal_instruction: * exception_line_1010_emulator: * exception_line_1111_emulator: * Assume the PC points to the word AFTER the instruction which caused the * trap. The value 2 will be subtracted from the PC in order to make them * point AT the instruction which caused the trap, as this is how the * exceptions work. */ Align(4); EmitLabel("exception_privilege_violation"); e("push dword 8*4\n"); /* privilege violation exception vector */ e("jmp short do_exception_fix_pc\n"); Align(4); EmitLabel("exception_illegal_instruction"); e("push dword 4*4\n"); /* illegal instruction exception vector */ e("jmp short do_exception_fix_pc\n"); Align(4); EmitLabel("exception_line_1010_emulator"); e("push dword 10*4\n"); /* Line 1010 emulator exception vector */ e("jmp short do_exception_fix_pc\n"); Align(4); EmitLabel("exception_line_1111_emulator"); e("push dword 11*4\n"); /* Line 1111 emulator exception vector */ e("jmp short do_exception_fix_pc\n"); EmitLabel("do_exception_fix_pc"); /* points PC at trap instruction */ e("sub esi, byte 2\n"); EmitLabel("do_exception"); e("mov edx, 0x2000\n"); /* what the SR will be */ e("xor edx, [sr]\n"); /* see if S bit is different */ e("test edx, 0x2000\n"); e("jz .no_sp_magic\n"); /* nope, don't swap SPs */ e("mov edx, [__sp]\n"); e("xchg [a+7*4], edx\n"); e("mov [__sp], edx\n"); SetSupervisorAddressSpace(); /* map in supervisor address space */ EmitLabel(".no_sp_magic"); e("mov ebx, [a+7*4]\n"); /* SP */ /* * If 68010, we have to push the format word on the stack first. */ if (mpu == 68010) { e("sub ebx, byte 2\n");/* SP-2 */ e("mov edx, [esp]\n"); /* vector offset | 0x00000000 */ SaveReg("run", "ebx"); WriteWord(); RestoreReg("run", "ebx"); } e("mov edx, esi\n"); e("sub edx, ebp\n"); /* PC->EDX */ e("sub ebx, byte 4\n"); /* SP-4 */ SaveReg("run", "ebx"); WriteLong(); RestoreReg("run", "ebx"); e("mov edx, [sr]\n"); /* get SR */ e("sub ebx, byte 2\n"); /* SP-2 */ SaveReg("run", "ebx"); WriteWord(); /* save SR to stack */ RestoreReg("run", "ebx"); e("or dh, 0x20\n"); /* set supervisor for exception */ e("and edx, 0xa71f\n"); /* clear unwanted bits */ e("mov [sr], edx\n"); /* set new SR, get old SR->EDX */ e("mov [a+7*4], ebx\n"); /* write back SP */ e("pop ebx\n"); /* get exception vector address */ if (mpu == 68010) e("add ebx, [vbr]\n"); ReadLong(); /* get PC */ e("mov esi, edx\n"); /* set new PC */ UpdateFetchPtr(); e("xor edi, edi\n"); e("sub ecx, byte 40\n"); /* timing... */ e("mov di, [esi]\n"); e("add esi, byte 2\n"); e("jmp [jmptab+edi*4]\n"); /* continue execution */ } /***************************************************************************** * Data */ void EmitData() { int i; CacheAlign(); EmitGlobalLabel(NameOfContext()); e("context_start:\n"); e("fetch dd 0\n"); /* pointer to fetch array */ e("pcfetch dd 0\n"); /* PC-relative fetch */ e("read_byte dd 0\n"); /* pointer to read_byte array */ e("read_word dd 0\n"); /* pointer to read_word array */ e("read_long dd 0\n"); /* pointer to read_long array */ e("write_byte dd 0\n"); /* pointer to write_byte array */ e("write_word dd 0\n"); /* pointer to write_word array */ e("write_long dd 0\n"); /* pointer to write_long array */ e("super_fetch dd 0\n"); /* supervisor memory map... */ e("super_pcfetch dd 0\n"); e("super_read_byte dd 0\n"); e("super_read_word dd 0\n"); e("super_read_long dd 0\n"); e("super_write_byte dd 0\n"); e("super_write_word dd 0\n"); e("super_write_long dd 0\n"); e("user_fetch dd 0\n"); /* user memory map... */ e("user_pcfetch dd 0\n"); e("user_read_byte dd 0\n"); e("user_read_word dd 0\n"); e("user_read_long dd 0\n"); e("user_write_byte dd 0\n"); e("user_write_word dd 0\n"); e("user_write_long dd 0\n"); e("intr times 8 dd 0\n"); /* intr[0-6]=vectors for interrupt levels 1-7 (0=not pending.) intr[7]=# of interrupts pending */ e("cycles dd 0\n"); /* cycles total to emulate */ e("remaining dd 0\n"); /* cycles remaining */ e("d times 8 dd 0\n"); /* D0-D7 */ e("a times 8 dd 0\n"); /* A0-A7 */ e("__sp dd 0\n"); /* User mode: SSP, Supervisor: USP */ e("sr dd 0\n"); /* SR */ e("pc dd 0\n"); /* PC */ if (mpu == 68010) { e("fc dd 0\n"); /* FC (DFC, SFC) */ e("vbr dd 0\n"); /* VBR */ } e("status dd 0\n"); /* bit 0:1=emulating, 0=not bit 1:1=stopped, 0=not bit 2:1=processing ints, 0=not */ e("InterruptAcknowledge dd 0\n"); /* interrupt acknowledge callback */ e("Reset dd 0\n"); /* pointer to RESET handler */ if (mpu == 68010) e("Bkpt dd 0\n"); /* pointer to BKPT handler */ e("Debug dd 0\n"); /* pointer to debug handler */ e("context_end:\n"); e("x dd 0\n"); /* X flag (maintained internally) */ for (i = 0; i < 7; i++) { char *reg[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp" }; e("memhandler_%s dd 0\n", reg[i]); } for (i = 0; i < 7; i++) { char *reg[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp" }; e("run_%s dd 0\n", reg[i]); } e("fetch_esi dd 0\n"); /* for UpdateFetchPtr() */ } /***************************************************************************** * Code */ void EmitCode() { int i; /* Turbo68KInit() */ /* * Decompresses the jump table. See main() for the compression format */ EmitGlobalLabel("Turbo68KInit"); e("push ecx\n"); e("push edx\n"); e("push esi\n"); e("push edi\n"); e("mov esi, compressed_jmptab\n"); e("mov edi, jmptab\n"); e("xor ecx, ecx\n"); /* we only need lower half (CX) */ EmitLabel(".l"); e("mov cx, [esi]\n"); /* get repeat information */ e("add esi, byte 2\n"); /* point at data */ e("mov edx, ecx\n"); e("and edx, 0xc000\n"); e("cmp dx, 0xc000\n"); /* #handlers * 1 each? */ e("je .r1\n"); e("cmp dx, 0x8000\n"); /* #handlers * 8 each? */ e("je .r8\n"); e("mov eax, [esi]\n"); /* get data and repeat */ e("add esi, byte 4\n"); e("cld\n"); e("rep stosd\n"); EmitLabel(".c"); e("cmp dword [esi], byte -1\n"); /* end of compressed data? */ e("jne .l\n"); e("pop edi\n"); e("pop esi\n"); e("pop edx\n"); e("pop ecx\n"); e("xor eax, eax\n"); e("ret\n"); EmitLabel(".r1"); e("and ecx, 0x3ff\n"); /* get rid of 0xc000 marker */ e("cld\n"); e("rep movsd\n"); /* move address to decomp'd jmptab */ e("jmp .c\n"); /* continue decompression */ EmitLabel(".r8"); e("and ecx, 0x7ff\n"); /* get rid of 0x8000 marker */ EmitLabel(".r8_l"); e("mov edx, ecx\n"); /* save counter temporarily */ e("mov ecx, 8\n"); /* repeat handler 8 times */ e("mov eax, [esi]\n"); /* get handler to repeat */ e("add esi, byte 4\n"); /* point to next */ e("cld\n"); e("rep stosd\n"); /* store! */ e("mov ecx, edx\n"); /* restore loop counter and loop */ e("dec ecx\n"); e("jnz .r8_l\n"); e("jmp .c\n"); /* Turbo68KReset() */ Align(4); EmitGlobalLabel("Turbo68KReset"); e("push ebx\n"); e("push ecx\n"); e("push edx\n"); e("push esi\n"); e("push edi\n"); e("push ebp\n"); SetSupervisorAddressSpace(); EMULATING(); UNSTOP_CPU(); /* in case we're STOPped, unstop */ if (mpu == 68010) /* clear 68010 VBR */ e("mov dword [vbr], 0\n"); e("xor ecx, ecx\n"); /* no cycles executed in case error */ /* * Read the PC vector from the fetch memory map */ if (!call_convention) /* stack calling convention */ e("push dword 4\n"); else /* register calling convention */ e("mov eax, 4\n"); e("call %sTurbo68KFetchPtr\n", id); if (!call_convention) e("add esp, byte 4\n"); e("test eax, eax\n"); e("jz near fetch_error\n"); e("mov eax, [eax]\n"); e("rol eax, byte 16\n"); /* memory is byte swapped */ e("mov dword "SR", 0x2700\n"); /* in supervisor mode at start */ e("mov dword [cycles], 0\n"); /* no cycles executed */ e("mov dword [remaining], 0\n"); /* ..ditto.. */ e("mov esi, eax\n"); /* from Turbo68KFetchPtr() */ e("xor eax, eax\n"); /* clear registers */ e("mov edi, d\n"); e("mov ecx, 16\n"); e("cld\n"); e("rep stosd\n"); e("mov edi, intr\n"); /* clear interrupt queue */ e("mov ecx, 8\n"); e("rep stosd\n"); e("xor ecx, ecx\n"); /* no cycles executed in case error */ UpdateFetchPtr(); /* this also makes sure it can be fetched */ WRITEPCTOMEM("pc"); /* * Read the SP vector from the fetch memory map */ if (!call_convention) /* stack calling convention */ e("push dword 0\n"); else /* register calling convention */ e("xor eax, eax\n"); e("call %sTurbo68KFetchPtr\n", id); if (!call_convention) e("add esp, byte 4\n"); e("test eax, eax\n"); e("jz near fetch_error\n"); e("mov eax, [eax]\n"); e("rol eax, byte 16\n"); /* memory is byte swapped */ e("mov "A7", eax\n"); /* supervisor */ e("mov "SP", eax\n"); /* user */ STOP_EMULATING(); /* no longer running */ e("pop ebp\n"); e("pop edi\n"); e("pop esi\n"); e("pop edx\n"); e("pop ecx\n"); e("pop ebx\n"); e("xor eax, eax\n"); if (mmx) e("emms\n"); e("ret\n"); EmitLabel("invalid_error"); /* invalid instruction */ SaveCCR(); STOP_EMULATING(); STOP_RUNNING(); /* save PC and cycles remaining */ e("pop ebp\n"); e("pop edi\n"); e("pop esi\n"); e("pop edx\n"); e("pop ecx\n"); e("pop ebx\n"); e("mov eax, %d\n", TURBO68K_ERROR_INVINST); e("ret\n"); /* * NOTE: Only UpdateFetchPtr() and Turbo68KReset() can call this */ EmitLabel("fetch_error"); /* could not fetch instruction */ e("mov [remaining], ecx\n"); /* save cycles remaining */ STOP_EMULATING(); e("mov [pc], esi\n"); /* UpdateFetchPtr() didn't yet change this */ e("pop ebp\n"); e("pop edi\n"); e("pop esi\n"); e("pop edx\n"); e("pop ecx\n"); e("pop ebx\n"); e("mov eax, %d\n", TURBO68K_ERROR_FETCH); e("ret\n"); /* Turbo68KReadPC() */ /* * Can be used anywhere, but usage while Turbo68K is emulating may * result in the value being offset a few bytes into the current * instruction, but it shouldn't */ Align(4); EmitGlobalLabel("Turbo68KReadPC"); e("mov eax, [pc]\n"); e("ret\n"); /* Turbo68KSetFetch() */ Align(4); EmitGlobalLabel("Turbo68KSetFetch"); e("push eax\n"); e("push edx\n"); GetArg("eax", 0, 12); e("sub eax, "SIZEOF_FETCHREGION"\n"); if (multiaddr) { GetArg("edx", 1, 16); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov [super_fetch], eax\n"); e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ e("jz near .not_super\n"); e("mov [fetch], eax\n"); EmitLabel(".not_super"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); EmitLabel(".user"); e("mov [user_fetch], eax\n"); e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ e("jnz near .not_user\n"); e("mov [fetch], eax\n"); EmitLabel(".not_user"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } else { e("mov [fetch], eax\n"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } /* Turbo68KGetFetch() */ Align(4); EmitGlobalLabel("Turbo68KGetFetch"); e("push edx\n"); if (multiaddr) { GetArg("edx", 0, 8); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov eax, [super_fetch]\n"); e("jmp near .finish\n"); EmitLabel(".user"); e("mov eax, [user_fetch]\n"); EmitLabel(".finish"); } else e("mov eax, [fetch]\n"); e("pop edx\n"); e("add eax, "SIZEOF_FETCHREGION"\n"); e("ret\n"); /* Turbo68KSetPCFetch() */ if (pcfetch) { Align(4); EmitGlobalLabel("Turbo68KSetPCFetch"); e("push eax\n"); e("push edx\n"); GetArg("eax", 0, 12); e("sub eax, "SIZEOF_FETCHREGION"\n"); if (multiaddr) { GetArg("edx", 1, 16); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov [super_pcfetch], eax\n"); e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ e("jz near .not_super\n"); e("mov [pcfetch], eax\n"); EmitLabel(".not_super"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); EmitLabel(".user"); e("mov [user_pcfetch], eax\n"); e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ e("jnz near .not_user\n"); e("mov [pcfetch], eax\n"); EmitLabel(".not_user"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } else { e("mov [pcfetch], eax\n"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } /* Turbo68KGetPCFetch() */ Align(4); EmitGlobalLabel("Turbo68KGetPCFetch"); e("push edx\n"); if (multiaddr) { GetArg("edx", 0, 8); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov eax, [super_pcfetch]\n"); e("jmp near .finish\n"); EmitLabel(".user"); e("mov eax, [user_pcfetch]\n"); EmitLabel(".finish"); } else e("mov eax, [pcfetch]\n"); e("pop edx\n"); e("add eax, "SIZEOF_FETCHREGION"\n"); e("ret\n"); } /* * If the memory map mode is NOT 0 (defmap), we do not add or subtract * from the pointers because they aren't arrays, but function pointers. */ /* Turbo68KSetReadByte() */ Align(4); EmitGlobalLabel("Turbo68KSetReadByte"); e("push eax\n"); e("push ebx\n"); GetArg("eax", 0, 12); if (memmap_type == 0) e("sub eax, "SIZEOF_DATAREGION"\n"); if (multiaddr) { GetArg("edx", 1, 16); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov [super_read_byte], eax\n"); e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ e("jz near .not_super\n"); e("mov [read_byte], eax\n"); EmitLabel(".not_super"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); EmitLabel(".user"); e("mov [user_read_byte], eax\n"); e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ e("jnz near .not_user\n"); e("mov [read_byte], eax\n"); EmitLabel(".not_user"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } else { e("mov [read_byte], eax\n"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } /* Turbo68KGetReadByte() */ Align(4); EmitGlobalLabel("Turbo68KGetReadByte"); e("push edx\n"); if (multiaddr) { GetArg("edx", 0, 8); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov eax, [super_read_byte]\n"); e("jmp near .finish\n"); EmitLabel(".user"); e("mov eax, [user_read_byte]\n"); EmitLabel(".finish"); } else e("mov eax, [read_byte]\n"); if (memmap_type == 0) e("add eax, "SIZEOF_DATAREGION"\n"); e("pop edx\n"); e("ret\n"); /* Turbo68KSetReadWord() */ Align(4); EmitGlobalLabel("Turbo68KSetReadWord"); e("push eax\n"); e("push ebx\n"); GetArg("eax", 0, 12); if (memmap_type == 0) e("sub eax, "SIZEOF_DATAREGION"\n"); if (multiaddr) { GetArg("edx", 1, 16); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov [super_read_word], eax\n"); e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ e("jz near .not_super\n"); e("mov [read_word], eax\n"); EmitLabel(".not_super"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); EmitLabel(".user"); e("mov [user_read_word], eax\n"); e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ e("jnz near .not_user\n"); e("mov [read_word], eax\n"); EmitLabel(".not_user"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } else { e("mov [read_word], eax\n"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } /* Turbo68KGetReadWord() */ Align(4); EmitGlobalLabel("Turbo68KGetReadWord"); e("push edx\n"); if (multiaddr) { GetArg("edx", 0, 8); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov eax, [super_read_word]\n"); e("jmp near .finish\n"); EmitLabel(".user"); e("mov eax, [user_read_word]\n"); EmitLabel(".finish"); } else e("mov eax, [read_word]\n"); if (memmap_type == 0) e("add eax, "SIZEOF_DATAREGION"\n"); e("pop edx\n"); e("ret\n"); /* Turbo68KSetReadLong() */ Align(4); EmitGlobalLabel("Turbo68KSetReadLong"); e("push eax\n"); e("push ebx\n"); GetArg("eax", 0, 12); if (memmap_type == 0) e("sub eax, "SIZEOF_DATAREGION"\n"); if (multiaddr) { GetArg("edx", 1, 16); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov [super_read_long], eax\n"); e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ e("jz near .not_super\n"); e("mov [read_long], eax\n"); EmitLabel(".not_super"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); EmitLabel(".user"); e("mov [user_read_long], eax\n"); e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ e("jnz near .not_user\n"); e("mov [read_long], eax\n"); EmitLabel(".not_user"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } else { e("mov [read_long], eax\n"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } /* Turbo68KGetReadLong() */ Align(4); EmitGlobalLabel("Turbo68KGetReadLong"); e("push edx\n"); if (multiaddr) { GetArg("edx", 0, 8); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov eax, [super_read_long]\n"); e("jmp near .finish\n"); EmitLabel(".user"); e("mov eax, [user_read_long]\n"); EmitLabel(".finish"); } else e("mov eax, [read_long]\n"); if (memmap_type == 0) e("add eax, "SIZEOF_DATAREGION"\n"); e("pop edx\n"); e("ret\n"); /* Turbo68KSetWriteByte() */ Align(4); EmitGlobalLabel("Turbo68KSetWriteByte"); e("push eax\n"); e("push edx\n"); GetArg("eax", 0, 12); if (memmap_type == 0) e("sub eax, "SIZEOF_DATAREGION"\n"); if (multiaddr) { GetArg("edx", 1, 16); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov [super_write_byte], eax\n"); e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ e("jz near .not_super\n"); e("mov [write_byte], eax\n"); EmitLabel(".not_super"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); EmitLabel(".user"); e("mov [user_write_byte], eax\n"); e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ e("jnz near .not_user\n"); e("mov [write_byte], eax\n"); EmitLabel(".not_user"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } else { e("mov [write_byte], eax\n"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } /* Turbo68KGetWriteByte() */ Align(4); EmitGlobalLabel("Turbo68KGetWriteByte"); e("push edx\n"); if (multiaddr) { GetArg("edx", 0, 8); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov eax, [super_write_byte]\n"); e("jmp near .finish\n"); EmitLabel(".user"); e("mov eax, [user_write_byte]\n"); EmitLabel(".finish"); } else e("mov eax, [write_byte]\n"); if (memmap_type == 0) e("add eax, "SIZEOF_DATAREGION"\n"); e("pop edx\n"); e("ret\n"); /* Turbo68KSetWriteWord() */ Align(4); EmitGlobalLabel("Turbo68KSetWriteWord"); e("push eax\n"); e("push edx\n"); GetArg("eax", 0, 12); if (memmap_type == 0) e("sub eax, "SIZEOF_DATAREGION"\n"); if (multiaddr) { GetArg("edx", 1, 16); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov [super_write_word], eax\n"); e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ e("jz near .not_super\n"); e("mov [write_word], eax\n"); EmitLabel(".not_super"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); EmitLabel(".user"); e("mov [user_write_word], eax\n"); e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ e("jnz near .not_user\n"); e("mov [write_word], eax\n"); EmitLabel(".not_user"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } else { e("mov [write_word], eax\n"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } /* Turbo68KGetWriteWord() */ Align(4); EmitGlobalLabel("Turbo68KGetWriteWord"); e("push edx\n"); if (multiaddr) { GetArg("edx", 0, 8); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov eax, [super_write_word]\n"); e("jmp near .finish\n"); EmitLabel(".user"); e("mov eax, [user_write_word]\n"); EmitLabel(".finish"); } else e("mov eax, [write_word]\n"); if (memmap_type == 0) e("add eax, "SIZEOF_DATAREGION"\n"); e("pop edx\n"); e("ret\n"); /* Turbo68KSetWriteLong() */ Align(4); EmitGlobalLabel("Turbo68KSetWriteLong"); e("push eax\n"); e("push edx\n"); GetArg("eax", 0, 12); if (memmap_type == 0) e("sub eax, "SIZEOF_DATAREGION"\n"); if (multiaddr) { GetArg("edx", 1, 16); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov [super_write_long], eax\n"); e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */ e("jz near .not_super\n"); e("mov [write_long], eax\n"); EmitLabel(".not_super"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); EmitLabel(".user"); e("mov [user_write_long], eax\n"); e("test byte [sr+1], 0x20\n"); /* if user, update fetch */ e("jnz near .not_user\n"); e("mov [write_long], eax\n"); EmitLabel(".not_user"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } else { e("mov [write_long], eax\n"); e("pop edx\n"); e("pop eax\n"); e("ret\n"); } /* Turbo68KGetWriteLong() */ Align(4); EmitGlobalLabel("Turbo68KGetWriteLong"); e("push edx\n"); if (multiaddr) { GetArg("edx", 0, 8); e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR); e("jne short .user\n"); e("mov eax, [super_write_long]\n"); e("jmp near .finish\n"); EmitLabel(".user"); e("mov eax, [user_write_long]\n"); EmitLabel(".finish"); } else e("mov eax, [write_long]\n"); if (memmap_type == 0) e("add eax, "SIZEOF_DATAREGION"\n"); e("pop edx\n"); e("ret\n"); /* Turbo68KFetchPtr() */ Align(4); EmitGlobalLabel("Turbo68KFetchPtr"); e("push ebx\n"); e("push edx\n"); e("push esi\n"); e("push ebp\n"); GetArg("esi", 0, 20); /* address */ if (multiaddr) { e("test byte [sr+1], 0x20\n"); /* set proper address space */ e("jz short .user\n"); SetSupervisorAddressSpace(); e("jmp short .continue\n"); EmitLabel(".user"); SetUserAddressSpace(); EmitLabel(".continue"); } AddrClip("esi"); e("mov edx, [fetch]\n"); e(".find_fetch_loop:\n"); e("add edx, byte "SIZEOF_FETCHREGION"\n"); e("cmp dword [edx], byte -1\n"); /* limit=-1? end. */ e("je short .not_found\n"); e("cmp esi, [edx]\n"); /* offset 0: base. above it? */ e("jb short .find_fetch_loop\n"); /* nope, not this region */ e("cmp esi, [edx+"OFFSET_FETCH_LIMIT"]\n"); /* limit. below it? */ e("ja short .find_fetch_loop\n"); /* nope, not this region */ e("mov ebp, [edx+"OFFSET_FETCH_PTR"]\n"); /* ebp=base ptr */ e("add esi, ebp\n"); /* +pc=pc ptr.. */ e("mov eax, esi\n"); e("pop ebp\n"); e("pop esi\n"); e("pop edx\n"); e("pop ebx\n"); e("ret\n"); EmitLabel(".not_found"); e("pop ebp\n"); e("pop esi\n"); e("pop edx\n"); e("pop ebx\n"); e("xor eax, eax\n"); e("ret\n"); /* Turbo68KReadByte() */ Align(4); EmitGlobalLabel("Turbo68KReadByte"); e("push ebx\n"); e("push ecx\n"); e("push edx\n"); e("push esi\n"); e("push edi\n"); e("push ebp\n"); e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ e("mov esi, [pc]\n"); if (multiaddr) { e("test byte [sr+1], 0x20\n"); /* set proper address space */ e("jz short .user\n"); SetSupervisorAddressSpace(); e("jmp short .continue\n"); EmitLabel(".user"); SetUserAddressSpace(); EmitLabel(".continue"); } GetArg("ebx", 0, 28); ReadByte(); e("mov eax, edx\n"); if (mmx) e("emms\n"); e("pop ebp\n"); e("pop edi\n"); e("pop esi\n"); e("pop edx\n"); e("pop ecx\n"); e("pop ebx\n"); e("ret\n"); /* Turbo68KReadWord() */ Align(4); EmitGlobalLabel("Turbo68KReadWord"); e("push ebx\n"); e("push ecx\n"); e("push edx\n"); e("push esi\n"); e("push edi\n"); e("push ebp\n"); e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ e("mov esi, [pc]\n"); if (multiaddr) { e("test byte [sr+1], 0x20\n"); /* set proper address space */ e("jz short .user\n"); SetSupervisorAddressSpace(); e("jmp short .continue\n"); EmitLabel(".user"); SetUserAddressSpace(); EmitLabel(".continue"); } GetArg("ebx", 0, 28); ReadWord(); e("mov eax, edx\n"); if (mmx) e("emms\n"); e("pop ebp\n"); e("pop edi\n"); e("pop esi\n"); e("pop edx\n"); e("pop ecx\n"); e("pop ebx\n"); e("ret\n"); /* Turbo68KReadLong() */ Align(4); EmitGlobalLabel("Turbo68KReadLong"); e("push ebx\n"); e("push ecx\n"); e("push edx\n"); e("push esi\n"); e("push edi\n"); e("push ebp\n"); e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ e("mov esi, [pc]\n"); if (multiaddr) { e("test byte [sr+1], 0x20\n"); /* set proper address space */ e("jz short .user\n"); SetSupervisorAddressSpace(); e("jmp short .continue\n"); EmitLabel(".user"); SetUserAddressSpace(); EmitLabel(".continue"); } GetArg("ebx", 0, 28); ReadLong(); e("mov eax, edx\n"); if (mmx) e("emms\n"); e("pop ebp\n"); e("pop edi\n"); e("pop esi\n"); e("pop edx\n"); e("pop ecx\n"); e("pop ebx\n"); e("ret\n"); /* Turbo68KWriteByte() */ Align(4); EmitGlobalLabel("Turbo68KWriteByte"); e("pusha\n"); e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ e("mov esi, [pc]\n"); if (multiaddr) { e("test byte [sr+1], 0x20\n"); /* set proper address space */ e("jz short .user\n"); SetSupervisorAddressSpace(); e("jmp short .continue\n"); EmitLabel(".user"); SetUserAddressSpace(); EmitLabel(".continue"); } GetArg("ebx", 0, 36); GetArg("edx", 1, 40); WriteByte(); if (mmx) e("emms\n"); e("popa\n"); e("ret\n"); /* Turbo68KWriteWord() */ Align(4); EmitGlobalLabel("Turbo68KWriteWord"); e("pusha\n"); e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ e("mov esi, [pc]\n"); if (multiaddr) { e("test byte [sr+1], 0x20\n"); /* set proper address space */ e("jz short .user\n"); SetSupervisorAddressSpace(); e("jmp short .continue\n"); EmitLabel(".user"); SetUserAddressSpace(); EmitLabel(".continue"); } GetArg("ebx", 0, 36); GetArg("edx", 1, 40); WriteWord(); if (mmx) e("emms\n"); e("popa\n"); e("ret\n"); /* Turbo68KWriteLong() */ Align(4); EmitGlobalLabel("Turbo68KWriteLong"); e("pusha\n"); e("xor ebp, ebp\n"); /* so PC doesn't get trashed */ e("mov esi, [pc]\n"); if (multiaddr) { e("test byte [sr+1], 0x20\n"); /* set proper address space */ e("jz short .user\n"); SetSupervisorAddressSpace(); e("jmp short .continue\n"); EmitLabel(".user"); SetUserAddressSpace(); EmitLabel(".continue"); } GetArg("ebx", 0, 36); GetArg("edx", 1, 40); WriteLong(); if (mmx) e("emms\n"); e("popa\n"); e("ret\n"); /* Turbo68KSet/GetContext() */ /* * Notice the trick here, all of the memory map pointers have 16 * subtracted from them, this helps optimize the read/write handlers * since an "add edi, 16" is possible straight off. This was NB's * idea. The fetch area is also handled this way */ Align(4); EmitGlobalLabel("Turbo68KSetContext"); e("push ecx\n"); e("push esi\n"); e("push edi\n"); GetArg("esi", 0, 16); e("mov edi, %s%s\n", id, NameOfContext()); if (mmx && !(SizeOfContext() & 7)) { e("mov ecx, " SIZEOF_CONTEXT " / 8\n"); EmitLabel(".l"); e("movq mm6, [esi]\n"); e("movq [edi], mm6\n"); e("add esi, byte 8\n"); e("add edi, byte 8\n"); e("dec ecx\n"); e("jnz short .l\n"); e("emms\n"); } else { e("mov ecx, " SIZEOF_CONTEXT " / 4\n"); e("cld\n"); e("rep movsd\n"); } e("sub dword [fetch], "SIZEOF_FETCHREGION"\n"); e("sub dword [pcfetch], "SIZEOF_FETCHREGION"\n"); e("sub dword [super_fetch], "SIZEOF_FETCHREGION"\n"); e("sub dword [super_pcfetch], "SIZEOF_FETCHREGION"\n"); e("sub dword [user_fetch], "SIZEOF_FETCHREGION"\n"); e("sub dword [user_pcfetch], "SIZEOF_FETCHREGION"\n"); if (memmap_type == 0) { e("sub dword [read_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [read_word], "SIZEOF_DATAREGION"\n"); e("sub dword [read_long], "SIZEOF_DATAREGION"\n"); e("sub dword [write_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [write_word], "SIZEOF_DATAREGION"\n"); e("sub dword [write_long], "SIZEOF_DATAREGION"\n"); e("sub dword [super_read_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [super_read_word], "SIZEOF_DATAREGION"\n"); e("sub dword [super_read_long], "SIZEOF_DATAREGION"\n"); e("sub dword [super_write_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [super_write_word], "SIZEOF_DATAREGION"\n"); e("sub dword [super_write_long], "SIZEOF_DATAREGION"\n"); e("sub dword [user_read_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [user_read_word], "SIZEOF_DATAREGION"\n"); e("sub dword [user_read_long], "SIZEOF_DATAREGION"\n"); e("sub dword [user_write_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [user_write_word], "SIZEOF_DATAREGION"\n"); e("sub dword [user_write_long], "SIZEOF_DATAREGION"\n"); } e("pop edi\n"); e("pop esi\n"); e("pop ecx\n"); e("ret\n"); Align(4); EmitGlobalLabel("Turbo68KGetContext"); e("push ecx\n"); e("push esi\n"); e("push edi\n"); e("add dword [fetch], "SIZEOF_FETCHREGION"\n"); e("add dword [pcfetch], "SIZEOF_FETCHREGION"\n"); e("add dword [super_fetch], "SIZEOF_FETCHREGION"\n"); e("add dword [super_pcfetch], "SIZEOF_FETCHREGION"\n"); e("add dword [user_fetch], "SIZEOF_FETCHREGION"\n"); e("add dword [user_pcfetch], "SIZEOF_FETCHREGION"\n"); if (memmap_type == 0) { e("add dword [read_byte], "SIZEOF_DATAREGION"\n"); e("add dword [read_word], "SIZEOF_DATAREGION"\n"); e("add dword [read_long], "SIZEOF_DATAREGION"\n"); e("add dword [write_byte], "SIZEOF_DATAREGION"\n"); e("add dword [write_word], "SIZEOF_DATAREGION"\n"); e("add dword [write_long], "SIZEOF_DATAREGION"\n"); e("add dword [super_read_byte], "SIZEOF_DATAREGION"\n"); e("add dword [super_read_word], "SIZEOF_DATAREGION"\n"); e("add dword [super_read_long], "SIZEOF_DATAREGION"\n"); e("add dword [super_write_byte], "SIZEOF_DATAREGION"\n"); e("add dword [super_write_word], "SIZEOF_DATAREGION"\n"); e("add dword [super_write_long], "SIZEOF_DATAREGION"\n"); e("add dword [user_read_byte], "SIZEOF_DATAREGION"\n"); e("add dword [user_read_word], "SIZEOF_DATAREGION"\n"); e("add dword [user_read_long], "SIZEOF_DATAREGION"\n"); e("add dword [user_write_byte], "SIZEOF_DATAREGION"\n"); e("add dword [user_write_word], "SIZEOF_DATAREGION"\n"); e("add dword [user_write_long], "SIZEOF_DATAREGION"\n"); } GetArg("edi", 0, 16); e("mov esi, %s%s\n", id, NameOfContext()); if (mmx && !(SizeOfContext() & 7)) { e("mov ecx, " SIZEOF_CONTEXT " / 8\n"); EmitLabel(".l"); e("movq mm6, [esi]\n"); e("movq [edi], mm6\n"); e("add esi, byte 8\n"); e("add edi, byte 8\n"); e("dec ecx\n"); e("jnz short .l\n"); e("emms\n"); } else { e("mov ecx, " SIZEOF_CONTEXT " / 4\n"); e("cld\n"); e("rep movsd\n"); } e("sub dword [fetch], "SIZEOF_FETCHREGION"\n"); e("sub dword [pcfetch], "SIZEOF_FETCHREGION"\n"); e("sub dword [super_fetch], "SIZEOF_FETCHREGION"\n"); e("sub dword [super_pcfetch], "SIZEOF_FETCHREGION"\n"); e("sub dword [user_fetch], "SIZEOF_FETCHREGION"\n"); e("sub dword [user_pcfetch], "SIZEOF_FETCHREGION"\n"); if (memmap_type == 0) { e("sub dword [read_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [read_word], "SIZEOF_DATAREGION"\n"); e("sub dword [read_long], "SIZEOF_DATAREGION"\n"); e("sub dword [write_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [write_word], "SIZEOF_DATAREGION"\n"); e("sub dword [write_long], "SIZEOF_DATAREGION"\n"); e("sub dword [super_read_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [super_read_word], "SIZEOF_DATAREGION"\n"); e("sub dword [super_read_long], "SIZEOF_DATAREGION"\n"); e("sub dword [super_write_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [super_write_word], "SIZEOF_DATAREGION"\n"); e("sub dword [super_write_long], "SIZEOF_DATAREGION"\n"); e("sub dword [user_read_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [user_read_word], "SIZEOF_DATAREGION"\n"); e("sub dword [user_read_long], "SIZEOF_DATAREGION"\n"); e("sub dword [user_write_byte], "SIZEOF_DATAREGION"\n"); e("sub dword [user_write_word], "SIZEOF_DATAREGION"\n"); e("sub dword [user_write_long], "SIZEOF_DATAREGION"\n"); } e("pop edi\n"); e("pop esi\n"); e("pop ecx\n"); e("ret\n"); /* Turbo68KGetContextSize() */ EmitGlobalLabel("Turbo68KGetContextSize"); e("mov eax, "SIZEOF_CONTEXT"\n"); e("ret\n"); /* Turbo68KClearCycles */ EmitGlobalLabel("Turbo68KClearCycles"); e("mov dword [cycles], 0\n"); e("mov dword [remaining], 0\n"); e("ret\n"); /* Turbo68KFreeTimeSlice */ EmitGlobalLabel("Turbo68KFreeTimeSlice"); e("push eax\n"); e("mov eax, [cycles]\n"); e("sub eax, [remaining]\n"); e("mov [cycles], eax\n"); e("mov dword [remaining], 0\n"); e("pop eax\n"); e("ret\n"); /* Turbo68KGetElapsedCycles */ Align(4); EmitGlobalLabel("Turbo68KGetElapsedCycles"); e("mov eax, [cycles]\n"); e("sub eax, [remaining]\n"); e("ret\n"); /* Turbo68KProcessInterrupts() */ Align(4); EmitGlobalLabel("Turbo68KProcessInterrupts"); e("pusha\n"); e("test byte [status], 1\n"); e("jnz near .end\n"); /* does not work while running */ e("mov ecx, [remaining]\n"); EMULATING(); /* running=1 */ e("mov esi, [pc]\n"); /* fetch PC */ UpdateFetchPtr(); e("call ProcessInterrupts\n"); STOP_RUNNING(); /* save PC and cycles remaining */ STOP_EMULATING(); EmitLabel(".end"); if (mmx) e("emms\n"); e("popa\n"); e("xor eax, eax\n"); e("ret\n"); /* Turbo68KInterrupt() */ /* * Note: This function assumes that TURBO68K_AUTOVECTOR == 256, make * sure the definition in TURBO68K.H reflects this * * If 68K is halted (STOP instruction), if an interrupt with a priority * equal to or lower than the SR priority is requested, is the interrupt * made pending or discarded? (ask) Are there any situations when invalid- * priority interrupts are discarded completely? */ Align(4); EmitGlobalLabel("Turbo68KInterrupt"); e("push\tebx\n"); e("push\tecx\n"); e("push\tedx\n"); e("push\tesi\n"); e("push\tedi\n"); GetArg("eax", 0, 24); e("test eax, eax\n"); /* 0 is not a valid level */ e("jz short .inv_level\n"); e("cmp eax, byte 7\n"); /* if greater than 7, invalid level */ e("ja short .inv_level\n"); GetArg("ebx", 1, 28); /* get vector */ e("cmp ebx, byte 2\n"); e("jb short .inv_vector\n"); e("cmp ebx, 256\n"); e("ja short .inv_vector\n"); e("jne short .not_auto\n"); e("mov ebx, eax\n"); e("add ebx, byte 24\n"); EmitLabel(".not_auto"); e("dec eax\n"); /* (level-1)*4=offset into intr[] */ e("shl eax, byte 2\n"); e("cmp [intr+eax], byte 0\n"); e("jne short .pending\n"); /* already pending */ e("mov [intr+eax], ebx\n"); /* store vector into intr[] */ e("inc dword [intr+7*4]\n"); /* one interrupt added */ e("pop\tedi\n"); e("pop\tesi\n"); e("pop\tedx\n"); e("pop\tecx\n"); e("pop\tebx\n"); e("xor eax, eax\n"); e("ret\n"); EmitLabel(".inv_level"); e("mov eax, %d\n", TURBO68K_ERROR_INTLEVEL); e("pop\tedi\n"); e("pop\tesi\n"); e("pop\tedx\n"); e("pop\tecx\n"); e("pop\tebx\n"); e("ret\n"); EmitLabel(".inv_vector"); e("mov eax, %d\n", TURBO68K_ERROR_INTVECTOR); e("pop\tedi\n"); e("pop\tesi\n"); e("pop\tedx\n"); e("pop\tecx\n"); e("pop\tebx\n"); e("ret\n"); EmitLabel(".pending"); e("mov eax, %d\n", TURBO68K_ERROR_INTPENDING); e("pop\tedi\n"); e("pop\tesi\n"); e("pop\tedx\n"); e("pop\tecx\n"); e("pop\tebx\n"); e("ret\n"); /* Turbo68KCancelInterrupt */ EmitGlobalLabel("Turbo68KCancelInterrupt"); e("push\tebx\n"); e("push\tecx\n"); e("push\tedx\n"); e("push\tesi\n"); e("push\tedi\n"); GetArg("eax", 0, 24); e("test eax, eax\n"); /* 0 is not a valid level */ e("jz short .inv_level\n"); e("cmp eax, byte 7\n"); /* if greater than 7, invalid level */ e("ja short .inv_level\n"); e("test byte [status], 0x04\n"); /* processing interrupts? */ e("jnz short .busy\n"); /* yep... can't do this */ e("dec eax\n"); /* make index into intr[] */ e("cmp dword [intr+eax*4], byte 0\n"); e("je .nothing_to_cancel\n"); e("mov dword [intr+eax*4], 0\n"); e("dec dword [intr+7*4]\n"); /* removed 1 interrupt */ EmitLabel(".busy"); EmitLabel(".nothing_to_cancel"); e("pop\tedi\n"); e("pop\tesi\n"); e("pop\tedx\n"); e("pop\tecx\n"); e("pop\tebx\n"); e("xor eax, eax\n"); e("ret\n"); EmitLabel(".inv_level"); e("pop\tedi\n"); e("pop\tesi\n"); e("pop\tedx\n"); e("pop\tecx\n"); e("pop\tebx\n"); e("mov eax, %d\n", TURBO68K_ERROR_INTLEVEL); e("ret\n"); /* * ProcessInterrupts: Processes any pending interrupts if allowed. * Assumes all registers have been set up as per Turbo68KRun */ Align(4); EmitLabel("ProcessInterrupts"); INTERRUPT_PROCESSING(); e("cmp dword [intr+7*4], byte 0\n"); e("je near .no_int_end\n"); /* no interrupts pending */ e("push eax\n"); /* EAX=flags, save it! */ e("mov al, [sr+1]\n"); e("and eax, byte 7\n"); /* EAX=interrupt priority */ e("cmp al, 7\n"); e("jne short .no_int7\n"); e("mov edi, intr+6*4\n"); e("jmp short .l\n"); EmitLabel(".no_int7"); e("mov edi, eax\n"); e("shl edi, byte 2\n"); /* *4, index into intr[] */ e("add edi, intr\n"); /* EDI=interrupt queue */ e("inc al\n"); /* new SR priority mask */ EmitLabel(".l"); e("cmp dword [edi], byte 0\n"); e("je near .skip\n"); /* no interrupt at this level */ e("test byte [sr+1], 0x20\n"); /* see if in User mode */ e("jnz short .no_sp_magic\n"); /* nope, don't need to swap SPs */ e("mov edx, [__sp]\n"); e("xchg [a+7*4], edx\n"); e("mov [__sp], edx\n"); SetSupervisorAddressSpace(); EmitLabel(".no_sp_magic"); e("mov ebx, [a+7*4]\n"); /* SP */ /* * If 68010, we have to push the format word on the stack. */ if (mpu == 68010) { e("mov edx, [edi]\n"); /* vector number */ e("sub ebx, byte 2\n");/* SP-2 */ e("shl edx, byte 2\n");/* vector*4 for offset */ e("push ebx\n"); e("push edi\n"); WriteWord(); e("pop edi\n"); e("pop ebx\n"); } e("mov edx, [pc]\n"); /* get old PC */ e("sub ebx, byte 4\n"); /* SP-4 */ e("push ebx\n"); e("push edi\n"); WriteLong(); e("pop edi\n"); e("pop ebx\n"); e("mov edx, [sr]\n"); /* get SR */ e("sub ebx, byte 2\n"); /* SP-2 */ e("push edi\n"); e("push ebx\n"); WriteWord(); /* save SR to stack */ e("pop ebx\n"); e("pop edi\n"); e("mov dh, al\n"); /* new interrupt priority bits */ e("or dh, 0x20\n"); /* set supervisor for exception */ e("and edx, 0xa71f\n"); /* clear unwanted bits */ e("mov [sr], edx\n"); /* set new SR */ e("mov [a+7*4], ebx\n"); /* write back SP */ e("mov ebx, [edi]\n"); /* vector of interrupt */ e("test dword [InterruptAcknowledge], -1\n"); /* interrupt call-back */ e("jz short .no_interrupt_ack_handler\n"); e("pushad\n"); e("push ebx\n"); e("call dword [InterruptAcknowledge]\n"); e("add esp, byte 4\n"); e("popad\n"); e(".no_interrupt_ack_handler:\n"); e("shl ebx, byte 2\n"); /* vector *= 4 */ if (mpu == 68010) e("add ebx, [vbr]\n"); e("push edi\n"); ReadLong(); /* get PC */ e("pop edi\n"); e("mov [pc], edx\n"); /* set new PC */ e("mov dword [edi], 0\n"); /* done w/ this interrupt */ e("dec dword [intr+7*4]\n"); /* processed 1 interrupt... */ e("sub ecx, byte 44\n"); /* interrupts take 44 clocks */ UNSTOP_CPU(); EmitLabel(".skip"); e("add edi, byte 4\n"); e("inc al\n"); /* next entry */ e("cmp edi, intr+7*4\n"); e("jne near .l\n"); e("mov esi, [pc]\n"); e("pop eax\n"); /* get flags back */ UpdateFetchPtr(); EmitLabel(".no_int_end"); INTERRUPT_DONE(); e("ret\n"); /* Turbo68KRun() */ Align(4); EmitGlobalLabel("Turbo68KRun"); e("pusha\n"); GetArg("ecx", 0, 36); /* ECX = cycles */ e("mov [cycles], ecx\n"); e("mov [remaining], ecx\n"); e("test byte [sr+1], 0x20\n"); e("jz near .set_user_space\n"); SetSupervisorAddressSpace(); e("jmp near .continue\n"); EmitLabel(".set_user_space"); SetUserAddressSpace(); EmitLabel(".continue"); EMULATING(); /* running=1 */ e("mov esi, [pc]\n"); /* fetch PC */ UpdateFetchPtr(); LoadCCR(); /* get flags */ e("call ProcessInterrupts\n"); e("test byte [status], 2\n"); /* stopped? */ e("jnz short .stopped\n"); e("xor edi, edi\n"); e("mov di, [esi]\n"); /* next instruction */ e("add esi, byte 2\n"); e("jmp dword [jmptab+edi*4]\n"); /* jump... */ EmitLabel(".stopped"); e("xor ecx, ecx\n"); e("jmp Turbo68KRun_done\n"); Align(4); EmitLabel("Turbo68KRun_done"); SaveCCR(); /* save flags */ STOP_RUNNING(); /* save PC and cycles */ STOP_EMULATING(); if (mmx) e("emms\n"); e("popa\n"); e("xor eax, eax\n"); /* everything okay... */ e("ret\n"); /* * NOTE: Only the 68010 RTE handler can call this. PC is assumed to point * one word after the faulty RTE. */ if (mpu == 68010) { EmitLabel("stackframe_error"); e("sub esi, byte 2\n"); /* point at faulty RTE */ SaveCCR(); STOP_EMULATING(); STOP_RUNNING(); /* save PC and cycles remaining */ e("popa\n"); e("mov eax, %d\n", TURBO68K_ERROR_STACKFRAME); e("ret\n"); } if (memmap_type == 2) /* low level handling? don't generate the rest */ return; /* * ReadXXXXPC: EBX = address * Out: EDX = data; DL=byte, EDX=upper 32-bits trashed, DX=word, * EDX=long-word * EBX=address * Notes: EDI is trashed in the process, but cleared at the end. * These functions read from the *pcfetch areas. There are no * SX functions, this is handled by the emitter. I did this to * save space. */ if (pcfetch) { for (i = 0; i < 3; i++) /* 0=byte, 1=word, 2=dword */ { CacheAlign(); switch (i) { case 0: EmitLabel("ReadBytePC"); break; case 1: EmitLabel("ReadWordPC"); break; case 2: EmitLabel("ReadLongPC"); break; } e("mov edi, [pcfetch]\n"); SaveReg("memhandler", "ebx"); /* save address */ AddrClip("ebx"); EmitLabel(".loop"); e("add edi, byte "SIZEOF_FETCHREGION"\n"); e("cmp dword [edi], byte -1\n"); /* base=-1? end. */ e("je short .not_found\n"); e("cmp ebx, [edi]\n"); /* offset 0: base. above it? */ e("jb short .loop\n"); /* nope, not this region */ e("cmp ebx, [edi+"OFFSET_FETCH_LIMIT"]\n"); /* limit. below it? */ e("ja short .loop\n"); /* nope, not this region */ e("mov edx, ebx\n"); if (!i) /* byte accesses need this because buffer is swapped */ e("xor dl, 1\n"); e("add edx, [edi+"OFFSET_FETCH_PTR"]\n"); /* into ptr.. */ if (!i) /* byte */ e("mov dl, [edx]\n"); /* fetch */ else if (i == 1) /* word */ e("mov edx, [edx]\n"); else /* long */ { e("mov edx, [edx]\n"); e("rol edx, byte 16\n"); /* swap words */ } e("xor edi, edi\n"); RestoreReg("memhandler", "ebx"); e("ret\n"); Align(4); EmitLabel(".not_found"); e("xor edi, edi\n"); e("xor edx, edx\n"); e("dec edx\n"); /* not found, return all 1s */ RestoreReg("memhandler", "ebx"); e("ret\n"); } } /* * ReadXXXX: EBX = address * Out: EDX = data; DL=byte, EDX=upper 32-bits trashed, DX=word, * EDX=long-word * EBX=address * Notes: EDI is trashed in the process, but cleared at the end. * This applies to all ReadXXX and WriteXXX handlers, some * instructions (NEGX) rely on this behavior. */ if (memmap_type == 0) /* default memory mapping system */ { for (i = 0; i < 3; i++) /* 0=byte, 1=word, 2=dword */ { CacheAlign(); switch (i) { case 0: EmitLabel("ReadByte"); e("mov edi, [read_byte]\n"); break; case 1: EmitLabel("ReadWord"); e("mov edi, [read_word]\n"); break; case 2: EmitLabel("ReadLong"); e("mov edi, [read_long]\n"); break; } SaveReg("memhandler", "ebx"); /* save address */ AddrClip("ebx"); EmitLabel(".loop"); e("add edi, byte "SIZEOF_DATAREGION"\n"); e("cmp dword [edi], byte -1\n"); /* base=-1? end. */ e("je short .not_found\n"); e("cmp ebx, [edi]\n"); /* offset 0: base. above it? */ e("jb short .loop\n"); /* nope, not this region */ e("cmp ebx, [edi+"OFFSET_DATA_LIMIT"]\n"); /* limit. below it? */ e("ja short .loop\n"); /* nope, not this region */ e("cmp dword [edi+"OFFSET_DATA_PTR"], byte 0\n"); e("je short .read_from_handler\n"); e("mov edx, ebx\n"); if (!i) /* byte accesses need this because buffer is swapped */ e("xor dl, 1\n"); e("add edx, [edi+"OFFSET_DATA_PTR"]\n"); /* into ptr.. */ if (!i) /* byte */ e("mov dl, [edx]\n"); /* fetch */ else if (i == 1) /* word */ e("mov edx, [edx]\n"); else /* long */ { e("mov edx, [edx]\n"); e("rol edx, byte 16\n"); /* swap words */ } e("xor edi, edi\n"); RestoreReg("memhandler", "ebx"); e("ret\n"); Align(4); EmitLabel(".not_found"); e("xor edi, edi\n"); e("xor edx, edx\n"); e("dec edx\n"); /* not found, return all 1s */ RestoreReg("memhandler", "ebx"); e("ret\n"); Align(4); EmitLabel(".read_from_handler"); SaveReg("memhandler", "eax"); SaveReg("memhandler", "ebp"); WRITEPCTOMEM("pc"); /* saves esi */ e("mov [remaining], ecx\n"); /* address */ if (!call_convention) /* stack-based */ e("push ebx\n"); else /* register-based */ e("mov eax, ebx\n"); e("call dword [edi+"OFFSET_DATA_HANDLER"]\n"); if (!call_convention) e("add esp, byte 4\n"); e("mov edx, eax\n"); /* get the data */ e("mov esi, [pc]\n"); /* handler could have changed PC */ e("mov ecx, [remaining]\n");/* this could have been changed */ RestoreReg("memhandler", "eax"); RestoreReg("memhandler", "ebp"); e("add esi, ebp\n"); /* base+pc=pc pointer */ e("xor edi, edi\n"); /* keep clear for fetch */ RestoreReg("memhandler", "ebx"); e("ret\n"); } } else if (memmap_type == 1) /* high level handler */ { for (i = 0; i < 3; i++) { CacheAlign(); switch (i) { case 0: EmitLabel("ReadByte"); break; case 1: EmitLabel("ReadWord"); break; case 2: EmitLabel("ReadLong"); break; } SaveReg("memhandler", "ebx"); /* save address */ AddrClip("ebx"); SaveReg("memhandler", "eax"); SaveReg("memhandler", "ebp"); WRITEPCTOMEM("pc"); /* saves esi */ e("mov [remaining], ecx\n"); /* address */ if (!call_convention) /* stack-based */ e("push ebx\n"); else /* register-based */ e("mov eax, ebx\n"); switch (i) { case 0: e("call dword [read_byte]\n"); break; case 1: e("call dword [read_word]\n"); break; case 2: e("call dword [read_long]\n"); break; } if (!call_convention) e("add esp, byte 4\n"); e("mov edx, eax\n"); /* get the data */ e("mov esi, [pc]\n"); /* handler could have changed PC */ e("mov ecx, [remaining]\n");/* this could have been changed */ RestoreReg("memhandler", "eax"); RestoreReg("memhandler", "ebp"); e("add esi, ebp\n"); /* base+pc=pc pointer */ e("xor edi, edi\n"); /* keep clear for fetch */ RestoreReg("memhandler", "ebx"); e("ret\n"); } } if (memmap_type == 0) { /* * ReadWordSX is the same as ReadWord except that it sign extends the * word to 32 bits. */ CacheAlign(); EmitLabel("ReadWordSX"); e("mov edi, [read_word]\n"); SaveReg("memhandler", "ebx"); /* save address */ AddrClip("ebx"); EmitLabel(".loop"); e("add edi, byte "SIZEOF_DATAREGION"\n"); e("cmp dword [edi], byte -1\n"); e("je short .not_found\n"); e("cmp ebx, [edi]\n"); e("jb short .loop\n"); e("cmp ebx, [edi+"OFFSET_DATA_LIMIT"]\n"); e("ja short .loop\n"); e("cmp dword [edi+"OFFSET_DATA_PTR"], byte 0\n"); e("je short .read_from_handler\n"); e("mov edx, ebx\n"); e("add edx, [edi+"OFFSET_DATA_PTR"]\n"); e("movsx edx, word [edx]\n"); /* sign extend! */ e("xor edi, edi\n"); RestoreReg("memhandler", "ebx"); e("ret\n"); Align(4); EmitLabel(".not_found"); e("xor edi, edi\n"); e("xor edx, edx\n"); e("dec edx\n"); /* not found, return all 1s */ RestoreReg("memhandler", "ebx"); e("ret\n"); Align(4); EmitLabel(".read_from_handler"); SaveReg("memhandler", "eax"); SaveReg("memhandler", "ebp"); WRITEPCTOMEM("pc"); e("mov [remaining], ecx\n"); /* address */ if (!call_convention) /* stack-based */ e("push ebx\n"); else /* register-based */ e("mov eax, ebx\n"); e("call dword [edi+"OFFSET_DATA_HANDLER"]\n"); if (!call_convention) e("add esp, byte 4\n"); e("movsx edx, ax\n"); /* sign extend! */ e("mov esi, [pc]\n"); e("mov ecx, [remaining]\n"); RestoreReg("memhandler", "eax"); RestoreReg("memhandler", "ebp"); e("add esi, ebp\n"); e("xor edi, edi\n"); RestoreReg("memhandler", "ebx"); e("ret\n"); } else if (memmap_type == 1) { CacheAlign(); EmitLabel("ReadWordSX"); SaveReg("memhandler", "ebx"); /* save address */ AddrClip("ebx"); SaveReg("memhandler", "eax"); SaveReg("memhandler", "ebp"); WRITEPCTOMEM("pc"); /* saves esi */ e("mov [remaining], ecx\n"); /* address */ if (!call_convention) /* stack-based */ e("push ebx\n"); else /* register-based */ e("mov eax, ebx\n"); e("call dword [read_word]\n"); if (!call_convention) e("add esp, byte 4\n"); e("movsx edx, ax\n"); /* sign extend */ e("mov esi, [pc]\n"); /* handler could have changed PC */ e("mov ecx, [remaining]\n");/* this could have been changed */ RestoreReg("memhandler", "eax"); RestoreReg("memhandler", "ebp"); e("add esi, ebp\n"); /* base+pc=pc pointer */ e("xor edi, edi\n"); /* keep clear for fetch */ RestoreReg("memhandler", "ebx"); e("ret\n"); } /* * WriteXXXX: EBX = address * In: EDX = data; DL=byte, EDX{DX}=word, * EDX=long-word * Out: EBX=trashed, EDX=preserved * Notes: EDI is trashed in the process, but cleared at the end */ if (memmap_type == 0) { for (i = 0; i < 3; i++) /* 0=byte, 1=word, 2=dword */ { CacheAlign(); switch (i) { case 0: EmitLabel("WriteByte"); e("mov edi, [write_byte]\n"); break; case 1: EmitLabel("WriteWord"); e("mov edi, [write_word]\n"); break; case 2: EmitLabel("WriteLong"); e("mov edi, [write_long]\n"); break; } AddrClip("ebx"); EmitLabel(".loop"); e("add edi, byte "SIZEOF_DATAREGION"\n"); e("cmp dword [edi], byte -1\n"); /* base=-1? end. */ e("je short .not_found\n"); e("cmp ebx, [edi]\n"); /* offset 0: base. above it? */ e("jb short .loop\n"); /* nope, not this region */ e("cmp ebx, [edi+"OFFSET_DATA_LIMIT"]\n"); /* limit. below it? */ e("ja short .loop\n"); /* nope, not this region */ e("cmp dword [edi+"OFFSET_DATA_PTR"], byte 0\n"); e("je short .write_with_handler\n"); if (!i) /* byte accesses need this because buffer is swapped */ e("xor bl, 1\n"); e("add ebx, [edi+"OFFSET_DATA_PTR"]\n"); /* into ptr.. */ if (!i) /* byte */ e("mov [ebx], dl\n"); /* write */ else if (i == 1) /* word */ e("mov [ebx], dx\n"); else /* long */ { e("ror edx, byte 16\n"); /* swap words */ e("mov [ebx], edx\n"); e("ror edx, byte 16\n"); /* reswap back */ } EmitLabel(".not_found"); e("xor edi, edi\n"); RestoreReg("memhandler", "ebx"); e("ret\n"); Align(4); EmitLabel(".write_with_handler"); SaveReg("memhandler", "eax"); SaveReg("memhandler", "ebp"); WRITEPCTOMEM("pc"); /* saves esi */ e("mov [remaining], ecx\n"); SaveReg("memhandler", "edx"); /* save params */ /* data, address */ if (!call_convention) /* stack-based */ { e("push edx\n"); e("push ebx\n"); } else /* register-based */ { e("mov eax, ebx\n"); /* don't need: e("mov edx, edx\n"); */ } e("call dword [edi+"OFFSET_DATA_HANDLER"]\n"); if (!call_convention) e("add esp, byte 8\n"); RestoreReg("memhandler", "edx"); /* restore params */ e("mov esi, [pc]\n"); e("mov ecx, [remaining]\n"); /* this could have been changed */ RestoreReg("memhandler", "eax"); RestoreReg("memhandler", "ebp"); e("add esi, ebp\n"); /* base+pc=pc pointer */ e("xor edi, edi\n"); /* keep clear for fetch */ e("ret\n"); } } else if (memmap_type == 1) { for (i = 0; i < 3; i++) { CacheAlign(); switch (i) { case 0: EmitLabel("WriteByte"); break; case 1: EmitLabel("WriteWord"); break; case 2: EmitLabel("WriteLong"); break; } AddrClip("ebx"); SaveReg("memhandler", "eax"); SaveReg("memhandler", "ebp"); WRITEPCTOMEM("pc"); /* saves esi */ e("mov [remaining], ecx\n"); SaveReg("memhandler", "edx"); /* save params */ /* data, address */ if (!call_convention) /* stack-based */ { e("push edx\n"); e("push ebx\n"); } else /* register-based */ { e("mov eax, ebx\n"); /* don't need: e("mov edx, edx\n"); */ } switch (i) { case 0: e("call dword [write_byte]\n"); break; case 1: e("call dword [write_word]\n"); break; case 2: e("call dword [write_long]\n"); break; } if (!call_convention) e("add esp, byte 8\n"); RestoreReg("memhandler", "edx"); /* restore params */ e("mov esi, [pc]\n"); e("mov ecx, [remaining]\n"); /* this could have been changed */ RestoreReg("memhandler", "eax"); RestoreReg("memhandler", "ebp"); e("add esi, ebp\n"); /* base+pc=pc pointer */ e("xor edi, edi\n"); /* keep clear for fetch */ e("ret\n"); } } } /***************************************************************************** * main() and Friends */ int FindV(char *option, int p, int h, int u, int argc, char **argv, int m) { static int t[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int i; char *c; if (m) /* mark as touched */ { if (m < 128) t[m] = 1; return 0; } if (argc > 128) argc = 128; /* maximum this function can handle is 128 */ if (u) /* find first untouched element */ { for (i = 1; i < argc; i++) { if (!t[i]) /* 0 indicates untouched */ return i; } return 0; } if (p) /* find option and return integer value following it */ { for (i = 1; i < argc; i++) { if (strcmp(argv[i], option) == 0) /* found */ { if (i >= (argc - 1)) /* bounds! */ return 0; t[i + 1] = t[i] = 1; /* touched */ if (!h) return atoi(argv[i + 1]); else return strtoul(argv[i + 1], &c, 16); } } return 0; /* no match */ } else /* find option and return position */ { for (i = 1; i < argc; i++) { if (strcmp(argv[i], option) == 0) { t[i] = 1; return i; /* found! return position */ } } return 0; } return 0; } void ShowHelp() { printf("Make68K Version "VERSION" by Bart Trzynadlowski: Turbo68K Source Emitter\n"); printf("Usage: make68k [options]\n"); printf("Options: -?,-h Show this help text\n"); printf(" -mpu Processor to emulate [Default=68000]\n"); printf(" -addr Address bus width [Default=24]\n"); printf(" -illegal Emulate illegal instruction exceptions [Default]\n"); printf(" -noillegal Treat illegal instructions as errors\n"); printf(" -dummyread Emulate 68000 dummy reads [Default]\n"); printf(" -nodummyread Do not emulate dummy reads\n"); printf(" -skip Skip over idle loops\n"); printf(" -noskip Do not skip over idle loops [Default]\n"); printf(" -brafetch Update fetch pointer on Bcc, BRA, BSR, DBcc\n"); printf(" -nobrafetch Do not update fetch pointer for branches [Default]\n"); printf(" -pcfetch Special PC-relative read mode\n"); printf(" -nopcfetch Use data regions for PC-relative reading [Default]\n"); printf(" -multiaddr Address spaces for supervisor and user [Default]\n"); printf(" -singleaddr Single address space for supervisor and user\n"); printf(" -defmap Definable memory map arrays [Default]\n"); printf(" -handler Single handlers for memory access\n"); printf(" -stackcall Stack calling conventions [Default]\n"); printf(" -regcall Register calling conventions\n"); printf(" -id Add string to beginning of identifiers\n"); exit(0); } void SetAddrMask(int num_bits) { addr_bits = num_bits; if (!num_bits) { addr_mask = 0xffffff; /* 24-bit is default address bus width */ addr_bits = 24; } else addr_mask = (unsigned) pow(2, num_bits) - 1; } int main(int argc, char **argv) { int i, j, k, l, f; #ifdef PROFILE for (i = 0; i < 512; i++) prof[i] = NULL; #endif if (argc <= 1 || FindV("-?", 0, 0, 0, argc, argv, 0) || FindV("-h", 0, 0, 0, argc, argv, 0)) ShowHelp(); mpu = FindV("-mpu", 1, 0, 0, argc, argv, 0); switch (mpu) /* make sure a valid MPU type was specified */ { case 0: mpu = 68000; break; case 68000: case 68010: break; default: fprintf(stderr, "Make68K: Unknown or unsupported processor type: %d\n", mpu); exit(1); } SetAddrMask(FindV("-addr", 1, 0, 0, argc, argv, 0)); if (FindV("-illegal", 0, 0, 0, argc, argv, 0)) illegal = 1; if (FindV("-noillegal", 0, 0, 0, argc, argv, 0)) illegal = 0; if (FindV("-dummyread", 0, 0, 0, argc, argv, 0)) dummyread = 1; if (FindV("-nodummyread", 0, 0, 0, argc, argv, 0)) dummyread = 0; if (FindV("-skip", 0, 0, 0, argc, argv, 0)) skip = 1; if (FindV("-noskip", 0, 0, 0, argc, argv, 0)) skip = 0; if (FindV("-brafetch", 0, 0, 0, argc, argv, 0)) brafetch = 1; if (FindV("-nobrafetch", 0, 0, 0, argc, argv, 0)) brafetch = 0; if (FindV("-pcfetch", 0, 0, 0, argc, argv, 0)) pcfetch = 1; if (FindV("-nopcfetch", 0, 0, 0, argc, argv, 0)) pcfetch = 0; if (FindV("-multiaddr", 0, 0, 0, argc, argv, 0)) multiaddr = 1; if (FindV("-singleaddr", 0, 0, 0, argc, argv, 0)) multiaddr = 0; if (FindV("-defmap", 0, 0, 0, argc, argv, 0)) memmap_type = 0; if (FindV("-handler", 0, 0, 0, argc, argv, 0)) memmap_type = 1; if (FindV("-stackcall", 0, 0, 0, argc, argv, 0)) call_convention = 0; if (FindV("-regcall", 0, 0, 0, argc, argv, 0)) call_convention = 1; if (FindV("-debug", 0, 0, 0, argc, argv, 0)) debug = 1; if ((i = FindV("-id", 0, 0, 0, argc, argv, 0))) { if ((i + 1) < argc) /* make sure we're within argv[] bounds */ { if (strlen(argv[i + 1]) > 16) { fprintf(stderr, "Make68K: Identifier string is too long (maximum is 16 characters)\n"); exit(1); } strncpy(id, argv[i + 1], 16); FindV(NULL, 0, 0, 0, argc, argv, i + 1); } } if (!(f = FindV(NULL, 0, 0, 1, argc, argv, 0))) { fprintf(stderr, "Make68K: No output file specified\n"); exit(1); } if ((fp = fopen(argv[f], "w")) == NULL) { fprintf(stderr, "Make68K: Failed to open file for writing: %s\n", argv[f]); exit(1); } for (i = 0; i < 65536; i++) decoded[i] = 0; printf("Make68K Version "VERSION" by Bart Trzynadlowski: Turbo68K Source Emitter\n"); printf("\n"); printf("Emitting file: %s\n", argv[f]); printf("Configuration:\n"); e(";\n"); e("; Turbo68K Version "VERSION": Motorola 680X0 emulator\n"); e("; Copyright 2000-2002 Bart Trzynadlowski, see \"README.TXT\" for terms of use\n"); e("; Assemble with NASM (http://www.web-sites.co.uk/nasm) only\n"); e(";\n"); e("; Configuration:\n"); printf("- %d processor\n", mpu); e("; - %d processor\n", mpu); printf("- %d-bit addresses\n", addr_bits); e("; - %d-bit addresses\n", addr_bits); printf("- %s\n", illegal ? "Illegal instruction, Line 1010, and Line 1111 exceptions" : "Illegal instructions reported as errors"); e("; - %s\n", illegal ? "Illegal instruction, Line 1010, and Line 1111 exceptions" : "Illegal instructions reported as errors"); if (mpu == 68000) { printf("- Dummy reads %s\n", dummyread ? "emulated" : "not emulated"); e("; - Dummy reads %s\n", dummyread ? "emulated" : "not emulated"); } printf("- Idle loop skipping %s\n", skip ? "enabled" : "disabled"); e("; - Idle loop skipping %s\n", skip ? "enabled" : "disabled"); printf("- Fetch pointer%supdated for Bcc, BRA, BSR, and DBcc\n", brafetch ? " " : " not "); e("; - Fetch pointer%supdated for Bcc, BRA, BSR, and DBcc\n", brafetch ? " " : " not "); printf("- %s regions used for PC-relative reading\n", pcfetch ? "Special PC fetch" : "Data"); e("; - %s regions used for PC-relative reading\n", pcfetch ? "Special PC fetch" : "Data"); printf("- %s for supervisor and user\n", multiaddr ? "Separate address spaces" : "Single address space"); e("; - %s for supervisor and user\n", multiaddr ? "Separate address spaces" : "Single address space"); switch (memmap_type) { case 0: printf("- Definable memory map arrays\n"); e("; - Definable memory map arrays\n"); break; case 1: printf("- Single memory handlers\n"); e("; - Single memory handlers\n"); break; case 2: printf("- Low-level memory handlers\n"); e("; - Low-level memory handlers\n"); break; } printf("- %s calling conventions\n", call_convention ? "Register" : "Stack"); e("; - %s calling conventions\n", call_convention ? "Register" : "Stack"); if (id[0] != '\0') { printf("- Identifers start with: %s\n", id); e("; - Identifiers start with: %s\n", id); } if (debug) { printf("- Debug function called at every instruction\n"); e("; - Debug function called at every instruction\n"); } e(";\n"); printf("\n"); e("bits 32\n"); e("section .data\n"); EmitData(); e("section .text\n"); EmitCode(); printf("Generating instruction handlers:\n"); EmitInstructions(); EmitExceptions(); e("section .bss\n"); CacheAlign(); e("jmptab resd 65536\n"); /* * Emit the compressed jump table * * Format: * dw 0x8000 + # ; # of handler addresses follow, each repeated 8 times * ... dd # ... ; handler addresses... * dw 0xc000 + # ; # of handler addresses follow, each repeated 1 time * ... dd # ... ; handler addresses... * dw 0x0000 + # ; repeat following address handler # times * dd # ; handler address to repeat * dd -1 ; terminator */ e("section .data\n"); Align(4); e("compressed_jmptab:\n"); i = 0; j = 0; while (i < 65536) { if (!decoded[i]) /* invalid */ { for (i = i; decoded[i] == 0 && i < 65536; i++) j++; e("dw 0x0000 + %d\n", j); e("dd I_Invalid\n"); j = 0; } else { if (decoded[i] == 8) /* if more handlers*8, make a big block */ { k = 0; l = i; /* save opcode # */ while (decoded[i] == 8) { i += decoded[i]; k++; } e("dw 0x8000 + %d\n", k); /* # of handlers w/ 8 reps */ while (k != 0) /* list handlers */ { e("dd I%04X\n", l); l += decoded[l]; k--; } } if (decoded[i] == 1) /* if more handlers*1, make a big block */ { k = 0; l = i; /* save opcode # */ while (decoded[i] == 1) { i += decoded[i]; k++; } e("dw 0xc000 + %d\n", k); /* # of handlers w/ 1 reps */ while (k != 0) /* list handlers */ { e("dd I%04X\n", l); l += decoded[l]; k--; } } else if (decoded[i] == 0) /* invalid instruction */ { e("dw 1\n"); e("dd I_Invalid\n"); i++; } else /* misc. */ { e("dw %d\n", decoded[i]); /* # of valids */ e("dd I%04X\n", i); /* opcode */ i += decoded[i]; } } } e("dd -1\n"); /* * Emit profile data * * Step 1: Output the names of the instructions. * Step 2: Output the usage count variables and pointers to the strings. */ #ifdef PROFILE for (i = 0; prof[i] != NULL && i < 512; i++) e("str%s db \"%s\",0\n", prof[i], prof[i]); e("global _t68k_prof\n"); e("_t68k_prof: dd begin_t68k_prof\n"); e("begin_t68k_prof:\n"); for (i = 0; prof[i] != NULL && i < 512; i++) { e("prof%s dd 0, str%s\n", prof[i], prof[i]); free(prof[i]); } e("dd -1, -1\n"); #endif fclose(fp); printf("Total:\t%d\n", num_handlers); printf("\nSee \"README.TXT\" for terms of use and documentation!\n"); return 0; } /* * * Developers' Notes: A Guide to the Guts of Turbo68K ;) * ----------------------------------------------------- * * Register Usage: * * EAX: ****************NZ*****C*******V * AH: NZ*****C AL: *******V * EBX: Address * ECX: Cycle counter * EDX: Data * ESI: Current fetch (PC) pointer * EDI: Instruction fetch/decoding (keep upper 16 bits clear!). At the * beginning of any instruction handler, contains opcode in lower 16 bits * EBP: Pointer to base of PC region. May be below the base if the unused PC * bits are non-zero * --- * MMX optimizations are unsupported -- DO NOT USE THEM! They happen to slow * things down * * MMX Register Usage: * * MM0: Saved EAX (memhandler_eax) * MM1: Saved EBX (memhandler_ebx) * MM2: Saved EBP (memhandler_ebp) * MM3: Saved EDX (run_edx) * MM4: Saved ESI (run_esi) * MM5: Saved EDI (run_edi) * MM6,MM7: Misc. usage * --- * Format of context.intr[]: * * intr[0-6] = Interrupt levels 1-7, elements contain the vector #, if 0 then * no interrupt is pending at this level * intr[7] = Number of interrupts pending (set to 0 at reset) * --- * Format of context.status: * * 0000 0000 0000 0000 0000 0000 0000 0ISR * * I=Interrupt; 1=interrupts being processed, 0=not * S=Stop; 1=68K is stopped (STOP instruction), 0=not * R=Running; 1=68K is running, 0=not * --- * Format of decoded[]: * * [0] = 1: 2 opcode 0 handlers * [1] = 0: see above.. this is unused * [2] = next handler... (0 + 2) * --- * SR values obtained from instructions are ANDed with 0xa71f to mask out * unused bits. CCR values are ANDed with 0x1f to mask out the unused bits. * This is how a real 68K behaves. * --- * Memory mapping types: The low-level handler mode code is present, but not * accessible. I decided to forbid using it because of performance problems. * --- * The unused address bits of the PC are preserved. They are only lost if the * PC goes out of bounds. */