/* ** Starscream 680x0 emulation library ** Copyright 1997, 1998, 1999 Neill Corlett ** ** Refer to STARDOC.TXT for terms of use, API reference, and directions on ** how to compile. */ /* DGen/SDL modifications: 2008-08-20 - Remove default label at end of block in selective_usereg(). 2011-08-21 - Add default label back followed by a break statement. - Remove emit() call in suffixes(). 2011-09-11 - In maskaddress(), emit additional globals without underscores in their names. It should be up to NASM to prefix them as necessary. - Append -dgen to version number. 2012-12-18 - Add inthandler callback. */ #define STAR_VERSION "0.26d-dgen" /***************************************************************************/ /* ** NOTE ** ** All 68020-related variables and functions are currently experimental, and ** unsupported. */ #include #include #include #include /***************************************************************************/ /* ** Register Usage ** -------------- ** ** This is fairly consistent throughout the file. Occasionally, EAX or EDX ** will be used as a scratch register (in some cases the x86 instruction set ** demands this). ** ** EAX: Bit 0 : V flag ** Bit 1-7 : MUST BE ZERO ** Bit 8 : C flag ** Bit 14 : Z flag ** Bit 15 : N flag ** Bit 16-31: undefined ** EBX: Lower 16 bits: Current instruction word or register number ** Upper 16 bits: zero ** ECX: Primary data ** EDX: Primary address ** EBP: Current base offset of PC ** ESI: Current PC, including base offset ** EDI: Cycle counter */ /***************************************************************************/ /* ** 68010 Loop Mode Timing ** ---------------------- ** ** Loop mode is implemented entirely in the DBRA instruction. It will ** detect when it's supposed to be in loop mode, and adjust its timing when ** applicable. ** ** The __loopmode variable controls when loop mode is active. It is set to ** 1 after an eligible loop is completed. It is set to 0 when the loop ** terminates, or when an interrupt / exception occurs. ** ** Loop info byte: ** ** Bits 1-3: Continuation cycles / 2 ** Bits 4-6: Termination cycles / 2 ** Bits 7-0: (Expiration cycles - continuation cycles) / 2 ** (bit 7 wraps around to bit 0) ** ** With the loop info byte in AL: ** To get the continuation cycles: ** and eax,byte 0Eh ** To get the termination cycles: ** shr al,3 ** and eax,byte 0Eh ** To get the continue/expire difference: ** rol al,2 ** and eax,byte 06h ** ** Default = DBh ** (11011011) ** 101 : 101 = 5 2*5 = 10 continuation cycles ** 101 : 101 = 5 2*5 = 10 termination cycles ** 1 1: 11 = 3 2*3 = 6 10+6 = 16 expiration cycles ** ** (10/10/16 corresponds to the normal DBRA timing behavior) */ /***************************************************************************/ /* ** Algorithm for trace checkpoint in s680x0exec: ** ** If the SR trace flag is set { ** Set the trace trickybit. This differentiates us from the out-of-time ** case above. ** Set cycles_leftover = cycles_needed. ** Force a context switch. ** } otherwise { ** Clear the trace trickybit. ** } ** Begin the fetch/decode/execute loop as usual. ** ** ** In selected ret_timing routines: ** ** Subtract usual number of cycles from edi ** If I'm out of time (edi is negative) { ** Jump to execend with SF set, as usual. ** } otherwise (guaranteed at least one more instruction) { ** Jump to the s680x0exec trace checkpoint. ** } ** ** ** Make sure that the group 1 exception handler clears the trace trickybit. ** ** ** Upon reaching execend: ** ** If the trace trickybit is set { ** Set cycles_needed = cycles_leftover. ** Add cycles_needed to edi. ** Generate a trace exception (clearing the SR trace flag in the process). ** Clear the trace trickybit. ** If edi is positive, resume the fetch/decode/execute loop. ** Otherwise, fall through to the usual epilogue code. ** } */ /***************************************************************************/ /* ** Rebasing notes ** -------------- ** ** Cached rebase happens on: ** * JMP, JSR, RTS, RTE, RTR, RTD ** * Exceptions (except hardware interrupts) ** ** Uncached rebase happens on: ** * Entry to s680x0exec() ** * Hardware interrupts ** * Supervisor flag change ** * Cache disable/invalidate (68020+) ** ** I can't think of any good reason why the hardware interrupt case should be ** uncached, except it happens to be convenient. */ typedef unsigned char byte; typedef unsigned short word; typedef unsigned int dword; static int use_stack = -1; static int hog = -1; static int addressbits = -1; static int cputype = -1; static char *sourcename = NULL; /* This counts the number of instruction handling routines. There's not much ** point to it except for curiosity. */ static int routine_counter = 0; /* Misc. constants */ static char *x86ax [5] = {"?", "al" , "ax" , "?", "eax" }; static char *x86bx [5] = {"?", "bl" , "bx" , "?", "ebx" }; static char *x86cx [5] = {"?", "cl" , "cx" , "?", "ecx" }; static char *x86dx [5] = {"?", "dl" , "dx" , "?", "edx" }; static char *sizename[5] = {"?", "byte", "word", "?", "dword"}; static int quickvalue[8] = {8, 1, 2, 3, 4, 5, 6, 7}; static char direction[2] = {'r','l'}; /* Output file where code will be emitted */ static FILE *codefile; /* Line number - used to make temporary labels i.e. "ln1234" */ static int linenum; /* Effective address modes */ enum eamode { dreg, areg, aind, ainc, adec, adsp, axdp, absw, absl, pcdp, pcxd, immd }; /* Loop information (68010) */ static int loop_c_cycles; static int loop_t_cycles; static int loop_x_cycles; static unsigned char loopinfo[0x10000]; /* ** Misc. global variables which are used while generating instruction ** handling routines. Some of these may assume more than one role. */ static enum eamode main_eamode; /* EA mode, usually source */ static enum eamode main_destmode; /* EA mode, destination (for MOVE) */ static int main_size; /* Operand size (1, 2, or 4) */ static int sizedef; /* Size field in instruction word */ static int main_reg; /* Register number */ static int main_cc; /* Condition code (0-F) */ static int main_dr; /* Direction (right or left) */ static int main_ir; /* Immediate or register (for shifts) */ static int main_qv; /* Quick value */ /* Emit a line of code (format string with other junk) */ static void emit(const char *fmt, ...) { va_list a; va_start(a, fmt); if(codefile) { vfprintf(codefile, fmt, a); } else { fprintf(stderr, "Bad news: Tried to emit() to null file\n"); exit(1); } } /* Dump all options. This is delivered to stderr and to the code file. */ static void optiondump(FILE *o, char *prefix) { fprintf(o, "%sCPU type: %d (%d-bit addresses)\n", prefix, cputype, addressbits); fprintf(o, "%sIdentifiers begin with \"%s\"\n", prefix, sourcename); fprintf(o, "%s%s calling conventions\n", prefix, use_stack ? "Stack" : "Register"); fprintf(o, "%sHog mode: %s\n", prefix, hog ? "On" : "Off"); } static void gen_banner(void) { emit("; Generated by STARSCREAM version " STAR_VERSION "\n"); emit("; For assembly by NASM only\n"); emit(";\n"); emit("; Options:\n"); optiondump(codefile, "; * "); emit(";\n"); emit("bits 32\n"); } static void align(int n) { emit("times ($$-$)&%d db 0\n", n - 1); } static void maskaddress(char *reg) { if(addressbits < 32) { emit("and %s,%d\n", reg, (1 << addressbits) - 1); } } static void begin_source_proc(char *fname) { emit("global _%s%s\n", sourcename, fname); emit("global %s%s_\n", sourcename, fname); emit("global %s%s\n", sourcename, fname); emit("_%s%s:\n", sourcename, fname); emit("%s%s_:\n", sourcename, fname); emit("%s%s:\n", sourcename, fname); } /* Generate variables */ static void gen_variables(void) { emit("section .data\n"); emit("bits 32\n"); emit("global _%scontext\n", sourcename); align(8); emit("_%scontext:\n", sourcename); emit("contextbegin:\n"); /* ** CONTEXTINFO_MEM16 ** CONTEXTINFO_MEM16FC ** ** 16-bit memory interface */ if(cputype <= 68010) { emit("__fetch dd 0\n"); emit("__readbyte dd 0\n"); emit("__readword dd 0\n"); emit("__writebyte dd 0\n"); emit("__writeword dd 0\n"); emit("__s_fetch dd 0\n"); emit("__s_readbyte dd 0\n"); emit("__s_readword dd 0\n"); emit("__s_writebyte dd 0\n"); emit("__s_writeword dd 0\n"); emit("__u_fetch dd 0\n"); emit("__u_readbyte dd 0\n"); emit("__u_readword dd 0\n"); emit("__u_writebyte dd 0\n"); emit("__u_writeword dd 0\n"); if(cputype == 68010) { emit("__f_readbyte dd 0\n"); emit("__f_readword dd 0\n"); emit("__f_writebyte dd 0\n"); emit("__f_writeword dd 0\n"); } /* ** CONTEXTINFO_MEM32 ** ** 32-bit memory interface */ } else { emit("__fetch dd 0\n"); emit("__readbus dd 0\n"); emit("__writebus dd 0\n"); emit("__s_fetch dd 0\n"); emit("__s_readbus dd 0\n"); emit("__s_writebus dd 0\n"); emit("__u_fetch dd 0\n"); emit("__u_readbus dd 0\n"); emit("__u_writebus dd 0\n"); emit("__f_readbus dd 0\n"); emit("__f_writebus dd 0\n"); } /* ** CONTEXTINFO_COMMON ** ** Registers and other info common to all CPU types ** ** It should be noted that on a double fault, bit 0 of both __pc and ** __interrupts will be set to 1. */ if(cputype >= 68000) { emit("__resethandler dd 0\n"); emit("__inthandler dd 0\n"); emit("__reg:\n"); emit("__dreg dd 0,0,0,0,0,0,0,0\n"); emit("__areg dd 0,0,0,0,0,0,0\n"); emit("__a7 dd 0\n"); emit("__asp dd 0\n"); emit("__pc dd 0\n"); emit("__odometer dd 0\n"); /* Bit 0 of __interrupts = stopped state */ emit("__interrupts db 0,0,0,0,0,0,0,0\n"); emit("__sr dw 0\n"); } /* ** CONTEXTINFO_68000SPECIFIC */ if(cputype == 68000) { emit("__contextfiller00 dw 0\n"); } /* ** CONTEXTINFO_68010 ** ** Registers used on the 68010 and higher */ if(cputype >= 68010) { emit("__sfc db 0\n"); emit("__dfc db 0\n"); emit("__vbr dd 0\n"); emit("__bkpthandler dd 0\n"); } /* ** CONTEXTINFO_68010SPECIFIC ** ** Registers used only on the 68010 */ if(cputype == 68010) { emit("__loopmode db 0\n"); emit("__contextfiller10 db 0,0,0\n"); } /* ** CONTEXTINFO_68020 ** ** Registers used on the 68020 and higher */ if(cputype >= 68020) { /* ** 68020 stack pointer rules (tentative) ** ** First of all, the 68000/68010 stack pointer behavior has ** not changed: ** ** 1. In supervisor mode, __a7 contains the supervisor stack ** pointer and __asp contains the user stack pointer. ** 2. In user mode, __a7 contains the user stack pointer and ** __asp contains the supervisor stack pointer. ** ** The only difference is that the "supervisor stack pointer" ** can be either ISP or MSP. __xsp contains whichever stack ** pointer is _not_ the current "supervisor stack pointer". ** ** Here's a table summarizing the above rules: ** ** S M | __a7 __asp __xsp ** ----+----------------- ** 0 0 | USP ISP MSP ** 0 1 | USP MSP ISP ** 1 0 | ISP USP MSP ** 1 1 | MSP USP ISP ** ** As usual, whenever SR changes, we have to play Stack ** Pointer Switcheroo: ** ** * If S changes: swap __asp and __a7 (as usual) ** * If M changes: ** - If S=0, swap __xsp and __asp ** - If S=1, swap __xsp and __a7 */ emit("__xsp dd 0\n"); } /* align(4);*/ emit("contextend:\n"); emit("__cycles_needed dd 0\n"); emit("__cycles_leftover dd 0\n"); emit("__fetch_region_start dd 0\n");/* Fetch region cache */ emit("__fetch_region_end dd 0\n"); emit("__xflag db 0\n"); /* ** Format of __execinfo: ** Bit 0: s680x0exec currently running ** Bit 1: PC out of bounds ** Bit 2: Special I/O section ** ** "Special I/O section" is enabled during group 0 exception ** processing, and it means a couple things: ** * Address and bus errors will not be tolerated (the CPU will ** just keel over and die). Therefore, information such as the ** current PC is not relevant. ** * Registers are not necessarily live. Since special I/O ** sections are guaranteed not to cause exceptions, this is not a ** problem. */ emit("__execinfo db 0\n"); emit("__trace_trickybit db 0\n");/* Pending trace exception */ emit("__filler db 0\n"); emit("__io_cycle_counter dd -1\n");/*always -1 when idle*/ emit("__io_fetchbase dd 0\n"); emit("__io_fetchbased_pc dd 0\n"); emit("__access_address dd 0\n"); emit("save_01 dd 0\n"); // Stef Fix (Gens) } /* Prepare to leave into the cold, dark world of compiled C code */ static void airlock_exit(void) { emit("mov [__io_cycle_counter],edi\n"); emit("mov [__io_fetchbase],ebp\n"); emit("mov [__io_fetchbased_pc],esi\n"); emit("push ebx\n"); emit("push eax\n"); } /* Prepare to return to the warm fuzzy world of assembly code ** (where everybody knows your name) */ static void airlock_enter(void) { emit("pop eax\n"); emit("pop ebx\n"); emit("mov edi,[__io_cycle_counter]\n"); emit("mov ebp,[__io_fetchbase]\n"); emit("mov esi,[__io_fetchbased_pc]\n"); } enum { airlock_stacksize = 8 }; static void cache_ccr(void) { emit("mov al,[__sr]\n"); /* read CCR -> AL */ /* ????????000XNZVC */ emit("mov ah,al\n"); /* copy to AH */ /* 000XNZVC000XNZVC */ emit("and ax,0C10h\n"); /* isolate NZ...X */ /* 0000NZ00000X0000 */ emit("shl ah,3\n"); /* put NZ almost where we want it */ /* 0NZ00000000X0000 */ emit("shr al,4\n"); /* shift X flag into bit 0 */ /* 0NZ000000000000X */ emit("mov [__xflag],al\n"); /* store X flag */ /* 0NZ000000000000X al -> xflag */ emit("mov al,[__sr]\n"); /* read CCR -> AL again */ /* 0NZ00000000XNZVC */ emit("and al,3\n"); /* isolate VC */ /* 0NZ00000000000VC */ emit("shr al,1\n"); /* just V */ /* 0NZ000000000000V carry */ emit("adc ah,ah\n"); /* append C to rest of flags */ /* NZ00000C0000000V */ } static void writeback_ccr(void) { emit("shr ah,1\n"); /* C flag -> x86 carry */ /* 0NZ?????0000000V carry */ emit("adc ax,ax\n"); /* append to V flag */ /* NZ?????0000000VC */ emit("and ax,0C003h\n"); /* isolate NZ.......VC */ /* NZ000000000000VC */ emit("or ah,[__xflag]\n"); /* load X flag */ /* NZ00000X000000VC */ emit("ror ah,4\n"); /* now we have XNZ....VC */ /* 000XNZ00000000VC */ emit("or al,ah\n"); /* OR them together */ /* 000XNZ00000XNZVC */ emit("mov [__sr],al\n"); /* store the result */ /* 000XNZ00000XNZVC al -> sr */ } /* ** This will make edi _very_ negative... far enough negative that the ** leftover cycle incorporation at the end of s68000exec() shouldn't be ** enough to make it positive. */ static void force_context_switch(void) { emit("sub edi,[__cycles_needed]\n"); emit("mov dword[__cycles_needed],0\n"); } /* ** Put all the unused cycles in the leftover cycle bank, so we can call ** attention to the tricky bit processor. */ static void force_trickybit_process(void) { emit("inc edi\n"); emit("add [__cycles_leftover],edi\n"); emit("or edi,byte -1\n");/* smaller than a mov */ } /* "newpc" has been renamed to this */ void perform_cached_rebase(void); /* Copy either __s_* or __u_* memory map pointers */ static void copy_memory_map(char *map, char *reg) { emit("mov %s,[__%s_fetch]\n", reg, map); emit("mov [__fetch],%s\n", reg); if(cputype < 68020) { emit("mov %s,[__%s_readbyte]\n" , reg, map); emit("mov [__readbyte],%s\n" , reg); emit("mov %s,[__%s_readword]\n" , reg, map); emit("mov [__readword],%s\n" , reg); emit("mov %s,[__%s_writebyte]\n", reg, map); emit("mov [__writebyte],%s\n", reg); emit("mov %s,[__%s_writeword]\n", reg, map); emit("mov [__writeword],%s\n", reg); } else { emit("mov %s,[__%s_readbus]\n" , reg, map); emit("mov [__readbus],%s\n" , reg); emit("mov %s,[__%s_writebus]\n" , reg, map); emit("mov [__writebus],%s\n" , reg); } } /***************************************************************************/ static void gen_interface(void) { emit("section .text\n"); emit("bits 32\n"); emit("top:\n"); /***************************************************************************/ /* ** s680x0init() ** ** Entry: Nothing ** Exit: Zero ** ** This must be called before anything else. It decompresses the main jump ** table (and loop info, in the case of the 68010). */ begin_source_proc("init"); emit("pushad\n"); emit("mov edi,__jmptbl\n"); emit("mov esi,__jmptblcomp\n"); if(cputype == 68010) { emit("mov ebx,__looptbl\n"); } emit(".decomp:\n"); emit("lodsd\n"); emit("mov ecx,eax\n"); emit("and eax,0FFFFFFh\n"); emit("shr ecx,24\n"); emit("add eax,top\n"); emit("inc ecx\n"); if(cputype == 68010) { emit("mov ebp,ecx\n"); } emit(".jloop:\n"); emit("mov [edi],eax\n"); emit("add edi,byte 4\n"); emit("dec ecx\n"); emit("jnz short .jloop\n"); if(cputype == 68010) { emit("lodsb\n"); emit(".lloop:\n"); emit("mov [ebx],al\n"); emit("inc ebx\n"); emit("dec ebp\n"); emit("jnz short .lloop\n"); } emit("cmp edi,__jmptbl+262144\n"); emit("jne short .decomp\n"); emit("popad\n"); emit("xor eax,eax\n"); emit("ret\n"); /***************************************************************************/ /* ** s680x0exec(cycles) ** ** Entry: EAX = # cycles to execute ** Exit: 80000000h: success ** 80000001h: PC out of range ** 80000002h: unsupported stack frame ** FFFFFFFFh: CPU is dead because of a double fault ** < 80000000h: invalid instruction = address of invalid instr. */ begin_source_proc("exec"); if(use_stack) emit("mov eax,[esp+4]\n"); /* ** Check for stopped and double-faulted states. */ emit("test byte[__interrupts],1\n"); emit("jz .notstopped\n"); emit("test byte[__pc],1\n"); emit("jz .notfaulted\n"); emit("or eax,byte -1\n"); emit("ret\n"); emit(".notfaulted:\n"); emit("add [__odometer],eax\n"); emit("mov eax,80000000h\n"); emit("ret\n"); emit(".notstopped:\n"); emit("push ebp\n"); emit("push ebx\n"); emit("push ecx\n"); emit("push edx\n"); emit("push esi\n"); emit("push edi\n"); emit("mov [__cycles_needed],eax\n"); emit("mov edi,eax\n");/* store # of cycles to execute */ emit("dec edi\n"); emit("xor ebx,ebx\n"); emit("mov esi,[__pc]\n"); cache_ccr(); emit("xor ebp,ebp\n"); emit("mov byte[__execinfo],1\n"); /* ** Force an uncached re-base. ** This fulfills the "Entry to s680x0exec()" case. */ emit("call basefunction\n"); emit("add esi,ebp\n"); emit("test byte[__execinfo],2\n"); /* Check for PC out of bounds */ emit("jnz near exec_bounderror\n"); emit("mov dword[__cycles_leftover],0\n");/* an extra precaution */ /* PPL and Trace checkpoint */ emit("exec_checkpoint:\n"); emit("js short execquit\n"); /* Check PPL */ emit("mov cl,[__sr+1]\n"); emit("and ecx,byte 7\n"); emit("inc ecx\n"); emit("mov ch,[__interrupts]\n"); emit("or ch,ch\n"); emit("js short .yesint\n"); emit("shr ch,cl\n"); emit("jz short .noint\n"); emit(".yesint:\n"); emit("call flush_interrupts\n"); /* Force an uncached re-base */ emit("call basefunction\n"); emit("add esi,ebp\n"); emit("test byte[__execinfo],2\n"); /* Check for PC out of bounds */ emit("jnz near exec_bounderror\n"); emit(".noint:\n"); /* ** If the SR Trace flag is set, generate a pending trace exception. */ emit("mov ch,[__sr+1]\n"); emit("and ch,80h\n"); /* isolate trace flag */ emit("mov [__trace_trickybit],ch\n"); emit("jz short execloop\n"); /* ** Activate the tricky bit processor. ** ** Because edi isn't checked for negativity before entering the ** fetch/decode/execute loop, we're guaranteed to execute at least ** one more instruction before any trace exception. ** ** If another group 1 exception happens in the course of executing ** this next instruction, then the group_1_exception routine will ** clear the trace tricky bit and re-adjust the cycle counters, and ** we'll pretend none of this ever happened. */ force_trickybit_process(); emit("execloop:\n"); /* emit("xor ebx,ebx\n");suffice to say, bits 16-31 should be zero... */ emit("mov bx,[esi]\n"); emit("add esi,byte 2\n"); emit("jmp dword[__jmptbl+ebx*4]\n"); /* Traditional loop - used when hog mode is off */ if(!hog) { emit("execend:\n"); emit("jns short execloop\n"); } emit("execquit:\n"); /* ** Tricky Bit Processor */ /* Look for trace tricky bit */ emit("cmp byte[__trace_trickybit],0\n"); emit("je short execquit_notrace\n"); /* Generate trace exception */ emit("mov edx,24h\n"); emit("call group_1_exception\n"); perform_cached_rebase(); /* Subtract time used by exception processing */ emit("sub edi,byte %d\n", (cputype == 68010) ? 38 : 34); emit("execquit_notrace:\n"); /* ** Look for pending interrupts that exceed the current PPL. These ** are higher priority and are therefore processed last (the ISR will ** end up getting control). */ emit("mov cl,[__sr+1]\n"); emit("and ecx,byte 7\n"); emit("inc ecx\n"); emit("mov ch,[__interrupts]\n"); emit("or ch,ch\n"); emit("js short execquit_yesinterrupt\n"); emit("shr ch,cl\n"); emit("jz short execquit_nointerrupt\n"); emit("execquit_yesinterrupt:\n"); emit("call flush_interrupts\n"); /* ** Force an uncached re-base. ** This fulfills the "Hardware interrupt" case. */ emit("call basefunction\n"); emit("add esi,ebp\n"); emit("test byte[__execinfo],2\n"); /* Check for PC out of bounds */ emit("jnz short exec_bounderror\n"); emit("execquit_nointerrupt:\n"); /* ** Incorporate leftover cycles (if any) and see if we should keep ** running. */ emit("add edi,[__cycles_leftover]\n"); emit("mov dword[__cycles_leftover],0\n"); emit("jns short execloop\n"); /* Leave s680x0exec with "Success" code. */ emit("mov ecx,80000000h\n"); /* ** Exit the s680x0exec routine. By this time the return code should ** already be in ecx. */ emit("execexit:\n"); emit("sub esi,ebp\n"); writeback_ccr(); emit("mov [__pc],esi\n"); emit("inc edi\n"); emit("mov edx,[__cycles_needed]\n"); emit("sub edx,edi\n"); emit("add [__odometer],edx\n"); emit("mov byte[__execinfo],0\n"); /* ** Remember: __io_cycle_counter is always -1 when idle! ** ** This prevents us from having to check __execinfo during the ** readOdometer / tripOdometer calls. */ emit("mov dword[__cycles_needed],0\n"); emit("mov dword[__io_cycle_counter],-1\n"); emit("mov eax,ecx\n");/* return code */ emit("pop edi\n"); emit("pop esi\n"); emit("pop edx\n"); emit("pop ecx\n"); emit("pop ebx\n"); emit("pop ebp\n"); emit("ret\n"); /* ** Leave s680x0exec with "Out of bounds" code. */ emit("exec_bounderror:\n"); emit("mov ecx,80000001h\n"); emit("jmp short execexit\n"); /* ** Invalid instruction handler */ emit("invalidins:\n"); emit("sub esi,byte 2\n"); /* back up one word */ emit("mov ecx,esi\n");/* get address in ecx */ emit("sub ecx,ebp\n");/* subtract base */ maskaddress("ecx"); if(addressbits == 32) { emit("and ecx,7FFFFFFFh\n"); } /* emit("or byte[__stopped],2\n");*/ emit("jmp short execexit\n"); /***************************************************************************/ /* ** s680x0reset() ** ** Entry: Nothing ** Exit: 0 on success ** 1 on failure: ** * if there's no Supervisor Program entry for address 0 ** * if s680x0exec() is active ** -1 on double fault */ begin_source_proc("reset"); emit("mov eax,1\n"); emit("test [__execinfo],al\n"); /* Ensure s680x0exec() inactive */ emit("jnz near .return\n"); emit("cmp dword[__s_fetch],0\n"); emit("je near .return\n"); emit("dec eax\n"); emit("mov [__execinfo],al\n"); emit("sub eax,byte 16\n"); emit(".gp:\n"); emit("mov dword[__reg+64+eax*4],0\n"); emit("inc eax\n"); emit("jnz short .gp\n"); emit("mov [__asp],eax\n"); if(cputype >= 68020) emit("mov [__xsp],eax\n"); /* Set up SR for no tracing, supervisor mode, ISP, PPL 7 */ emit("mov word[__sr],2700h\n"); if(cputype >= 68010) { emit("mov [__vbr],eax\n"); emit("mov [__sfc],al\n"); emit("mov [__dfc],al\n"); } if(cputype == 68010) { emit("mov [__loopmode],al\n"); } /* Copy supervisor address space information */ copy_memory_map("s", "eax"); /* Generate Supervisor Program Space reads to get the initial PC and ** SSP/ISP */ emit("mov eax,1\n"); /* assume failure */ emit("mov [__pc],eax\n"); emit("mov [__interrupts],al\n"); emit("push esi\n"); emit("push ebp\n"); emit("xor esi,esi\n"); emit("call basefunction\n");/* will preserve eax */ emit("test byte[__execinfo],2\n"); emit("jnz short .exit\n"); emit("add esi,ebp\n"); emit("mov eax,[esi]\n"); emit("rol eax,16\n"); emit("mov [__a7],eax\n"); emit("mov eax,[esi+4]\n"); emit("rol eax,16\n"); emit("mov [__pc],eax\n"); /* An address error here will cause a double fault */ emit("and eax,byte 1\n"); emit("mov [__interrupts],al\n"); emit("neg eax\n"); /* -1 on double fault, 0 on success */ emit(".exit:\n"); emit("pop ebp\n"); emit("pop esi\n"); emit(".return:\n"); emit("ret\n"); /***************************************************************************/ /* ** s680x0interrupt(level, vector) ** ** Entry: EAX = interrupt level ** EDX = vector (-1 for auto, -2 for spurious) ** Exit: EAX = 0 on success ** 1 on failure, previous vector exists ** 2 on invalid input */ begin_source_proc("interrupt"); emit("push edx\n"); if(use_stack) { emit("mov eax,[esp+8]\n"); /* eax = level */ emit("mov edx,[esp+12]\n"); /* edx = vector */ } /* ** Verify parameters. */ emit("cmp eax,byte 7\n"); emit("ja short .badinput\n"); emit("or eax,eax\n"); emit("jz short .badinput\n"); emit("cmp edx,255\n"); emit("jg short .badinput\n"); emit("cmp edx,byte -2\n"); emit("jl short .badinput\n"); /* ** Calculate the vector number. */ emit("jne short .notspurious\n"); emit("mov edx,18h\n"); emit(".notspurious:\n"); emit("or edx,edx\n"); emit("jns short .notauto\n"); emit("lea edx,[eax+18h]\n"); emit(".notauto:\n"); /* ** Test to see if this interrupt level is already pending. ** If it is, return with failure. */ emit("push ecx\n"); emit("mov cl,al\n"); emit("mov ah,1\n"); emit("shl ah,cl\n"); emit("pop ecx\n"); emit("test [__interrupts],ah\n"); emit("jnz .failure\n"); /* ** Commit the given interrupt and vector number. */ emit("or [__interrupts],ah\n"); emit("mov ah,0\n"); emit("mov [__interrupts+eax],dl\n"); emit("and byte[__interrupts],0FEh\n"); /* ** Notify the tricky bit handler. If we're doing this outside of ** s680x0exec(), then the notification will have no effect, because ** __io_cycle_counter is always -1 when idle. */ emit("mov edx,[__io_cycle_counter]\n"); emit("inc edx\n"); emit("add [__cycles_leftover],edx\n"); emit("mov dword[__io_cycle_counter],-1\n"); /* ** Success (0) */ emit("pop edx\n"); emit("xor eax,eax\n"); emit("ret\n"); /* ** Failure (1) */ emit(".failure:\n"); emit("pop edx\n"); emit("mov eax,1\n"); emit("ret\n"); /* ** Bad input (2) */ emit(".badinput:\n"); emit("pop edx\n"); emit("mov eax,2\n"); emit("ret\n"); /***************************************************************************/ /* ** s680x0flushInterrupts() ** ** Flushes all pending interrupts. ** ** Entry: Nothing ** Exit: Nothing */ begin_source_proc("flushInterrupts"); /* ** If s680x0exec() is already running, then the interrupts are about ** to get flushed anyway. So ignore this call. */ emit("test byte[__execinfo],1\n"); emit("jnz .noflush\n"); /* Make registers "live" */ emit("pushad\n"); emit("mov esi,[__pc]\n"); emit("xor ebp,ebp\n"); cache_ccr(); emit("xor edi,edi\n"); /* well, semi-live */ emit("call flush_interrupts\n"); emit("sub [__odometer],edi\n"); /* edi will be <= 0 here */ emit("mov [__pc],esi\n"); /* PC guaranteed unbased */ writeback_ccr(); emit("popad\n"); emit(".noflush:\n"); emit("ret\n"); /***************************************************************************/ /* ** s680x0GetContextSize() ** ** Entry: Nothing ** Exit: Size of context array (in bytes) */ begin_source_proc("GetContextSize"); emit("mov eax,contextend-contextbegin\n"); emit("ret\n"); /***************************************************************************/ /* ** s680x0GetContext(context) ** ** Entry: Address of context in EAX ** Exit: Nothing */ begin_source_proc("GetContext"); emit("push edx\n"); emit("push edi\n"); if(use_stack) emit("mov edi,[esp+12]\n"); else emit("mov edi,eax\n"); emit("%%assign i 0\n"); emit("%%rep ((contextend-contextbegin) / 8)\n"); emit(" mov eax,[contextbegin+i+0]\n"); emit(" mov edx,[contextbegin+i+4]\n"); emit(" mov [edi+i+0],eax\n"); emit(" mov [edi+i+4],edx\n"); emit("%%assign i i+8\n"); emit("%%endrep\n"); emit("%%if ((contextend-contextbegin) %% 8)!=0\n"); emit(" mov eax,[contextbegin+i+0]\n"); emit(" mov [edi+i+0],eax\n"); emit("%%endif\n"); emit("pop edi\n"); emit("pop edx\n"); emit("xor eax,eax\n"); emit("ret\n"); /***************************************************************************/ /* ** s680x0SetContext(context) ** ** Entry: Address of context in EAX ** Exit: Nothing */ begin_source_proc("SetContext"); emit("push edx\n"); emit("push esi\n"); if(use_stack) emit("mov esi,[esp+12]\n"); else emit("mov esi,eax\n"); emit("%%assign i 0\n"); emit("%%rep ((contextend-contextbegin) / 8)\n"); emit(" mov eax,[esi+i+0]\n"); emit(" mov edx,[esi+i+4]\n"); emit(" mov [contextbegin+i+0],eax\n"); emit(" mov [contextbegin+i+4],edx\n"); emit("%%assign i i+8\n"); emit("%%endrep\n"); emit("%%if ((contextend-contextbegin) %% 8)!=0\n"); emit(" mov eax,[esi+i+0]\n"); emit(" mov [contextbegin+i+0],eax\n"); emit("%%endif\n"); emit("pop esi\n"); emit("pop edx\n"); emit("xor eax,eax\n"); emit("ret\n"); /***************************************************************************/ /* ** s680x0fetch(address) ** ** (Will need to be rewritten to handle function codes) ** ** Reads a word from memory using the _supervisor_ fetch map. ** Entry: Address in EAX ** Exit: The fetched word (or -1 if the address was invalid) */ begin_source_proc("fetch"); if(use_stack) emit("mov eax,[esp+4]\n"); emit("push ebx\n"); emit("push esi\n"); emit("push edi\n");/* can be destroyed by rebase */ emit("push ebp\n"); emit("push dword[__fetch]\n"); emit("mov ebx,[__s_fetch]\n"); emit("mov [__fetch],ebx\n"); /* Must save the fetch region cache as well */ emit("push dword[__fetch_region_start]\n"); emit("push dword[__fetch_region_end]\n"); /* and __execinfo */ emit("mov bl,[__execinfo]\n"); emit("push ebx\n"); emit("xor ebp,ebp\n"); emit("mov esi,eax\n"); emit("and byte[__execinfo],0FDh\n"); /* Force re-base */ emit("call basefunction\n"); emit("test byte[__execinfo],2\n"); emit("mov eax,-1\n"); emit("jnz short .badfetch\n"); emit("add esi,ebp\n"); emit("inc eax\n"); /* make eax zero */ emit("mov ax,[esi]\n"); emit(".badfetch:\n"); emit("pop ebx\n"); emit("mov [__execinfo],bl\n"); emit("pop dword[__fetch_region_end]\n"); emit("pop dword[__fetch_region_start]\n"); emit("pop dword[__fetch]\n"); emit("pop ebp\n"); emit("pop edi\n"); emit("pop esi\n"); emit("pop ebx\n"); emit("ret\n"); /***************************************************************************/ /* ** s680x0readOdometer() ** ** Reads the odometer (works from anywhere) ** Entry: Nothing ** Exit: Odometer in EAX */ begin_source_proc("readOdometer"); emit("mov eax,[__cycles_needed]\n"); emit("sub eax,[__io_cycle_counter]\n"); emit("dec eax\n"); /* eax = elapsed cycles */ emit("sub eax,[__cycles_leftover]\n"); emit("add eax,[__odometer]\n"); /* add to old __odometer */ emit("ret\n"); /***************************************************************************/ /* ** s680x0tripOdometer() ** ** Reads and then clears the odometer (works from anywhere) ** Entry: Nothing ** Exit: Old odometer value in EAX */ begin_source_proc("tripOdometer"); /* Read */ emit("mov eax,[__cycles_needed]\n"); emit("sub eax,[__io_cycle_counter]\n"); emit("dec eax\n"); /* eax = elapsed cycles */ emit("sub eax,[__cycles_leftover]\n"); emit("add [__odometer],eax\n"); /* add to old __odometer */ /* Clear */ emit("mov eax,[__io_cycle_counter]\n"); emit("inc eax\n"); emit("mov [__cycles_needed],eax\n"); emit("mov eax,[__odometer]\n"); emit("mov dword[__odometer],0\n"); emit("ret\n"); /***************************************************************************/ /* ** s680x0controlOdometer(n) ** ** Reads the odometer, clears it only if n != 0 ** Entry: n in EAX ** Exit: Old odometer value in EAX */ begin_source_proc("controlOdometer"); if(use_stack) emit("mov eax,[esp+4]\n"); emit("or eax,eax\n"); emit("jnz short _%stripOdometer\n", sourcename); emit("jmp short _%sreadOdometer\n", sourcename); /***************************************************************************/ /* ** s680x0releaseTimeslice() ** ** Ends the s680x0exec call prematurely. The early exit is reflected in ** __odometer. ** Entry: Nothing ** Exit: Nothing */ begin_source_proc("releaseTimeslice"); emit("mov eax,[__cycles_needed]\n"); emit("sub [__io_cycle_counter],eax\n"); emit("xor eax,eax\n"); emit("mov [__cycles_needed],eax\n"); emit("ret\n"); /***************************************************************************/ /* ** s680x0readPC() ** ** Returns the current program counter. Works anywhere, including I/O, ** RESET, and BKPT handlers. ** ** Note that the value returned won't necessarily coincide exactly with the ** beginning of an instruction. */ begin_source_proc("readPC"); emit("test byte[__execinfo],1\n"); emit("jnz short .live\n"); emit("mov eax,[__pc]\n"); emit("ret\n"); emit(".live:\n"); emit("mov eax,[__io_fetchbased_pc]\n"); emit("sub eax,[__io_fetchbase]\n"); emit("ret\n"); } /***************************************************************************/ /* ** Routine that flushes pending interrupts (with correct priority). ** Assumes "live" registers, including EDI and EBP. ** ** Does not rebase the PC. In fact, it will un-base the PC even if there ** aren't any pending interrupts. ** ** s680x0flushInterrupts() is actually a wrapper for this. */ static void gen_flush_interrupts(void) { int cycles = (cputype == 68010) ? 46 : 44; align(16); emit("flush_interrupts:\n"); /* Unbase PC */ emit("sub esi,ebp\n"); emit("xor ebp,ebp\n"); /* This loop is intentionally post-tested because interrupt level 7 ** is non-maskable. */ emit("mov edx,7\n"); emit("mov cl,80h\n"); emit("mov ch,[__sr+1]\n"); /* current PPL */ emit("and ch,7\n"); emit(".loop:\n"); emit("test [__interrupts],cl\n"); emit("jz short .noint\n"); emit("mov [save_01], dl\n"); // Stef Fix (Gens) emit("mov dl,[__interrupts+edx]\n"); emit("not cl\n"); emit("and [__interrupts],cl\n"); emit("shl edx,2\n"); emit("call group_1_exception\n"); emit("and [__sr + 1], byte 0xF8\n"); // Stef Fix (Gens) emit("mov dl, [save_01]\n"); // Stef Fix (Gens) emit("sub edi,byte %d\n", cycles); emit("or [__sr + 1], dl\n"); // Stef Fix (Gens) emit("mov ecx,[__inthandler]\n"); emit("or ecx,ecx\n"); emit("jz short .intdone\n"); airlock_exit(); emit("call ecx\n"); airlock_enter(); emit("jmp short .intdone\n"); emit(".noint:\n"); emit("dec edx\n"); emit("jz short .intdone\n"); emit("shr cl,1\n"); emit("cmp dl,ch\n"); emit("jg short .loop\n"); emit(".intdone:\n"); emit("ret\n"); } /***************************************************************************/ static void ret_timing(int n) { if(n) { emit("sub edi,%s%d\n", (n < 128) ? "byte " : "", n); } else { emit("or edi,edi\n"); } /* If hog mode is off, jump back to the main loop */ if(!hog) { emit("jmp execend\n"); /* If hog mode is on, fetch and execute the next instruction */ } else { emit("js near execquit\n"); emit("mov bx,[esi]\n"); emit("add esi,byte 2\n"); emit("jmp dword[__jmptbl+ebx*4]\n"); } } /* ** This version of ret_timing does a trace flag check. ** ** Note: this only needs to be used for instructions which can potentially ** _set_ the trace flag. Instructions which can't set the trace flag (even ** if they can clear it) are OK to use ret_timing as usual. Why? Well, if ** an instruction is run in trace mode, that instruction is doomed to be ** traced, regardless if it clears the trace flag during its execution. ** Furthermore, the trace exception (being a group 1 exception after all) ** will clear the trace tricky bit as well as the trace flag. */ static void ret_timing_checkpoint(int n) { if(n) { emit("sub edi,%s%d\n", (n < 128) ? "byte " : "", n); } else { emit("or edi,edi\n"); } emit("jmp exec_checkpoint\n"); } /***************************************************************************/ /* This routine decodes an extension word into EDX. */ static void gen_decode_ext(void) { align(16); emit("decode_ext:\n"); if(cputype <= 68010) { emit("push ecx\n"); emit("movzx edx,word[esi]\n"); emit("movsx ecx,dl\n"); emit("add esi,byte 2\n"); emit("shr edx,12\n"); emit("mov edx,[__reg+edx*4]\n"); emit("jc short .long\n"); emit("movsx edx,dx\n"); emit(".long:\n"); emit("add edx,ecx\n"); emit("pop ecx\n"); emit("ret\n"); } else { /* For future expansion... */ /* need an extra jump table here */ } } /***************************************************************************/ /* ** Perform a cached rebase */ void perform_cached_rebase(void) { int myline = linenum; linenum += 2; emit("cmp esi,[__fetch_region_start]\n"); emit("jb short ln%d\n", myline); emit("cmp esi,[__fetch_region_end]\n"); emit("jbe short ln%d\n", myline + 1); emit("ln%d:\n", myline); emit("call basefunction\n"); emit("ln%d:\n", myline + 1); emit("add esi,ebp\n"); } /***************************************************************************/ /* ** This is the function that generates a base for a given 68K PC. ** ** Entry: 68K PC in ESI ** Exit: Newly calculated base in EBP ** ** Sounds like a simple lookup into the __fetch array, and in the case of ** 32-bit addresses, it is. But for anything less, we'll need to compensate ** for potential garbage in the unused address bits, by subtracting the value ** of these unused bits from the base. This way the full 32 bits of the PC ** are preserved, even if they're not used. ** ** The only registers which need to be "live" here are ESI and EBP. The ** fetch region cache is updated, and bit 1 of __execinfo is set if the base ** couldn't be calculated. */ static void gen_basefunction(void) { align(16); emit("basefunction:\n"); /* ** Prepare ESI by masking off unused address bits (but save it ** first). */ if(addressbits < 32) { emit("push esi\n"); maskaddress("esi"); } emit("mov ebp,[__fetch]\n"); emit(".check:\n"); emit("db 3Eh\n"); emit("cmp esi,[ebp]\n"); /* Are we smaller? */ emit("jb short .next\n"); /* Yes, go to next address */ emit("db 3Eh\n"); emit("cmp esi,[ebp+4]\n"); /* Are we bigger? */ emit("jbe short .base\n"); emit(".next:\n"); emit("db 3Eh\n"); emit("cmp dword [ebp],byte -1\n"); /* End of list? */ emit("je short .outofrange\n"); emit("add ebp,byte 12\n"); /* To the next structure */ emit("jmp short .check\n"); /* Bad news... we jumped out into the weeds. */ emit(".outofrange:\n"); if(addressbits < 32) emit("pop esi\n"); emit("xor ebp,ebp\n"); emit("mov dword[__fetch_region_start],-1\n"); emit("mov dword[__fetch_region_end],ebp\n"); force_context_switch(); emit("or byte[__execinfo],2\n"); emit("ret\n"); emit(".base:\n"); /* ** Dealing with addressbits < 32 again... if the unused PC bits are ** anything but zero, then we'll need to adjust the base to ** compensate. */ if(addressbits < 32) { emit("mov esi,[esp]\n"); emit("and esi,%d\n", 0xFFFFFFFF << addressbits); } emit("push edx\n"); emit("mov edx,ebp\n"); /* ** Update the fetch region cache, adding in the garbage bits where ** applicable. */ emit("mov ebp,[edx]\n"); if(addressbits < 32) emit("or ebp,esi\n"); emit("mov [__fetch_region_start],ebp\n"); emit("mov ebp,[edx+4]\n"); if(addressbits < 32) emit("or ebp,esi\n"); emit("mov [__fetch_region_end],ebp\n"); emit("mov ebp,[edx+8]\n"); emit("pop edx\n"); if(addressbits < 32) { /* ** Subtract garbage bits from the base, and restore the ** original 32-bit PC value. */ emit("sub ebp,esi\n"); emit("pop esi\n"); } emit("ret\n"); } /***************************************************************************/ /* Read flags from CL into our CCR. CX is unmodified. */ static void cl2ccr(void){ emit("mov al,cl\n"); /* read CCR -> AL */ /* ???????????XNZVC */ emit("mov ah,al\n"); /* copy to AH */ /* ???XNZVC???XNZVC */ emit("and ax,0C10h\n"); /* isolate NZ...X */ /* 0000NZ00000X0000 */ emit("shl ah,3\n"); /* put NZ almost where we want it */ /* 0NZ00000000X0000 */ emit("shr al,4\n"); /* shift X flag into bit 0 */ /* 0NZ000000000000X */ emit("mov [__xflag],al\n"); /* store X flag */ /* 0NZ000000000000X al -> xflag */ emit("mov al,cl\n"); /* read CCR -> AL again */ /* 0NZ00000000XNZVC */ emit("and al,3\n"); /* isolate VC */ /* 0NZ00000000000VC */ emit("shr al,1\n"); /* just V */ /* 0NZ000000000000V carry */ emit("adc ah,ah\n"); /* append C to rest of flags */ /* NZ00000C0000000V */ } /* ** Read flags from CX into our SR, performing a mode switch where applicable. ** CX is unmodified. Uses 4 bytes of stack. ** ** This does not do any of the trace flag mojo, so be sure to check for it ** explicitly where applicable (hint: ret_timing_checkpoint). */ static void cx2sr(void){ int myline = linenum; linenum += 2; emit("push ecx\n"); /* Step 1: switch supervisor mode */ /* Is the new mode different from the last? */ emit("mov cl,[__sr+1]\n"); emit("and cx,2020h\n"); emit("xor ch,cl\n"); emit("jz near ln%d\n", myline); /* If so, swap stack pointers */ emit("mov ecx,[__a7]\n"); emit("xchg ecx,[__asp]\n"); emit("mov [__a7],ecx\n"); /* and copy the appropriate memory map */ emit("test byte[esp+1],20h\n"); emit("jz short ln%d\n", myline + 1); copy_memory_map("s", "ecx"); emit("jmp short ln%d\n", myline); emit("ln%d:\n", myline + 1); copy_memory_map("u", "ecx"); emit("ln%d:\n", myline); emit("pop ecx\n"); /* Step 2: set new PPL / supervisor mode / trace flag */ emit("mov [__sr+1],ch\n"); emit("and byte[__sr+1],0A7h\n"); /* Step 3: Store CL into CCR */ cl2ccr(); } /* Read flags from our CCR into CL. CH is zeroed. */ static void ccr2cl(void){ /* Read XNZ */ emit("mov ch,[__xflag]\n"); /* 0000000X???????? */ emit("mov cl,ah\n"); /* 0000000XNZ?????C */ emit("shr cx,6\n"); /* 0000000000000XNZ */ /* Read V */ emit("add cl,cl\n"); /* 000000000000XNZ0 */ emit("or cl,al\n"); /* 000000000000XNZV */ /* Read C */ emit("mov ch,ah\n"); /* NZ?????C0000XNZV */ emit("shl ch,8\n"); /* 000000000000XNZV carry */ emit("adc cl,cl\n"); /* 00000000000XNZVC */ } /* Read flags from our SR into CX. */ static void sr2cx(void){ /* Condition codes */ ccr2cl(); /* PPL / supervisor mode / trace flag */ emit("mov ch,[__sr+1]\n"); } /* Switch to supervisor mode. Can potentially destroy ECX. */ static void supervisor(void){ int myline=linenum;linenum++; emit("test byte[__sr+1],20h\n"); emit("jnz short ln%d\n",myline); emit("mov ecx,[__a7]\n"); emit("xchg ecx,[__asp]\n"); emit("mov [__a7],ecx\n"); copy_memory_map("s", "ecx"); emit("or byte[__sr+1],20h\n"); emit("ln%d:\n",myline); } /***************************************************************************/ static void gen_readbw(int size){ char z='x'; if(size==1)z='b'; if(size==2)z='w'; align(16); emit("readmemory%s:\n",sizename[size]); emit("mov [__access_address],edx\n"); maskaddress("edx"); emit("mov ecx,[__read%s]\n",sizename[size]); emit("read%c_check:\n",z); emit("cmp edx,[ecx]\n"); /* Are we smaller? */ emit("jb short read%c_next\n",z); /* Yes, go to next address */ emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */ emit("jbe short read%c_call\n",z); emit("read%c_next:\n",z); emit("cmp dword[ecx],byte -1\n");/* End of list? */ emit("je short read%c_outofrange\n",z); emit("add ecx,byte 16\n"); /* To the next structure */ emit("jmp short read%c_check\n",z); emit("read%c_outofrange:\n",z); emit("or ecx,byte -1\n"); emit("mov edx,[__access_address]\n"); emit("ret\n"); emit("read%c_call:\n",z); emit("cmp dword[ecx+8],byte 0\n"); emit("jne short read%c_callio\n",z); emit("sub edx,[ecx]\n"); emit("add edx,[ecx+12]\n"); if(size==1){ emit("xor edx,byte 1\n"); emit("mov cl,[edx]\n"); }else{ emit("mov cx,[edx]\n"); } emit("mov edx,[__access_address]\n"); emit("ret\n"); emit("read%c_callio:\n",z); airlock_exit(); /* ** What's this undocumented thingy? Read/write handlers actually get ** an extra parameter (the address of the record that caused the ** call). */ emit("mov eax,edx\n"); /* address */ emit("mov edx,ecx\n"); /* pointer to structure */ if(use_stack){ emit("push edx\n"); emit("push eax\n"); } emit("call dword[edx+8]\n"); if(use_stack)emit("add esp,byte 8\n"); emit("mov ecx,eax\n"); airlock_enter(); emit("mov edx,[__access_address]\n"); emit("ret\n"); } static void gen_readl(void){ align(16); emit("readmemory%s:\n",sizename[4]); emit("mov [__access_address],edx\n"); maskaddress("edx"); emit("mov ecx,[__readword]\n"); emit("readl_check:\n"); emit("cmp edx,[ecx]\n"); /* Are we smaller? */ emit("jb short readl_next\n"); /* Yes, go to next address */ emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */ emit("jbe short readl_call\n"); emit("readl_next:\n"); emit("cmp dword[ecx],byte -1\n");/* End of list? */ emit("je short readl_outofrange\n"); emit("add ecx,byte 16\n"); /* To the next structure */ emit("jmp short readl_check\n"); emit("readl_outofrange:\n"); emit("add edx,byte 2\n"); emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */ emit("mov ecx,-1\n"); emit("ja near readl_lower\n"); emit("mov edx,[__access_address]\n"); emit("ret\n"); emit("readl_call:\n"); emit("cmp dword[ecx+8],byte 0\n"); emit("jne short readl_callio\n"); emit("add edx,byte 2\n"); emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */ emit("ja short readl_split\n"); /* Unsplit version */ emit("sub edx,[ecx]\n"); emit("add edx,[ecx+12]\n"); emit("mov ecx,[edx-2]\n"); emit("rol ecx,16\n"); emit("mov edx,[__access_address]\n"); emit("ret\n"); emit("readl_callio:\n"); emit("add edx,byte 2\n"); emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */ emit("ja near readl_iosplit\n"); emit("sub edx,byte 2\n"); /* Unsplit version */ airlock_exit(); emit("mov eax,edx\n"); /* address */ emit("mov edx,ecx\n"); /* pointer to structure */ emit("push eax\n"); emit("push edx\n"); if(use_stack){ emit("push edx\n"); emit("push eax\n"); } emit("call dword[edx+8]\n"); if(use_stack)emit("add esp,byte 8\n"); emit("pop edx\n"); emit("xchg eax,[esp]\n"); emit("add eax,byte 2\n"); /* maskaddress("eax");*/ /* this case would force a split read anyway */ if(use_stack){ emit("push edx\n"); emit("push eax\n"); } emit("call dword[edx+8]\n"); if(use_stack)emit("add esp,byte 8\n"); emit("pop ecx\n"); emit("shl ecx,16\n"); emit("mov cx,ax\n"); airlock_enter(); emit("mov edx,[__access_address]\n"); emit("ret\n"); emit("readl_split:\n"); emit("sub edx,[ecx]\n"); emit("add edx,[ecx+12]\n"); emit("mov cx,[edx-2]\n"); emit("readl_lower:\n"); emit("mov edx,[esp]\n"); emit("add edx,byte 2\n"); emit("shl ecx,16\n"); emit("push ecx\n"); emit("call readmemoryword\n"); emit("and ecx,0FFFFh\n"); emit("or ecx,[esp]\n"); emit("add esp,byte 4\n"); emit("mov edx,[__access_address]\n"); emit("ret\n"); emit("readl_iosplit:\n"); emit("sub edx,byte 2\n"); airlock_exit(); emit("mov eax,edx\n"); /* address */ emit("mov edx,ecx\n"); /* pointer to structure */ if(use_stack){ emit("push edx\n"); emit("push eax\n"); } emit("call dword[edx+8]\n"); if(use_stack)emit("add esp,byte 8\n"); emit("mov ecx,eax\n"); airlock_enter(); emit("jmp short readl_lower\n"); } static void gen_writebw(int size){ char z='x'; if(size==1)z='b'; if(size==2)z='w'; align(16); emit("writememory%s:\n",sizename[size]); emit("mov [__access_address],edx\n"); emit("push ecx\n"); emit("write%c_top:\n",z); maskaddress("edx"); emit("mov ecx,[__write%s]\n",sizename[size]); emit("write%c_check:\n",z); emit("cmp edx,[ecx]\n"); /* Are we smaller? */ emit("jb short write%c_next\n",z); /* Yes, go to next address */ emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */ emit("jbe short write%c_call\n",z); emit("write%c_next:\n",z); emit("cmp dword[ecx],byte -1\n");/* End of list? */ emit("je short write%c_end\n",z); emit("add ecx,byte 16\n"); /* To the next structure */ emit("jmp short write%c_check\n",z); emit("write%c_call:\n",z); emit("cmp dword[ecx+8],byte 0\n"); emit("jne short write%c_callio\n",z); emit("sub edx,[ecx]\n"); emit("add edx,[ecx+12]\n"); if(z=='b')emit("xor edx,byte 1\n"); emit("pop ecx\n"); emit("mov [edx],%s\n",x86cx[size]); emit("mov edx,[__access_address]\n"); emit("ret\n"); emit("write%c_callio:\n",z); /* --- we have: ** index in ecx ** address in edx ** value in [esp] ** --- we want: ** index in anything (saved first) ** address in eax ** value in edx */ airlock_exit(); emit("mov ebx,ecx\n"); /* pointer to structure */ emit("mov eax,edx\n"); /* address */ emit("xor edx,edx\n"); /* data */ emit("mov %s,[esp+%d]\n",x86dx[size],airlock_stacksize); if(use_stack){ emit("push ebx\n"); emit("push edx\n"); emit("push eax\n"); } emit("call dword[ebx+8]\n"); if(use_stack)emit("add esp,byte 12\n"); airlock_enter(); emit("write%c_end:\n",z); emit("pop ecx\n"); emit("mov edx,[__access_address]\n"); emit("ret\n"); } static void gen_writel(void){ align(16); emit("writememory%s:\n",sizename[4]); emit("mov [__access_address],edx\n"); emit("push ecx\n"); maskaddress("edx"); emit("mov ecx,[__write%s]\n",sizename[2]); emit("writel_check:\n"); emit("cmp edx,[ecx]\n"); /* Are we smaller? */ emit("jb short writel_next\n"); /* Yes, go to next address */ emit("cmp edx,[ecx+4]\n"); /* Are we bigger? */ emit("jbe short writel_call\n"); emit("writel_next:\n"); emit("cmp dword[ecx],byte -1\n");/* End of list? */ emit("je short writel_outofrange\n"); emit("add ecx,byte 16\n"); /* To the next structure */ emit("jmp short writel_check\n"); /* The top word was out of range, but the bottom word might not be */ emit("writel_outofrange:\n"); emit("add edx,byte 2\n"); emit("jmp writew_top\n"); emit("writel_call:\n"); emit("cmp dword[ecx+8],byte 0\n"); emit("jne short writel_callio\n"); emit("add edx,byte 2\n"); emit("cmp edx,[ecx+4]\n"); emit("ja short writel_split\n"); emit("sub edx,[ecx]\n"); emit("add edx,[ecx+12]\n"); emit("pop ecx\n"); emit("rol ecx,16\n"); emit("mov [edx-2],ecx\n"); emit("mov edx,[__access_address]\n"); emit("rol ecx,16\n"); // Stef Fix (Gens) emit("ret\n"); emit("writel_callio:\n"); emit("add edx,byte 2\n"); emit("cmp edx,[ecx+4]\n"); emit("ja short writel_iosplit\n"); emit("sub edx,byte 2\n"); /* Unsplit version */ /* --- we have: ** index in ecx ** address in edx ** value in [esp] ** --- we want: ** index in anything (saved first) ** address in eax ** value in edx */ airlock_exit(); emit("mov ebx,ecx\n"); /* pointer to structure */ emit("mov eax,edx\n"); /* address */ emit("xor edx,edx\n"); /* data */ emit("mov dx,[esp+%d]\n",airlock_stacksize+2); emit("push eax\n"); emit("push ebx\n"); if(use_stack){ emit("push ebx\n"); emit("push edx\n"); emit("push eax\n"); } emit("call dword[ebx+8]\n"); if(use_stack)emit("add esp,byte 12\n"); emit("pop ebx\n"); emit("pop eax\n"); emit("add eax,byte 2\n"); /* maskaddress("eax");*/ /*this case would force a split write anyway */ emit("xor edx,edx\n"); /* data */ emit("mov dx,[esp+%d]\n",airlock_stacksize); if(use_stack){ emit("push ebx\n"); emit("push edx\n"); emit("push eax\n"); } emit("call dword[ebx+8]\n"); if(use_stack)emit("add esp,byte 12\n"); airlock_enter(); emit("writel_end:\n"); emit("pop ecx\n"); emit("mov edx,[__access_address]\n"); emit("ret\n"); emit("writel_split:\n"); emit("sub edx,[ecx]\n"); emit("add edx,[ecx+12]\n"); emit("mov cx,[esp+2]\n"); emit("mov [edx-2],cx\n"); emit("mov edx,[esp+4]\n"); emit("add edx,byte 2\n"); emit("jmp writew_top\n"); emit("writel_iosplit:\n"); /* --- we have: ** index in ecx ** address in edx ** value in [esp] ** --- we want: ** index in anything (saved first) ** address in eax ** value in edx */ airlock_exit(); emit("mov ebx,ecx\n"); /* pointer to structure */ emit("mov eax,edx\n"); /* address */ emit("xor edx,edx\n"); /* data */ emit("mov dx,[esp+%d]\n",airlock_stacksize+2); if(use_stack){ emit("push ebx\n"); emit("push edx\n"); emit("push eax\n"); } emit("call dword[ebx+8]\n"); if(use_stack)emit("add esp,byte 12\n"); airlock_enter(); emit("mov edx,[esp+4]\n"); emit("add edx,byte 2\n"); emit("jmp writew_top\n"); } /***************************************************************************/ /* ** Group 1 and 2 exceptions ** Exception address is passed in EDX ** ** Does not fix the new PC! */ static void gen_group_12_exception(void) { align(16); emit("group_1_exception:\n"); emit("group_2_exception:\n"); emit("and byte[__interrupts],0FEh\n"); /* first thing's first */ if(cputype == 68010) { emit("mov byte[__loopmode],0\n"); } if(cputype >= 68010) { emit("push edx\n"); emit("add edx,[__vbr]\n"); } emit("call readmemorydword\n"); if(cputype >= 68010) { emit("pop edx\n"); } emit("push ecx\n");/* dest. PC */ sr2cx(); emit("push ecx\n");/* old SR */ supervisor(); /* ** Exception handlers do not like being traced, so clear the SR trace ** flag as well as the trace tricky bit. ** ** Leave the cycle leftover count alone, in case we still need to ** call attention to other unrelated tricky bits. */ emit("and byte[__sr+1],27h\n"); emit("mov byte[__trace_trickybit],0\n"); emit("mov ecx,esi\n"); emit("sub ecx,ebp\n"); if(cputype >= 68010) { emit("push ecx\n");/* old PC */ emit("mov ecx,edx\n"); } emit("mov edx,[__a7]\n"); if(cputype >= 68010) { emit("and ecx,0FFCh\n");/* Format code */ emit("sub edx,byte 2\n"); emit("call writememoryword\n"); emit("pop ecx\n"); } emit("sub edx,byte 4\n"); emit("call writememorydword\n"); emit("pop ecx\n");/* old SR */ emit("sub edx,byte 2\n"); emit("call writememoryword\n"); emit("mov [__a7],edx\n"); emit("pop esi\n");/* dest. PC */ emit("ret\n"); } /***************************************************************************/ /* Privilege violation */ static void gen_privilege_violation(void){ align(16); emit("privilege_violation:\n"); emit("sub esi,byte 2\n"); emit("mov edx,20h\n"); emit("call group_1_exception\n"); perform_cached_rebase(); ret_timing((cputype==68010)?38:34); } /***************************************************************************/ static void usereg(void) { emit("and ebx,byte 7\n"); } /* usereg only where applicable */ static void selective_usereg(void) { switch(main_eamode) { case dreg: case areg: case aind: case ainc: case adec: case adsp: case axdp: usereg(); default: break; } } static void selftest(int size) { emit("test %s,%s\n", x86cx[size], x86cx[size]); } /***************************************************************************/ /* Get condition: Less Than (N^V) ** If true, the x86 sign flag will be set */ static void getcondition_l_s_ns(void) { emit("push eax\n"); emit("neg al\n"); emit("xor al,ah\n"); emit("pop eax\n"); } /* Get condition: Less Than or Equal ((N^V)|Z) ** If true, the x86 sign flag will be set */ static void getcondition_le_s_ns(void) { emit("push eax\n"); emit("neg al\n"); emit("xor al,ah\n"); emit("add ah,ah\n"); emit("or al,ah\n"); emit("pop eax\n"); } static char optcc[5]; static char optrc[5]; static void getcondition(int cc) { switch(cc) { case 0x0: case 0x1: break; case 0x2:/* a */ emit("test ah,41h\n"); sprintf(optcc, "z"); sprintf(optrc, "nz"); break; case 0x3:/* be */ emit("test ah,41h\n"); sprintf(optcc, "nz"); sprintf(optrc, "z"); break; case 0x4:/* nc */ emit("test ah,1\n"); sprintf(optcc, "z"); sprintf(optrc, "nz"); break; case 0x5:/* c */ emit("test ah,1\n"); sprintf(optcc, "nz"); sprintf(optrc, "z"); break; case 0x6:/* ne */ emit("test ah,40h\n"); sprintf(optcc, "z"); sprintf(optrc, "nz"); break; case 0x7:/* e */ emit("test ah,40h\n"); sprintf(optcc, "nz"); sprintf(optrc, "z"); break; case 0x8:/* no */ emit("test al,1\n"); sprintf(optcc, "z"); sprintf(optrc, "nz"); break; case 0x9:/* o */ emit("test al,1\n"); sprintf(optcc, "nz"); sprintf(optrc, "z"); break; case 0xA:/* ns */ emit("or ah,ah\n"); sprintf(optcc, "ns"); sprintf(optrc, "s"); break; case 0xB:/* s */ emit("or ah,ah\n"); sprintf(optcc, "s"); sprintf(optrc, "ns"); break; case 0xC:/* ge */ getcondition_l_s_ns(); sprintf(optcc, "ns"); sprintf(optrc, "s"); break; case 0xD:/* l */ getcondition_l_s_ns(); sprintf(optcc, "s"); sprintf(optrc, "ns"); break; case 0xE:/* g */ getcondition_le_s_ns(); sprintf(optcc, "ns"); sprintf(optrc, "s"); break; case 0xF:/* le */ getcondition_le_s_ns(); sprintf(optcc, "s"); sprintf(optrc, "ns"); break; default:break; } } static void flags(void) { emit("lahf\n"); emit("seto al\n"); } static void flags_v0(void) { emit("lahf\n"); emit("mov al,0\n"); } /* Put one of the x86 flags into the 68K zero flag. */ static void flag_to_z(char *f) { int myline = linenum; linenum += 2; emit("j%s short ln%d\n", f, myline); emit("and ah,0BFh\n"); emit("jmp short ln%d\n", myline + 1); emit("ln%d:\n", myline); emit("or ah,40h\n"); emit("ln%d:\n", myline + 1); } /* carry to X flag */ static void c2x(void) { emit("setc [__xflag]\n"); } /* with previous flags in another register, adjust for non-changing zero */ static void adjzero(char *reg) { int myline = linenum; linenum++; emit("jnz short ln%d\n", myline); emit("or %s,0BFh\n", reg); emit("and ah,%s\n", reg); emit("ln%d:\n", myline); } /* Check for privilege violation */ static void privilegecheck(void) { emit("test byte[__sr+1],20h\n"); emit("jz near privilege_violation\n"); } /**************************************************************************** ** EFFECTIVE ADDRESS GENERATION ****************************************************************************/ /* ** There are five types of EA activity: ** ** 1. Read: precalc -> read -> postcalc ** 2. Write: precalc -> write -> postcalc ** 3. R-M-W: precalc -> read -> (modify) -> write -> postcalc ** 4. Move: Read followed by Write ** 5. Control: precalc */ /* Calculate address */ static void ea_step_precalc(int size, enum eamode mode, int reg) { char regs[100]; if(reg == -1) sprintf(regs, "ebx*4"); else sprintf(regs, "%d", reg * 4); switch(mode) { case dreg: case areg: break; case aind: case ainc: case adec: emit("mov edx,[__areg+%s]\n",regs); if(mode == adec) { /* Compensate for byte-sized stack ops */ if(size == 1) { if(reg == -1) { emit("cmp bl,7\n"); emit("adc edx,byte -2\n"); } else if(reg == 7) { emit("sub edx,byte 2\n"); } else { emit("dec edx\n"); } } else { emit("sub edx,byte %d\n", size); } } break; case adsp: emit("movsx edx,word[esi]\n"); emit("add esi,byte 2\n"); emit("add edx,[__areg+%s]\n", regs); break; case axdp: emit("call decode_ext\n"); emit("add edx,[__areg+%s]\n", regs); break; case absw: emit("movsx edx,word[esi]\n"); emit("add esi,byte 2\n"); break; case absl: emit("mov edx,dword[esi]\n"); emit("add esi,byte 4\n"); emit("rol edx,16\n"); break; case pcdp: emit("movsx edx,word[esi]\n"); emit("add edx,esi\n"); emit("sub edx,ebp\n"); emit("add esi,byte 2\n"); break; case pcxd: emit("call decode_ext\n"); emit("add edx,esi\n"); emit("sub edx,ebp\n"); emit("sub edx,byte 2\n"); break; case immd: break; default: emit("#error ea_step_precalc\n"); break; } } static void ea_step_read(int size, enum eamode mode, int reg) { char regs[100]; if(reg == -1) sprintf(regs, "ebx*4"); else sprintf(regs, "%d", reg * 4); switch(mode) { case dreg: emit("mov ecx,[__dreg+%s]\n", regs); break; case areg: emit("mov ecx,[__areg+%s]\n", regs); break; case aind: case ainc: case adec: case adsp: case axdp: case absw: case absl: case pcdp: case pcxd: emit("call readmemory%s\n", sizename[size]); break; case immd: switch(size) { case 1: case 2: emit("mov cx,[esi]\n"); emit("add esi,byte 2\n"); break; case 4: emit("mov ecx,[esi]\n"); emit("add esi,byte 4\n"); emit("rol ecx,16\n"); break; default: emit("#error ea_step_read\n"); break; } break; default: emit("#error ea_step_read\n"); break; } } /* ** Special case for when you need to load a word and sign-extend it. ** This cuts some fat out of a few instructions (i.e. MOVEA). */ static void ea_step_read_signword(enum eamode mode, int reg) { char regs[100]; if(reg == -1) sprintf(regs, "ebx*4"); else sprintf(regs, "%d", reg * 4); switch(mode) { case dreg: emit("movsx ecx,word[__dreg+%s]\n", regs); break; case areg: emit("movsx ecx,word[__areg+%s]\n", regs); break; case aind: case ainc: case adec: case adsp: case axdp: case absw: case absl: case pcdp: case pcxd: emit("call readmemory%s\n", sizename[2]); emit("movsx ecx,cx\n"); break; case immd: emit("movsx ecx,word[esi]\n"); emit("add esi,byte 2\n"); break; default: emit("#error ea_step_read_signword\n"); break; } } static void ea_step_write(int size, enum eamode mode, int reg) { char regs[100]; if(reg == -1) sprintf(regs, "ebx*4"); else sprintf(regs, "%d", reg * 4); switch(mode) { case dreg: emit("mov [__dreg+%s],%s\n", regs, x86cx[size]); break; case aind: case ainc: case adec: case adsp: case axdp: case absw: case absl: emit("call writememory%s\n", sizename[size]); break; default: emit("#error ea_step_write\n"); break; } } static void ea_step_postcalc(int size, enum eamode mode, int reg) { char regs[100]; if(reg == -1) sprintf(regs, "ebx*4"); else sprintf(regs, "%d", reg * 4); switch(mode) { case ainc: /* Compensate for byte-sized stack ops */ if(size == 1) { if(reg == -1) { emit("cmp bl,7\n"); emit("sbb edx,byte -2\n"); } else if(reg == 7) { emit("add edx,byte 2\n"); } else { emit("inc edx\n"); } } else { emit("add edx,byte %d\n", size); } /* Fall through */ case adec: /* Store already-predecremented address */ emit("mov [__areg+%s],edx\n", regs); break; case dreg: case areg: case aind: case adsp: case axdp: case absw: case absl: case pcdp: case pcxd: case immd: break; default: emit("#error ea_step_postcalc\n"); break; } } /* Combined EA routines */ static void ea_load(int size, enum eamode mode, int reg) { ea_step_precalc (size, mode, reg); ea_step_read (size, mode, reg); ea_step_postcalc(size, mode, reg); } static void ea_load_signword(enum eamode mode, int reg) { ea_step_precalc (2, mode, reg); ea_step_read_signword( mode, reg); ea_step_postcalc (2, mode, reg); } static void ea_store(int size, enum eamode mode, int reg) { ea_step_precalc (size, mode, reg); ea_step_write (size, mode, reg); ea_step_postcalc(size, mode, reg); } static void ea_rmw_load(int size, enum eamode mode, int reg) { ea_step_precalc (size, mode, reg); ea_step_read (size, mode, reg); } static void ea_rmw_store(int size, enum eamode mode, int reg) { ea_step_write (size, mode, reg); ea_step_postcalc(size, mode, reg); } static void ea_control(enum eamode mode, int reg) { ea_step_precalc (0, mode, reg); } static void main_ea_load(void) { ea_load(main_size, main_eamode, -1); } static void main_ea_load_signed(void) { if(main_size < 4) { ea_load_signword(main_eamode, -1); } else { ea_load(main_size, main_eamode, -1); } } static void main_ea_store(void) { ea_store(main_size, main_eamode, -1); } static void main_ea_rmw_load(void) { ea_rmw_load(main_size, main_eamode, -1); } static void main_ea_rmw_store(void) { ea_rmw_store(main_size, main_eamode, -1); } static void main_ea_control(void) { ea_control(main_eamode, -1); } /***************************************************************************/ /* ** Calculate cycles for main EA mode ** (68000, 68010) */ static int main_ea_cycles(void) { int l; if(main_size == 4) l = 4; else l = 0; switch(main_eamode) { case aind: return(l + 4); case ainc: return(l + 4); case adec: return(l + 6); case adsp: return(l + 8); case axdp: return(l + 10); case absw: return(l + 8); case absl: return(l + 12); case pcdp: return(l + 8); case pcxd: return(l + 10); case immd: return(l + 4); default: break; } return 0; } /* Calculate cycles for main EA mode, without fetching (68010 only) */ static int main_ea_cycles_nofetch(void){ switch(main_eamode) { case aind: return(2); case ainc: return(4); case adec: return(4); case adsp: return(4); case axdp: return(8); case absw: return(4); case absl: return(8); default: break; } return 0; } /**************************************************************************** ** PREFIXES / SUFFIXES ****************************************************************************/ /* ** Prefixes - stuff that appears before the instruction handling routines */ static void prefixes(void) { /* Basic stuff - banner, variable section, API */ gen_banner(); gen_variables(); gen_interface(); /* Internal functions - PC rebasing, I/O, etc. */ gen_basefunction(); gen_decode_ext(); gen_readbw(1); gen_readbw(2); gen_readl(); gen_writebw(1); gen_writebw(2); gen_writel(); gen_group_12_exception(); gen_privilege_violation(); gen_flush_interrupts(); } /* ** Suffixes - stuff that appears after the instruction handling routines and ** the jump table / loop info table */ static void suffixes(void) { emit("\n"); emit("%%ifdef NASM_STACK_NOEXEC\n"); emit("section .note.GNU-stack noalloc noexec nowrite progbits\n"); emit("%%endif\n"); } /**************************************************************************** ** INSTRUCTION HANDLING ROUTINES ****************************************************************************/ /* called 600 times (!) */ static void i_move(void) { int cycles; selective_usereg(); main_ea_load(); ea_store(main_size, main_destmode, main_reg); selftest(main_size); flags_v0(); cycles = 4 + main_ea_cycles(); switch(main_destmode) { case aind: if(main_size == 4) cycles += 4; cycles += 4; break; case ainc: if(main_size == 4) cycles += 4; cycles += 4; break; case adec: if(main_size == 4) cycles += 4; cycles += 4; break; case adsp: if(main_size == 4) cycles += 4; cycles += 8; break; case axdp: if(main_size == 4) cycles += 4; cycles += 10; break; case absw: if(main_size == 4) cycles += 4; cycles += 8; break; case absl: if(main_size == 4) cycles += 4; cycles += 12; break; default: break; } /* Calculate loop mode timings */ if(cputype == 68010) { switch(main_eamode) { case dreg: case areg: switch(main_destmode) { case aind: case ainc: if(main_size <= 2) { loop_c_cycles = 2; loop_t_cycles = 10; loop_x_cycles = 8; }else{ loop_c_cycles = 2; loop_t_cycles = 8; loop_x_cycles = 6; } break; default:break; } break; case aind: case ainc: case adec: switch(main_destmode) { case aind: case ainc: loop_c_cycles = 2; loop_t_cycles = 8; loop_x_cycles = 6; break; case adec: loop_c_cycles = 4; loop_t_cycles = 10; loop_x_cycles = 8; break; default:break; } break; default:break; } } ret_timing(cycles); } static void i_moveq(void) { emit("movsx ecx,bl\n"); emit("mov [__dreg+%d],ecx\n", main_reg * 4); selftest(1); flags_v0(); /* No loop mode */ ret_timing(4); } static void op_to_areg(char *s) { selective_usereg(); main_ea_load_signed(); /* sign extends to ecx where necessary */ emit("%s [__areg+%d],ecx\n", s, main_reg * 4); } static void i_movea(void) { op_to_areg("mov"); /* No loop mode */ ret_timing(4 + main_ea_cycles()); } /* ADDA or SUBA */ static void addsuba(char *op) { int base_cycles; op_to_areg(op); if(main_size==4){ base_cycles=6; /* Register direct / immediate penalty (68000) */ if(cputype==68000){ switch(main_eamode){ case areg:case dreg:case immd: base_cycles+=2; break; default:break; } } }else{ base_cycles=8; } /* Calculate loop mode timings */ if(cputype==68010){ switch(main_eamode){ case aind:case ainc:case adec: if(main_size<=2){ loop_c_cycles = 6; loop_t_cycles = 12; loop_x_cycles = 10; }else{ loop_c_cycles = 8; loop_t_cycles = 14; loop_x_cycles = 12; } break; default:break; } } ret_timing(base_cycles+main_ea_cycles()); } static void i_adda(void){addsuba("add");} static void i_suba(void){addsuba("sub");} static void i_cmpa(void){ op_to_areg("cmp"); flags(); /* Calculate loop mode timings */ if(cputype==68010){ switch(main_eamode){ case aind:case ainc:case adec: if(main_size<=2){ loop_c_cycles = 2; loop_t_cycles = 8; loop_x_cycles = 6; }else{ loop_c_cycles = 4; loop_t_cycles = 10; loop_x_cycles = 6; } break; default:break; } } ret_timing(6+main_ea_cycles()); } static void i_move_to_sr(void){ main_size=2; privilegecheck(); selective_usereg(); main_ea_load(); cx2sr(); /* No loop mode; check PPL and trace flag */ ret_timing_checkpoint(12+main_ea_cycles()); } static void i_move_to_ccr(void){ main_size=2;/* WEIRD! But it works! */ selective_usereg(); main_ea_load(); cl2ccr(); /* No loop mode */ ret_timing(12+main_ea_cycles()); } static void i_move_from_sr(void){ int cycles; /* This is privileged on 68010 and up */ if(cputype>=68010)privilegecheck(); main_size=2; selective_usereg(); sr2cx(); main_ea_store(); if(cputype==68010){ cycles=8+main_ea_cycles_nofetch(); }else{ cycles=8+main_ea_cycles(); } if((main_eamode==dreg)||(main_eamode==areg)){ cycles-=2; /* Speed demon 68010 can do it in 2 fewer cycles... :p */ if(cputype==68010)cycles-=2; } /* No loop mode */ ret_timing(cycles); } /* 68000/68008 aren't supposed to have this */ static void i_move_from_ccr(void){ int cycles; main_size=2; selective_usereg(); ccr2cl(); main_ea_store(); if(cputype==68010){ cycles=8+main_ea_cycles_nofetch(); }else{ cycles=8+main_ea_cycles(); } if((main_eamode==dreg)||(main_eamode==areg)){ cycles-=2; if(cputype==68010)cycles-=2; } /* No loop mode */ ret_timing(cycles); } static void op_to_ccr(char*op){ ccr2cl(); emit("%s cl,[esi]\n",op); emit("add esi,byte 2\n"); cl2ccr(); /* No loop mode */ ret_timing((cputype==68010)?16:20); } static void op_to_sr(char*op){ privilegecheck(); sr2cx(); emit("%s cx,[esi]\n",op); emit("add esi,byte 2\n"); cx2sr(); /* No loop mode */ /* Check PPL and trace flag */ ret_timing_checkpoint((cputype==68010)?16:20); } static void i_ori_ccr(void){op_to_ccr("or" );} static void i_andi_ccr(void){op_to_ccr("and");} static void i_eori_ccr(void){op_to_ccr("xor");} static void i_ori_sr (void){op_to_sr ("or" );} static void i_andi_sr (void){op_to_sr ("and");} static void i_eori_sr (void){op_to_sr ("xor");} static void i_clr(void){ int cycles=0; selective_usereg(); emit("xor ecx,ecx\n"); main_ea_store(); if(cputype==68000){ cycles=main_ea_cycles(); if((main_eamode==dreg)||(main_eamode==areg)){ cycles+=4;if(main_size==4)cycles+=4; }else{ cycles+=6;if(main_size==4)cycles+=6; } }else if(cputype==68010){ switch(main_eamode){ case dreg:cycles= 4;break; case aind:cycles= 8;break; case ainc:cycles= 8;break; case adec:cycles=10;break; case adsp:cycles=12;break; case axdp:cycles=16;break; case absw:cycles=12;break; case absl:cycles=16;break; default:break; } if(main_size==4){ cycles+=4; if(main_eamode==dreg)cycles-=2; } /* Calculate loop mode timings */ switch(main_eamode){ case aind:case ainc:case adec: loop_c_cycles = 2; loop_t_cycles = 10; loop_x_cycles = 8; break; default:break; } } emit("mov ax,4000h\n"); ret_timing(cycles); } static void i_tst(void){ selective_usereg(); main_ea_load(); /* Calculate loop mode timings */ if(cputype==68010){ switch(main_eamode){ case aind:case ainc:case adec: if(main_size<=2){ loop_c_cycles = 4; loop_t_cycles = 10; loop_x_cycles = 8; }else{ loop_c_cycles = 6; loop_t_cycles = 12; loop_x_cycles = 8; } break; default:break; } } selftest(main_size); flags_v0(); ret_timing(4+main_ea_cycles()); } /* Always affects X flag ** (except for #,An which affects no flags whatsoever) */ static void op_quick(char*op){ int cycles; selective_usereg(); if(main_eamode==dreg){ emit( "%s %s[__dreg+ebx*4],byte %d\n", op,sizename[main_size],quickvalue[main_qv] ); flags(); c2x(); cycles=4; }else if(main_eamode==areg){ emit( "%s dword[__areg+ebx*4],byte %d\n", op,quickvalue[main_qv] ); cycles=4; /* SUBQ.W #,An incurs 4-cycle penalty (68000 only) */ if(cputype==68000){ if((main_size==2)&&(op[0]=='s'))cycles+=4; } }else{ main_ea_rmw_load(); emit( "%s %s,byte %d\n",op,x86cx[main_size],quickvalue[main_qv] ); flags(); c2x(); main_ea_rmw_store(); cycles=8+main_ea_cycles(); } if(main_size==4)cycles+=4; /* No loop mode */ ret_timing(cycles); } static void i_addq(void){op_quick("add");} static void i_subq(void){op_quick("sub");} static void op_to_dn(char*op,int affectx,int logical){ int cycles; selective_usereg(); main_ea_load(); emit("%s [__dreg+%d],%s\n", op,main_reg*4,x86cx[main_size] ); if(logical){ flags_v0(); }else{ flags(); } if(affectx)c2x(); cycles=4+main_ea_cycles(); if(main_size==4){ cycles+=2; /* Register direct / immediate penalty (68000) */ if((cputype==68000)&&(op[0]!='c')){ switch(main_eamode){ case areg:case dreg:case immd: cycles+=2; break; default:break; } } } /* Calculate loop mode timings */ if(cputype==68010){ switch(main_eamode){ case aind:case ainc:case adec: switch(op[0]){ case 'a':/* ADD, AND */ case 'o':/* OR */ loop_c_cycles = 8; loop_t_cycles = 14; loop_x_cycles = 12; break; case 's':/* SUB */ if(main_size<=2){ loop_c_cycles = 8; loop_t_cycles = 14; loop_x_cycles = 12; }else{ loop_c_cycles = 6; loop_t_cycles = 12; loop_x_cycles = 10; } break; case 'c':/* CMP */ loop_c_cycles = 4; loop_t_cycles = 10; loop_x_cycles = 8; if(main_size==4)loop_x_cycles = 6; break; default:break; } break; default:break; } } ret_timing(cycles); } static void i_cmp_dn(void){op_to_dn("cmp",0,0);} static void i_add_dn(void){op_to_dn("add",1,0);} static void i_sub_dn(void){op_to_dn("sub",1,0);} static void i_and_dn(void){op_to_dn("and",0,1);} static void i_or_dn (void){op_to_dn("or" ,0,1);} static void op_to_ea(char*op,int logical){ int cycles; selective_usereg(); main_ea_rmw_load(); emit( "%s %s,[__dreg+%d]\n", op,x86cx[main_size],main_reg*4 ); if(logical){ flags_v0(); }else{ flags(); } /* Logical instructions don't affect X flag */ if(!logical)c2x(); main_ea_rmw_store(); cycles=8+main_ea_cycles(); if(main_size==4)cycles+=4; /* EOR Dn,Dn takes fewer cycles than we'd expect */ if((op[0]=='x')&&(main_eamode==dreg)){ cycles-=4; if(cputype==68010)cycles-=2; } /* Calculate loop mode timings */ if(cputype==68010){ switch(main_eamode){ case aind:case ainc:case adec: loop_c_cycles = 4; loop_t_cycles = 10; loop_x_cycles = 8; break; default:break; } } ret_timing(cycles); } static void i_eor_ea(void){op_to_ea("xor",1);} static void i_add_ea(void){op_to_ea("add",0);} static void i_sub_ea(void){op_to_ea("sub",0);} static void i_and_ea(void){op_to_ea("and",1);} static void i_or_ea (void){op_to_ea("or" ,1);} /* called 144 times */ /* ** c1: Total cycles for #,Dn (byte/word) ** c2: Total cycles for #,Dn (long) ** c3: Basic cycles for #,M (byte/word) ** c4: Basic cycles for #,M (long) */ static void im_to_ea(char*op,int wback,int affectx,int logical, int c1,int c2,int c3,int c4){ int cycles; selective_usereg(); switch(main_size){ case 1: case 2: emit("mov cx,[esi]\n"); emit("add esi,byte 2\n"); break; case 4: emit("mov ecx,[esi]\n"); emit("rol ecx,16\n"); emit("add esi,byte 4\n"); break; default:break; } if(main_eamode==dreg){ emit("%s [__dreg+ebx*4],%s\n", op,x86cx[main_size] ); if(logical){ flags_v0(); }else{ flags(); } if(affectx)c2x(); if(main_size<4)cycles=c1;else cycles=c2; }else{ emit("push ecx\n"); if(wback)main_ea_rmw_load();else main_ea_load(); emit("%s %s,[esp]\n",op,x86cx[main_size]); if(logical){ flags_v0(); }else{ flags(); } if(affectx)c2x(); emit("add esp,byte 4\n"); if(wback)main_ea_rmw_store(); if(main_size<4)cycles=c3;else cycles=c4; cycles+=main_ea_cycles(); } /* No loop mode */ ret_timing(cycles); } /* each called 24 times */ static void i_addi(void){im_to_ea("add",1,1,0,8,(cputype==68010)?14:16,12,20);} static void i_subi(void){im_to_ea("sub",1,1,0,8,(cputype==68010)?14:16,12,20);} static void i_cmpi(void){im_to_ea("cmp",0,0,0,8,(cputype==68010)?12:14, 8,12);} static void i_andi(void){im_to_ea("and",1,0,1,8,(cputype==68010)?14:14,12,20);} static void i_ori (void){im_to_ea("or" ,1,0,1,8,(cputype==68010)?14:16,12,20);} static void i_eori(void){im_to_ea("xor",1,0,1,8,(cputype==68010)?14:16,12,20);} static void flick_reg(char*op,int needxf,int affectx,int asl,int rotate){ int cycles; char tmps[5]; int i; usereg(); cycles=6; if(main_size==4)cycles+=2; /* ASR doesn't need overflow checking */ if(direction[main_dr]=='r')asl=0; if(main_ir==1){ int myline=linenum;linenum++; emit("mov ecx,[__dreg+%d]\n",main_reg*4); emit("and ecx,byte 63\n"); emit("jnz short ln%d\n",myline); /* The shift count was zero. Strange things are about to ** happen... */ ea_load(main_size,dreg,-1);/* get data in eax */ selftest(main_size); flags_v0(); if(needxf){/* ROXL/ROXR: Set C flag equal to X */ emit("and ah,0FEh\n"); emit("or ah,[__xflag]\n"); } ret_timing(cycles); /* Shift count non-zero */ emit("ln%d:\n",myline); emit("sub edi,ecx\n"); emit("sub edi,ecx\n"); sprintf(tmps,"cl"); }else{ sprintf(tmps,"%d",quickvalue[main_reg]); cycles+=2*quickvalue[main_reg]; } if(asl){ switch(tmps[0]){ case 'c':/* register shift count */ emit("mov edx,[__dreg+ebx*4]\n"); emit("mov al,0\n");/* overflow starts at 0 */ emit("ln%d:\n",linenum); emit("add %s,%s\n", x86dx[main_size],x86dx[main_size] ); emit("lahf\n");/* grab N,Z,C flags */ emit("seto ch\n"); emit("or al,ch\n");/* add overflow */ emit("dec cl\n"); emit("jnz short ln%d\n",linenum);linenum++; emit("mov [__dreg+ebx*4],%s\n",x86dx[main_size]); if(affectx){ emit("mov cl,ah\n"); emit("and cl,1\n"); emit("mov [__xflag],cl\n"); } break; case '1':/* immediate shift count ==1 */ emit("sal %s[__dreg+ebx*4],1\n", sizename[main_size] ); flags(); if(affectx)c2x(); break; default:/* immediate shift count >1 */ emit("mov edx,[__dreg+ebx*4]\n"); emit("mov al,0\n");/* overflow starts at 0 */ for(i='1';i<=tmps[0];i++){ emit("add %s,%s\n", x86dx[main_size],x86dx[main_size] ); if(i==tmps[0]){ /* grab N,Z,C flags */ emit("lahf\n"); } emit("seto ch\n"); emit("or al,ch\n");/* add overflow */ } emit("mov [__dreg+ebx*4],%s\n",x86dx[main_size]); if(affectx){ emit("mov cl,ah\n"); emit("and cl,1\n"); emit("mov [__xflag],cl\n"); } break; } }else{ if(rotate){ emit("mov edx,[__dreg+ebx*4]\n"); /******** Stef Fix (Gens) *********/ // old code /* if(needxf){ emit("mov al,[__xflag]\n"); }else{ emit("mov al,0\n"); } emit("%s%c %s,%s\n", op,direction[main_dr],x86dx[main_size],tmps ); */ // new code if(needxf){ emit("mov al,[__xflag]\n"); emit("shr al,1\n"); }else{ emit("mov al,0\n"); } switch(tmps[0]) { case 'c':/* register shift count */ emit("cmp cl, 32\n"); emit("jb short ln%d\n",linenum); emit("%s%c %s, 16\n", op,direction[main_dr],x86dx[main_size]); emit("sub cl, 31\n"); emit("%s%c %s, 15\n", op,direction[main_dr],x86dx[main_size]); emit("ln%d:\n",linenum); linenum++; emit("%s%c %s,%s\n", op,direction[main_dr],x86dx[main_size],tmps); break; default:/* immediate shift count >1 */ emit("%s%c %s,%s\n", op,direction[main_dr],x86dx[main_size],tmps); break; } /******** End Fix *********/ emit("adc al,al\n"); emit("or %s,%s\n", x86dx[main_size],x86dx[main_size] ); emit("lahf\n"); emit("or ah,al\n"); if(affectx){ emit("mov [__xflag],al\n"); } emit("mov al,0\n"); emit("mov [__dreg+ebx*4],%s\n",x86dx[main_size]); }else{ if(needxf){ emit("mov al,[__xflag]\n"); }else{ emit("mov al,0\n"); } /******** Stef Fix (Gens) *********/ switch(tmps[0]) { case 'c':/* register shift count */ emit("cmp cl, 32\n"); emit("jb short ln%d\n",linenum); emit("sub cl, 31\n"); if(needxf){ emit("shr al, 1\n"); } emit("%s%c %s[__dreg+ebx*4], 31\n", op,direction[main_dr],sizename[main_size]); emit("jmp short ln%d\n",linenum + 1); emit("ln%d:\n",linenum); linenum++; if(needxf){ emit("shr al, 1\n"); } emit("%s%c %s[__dreg+ebx*4],%s\n", op,direction[main_dr],sizename[main_size],tmps); emit("ln%d:\n",linenum); linenum++; break; default:/* immediate shift count >1 */ if(needxf){ emit("shr al, 1\n"); } emit("%s%c %s[__dreg+ebx*4],%s\n", op,direction[main_dr],sizename[main_size],tmps); break; } /******** End Fix *********/ emit("lahf\n"); if(affectx)c2x(); } } /* No loop mode */ ret_timing(cycles); } static void i_lsx_reg(void){flick_reg("sh",0,1,0,0);} static void i_asx_reg(void){flick_reg("sa",0,1,1,0);} static void i_rox_reg(void){flick_reg("ro",0,0,0,1);} static void i_rxx_reg(void){flick_reg("rc",1,1,0,1);} static void flick_mem(char*op,int needxf,int affectx,int vf,int rotate){ /* ASR doesn't need overflow checking */ if(direction[main_dr]=='r')vf=0; main_size=2; selective_usereg(); main_ea_rmw_load(); if(needxf){ emit("mov al,[__xflag]\n"); emit("shr al,1\n"); }else{ if(rotate)emit("mov al,0\n"); } emit("%s%c cx,1\n",op,direction[main_dr]); if(rotate){ emit("adc al,al\n"); emit("test cx,cx\n"); emit("lahf\n"); emit("or ah,al\n"); if(affectx)emit("mov [__xflag],al\n"); emit("mov al,0\n"); }else{ if(vf){ flags(); }else{ flags_v0(); } if(affectx)c2x(); } main_ea_rmw_store(); /* Calculate loop mode timings */ if(cputype==68010){ switch(main_eamode){ case aind:case ainc:case adec: loop_c_cycles = 6; loop_t_cycles = 12; loop_x_cycles = 10; break; default:break; } } ret_timing(8+main_ea_cycles()); } static void i_lsx_mem(void){flick_mem("sh",0,1,0,0);} static void i_asx_mem(void){flick_mem("sa",0,1,1,0);} static void i_rox_mem(void){flick_mem("ro",0,0,0,1);} static void i_rxx_mem(void){flick_mem("rc",1,1,0,1);} static int created_bra_b=0; static void i_bra_b(void){ if(!created_bra_b){emit("r_bra_b:\n");created_bra_b=1;} emit("movsx ebx,bl\n"); emit("add esi,ebx\n"); emit("xor ebx,ebx\n"); ret_timing(10); } static int created_bra_w=0; static void i_bra_w(void){ if(!created_bra_w){emit("r_bra_w:\n");created_bra_w=1;} emit("movsx ebx,word[esi]\n"); emit("add esi,ebx\n"); emit("xor ebx,ebx\n"); ret_timing(10); } static void i_bsr_b(void){ emit("movsx ebx,bl\n"); emit("mov ecx,esi\n"); emit("sub ecx,ebp\n"); emit("add esi,ebx\n"); emit("xor ebx,ebx\n"); ea_store(4,adec,7); ret_timing(18); } static void i_bsr_w(void){ emit("movsx ebx,word[esi]\n"); emit("mov ecx,esi\n"); emit("add ecx,byte 2\n"); emit("sub ecx,ebp\n"); emit("add esi,ebx\n"); emit("xor ebx,ebx\n"); ea_store(4,adec,7); ret_timing(18); } static void i_bcc_b(void){ getcondition(main_cc); emit("j%s near r_bra_b\n",optcc); ret_timing((cputype==68010)?6:8); } static void i_bcc_w(void){ getcondition(main_cc); emit("j%s near r_bra_w\n",optcc); emit("add esi,byte 2\n");/* skip relative offset */ ret_timing(12); } /* called once */ static void i_dbra(void){ emit("r_dbra:\n"); usereg(); if(cputype==68010){ emit("movsx ecx,word[esi]\n"); emit("cmp ecx,byte -4\n"); emit("je short r_loopmode_dbra\n"); /* Regular DBRA */ emit("r_regular_dbra:\n"); emit("sub word[__dreg+ebx*4],byte 1\n"); emit("jc short r_dbra_expire\n"); emit("add esi,ecx\n"); ret_timing(10); emit("r_dbra_expire:\n"); emit("add esi,byte 2\n"); ret_timing(16); /* Loop mode DBRA */ emit("r_loopmode_dbra:\n"); emit("cmp byte[__loopmode],0\n"); emit("jnz short r_loopmode_dbra_inloop\n"); emit("mov byte[__loopmode],1\n"); emit("jmp short r_regular_dbra\n"); emit("r_loopmode_dbra_inloop:\n"); emit("sub word[__dreg+ebx*4],byte 1\n"); emit("jc short r_loopmode_dbra_expire\n"); /* Continue */ emit("mov bx,word[esi-4]\n"); emit("add esi,ecx\n"); /* Subtract continuation cycles */ emit("mov cl,byte[__looptbl+ebx]\n"); emit("and ecx,byte 0Eh\n"); emit("sub edi,ecx\n"); ret_timing(0); /* Expire */ emit("r_loopmode_dbra_expire:\n"); emit("mov bx,word[esi-4]\n"); emit("mov byte[__loopmode],0\n"); /* Subtract continuation and extra expiration cycles */ emit("mov cl,byte[__looptbl+ebx]\n"); emit("mov ebx,ecx\n"); emit("rol cl,2\n"); emit("add esi,byte 2\n"); emit("and ebx,byte 0Eh\n"); emit("and ecx,byte 06h\n"); emit("sub edi,ebx\n"); emit("sub edi,ecx\n"); ret_timing(0); /* Terminate (visited externally by i_dbcc) */ emit("r_dbra_terminate:\n"); emit("cmp word[esi],byte -4\n"); emit("jne short r_dbra_terminate_regular\n"); emit("cmp byte[__loopmode],0\n"); emit("je short r_dbra_terminate_regular\n"); emit("mov bx,word[esi-4]\n"); emit("mov byte[__loopmode],0\n"); /* Subtract termination cycles */ emit("mov cl,byte[__looptbl+ebx]\n"); emit("shr cl,3\n"); emit("add esi,byte 2\n"); emit("and ecx,byte 0Eh\n"); emit("sub edi,ecx\n"); ret_timing(0); emit("r_dbra_terminate_regular:\n"); ret_timing(10); }else{ emit("sub word[__dreg+ebx*4],byte 1\n"); emit("jnc near r_bra_w\n"); emit("add esi,byte 2\n"); ret_timing(14); } } /******** Stef Fix (Gens) *********/ // code added static void i_dbtr(void){ emit("add esi,byte 2\n"); ret_timing(8); } /******** End Fix *********/ static void i_dbcc(void){ getcondition(main_cc); emit("j%s near r_dbra\n",optrc); /* Terminate */ if(cputype==68010){ emit("jmp r_dbra_terminate\n"); }else{ emit("add esi,byte 2\n"); ret_timing(12); } } static void i_scc(void){ int cycles; main_size=1; selective_usereg(); if(main_cc>1){ if((main_eamode==dreg)||(main_eamode==areg)){ if(cputype==68000){ emit("xor ecx,ecx\n"); } getcondition(main_cc); emit("set%s cl\n",optcc); if(cputype==68000){ /* 2 extra cycles if it's true */ emit("sub edi,ecx\n"); emit("sub edi,ecx\n"); } emit("neg cl\n"); cycles=4; }else{ getcondition(main_cc); emit("set%s cl\n",optcc); emit("neg cl\n"); cycles=8; } }else{ emit("mov cl,%d\n",(main_cc^1)*0xFF); if((main_eamode==dreg)||(main_eamode==areg)){ cycles=4; if(cputype==68000){ /* 2 extra cycles if it's true */ if(!main_cc)cycles+=2; } }else{ cycles=8; } } main_ea_store(); if(cputype==68010){ cycles+=main_ea_cycles_nofetch(); }else{ cycles+=main_ea_cycles(); } /* No loop mode */ ret_timing(cycles); } /* bit operations */ /* called 315 times */ /* 0=btst,1=bchg,2=bclr,3=bset */ static void bitop(int static_cycles){ int cycles; selective_usereg(); emit("and ecx,byte %d\n",(main_eamode==dreg)?31:7); if(main_eamode==dreg){ main_size=4; if(!main_cc){/* BTST */ emit("mov edx,1\n"); emit("shl edx,cl\n"); emit("test [__dreg+ebx*4],edx\n"); flag_to_z("z"); }else{ emit("mov edx,1\n"); emit("shl edx,cl\n"); emit("mov ecx,[__dreg+ebx*4]\n"); emit("test ecx,edx\n"); flag_to_z("z"); switch(main_cc){ case 1:emit("xor ecx,edx\n");break; case 2:emit("not edx\nand ecx,edx\n");break; case 3:emit("or ecx,edx\n");break; default:break; } emit("mov [__dreg+ebx*4],ecx\n"); } cycles=6+static_cycles; if(main_cc)cycles+=2; if(main_cc==2)cycles+=2; }else{ main_size=1; if(!main_cc){ emit("push ecx\n"); main_ea_load(); emit("mov edx,ecx\n"); emit("pop ecx\n"); emit("inc cl\n"); emit("shr dl,cl\n"); flag_to_z("nc"); }else{ emit("mov dl,1\n"); emit("shl dl,cl\n"); emit("push edx\n"); main_ea_rmw_load(); emit("xchg edx,[esp]\n"); emit("test cl,dl\n"); flag_to_z("z"); switch(main_cc){ case 1:emit("xor cl,dl\n");break; case 2:emit("not dl\nand cl,dl\n");break; case 3:emit("or cl,dl\n");break; default:break; } emit("pop edx\n"); main_ea_rmw_store(); } cycles=4+static_cycles+main_ea_cycles(); if(main_cc)cycles+=4; if((cputype==68010)&&(main_cc==2))cycles+=2; } ret_timing(cycles); } /* called 35 times */ static void i_bitop_imm(void){ emit("mov cl,[esi]\n"); emit("add esi,byte 2\n"); bitop(4); } /* called 280 times */ static void i_bitop_reg(void){ emit("mov cl,byte[__dreg+%d]\n",main_reg*4); bitop(0); } static void i_jmp(void){ int cycles=0; selective_usereg(); main_ea_control(); emit("mov esi,edx\n"); perform_cached_rebase(); switch(main_eamode){ case aind:cycles= 8;break; case adsp:cycles=10;break; case axdp:cycles=14;break; case absw:cycles=10;break; case absl:cycles=12;break; case pcdp:cycles=10;break; case pcxd:cycles=14;break; default:break; } /* No loop mode */ ret_timing(cycles); } static void i_jsr(void){ int cycles=0; selective_usereg(); main_ea_control(); emit("mov ecx,esi\n"); emit("sub ecx,ebp\n"); emit("mov esi,edx\n"); perform_cached_rebase(); ea_store(4,adec,7); switch(main_eamode){ case aind:cycles=16;break; case adsp:cycles=18;break; case axdp:cycles=22;break; case absw:cycles=18;break; case absl:cycles=20;break; case pcdp:cycles=18;break; case pcxd:cycles=22;break; default:break; } /* No loop mode */ ret_timing(cycles); } static void i_rts(void){ ea_load(4,ainc,7); emit("mov esi,ecx\n"); perform_cached_rebase(); /* No loop mode */ ret_timing(16); } /* 68010 and higher */ static void i_rtd(void){ emit("mov edx,[__a7]\n"); emit("call readmemorydword\n"); emit("movsx ebx,word[esi]\n"); emit("add edx,ebx\n"); emit("mov esi,ecx\n"); emit("add edx,byte 4\n"); emit("xor ebx,ebx\n"); emit("mov [__a7],edx\n"); perform_cached_rebase(); /* No loop mode */ ret_timing(16); } static void i_rtr(void){ ea_load(2,ainc,7); cl2ccr(); ea_load(4,ainc,7); emit("mov esi,ecx\n"); perform_cached_rebase(); /* No loop mode */ ret_timing(20); } static void i_rte(void){ int myline=linenum; linenum++; privilegecheck(); if(cputype>=68010){ /* Check stack frame format - must be 0xxx or 8xxx */ emit("mov edx,[__a7]\n"); emit("add edx,byte 6\n"); emit("call readmemory%s\n",sizename[2]); emit("test ch,70h\n"); emit("jnz short ln%d_formatok\n",myline); /* Generate Format Error exception where necessary */ emit("mov edx,38h\n"); emit("call group_1_exception\n"); perform_cached_rebase(); ret_timing(50);/* RTE, Illegal Format */ emit("ln%d_formatok:\n",myline); /* Now _we_ check to make sure the format isn't 8xxx (since ** that's not implemented yet). */ emit("or ch,ch\n"); emit("jns short ln%d_formatok2\n",myline); /* Double fault with error code 80000002h. */ emit("mov ecx,80000002h\n"); emit("or byte[__pc],1\n"); emit("or byte[__interrupts],1\n"); emit("jmp execexit\n"); emit("ln%d_formatok2:\n",myline); /* Now RTE as usual */ emit("sub edx,byte 6\n"); emit("call readmemory%s\n",sizename[2]); emit("add edx,byte 2\n"); cx2sr(); emit("test ch,20h\n"); emit("jz short ln%d_nosupe\n",myline); emit("add dword [__a7],byte 8\n"); emit("jmp short ln%d_finish\n",myline); emit("ln%d_nosupe:\n",myline); emit("add dword [__asp],byte 8\n"); emit("ln%d_finish:\n",myline); emit("call readmemory%s\n",sizename[4]); emit("mov esi,ecx\n"); perform_cached_rebase(); ret_timing_checkpoint(24);/* RTE with no trouble */ }else{ emit("mov edx,[__a7]\n"); emit("call readmemory%s\n",sizename[2]); emit("add edx,byte 2\n"); cx2sr(); emit("test ch,20h\n"); emit("jz short ln%d_nosupe\n",myline); emit("add dword [__a7],byte 6\n"); emit("jmp short ln%d_finish\n",myline); emit("ln%d_nosupe:\n",myline); emit("add dword [__asp],byte 6\n"); emit("ln%d_finish:\n",myline); emit("call readmemory%s\n",sizename[4]); emit("mov esi,ecx\n"); perform_cached_rebase(); ret_timing_checkpoint(20); } } static void i_lea(void){ int cycles=0; selective_usereg(); main_ea_control(); emit("mov [__areg+%d],edx\n",main_reg*4); switch(main_eamode){ case aind:cycles= 4;break; case adsp:cycles= 8;break; case axdp:cycles=12;break; case absw:cycles= 8;break; case absl:cycles=12;break; case pcdp:cycles= 8;break; case pcxd:cycles=12;break; default:break; } /* No loop mode */ ret_timing(cycles); } static void i_pea(void){ int cycles=0; selective_usereg(); main_ea_control(); emit("mov ecx,edx\n"); ea_store(4,adec,7); switch(main_eamode){ case aind:cycles=12;break; case adsp:cycles=16;break; case axdp:cycles=20;break; case absw:cycles=16;break; case absl:cycles=20;break; case pcdp:cycles=16;break; case pcxd:cycles=20;break; default:break; } /* No loop mode */ ret_timing(cycles); } static void i_nop(void){ /* No loop mode */ ret_timing(4); } static void i_movem_control(void){ int cycles=0; int myline=linenum;linenum+=2; emit("push eax\n"); selective_usereg(); emit("mov ax,[esi]\n"); emit("add esi,byte 2\n"); main_ea_control(); emit("xor ebx,ebx\n"); emit("ln%d:\n",myline); emit("shr eax,1\n"); emit("jnc short ln%d\n",myline+1); if(main_dr==0){/*register->memory*/ switch(main_eamode){ case aind:cycles= 8;break; case adsp:cycles=12;break; case axdp:cycles=14;break; case absw:cycles=12;break; case absl:cycles=16;break; default:break; } emit("mov ecx,[__reg+ebx]\n"); emit("call writememory%s\n",sizename[main_size]); }else{/*memory->register*/ switch(main_eamode){ case aind:cycles=12; if((cputype==68010)&&(main_size==4))cycles=24; break; case adsp:cycles=16;break; case axdp:cycles=18;break; case absw:cycles=16;break; case absl:cycles=20;break; case pcdp:cycles=16;break; case pcxd:cycles=18;break; default:break; } emit("call readmemory%s\n",sizename[main_size]); if(main_size==2)emit("movsx ecx,cx\n"); emit("mov [__reg+ebx],ecx\n"); } emit("add edx,byte %d\n",main_size); emit("sub edi,byte %d\n",main_size*2); emit("ln%d:\n",myline+1); emit("add ebx,byte 4\n"); emit("cmp ebx,byte 64\n"); emit("jne short ln%d\n",myline); emit("pop eax\n"); ret_timing(cycles); } static void i_movem_postinc(void){ int myline=linenum;linenum+=2; usereg(); emit("push eax\n"); emit("mov ax,[esi]\n"); emit("add esi,byte 2\n"); emit("mov edx,[__areg+ebx*4]\n"); emit("push ebx\n"); emit("xor ebx,ebx\n"); emit("ln%d:\n",myline); emit("shr eax,1\n"); emit("jnc short ln%d\n",myline+1); emit("call readmemory%s\n",sizename[main_size]); if(main_size==2)emit("movsx ecx,cx\n"); emit("mov [__reg+ebx],ecx\n"); emit("add edx,byte %d\n",main_size); emit("sub edi,byte %d\n",main_size*2); emit("ln%d:\n",myline+1); emit("add ebx,byte 4\n"); emit("cmp ebx,byte 64\n"); emit("jne short ln%d\n",myline); emit("pop ebx\n"); emit("pop eax\n"); emit("mov [__areg+ebx*4],edx\n"); ret_timing(12); } static void i_movem_predec(void){ int myline=linenum;linenum+=2; usereg(); emit("push eax\n"); emit("mov ax,[esi]\n"); emit("add esi,byte 2\n"); emit("mov edx,[__areg+ebx*4]\n"); emit("push ebx\n"); emit("mov ebx,60\n"); emit("ln%d:\n",myline); emit("shr eax,1\n"); emit("jnc short ln%d\n",myline+1); emit("mov ecx,[__reg+ebx]\n"); emit("sub edx,byte %d\n",main_size); emit("sub edi,byte %d\n",main_size*2); emit("call writememory%s\n",sizename[main_size]); emit("ln%d:\n",myline+1); emit("sub ebx,byte 4\n"); emit("jns short ln%d\n",myline); emit("pop ebx\n"); emit("pop eax\n"); emit("mov [__areg+ebx*4],edx\n"); ret_timing(8); } static void i_link(void){ usereg(); emit("mov ecx,[__areg+ebx*4]\n"); ea_store(4,adec,7); emit("mov ecx,[__a7]\n"); emit("mov [__areg+ebx*4],ecx\n"); emit("movsx edx,word[esi]\n"); emit("add ecx,edx\n"); emit("mov [__a7],ecx\n"); emit("add esi,byte 2\n"); ret_timing(16); } static void i_unlk(void){ usereg(); emit("mov ecx,[__areg+ebx*4]\n"); emit("mov [__a7],ecx\n"); ea_load(4,ainc,7); emit("mov [__areg+ebx*4],ecx\n"); ret_timing(12); } static void i_move_from_usp(void){ privilegecheck(); /* makes our job much easier... */ usereg(); emit("mov ecx,[__asp]\n"); emit("mov [__areg+ebx*4],ecx\n"); ret_timing((cputype==68010)?6:4); } static void i_move_to_usp(void){ privilegecheck(); usereg(); emit("mov ecx,[__areg+ebx*4]\n"); emit("mov [__asp],ecx\n"); ret_timing((cputype==68010)?6:4); } static void i_trap(void){ emit("and ebx,byte 0Fh\n"); emit("lea edx,[80h+ebx*4]\n"); emit("call group_2_exception\n"); perform_cached_rebase(); ret_timing((cputype==68010)?38:34); } static void i_trapv(void){ int myline=linenum;linenum++; emit("test al,1\n"); emit("jnz short ln%d\n",myline); ret_timing(4); emit("ln%d:\n",myline); emit("mov edx,1Ch\n"); emit("call group_2_exception\n"); perform_cached_rebase(); ret_timing(4+((cputype==68010)?38:34)); } static void i_stop(void){ int myline=linenum;linenum++; privilegecheck(); emit("mov cx,[esi]\n"); emit("add esi,2\n"); cx2sr(); emit("or byte[__interrupts],1\n"); /* Forfeit all remaining cycles */ emit("sub edi,byte 4\n"); emit("js short ln%d\n",myline); emit("xor edi,edi\n"); emit("dec edi\n"); emit("ln%d:\n",myline); ret_timing(0); } static void i_extbw(void){ usereg(); emit("movsx cx,byte[__dreg+ebx*4]\n"); emit("mov [__dreg+ebx*4],cx\n"); selftest(2); flags_v0(); ret_timing(4); } static void i_extwl(void){ usereg(); emit("movsx ecx,word[__dreg+ebx*4]\n"); emit("mov [__dreg+ebx*4],ecx\n"); selftest(4); flags_v0(); ret_timing(4); } static void i_swap(void){ usereg(); emit("mov ecx,[__dreg+ebx*4]\n"); emit("rol ecx,16\n"); emit("mov [__dreg+ebx*4],ecx\n"); selftest(4); flags_v0(); ret_timing(4); } /* if main_cc==1 then it's signed */ static void i_mul(void){ int base_cycles; selective_usereg(); main_size=2; main_ea_load(); emit("mov eax,ecx\n"); /* Finally! Real MULS/MULU timing! */ if(cputype==68000){ emit("mov dl,0\n"); emit("mov bl,16\n"); emit("ln%d:\n",linenum); emit("add cx,cx\n"); if(main_cc==1){ /* MULS: count the number of 10 or 01 pairs */ emit("seto dh\n"); emit("add dl,dh\n"); }else{ /* MULU: count the number of 1s */ emit("adc dl,0\n"); } emit("dec bl\n"); emit("jnz ln%d\n",linenum);linenum++; emit("and edx,byte 127\n"); emit("sub edi,edx\n"); emit("sub edi,edx\n"); } emit("%smul word[__dreg+%d]\n", (main_cc==1)?"i":"",main_reg*4 ); emit("shl edx,16\n"); emit("and eax,0FFFFh\n"); emit("mov ecx,edx\n"); emit("or ecx,eax\n"); flags_v0(); emit("mov [__dreg+%d],ecx\n",main_reg*4); if(cputype==68010){ /* Maximum is 42 signed, 40 unsigned */ base_cycles=36; if(main_cc)base_cycles+=2; }else{ /* 38+2n, signed or unsigned, maximum is 70 */ base_cycles=38; } /* No loop mode */ ret_timing(base_cycles+main_ea_cycles()); } /* if main_cc=1 then it's signed */ static void i_div(void){ int base_cycles; int myline=linenum;linenum+=2; selective_usereg(); main_size=2; main_ea_load(); emit("test cx,cx\n"); emit("jnz short ln%d\n",myline); /* Forgot to put on our Division by Zero Suit... */ emit("mov edx,14h\n"); emit("call group_2_exception\n"); perform_cached_rebase(); base_cycles=38; if(cputype==68010)base_cycles+=4; ret_timing(base_cycles+main_ea_cycles()); emit("ln%d:\n",myline);myline++; if(main_cc){ emit("movsx ecx,cx\n"); }else{ emit("and ecx,0FFFFh\n"); } emit("mov eax,[__dreg+%d]\n",main_reg*4); if(main_cc){ emit("mov edx,eax\n"); emit("sar edx,31\n"); }else{ emit("xor edx,edx\n"); } emit("%sdiv ecx\n",main_cc?"i":""); if(main_cc){ emit("mov ecx,eax\n"); emit("sar cx,15\n"); emit("or ecx,ecx\n"); emit("je short ln%d\n",linenum); emit("inc ecx\n"); emit("jne short ln%d\n",myline); emit("ln%d:\n",linenum);linenum++; emit("and eax,0FFFFh\n"); }else{ emit("test eax,0FFFF0000h\n"); emit("jnz short ln%d\n",myline); } emit("shl edx,16\n"); emit("mov dx,ax\n"); emit("test dx,dx\n"); flags_v0(); emit("mov [__dreg+%d],edx\n",main_reg*4); if(cputype==68010){ base_cycles=108; if(main_cc)base_cycles=122; }else{ /* Varies from 142-158 signed, 126-140 unsigned */ base_cycles=133; if(main_cc)base_cycles=150; } ret_timing(base_cycles+main_ea_cycles()); /* Overflow */ emit("ln%d:\n",myline);myline++; emit("mov ax,1\n"); /* No loop mode */ ret_timing(base_cycles+main_ea_cycles()); } static void i_neg(void){ int cycles; selective_usereg(); cycles=4; if(main_size==4)cycles=6; if(main_eamode==dreg){ emit("neg %s[__dreg+ebx*4]\n", sizename[main_size] ); flags(); c2x(); }else{ cycles*=2; main_ea_rmw_load(); emit("neg %s\n",x86cx[main_size]); flags(); c2x(); main_ea_rmw_store(); cycles+=main_ea_cycles(); } /* Calculate loop mode timings */ if(cputype==68010){ switch(main_eamode){ case aind:case ainc:case adec: loop_c_cycles = 4; loop_t_cycles = 10; loop_x_cycles = 8; break; default:break; } } ret_timing(cycles); } static void i_negx(void){ int cycles; selective_usereg(); cycles=4; if(main_size==4)cycles=6; if(main_eamode==dreg){ emit("mov cl,[__xflag]\n"); emit("shr cl,1\n"); if(main_size==1)emit("mov cl,0\n"); else emit("mov ecx,0\n"); emit("sbb %s,[__dreg+ebx*4]\n",x86cx[main_size]); emit("mov edx,eax\n"); flags();c2x(); adjzero("dh"); emit("mov [__dreg+ebx*4],%s\n",x86cx[main_size]); }else{ cycles*=2; main_ea_rmw_load(); emit("push ebx\n"); emit("mov bl,[__xflag]\n"); emit("shr bl,1\n"); if(main_size==1)emit("mov bl,0\n"); else emit("mov ebx,0\n"); emit("sbb %s,%s\n",x86bx[main_size],x86cx[main_size]); emit("mov ecx,ebx\n"); emit("mov ebx,eax\n"); flags();c2x(); adjzero("bh"); emit("pop ebx\n"); main_ea_rmw_store(); cycles+=main_ea_cycles(); } /* Calculate loop mode timings */ if(cputype==68010){ switch(main_eamode){ case aind:case ainc:case adec: loop_c_cycles = 4; loop_t_cycles = 10; loop_x_cycles = 8; break; default:break; } } ret_timing(cycles); } static void i_nbcd(void){ int cycles; main_size=1; selective_usereg(); main_ea_rmw_load(); /* Get the X flag into carry */ emit("mov cl,[__xflag]\n"); emit("shr cl,1\n"); /* Save the previous Z flag in CH */ emit("mov ch,ah\n"); /* Perform the BCD subtraction */ emit("mov al,0\n"); emit("sbb al,cl\n"); emit("das\n"); /* Save result in CL */ emit("mov cl,al\n"); /* Set flags - V undefined */ flags_v0(); c2x(); /* Adjust for non-changing Z (previous Z flag in CH) */ adjzero("ch"); main_ea_rmw_store(); if(main_eamode==dreg){ cycles=6; }else{ cycles=8+main_ea_cycles(); } /* Calculate loop mode timings */ if(cputype==68010){ switch(main_eamode){ case aind:case ainc:case adec: loop_c_cycles = 6; loop_t_cycles = 12; loop_x_cycles = 10; break; default:break; } } ret_timing(cycles); } static void i_tas(void){ int cycles; main_size=1; selective_usereg(); main_ea_rmw_load(); selftest(1); flags_v0(); emit("or cl,80h\n"); main_ea_rmw_store(); if((main_eamode==dreg)||(main_eamode==areg)){ cycles=4; }else{ cycles=14+main_ea_cycles(); } /* No loop mode */ ret_timing(cycles); } static void i_not(void){ int cycles; selective_usereg(); cycles=4; if(main_size==4)cycles=6; if(main_eamode==dreg){ emit("xor %s[__dreg+ebx*4],byte -1\n", sizename[main_size] ); flags_v0(); }else{ cycles*=2; main_ea_rmw_load(); emit("xor %s,byte -1\n",x86cx[main_size]); flags_v0(); main_ea_rmw_store(); cycles+=main_ea_cycles(); } /* Calculate loop mode timings */ if(cputype==68010){ switch(main_eamode){ case aind:case ainc:case adec: loop_c_cycles = 4; loop_t_cycles = 10; loop_x_cycles = 8; break; default:break; } } ret_timing(cycles); } /* main_reg=rx, main_dr is 0 or 32 (rx a/d), main_ir is 0 or 32 (ry a/d) */ static void i_exg(void){ usereg(); emit("mov ecx,[__reg+%d]\n",(main_reg*4)+main_dr); emit("mov edx,[__reg+%d+ebx*4]\n",main_ir); emit("mov [__reg+%d],edx\n",(main_reg*4)+main_dr); emit("mov [__reg+%d+ebx*4],ecx\n",main_ir); /* No loop mode */ ret_timing(6); } static void i_cmpm(void){ usereg(); ea_load(main_size,ainc,-1);/* Keep this in order */ emit("mov eax,ecx\n"); ea_load(main_size,ainc,main_reg); emit("cmp %s,%s\n",x86cx[main_size],x86ax[main_size]); flags(); /* Calculate loop mode timings */ if(cputype==68010){ if(main_size<=2){ loop_c_cycles = 2; loop_t_cycles = 8; loop_x_cycles = 6; }else{ loop_c_cycles = 4; loop_t_cycles = 10; loop_x_cycles = 6; } } ret_timing((main_size==4)?20:12); } static void opx_dreg(char*op,char*adjust){ int cycles; usereg(); emit("mov ch,ah\n");/* Save old Z flag in CH */ emit("mov cl,[__xflag]\n"); emit("shr cl,1\n");/* X -> x86 carry */ emit("mov eax,[__dreg+%d]\n",main_reg*4); emit("%s %s,[__dreg+ebx*4]\n",op,x86ax[main_size]); if(adjust[0]){ emit("%s\n",adjust); } emit("mov [__dreg+%d],%s\n",main_reg*4,x86ax[main_size]); emit("lahf\n"); if(adjust[0]){ emit("mov al,0\n"); }else{ emit("seto al\n"); } c2x(); adjzero("ch"); if(main_size<=2){ cycles=4; if(adjust[0])cycles=6; }else{ cycles=8; if(cputype==68010)cycles=6; } /* No loop mode */ ret_timing(cycles); } static void opx_adec(char*op,char*adjust){ int cycles; usereg(); ea_load(main_size,adec,-1);/* Keep this in order */ emit("mov ebx,ecx\n"); ea_rmw_load(main_size,adec,main_reg); emit("xchg ecx,eax\n");/* flags -> ECX, dest -> EAX */ emit("mov cl,[__xflag]\n"); emit("shr cl,1\n");/* X -> x86 carry */ emit("%s %s,%s\n",op,x86ax[main_size],x86bx[main_size]); if(adjust[0]){ emit("%s\n",adjust); } emit("mov ebx,eax\n"); emit("lahf\n"); if(adjust[0]){ emit("mov al,0\n"); }else{ emit("seto al\n"); } c2x(); adjzero("ch"); emit("mov ecx,ebx\n"); emit("xor ebx,ebx\n"); ea_rmw_store(main_size,adec,main_reg); if(main_size<=2){ cycles=18; }else{ cycles=30; } /* Calculate loop mode timings */ if(cputype==68010){ if(adjust[0]){ loop_c_cycles = 6; loop_t_cycles = 12; loop_x_cycles = 10; }else{ if(main_size<=2){ loop_c_cycles = 4; loop_t_cycles = 10; loop_x_cycles = 8; }else{ loop_c_cycles = 2; loop_t_cycles = 8; loop_x_cycles = 6; } } } ret_timing(cycles); } static void i_addx_dreg(void){opx_dreg("adc","");} static void i_addx_adec(void){opx_adec("adc","");} static void i_subx_dreg(void){opx_dreg("sbb","");} static void i_subx_adec(void){opx_adec("sbb","");} static void i_abcd_dreg(void){main_size=1;opx_dreg("adc","daa");} static void i_abcd_adec(void){main_size=1;opx_adec("adc","daa");} static void i_sbcd_dreg(void){main_size=1;opx_dreg("sbb","das");} static void i_sbcd_adec(void){main_size=1;opx_adec("sbb","das");} static void i_movep_mem2reg(void){ int cycles; usereg(); emit("movsx edx,word[esi]\n"); emit("add esi,byte 2\n"); emit("add edx,[__areg+ebx*4]\n"); emit("call readmemorybyte\n"); emit("mov bh,cl\n"); emit("add edx,byte 2\n"); emit("call readmemorybyte\n"); emit("mov bl,cl\n"); if(main_size==2){ emit("mov [__dreg+%d],bx\n",main_reg*4); cycles=16; }else{ emit("add edx,byte 2\n"); emit("shl ebx,16\n"); emit("call readmemorybyte\n"); emit("mov bh,cl\n"); emit("add edx,byte 2\n"); emit("call readmemorybyte\n"); emit("mov bl,cl\n"); emit("mov [__dreg+%d],ebx\n",main_reg*4); emit("xor ebx,ebx\n"); cycles=24; } /* No loop mode */ ret_timing(cycles); } static void i_movep_reg2mem(void){ int cycles; usereg(); emit("movsx edx,word[esi]\n"); emit("add esi,byte 2\n"); emit("add edx,[__areg+ebx*4]\n"); emit("mov ebx,[__dreg+%d]\n",main_reg*4); if(main_size==4)emit("rol ebx,16\n"); emit("mov cl,bh\n"); emit("call writememorybyte\n"); emit("add edx,byte 2\n"); emit("mov cl,bl\n"); emit("call writememorybyte\n"); if(main_size==4){ emit("add edx,byte 2\n"); emit("rol ebx,16\n"); emit("mov cl,bh\n"); emit("call writememorybyte\n"); emit("add edx,byte 2\n"); emit("mov cl,bl\n"); emit("call writememorybyte\n"); cycles=24; }else{ cycles=16; } emit("xor ebx,ebx\n"); /* No loop mode */ ret_timing(cycles); } static void i_chk(void){ int cycles; int myline=linenum;linenum++; selective_usereg(); main_ea_load(); emit("cmp %s[__dreg+%d],byte 0\n", sizename[main_size],main_reg*4 ); emit("mov ax,8000h\n"); emit("jl short ln%d\n",myline); emit("cmp [__dreg+%d],%s\n", main_reg*4,x86cx[main_size] ); emit("mov ax,0\n"); emit("jg short ln%d\n",myline); cycles=10; if(cputype==68010)cycles=8; ret_timing(cycles+main_ea_cycles()); /* Out of bounds, so generate CHK exception */ emit("ln%d:",myline); emit("mov edx,18h\n"); emit("call group_2_exception\n"); perform_cached_rebase(); cycles=40; if(cputype==68010)cycles=44; /* No loop mode */ ret_timing(cycles+main_ea_cycles()); } static int created_illegal=0; static void i_illegal(void){ if(!created_illegal){emit("r_illegal:\n");created_illegal=1;} emit("sub esi,byte 2\n"); emit("mov edx,10h\n"); emit("call group_1_exception\n"); perform_cached_rebase(); ret_timing((cputype==68010)?38:34); } /* Breakpoint - notify hardware */ static void i_bkpt(void){ int myline=linenum; linenum++; emit("mov ecx,[__bkpthandler]\n"); emit("or ecx,ecx\n"); emit("jz ln%d\n",myline); airlock_exit(); emit("call ecx\n"); airlock_enter(); emit("ln%d:\n",myline); emit("mov edx,10h\n"); emit("call group_1_exception\n"); perform_cached_rebase(); ret_timing((cputype==68010)?38:34); } static void i_aline(void){ emit("sub esi,byte 2\n"); emit("mov edx,28h\n"); emit("call group_1_exception\n"); perform_cached_rebase(); /* This is just a guess */ ret_timing((cputype==68010)?38:34); } static void i_fline(void){ emit("sub esi,byte 2\n"); emit("mov edx,2Ch\n"); emit("call group_1_exception\n"); perform_cached_rebase(); /* This is just a guess */ ret_timing((cputype==68010)?38:34); } static void i_reset(void){ privilegecheck(); emit("mov ecx,[__resethandler]\n"); emit("or ecx,ecx\n"); emit("jz near invalidins\n"); airlock_exit(); emit("call ecx\n"); airlock_enter(); ret_timing((cputype==68010)?130:132); } static void i_movec_c_to_r(void){ int myline=linenum;linenum++; privilegecheck(); emit("mov bx,word[esi]\n"); emit("mov edx,ebx\n"); emit("shr ebx,12\n"); emit("and edx,0FFFh\n"); emit("jnz short ln%d\n",linenum); emit("mov cl,[__sfc]\n"); emit("and ecx,byte 7\n"); emit("jmp short ln%d\n",myline); emit("ln%d:\n",linenum);linenum++; emit("cmp edx,byte 1\n"); emit("jnz short ln%d\n",linenum); emit("mov cl,[__dfc]\n"); emit("and ecx,byte 7\n"); emit("jmp short ln%d\n",myline); emit("ln%d:\n",linenum);linenum++; emit("cmp edx,0800h\n"); emit("jnz short ln%d\n",linenum); emit("mov ecx,[__asp]\n"); emit("jmp short ln%d\n",myline); emit("ln%d:\n",linenum);linenum++; emit("cmp edx,0801h\n"); emit("jnz short ln%d\n",linenum); emit("mov ecx,[__vbr]\n"); emit("jmp short ln%d\n",myline); emit("ln%d:\n",linenum);linenum++; emit("jmp r_illegal\n"); emit("ln%d:\n",myline); emit("add esi,byte 2\n"); emit("mov dword[__reg+ebx*4],ecx\n"); ret_timing(12); } static void i_movec_r_to_c(void){ int myline=linenum;linenum++; privilegecheck(); emit("mov bx,word[esi]\n"); emit("mov edx,ebx\n"); emit("shr ebx,12\n"); emit("mov ecx,dword[__reg+ebx*4]\n"); emit("and edx,0FFFh\n"); emit("jnz short ln%d\n",linenum); emit("and cl,7\n"); emit("mov [__sfc],cl\n"); emit("jmp short ln%d\n",myline); emit("ln%d:\n",linenum);linenum++; emit("cmp edx,byte 1\n"); emit("jnz short ln%d\n",linenum); emit("and cl,7\n"); emit("mov [__dfc],cl\n"); emit("jmp short ln%d\n",myline); emit("ln%d:\n",linenum);linenum++; emit("cmp edx,0800h\n"); emit("jnz short ln%d\n",linenum); emit("mov [__asp],ecx\n"); emit("jmp short ln%d\n",myline); emit("ln%d:\n",linenum);linenum++; emit("cmp edx,0801h\n"); emit("jnz short ln%d\n",linenum); emit("mov [__vbr],ecx\n"); emit("jmp short ln%d\n",myline); emit("ln%d:\n",linenum);linenum++; emit("jmp r_illegal\n"); emit("ln%d:\n",myline); emit("add esi,byte 2\n"); ret_timing(10); } #if 0 /* called not-quite-so-many times as i_move */ static void i_moves(void){ int cycles; int unitsize = main_size == 4 ? 2 : main_size; int myline=linenum; linenum++; selective_usereg(); emit("push esi\n"); /* save in case it's invalid */ emit("movzx ecx,word[esi]\n"); emit("add esi,byte 2\n"); ea_step_precalc(main_size,main_eamode,-1); /* edx=address */ emit("shr ecx,12\n"); emit("jc short ln%d_write\n",myline); /* read */ emit("cmp byte[__sfc],1\n"); emit("je ln%d_read_userdata\n"); emit("cmp byte[__sfc],2\n"); emit("je ln%d_read_userprogram\n"); emit("cmp byte[__sfc],5\n"); emit("je ln%d_read_superdata\n"); emit("cmp byte[__sfc],6\n"); emit("je ln%d_read_superprogram\n"); /* ** Generic address space read routine; SFC in {0,3,4,7} */ emit("cmp dword[__fc_read%s],byte 0\n",sizename[unitsize]); emit("je short ln%d_inv\n",myline); emit("push ecx\n"); emit("push edx\n"); airlock_exit(); emit("mov al,[__sfc]\n"); emit("and eax,byte 7\n"); if(use_stack) { emit("push edx\n"); emit("push eax\n"); } emit("call dword[__fc_read%s]\n"); if(use_stack) { emit("add esp,byte 8\n"); } emit("mov ecx,[esp+%d]\n",airlock_stacksize+4); switch(main_size) { case 1:emit("mov [__reg+ecx*4],al\n");break; case 2:emit("mov [__reg+ecx*4],ax\n");break; case 4:emit("mov [__reg+ecx*4+2],ax\n"); emit("mov edx,[esp+%d]\n",airlock_stacksize); emit("add edx,byte 2\n"); emit("mov al,[__sfc]\n"); emit("and eax,byte 7\n"); if(use_stack) { emit("push edx\n"); emit("push eax\n"); } emit("call dword[__fc_read%s]\n"); if(use_stack) { emit("add esp,byte 8\n"); } emit("mov ecx,[esp+%d]\n",airlock_stacksize+4); emit("mov [__reg+ecx*4],ax\n"); break; } airlock_enter(); emit("pop edx\n"); emit("pop ecx\n"); ea_step_postcalc(main_size,main_eamode,-1); /* edx=address */ emit("jmp ln%d_end\n"); ret_timing(cycles); } #endif /**************************************************************************** ** DECODE ROUTINES ****************************************************************************/ static int rproc [0x10000]; static byte unique[0x10000]; static int cease_decode; static int test(int n, int m, int op) { int t; if((n & m) != op) return 0; for(t = op & 0xF000; t < n; t++) { if((!unique[t]) && ((t & m) == (n & m))) { rproc[n] = t; return 2; } } unique[n] = (m >> 16) & 1; rproc[n] = n; t = (m ^ 0xFFFF) & 0xFFF; if(!t) { emit("; Opcode %04X\n", n); } else { emit("; Opcodes %04X - %04X\n", n, op + t); } /* align(4);*/ emit("%c%03X:\n", ((n >> 12) & 0xF) + 'K', n & 0xFFF); routine_counter++; return 1; } /* Instruction definition routine */ static void idef( int n, int mask, int op, void(*proc)(void) ) { if(cease_decode) return; cease_decode = test(n, mask, op); if(cease_decode == 1) { if(cputype == 68010) { loop_c_cycles = 10; loop_t_cycles = 10; loop_x_cycles = 16; } proc(); if(cputype == 68010) { if(loop_c_cycles > 14) { fprintf(stderr, "Bad news: instruction %04X:\n" "loop_c_cycles (%d) exceeds limit\n", n, loop_c_cycles ); exit(1); } if(loop_t_cycles > 14) { fprintf(stderr, "Bad news: instruction %04X:\n" "loop_t_cycles (%d) exceeds limit\n", n, loop_t_cycles ); exit(1); } if(loop_x_cycles > (loop_c_cycles + 6)) { fprintf(stderr, "Bad news: instruction %04X:\n" "loop_c_cycles (%d) and " "loop_x_cycles (%d) too far apart\n", n, loop_c_cycles, loop_x_cycles ); exit(1); } loop_x_cycles -= loop_c_cycles; loopinfo[n] = (((loop_c_cycles ) & 0x0E) | ((loop_t_cycles << 3) & 0x70)) | (((loop_x_cycles << 6) & 0x80) | ((loop_x_cycles >> 2) & 0x01)); } } } /* Batch idef for all addressing modes */ static void eadef_all( int n, int m, int op, void(*proc)(void) ) { if(cease_decode) return; main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc); main_eamode = areg; idef(n, m | 0x38, op | 0x08, proc); main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc); main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc); main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc); main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc); main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc); main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc); main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc); main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc); main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc); main_eamode = immd; idef(n, m | 0x3F, op | 0x3C, proc); } /* Batch idef for all addressing modes, excluding Address Register Direct ** mode when the operand size is 1 */ static void eadef_all_nobyteaddress( int n, int m, int op, void(*proc)(void) ) { if(cease_decode) return; main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc); if(main_size != 1) { main_eamode = areg; idef(n, m | 0x38, op | 0x08, proc); } main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc); main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc); main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc); main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc); main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc); main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc); main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc); main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc); main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc); main_eamode = immd; idef(n, m | 0x3F, op | 0x3C, proc); } /* Batch idef for all data addressing modes */ static void eadef_data( int n, int m, int op, void(*proc)(void) ) { if(cease_decode) return; main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc); main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc); main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc); main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc); main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc); main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc); main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc); main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc); main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc); main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc); main_eamode = immd; idef(n, m | 0x3F, op | 0x3C, proc); } /* Batch idef for all alterable addressing modes, excluding Address Register ** Direct mode when the operand size is 1 */ static void eadef_alterable_nobyteaddress( int n, int m, int op, void(*proc)(void) ) { if(cease_decode) return; main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc); if(main_size != 1) { main_eamode = areg; idef(n, m | 0x38, op | 0x08, proc); } main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc); main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc); main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc); main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc); main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc); main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc); main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc); } /* Batch idef for all data alterable addressing modes */ static void eadef_data_alterable( int n, int m, int op, void(*proc)(void) ) { if(cease_decode) return; main_eamode = dreg; idef(n, m | 0x38, op | 0x00, proc); main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc); main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc); main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc); main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc); main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc); main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc); main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc); } /* Batch idef for all memory alterable addressing modes */ static void eadef_memory_alterable( int n, int m, int op, void(*proc)(void) ) { if(cease_decode) return; main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc); main_eamode = ainc; idef(n, m | 0x38, op | 0x18, proc); main_eamode = adec; idef(n, m | 0x38, op | 0x20, proc); main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc); main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc); main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc); main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc); } /* Batch idef for all control addressing modes */ static void eadef_control( int n, int m, int op, void(*proc)(void) ) { if(cease_decode) return; main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc); main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc); main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc); main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc); main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc); main_eamode = pcdp; idef(n, m | 0x3F, op | 0x3A, proc); main_eamode = pcxd; idef(n, m | 0x3F, op | 0x3B, proc); } /* Batch idef for all control alterable addressing modes */ static void eadef_control_alterable( int n, int m, int op, void(*proc)(void) ) { if(cease_decode) return; main_eamode = aind; idef(n, m | 0x38, op | 0x10, proc); main_eamode = adsp; idef(n, m | 0x38, op | 0x28, proc); main_eamode = axdp; idef(n, m | 0x38, op | 0x30, proc); main_eamode = absw; idef(n, m | 0x3F, op | 0x38, proc); main_eamode = absl; idef(n, m | 0x3F, op | 0x39, proc); } /* Batch eadef for MOVE instructions */ static void defmove(int majorop, int n) { if(cease_decode) return; for(main_reg = 0; main_reg < 8; main_reg++) { word w = majorop | (main_reg << 9); main_destmode = dreg; eadef_all(n, 0xFFC0, 0x000 | w, i_move); main_destmode = aind; eadef_all(n, 0xFFC0, 0x080 | w, i_move); main_destmode = ainc; eadef_all(n, 0xFFC0, 0x0C0 | w, i_move); main_destmode = adec; eadef_all(n, 0xFFC0, 0x100 | w, i_move); main_destmode = adsp; eadef_all(n, 0xFFC0, 0x140 | w, i_move); main_destmode = axdp; eadef_all(n, 0xFFC0, 0x180 | w, i_move); } main_destmode = absw; eadef_all(n, 0xFFC0, 0x1C0 | majorop, i_move); main_destmode = absl; eadef_all(n, 0xFFC0, 0x3C0 | majorop, i_move); } /***************************************************************************/ static void decode0(int n) { cease_decode = 0; for(sizedef = 0; sizedef < 3; sizedef++) { main_size = 1 << sizedef; eadef_data_alterable(n, 0xFFC0, 0x0000 | (sizedef << 6), i_ori); eadef_data_alterable(n, 0xFFC0, 0x0200 | (sizedef << 6), i_andi); eadef_data_alterable(n, 0xFFC0, 0x0400 | (sizedef << 6), i_subi); eadef_data_alterable(n, 0xFFC0, 0x0600 | (sizedef << 6), i_addi); eadef_data_alterable(n, 0xFFC0, 0x0A00 | (sizedef << 6), i_eori); eadef_data_alterable(n, 0xFFC0, 0x0C00 | (sizedef << 6), i_cmpi); /* Not quite ready for prime time yet */ /* if(cputype >= 68010) { eadef_memory_alterable(n, 0xFFC0, 0x0E00 | (sizedef << 6), i_moves); }*/ } /* ** Bit operations ** BTST (main_cc 0) works with all data addressing modes; the others ** require data alterable addressing modes */ for(main_cc = 0; main_cc < 4; main_cc++) { void (*eadef)(int n, int m, int op, void(*proc)(void)) = main_cc ? eadef_data_alterable : eadef_data; eadef(n, 0xFFC0, 0x0800 | (main_cc << 6), i_bitop_imm); for(main_reg = 0; main_reg < 8; main_reg++) { eadef(n, 0xFFC0, 0x0100 | (main_cc << 6) | (main_reg << 9), i_bitop_reg ); } } idef(n, 0xFFFF, 0x003C, i_ori_ccr ); idef(n, 0xFFFF, 0x023C, i_andi_ccr); idef(n, 0xFFFF, 0x0A3C, i_eori_ccr); idef(n, 0xFFFF, 0x007C, i_ori_sr ); idef(n, 0xFFFF, 0x027C, i_andi_sr ); idef(n, 0xFFFF, 0x0A7C, i_eori_sr ); for(main_reg=0;main_reg<8;main_reg++)for(main_size=2;main_size<=4;main_size+=2){ idef(n,0xFFF8,0x0108|((main_size&4)<<4)|(main_reg<<9),i_movep_mem2reg); idef(n,0xFFF8,0x0188|((main_size&4)<<4)|(main_reg<<9),i_movep_reg2mem); } } static void decode1(int n) { cease_decode = 0; main_size = 1; defmove(0x1000, n); } static void decode2(int n) { cease_decode = 0; main_size = 4; defmove(0x2000, n); for(main_reg = 0; main_reg < 8; main_reg++) { eadef_all(n, 0xFFC0, 0x2040 | (main_reg << 9), i_movea); } } static void decode3(int n) { cease_decode = 0; main_size = 2; defmove(0x3000, n); for(main_reg = 0; main_reg < 8; main_reg++) { eadef_all(n, 0xFFC0, 0x3040 | (main_reg << 9), i_movea); } } static void decode4(int n) { cease_decode = 0; eadef_data_alterable(n, 0xFFC0, 0x40C0, i_move_from_sr); if(cputype >= 68010) { eadef_data_alterable(n, 0xFFC0, 0x42C0, i_move_from_ccr); } eadef_data(n, 0xFFC0, 0x44C0, i_move_to_ccr); eadef_data(n, 0xFFC0, 0x46C0, i_move_to_sr ); eadef_control(n, 0xFFC0, 0x4EC0, i_jmp); eadef_control(n, 0xFFC0, 0x4E80, i_jsr); for(main_reg=0;main_reg<8;main_reg++)eadef_control(n,0xFFC0,0x41C0|(main_reg<<9),i_lea); /******** Stef Fix (Gens) *********/ // old code // main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_data(n,0xFFC0,0x4100|(main_reg<<9),i_chk); // new code main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_data(n,0xFFC0,0x4180|(main_reg<<9),i_chk); /******** End Fix *********/ eadef_control(n,0xFFC0,0x4840,i_pea); for(sizedef = 0; sizedef < 3; sizedef++) { main_size = 1 << sizedef; eadef_data_alterable(n, 0xFFC0, 0x4200 | (sizedef << 6), i_clr); eadef_data_alterable(n, 0xFFC0, 0x4A00 | (sizedef << 6), i_tst); } idef(n, 0xFFFF, 0x4E70, i_reset); idef(n, 0xFFFF, 0x4E71, i_nop); idef(n, 0xFFFF, 0x4E72, i_stop); idef(n, 0xFFFF, 0x4E73, i_rte); idef(n, 0xFFFF, 0x4E75, i_rts); idef(n, 0xFFFF, 0x4E76, i_trapv); idef(n, 0xFFFF, 0x4E77, i_rtr); if(cputype >= 68010) { idef(n, 0xFFFF, 0x4E74, i_rtd); idef(n, 0xFFFF, 0x4E7A, i_movec_c_to_r); idef(n, 0xFFFF, 0x4E7B, i_movec_r_to_c); } main_dr=0;for(sizedef=0;sizedef<2;sizedef++){main_size=1<<(sizedef+1);eadef_control_alterable(n,0xFFC0,0x4880|(main_dr<<10)|(sizedef<<6),i_movem_control);} main_dr=1;for(sizedef=0;sizedef<2;sizedef++){main_size=1<<(sizedef+1);eadef_control (n,0xFFC0,0x4880|(main_dr<<10)|(sizedef<<6),i_movem_control);} for(sizedef = 0; sizedef < 2; sizedef++) { main_size = 1 << (sizedef + 1); idef(n, 0xFFF8, 0x4C98 | (sizedef << 6), i_movem_postinc); idef(n, 0xFFF8, 0x48A0 | (sizedef << 6), i_movem_predec ); } idef(n, 0xFFF8, 0x4E50, i_link); idef(n, 0xFFF8, 0x4E58, i_unlk); idef(n, 0xFFF0, 0x4E40, i_trap); idef(n, 0xFFF8, 0x4E60, i_move_to_usp); idef(n, 0xFFF8, 0x4E68, i_move_from_usp); idef(n, 0xFFF8, 0x4840, i_swap); idef(n, 0xFFF8, 0x4880, i_extbw); idef(n, 0xFFF8, 0x48C0, i_extwl); for(sizedef = 0; sizedef < 3; sizedef++) { main_size = 1 << sizedef; eadef_data_alterable(n, 0xFFC0, 0x4000 | (sizedef << 6), i_negx); eadef_data_alterable(n, 0xFFC0, 0x4400 | (sizedef << 6), i_neg); eadef_data_alterable(n, 0xFFC0, 0x4600 | (sizedef << 6), i_not); } eadef_data_alterable(n, 0xFFC0, 0x4800, i_nbcd); eadef_data_alterable(n, 0xFFC0, 0x4AC0, i_tas); if(cputype == 68010) idef(n, 0xFFF8, 0x4848, i_bkpt); idef(n, 0xFFFF, 0x4AFA, i_illegal); idef(n, 0xFFFF, 0x4AFB, i_illegal); idef(n, 0xFFFF, 0x4AFC, i_illegal); } static void decode5(int n) { cease_decode = 0; for(sizedef = 0; sizedef < 3; sizedef++) { main_size = 1 << sizedef; for(main_qv = 0; main_qv < 8; main_qv++) { word w = (sizedef << 6) | (main_qv << 9); eadef_alterable_nobyteaddress(n, 0xFFC0, 0x5000 | w, i_addq); eadef_alterable_nobyteaddress(n, 0xFFC0, 0x5100 | w, i_subq); } } for(main_cc = 0x2; main_cc <= 0xF; main_cc++) { idef(n, 0xFFF8, 0x50C8 | (main_cc << 8), i_dbcc); } /******** Stef Fix (Gens) *********/ // code added idef(n, 0xFFF8, 0x50C8, i_dbtr); /******** End Fix *********/ idef(n, 0xFFF8, 0x51C8, i_dbra); main_size = 1; for(main_cc = 0x0; main_cc <= 0xF; main_cc++) { eadef_data_alterable(n, 0xFFC0, 0x50C0 | (main_cc << 8), i_scc); } } static void decode6(int n){ cease_decode=0; idef(n,0x1FFFF,0x6000,i_bra_w); idef(n,0x1FFFF,0x6100,i_bsr_w); for(main_cc=0x2;main_cc<=0xF;main_cc++){ idef(n,0x1FFFF,0x6000|(main_cc<<8),i_bcc_w); } idef(n,0x0FF00,0x6000,i_bra_b); idef(n,0x0FF00,0x6100,i_bsr_b); for(main_cc=0x2;main_cc<=0xF;main_cc++){ idef(n,0x0FF00,0x6000|(main_cc<<8),i_bcc_b); } } static void decode7(int n){ cease_decode=0; for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFF00,0x7000|(main_reg<<9),i_moveq); } static void decode8(int n){ cease_decode=0; for(sizedef=0;sizedef<3;sizedef++){main_size=1<> 12) & 0xF) + 'K', last & 0xFFF ); } if(rl > 1) emit("+%u", ((dword)(rl - 1)) << 24); emit("\n"); if(cputype == 68010) emit("db %d\n", loopinfo[last]); } /* Return the next parameter (or NULL if there isn't one */ static char *getparameter(int *ip, int argc, char **argv) { int i; (*ip)++; i = (*ip); if(i >= argc) { fprintf(stderr, "Invalid use of %s option\n", argv[i - 1]); return NULL; } return argv[i]; } int main(int argc, char **argv) { int i, j, last, rl, bank; char *codefilename = NULL; char default_sourcename[10]; fprintf(stderr, "STARSCREAM version " STAR_VERSION "\n"); /* Read options from the command line */ for(i = 1; i < argc; i++) { char *a = argv[i]; if(*a == '-') { a++; if(!strcmp("regcall" , a)) { use_stack = 0; } else if(!strcmp("stackcall" , a)) { use_stack = 1; } else if(!strcmp("nohog" , a)) { hog = 0; } else if(!strcmp("hog" , a)) { hog = 1; } else if(!strcmp("addressbits", a)) { int n; char *s = getparameter(&i, argc, argv); if(!s) return 1; n = atol(s); if(n < 1 || n > 32) { fprintf(stderr, "Invalid number of address " "bits: \"%s\"\n", argv[i] ); return 1; } addressbits = n; } else if(!strcmp("cputype" , a)) { int n; char *s = getparameter(&i, argc, argv); if(!s) return 1; n = atol(s); switch(n) { case 68000: case 68010: case 68020: cputype = n; break; default: fprintf(stderr, "Invalid CPU type: \"%s\"\n", argv[i] ); return 1; } } else if(!strcmp("name" , a)) { sourcename = getparameter(&i, argc, argv); if(!sourcename) return 1; } else { fprintf(stderr, "\nUnrecognized option: \"%s\"\n", argv[i] ); return 1; } } else { if(codefilename) { fprintf(stderr, "\n\"%s\": only one output filename " "is allowed\n", argv[i] ); return 1; } codefilename = argv[i]; } } if(!codefilename) { fprintf(stderr, "usage: %s outputfile [options]\n", argv[0]); fprintf(stderr, "see STARDOC.TXT for details\n"); return 1; } /* Set default options where applicable */ if(use_stack < 0) use_stack = 1; if(hog < 0) hog = 0; if(cputype < 0) cputype = 68000; if(addressbits < 0) { if(cputype <= 68010) addressbits = 24; else addressbits = 32; } if(!sourcename) { sprintf(default_sourcename, "s%d", cputype); sourcename = default_sourcename; } /* Prepare to generate the code file */ linenum = 0; fflush(stdout); fflush(stderr); codefile = fopen(codefilename, "w"); if(!codefile) { perror(codefilename); return 1; } fprintf(stderr, "Generating \"%s\" with the following options:\n", codefilename ); optiondump(stderr, " * "); prefixes(); for(i = 0; i < 0x10000; i++) rproc[i] = -1; /* Clear loop timings for 68010 */ if(cputype == 68010) { for(i = 0; i < 0x10000; i++) loopinfo[i] = 0xDB; } /* ** Decode instructions ** (this is where the vast majority of the code is emitted) */ fprintf(stderr, "Decoding instructions: "); for(bank = 0; bank <= 0xF; bank++) { int bankend = (bank + 1) << 12; void (*decoderoutine)(int n) = decodetable[bank]; fprintf(stderr, "%X", bank); fflush(stderr); for(i = bank << 12; i < bankend; i++) decoderoutine(i); } fprintf(stderr, " done\n"); /* ** Build the main jump table (all CPUs) / loop info table (68010) */ fprintf(stderr, "Building table: "); emit("section .bss\n"); emit("bits 32\n"); align(4); emit("__jmptbl resb 262144\n"); if(cputype == 68010) emit("__looptbl resb 65536\n"); emit("section .data\n"); emit("bits 32\n"); align(4); emit("__jmptblcomp:\n"); last = -2; rl = 0; for(i = 0; i < 0x10000; i++) { j = rproc[i]; if(j == last){ if(rl == 256) { tableentry(last, rl); rl = 1; } else { rl++; } } else { if(rl) tableentry(last, rl); rl = 1; last = j; } } tableentry(last, rl); align(4); /* Finish up */ suffixes(); fprintf(stderr, "done\n"); fprintf(stderr, "routine_counter = %d\n", routine_counter); fclose(codefile); return 0; }