📄 star.c
字号:
** 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 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("sub edi,byte %d\n", cycles); 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");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -