?? star.c
字號:
#define RAINE/*Raine version:- fixed writel trashing ecx- commented out read/writel_split checks- commented out address offset subs, raine does them beforehand*//*** 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.*/#ifndef RAINE#define VERSION "0.26a"#else#define VERSION "0.26r"#endif/***************************************************************************//*** NOTE**** All 68020-related variables and functions are currently experimental, and** unsupported.*/#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdarg.h>/***************************************************************************//*** 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 " 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("_%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("__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");}/* 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 */}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -