kolibrios-gitea/programs/emulator/dgen-sdl-1.33/star/star.c

5294 lines
138 KiB
C
Raw Normal View History

/*
** 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 <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 " 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<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_data (n,0xFFC0,0x8000|(main_reg<<9)|(sizedef<<6),i_or_dn );}}
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0x8100|(main_reg<<9)|(sizedef<<6),i_or_ea );}}
for(main_cc=0;main_cc<2;main_cc++)for(main_reg=0;main_reg<8;main_reg++)eadef_data(n,0xFFC0,0x80C0|(main_reg<<9)|(main_cc<<8),i_div);
main_size=1;
for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x8100|(main_reg<<9),i_sbcd_dreg);
for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x8108|(main_reg<<9),i_sbcd_adec);
}
static void decode9(int n){
cease_decode=0;
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_all_nobyteaddress(n,0xFFC0,0x9000|(main_reg<<9)|(sizedef<<6),i_sub_dn);}}
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0x9100|(main_reg<<9)|(sizedef<<6),i_sub_ea);}}
main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0x90C0|(main_reg<<9),i_suba);
main_size=4;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0x91C0|(main_reg<<9),i_suba);
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x9100|(main_reg<<9)|(sizedef<<6),i_subx_dreg);}
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0x9108|(main_reg<<9)|(sizedef<<6),i_subx_adec);}
}
static void decodeA(int n){
cease_decode=0;
idef(n,0xF000,0xA000,i_aline);
}
static void decodeB(int n){
cease_decode=0;
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_all_nobyteaddress(n,0xFFC0,0xB000|(main_reg<<9)|(sizedef<<6),i_cmp_dn);}}
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_data_alterable (n,0xFFC0,0xB100|(main_reg<<9)|(sizedef<<6),i_eor_ea);}}
main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xB0C0|(main_reg<<9),i_cmpa);
main_size=4;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xB1C0|(main_reg<<9),i_cmpa);
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xB108|(sizedef<<6)|(main_reg<<9),i_cmpm);}
}
static void decodeC(int n){
cease_decode=0;
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_data (n,0xFFC0,0xC000|(main_reg<<9)|(sizedef<<6),i_and_dn);}}
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0xC100|(main_reg<<9)|(sizedef<<6),i_and_ea);}}
for(main_cc=0;main_cc<2;main_cc++)for(main_reg=0;main_reg<8;main_reg++)eadef_data(n,0xFFC0,0xC0C0|(main_reg<<9)|(main_cc<<8),i_mul);
main_dr=0 ;main_ir=0 ;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC140|(main_reg<<9),i_exg);
main_dr=32;main_ir=32;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC148|(main_reg<<9),i_exg);
main_dr=0 ;main_ir=32;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC188|(main_reg<<9),i_exg);
main_size=1;
for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC100|(main_reg<<9),i_abcd_dreg);
for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xC108|(main_reg<<9),i_abcd_adec);
}
static void decodeD(int n){
cease_decode=0;
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_all_nobyteaddress(n,0xFFC0,0xD000|(main_reg<<9)|(sizedef<<6),i_add_dn);}}
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++){eadef_memory_alterable (n,0xFFC0,0xD100|(main_reg<<9)|(sizedef<<6),i_add_ea);}}
main_size=2;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xD0C0|(main_reg<<9),i_adda);
main_size=4;for(main_reg=0;main_reg<8;main_reg++)eadef_all(n,0xFFC0,0xD1C0|(main_reg<<9),i_adda);
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xD100|(main_reg<<9)|(sizedef<<6),i_addx_dreg);}
for(sizedef=0;sizedef<3;sizedef++){main_size=1<<sizedef;for(main_reg=0;main_reg<8;main_reg++)idef(n,0xFFF8,0xD108|(main_reg<<9)|(sizedef<<6),i_addx_adec);}
}
static void decodeE(int n){
cease_decode=0;
for(main_reg=0;main_reg<8;main_reg++)
for(main_dr=0;main_dr<2;main_dr++)
for(main_ir=0;main_ir<2;main_ir++)
for(sizedef=0;sizedef<3;sizedef++){
main_size=1<<sizedef;
idef(n,0xFFF8,0xE000|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_asx_reg);
idef(n,0xFFF8,0xE008|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_lsx_reg);
idef(n,0xFFF8,0xE010|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_rxx_reg);
idef(n,0xFFF8,0xE018|(main_reg<<9)|(main_dr<<8)|(sizedef<<6)|(main_ir<<5),i_rox_reg);
}
for(main_dr=0;main_dr<2;main_dr++){
eadef_memory_alterable(n,0xFFC0,0xE0C0|(main_dr<<8),i_asx_mem);
eadef_memory_alterable(n,0xFFC0,0xE2C0|(main_dr<<8),i_lsx_mem);
eadef_memory_alterable(n,0xFFC0,0xE4C0|(main_dr<<8),i_rxx_mem);
eadef_memory_alterable(n,0xFFC0,0xE6C0|(main_dr<<8),i_rox_mem);
}
}
static void decodeF(int n) {
cease_decode = 0;
idef(n, 0xF000, 0xF000, i_fline);
}
static void (*(decodetable[16]))(int n) =
{decode0, decode1, decode2, decode3, decode4, decode5, decode6, decode7,
decode8, decode9, decodeA, decodeB, decodeC, decodeD, decodeE, decodeF};
/***************************************************************************/
/* Build a jump table entry (including loop info for 68010) */
static void tableentry(int last, int rl) {
if(last == -1){
emit("dd r_illegal-top");
} else {
emit("dd %c%03X-top",
((last >> 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;
}