forked from KolibriOS/kolibrios
ea1a60faa3
git-svn-id: svn://kolibrios.org@9837 a494cfbc-eb01-0410-851d-a64ba20cac60
5294 lines
138 KiB
C
5294 lines
138 KiB
C
/*
|
|
** 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;
|
|
}
|