Supermodel/Src/CPU/68K/Turbo68K/Make68K.c

9008 lines
309 KiB
C

/**
** Supermodel
** A Sega Model 3 Arcade Emulator.
** Copyright 2011 Bart Trzynadlowski, Nik Henson
**
** This file is part of Supermodel.
**
** Supermodel is free software: you can redistribute it and/or modify it under
** the terms of the GNU General Public License as published by the Free
** Software Foundation, either version 3 of the License, or (at your option)
** any later version.
**
** Supermodel is distributed in the hope that it will be useful, but WITHOUT
** ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
** FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
** more details.
**
** You should have received a copy of the GNU General Public License along
** with Supermodel. If not, see <http://www.gnu.org/licenses/>.
**/
/*
* Turbo68K: Motorola 680X0 emulator
* Copyright 2000-2002 Bart Trzynadlowski, see "README.TXT" for terms of use
*/
/*
* Make68K: This program emits Turbo68K. Please see README.TXT for details.
*
* Note: If you get a link error involving pow(), remember to link in the math
* library. Try using "-lm".
*
* Note: I am not certain about dummy reads. The manual says they occur with
* some instructions on the MC68000 and MC68008 only. Does this include
* the MC68EC000? I'm assuming it does. Also, is a dummy read made for
* all Scc instructions, or just ST? I'm assuming all of them do it.
*
* Note: Do not use the profiling feature (#define PROFILE.) It will add
* useless junk to the emulator.
*
* Note: Future plans: I intend on removing as much of the Save/RestoreReg()
* junk from the API functions as possible. PUSH/POPs should work much
* better.
*
* Additional developers' notes pertaining to all aspects of Turbo68K can be
* found at the very end of this file.
*/
#include <math.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "turbo68k.h"
#define VERSION "0.6"
#define SIZEOF_CONTEXT "(context_end - context_start)"
#define SIZEOF_FETCHREGION "12"
#define SIZEOF_DATAREGION "16"
#define OFFSET_DATA_BASE "0"
#define OFFSET_DATA_LIMIT "4"
#define OFFSET_DATA_PTR "8"
#define OFFSET_DATA_HANDLER "12"
#define OFFSET_FETCH_BASE "0"
#define OFFSET_FETCH_LIMIT "4"
#define OFFSET_FETCH_PTR "8"
#define WRITEPCTOMEM(dest) { \
e("mov dword ["dest"], esi\n");\
e("sub dword ["dest"], ebp\n");\
}
#define WRITEPCTOREG(dest) { \
e("mov "dest", esi\n"); \
e("sub "dest", ebp\n"); \
}
#define EMULATING() e("or byte [status], 1\n");
#define STOP_EMULATING() e("and byte [status], 0xfe\n");
#define STOP_CPU() e("or byte [status], 2\n");
#define UNSTOP_CPU() e("and byte [status], 0xfd\n");
#define INTERRUPT_PROCESSING() e("or byte [status], 4\n");
#define INTERRUPT_DONE() e("and byte [status], 0xfb\n");
#define STOP_RUNNING() { \
e("mov [remaining], ecx\n"); \
WRITEPCTOMEM("pc"); \
}
#define A7 "[a+7*4]"
#define SP "[__sp]"
#define SR "[sr]"
#define UNKNOWN_SIZE 0
#define BYTE_SIZE 1
#define WORD_SIZE 2
#define LONG_SIZE 3
unsigned short decoded[65536]; /* compressed jump table data */
char id[17] = { '\0' }; /* prepended to identifiers */
FILE *fp; /* output file */
int mpu = 68000; /* 68000 by default */
unsigned addr_bits = 24; /* 24-bit by default */
unsigned addr_mask = 0xffffff;
int illegal = 1; /* illegal instruction traps */
int dummyread = 1; /* dummy reads (68000 only) */
int skip = 0; /* CPU (idle loop) skipping */
int brafetch = 0; /* branch fetch ptr updating */
int pcfetch = 0; /* special PC-relative fetching */
int multiaddr = 1; /* supervisor and user spaces */
int memmap_type = 0; /* memory map type */
int call_convention = 0; /* calling convention */
int debug = 0; /* call debug function at every instruction */
int mmx = 0; /* MMX support -- don't use! */
unsigned num_handlers = 0; /* # of inst. handlers emitted */
#ifdef PROFILE
char *prof[512];
int prof_i = 0;
#endif
/*****************************************************************************
* General Emitters */
void e(char *input, ...)
{
va_list arg;
char text[256];
va_start(arg, input);
vsprintf(text, input, arg);
va_end(arg);
fprintf(fp, text);
}
void EmitLabel(char *label)
{
e("%s:\n", label);
}
void Align(int i)
{
e("times ($$-$) & %d nop\n", i - 1);
}
void CacheAlign()
{
Align(32);
}
void EmitGlobalLabel(char *label)
{
e("global %s%s, _%s%s, %s%s_\n", id, label, id, label, id, label);
e("%s%s:\n_%s%s:\n%s%s_:\n", id, label, id, label, id, label);
}
/*****************************************************************************
* Utility Functions */
/*
* SizeOfContext(): Returns the size of the context if needed by the emitter.
* This should be used instead of C's sizeof().
*/
int SizeOfContext()
{
switch (mpu)
{
case 68010:
return sizeof(struct TURBO68K_CONTEXT_68010);
}
return sizeof(struct TURBO68K_CONTEXT_68000);
}
/*
* NameOfContext(): Returns the name of the context based on the MPU type,
* but NOT with the identifier in front of it.
*/
static char *NameOfContext()
{
static char name[16 + 6 + 1];
sprintf(name, "turbo68kcontext_%d", mpu);
return name;
}
/*
* NOTE: Only pass 32-bit or 8-bit (XL, not XH regs) to the SaveReg/SaveRegTo,
* RestoreReg/RestoreRegTo functions. Don't ever pass 16-bit regs!
*/
/*
* SaveReg/RestoreReg: Save/restore registers to/from a specific save slot
* area.
*/
void SaveReg(char *area, char *reg)
{
if (mmx)
{
if (!strcmp(area, "memhandler"))
{ /* memhandler always saves full reg */
if (!strcmp(reg, "eax")) { e("movd mm0, eax\n"); return; }
if (!strcmp(reg, "ebx")) { e("movd mm1, ebx\n"); return; }
if (!strcmp(reg, "ebp")) { e("movd mm2, ebp\n"); return; }
}
else if (!strcmp(area, "run"))
{ /* we have to check if its a full or byte reg */
if (!strcmp(reg, "edx") || !strcmp(reg, "dl"))
{
e("movd mm3, edx\n");
return;
}
if (!strcmp(reg, "esi")) { e("movd mm4, esi\n"); return; }
if (!strcmp(reg, "edi")) { e("movd mm5, edi\n"); return; }
}
}
e("mov [%s_", area);
if (reg[1] == 'l') /* ?l byte reg */
{
switch (reg[0])
{
case 'a': e("eax"); break;
case 'b': e("ebx"); break;
case 'c': e("ecx"); break;
case 'd': e("edx"); break;
}
}
else
e("%s", reg);
e("], %s\n", reg);
}
void RestoreReg(char *area, char *reg)
{
if (mmx)
{
if (!strcmp(area, "memhandler"))
{ /* memhandler always restores full reg */
if (!strcmp(reg, "eax")) { e("movd eax, mm0\n"); return; }
if (!strcmp(reg, "ebx")) { e("movd ebx, mm1\n"); return; }
if (!strcmp(reg, "ebp")) { e("movd ebp, mm2\n"); return; }
}
else if (!strcmp(area, "run"))
{ /* we have to check if its a full or byte reg */
if (!strcmp(reg, "edx") || !strcmp(reg, "dl"))
{
e("movd edx, mm3\n");
return;
}
if (!strcmp(reg, "esi")) { e("movd esi, mm4\n"); return; }
if (!strcmp(reg, "edi")) { e("movd edi, mm5\n"); return; }
}
}
e("mov %s, [%s_", reg, area);
if (reg[1] == 'l') /* ?l byte reg */
{
switch (reg[0])
{
case 'a': e("eax"); break;
case 'b': e("ebx"); break;
case 'c': e("ecx"); break;
case 'd': e("edx"); break;
}
}
else
e("%s", reg);
e("]\n");
}
/*
* SaveRegTo/RestoreRegTo: Saves/restore a register to a different register
* than indicated by the name of the save slot.
*/
void SaveRegTo(char *area, char *d_reg, char *s_reg)
{
if (mmx)
{
/* currently, memhandlers don't need this, only run_ does. */
if (!strcmp(area, "run"))
{
/* test to make sure dest is MMX-able */
if (!strcmp(d_reg, "edx") || !strcmp(d_reg, "esi") || !strcmp(d_reg, "edi"))
{
e("movd ");
if (!strcmp(d_reg, "edx")) e("mm3, ");
else if (!strcmp(d_reg, "esi")) e("mm4, ");
else if (!strcmp(d_reg, "edi")) e("mm5, ");
if (s_reg[1] == 'l') /* ?l byte reg */
{
switch (s_reg[0])
{
case 'a': e("eax\n"); break;
case 'b': e("ebx\n"); break;
case 'c': e("ecx\n"); break;
case 'd': e("edx\n"); break;
}
}
else
e("%s\n", s_reg);
return;
}
}
}
e("mov [%s_%s], %s\n", area, d_reg, s_reg);
}
void RestoreRegTo(char *area, char *s_reg, char *d_reg)
{
if (mmx)
{
/* currently, memhandlers don't need this, only run_ does. */
if (!strcmp(area, "run"))
{
/* test to make sure src is MMX-able */
if (!strcmp(s_reg, "edx") || !strcmp(s_reg, "esi") || !strcmp(s_reg, "edi"))
{
e("movd ");
if (d_reg[1] == 'l') /* ?l byte reg */
{
switch (d_reg[0])
{
case 'a': e("eax, "); break;
case 'b': e("ebx, "); break;
case 'c': e("ecx, "); break;
case 'd': e("edx, "); break;
}
}
else
e("%s, ", d_reg);
if (!strcmp(s_reg, "edx")) e("mm3\n");
else if (!strcmp(s_reg, "esi")) e("mm4\n");
else if (!strcmp(s_reg, "edi")) e("mm5\n");
return;
}
}
}
e("mov %s, [%s_%s]\n", d_reg, area, s_reg);
}
void AddrClip(char *reg)
{ /* if it's 32-bit we don't need this */
if (addr_mask != 0xffffffff) e("and %s, 0x%08X\n", reg, addr_mask);
}
void RideIntoTheDangerZone() /* where EBP is modified and must be saved */
{
e("push ebp\n");
}
void RideOutOfTheDangerZone()
{
e("pop ebp\n");
}
void GetArg(char *dest_reg, int num_arg, int stack_offs)
{
char *reg[2] = { "eax", "edx" }; /* currently, only 2 args supported */
if (num_arg > 1) /* error! */
{
fprintf(stderr, "Make68K: Internal Error: GetArg() called with num_arg = %d. Please contact Bart Trzynadlowski!\n", num_arg);
if (call_convention)
return;
}
if (!call_convention)
e("mov %s, [esp+%d]\n", dest_reg, stack_offs);
else
{
if ((!strcmp(dest_reg, "eax") && !num_arg) || (!strcmp(dest_reg, "edx") && num_arg == 1))
; /* EAX,EAX or EDX,EDX */
else
e("mov %s, %s\n", dest_reg, reg[num_arg]);
}
}
void GetArgFromTheDangerZone(char *dest_reg, int num)
{
char *reg[2] = { "eax", "edx" }; /* no more than 2 args usually */
if (!call_convention) /* stack-based */
e("mov %s, [esp+8+(%d*4)]\n", dest_reg, num);
else /* register-based */
{
if ((!strcmp(dest_reg, "eax") && !num) || (!strcmp(dest_reg, "edx") && num == 1))
; /* EAX,EAX or EDX,EDX */
else
e("mov %s, %s\n", dest_reg, reg[num]);
}
}
void GetArgNoStringsAttached(char *dest_reg, int num)
{
char *reg[2] = { "eax", "edx" }; /* no more than 2 args usually */
if (!call_convention)
e("mov %s, [esp+4+(%d*4)]\n", dest_reg, num);
else
{
if ((!strcmp(dest_reg, "eax") && !num) || (!strcmp(dest_reg, "edx") && num == 1))
; /* EAX,EAX or EDX,EDX */
else
e("mov %s, %s\n", dest_reg, reg[num]);
}
}
void ReadLong()
{
if (memmap_type == 0 || memmap_type == 1) e("call ReadLong\n");
else if (memmap_type == 2) e("call dword [read_long]\n");
}
void ReadByte()
{
if (memmap_type == 0 || memmap_type == 1) e("call ReadByte\n");
else if (memmap_type == 2) e("call dword [read_byte]\n");
}
void ReadWord()
{
if (memmap_type == 0 || memmap_type == 1) e("call ReadWord\n");
else if (memmap_type == 2) e("call dword [read_word]\n");
}
void WriteLong()
{
if (memmap_type == 0 || memmap_type == 1) e("call WriteLong\n");
else if (memmap_type == 2) e("call dword [write_long]\n");
}
void WriteByte()
{
if (memmap_type == 0 || memmap_type == 1) e("call WriteByte\n");
else if (memmap_type == 2) e("call dword [write_byte]\n");
}
void WriteWord()
{
if (memmap_type == 0 || memmap_type == 1) e("call WriteWord\n");
else if (memmap_type == 2) e("call dword [write_word]\n");
}
void ReadByteSX()
{
fprintf(stderr, "Make68K: Internal Error: Sign-extended byte read handler required. Please contact Bart Trzynadlowski!\n");
exit(1);
}
void ReadWordSX()
{
if (memmap_type == 0 || memmap_type == 1) e("call ReadWordSX\n");
else if (memmap_type == 2)
{
e("call dword [read_word]\n");
e("movsx edx, dx\n");
}
}
/*
* LoadFromEA() and MOVEM() use the ReadXXXPC() functions.
*/
void ReadLongPC()
{
e("call ReadLongPC\n");
}
void ReadBytePC()
{
e("call ReadBytePC\n");
}
void ReadWordPC()
{
e("call ReadWordPC\n");
}
void ReadByteSXPC()
{
fprintf(stderr, "Make68K: Internal Error: Sign-extended byte read handler required. Please contact Bart Trzynadlowski!\n");
exit(1);
e("call ReadBytePC\n");
e("movsx edx, dl\n");
}
void ReadWordSXPC()
{
e("call ReadWordPC\n");
e("movsx edx, dx\n");
}
void UpdateFetchPtr() /* scan the fetch area */
{
int inst = 0;
/*
* In: ESI = address
* Out: ESI = PC + ptr
* EBP = ptr (base)
* EDX = trashed
*/
/*
* Note: If address bus is NOT 32-bits, the following applies.
*
* The unused bits of the PC MUST be preserved. Therefore, those unused
* bits are subtracted from the base pointer so that when we add a PC
* WITH unused bits, it won't go out of range, because it will be far
* below range to begin with.
*
* The reasoning here is that if we subtract the unused bits from the base
* pointer, the unused bits in the normalized PC pointer (ESI) will cancel
* out.
*/
if (addr_mask != 0xffffffff)
SaveReg("fetch", "esi");
AddrClip("esi");
e("mov edx, [fetch]\n");
e(".find_fetch_loop%d:\n", inst);
e("add edx, byte "SIZEOF_FETCHREGION"\n");
e("cmp dword [edx], byte -1\n"); /* limit=-1? end. */
e("je near fetch_error\n");
e("cmp esi, [edx]\n"); /* offset 0: base. above it? */
e("jb .find_fetch_loop%d\n", inst); /* nope, not this region */
e("cmp esi, [edx+"OFFSET_FETCH_LIMIT"]\n"); /* limit. below it? */
e("ja .find_fetch_loop%d\n", inst); /* nope, not this region */
e("mov ebp, [edx+"OFFSET_FETCH_PTR"]\n"); /* ebp=base ptr */
if (addr_mask != 0xffffffff) /* to handle those nasty unused bits! */
{
e("mov edx, [fetch_esi]\n");
e("mov esi, edx\n"); /* this is the ACTUAL PC we saved earlier */
e("and edx, 0x%08X\n", ~addr_mask);/* we only want the unused bits */
e("sub ebp, edx\n");
}
e("add esi, ebp\n"); /* +pc=pc ptr.. */
}
void LoadCCR() /* CCR->AX ... trashes AX and DX */
{
e("mov dh, [sr]\n"); /* DH:***XNZVC */
e("mov ah, dh\n"); /* AH:***XNZVC */
e("shl ah, byte 4\n"); /* AH:NZVC0000 */
e("test dh, byte 2\n"); /* V flag? */
e("setnz al\n"); /* AL:0000000V */
e("test dh, 0x10\n"); /* X flag? */
e("setnz byte [x]\n"); /* X:0000000000000000000000000000000X */
e("and dh, byte 1\n"); /* DH:0000000C */
e("or ah, dh\n"); /* AH:NZ**000C */
}
void SaveCCR() /* AX->CCR... trashes AX and DX */
{
e("and eax, 0xc101\n"); /* EAX:0000000000000000NZ00000C0000000V */
e("mov dh, ah\n"); /* DH:NZ00000C */
e("shr dh, byte 4\n"); /* DH:0000NZ00 */
e("shl al, byte 1\n"); /* AL:000000V0 */
e("or dh, al\n"); /* DH:0000NZV0 */
e("shr ah, byte 1\n"); /* CF:C */
e("adc dh, 0\n"); /* DH:0000NZVC */
e("mov ah, [x]\n"); /* AH:0000000X */
e("shl ah, byte 4\n"); /* AH:000X0000 */
e("or dh, ah\n"); /* DH:000XNZVC */
e("mov [sr], dh\n");
}
void InstBegin(unsigned short op, char *mnem, unsigned num)
{
#ifdef PROFILE
int i;
#endif
Align(4);
if (num == 1)
e("I%04X: ; %s\t\t0x%04X\n", op, mnem, op);
else
e("I%04X: ; %s\t\t0x%04X-0x%04X\n", op, mnem, op, op+(num-1));
if (debug)
{
// Debugging code
char noDebugLabel[16];
char noPCChgLabel[16];
sprintf(noDebugLabel, ".no_debug%04X", op);
sprintf(noPCChgLabel, ".no_pcchg%04X", op);
e("mov ebx, [Debug]\n"); /* Get debug handler */
e("test ebx, ebx\n"); /* If no handler set, skip debugging */
e("jz short %s\n", noDebugLabel);
e("push eax\n"); /* Note: __cdecl calling convention used to call handler */
e("push edi\n");
e("push ebp\n");
WRITEPCTOMEM("pc"); /* Save esi */
e("mov [remaining], ecx\n");
e("push edi\n"); /* Pass instruction opcode to handler */
e("sub esi, ebp\n"); /* Convert PC to base offset */
e("sub esi, byte 2\n"); /* Adjust back to beginning of instruction */
e("push esi\n"); /* Pass result to handler */
e("call ebx\n"); /* Call handler */
e("add esp, byte 8\n");
e("mov esi, [pc]\n"); /* Restore esi (this could have been changed) */
e("mov ecx, [remaining]\n"); /* This could have been changed */
e("pop ebp\n");
e("pop edi\n");
e("add esi, ebp\n"); /* Convert esi to pointer (base+pc) */
e("test eax, eax\n"); /* Check if debug handler changed PC */
e("jz short %s\n", noPCChgLabel);
e("pop eax\n"); /* If so, move onto instruction at new PC */
e("mov di, [esi]\n");
e("add esi, byte 2\n"); /* Point to word after next opcode */
e("jmp dword [jmptab+edi*4]\n");
EmitLabel(noPCChgLabel);
e("pop eax\n");
EmitLabel(noDebugLabel);
}
decoded[op] = num;
num_handlers++;
#ifdef PROFILE
for (i = 0; i < strlen(mnem); i++) /* remove spaces */
{
if (mnem[i] == ' ')
mnem[i] = '_';
}
e("inc dword [prof%s]\n", mnem);
for (i = 0; prof[i] != NULL && i < 512; i++)
{
if (!strcmp(prof[i], mnem)) /* already have one of these... */
return;
}
prof[prof_i] = calloc(strlen(mnem) + 1, 1);
strcpy(prof[prof_i], mnem);
prof_i++;
#endif
}
void InstEnd()
{
e("mov di, [esi]\n");
e("add esi, byte 2\n"); /* point to word after next opcode */
e("jmp dword [jmptab+edi*4]\n");
}
/*****************************************************************************
* Address Space Management */
/*
* Input: Nothing
* Notes: Assumes that run_ecx, run_edi, and run_esi are available for use.
*/
void SetSupervisorAddressSpace()
{
if (!multiaddr) return;
e("push ecx\n");
e("cld\n"); /* direction: forward */
e("push esi\n");
e("push edi\n");
e("mov ecx, 8\n"); /* 8 dword-size pointers to copy */
e("mov edi, fetch\n");
e("mov esi, super_fetch\n");
e("rep movsd\n");
e("pop edi\n");
e("pop esi\n");
e("pop ecx\n");
}
void SetUserAddressSpace()
{
if (!multiaddr) return;
e("push ecx\n");
e("cld\n"); /* direction: forward */
e("push esi\n");
e("push edi\n");
e("mov ecx, 8\n"); /* 8 dword-size pointers to copy */
e("mov edi, fetch\n");
e("mov esi, user_fetch\n");
e("rep movsd\n");
e("pop edi\n");
e("pop esi\n");
e("pop ecx\n");
}
/*****************************************************************************
* Timing */
/* TimingEA: calculates amount of clocks taken by a given EA mode */
unsigned TimingEA(unsigned ea, int size)
{ /* EA format = MMMRRR (mode/register) */
switch ((ea >> 3) & 7) /* mode */
{
case 0: case 1: return 0; break;
case 2: case 3:
switch (size) { case BYTE_SIZE: case WORD_SIZE: return 4; break;
case LONG_SIZE: return 8; break; } break;
case 4:
switch (size) { case BYTE_SIZE: case WORD_SIZE: return 6; break;
case LONG_SIZE: return 10; break; } break;
case 5:
switch (size) { case BYTE_SIZE: case WORD_SIZE: return 8; break;
case LONG_SIZE: return 12; break; } break;
case 6:
switch (size) { case BYTE_SIZE: case WORD_SIZE: return 10; break;
case LONG_SIZE: return 14; break; } break;
case 7: /* misc... */
switch (ea & 0x7) { case 0: /* (xxx).W */
switch (size)
{ case BYTE_SIZE: case WORD_SIZE: return 8; break;
case LONG_SIZE: return 12; break; } break;
case 1: /* (xxx).L */
switch (size)
{ case BYTE_SIZE: case WORD_SIZE: return 12; break;
case LONG_SIZE: return 16; break; } break;
case 4: /* #<data> */
switch (size)
{ case BYTE_SIZE: case WORD_SIZE: return 4; break;
case LONG_SIZE: return 8; break; } break;
case 2: /* (d16,pc) */
switch (size)
{ case BYTE_SIZE: case WORD_SIZE: return 8; break;
case LONG_SIZE: return 12; break; } break;
case 3: /* (d8,pc,Xn) */
switch (size)
{ case BYTE_SIZE: case WORD_SIZE: return 10; break;
case LONG_SIZE: return 14; break; } break;
}
} return 0;
}
void EmitTiming(unsigned cycles)
{
if (cycles)
{
if (cycles == 1) e("dec ecx\n");
else if (!(cycles & 0x80)) e("sub ecx, byte %d\n", cycles);
else e("sub ecx, %d\n", cycles);
e("js near Turbo68KRun_done\n"); /* below 0, finished */
}
}
/*****************************************************************************
* Effective Address Code */
int CheckEA(unsigned ea, char *valid) /* EA = MMMRRR (mode/reg) */
{
/*
* valid[] string is a mask for valid addressing modes as they appear
* in the 68K manual, in order, 1 = okay, 0 = not allowed
*/
if (((ea >> 3) & 7) != 7) /* mode 7 is handled separately */
{
if (valid[(ea >> 3) & 7] != '1') return 0; /* not allowed! */
else return 1;
}
else /* handle mode 7 here */
{
/* first check if the register is invalid */
if ((ea & 7) >= 5) return 0; /* no such thing! */
else
{
switch (ea & 7)
{
case 0:
case 1:
if (valid[7+(ea & 7)] != '1') return 0;
else return 1; break;
case 4:
if (valid[7+2] != '1') return 0;
else return 1; break;
case 2:
if (valid[7+3] != '1') return 0;
else return 1; break;
case 3:
if (valid[7+4] != '1') return 0;
else return 1; break;
}
}
} return 0;
}
int NumDecodedEA(unsigned ea) /* how many handlers can this feed? */
{
/*
* Turbo68K does not generate an instruction handler for every possible
* opcode, instead, it generates one for every possible EA mode of a given
* instruction. This means if reg==0, it returns the # to pass to InstEnd
* (which indicates how many opcodes this will cover for) Otherwise, this
* function returns 0 and the instruction handler should not be emitted.
*/
if (((ea >> 3) & 7) <= 6 && (ea & 7) != 0) return 0;
else if (((ea >> 3) & 7) <= 6 && (ea & 7) == 0) return 8; /* 8 regs */
else if (((ea >> 3) & 7) == 7 && (ea & 7) <= 4) return 1; /* 1 reg */
return 0;
}
/*
* Effective Address calculations
*
* Note: When loading words, usually the whole dword is loaded. When storing,
* only the word part is stored
*/
/*
* Returns: Data in EDX, address preserved in EBX
* Used: EBX, EDX, EDI (unless reading from register)
* Notes: ESI is assumed to point at word after opcode
* Assumes EDI contains opcode and that it has EA at the very end
* This function is only to be used when EA mode fields are located
* at bits 0-5.
*/
void LoadFromEA(unsigned ea, int size, int sign_extend)
{
switch ((ea >> 3) & 7)
{
case 0: /* Dn */
case 1: /* An */
{
char r = ((ea >> 3) & 7) ? 'a' : 'd';
e("and edi, byte 7\n"); /* get reg number, EDI contains opcode */
if (size == LONG_SIZE)
e("mov edx, [%c+edi*4]\n", r);
else
{
if (size == BYTE_SIZE)
{
if (!sign_extend)
e("mov dl, [%c+edi*4]\n", r);
else
e("movsx edx, byte [%c+edi*4]\n", r);
}
else /* word size */
{
if (!sign_extend)
e("mov edx, [%c+edi*4]\n", r);
else
e("movsx edx, word [%c+edi*4]\n", r);
}
}
}
break;
case 2: /* (An) */
case 3: /* (An)+ */
case 4: /* -(An) */
case 5: /* (d16,An) */
e("and edi, byte 7\n"); /* reg */
if (((ea >> 3) & 7) == 4) /* pre-dec */
{
if (size == BYTE_SIZE)
{
e("cmp edi, byte 7\n"); /* A7 bytes */
e("cmc\n"); /* switch carry */
e("sbb dword [a+edi*4], byte 1\n");
}
else if (size == WORD_SIZE)
e("sub dword [a+edi*4], byte 2\n");
else if (size == LONG_SIZE)
e("sub dword [a+edi*4], byte 4\n");
}
if (((ea >> 3) & 7) == 5) /* (d16,An) */
{
e("movsx ebx, word [esi]\n");
e("add ebx, [a+edi*4]\n");
e("add esi, byte 2\n"); /* past d16, next instruction */
}
else
e("mov ebx, [a+edi*4]\n");
if (((ea >> 3) & 7) == 3) /* post-inc */
{
if (size == BYTE_SIZE)
{
e("cmp edi, byte 7\n"); /* A7 bytes */
e("cmc\n"); /* switch carry */
e("adc dword [a+edi*4], byte 1\n");
}
else if (size == WORD_SIZE)
e("add dword [a+edi*4], byte 2\n");
else if (size == LONG_SIZE)
e("add dword [a+edi*4], byte 4\n");
}
switch (size)
{
case BYTE_SIZE:
if (sign_extend)
ReadByteSX();
else
ReadByte();
break;
case WORD_SIZE:
if (sign_extend)
ReadWordSX();
else
ReadWord();
break;
case LONG_SIZE:
ReadLong();
break;
}
break;
case 6: /* (d8,An,Xn) */
e("and edi, byte 7\n"); /* An reg # */
e("xor ebx, ebx\n"); /* so the high part is clear */
e("mov bx, [esi]\n"); /* fetch extension word */
e("movsx edx, bl\n"); /* sign-extended index */
e("add esi, byte 2\n"); /* point at next word */
e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */
/* the shr sets CF if Long reg */
e("mov ebx, [d+ebx*4]\n");
e("jc short .ll\n"); /* CF=1? index reg is LONG */
e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */
EmitLabel(".ll");
e("add ebx, edx\n"); /* disp+index_reg */
e("add ebx, [a+edi*4]\n");
switch (size)
{
case BYTE_SIZE:
if (sign_extend)
ReadByteSX();
else
ReadByte();
break;
case WORD_SIZE:
if (sign_extend)
ReadWordSX();
else
ReadWord();
break;
case LONG_SIZE:
ReadLong();
break;
}
break;
case 0x7:
switch (ea & 7)
{
case 0: /* (xxx).W */
case 1: /* (xxx).L */
if ((ea & 7) == 0) /* word */
{
e("movsx ebx, word [esi]\n");
e("add esi, byte 2\n");
}
else /* long */
{
e("mov ebx, [esi]\n");
e("ror ebx, byte 16\n"); /* word-swap */
e("add esi, byte 4\n");
}
switch (size)
{
case BYTE_SIZE:
if (sign_extend)
ReadByteSX();
else
ReadByte();
break;
case WORD_SIZE:
if (sign_extend)
ReadWordSX();
else
ReadWord();
break;
case LONG_SIZE:
ReadLong();
break;
}
break;
case 4: /* #<data> */
switch (size)
{
case BYTE_SIZE:
if (!sign_extend)
e("mov dl, [esi]\n"); /* get low byte of ext. word */
else
e("movsx edx, byte [esi]\n");
e("add esi, byte 2\n");
break;
case WORD_SIZE:
if (!sign_extend)
e("mov edx, [esi]\n"); /* bx = word, ebx=????:word */
else
e("movsx edx, word [esi]\n");
e("add esi, byte 2\n");
break;
case LONG_SIZE:
e("mov edx, [esi]\n"); /* get long-word */
e("add esi, byte 4\n");
e("ror edx, byte 16\n"); /* word swap to work... */
break;
}
break;
case 2: /* (d16,PC) */
/*
* The PC is assumed to be pointing at the extension word, which
* is exactly how the emulator works
*/
e("movsx ebx, word [esi]\n");
e("add ebx, esi\n");
e("sub ebx, ebp\n");
e("add esi, byte 2\n");
switch (size)
{
case BYTE_SIZE:
if (sign_extend)
pcfetch ? ReadByteSXPC() : ReadByteSX();
else
pcfetch ? ReadBytePC() : ReadByte();
break;
case WORD_SIZE:
if (sign_extend)
pcfetch ? ReadWordSXPC() : ReadWordSX();
else
pcfetch ? ReadWordPC() : ReadWord();
break;
case LONG_SIZE:
pcfetch ? ReadLongPC() : ReadLong();
break;
}
break;
case 3: /* (d8,PC,Xn) */
e("xor ebx, ebx\n"); /* so the high part is clear */
e("mov bx, [esi]\n"); /* fetch extension word */
e("movsx edx, bl\n"); /* sign-extended index */
e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */
/* the shr sets CF if Long reg */
e("mov ebx, [d+ebx*4]\n");
e("jc short .ll\n"); /* CF=1? index reg is LONG */
e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */
EmitLabel(".ll");
e("add ebx, edx\n"); /* disp+index_reg */
e("add ebx, esi\n"); /* add the PC */
e("sub ebx, ebp\n");
e("add esi, byte 2\n"); /* point at next word */
switch (size)
{
case BYTE_SIZE:
if (sign_extend)
pcfetch ? ReadByteSXPC() : ReadByteSX();
else
pcfetch ? ReadBytePC() : ReadByte();
break;
case WORD_SIZE:
if (sign_extend)
pcfetch ? ReadWordSXPC() : ReadWordSX();
else
pcfetch ? ReadWordPC() : ReadWord();
break;
case LONG_SIZE:
pcfetch ? ReadLongPC() : ReadLong();
break;
}
break;
}
break;
default:
printf("Error!\a\n");
break;
}
}
/*
* Expects: Data in EDX, address in EBX
* Notes: Destroys EBX, make sure to preserve the address if you need it.
*/
void StoreToEA(unsigned ea, int size, int same)
{ /* the "same" parameter is ignored. it no longer is needed */
switch ((ea >> 3) & 7)
{
case 0: /* Dn */
case 1: /* An */
{
char r = ((ea >> 3) & 7) ? 'a' : 'd';
if (size == LONG_SIZE)
e("mov [%c+%d*4], edx\n", r, ea & 7);
else if (size == BYTE_SIZE)
e("mov [%c+%d*4], dl\n", r, ea & 7);
else if (size == WORD_SIZE)
e("mov [%c+%d*4], dx\n", r, ea & 7);
}
break;
case 2: /* (An) */
case 3: /* (An)+ */
case 4: /* -(An) */
case 5: /* (d16,An) */
if (((ea >> 3) & 7) == 4) /* pre-dec */
{
if (size == BYTE_SIZE)
{
if ((ea & 7) == 7) /* A7 kept word aligned */
e("sub dword [a+%d*4], byte 2\n", ea & 7);
else
e("dec dword [a+%d*4]\n", ea & 7);
}
else if (size == WORD_SIZE)
e("sub dword [a+%d*4], byte 2\n", ea & 7);
else if (size == LONG_SIZE)
e("sub dword [a+%d*4], byte 4\n", ea & 7);
}
if (((ea >> 3) & 7) == 5) /* (d16,An) */
{
e("movsx ebx, word [esi]\n");
e("add ebx, [a+%d*4]\n", ea & 7);
e("add esi, byte 2\n"); /* past d16, next instruction */
}
else
e("mov ebx, [a+%d*4]\n", ea & 7);
if (((ea >> 3) & 7) == 3) /* post-inc */
{
if (size == BYTE_SIZE)
{
if ((ea & 7) == 7) /* A7 kept word aligned */
e("add dword [a+%d*4], byte 2\n", ea & 7);
else
e("inc dword [a+%d*4]\n", ea & 7);
}
else if (size == WORD_SIZE)
e("add dword [a+%d*4], byte 2\n", ea & 7);
else if (size == LONG_SIZE)
e("add dword [a+%d*4], byte 4\n", ea & 7);
}
switch (size)
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break; }
break;
case 6: /* (d8,An,Xn) */
e("xor ebx, ebx\n"); /* so the high part is clear */
e("mov bx, [esi]\n"); /* fetch extension word */
e("movsx edi, bl\n"); /* sign-extended index */
e("add esi, byte 2\n"); /* point at next word */
e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */
/* the shr sets CF if Long reg */
e("mov ebx, [d+ebx*4]\n");
e("jc short .ls\n"); /* CF=1? index reg is LONG */
e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */
EmitLabel(".ls");
e("add ebx, edi\n"); /* disp+index_reg */
e("add ebx, [a+%d*4]\n", ea & 7);
switch (size) /* WriteXXX() clears EDI */
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break; }
break;
case 0x7:
switch (ea & 7)
{
case 0: /* (xxx).W */
case 1: /* (xxx).L */
if ((ea & 7) == 0) /* word */
{
e("movsx ebx, word [esi]\n");
e("add esi, byte 2\n");
}
else /* long */
{
e("mov ebx, [esi]\n");
e("ror ebx, byte 16\n"); /* word-swap */
e("add esi, byte 4\n");
}
switch (size)
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break; }
break;
case 4: /* #<data> */
/* can't store to #data! */
printf("Error!\a\n");
break;
case 2: /* (d16,PC) */
/*
* The PC is assumed to be pointing at the extension word, which
* is exactly how the emulator works
*/
e("movsx ebx, word [esi]\n");
e("add ebx, esi\n");
e("sub ebx, ebp\n");
e("add esi, byte 2\n");
switch (size)
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break; }
break;
case 3: /* (d8,PC,Xn) */
e("xor ebx, ebx\n"); /* so the high part is clear */
e("mov bx, [esi]\n"); /* fetch extension word */
e("movsx edi, bl\n"); /* sign-extended index */
e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */
/* the shr sets CF if Long reg */
e("mov ebx, [d+ebx*4]\n");
e("jc short .ls\n"); /* CF=1? index reg is LONG */
e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */
EmitLabel(".ls");
e("add ebx, edi\n"); /* disp+index_reg */
e("add ebx, esi\n"); /* add the PC */
e("sub ebx, ebp\n");
e("add esi, byte 2\n"); /* point at next word */
switch (size) /* WriteXXX() clears EDI */
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break; }
break;
}
break;
default:
break;
}
}
/*
* This is the same as StoreToEA() except that it uses EDI&7 to find the
* register. It is assumed that EA is bits 0-5 of the opcode. This only works
* in special situations like CLR and Scc.
*/
void StoreToEAUsingEDI(unsigned ea, int size, int same)
{ /* the "same" parameter is ignored. it no longer is needed */
switch ((ea >> 3) & 7)
{
case 0: /* Dn */
case 1: /* An */
{
char r = ((ea >> 3) & 7) ? 'a' : 'd';
e("and edi, byte 7\n");
if (size == LONG_SIZE)
e("mov [%c+edi*4], edx\n", r);
else if (size == BYTE_SIZE)
e("mov [%c+edi*4], dl\n", r);
else if (size == WORD_SIZE)
e("mov [%c+edi*4], dx\n", r);
}
break;
case 2: /* (An) */
case 3: /* (An)+ */
case 4: /* -(An) */
case 5: /* (d16,An) */
e("and edi, byte 7\n");
if (((ea >> 3) & 7) == 4) /* pre-dec */
{
if (size == BYTE_SIZE)
{
e("cmp edi, byte 7\n"); /* A7 bytes */
e("cmc\n"); /* switch carry */
e("sbb dword [a+edi*4], byte 1\n");
}
else if (size == WORD_SIZE)
e("sub dword [a+edi*4], byte 2\n");
else if (size == LONG_SIZE)
e("sub dword [a+edi*4], byte 4\n");
}
if (((ea >> 3) & 7) == 5) /* (d16,An) */
{
e("movsx ebx, word [esi]\n");
e("add ebx, [a+edi*4]\n");
e("add esi, byte 2\n"); /* past d16, next instruction */
}
else
e("mov ebx, [a+edi*4]\n");
if (((ea >> 3) & 7) == 3) /* post-inc */
{
if (size == BYTE_SIZE)
{
e("cmp edi, byte 7\n"); /* A7 bytes */
e("cmc\n"); /* switch carry */
e("adc dword [a+edi*4], byte 1\n");
}
else if (size == WORD_SIZE)
e("add dword [a+edi*4], byte 2\n");
else if (size == LONG_SIZE)
e("add dword [a+edi*4], byte 4\n");
}
switch (size)
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break; }
break;
case 6: /* (d8,An,Xn) */
e("and edi, byte 7\n"); /* An reg # */
SaveReg("memhandler", "edi"); /* no memhandlers using this now... */
e("xor ebx, ebx\n"); /* so the high part is clear */
e("mov bx, [esi]\n"); /* fetch extension word */
e("movsx edi, bl\n"); /* sign-extended index */
e("add esi, byte 2\n"); /* point at next word */
e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */
/* the shr sets CF if Long reg */
e("mov ebx, [d+ebx*4]\n");
e("jc short .ll\n"); /* CF=1? index reg is LONG */
e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */
EmitLabel(".ll");
e("add ebx, edi\n"); /* disp+index_reg */
RestoreReg("memhandler", "edi");
e("add ebx, [a+edi*4]\n");
switch (size) /* WriteXXX() clears EDI */
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break; }
break;
case 0x7:
switch (ea & 7)
{
case 0: /* (xxx).W */
case 1: /* (xxx).L */
if ((ea & 7) == 0) /* word */
{
e("movsx ebx, word [esi]\n");
e("add esi, byte 2\n");
}
else /* long */
{
e("mov ebx, [esi]\n");
e("ror ebx, byte 16\n"); /* word-swap */
e("add esi, byte 4\n");
}
switch (size)
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break; }
break;
case 4: /* #<data> */
/* can't store to #data! */
break;
case 2: /* (d16,PC) */
/*
* The PC is assumed to be pointing at the extension word, which
* is exactly how the emulator works
*/
e("movsx ebx, word [esi]\n");
e("add ebx, esi\n");
e("sub ebx, ebp\n");
e("add esi, byte 2\n");
switch (size)
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break; }
break;
case 3: /* (d8,PC,Xn) */
e("xor ebx, ebx\n"); /* so the high part is clear */
e("mov bx, [esi]\n"); /* fetch extension word */
e("movsx edi, bl\n"); /* sign-extended index */
e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */
/* the shr sets CF if Long reg */
e("mov ebx, [d+ebx*4]\n");
e("jc short .ls\n"); /* CF=1? index reg is LONG */
e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */
EmitLabel(".ls");
e("add ebx, edi\n"); /* disp+index_reg */
e("add ebx, esi\n"); /* add the PC */
e("sub ebx, ebp\n");
e("add esi, byte 2\n"); /* point at next word */
switch (size) /* WriteXXX() clears EDI */
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break; }
break;
}
break;
default:
printf("Error!\a\n");
break;
}
}
/*
* Returns: Address in EBX
* Used: EBX, EDX
* Notes: ESI is assumed to point at word after opcode
* Assumes EDI contains opcode and that it has EA at the very end
* This function is only to be used when EA mode fields are located
* at bits 0-5.
*/
void LoadControlEA(unsigned ea)
{
switch ((ea >> 3) & 7)
{
case 2: /* (An) */
case 3: /* (An)+ (just load what's in (An) -- MOVEM needs this) */
case 4: /* -(An) (just load what's in (An) -- MOVEM needs this) */
case 5: /* (d16,An) */
e("and edi, byte 7\n"); /* reg */
if (((ea >> 3) & 7) == 5) /* (d16,An) */
{
e("movsx ebx, word [esi]\n");
e("add ebx, [a+edi*4]\n");
e("add esi, byte 2\n"); /* past d16, next instruction */
}
else
e("mov ebx, [a+edi*4]\n");
break;
case 6: /* (d8,An,Xn) */
e("and edi, byte 7\n"); /* edi=An # */
e("xor ebx, ebx\n"); /* so the high part is clear */
e("mov bx, [esi]\n"); /* fetch extension word */
e("movsx edx, bl\n"); /* sign-extended index */
e("add esi, byte 2\n"); /* point at next word */
e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */
/* the shr sets CF if Long reg */
e("mov ebx, [d+ebx*4]\n");
e("jc short .ll\n"); /* CF=1? index reg is LONG */
e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */
EmitLabel(".ll");
e("add ebx, edx\n"); /* disp+index_reg */
e("add ebx, [a+edi*4]\n");
break;
case 0x7:
switch (ea & 7)
{
case 0: /* (xxx).W */
case 1: /* (xxx).L */
if ((ea & 7) == 0) /* word */
{
e("movsx ebx, word [esi]\n");
e("add esi, byte 2\n");
}
else /* long */
{
e("mov ebx, [esi]\n");
e("ror ebx, byte 16\n"); /* word-swap */
e("add esi, byte 4\n");
}
break;
case 2: /* (d16,PC) */
/*
* The PC is assumed to be pointing at the extension word, which
* is exactly how the emulator works
*/
e("movsx ebx, word [esi]\n");
e("add ebx, esi\n");
e("sub ebx, ebp\n");
e("add esi, byte 2\n");
break;
case 3: /* (d8,PC,Xn) */
e("xor ebx, ebx\n"); /* so the high part is clear */
e("mov bx, [esi]\n"); /* fetch extension word */
e("movsx edx, bl\n"); /* sign-extended index */
e("shr ebx, byte 12\n"); /* regs must be: d, a for this to work */
/* the shr sets CF if Long reg */
e("mov ebx, [d+ebx*4]\n");
e("jc short .ll\n"); /* CF=1? index reg is LONG */
e("movsx ebx, bx\n"); /* otherwise: sign-extended WORD */
EmitLabel(".ll");
e("add ebx, edx\n"); /* disp+index_reg */
e("add ebx, esi\n"); /* add the PC */
e("sub ebx, ebp\n");
e("add esi, byte 2\n"); /* point at next word */
break;
}
break;
default:
printf("Warning: LoadControlEA() defaulted\n");
break;
}
}
/*****************************************************************************
* Instruction Handlers */
int RTD(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
e("mov ebx, [a+7*4]\n");
ReadLong();
e("xor ebx, ebx\n");
e("add dword [a+7*4], byte 4\n");
e("mov bx, [esi]\n");
e("add dword [a+7*4], ebx\n"); /* SP+4+disp */
e("mov esi, edx\n");
UpdateFetchPtr();
EmitTiming(base_timing);
InstEnd();
return 1;
}
int MOVEC(unsigned short op, char *mnem, unsigned base_timing)
{
/*
* Timing for an illegal control register is not handled, control is
* simply passed to the illegal instruction exception handler, which uses
* different timing.
*/
InstBegin(op, mnem, 2);
e("test byte [sr+1], 0x20\n"); /* in supervisor? */
e("jz near exception_privilege_violation\n");
e("xor ebx, ebx\n");
e("xor edx, edx\n");
e("mov dx, [esi]\n"); /* fetch data word */
e("mov ebx, edx\n");
e("shr ebx, byte 12\n"); /* EBX=general register */
e("shr edi, byte 1\n"); /* dr field */
e("jc short .to_control\n");
/*
* Test for control register. The control register value is placed in
* EDX. At the end, EDX is moved to [d+ebx*4].
*/
e("and edx, 0xfff\n");
e("jnz short .from_dfc\n");
e("mov dl, [fc]\n"); /* SFC */
e("jmp short .end_from\n");
EmitLabel(".from_dfc");
e("cmp edx, byte 1\n");
e("jne short .from_usp\n");
e("mov dl, [fc+1]\n"); /* DFC */
e("jmp short .end_from\n");
EmitLabel(".from_usp");
e("cmp edx, 0x800\n");
e("jne short .from_vbr\n");
e("mov edx, [__sp]\n"); /* we're in supervisor, USP is in __sp */
e("jmp short .end_from\n");
EmitLabel(".from_vbr");
e("cmp edx, 0x801\n");
e("jne near exception_illegal_instruction\n"); /* invalid control reg */
e("mov edx, [vbr]\n"); /* VBR */
EmitLabel(".end_from");
e("mov [d+ebx*4], edx\n"); /* move to general reg */
e("add esi, byte 2\n"); /* remember, we fetched a word */
EmitTiming(12);
e("jmp short .end\n");
/*
* Test for control register. General reg is written to control reg.
*/
EmitLabel(".to_control");
e("mov ebx, [d+ebx*4]\n"); /* general reg */
e("and edx, 0xfff\n");
e("jnz short .to_dfc\n");
e("and bl, 3\n");
e("mov [fc], bl\n"); /* SFC */
e("jmp short .end_to\n");
EmitLabel(".to_dfc");
e("cmp edx, byte 1\n");
e("jne short .to_usp\n");
e("and bl, 3\n");
e("mov [fc+1], bl\n"); /* DFC */
e("jmp short .end_to\n");
EmitLabel(".to_usp");
e("cmp edx, 0x800\n");
e("jne short .to_vbr\n");
e("mov [__sp], ebx\n"); /* we're in supervisor, USP is in __sp */
e("jmp short .end_to\n");
EmitLabel(".to_vbr");
e("cmp edx, 0x801\n");
e("jne near exception_illegal_instruction\n"); /* invalid control reg */
e("mov [vbr], ebx\n"); /* VBR */
EmitLabel(".end_to");
e("add esi, byte 2\n"); /* remember, we fetched a word */
EmitTiming(10);
/*
* End
*/
EmitLabel(".end");
InstEnd();
return 2;
}
int BKPT(unsigned short op, char *mnem, unsigned base_timing)
{
/*
* NOTE: Timing isn't done here, the exception processing code will do it.
* The exception code doesn't use the proper timing for a BKPT, but it's
* good enough ;)
*/
InstBegin(op, mnem, 8);
e("and edi, byte 7\n"); /* vector # */
e("mov ebx, [Bkpt]\n"); /* BKPT handler */
e("test ebx, ebx\n"); /* if no handler, don't do anything */
e("jz short .no_handler\n");
e("push eax\n");
e("push edi\n");
e("push ebp\n");
WRITEPCTOMEM("pc"); /* saves esi */
e("mov [remaining], ecx\n");
e("push edi\n"); /* vector # */
e("call ebx\n");
e("add esp, byte 4\n");
e("mov esi, [pc]\n");
e("mov ecx, [remaining]\n"); /* this could have been changed */
e("pop ebp\n");
e("pop edi\n");
e("pop eax\n");
e("add esi, ebp\n"); /* base+pc=pc pointer */
EmitLabel(".no_handler");
e("jmp near exception_illegal_instruction\n");
return 8;
}
int MOVEfromCCR(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned ea = op & 0x3f, t = 4;
if (!CheckEA(ea, "101111111000")) return 0;
if (!NumDecodedEA(ea)) return 0;
InstBegin(op, mnem, NumDecodedEA(ea));
e("xor edx, edx\n"); /* CCR->(sign extend)->word */
e("mov dl, [sr]\n");
StoreToEA(ea, WORD_SIZE, 0);
if ((ea >> 3) != 0) t += 4; /* mem */
EmitTiming(t + TimingEA(ea, WORD_SIZE)); /* technically, word sized */
InstEnd();
return 1;
}
int TRAPV(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
e("test al, al\n"); /* do only if V */
e("jz near .no_trap\n");
e("mov edx, 0x2000\n"); /* what the SR will be */
e("xor edx, [sr]\n"); /* see if S bit is different */
e("test edx, 0x2000\n");
e("jz .no_sp_magic\n"); /* nope, don't swap SPs */
e("mov edx, [__sp]\n");
e("xchg [a+7*4], edx\n");
e("mov [__sp], edx\n");
SetSupervisorAddressSpace();
EmitLabel(".no_sp_magic");
e("mov ebx, [a+7*4]\n"); /* SP */
/*
* If 68010, push format info
*/
if (mpu == 68010)
{
e("sub ebx, byte 2\n");/* SP-2 */
e("mov edx, 7*4\n"); /* TRAPV vector | 0x00000000 */
e("push ebx\n");
WriteWord();
e("pop ebx\n");
}
e("mov edx, esi\n");
e("sub edx, ebp\n"); /* PC->EDX */
e("sub ebx, byte 4\n"); /* SP-4 */
e("push ebx\n");
WriteLong();
e("pop ebx\n");
e("mov edx, [sr]\n"); /* get SR */
e("sub ebx, byte 2\n"); /* SP-2 */
e("push ebx\n");
WriteWord(); /* save SR to stack */
e("pop ebx\n");
e("or dh, 0x20\n"); /* set supervisor for exception */
e("and edx, 0xa71f\n"); /* clear unwanted bits */
e("xchg [sr], edx\n"); /* set new SR, get old SR->EDX */
e("mov [a+7*4], ebx\n"); /* write back SP */
e("mov ebx, 7*4\n"); /* TRAPV vector */
if (mpu == 68010)
e("add ebx, [vbr]\n");
ReadLong(); /* get PC */
e("mov esi, edx\n"); /* set new PC */
UpdateFetchPtr();
EmitTiming(34+4); /* TRAPV=4, exception=34 */
InstEnd();
EmitLabel(".no_trap");
e("xor edi, edi\n");
EmitTiming(4);
InstEnd();
return 1;
}
int STOP(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
e("test byte [sr+1], 0x20\n"); /* in supervisor? */
e("jz near exception_privilege_violation\n");
e("mov edx, [esi]\n");
e("add esi, byte 2\n");
e("and edx, 0xa71f\n"); /* mask out unimplemented bits */
e("mov [sr], dx\n");
LoadCCR();
STOP_CPU();
EmitTiming(base_timing);
e("mov dword [remaining], 0\n"); /* stop executing */
e("jmp near Turbo68KRun_done\n");
return 1;
}
int RESET(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
e("test byte [sr+1], 0x20\n"); /* in supervisor? */
e("jz near exception_privilege_violation\n");
e("mov ebx, [Reset]\n"); /* Reset handler */
e("test ebx, ebx\n"); /* if no handler, don't do anything */
e("jz short .no_reset\n");
e("push eax\n");
e("push ebp\n");
WRITEPCTOMEM("pc"); /* saves esi */
e("mov [remaining], ecx\n");
e("call ebx\n");
e("mov esi, [pc]\n");
e("mov ecx, [remaining]\n"); /* this could have been changed */
e("pop ebp\n");
e("pop eax\n");
e("add esi, ebp\n"); /* base+pc=pc pointer */
e("xor edi, edi\n"); /* keep clear for fetch */
EmitLabel(".no_reset");
EmitTiming(base_timing);
InstEnd();
return 1;
}
int TAS(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned ea = op & 0x3f;
if (!CheckEA(ea, "101111111000")) return 0;
if (!NumDecodedEA(ea)) return 0;
InstBegin(op, mnem, NumDecodedEA(ea));
LoadFromEA(ea, BYTE_SIZE, 0);
e("test dl, dl\n"); /* test... */
e("lahf\n");
e("xor al, al\n");
e("or dl, 0x80\n"); /* ...and set bit 7 */
if ((ea >> 3) == 0)
{
e("mov [d+edi*4], dl\n");
EmitTiming(4);
}
else
{
/*
* NOTE:
* -----
*
* You may wish to uncomment the WriteByte() below when emulating the Sega
* Genesis if you wish to get Gargoyles running. The TAS instruction is used
* to synchronize multiple 68Ks by grabbing the bus, but on the Genesis, the
* VDP will not release the bus resulting in data not being written.
*/
WriteByte();
EmitTiming(14 + TimingEA(ea, BYTE_SIZE));
}
InstEnd();
return 1;
}
int NBCD(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned ea = op & 0x3f, t;
/*
* Notes About Undefined Flags:
* ----------------------------
*
* N flag = Set the same as the MSB of the result
* V flag = If MSB changes from 1 to 0 when result is adjusted for BCD,
* the V flag is set, otherwise it is cleared.
*/
if (!CheckEA(ea, "101111111000")) return 0;
if (!NumDecodedEA(ea)) return 0;
InstBegin(op, mnem, NumDecodedEA(ea));
if ((ea >> 3) == 0) /* Dn */
{
e("and edi, byte 7\n");
e("xor al, al\n"); /* we use AL so we can use DAS */
e("shr byte [x], 1\n"); /* X->CF */
e("sbb al, [d+edi*4]\n");
e("mov bh, al\n"); /* save unadjusted result in BH */
e("das\n"); /* BCD! */
e("mov bl, ah\n"); /* store old flags */
e("lahf\n");
e("setc byte [x]\n");
e("jnz short .clr\n"); /* Z clear */
e("and bl, byte 0x40\n"); /* isolate old Z */
e("and ah, byte 0x3f\n"); /* kill new Z */
e("or ah, bl\n"); /* in w/ old Z */
EmitLabel(".clr");
e("test al, al\n"); /* N flag */
e("sets dh\n");
e("ror dh, 1\n"); /* into MSB of DH */
e("or ah, dh\n"); /* -> N flag */
e("mov [d+edi*4], al\n");
/*
* V flag calculation: Put unadjusted result's MSB into bit 1, and the
* adjusted result's MSB into bit 0. If the result is 2, the V flag
* is set.
*/
e("shr bh, byte 6\n");
e("shr al, byte 7\n");
e("and bh, 2\n");
e("or bh, al\n");
e("xor al, al\n");
e("cmp bh, 2\n");
e("jne short .no_v\n");
e("mov al, 1\n"); /* move 1 into V flag */
EmitLabel(".no_v");
t = 6;
}
else /* memory */
{
LoadFromEA(ea, BYTE_SIZE, 0);
e("xor al, al\n");
e("shr byte [x], 1\n"); /* X->CF */
e("sbb al, dl\n");
e("mov dh, al\n"); /* DH now has unadjusted result */
e("mov dl, ah\n");
e("das\n"); /* BCD! */
e("lahf\n");
e("setc byte [x]\n");
e("jnz short .clr\n");
e("and dl, byte 0x40\n");
e("and ah, byte 0x3f\n");
e("or ah, dl\n");
EmitLabel(".clr");
e("test al, al\n"); /* N flag */
e("sets dl\n");
e("ror dl, 1\n"); /* into MSB of DL */
e("or ah, dl\n"); /* -> N flag */
e("mov dl, al\n"); /* move result into DL to write it */
/*
* V flag calculation: Put unadjusted result's MSB into bit 1, and the
* adjusted result's MSB into bit 0. If the result is 2, the V flag
* is set.
*/
e("shr dh, byte 6\n");
e("shr al, byte 7\n");
e("and dh, 2\n");
e("or dh, al\n");
e("xor al, al\n");
e("cmp dh, 2\n");
e("jne short .no_v\n");
e("mov al, 1\n"); /* move 1 into V flag */
EmitLabel(".no_v");
/* WriteXXX will clear EDI for us */
WriteByte();
t = 8 + TimingEA(ea, BYTE_SIZE);
}
EmitTiming(t);
InstEnd();
return 1;
}
int CHK(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned reg = (op >> 9) & 7, size, ea = op & 0x3f;
/*
* Notes About Undefined Flags:
* ----------------------------
*
* Z flag = Set if register operand is 0 (second operand), cleared
* otherwise
* V flag = Always cleared (?)
* C flag = Always cleared (?)
*/
if (!CheckEA(ea, "101111111111")) return 0;
if (!NumDecodedEA(ea)) return 0;
switch ((op >> 7) & 3)
{
case 3: size = WORD_SIZE; break;
default: return 0;
}
InstBegin(op, mnem, NumDecodedEA(ea));
/*
* We have to emit the EA timing first because it must be accounted for
* whether or not a trap occurs. DIVU and DIVS work like this too.
*/
if (TimingEA(ea, size))
e("sub ecx, byte %d\n", TimingEA(ea, size));
LoadFromEA(ea, size, 0);
e("xor al, al\n"); /* V should be cleared */
e("test word [d+%d*4], 0xffff\n", reg); /* If Dn == 0 Then Z=1 */
e("setz ah\n"); /* this also clears N and C */
e("shl ah, 6\n"); /* put Z in proper position */
e("test byte [d+%d*4+1], 0x80\n", reg); /* If Dn < 0 Then TRAP */
e("jnz near .trap_dn\n");
e("cmp [d+%d*4], dx\n", reg); /* If Dn > Source Then TRAP */
e("jg near .trap_ea\n");
EmitTiming(base_timing);
InstEnd();
EmitLabel(".trap_dn"); /* set N to 1 because Dn<0 test failed */
e("or ah, 0x80\n");
e("jmp short .trap\n");
EmitLabel(".trap_ea"); /* clear N because Dn>Source test failed */
e("and ah, 0x7f\n");
EmitLabel(".trap");
e("jmp near exception_chk\n");
return 1;
}
int MOVEP(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned dx = (op >> 9) & 7, opmode = (op >> 6) & 7, t;
if (opmode != 4 && opmode != 5 && opmode != 6 && opmode != 7) return 0;
InstBegin(op, mnem, 8);
e("and edi, byte 7\n");
e("movsx ebx, word [esi]\n"); /* (d16,An) */
e("add ebx, [a+edi*4]\n");
e("add esi, byte 2\n");
switch (opmode & 2)
{
case 2: /* reg->mem */
if (opmode & 1) /* long */
{
t = 24;
e("push eax\n"); /* we will be using EAX to save EBX */
e("mov edx, [d+%d*4]\n", dx);
e("mov eax, ebx\n"); /* save EBX */
e("rol edx, byte 8\n");/* write high order */
WriteByte();
e("add eax, byte 2\n");
e("rol edx, byte 8\n");/* write mid-upper */
e("mov ebx, eax\n");
WriteByte();
e("add eax, byte 2\n");
e("rol edx, byte 8\n");/* write mid-lower */
e("mov ebx, eax\n");
WriteByte();
e("add eax, byte 2\n");
e("rol edx, byte 8\n");/* write lower order */
e("mov ebx, eax\n");
WriteByte();
e("pop eax\n");
}
else /* word */
{
t = 16;
e("push eax\n"); /* we will be using EAX to save EBX */
e("mov dx, [d+%d*4]\n", dx);
e("mov eax, ebx\n"); /* save EBX */
e("ror edx, byte 8\n");/* write high order */
WriteByte();
e("add eax, byte 2\n");
e("rol edx, byte 8\n");/* write low order */
e("mov ebx, eax\n");
WriteByte();
e("pop eax\n");
}
break;
case 0: /* mem->reg */
if (opmode & 1) /* long */
{
t = 24;
ReadByte(); /* read high order */
e("mov [d+%d*4+3], dl\n", dx); /* store high order in Dn */
e("add ebx, byte 2\n");
ReadByte(); /* mid-upper order */
e("mov [d+%d*4+2], dl\n", dx); /* store mid-upper order */
e("add ebx, byte 2\n");
ReadByte(); /* mid-lower order */
e("mov [d+%d*4+1], dl\n", dx); /* store mid-lower order */
e("add ebx, byte 2\n");
ReadByte(); /* low order */
e("mov [d+%d*4+0], dl\n", dx); /* store low order */
}
else /* word */
{
t = 16;
ReadByte(); /* read high order */
e("mov [d+%d*4+1], dl\n", dx); /* store high order in Dn */
e("add ebx, byte 2\n");
ReadByte(); /* read low order */
e("mov [d+%d*4+0], dl\n", dx); /* store low order */
}
break;
}
EmitTiming(t);
InstEnd();
return 1;
}
int CMPM(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned size, ax = (op >> 9) & 7;
switch ((op >> 6) & 3)
{
case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0;
}
InstBegin(op, mnem, 8);
e("and edi, byte 7\n");
e("mov ebx, [a+edi*4]\n"); /* get Ay */
if (size == BYTE_SIZE) /* Ay */
{
e("cmp edi, byte 7\n"); /* A7 bytes */
e("cmc\n"); /* switch carry */
e("adc dword [a+edi*4], byte 1\n");
}
else if (size == WORD_SIZE)
e("add dword [a+edi*4], byte 2\n");
else if (size == LONG_SIZE)
e("add dword [a+edi*4], byte 4\n");
switch (size)
{ case BYTE_SIZE: ReadByte(); break;
case WORD_SIZE: ReadWord(); break;
case LONG_SIZE: ReadLong(); break; }
SaveReg("run", "edx"); /* save Ay */
e("mov ebx, [a+%d*4]\n", ax);/* get Ax */
if (size == BYTE_SIZE) /* Ax */
{
if (ax == 7)
e("add dword [a+7*4], byte 2\n");
else
e("inc dword [a+%d*4]\n", ax);
}
else if (size == WORD_SIZE)
e("add dword [a+%d*4], byte 2\n", ax);
else /* LONG */
e("add dword [a+%d*4], byte 4\n", ax);
switch (size)
{ case BYTE_SIZE: ReadByte(); break;
case WORD_SIZE: ReadWord(); break;
case LONG_SIZE: ReadLong(); break; }
switch (size) /* compare */
{
case BYTE_SIZE:
RestoreRegTo("run", "edx", "bl"); /* [run_edx]->bl */
e("cmp dl, bl\n");
break;
case WORD_SIZE:
RestoreRegTo("run", "edx", "ebx"); /* [run_edx]->ebx */
e("cmp dx, bx\n");
break;
case LONG_SIZE:
RestoreRegTo("run", "edx", "ebx"); /* [run_edx]->ebx */
e("cmp edx, ebx\n");
break;
}
e("lahf\n");
e("seto al\n");
if (size != LONG_SIZE) /* byte, word */
EmitTiming(12);
else /* long */
EmitTiming(20);
InstEnd();
return 1;
}
int TRAP(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 16);
e("and edi, byte 0xf\n"); /* lower 4 bits=vector */
SaveReg("run", "edi");
e("mov edx, 0x2000\n"); /* what the SR will be */
e("xor edx, [sr]\n"); /* see if S bit is different */
e("test edx, 0x2000\n");
e("jz .no_sp_magic\n"); /* nope, don't swap SPs */
e("mov edx, [__sp]\n");
e("xchg [a+7*4], edx\n");
e("mov [__sp], edx\n");
SetSupervisorAddressSpace();
EmitLabel(".no_sp_magic");
e("mov ebx, [a+7*4]\n"); /* SP */
/*
* If 68010, push format info
*/
if (mpu == 68010)
{
e("sub ebx, byte 2\n");/* SP-2 */
RestoreReg("run", "edi"); /* vector */
e("mov edx, edi\n");
SaveReg("run", "edi");
e("add edx, byte 32\n");
e("shl edx, byte 2\n"); /* TRAP vector | 0x00000000 */
SaveReg("run", "ebx");
WriteWord();
RestoreReg("run", "ebx");
}
e("mov edx, esi\n");
e("sub edx, ebp\n"); /* PC->EDX */
e("sub ebx, byte 4\n"); /* SP-4 */
e("push ebx\n");
WriteLong();
e("pop ebx\n");
e("mov edx, [sr]\n"); /* get SR */
e("sub ebx, byte 2\n"); /* SP-2 */
e("push ebx\n");
SaveReg("run", "ebx");
WriteWord(); /* save SR to stack */
e("pop ebx\n");
e("or dh, 0x20\n"); /* set supervisor for exception */
e("and edx, 0xa71f\n"); /* clear unwanted bits */
e("xchg [sr], edx\n"); /* set new SR, get old SR->EDX */
e("mov [a+7*4], ebx\n"); /* write back SP */
RestoreReg("run", "edi");
e("mov ebx, edi\n");
e("add ebx, byte 32\n"); /* vector+32=trap vector */
e("shl ebx, 2\n");
if (mpu == 68010)
e("add ebx, [vbr]\n");
ReadLong(); /* get PC */
e("mov esi, edx\n"); /* set new PC */
UpdateFetchPtr();
e("xor edi, edi\n");
EmitTiming(base_timing);
InstEnd();
return 1;
}
int EORItoSR(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
e("test byte [sr+1], 0x20\n"); /* in supervisor? */
e("jz near exception_privilege_violation\n");
SaveCCR(); /* save flags to SR */
e("mov edx, [esi]\n");
e("and edx, 0xa71f\n"); /* mask out unwanted bits */
e("xor [sr], dx\n");
e("add esi, byte 2\n");
LoadCCR(); /* get flags back */
e("test byte [sr+1], 0x20\n"); /* if we changed to User, swap SPs */
e("jnz short .in_s\n");
e("mov ebx, [a+7*4]\n");
e("mov edx, [__sp]\n");
e("mov [a+7*4], edx\n");
e("mov [__sp], ebx\n");
SetUserAddressSpace(); /* map in user address space */
EmitLabel(".in_s");
EmitTiming(base_timing);
InstEnd();
return 1;
}
int EORItoCCR(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
SaveCCR();
e("mov dl, [esi]\n");
e("and dl, 0x1f\n");
e("xor [sr], dl\n");
LoadCCR();
e("add esi, byte 2\n");
EmitTiming(base_timing);
InstEnd();
return 1;
}
int RTR(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
e("mov ebx, [a+7*4]\n"); /* (SP)->CCR */
ReadWord();
e("and dl, 0x1f\n");
e("mov [sr], dl\n");
e("add dword [a+7*4], byte 2\n"); /* SP+2->SP */
LoadCCR();
e("mov ebx, [a+7*4]\n"); /* (SP)->PC */
ReadLong();
e("add dword [a+7*4], byte 4\n"); /* SP+4->SP */
e("mov esi, edx\n");
UpdateFetchPtr();
EmitTiming(base_timing);
InstEnd();
return 1;
}
int SUBX(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1, size;
switch ((op >> 6) & 3)
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
InstBegin(op, mnem, 8);
e("and edi, byte 7\n");
if (!rm) /* Dy,Dx */
{
if (size == BYTE_SIZE) /* EBX,BX,BL=destination */
e("mov bl, [d+%d*4]\n", rx);
else
e("mov ebx, [d+%d*4]\n", rx);
if (size == BYTE_SIZE) /* EDX,DX,DL=source */
e("mov dl, [d+edi*4]\n");
else
e("mov edx, [d+edi*4]\n");
e("shr byte [x], 1\n"); /* X->CF */
switch (size)
{ case BYTE_SIZE: e("sbb bl, dl\n"); break;
case WORD_SIZE: e("sbb bx, dx\n"); break;
case LONG_SIZE: e("sbb ebx, edx\n"); break;
}
e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */
e("lahf\n");
e("setc byte [x]\n");
e("seto al\n");
e("jnz short .z\n"); /* if non-zero, cleared */
e("and dl, 0x40\n"); /* otherwise, unchanged */
e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */
e("or ah, dl\n"); /* OR in the old, unchanged Z flag */
EmitLabel(".z");
switch (size) /* store results */
{ case BYTE_SIZE: e("mov [d+%d*4], bl\n", rx); break;
case WORD_SIZE: e("mov [d+%d*4], bx\n", rx); break;
case LONG_SIZE: e("mov [d+%d*4], ebx\n", rx); break;
}
}
else /* -(Ay),-(Ax) */
{
if (size == BYTE_SIZE)
{
e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */
e("cmc\n");
e("sbb dword [a+edi*4], byte 1\n");
if (rx != 7)
e("dec dword [a+%d*4]\n", rx);
else
e("sub dword [a+7*4], byte 2\n");
}
else if (size == WORD_SIZE)
{
e("sub dword [a+edi*4], byte 2\n");
e("sub dword [a+%d*4], byte 2\n", rx);
}
else if (size == LONG_SIZE)
{
e("sub dword [a+edi*4], byte 4\n");
e("sub dword [a+%d*4], byte 4\n", rx);
}
e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */
switch (size)
{ case BYTE_SIZE: ReadByte(); break;
case WORD_SIZE: ReadWord(); break;
case LONG_SIZE: ReadLong(); break;
}
SaveReg("run", "edx"); /* save data obtained */
e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */
switch (size)
{ case BYTE_SIZE: ReadByte(); break;
case WORD_SIZE: ReadWord(); break;
case LONG_SIZE: ReadLong(); break;
}
SaveReg("run", "ebx");
e("mov ebx, edx\n"); /* EBX,BX,BL=destination */
RestoreReg("run", "edx"); /* EDX,DX,DL=source */
e("shr byte [x], 1\n"); /* X->CF */
switch (size)
{ case BYTE_SIZE: e("sbb bl, dl\n"); break;
case WORD_SIZE: e("sbb bx, dx\n"); break;
case LONG_SIZE: e("sbb ebx, edx\n"); break;
}
e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */
e("lahf\n");
e("setc byte [x]\n");
e("seto al\n");
e("jnz short .z\n"); /* if non-zero, cleared */
e("and dl, 0x40\n"); /* otherwise, unchanged */
e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */
e("or ah, dl\n"); /* OR in the old, unchanged Z flag */
EmitLabel(".z");
e("mov edx, ebx\n"); /* store result */
RestoreReg("run", "ebx");
switch (size)
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break;
}
}
if (!rm)
{
if (size == LONG_SIZE)
EmitTiming(8);
else
EmitTiming(4);
}
else
{
if (size == LONG_SIZE)
EmitTiming(30);
else
EmitTiming(18);
}
InstEnd();
return 1;
}
int ADDX(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1, size;
switch ((op >> 6) & 3)
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
InstBegin(op, mnem, 8);
e("and edi, byte 7\n");
if (!rm) /* Dy,Dx */
{
if (size == BYTE_SIZE) /* EBX,BX,BL=destination */
e("mov bl, [d+%d*4]\n", rx);
else
e("mov ebx, [d+%d*4]\n", rx);
if (size == BYTE_SIZE) /* EDX,DX,DL=source */
e("mov dl, [d+edi*4]\n");
else
e("mov edx, [d+edi*4]\n");
e("shr byte [x], 1\n"); /* X->CF */
switch (size)
{ case BYTE_SIZE: e("adc bl, dl\n"); break;
case WORD_SIZE: e("adc bx, dx\n"); break;
case LONG_SIZE: e("adc ebx, edx\n"); break;
}
e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */
e("lahf\n");
e("setc byte [x]\n");
e("seto al\n");
e("jnz short .z\n"); /* if non-zero, cleared */
e("and dl, 0x40\n"); /* otherwise, unchanged */
e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */
e("or ah, dl\n"); /* OR in the old, unchanged Z flag */
EmitLabel(".z");
switch (size) /* store results */
{ case BYTE_SIZE: e("mov [d+%d*4], bl\n", rx); break;
case WORD_SIZE: e("mov [d+%d*4], bx\n", rx); break;
case LONG_SIZE: e("mov [d+%d*4], ebx\n", rx); break;
}
}
else /* -(Ay),-(Ax) */
{
if (size == BYTE_SIZE)
{
e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */
e("cmc\n");
e("sbb dword [a+edi*4], byte 1\n");
if (rx != 7)
e("dec dword [a+%d*4]\n", rx);
else
e("sub dword [a+7*4], byte 2\n");
}
else if (size == WORD_SIZE)
{
e("sub dword [a+edi*4], byte 2\n");
e("sub dword [a+%d*4], byte 2\n", rx);
}
else if (size == LONG_SIZE)
{
e("sub dword [a+edi*4], byte 4\n");
e("sub dword [a+%d*4], byte 4\n", rx);
}
e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */
switch (size)
{ case BYTE_SIZE: ReadByte(); break;
case WORD_SIZE: ReadWord(); break;
case LONG_SIZE: ReadLong(); break;
}
SaveReg("run", "edx"); /* save data obtained */
e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */
switch (size)
{ case BYTE_SIZE: ReadByte(); break;
case WORD_SIZE: ReadWord(); break;
case LONG_SIZE: ReadLong(); break;
}
SaveReg("run", "ebx");
e("mov ebx, edx\n"); /* EBX,BX,BL=destination */
RestoreReg("run", "edx"); /* EDX,DX,DL=source */
e("shr byte [x], 1\n"); /* X->CF */
switch (size)
{ case BYTE_SIZE: e("adc bl, dl\n"); break;
case WORD_SIZE: e("adc bx, dx\n"); break;
case LONG_SIZE: e("adc ebx, edx\n"); break;
}
e("mov dl, ah\n"); /* keep temporary copy of old flags in DL */
e("lahf\n");
e("setc byte [x]\n");
e("seto al\n");
e("jnz short .z\n"); /* if non-zero, cleared */
e("and dl, 0x40\n"); /* otherwise, unchanged */
e("and ah, 0xbf\n"); /* (get rid of new unwanted Z) */
e("or ah, dl\n"); /* OR in the old, unchanged Z flag */
EmitLabel(".z");
e("mov edx, ebx\n"); /* store result */
RestoreReg("run", "ebx");
switch (size)
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break;
}
}
if (!rm)
{
if (size == LONG_SIZE)
EmitTiming(8);
else
EmitTiming(4);
}
else
{
if (size == LONG_SIZE)
EmitTiming(30);
else
EmitTiming(18);
}
InstEnd();
return 1;
}
int SBCD(unsigned short op, char *mnem, unsigned base_timing)
{
/*
* Notes About Undefined Flags:
* ----------------------------
*
* N flag = Set the same as the MSB of the result
* V flag = If MSB changes from 1 to 0 when result is adjusted for BCD,
* the V flag is set, otherwise it is cleared.
*/
unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1;
InstBegin(op, mnem, 8);
e("and edi, byte 7\n");
if (!rm) /* Dy,Dx */
{
e("mov al, [d+%d*4]\n", rx); /* AL=destination */
e("mov bl, [d+edi*4]\n"); /* BL=source */
e("shr byte [x], 1\n"); /* X->CF */
e("sbb al, bl\n"); /* subtract */
e("mov bh, al\n"); /* save the unadjusted result in BH */
e("das\n"); /* adjust the packed BCD result */
e("setc dl\n");
e("setc byte [x]\n");
e("jz short .unchanged_z\n");
e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */
EmitLabel(".unchanged_z");
e("and ah, 0x7e\n");
e("or ah, dl\n"); /* set C */
e("test al, al\n"); /* N flag */
e("sets dl\n");
e("ror dl, 1\n"); /* N flag -> MSB of DL */
e("or ah, dl\n"); /* into N flag */
e("mov [d+%d*4], al\n", rx); /* store result */
/*
* V flag calculation: Put unadjusted result's MSB into bit 1, and the
* adjusted result's MSB into bit 0. If the result is 2, the V flag
* is set.
*/
e("shr bh, byte 6\n");
e("shr al, byte 7\n");
e("and bh, 2\n");
e("or bh, al\n");
e("cmp bh, 2\n");
e("sete al\n"); /* 1 -> V */
}
else /* -(Ay),-(Ax) */
{
e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */
e("cmc\n");
e("sbb dword [a+edi*4], byte 1\n");
if (rx != 7)
e("dec dword [a+%d*4]\n", rx);
else
e("sub dword [a+7*4], byte 2\n");
e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */
ReadByte();
SaveReg("run", "dl"); /* save byte obtained */
e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */
ReadByte();
e("mov al, dl\n"); /* AL=destination */
RestoreReg("run", "dl"); /* DL=source */
e("shr byte [x], 1\n"); /* X->CF */
e("sbb al, dl\n"); /* subtract */
e("mov dh, al\n"); /* save unadjusted result in DH */
e("das\n"); /* adjust the packed BCD result */
e("setc dl\n");
e("setc byte [x]\n");
e("jz short .unchanged_z\n");
e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */
EmitLabel(".unchanged_z");
e("and ah, 0x7e\n");
e("or ah, dl\n"); /* set C */
e("test al, al\n"); /* N flag */
e("sets dl\n");
e("ror dl, 1\n"); /* N flag -> MSB of DL */
e("or ah, dl\n"); /* into N flag */
e("mov dl, al\n"); /* prepare to write result */
/*
* V flag calculation: Put unadjusted result's MSB into bit 1, and the
* adjusted result's MSB into bit 0. If the result is 2, the V flag
* is set.
*/
e("shr dh, byte 6\n");
e("shr al, byte 7\n");
e("and dh, 2\n");
e("or dh, al\n");
e("cmp dh, 2\n");
e("sete al\n"); /* 1->V */
WriteByte();
}
if (!rm)
EmitTiming(6);
else
EmitTiming(18);
InstEnd();
return 1;
}
int ABCD(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned rx = (op >> 9) & 7, rm = (op >> 3) & 1;
/*
* Notes About Undefined Flags:
* ----------------------------
*
* N flag = Set the same as the MSB of the result
* V flag = If MSB changes from 0 to 1 when result is adjusted for BCD,
* the V flag is set, otherwise it is cleared.
*/
InstBegin(op, mnem, 8);
e("and edi, byte 7\n");
if (!rm) /* Dy,Dx */
{
e("mov al, [d+%d*4]\n", rx); /* AL=destination */
e("mov bl, [d+edi*4]\n"); /* BL=source */
e("shr byte [x], 1\n"); /* X->CF */
e("adc al, bl\n"); /* add with extend */
e("mov bh, al\n"); /* save unadjusted result in BH */
e("daa\n"); /* adjust the packed BCD result */
e("setc dl\n");
e("setc byte [x]\n");
e("jz short .unchanged_z\n");
e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */
EmitLabel(".unchanged_z");
e("and ah, 0x7e\n");
e("or ah, dl\n"); /* set C */
e("test al, al\n"); /* N flag */
e("sets dl\n");
e("ror dl, 1\n"); /* N flag -> MSB of DL */
e("or ah, dl\n"); /* into N flag */
e("mov [d+%d*4], al\n", rx); /* store result */
/*
* V flag calculation: Put unadjusted result's MSB into bit 1, and the
* adjusted result's MSB into bit 0. If the result is 01, the V flag
* is set.
*/
e("shr bh, byte 6\n");
e("shr al, byte 7\n");
e("and bh, 2\n");
e("or bh, al\n");
e("cmp bh, 1\n");
e("sete al\n"); /* move 1 into V flag */
}
else /* -(Ay),-(Ax) */
{
e("cmp edi, byte 7\n"); /* decrement Ay register (-2 for A7) */
e("cmc\n");
e("sbb dword [a+edi*4], byte 1\n");
if (rx != 7)
e("dec dword [a+%d*4]\n", rx);
else
e("sub dword [a+7*4], byte 2\n");
e("mov ebx, [a+edi*4]\n"); /* first, load up -(Ay) */
ReadByte();
SaveReg("run", "dl"); /* save byte obtained */
e("mov ebx, [a+%d*4]\n", rx);/* next, load up -(Ax) */
ReadByte();
e("mov al, dl\n"); /* AL=destination */
RestoreReg("run", "dl"); /* DL=source */
e("shr byte [x], 1\n"); /* X->CF */
e("adc al, dl\n"); /* add */
e("mov dh, al\n"); /* save unadjusted result in DH */
e("daa\n"); /* adjust the packed BCD result */
e("setc dl\n");
e("setc byte [x]\n");
e("jz short .unchanged_z\n");
e("and ah, 0xbf\n"); /* cleared if nonzero, otherwise unchanged */
EmitLabel(".unchanged_z");
e("and ah, 0x7e\n");
e("or ah, dl\n"); /* set C */
e("test al, al\n"); /* N flag */
e("sets dl\n");
e("ror dl, 1\n"); /* N flag -> MSB of DL */
e("or ah, dl\n"); /* into N flag */
e("mov dl, al\n"); /* store result */
/*
* V flag calculation: Put unadjusted result's MSB into bit 1, and the
* adjusted result's MSB into bit 0. If the result is 1, the V flag
* is set.
*/
e("shr dh, byte 6\n");
e("shr al, byte 7\n");
e("and dh, 2\n");
e("or dh, al\n");
e("cmp dh, 1\n");
e("sete al\n"); /* 1 -> V */
WriteByte();
}
if (!rm)
EmitTiming(6);
else
EmitTiming(18);
InstEnd();
return 1;
}
int ORItoCCR(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
SaveCCR();
e("mov dl, [esi]\n"); /* get data */
e("and dl, 0x1f\n"); /* mask out unwanted bits */
e("or [sr], dl\n");
e("add esi, byte 2\n");
LoadCCR();
EmitTiming(base_timing);
InstEnd();
e("db 66, 65, 82, 84, 33, 1\n");
return 1;
}
int Scc(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned ea = op & 0x3f, cond = (op >> 8) & 0xf;
char mnem[4];
char *mnem_t[] = { "ST", "SF", "SHI", "SLS",
"SCC", "SCS", "SNE", "SEQ",
"SVC", "SVS", "SPL", "SMI",
"SGE", "SLT", "SGT", "SLE" };
if (!CheckEA(ea, "101111111000")) return 0;
if (!NumDecodedEA(ea)) return 0;
strcpy(mnem, mnem_t[cond]);
InstBegin(op, mnem, NumDecodedEA(ea));
switch (cond)
{
/* EAX:0000000000000000 NZ00 000C 0000 000V */
case 0: /* T: Always true */
if ((ea >> 3) == 0) /* Dn */
{
e("and edi, byte 7\n");
e("mov byte [d+edi*4], 0xff\n");
EmitTiming(6); /* reg: byte, true */
}
else
{
if (mpu == 68000 && dummyread && (ea >> 3))
{
/*
* If the processor type is 68000, and the EA mode is not
* register, we can emulate dummy reads!
*/
LoadFromEA(ea, BYTE_SIZE, 0);
e("mov dl, 0xff\n");
WriteByte();
}
else
{
e("mov dl, 0xff\n");
StoreToEAUsingEDI(ea, BYTE_SIZE, 0);
}
EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, true */
}
InstEnd();
return 1;
break;
case 1: /* F: Always false */
if ((ea >> 3) == 0) /* Dn */
{
e("and edi, byte 7\n");
e("mov byte [d+edi*4], 0\n");
EmitTiming(4); /* reg: byte, false */
}
else
{
if (mpu == 68000 && dummyread && (ea >> 3))
{
/*
* If the processor type is 68000, and the EA mode is not
* register, we can emulate dummy reads!
*/
LoadFromEA(ea, BYTE_SIZE, 0);
e("xor dl, dl\n");
WriteByte();
}
else
{
e("xor dl, dl\n");
StoreToEAUsingEDI(ea, BYTE_SIZE, 0);
}
EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, false */
}
InstEnd();
return 1;
break;
case 2: /* HI: !C & !Z */
e("test ah, byte 0x41\n");
e("jz short do%d\n", op);
break;
case 3: /* LS: C | Z */
e("test ah, byte 0x41\n");
e("jnz short do%d\n", op);
break;
case 4: /* CC: !C */
e("test ah, byte 1\n");
e("jz short do%d\n", op);
break;
case 5: /* CS: C */
e("test ah, byte 1\n");
e("jnz short do%d\n", op);
break;
case 6: /* NE: !Z */
e("test ah, byte 0x40\n");
e("jz short do%d\n", op);
break;
case 7: /* EQ: Z */
e("test ah, byte 0x40\n");
e("jnz short do%d\n", op);
break;
case 8: /* VC: !V */
e("test al, byte 1\n");
e("jz short do%d\n", op);
break;
case 9: /* VS: V */
e("test al, byte 1\n");
e("jnz short do%d\n", op);
break;
case 0xa: /* PL: !N */
e("test ah, byte 0x80\n");
e("jz short do%d\n", op);
break;
case 0xb: /* MI: N */
e("test ah, byte 0x80\n");
e("jnz short do%d\n", op);
break;
case 0xc: /* GE: N & V | !N & !V (N&V || !N&!V) */
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpe short do%d\n", op);
break;
case 0xd: /* LT: N & !V | !N & V */
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpo short do%d\n", op);
EmitLabel(".dont_do");
break;
case 0xe: /* GT: N & V & !Z | !N & !V & !Z */
/* if Z, then it is GE and we don't take this branch */
e("test ah, byte 0x40\n");
e("jnz short .dont_do\n");
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpe short do%d\n", op);
EmitLabel(".dont_do");
break;
case 0xf: /* LE: Z | N & !V | !N & V */
e("test ah, 0x40\n");
e("jnz short do%d\n", op);
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpo short do%d\n", op);
break;
}
if ((ea >> 3) == 0) /* Dn */
{
e("and edi, byte 7\n");
e("mov byte [d+edi*4], 0\n");
EmitTiming(4); /* reg: byte, false */
}
else
{
e("false%d:\n", op); /* this prevents redefinitions of labels in
the EA code */
if (mpu == 68000 && dummyread && (ea >> 3))
{
/*
* If the processor type is 68000, and the EA mode is not
* register, we can emulate dummy reads!
*/
LoadFromEA(ea, BYTE_SIZE, 0);
e("xor dl, dl\n");
WriteByte();
}
else
{
e("xor dl, dl\n");
StoreToEAUsingEDI(ea, BYTE_SIZE, 0);
}
EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, false */
}
InstEnd();
Align(4);
e("do%d:\n", op);
if ((ea >> 3) == 0) /* Dn */
{
e("and edi, byte 7\n");
e("mov byte [d+edi*4], 0xff\n");
EmitTiming(6); /* reg: byte, true */
}
else
{
e("true%d:\n", op); /* this prevents redefinitions of labels in
the EA code */
if (mpu == 68000 && dummyread && (ea >> 3))
{
/*
* If the processor type is 68000, and the EA mode is not
* register, we can emulate dummy reads!
*/
LoadFromEA(ea, BYTE_SIZE, 0);
e("mov dl, 0xff\n");
WriteByte();
}
else
{
e("mov dl, 0xff\n");
StoreToEAUsingEDI(ea, BYTE_SIZE, 0);
}
EmitTiming(8 + TimingEA(ea, BYTE_SIZE)); /* mem: byte, true */
}
InstEnd();
return 1;
}
int EXG(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned rx = (op >> 9) & 7, opmode = (op >> 3) & 0x1f;
if (opmode != 0x8 && opmode != 0x9 && opmode != 0x11) return 0;
InstBegin(op, mnem, 8);
e("and edi, byte 7\n");
switch (opmode)
{
case 8: /* Dx,Dy */
e("mov ebx, [d+%d*4]\n", rx);
e("mov edx, [d+edi*4]\n");
e("mov [d+%d*4], edx\n", rx);
e("mov [d+edi*4], ebx\n");
break;
case 9: /* Ax,Ay */
e("mov ebx, [a+%d*4]\n", rx);
e("mov edx, [a+edi*4]\n");
e("mov [a+%d*4], edx\n", rx);
e("mov [a+edi*4], ebx\n");
break;
case 0x11: /* Dx,Ay */
e("mov ebx, [d+%d*4]\n", rx);
e("mov edx, [a+edi*4]\n");
e("mov [d+%d*4], edx\n", rx);
e("mov [a+edi*4], ebx\n");
break;
}
EmitTiming(base_timing);
InstEnd();
return 1;
}
int MULS(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned ea = op & 0x3f, dn = (op >> 9) & 7;
if (!CheckEA(ea, "101111111111")) return 0;
if (!NumDecodedEA(ea)) return 0;
InstBegin(op, mnem, NumDecodedEA(ea));
LoadFromEA(ea, WORD_SIZE, 0);
e("mov ax, [d+%d*4]\n", dn);
e("imul dx\n"); /* ax*dx->dx:ax */
e("shl edx, byte 16\n"); /* EDX=32-bit result */
e("mov dx, ax\n");
e("mov [d+%d*4], edx\n", dn);
e("test edx, edx\n"); /* C is always cleared */
e("lahf\n");
e("xor al, al\n"); /* V=1 only occurs on 32*32 (not on 68000) */
EmitTiming(base_timing + TimingEA(ea, WORD_SIZE));
InstEnd();
return 1;
}
int MULU(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned ea = op & 0x3f, dn = (op >> 9) & 7;
if (!CheckEA(ea, "101111111111")) return 0;
if (!NumDecodedEA(ea)) return 0;
InstBegin(op, mnem, NumDecodedEA(ea));
LoadFromEA(ea, WORD_SIZE, 0);
e("mov ax, [d+%d*4]\n", dn);
e("mul dx\n"); /* ax*dx->dx:ax */
e("shl edx, byte 16\n"); /* EDX=32-bit result */
e("mov dx, ax\n");
e("mov [d+%d*4], edx\n", dn);
e("test edx, edx\n"); /* C is always cleared */
e("lahf\n");
e("xor al, al\n"); /* V=1 only occurs on 32*32 (not on 68000) */
EmitTiming(base_timing + TimingEA(ea, WORD_SIZE));
InstEnd();
return 1;
}
int DIVS(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned ea = op & 0x3f, dn = (op >> 9) & 7;
if (!CheckEA(ea, "101111111111")) return 0;
if (!NumDecodedEA(ea)) return 0;
/* See the note in DIVU() for info on how overflow is detected */
InstBegin(op, mnem, NumDecodedEA(ea));
if (TimingEA(ea, WORD_SIZE))
e("sub ecx, byte %d\n", TimingEA(ea, WORD_SIZE)); /* do 1st.. */
LoadFromEA(ea, WORD_SIZE, 0);
e("test dx, dx\n");
e("jz .divide_by_zero\n");
e("movsx ebx, dx\n"); /* divisor->EBX (sign extend to work) */
SaveReg("run", "eax");
e("mov eax, [d+%d*4]\n", dn); /* dividend */
e("cdq\n"); /* EAX->sign extend->EDX:EAX */
e("idiv ebx\n");
e("cmp eax, -32768\n");
e("jl .overflow\n");
e("cmp eax, 32767\n");
e("jg .overflow\n");
e("test ax, ax\n"); /* test for flags */
e("mov [d+(%d*4)+2], dx\n", dn); /* store back to Dn */
e("mov [d+(%d*4)+0], ax\n", dn);
e("lahf\n"); /* the mov's did not affect the flags... */
e("xor al, al\n"); /* C cleared by TEST */
EmitTiming(base_timing);
InstEnd();
EmitLabel(".overflow");
RestoreReg("run", "eax");
e("mov eax, 1\n"); /* clear C and set overflow */
EmitTiming(base_timing);
InstEnd();
EmitLabel(".divide_by_zero");
e("xor eax, eax\n"); /* clear C */
e("jmp near exception_divide_by_zero\n");
return 1;
}
int DIVU(unsigned short op, char *mnem, unsigned base_timing)
{
/*
* Overflow Detection:
*
* Overflow detection is actually pretty simple. The 68000 DIVU and DIVS
* instructions divide 32-bit operands by 16-bit operands. What I've done
* is zero extend the operands to 64-bit and 32-bit and then divide. If
* the result is larger than 0xFFFF (16 bits), the overflow condition is
* set.
*
* See the end of this function for an alternate method.
*/
unsigned ea = op & 0x3f, dn = (op >> 9) & 7;
if (!CheckEA(ea, "101111111111")) return 0;
if (!NumDecodedEA(ea)) return 0;
InstBegin(op, mnem, NumDecodedEA(ea));
LoadFromEA(ea, WORD_SIZE, 0);
if (TimingEA(ea, WORD_SIZE))
e("sub ecx, byte %d\n", TimingEA(ea, WORD_SIZE)); /* do 1st.. */
e("test dx, dx\n");
e("jz .divide_by_zero\n");
e("mov ebx, edx\n");
e("and ebx, 0xffff\n"); /* divisor->EBX */
SaveReg("run", "eax");
e("mov eax, [d+%d*4]\n", dn); /* dividend */
e("xor edx, edx\n"); /* EDX:EAX=dividend */
e("div ebx\n");
e("cmp eax, 0xffff\n");
e("jg .overflow\n");
e("test ax, ax\n"); /* test for flags */
e("mov [d+(%d*4)+2], dx\n", dn); /* store back to Dn */
e("mov [d+(%d*4)+0], ax\n", dn);
e("lahf\n"); /* the mov's did not affect the flags... */
e("xor al, al\n"); /* C cleared by TEST */
EmitTiming(base_timing);
InstEnd();
EmitLabel(".overflow");
RestoreReg("run", "eax");
e("mov eax, 1\n"); /* clear C and set overflow */
EmitTiming(base_timing);
InstEnd();
EmitLabel(".divide_by_zero");
e("xor eax, eax\n"); /* clear C */
e("jmp near exception_divide_by_zero\n");
return 1;
/*
*
* An alternate method to detecting overflows is shown below. It doesn't
* seem to be as acurrate as the method that is actually used, but perhaps
* I implemented it wrong or misunderstood it. It broke Comix Zone, Hard
* Drivin', and Race Drivin' (well, caused graphical glitches actually.)
*
* Kuwanger in #programmers (DALnet, of course) came up with a cool idea
* on how to detect overflow before actually dividing. He used an example
* of AH:AL/BL ... in the emulator, it is DX:AX/BX. The dividend, DX:AX
* is stored entirely in EDX, and it is shifted right by the bit number of
* the highest 1 bit in BX, the divisor. If the high 16-bits of EDX (which
* would be DX in DX:AX) are not clear, then an overflow would occur.
*
* Here's Kuwanger's classic lecture:
*
* <Kuwanger> Maybe I should use a smaller example.
* <Kuwanger> Lets use, ah:al/bl
* ...
* <Kuwanger> trzy: You could just convert one to a bit value.
* <Kuwanger> trzy: Ie, hb(bl)..
* <Kuwanger> And then shift the other one ah:al
* ...
* <Kuwanger> Though hb(bl) would have to return 0, that time.
* <Kuwanger> then ah:all>>hb(bl)
* <Kuwanger> err, ah:al, rather
* <Kuwanger> Ie, you shift by the most prominent bit.
* <Kuwanger> And if ah is non-zero..
* <Kuwanger> Well, then the number won't fit.
* ...
* <Kuwanger> Lets try an extreme case.
* <Kuwanger> 65535/255
* <Kuwanger> 65535>>7
* ...
* <Kuwanger> That obviously doesn't fit. :)
*
* Hip-hip-HURRAY!
*/
}
int NOT(unsigned short op, char *mnem, unsigned base_timing)
{
int size;
unsigned ea = op & 0x3f, t;
if (!CheckEA(ea, "101111111000")) return 0;
if (!NumDecodedEA(ea)) return 0;
switch ((op >> 6) & 3)
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
InstBegin(op, mnem, NumDecodedEA(ea));
if ((ea >> 3) == 0) /* Dn */
{
e("and edi, byte 7\n");
if (size == BYTE_SIZE)
e("mov dl, [d+edi*4]\n");
else
e("mov edx, [d+edi*4]\n");
switch (size)
{ case BYTE_SIZE: e("not dl\n"); e("test dl, dl\n"); break;
case WORD_SIZE: e("not dx\n"); e("test dx, dx\n"); break;
case LONG_SIZE: e("not edx\n"); e("test edx, edx\n"); break;
}
e("lahf\n"); /* C cleared */
e("xor al, al\n"); /* V cleared */
switch (size)
{ case BYTE_SIZE: e("mov [d+edi*4], dl\n"); break;
case WORD_SIZE: e("mov [d+edi*4], dx\n"); break;
case LONG_SIZE: e("mov [d+edi*4], edx\n"); break;
}
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE:
t = 4; break;
case LONG_SIZE:
t = 6; break; }
}
else /* mem */
{
LoadFromEA(ea, size, 0);
switch (size)
{ case BYTE_SIZE: e("not dl\n"); e("test dl, dl\n"); break;
case WORD_SIZE: e("not dx\n"); e("test dx, dx\n"); break;
case LONG_SIZE: e("not edx\n"); e("test edx, edx\n"); break;
}
e("lahf\n"); /* C cleared */
e("xor al, al\n"); /* V cleared */
switch (size)
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break;
}
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE:
t = 8; break;
case LONG_SIZE:
t = 12; break; }
t += TimingEA(ea, size);
}
EmitTiming(t);
InstEnd();
return 1;
}
int RTE(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
e("test byte [sr+1], 0x20\n"); /* must be in supervisor mode */
e("jz near exception_privilege_violation\n");
e("mov ebx, [a+7*4]\n");
SaveReg("run", "ebx");
ReadWord(); /* get SR */
RestoreReg("run", "ebx");
e("and edx, 0xa71f\n");
e("mov [sr], edx\n");
LoadCCR();
e("add ebx, byte 2\n");
SaveReg("run", "ebx");
ReadLong(); /* get PC */
RestoreReg("run", "ebx");
e("add ebx, byte 4\n");
/*
* If 68010, we have to fetch the format word and check it. If invalid,
* we branch to stackframe_error. The PC points at the word AFTER the
* faulty RTE.
*/
if (mpu == 68010)
{
SaveReg("run", "edx"); /* EDX has the PC! */
SaveReg("run", "ebx");
ReadWord();
RestoreReg("run", "ebx");
e("add ebx, byte 2\n");
e("test dh, 0xf0\n"); /* format should only be 0x0000 */
e("jnz near stackframe_error\n");
RestoreReg("run", "edx");
}
e("mov [a+7*4], ebx\n");
e("mov esi, edx\n");
UpdateFetchPtr();
e("test byte [sr+1], 0x20\n"); /* if no longer in supervisor,
SP->SSP, USP->SP */
e("jnz short .in_s\n");
e("mov ebx, [a+7*4]\n");
e("mov edx, [__sp]\n");
e("mov [a+7*4], edx\n");
e("mov [__sp], ebx\n");
SetUserAddressSpace(); /* map in user address space */
EmitLabel(".in_s");
EmitTiming(base_timing);
InstEnd();
return 1;
}
int ANDItoCCR(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
SaveCCR();
e("mov dl, [esi]\n"); /* get data */
e("and dl, 0x1f\n"); /* mask out unwanted bits */
e("and [sr], dl\n");
e("add esi, byte 2\n");
LoadCCR();
EmitTiming(base_timing);
InstEnd();
return 1;
}
int CMPA(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned opmode = (op >> 6) & 7, t;
if (!CheckEA(op & 0x3f, "111111111111")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
if (opmode != 3 && opmode != 7) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
if (opmode == 3) LoadFromEA(op & 0x3f, WORD_SIZE, 1);
else LoadFromEA(op & 0x3f, LONG_SIZE, 0);
e("cmp [a+%d*4], edx\n", (op >> 9) & 7);
e("lahf\n");
e("seto al\n");
if (opmode == 3) /* word size */
t = 6 + TimingEA(op & 0x3f, WORD_SIZE);
else /* long */
t = 6 + TimingEA(op & 0x3f, LONG_SIZE);
EmitTiming(t);
InstEnd();
return 1;
}
int PEA(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned t;
if (!CheckEA(op & 0x3f, "001001111011")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
LoadControlEA(op & 0x3f);
e("sub dword [a+7*4], byte 4\n"); /* SP-4->SP */
e("mov edx, ebx\n");
e("mov ebx, [a+7*4]\n");
WriteLong(); /* <ea>->(SP) */
switch ((op >> 3) & 7) /* timing based on control addressing mode */
{ case 2: t = 12; break; /* (An) */
case 5: t = 16; break; /* (d16,An) */
case 6: t = 20; break; /* (d8,An,Xn) */
case 7:
switch (op & 7)
{ case 0: t = 16; break; /* (xxx).W */
case 1: t = 20; break; /* (xxx).L */
case 2: t = 16; break; /* (d16,pc) */
case 3: t = 20; break; /* (d8,pc,Xn) */
} break;
}
EmitTiming(t);
InstEnd();
return 1;
}
int SWAP(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 8);
e("and edi, byte 7\n");
e("mov edx, [d+edi*4]\n");
e("rol edx, byte 16\n"); /* swap words */
e("test edx, edx\n"); /* clears CF, will set N and Z */
e("lahf\n");
e("xor al, al\n"); /* clear V */
e("mov [d+edi*4], edx\n");
EmitTiming(base_timing);
InstEnd();
return 1;
}
int BSET(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned t;
if (((op >> 6) & 7) != 7 && ((op >> 6) & 7) != 3) return 0;
if (op & 0x100) /* dynamic */
{
if (!CheckEA(op & 0x3f, "101111111000")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
if (((op >> 3) & 7) == 0) /* Dn is long */
{
t = 8;
e("and edi, byte 7\n");
e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */
e("and ebx, byte 31\n"); /* same as modulo 32 */
e("bts dword [d+edi*4], ebx\n"); /* bit->CF, set */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
}
else /* mem is byte */
{
t = 8;
LoadFromEA(op & 0x3f, BYTE_SIZE, 0);
e("mov edi, [d+%d*4]\n", (op >> 9) & 7); /* bit # */
e("and edi, byte 7\n"); /* same as modulo 8 */
e("bts edx, edi\n"); /* bit->CF, set */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
WriteByte(); /* clears EDI for us */
}
}
else if (!(op & 0x100)) /* static */
{
if (!CheckEA(op & 0x3f, "101111111000")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
if (((op >> 3) & 7) == 0) /* Dn is long */
{
t = 12;
e("and edi, byte 7\n"); /* isolate Dn reg # */
e("mov ebx, [esi]\n"); /* get bit # */
e("and ebx, byte 31\n"); /* same as modulo 32 */
e("add esi, byte 2\n");
e("bts dword [d+edi*4], ebx\n"); /* bit->CF, set */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
}
else /* mem is byte */
{
t = 12;
SaveRegTo("run", "edi", "esi"); /* save to EDI spot */
e("add esi, byte 2\n");
LoadFromEA(op & 0x3f, BYTE_SIZE, 0);
RestoreReg("run", "edi");
e("mov edi, [edi]\n");
e("and edi, byte 7\n"); /* same as modulo 8 */
e("bts edx, edi\n"); /* bit->CF, set */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
WriteByte(); /* write back, EDI is cleared */
}
}
if (((op & 0x3f) & 7) == 0) /* Dn is long */
EmitTiming(t);
else /* mem is byte */
EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE));
InstEnd();
return 1;
}
int BCLR(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned t;
if (((op >> 6) & 7) != 6 && ((op >> 6) & 7) != 2) return 0;
if (op & 0x100) /* dynamic */
{
if (!CheckEA(op & 0x3f, "101111111000")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
if (((op >> 3) & 7) == 0) /* Dn is long */
{
t = 10;
e("and edi, byte 7\n");
e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */
e("and ebx, byte 31\n"); /* same as modulo 32 */
e("btr dword [d+edi*4], ebx\n"); /* bit->CF, clear */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
}
else /* mem is byte */
{
t = 8;
LoadFromEA(op & 0x3f, BYTE_SIZE, 0);
e("mov edi, [d+%d*4]\n", (op >> 9) & 7); /* bit # */
e("and edi, byte 7\n"); /* same as modulo 8 */
e("btr edx, edi\n"); /* bit->CF, clear */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
WriteByte(); /* clears EDI for us */
}
}
else if (!(op & 0x100)) /* static */
{
if (!CheckEA(op & 0x3f, "101111111000")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
if (((op >> 3) & 7) == 0) /* Dn is long */
{
t = 14;
e("and edi, byte 7\n"); /* isolate Dn reg # */
e("mov ebx, [esi]\n"); /* get bit # */
e("and ebx, byte 31\n"); /* same as modulo 32 */
e("add esi, byte 2\n");
e("btr dword [d+edi*4], ebx\n"); /* bit->CF, clear */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
}
else /* mem is byte */
{
t = 12;
SaveRegTo("run", "edi", "esi"); /* save to EDI spot */
e("add esi, byte 2\n");
LoadFromEA(op & 0x3f, BYTE_SIZE, 0);
RestoreReg("run", "edi");
e("mov edi, [edi]\n");
e("and edi, byte 7\n"); /* same as modulo 8 */
e("btr edx, edi\n"); /* bit->CF, clear */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
WriteByte(); /* write back, EDI is cleared */
}
}
if (((op & 0x3f) & 7) == 0) /* Dn is long */
EmitTiming(t);
else /* mem is byte */
EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE));
InstEnd();
return 1;
}
int BCHG(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned t;
if (((op >> 6) & 7) != 5 && ((op >> 6) & 7) != 1) return 0;
if (op & 0x100) /* dynamic */
{
if (!CheckEA(op & 0x3f, "101111111000")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
if (((op >> 3) & 7) == 0) /* Dn is long */
{
t = 8;
e("and edi, byte 7\n");
e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */
e("and ebx, byte 31\n"); /* same as modulo 32 */
e("btc dword [d+edi*4], ebx\n"); /* bit->CF, change */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
}
else /* mem is byte */
{
t = 8;
LoadFromEA(op & 0x3f, BYTE_SIZE, 0);
e("mov edi, [d+%d*4]\n", (op >> 9) & 7); /* bit # */
e("and edi, byte 7\n"); /* same as modulo 8 */
e("btc edx, edi\n"); /* bit->CF, change */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
WriteByte(); /* clears EDI for us */
}
}
else if (!(op & 0x100)) /* static */
{
if (!CheckEA(op & 0x3f, "101111111000")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
if (((op >> 3) & 7) == 0) /* Dn is long */
{
t = 12;
e("and edi, byte 7\n"); /* isolate Dn reg # */
e("mov ebx, [esi]\n"); /* get bit # */
e("and ebx, byte 31\n"); /* same as modulo 32 */
e("add esi, byte 2\n");
e("btc dword [d+edi*4], ebx\n"); /* bit->CF, change */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
}
else /* mem is byte */
{
t = 12;
SaveRegTo("run", "edi", "esi"); /* save to EDI spot */
e("add esi, byte 2\n");
LoadFromEA(op & 0x3f, BYTE_SIZE, 0);
RestoreReg("run", "edi");
e("mov edi, [edi]\n");
e("and edi, byte 7\n"); /* same as modulo 8 */
e("btc edx, edi\n"); /* bit->CF, change */
e("setnc dh\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl dh, 6\n"); /* put C in Z position */
e("or ah, dh\n"); /* set the new Z */
WriteByte(); /* write back, EDI is cleared */
}
}
if (((op & 0x3f) & 7) == 0) /* Dn is long */
EmitTiming(t);
else /* mem is byte */
EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE));
InstEnd();
return 1;
}
int ANDItoSR(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
e("test byte [sr+1], 0x20\n"); /* in supervisor? */
e("jz near exception_privilege_violation\n");
SaveCCR(); /* save flags to SR */
e("mov edx, [esi]\n");
e("and edx, 0xa71f\n"); /* mask out unwanted bits */
e("and [sr], dx\n");
e("add esi, byte 2\n");
LoadCCR(); /* get flags back */
e("test byte [sr+1], 0x20\n"); /* if we changed to User, swap SPs */
e("jnz short .in_s\n");
e("mov ebx, [a+7*4]\n");
e("mov edx, [__sp]\n");
e("mov [a+7*4], edx\n");
e("mov [__sp], ebx\n");
SetUserAddressSpace(); /* map in user address space */
EmitLabel(".in_s");
EmitTiming(base_timing);
InstEnd();
return 1;
}
int UNLK(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 8);
e("and edi, byte 7\n");
e("mov ebx, [a+edi*4]\n"); /* An->SP */
SaveReg("run", "ebx"); /* to avoid addrclip() */
SaveReg("run", "edi");
ReadLong(); /* (SP)->An */
RestoreReg("run", "edi");
RestoreReg("run", "ebx");
e("mov [a+edi*4], edx\n");
e("add ebx, byte 4\n");
e("mov [a+7*4], ebx\n"); /* SP+4->SP */
EmitTiming(base_timing);
InstEnd();
return 1;
}
int LINK(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 8);
e("and edi, byte 7\n");
e("mov ebx, [a+7*4]\n"); /* EBX=SP */
e("sub ebx, byte 4\n");
e("mov edx, [a+edi*4]\n");
SaveReg("run", "ebx");
SaveReg("run", "edi");
WriteLong(); /* An->(SP) */
RestoreReg("run", "edi");
RestoreReg("run", "ebx");
e("mov [a+edi*4], ebx\n"); /* SP->An */
e("movsx edx, word [esi]\n");
e("add ebx, edx\n"); /* dn + SP->SP */
e("mov [a+7*4], ebx\n");
e("add esi, byte 2\n");
EmitTiming(base_timing);
InstEnd();
return 1;
}
int ROxMem(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned ea = op & 0x3f, dr = (op >> 8) & 1;
char mnem[4];
char *mnem_t[] = { "ROR", "ROL" };
if (!CheckEA(ea, "001111111000")) return 0;
if (!NumDecodedEA(ea)) return 0;
strcpy(mnem, mnem_t[(op >> 8) & 1]);
InstBegin(op, mnem, NumDecodedEA(ea));
LoadFromEA(ea, WORD_SIZE, 0);
if (dr) /* left */
e("rol dx, byte 1\n");
else
e("ror dx, byte 1\n");
e("setc al\n"); /* AL=CF */
e("test dx, dx\n"); /* CF is cleared */
e("lahf\n");
e("or ah, al\n"); /* put in CF */
e("xor al, al\n"); /* V is always cleared */
WriteWord(); /* write back to mem */
EmitTiming(base_timing + TimingEA(ea, WORD_SIZE));
InstEnd();
return 1;
}
int ROxReg(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned size, t, opr_i, count;
char *opr[] = { "ror", "rol" };
char mnem[4];
char *mnem_t[] = { "ROR", "ROL" };
switch ((op >> 6) & 3)
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
opr_i = (op >> 8) & 1;
strcpy(mnem, mnem_t[opr_i]);
count = (op >> 9) & 7;
if (!count) count = 8;
InstBegin(op, mnem, 8); /* 8 regs */
e("and edi, byte 7\n");
if ((op >> 5) & 1) /* reg contains count */
{
e("mov ebx, ecx\n"); /* save ECX */
e("mov ecx, [d+%d*4]\n", (op >> 9) & 7);
e("and ecx, byte 0x3f\n"); /* modulo 64 */
e("mov edx, [d+edi*4]\n");
switch (size)
{ case BYTE_SIZE: e("%s dl, cl\n", opr[opr_i]); e("setc al\n"); e("test dl, dl\n"); break;
case WORD_SIZE: e("%s dx, cl\n", opr[opr_i]); e("setc al\n"); e("test dx, dx\n"); break;
case LONG_SIZE: e("%s edx, cl\n", opr[opr_i]); e("setc al\n"); e("test edx, edx\n"); break;
}
e("lahf\n");
e("test cl, cl\n"); /* if shift count of 0, X is unaffected */
e("jnz short .not_zero\n");
e("xor al, al\n"); /* clear CF, count was 0 */
EmitLabel(".not_zero");
e("or ah, al\n"); /* put CF in with rest of flags */
e("xor al, al\n"); /* V always cleared */
switch (size) /* write back results */
{ case BYTE_SIZE: e("mov [d+edi*4], dl\n", opr[opr_i]); break;
case WORD_SIZE: e("mov [d+edi*4], dx\n", opr[opr_i]); break;
case LONG_SIZE: e("mov [d+edi*4], edx\n", opr[opr_i]); break;
}
/* timing */
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE: t = 6; break;
case LONG_SIZE: t = 8; break; }
e("shl cl, byte 1\n"); /* 2n */
e("add cl, byte %d\n", t);
e("neg ecx\n");
e("add ecx, ebx\n");
e("js near Turbo68KRun_done\n");
}
else /* immediate count */
{
e("mov edx, [d+edi*4]\n");
switch (size)
{ case BYTE_SIZE: e("%s dl, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dl, dl\n"); break;
case WORD_SIZE: e("%s dx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dx, dx\n"); break;
case LONG_SIZE: e("%s edx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test edx, edx\n"); break;
}
e("lahf\n");
e("or ah, al\n"); /* set CF */
e("xor al, al\n"); /* V always cleared */
switch (size) /* write back results */
{ case BYTE_SIZE: e("mov [d+edi*4], dl\n"); break;
case WORD_SIZE: e("mov [d+edi*4], dx\n"); break;
case LONG_SIZE: e("mov [d+edi*4], edx\n"); break;
}
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE: t = 6; break;
case LONG_SIZE: t = 8; break; }
EmitTiming(t + count * 2);
}
InstEnd();
return 1;
}
int EXT(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned opmode = (op >> 6) & 7;
if (opmode != 2 && opmode != 3) return 0;
InstBegin(op, mnem, 8);
e("and edi, byte 7\n");
e("mov edx, [d+edi*4]\n");
if (opmode == 2) /* byte -> word */
{
e("movsx dx, dl\n");
e("mov [d+edi*4], dx\n");
e("test dx, dx\n");
}
else if (opmode == 3) /* word -> long */
{
e("movsx edx, dx\n");
e("mov [d+edi*4], edx\n");
e("test edx, edx\n");
}
else if (opmode == 7) /* byte -> long */
{
/*
* Note: This code is never executed. EXTB is present on 68020 and
* higher processors. Turbo68K was only intended to emulate the
* 68000. This code was an accident, but it will be kept here in case
* in the future it is needed.
*/
e("movsx edx, dl\n");
e("mov [d+edi*4], edx\n");
e("test edx, edx\n");
}
e("lahf\n"); /* CF is already cleared by TEST */
e("xor al, al\n"); /* V always cleared */
EmitTiming(base_timing);
InstEnd();
return 1;
}
int ROXxMem(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned ea = op & 0x3f, dr = (op >> 8) & 1;
char mnem[5];
char *mnem_t[] = { "ROXR", "ROXL" };
if (!CheckEA(ea, "001111111000")) return 0;
if (!NumDecodedEA(ea)) return 0;
strcpy(mnem, mnem_t[(op >> 8) & 1]);
InstBegin(op, mnem, NumDecodedEA(ea));
LoadFromEA(ea, WORD_SIZE, 0);
e("shr byte [x], 1\n");
if (dr) /* left */
e("rcl dx, byte 1\n");
else
e("rcr dx, byte 1\n");
e("setc al\n"); /* AL=CF */
e("setc byte [x]\n");
e("test dx, dx\n"); /* CF is cleared */
e("lahf\n");
e("or ah, al\n"); /* put in CF */
e("xor al, al\n");
WriteWord(); /* write back to mem */
EmitTiming(base_timing + TimingEA(ea, WORD_SIZE));
InstEnd();
return 1;
}
int ROXxReg(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned size, t, opr_i, count;
char *opr[] = { "rcr", "rcl" };
char mnem[5];
char *mnem_t[] = { "ROXR", "ROXL" };
switch ((op >> 6) & 3)
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
opr_i = (op >> 8) & 1;
strcpy(mnem, mnem_t[opr_i]);
count = (op >> 9) & 7;
if (!count) count = 8;
InstBegin(op, mnem, 8); /* 8 regs */
e("and edi, byte 7\n");
if ((op >> 5) & 1) /* reg contains count */
{
e("mov ebx, ecx\n"); /* save ECX */
e("mov ecx, [d+%d*4]\n", (op >> 9) & 7);
e("and ecx, byte 0x3f\n"); /* modulo 64 */
e("mov dl, [x]\n");
e("shr dl, byte 1\n"); /* X->CF so we can use RCL/RCR */
e("mov edx, [d+edi*4]\n");
switch (size)
{ case BYTE_SIZE: e("%s dl, cl\n", opr[opr_i]); e("setc al\n"); e("test dl, dl\n"); break;
case WORD_SIZE: e("%s dx, cl\n", opr[opr_i]); e("setc al\n"); e("test dx, dx\n"); break;
case LONG_SIZE: e("%s edx, cl\n", opr[opr_i]); e("setc al\n"); e("test edx, edx\n"); break;
}
e("lahf\n");
e("test cl, cl\n"); /* if shift count of 0, X is unaffected */
e("jz short .zero_count\n");
e("or ah, al\n"); /* set CF */
e("test ah, 1\n"); /* CF */
e("setnz byte [x]\n");
e("jmp short .skip\n");
EmitLabel(".zero_count");
e("mov al, [x]\n"); /* X->C */
e("and al, byte 1\n");
e("and ah, 0xfe\n"); /* get rid of old C before putting new C in */
e("or ah, al\n");
EmitLabel(".skip");
e("xor al, al\n"); /* V always cleared */
switch (size) /* write back results */
{ case BYTE_SIZE: e("mov [d+edi*4], dl\n", opr[opr_i]); break;
case WORD_SIZE: e("mov [d+edi*4], dx\n", opr[opr_i]); break;
case LONG_SIZE: e("mov [d+edi*4], edx\n", opr[opr_i]); break;
}
/* timing */
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE: t = 6; break;
case LONG_SIZE: t = 8; break; }
e("shl cl, byte 1\n"); /* 2n */
e("add cl, byte %d\n", t);
e("neg ecx\n");
e("add ecx, ebx\n");
e("js near Turbo68KRun_done\n");
}
else /* immediate count */
{
e("mov dl, [x]\n");
e("shr dl, byte 1\n"); /* X->CF so we can use RCL/RCR */
e("mov edx, [d+edi*4]\n");
switch (size)
{ case BYTE_SIZE: e("%s dl, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dl, dl\n"); break;
case WORD_SIZE: e("%s dx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test dx, dx\n"); break;
case LONG_SIZE: e("%s edx, byte %d\n", opr[opr_i], count); e("setc al\n"); e("test edx, edx\n"); break;
}
e("lahf\n");
e("or ah, al\n"); /* set CF */
e("test ah, 1\n"); /* CF */
e("setnz byte [x]\n");
e("xor al, al\n"); /* V always cleared */
switch (size) /* write back results */
{ case BYTE_SIZE: e("mov [d+edi*4], dl\n", opr[opr_i]); break;
case WORD_SIZE: e("mov [d+edi*4], dx\n", opr[opr_i]); break;
case LONG_SIZE: e("mov [d+edi*4], edx\n", opr[opr_i]); break;
}
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE: t = 6; break;
case LONG_SIZE: t = 8; break; }
EmitTiming(t + count * 2);
}
InstEnd();
return 1;
}
int MOVEtoCCR(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned ea = op & 0x3f;
if (!CheckEA(ea, "101111111111")) return 0;
if (!NumDecodedEA(ea)) return 0;
InstBegin(op, mnem, NumDecodedEA(ea));
LoadFromEA(ea, WORD_SIZE, 0);
e("mov [sr], dl\n");
LoadCCR(); /* update flags */
EmitTiming(base_timing + TimingEA(ea, WORD_SIZE)); /* technically, word
sized */
InstEnd();
return 1;
}
int LSxMem(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned ea = op & 0x3f, dr = (op >> 8) & 1;
char mnem[4];
char *mnem_t[] = { "LSR", "LSL" };
if (!CheckEA(ea, "001111111000")) return 0;
if (!NumDecodedEA(ea)) return 0;
strcpy(mnem, mnem_t[(op >> 8) & 1]);
InstBegin(op, mnem, NumDecodedEA(ea));
LoadFromEA(ea, WORD_SIZE, 0);
if (dr) /* left */
e("shl dx, byte 1\n");
else
e("shr dx, byte 1\n");
e("lahf\n");
e("setc byte [x]\n");
e("xor al, al\n");
WriteWord(); /* write back to mem */
EmitTiming(base_timing + TimingEA(ea, WORD_SIZE));
InstEnd();
return 1;
}
int LSxReg(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned size, t, opr_i, count;
char *opr[] = { "shr", "shl" };
char mnem[4];
char *mnem_t[] = { "LSR", "LSL" };
switch ((op >> 6) & 3)
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
opr_i = (op >> 8) & 1;
strcpy(mnem, mnem_t[opr_i]);
count = (op >> 9) & 7;
if (!count) count = 8;
InstBegin(op, mnem, 8); /* 8 regs */
e("and edi, byte 7\n");
if ((op >> 5) & 1) /* reg contains count */
{
e("mov ebx, ecx\n"); /* save ECX */
e("mov ecx, [d+%d*4]\n", (op >> 9) & 7);
e("and ecx, byte 0x3f\n"); /* modulo 64 */
e("mov edx, [d+edi*4]\n"); /* get register... */
switch (size)
{ case BYTE_SIZE: e("%s dl, cl\n", opr[opr_i]);
e("setc al\n"); /* CF -> AL */
e("test dl, dl\n");
e("mov [d+edi*4], dl\n"); /* MOV does not
affect flags */
break;
case WORD_SIZE: e("%s dx, cl\n", opr[opr_i]);
e("setc al\n");
e("test dx, dx\n");
e("mov [d+edi*4], dx\n");
break;
case LONG_SIZE: e("%s edx, cl\n", opr[opr_i]);
e("setc al\n");
e("test edx, edx\n");
e("mov [d+edi*4], edx\n");
break;
}
e("lahf\n");
e("test cl, cl\n"); /* if zero count... */
e("jz short .zero\n");
e("or ah, al\n"); /* new CF... the first test cleared old C */
e("test al, 1\n"); /* C->X */
e("setnz byte [x]\n");
EmitLabel(".zero");
e("xor al, al\n"); /* V cleared */
/* timing */
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE: t = 6; break;
case LONG_SIZE: t = 8; break; }
e("shl cl, byte 1\n"); /* 2n */
e("add cl, byte %d\n", t);
e("neg ecx\n");
e("add ecx, ebx\n");
e("js near Turbo68KRun_done\n");
}
else /* immediate count */
{
e("%s ", opr[opr_i]);
switch (size)
{ case BYTE_SIZE: e("byte "); break;
case WORD_SIZE: e("word "); break;
case LONG_SIZE: e("dword "); break; }
e("[d+edi*4], %d\n", count);
e("lahf\n");
e("setc byte [x]\n");
e("xor al, al\n");
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE: t = 6; break;
case LONG_SIZE: t = 8; break; }
EmitTiming(t + count * 2);
}
InstEnd();
return 1;
}
int NOP(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
EmitTiming(base_timing);
InstEnd();
return 1;
}
int CLR(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned ea = op & 0x3f, size, t;
if (!CheckEA(ea, "101111111000")) return 0;
if (!NumDecodedEA(ea)) return 0;
switch ((op >> 6) & 3)
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
InstBegin(op, mnem, NumDecodedEA(ea));
e("mov eax, 0x4000\n"); /* Z always set, everything else cleared */
if (mpu == 68000 && dummyread && (ea >> 3))
{
/*
* Emulate the dummy read for memory addressing modes, if necessary.
* We only do it for memory modes, because it really can't make any
* difference if the destination is a register!
*/
LoadFromEA(ea, size, 0); /* do the dummy read */
e("xor edx, edx\n"); /* now, write a 0 */
switch (size) /* write */
{
case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break;
}
}
else
{
/*
* This method is quicker, since dummy reads aren't needed
*/
e("xor edx, edx\n");
StoreToEAUsingEDI(ea, size, 0);
}
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE:
if ((ea >> 3) == 0) t = 4; /* reg */
else t = 8 + TimingEA(ea, size); /* mem */
break;
case LONG_SIZE:
if ((ea >> 3) == 0) t = 6; /* reg */
else t = 12 + TimingEA(ea, size); /* mem */
break;
}
EmitTiming(t);
InstEnd();
return 1;
}
int ORItoSR(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
e("test byte [sr+1], 0x20\n"); /* in supervisor? */
e("jz near exception_privilege_violation\n");
SaveCCR(); /* save flags to SR */
e("mov edx, [esi]\n");
e("and edx, 0xa71f\n"); /* mask out unwanted bits */
e("or [sr], dx\n");
e("add esi, byte 2\n");
LoadCCR(); /* get flags back */
EmitTiming(base_timing);
InstEnd();
return 1;
}
int MOVEfromSR(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned ea = op & 0x3f;
if (!CheckEA(ea, "101111111000")) return 0;
InstBegin(op, mnem, 1);
/*
* This instruction is not privileged for 68000 and 68008 processors.
*/
if (mpu == 68010)
{
e("test byte [sr+1], 0x20\n"); /* in supervisor? */
e("jz near exception_privilege_violation\n");
}
SaveCCR(); /* save flags to SR */
if (mpu == 68000 && dummyread && (ea >> 3))
{
/*
* When dummy reads are enabled, and the EA mode is not register, we
* perform a dummy read
*/
LoadFromEA(ea, WORD_SIZE, 0);
e("mov edx, [sr]\n");
WriteWord();
}
else
{
e("mov edx, [sr]\n"); /* get SR */
StoreToEAUsingEDI(ea, WORD_SIZE, 0);
}
LoadCCR(); /* get back SR */
if ((ea >> 3) == 0)
EmitTiming(6);
else
EmitTiming(8 + TimingEA(ea, WORD_SIZE));
InstEnd();
return 1;
}
int ArithmeticA(unsigned short op, char *mnem, unsigned base_timing)
{
int size = UNKNOWN_SIZE, t;
if (!CheckEA(op & 0x3f, "111111111111")) /* check EA mode */
return 0;
if (!NumDecodedEA(op & 0x3f)) /* check if decoded this EA */
return 0;
switch ((op >> 6) & 7) /* set allowed sizes */
{ case 3: size = WORD_SIZE; break;
case 7: size = LONG_SIZE; break;
default: return 0; }
if (((op >> 12) & 0xf) != 0xd && ((op >> 12) & 0xf) != 9) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
LoadFromEA(op & 0x3f, size, 1); /* load */
if (((op >> 12) & 0xf) == 0xd) /* ADDA */
e("add [a+%d*4], edx\n", (op >> 9) & 7); /* store */
else /* SUBA */
e("sub [a+%d*4], edx\n", (op >> 9) & 7);
switch (size)
{ case WORD_SIZE: t = 8; break;
case LONG_SIZE: t = 6;
if (((op & 0x3f) >> 3) == 0) t += 2; /* Dn */
else if (((op & 0x3f) >> 3) == 1) t += 2; /* An */
else if (((op & 0x3f) >> 3) == 7 && (op & 7) == 4)
t += 2; /* #<data> */
break;
}
EmitTiming(TimingEA(op & 0x3f, size) + t);
InstEnd();
return 1;
}
int ASxMem(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned ea = op & 0x3f, dr = (op >> 8) & 1;
char mnem[4];
char *mnem_t[] = { "ASR", "ASL" };
if (!CheckEA(ea, "001111111000")) return 0;
if (!NumDecodedEA(ea)) return 0;
strcpy(mnem, mnem_t[(op >> 8) & 1]);
InstBegin(op, mnem, NumDecodedEA(ea));
LoadFromEA(ea, WORD_SIZE, 0);
if (dr) /* left */
e("shl dx, byte 1\n");
else
e("sar dx, byte 1\n");
e("lahf\n");
e("setc byte [x]\n");
if (dr) /* only for left shifts, right shifts don't change the MSB */
{
e("sets al\n"); /* AL=current MSB */
e("adc al, byte 0\n"); /* CF contains old MSB */
e("and al, 1\n"); /* preserve only the flag bit */
}
WriteWord(); /* write back to mem */
EmitTiming(base_timing + TimingEA(ea, WORD_SIZE));
InstEnd();
return 1;
}
int ASxReg(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned i, size, t = 0, opr_i, count;
char mnem[4];
char *mnem_t[] = { "ASR", "ASL" };
switch ((op >> 6) & 3)
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
opr_i = (op >> 8) & 1;
strcpy(mnem, mnem_t[opr_i]);
count = (op >> 9) & 7;
if (!count) count = 8;
InstBegin(op, mnem, 8); /* 8 regs */
e("and edi, byte 7\n");
if ((op >> 5) & 1) /* reg contains count */
{
if (opr_i) /* ASL */
{
e("xor al, al\n");
SaveReg("run", "edi"); /* save EDI */
e("mov edx, [d+edi*4]\n"); /* get reg to shift */
e("mov edi, [d+%d*4]\n", (op >> 9) & 7);
e("and edi, byte 0x3f\n"); /* modulo 64 */
e("jz short .zero_shift\n");
EmitLabel(".loop");
e("shl ");
switch (size)
{ case BYTE_SIZE: e("dl, "); break;
case WORD_SIZE: e("dx, "); break;
case LONG_SIZE: e("edx, "); break; }
e("1\n");
e("setc bl\n"); /* old MSB */
e("sets bh\n"); /* new MSB */
e("xor bh, bl\n"); /* detect change */
e("or al, bh\n"); /* reflect change in V */
e("sub ecx, byte 2\n"); /* each rotation=2 cycles */
e("dec edi\n");
e("jnz short .loop\n");
RestoreReg("run", "edi");
switch (size) /* store reg, get flags */
{ case BYTE_SIZE: e("mov [d+edi*4], dl\n");
e("test dl, dl\n"); break;
case WORD_SIZE: e("mov [d+edi*4], dx\n");
e("test dx, dx\n"); break;
case LONG_SIZE: e("mov [d+edi*4], edx\n");
e("test edx, edx\n"); break;
}
e("lahf\n");
e("or ah, bl\n"); /* CF */
e("test bl, bl\n");
e("setnz byte [x]\n"); /* CF==X */
e("jmp short .end\n");
EmitLabel(".zero_shift");
switch (size)
{ case BYTE_SIZE: e("test dl, dl\n"); break;
case WORD_SIZE: e("test dx, dx\n"); break;
case LONG_SIZE: e("test edx, edx\n"); break;
}
e("lahf\n");
e("xor al, al\n");
EmitLabel(".end");
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE: t = 6; break;
case LONG_SIZE: t = 8; break; }
EmitTiming(t);
}
else /* ASR */
{
e("mov ebx, ecx\n"); /* save ECX */
e("mov ecx, [d+%d*4]\n", (op >> 9) & 7);
e("and ecx, byte 0x3f\n"); /* modulo 64 */
switch (size)
{ case BYTE_SIZE: e("mov dl, [d+edi*4]\n");
e("sar dl, cl\n");
e("setc bl\n");
e("test dl, dl\n");
break;
case WORD_SIZE: e("mov edx, [d+edi*4]\n");
e("sar dx, cl\n");
e("setc bl\n");
e("test dx, dx\n");
break;
case LONG_SIZE: e("mov edx, [d+edi*4]\n");
e("sar edx, cl\n");
e("setc bl\n"); /* get CF from SHL/SHR */
e("test edx, edx\n");/* this trashes the CF */
break;
}
e("lahf\n");
e("test cl, cl\n"); /* if shift count of 0, X is unaffected, C=0 */
e("jz short .no_x\n");
e("or ah, bl\n"); /* CF! */
e("test ah, 1\n"); /* CF */
e("setnz byte [x]\n");
EmitLabel(".no_x");
switch (size) /* write back and detect MSB change */
{ case BYTE_SIZE: e("mov [d+edi*4], dl\n");
break;
case WORD_SIZE: e("mov [d+edi*4], dx\n");
break;
case LONG_SIZE: e("mov [d+edi*4], edx\n");
break;
}
e("xor al, al\n"); /* V=0 because MSB never changes w/ ASR */
/* timing */
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE: t = 6; break;
case LONG_SIZE: t = 8; break; }
e("shl cl, byte 1\n"); /* 2n */
e("add cl, byte %d\n", t);
e("neg ecx\n");
e("add ecx, ebx\n");
e("js near Turbo68KRun_done\n");
}
}
else /* immediate count */
{
if (!opr_i) /* ASR */
{
e("sar ");
switch (size)
{ case BYTE_SIZE: e("byte "); break;
case WORD_SIZE: e("word "); break;
case LONG_SIZE: e("dword "); break; }
e("[d+edi*4], %d\n", count);
e("lahf\n");
e("setc byte [x]\n");
e("xor al, al\n");
t = count * 2; /* 2 cycles per shift */
}
else /* ASL */
{
e("xor al, al\n"); /* clear V */
switch (size)
{ case BYTE_SIZE: e("mov dl, [d+edi*4]\n"); break;
case WORD_SIZE: e("mov dx, [d+edi*4]\n"); break;
case LONG_SIZE: e("mov edx, [d+edi*4]\n"); break; }
for (i = 1; i <= count; i++)
{
e("shl ");
switch (size)
{ case BYTE_SIZE: e("dl, "); break;
case WORD_SIZE: e("dx, "); break;
case LONG_SIZE: e("edx, "); break; }
e("1\n");
if (i == count)
{
e("lahf\n");
e("setc byte [x]\n");
}
e("setc bl\n"); /* old MSB */
e("sets bh\n"); /* new MSB */
e("xor bh, bl\n"); /* detect change */
e("or al, bh\n"); /* reflect change in V */
e("sub ecx, byte 2\n"); /* each rotation=2 cycles */
}
switch (size) /* write results back to Dn */
{ case BYTE_SIZE: e("mov [d+edi*4], dl\n"); break;
case WORD_SIZE: e("mov [d+edi*4], dx\n"); break;
case LONG_SIZE: e("mov [d+edi*4], edx\n"); break; }
}
switch (size)
{ case BYTE_SIZE:
case WORD_SIZE: t += 6; break;
case LONG_SIZE: t += 8; break; }
EmitTiming(t);
}
InstEnd();
return 1;
}
int ArithmeticQ(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned ea = op & 0x3f, size, t, data, opr_i;
char mnem[5];
char *mnem_t[] = { "ADDQ", "SUBQ" };
char *opr[] = { "add", "sub" };
if (!CheckEA(ea, "111111111000")) return 0;
t = base_timing;
switch ((op >> 6) & 3)
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; t += 4; break;
default: return 0; }
if ((ea >> 3) != 0 && (ea >> 3) != 1) t += 4; /* mem */
if (size == BYTE_SIZE && (ea >> 3) == 1) /* byte An not allowed */
return 0;
if (!NumDecodedEA(ea)) return 0;
data = (op >> 9) & 7;
if (!data) data = 8;
strcpy(mnem, mnem_t[(op >> 8) & 1]);
opr_i = (op >> 8) & 1;
InstBegin(op, mnem, NumDecodedEA(ea));
if ((ea >> 3) == 0) /* Dn */
{
e("and edi, byte 7\n");
switch (size)
{ case BYTE_SIZE:
e("%s byte [d+edi*4], %d\n", opr[opr_i], data);
break;
case WORD_SIZE:
e("%s word [d+edi*4], %d\n", opr[opr_i], data);
break;
case LONG_SIZE:
e("%s dword [d+edi*4], byte %d\n", opr[opr_i], data);
break;
}
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
}
else if ((ea >> 3) == 1) /* An */
{
/*
* Manual says the entire address register is used despite the size
* of the operation.
*/
e("and edi, byte 7\n");
e("%s dword [a+edi*4], byte %d\n", opr[opr_i], data);
}
else /* memory */
{
LoadFromEA(ea, size, 0);
switch (size)
{ case BYTE_SIZE: e("%s dl, byte %d\n", opr[opr_i], data); break;
case WORD_SIZE: e("%s dx, %d\n", opr[opr_i], data); break;
case LONG_SIZE: e("%s edx, byte %d\n", opr[opr_i], data); break; }
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
switch (size)
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break; }
}
EmitTiming(t + TimingEA(ea, size));
InstEnd();
return 1;
}
int NEGx(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned ea = op & 0x3f, size, t;
char mnem[5];
char *mnem_t[] = { "NEGX", "NEG" };
if (!CheckEA(ea, "101111111000")) return 0;
if (!NumDecodedEA(ea)) return 0;
switch ((op >> 6) & 3)
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
strcpy(mnem, mnem_t[(op >> 10) & 1]);
InstBegin(op, mnem, NumDecodedEA(ea));
if ((ea >> 3) == 0) /* Dn */
{
e("and edi, byte 7\n");
if (!(op & 0x0400)) /* NEGX */
{
switch (size)
{ case BYTE_SIZE: e("xor dl, dl\n"); break;
case WORD_SIZE: case LONG_SIZE:
e("xor edx, edx\n"); break;
}
e("shr byte [x], 1\n"); /* X->CF */
switch (size)
{ case BYTE_SIZE: e("sbb dl, [d+edi*4]\n");
e("mov bl, ah\n"); /* store old flags */
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
e("jnz short .clr\n"); /* Z clear */
e("and bl, byte 0x40\n"); /* isolate old Z */
e("and ah, byte 0xbf\n"); /* kill new Z */
e("or ah, bl\n"); /* in w/ old Z */
EmitLabel(".clr");
e("mov [d+edi*4], dl\n"); t = 4; break;
case WORD_SIZE: e("sbb dx, [d+edi*4]\n");
e("mov bl, ah\n");
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
e("jnz short .clr\n");
e("and bl, byte 0x40\n");
e("and ah, byte 0xbf\n");
e("or ah, bl\n");
EmitLabel(".clr");
e("mov [d+edi*4], dx\n"); t = 4; break;
case LONG_SIZE: e("sbb edx, [d+edi*4]\n");
e("mov bl, ah\n");
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
e("jnz short .clr\n");
e("and bl, byte 0x40\n");
e("and ah, byte 0xbf\n");
e("or ah, bl\n");
EmitLabel(".clr");
e("mov [d+edi*4], edx\n"); t = 6; break; }
}
else /* NEG */
{
switch (size)
{ case BYTE_SIZE: e("neg byte [d+edi*4]\n");
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
t = 4; break;
case WORD_SIZE: e("neg word [d+edi*4]\n");
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
t = 4; break;
case LONG_SIZE: e("neg dword [d+edi*4]\n");
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
t = 6; break;
}
}
}
else /* memory */
{
if (!(op & 0x0400)) /* NEGX */
{
LoadFromEA(ea, size, 0);
e("mov edi, ebx\n"); /* save address where data came from */
switch (size)
{ case BYTE_SIZE: e("xor bl, bl\n"); break;
case WORD_SIZE: case LONG_SIZE:
e("xor ebx, ebx\n"); break;
}
e("shr byte [x], 1\n"); /* X->CF */
switch (size)
{ case BYTE_SIZE: e("sbb bl, dl\n");
e("mov dl, ah\n");
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
e("jnz short .clr\n");
e("and dl, byte 0x40\n");
e("and ah, byte 0xbf\n");
e("or ah, dl\n");
EmitLabel(".clr");
break;
case WORD_SIZE: e("sbb bx, dx\n");
e("mov dl, ah\n");
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
e("jnz short .clr\n");
e("and dl, byte 0x40\n");
e("and ah, byte 0xbf\n");
e("or ah, dl\n");
EmitLabel(".clr");
break;
case LONG_SIZE: e("sbb ebx, edx\n");
e("mov dl, ah\n");
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
e("jnz short .clr\n");
e("and dl, byte 0x40\n");
e("and ah, byte 0xbf\n");
e("or ah, dl\n");
EmitLabel(".clr");
break; }
e("mov edx, ebx\n");
e("mov ebx, edi\n");
/* WriteXXX will clear EDI for us */
switch (size)
{ case BYTE_SIZE: WriteByte(); t = 8; break;
case WORD_SIZE: WriteWord(); t = 8; break;
case LONG_SIZE: WriteLong(); t = 12; break; }
t += TimingEA(ea, size);
}
else /* NEG */
{
LoadFromEA(ea, size, 0);
switch (size)
{ case BYTE_SIZE: e("neg dl\n");
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
WriteByte(); t = 8; break;
case WORD_SIZE: e("neg dx\n");
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
WriteWord(); t = 8; break;
case LONG_SIZE: e("neg edx\n");
e("lahf\n");
e("seto al\n");
e("setc byte [x]\n");
WriteLong(); t = 12; break; }
t += TimingEA(ea, size);
}
}
EmitTiming(t);
InstEnd();
return 1;
}
int RTS(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 1);
e("mov ebx, [a+7*4]\n");
ReadLong();
e("add dword [a+7*4], byte 4\n");
e("mov esi, edx\n");
UpdateFetchPtr();
EmitTiming(base_timing);
InstEnd();
return 1;
}
int MOVEtoSR(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned ea = op & 0x3f;
if (!CheckEA(ea, "101111111111")) return 0;
if (!NumDecodedEA(ea)) return 0;
InstBegin(op, mnem, NumDecodedEA(ea));
LoadFromEA(ea, WORD_SIZE, 0);
e("test byte [sr+1], 0x20\n"); /* in supervisor? */
e("jz near exception_privilege_violation\n");
e("test dh, 0x20\n"); /* if still in supervisor... */
e("jnz .still_s\n"); /* ...no changes need to be made */
e("mov ebx, [__sp]\n"); /* swap SSP and USP */
e("xchg [a+7*4], ebx\n");
e("mov [__sp], ebx\n");
SetUserAddressSpace(); /* map in user address space */
EmitLabel(".still_s");
e("and edx, 0xa71f\n"); /* mask out unwanted bits */
e("mov [sr], edx\n"); /* load new SR */
LoadCCR(); /* reload flags we changed in CCR */
EmitTiming(base_timing + TimingEA(ea, WORD_SIZE));
InstEnd();
return 1;
}
int DBcc(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned cond = (op >> 8) & 0xf;
char mnem[5];
char *mnem_t[] = { "DBT", "DBF", "DBHI", "DBLS",
"DBCC", "DBCS", "DBNE", "DBEQ",
"DBVC", "DBVS", "DBPL", "DBMI",
"DBGE", "DBLT", "DBGT", "DBLE" };
strcpy(mnem, mnem_t[cond]); /* set mnemonic */
InstBegin(op, mnem, 8);
switch (cond)
{
/* EAX:0000000000000000 NZ00 000C 0000 000V */
case 0: /* always true */
e("add esi, byte 2\n");
EmitTiming(10);
InstEnd();
return 1;
case 1: /* DBF/DBRA: no condition to test, just loop */
break;
case 2: /* HI: !C & !Z */
e("test ah, byte 0x41\n");
e("jz short .do\n");
break;
case 3: /* LS: C | Z */
e("test ah, byte 0x41\n");
e("jnz short .do\n");
break;
case 4: /* CC: !C */
e("test ah, byte 1\n");
e("jz short .do\n");
break;
case 5: /* CS: C */
e("test ah, byte 1\n");
e("jnz short .do\n");
break;
case 6: /* NE: !Z */
e("test ah, byte 0x40\n");
e("jz short .do\n");
break;
case 7: /* EQ: Z */
e("test ah, byte 0x40\n");
e("jnz short .do\n");
break;
case 8: /* VC: !V */
e("test al, byte 1\n");
e("jz short .do\n");
break;
case 9: /* VS: V */
e("test al, byte 1\n");
e("jnz short .do\n");
break;
case 0xa: /* PL: !N */
e("test ah, byte 0x80\n");
e("jz short .do\n");
break;
case 0xb: /* MI: N */
e("test ah, byte 0x80\n");
e("jnz short .do\n");
break;
case 0xc: /* GE: N & V | !N & !V (N&V || !N&!V) */
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpe short .do\n");
break;
case 0xd: /* LT: N & !V | !N & V */
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpo short .do\n");
EmitLabel(".dont_do");
break;
case 0xe: /* GT: N & V & !Z | !N & !V & !Z */
/* if Z, then it is GE and we don't take this branch */
e("test ah, byte 0x40\n");
e("jnz short .dont_do\n");
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpe short .do\n");
EmitLabel(".dont_do");
break;
case 0xf: /* LE: Z | N & !V | !N & V */
e("test ah, 0x40\n");
e("jnz short .do\n");
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpo short .do\n");
break;
}
e("and edi, byte 7\n");/* condition not met, decrement and branch */
if (cond == 1 && skip) /* idle loop skipping w/ DBRA */
e("mov edx, esi\n");
e("sub word [d+edi*4], 1\n");
e("jc short .do_loop_expired\n"); /* loop expired... */
e("movsx ebx, word [esi]\n");
e("add esi, ebx\n");
if (brafetch)
{
if (skip) /* skip uses EDX, we must save it */
SaveReg("run", "edx");
e("sub esi, ebp\n"); /* ESI=denormalized PC */
UpdateFetchPtr();
if (skip)
RestoreReg("run", "edx");
}
if (cond == 1 && skip)
{
e("sub edx, byte 2\n"); /* to get address of instruction */
e("cmp edx, esi\n");
e("je short .skip\n"); /* the same: this is an idle loop */
}
EmitTiming(10);
InstEnd();
Align(4);
EmitLabel(".do"); /* condition was met, go on to next instruction */
e("add esi, byte 2\n");
EmitTiming(12);
InstEnd();
Align(4);
EmitLabel(".do_loop_expired"); /* condition false, but loop expired */
e("add esi, byte 2\n");
EmitTiming(14);
InstEnd();
if (cond == 1 && skip)
{
/*
* This code makes it look as if the loop was executed and expired.
*/
EmitLabel(".skip");
e("add edx, byte 4\n");
e("mov esi, edx\n"); /* EDX contained PC for DBRA */
e("mov edx, [d+edi*4]\n"); /* get counter loop */
e("and edx, 0xffff\n");
e("inc edx\n"); /* we subtracted 1 a little ways above */
e("mov word [d+edi*4], 0xffff\n");
e("mov ebx, edx\n");
e("shl edx, byte 3\n"); /* (i << 3) + (i << 2) = i*10 */
e("shl ebx, byte 2\n");
e("sub ecx, edx\n");
e("sub ecx, ebx\n");
e("js near Turbo68KRun_done\n");
InstEnd();
}
return 1;
}
int MOVEM(unsigned short op, char *mnem, unsigned base_timing)
{
/*
* If a non-post/pre-inc/decrement mode is used, the registers are
* transferred to the address, and that address is inc/decremented but the
* change is NOT reflected in the address register.
*
* If the mode is pre-dec or post-inc, it IS reflected. First, load up the
* value of the register, and then write it back and the end, but don't
* modify it while performing the operation, use a temp. X86 register
* instead.
*
* NOTE: The reg->mem mode should be fine, I've tested it -- but there
* might be some bugs.
*
* NOTE (April 8, 2001): Pete Dabbs just emailed me to report a problem
* with MOVEM mem->reg. Basically, code like this wasn't working properly:
*
* MOVEM.L (A7)+,A7
*
* What should happen there is the data from (A7) is read. Then, 4 is added
* to A7, and finally, the data is written to A7. What Turbo68K was doing
* wrong was it was writing the data from (A7) to A7 and THEN incrementing
* A7. Not sure if the fix is correct, but I hope it is... I didn't change
* anything in MOVEM reg->mem...
*/
int dr = (op >> 10) & 1, size = UNKNOWN_SIZE;
unsigned ea = op & 0x3f;
switch ((op >> 6) & 1)
{ case 0: size = WORD_SIZE; break;
case 1: size = LONG_SIZE; break;
default: return 0; }
if (!NumDecodedEA(ea)) return 0;
if (!dr) /* reg->mem */
{
if (!CheckEA(ea, "001011111000")) return 0;
}
else /* mem->reg */
if (!CheckEA(ea, "001101111011")) return 0;
InstBegin(op, mnem, NumDecodedEA(ea));
if (!dr) /* reg->mem */
{
SaveReg("run", "eax"); /* save flags */
e("mov eax, [esi]\n"); /* AX=register list mask */
e("add esi, byte 2\n");
LoadControlEA(ea); /* EBX=store address */
SaveReg("run", "esi");
if ((ea >> 3) == 4) /* write back if (An)+,-(An) */
SaveReg("run", "edi"); /* EDI may contain reg # */
if ((ea >> 3) == 4) /* -(An) has reg mask reversed */
e("mov esi, d+15*4\n"); /* start from top and decrement */
else
e("mov esi, d\n");
EmitLabel(".loop");
if (size == WORD_SIZE)
e("sub ecx, byte 4\n");
else
e("sub ecx, byte 8\n");
e("shr eax, byte 1\n"); /* mask bit->CF */
e("jnc short .skip\n"); /* not 1? skip this reg */
if ((ea >> 3) == 4) /* -(An) */
{
if (size == WORD_SIZE) e("sub ebx, byte 2\n"); /* predec */
else e("sub ebx, byte 4\n");
}
e("mov edx, [esi]\n"); /* get reg to store... */
SaveReg("run", "ebx");
if (size == WORD_SIZE) WriteWord(); else WriteLong(); /* get mem */
RestoreReg("run", "ebx");
if (!((ea >> 3) == 4)) /* not predec, increment */
{
if (size == WORD_SIZE) e("add ebx, byte 2\n");
else e("add ebx, byte 4\n");
}
EmitLabel(".skip");
if ((ea >> 3) == 4) /* -(An) */
{
e("sub esi, byte 4\n"); /* next reg down */
e("cmp esi, d-4\n"); /* done? */
e("jne short .loop\n"); /* not done, keep looping */
}
else
{
e("add esi, byte 4\n"); /* next reg up */
e("cmp esi, d+16*4\n"); /* done? */
e("jne short .loop\n"); /* not done, keep looping */
}
if ((ea >> 3) == 4) /* write back if -(An) */
{
RestoreReg("run", "edi");
e("mov [a+edi*4], ebx\n");
}
RestoreReg("run", "esi");
RestoreReg("run", "eax");
switch (ea >> 3) /* timing for address mode */
{ case 2: base_timing = 8; break; /* (An) */
case 4: base_timing = 8; break; /* -(An) */
case 5: base_timing = 12; break; /* (d16,An) */
case 6: base_timing = 14; break; /* (d8,An,Xn) */
case 7:
switch (ea & 7)
{ case 0: base_timing = 12; break; /* (xxx).W */
case 1: base_timing = 16; break; /* (xxx).L */
} break;
}
}
else /* mem->reg */
{
SaveReg("run", "eax"); /* save flags */
e("mov eax, [esi]\n"); /* AX=register list mask */
e("add esi, byte 2\n");
LoadControlEA(ea); /* EBX=load address */
SaveReg("run", "esi");
if ((ea >> 3) == 3) /* write back if (An)+,-(An) */
SaveReg("run", "edi"); /* EDI may contain reg # */
e("mov esi, d\n"); /* start from bottom and increment */
EmitLabel(".loop");
if (size == WORD_SIZE)
e("sub ecx, byte 4\n");
else
e("sub ecx, byte 8\n");
e("shr eax, byte 1\n"); /* mask bit->CF */
e("jnc short .skip\n"); /* not 1? skip this reg */
/* if word, sign extend to long */
if (size == WORD_SIZE)
{
if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3))
pcfetch ? ReadWordSXPC() : ReadWordSX(); /* PC-relative is special */
else
ReadWordSX();
}
else
{
if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3))
pcfetch ? ReadLongPC() : ReadLong();
else
ReadLong();
}
if (size == WORD_SIZE) e("add ebx, byte 2\n");
else e("add ebx, byte 4\n");
if ((ea >> 3) == 3)
{
RestoreReg("run", "edi");
e("mov [a+edi*4], ebx\n");
SaveReg("run", "edi");
}
e("mov [esi], edx\n"); /* store to reg */
EmitLabel(".skip");
e("add esi, byte 4\n"); /* next reg up */
e("cmp esi, d+16*4\n"); /* done? */
e("jne short .loop\n"); /* not done, keep looping */
if ((ea >> 3) == 3) /* just in case SaveReg()/RestoreReg() use stack */
RestoreReg("run", "edi");
RestoreReg("run", "esi");
RestoreReg("run", "eax");
switch (ea >> 3) /* timing for address mode */
{ case 2: base_timing = 12; break; /* (An) */
case 3: base_timing = 12; break; /* (An)+ */
case 5: base_timing = 16; break; /* (d16,An) */
case 6: base_timing = 18; break; /* (d8,An,Xn) */
case 7:
switch (ea & 7)
{ case 0: base_timing = 16; break; /* (xxx).W */
case 1: base_timing = 20; break; /* (xxx).L */
case 2: base_timing = 16; break; /* (d16,PC) */
case 3: base_timing = 18; break; /* (d16,PC,Xn */
} break;
}
}
EmitTiming(base_timing);
InstEnd();
return 1;
}
/* In case I screwed up MOVEM(), I have a back-up right here ;) */
int OldMOVEM(unsigned short op, char *mnem, unsigned base_timing)
{
/*
* If a non-post/pre-inc/decrement mode is used, the registers are
* transferred to the address, and that address is inc/decremented but the
* change is NOT reflected in the address register.
*
* If the mode is pre-dec or post-inc, it IS reflected. First, load up the
* value of the register, and then write it back and the end, but don't
* modify it while performing the operation, use a temp. X86 register
* instead.
*
* NOTE: The reg->mem mode should be fine, I've tested it -- but there
* might be some bugs.
*/
int dr = (op >> 10) & 1, size = UNKNOWN_SIZE;
unsigned ea = op & 0x3f;
switch ((op >> 6) & 1)
{ case 0: size = WORD_SIZE; break;
case 1: size = LONG_SIZE; break;
default: return 0; }
if (!NumDecodedEA(ea)) return 0;
if (!dr) /* reg->mem */
{
if (!CheckEA(ea, "001011111000")) return 0;
}
else /* mem->reg */
if (!CheckEA(ea, "001101111011")) return 0;
InstBegin(op, mnem, NumDecodedEA(ea));
if (!dr) /* reg->mem */
{
SaveReg("run", "eax"); /* save flags */
e("mov eax, [esi]\n"); /* AX=register list mask */
e("add esi, byte 2\n");
LoadControlEA(ea); /* EBX=store address */
SaveReg("run", "esi");
if ((ea >> 3) == 4) /* write back if (An)+,-(An) */
SaveReg("run", "edi"); /* EDI may contain reg # */
if ((ea >> 3) == 4) /* -(An) has reg mask reversed */
e("mov esi, d+15*4\n"); /* start from top and decrement */
else
e("mov esi, d\n");
EmitLabel(".loop");
if (size == WORD_SIZE)
e("sub ecx, byte 4\n");
else
e("sub ecx, byte 8\n");
e("shr eax, byte 1\n"); /* mask bit->CF */
e("jnc short .skip\n"); /* not 1? skip this reg */
if ((ea >> 3) == 4) /* -(An) */
{
if (size == WORD_SIZE) e("sub ebx, byte 2\n"); /* predec */
else e("sub ebx, byte 4\n");
}
e("mov edx, [esi]\n"); /* get reg to store... */
SaveReg("run", "ebx");
if (size == WORD_SIZE) WriteWord(); else WriteLong(); /* get mem */
RestoreReg("run", "ebx");
if (!((ea >> 3) == 4)) /* not predec, increment */
{
if (size == WORD_SIZE) e("add ebx, byte 2\n");
else e("add ebx, byte 4\n");
}
EmitLabel(".skip");
if ((ea >> 3) == 4) /* -(An) */
{
e("sub esi, byte 4\n"); /* next reg down */
e("cmp esi, d-4\n"); /* done? */
e("jne short .loop\n"); /* not done, keep looping */
}
else
{
e("add esi, byte 4\n"); /* next reg up */
e("cmp esi, d+16*4\n"); /* done? */
e("jne short .loop\n"); /* not done, keep looping */
}
if ((ea >> 3) == 4) /* write back if -(An) */
{
RestoreReg("run", "edi");
e("mov [a+edi*4], ebx\n");
}
RestoreReg("run", "esi");
RestoreReg("run", "eax");
switch (ea >> 3) /* timing for address mode */
{ case 2: base_timing = 8; break; /* (An) */
case 4: base_timing = 8; break; /* -(An) */
case 5: base_timing = 12; break; /* (d16,An) */
case 6: base_timing = 14; break; /* (d8,An,Xn) */
case 7:
switch (ea & 7)
{ case 0: base_timing = 12; break; /* (xxx).W */
case 1: base_timing = 16; break; /* (xxx).L */
} break;
}
}
else /* mem->reg */
{
SaveReg("run", "eax"); /* save flags */
e("mov eax, [esi]\n"); /* AX=register list mask */
e("add esi, byte 2\n");
LoadControlEA(ea); /* EBX=load address */
SaveReg("run", "esi");
if ((ea >> 3) == 3) /* write back if (An)+,-(An) */
SaveReg("run", "edi"); /* EDI may contain reg # */
e("mov esi, d\n"); /* start from bottom and increment */
EmitLabel(".loop");
if (size == WORD_SIZE)
e("sub ecx, byte 4\n");
else
e("sub ecx, byte 8\n");
e("shr eax, byte 1\n"); /* mask bit->CF */
e("jnc short .skip\n"); /* not 1? skip this reg */
/* if word, sign extend to long */
if (size == WORD_SIZE)
{
if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3))
pcfetch ? ReadWordSXPC() : ReadWordSX(); /* PC-relative is special */
else
ReadWordSX();
}
else
{
if ((ea >> 3) == 7 && ((ea & 7) == 2 || (ea & 7) == 3))
pcfetch ? ReadLongPC() : ReadLong();
else
ReadLong();
}
e("mov [esi], edx\n"); /* store to reg */
if (size == WORD_SIZE) e("add ebx, byte 2\n");
else e("add ebx, byte 4\n");
EmitLabel(".skip");
e("add esi, byte 4\n"); /* next reg up */
e("cmp esi, d+16*4\n"); /* done? */
e("jne short .loop\n"); /* not done, keep looping */
if ((ea >> 3) == 3) /* write back if (An)+ */
{
RestoreReg("run", "edi");
e("mov [a+edi*4], ebx\n");
}
RestoreReg("run", "esi");
RestoreReg("run", "eax");
switch (ea >> 3) /* timing for address mode */
{ case 2: base_timing = 12; break; /* (An) */
case 3: base_timing = 12; break; /* (An)+ */
case 5: base_timing = 16; break; /* (d16,An) */
case 6: base_timing = 18; break; /* (d8,An,Xn) */
case 7:
switch (ea & 7)
{ case 0: base_timing = 16; break; /* (xxx).W */
case 1: base_timing = 20; break; /* (xxx).L */
case 2: base_timing = 16; break; /* (d16,PC) */
case 3: base_timing = 18; break; /* (d16,PC,Xn */
} break;
}
}
EmitTiming(base_timing);
InstEnd();
return 1;
}
int MOVEUSP(unsigned short op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 8); /* handles all 8 possible An regs */
e("and edi, byte 7\n");
e("test byte [sr+1], 0x20\n");
e("jz near exception_privilege_violation\n");
if (op & 8) /* USP->An */
{
e("mov edx, [__sp]\n");
e("mov [a+edi*4], edx\n");
}
else /* An->USP */
{
e("mov edx, [a+edi*4]\n");
e("mov [__sp], edx\n");
}
EmitTiming(base_timing);
InstEnd();
return 1;
}
int Jxx(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned t;
if (!CheckEA(op & 0x3f, "001001111011")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
LoadControlEA(op & 0x3f);
if ((op & 0xffc0) == 0x4e80) /* JSR */
{
e("mov edx, esi\n"); /* EDX=PC to save to stack */
e("sub edx, ebp\n");
SaveReg("run", "edx");
}
e("mov esi, ebx\n");
UpdateFetchPtr();
if ((op & 0xffc0) == 0x4e80) /* JSR stuff again... */
{
RestoreReg("run", "edx"); /* remember? EDX contains PC to save */
e("sub dword [a+7*4], byte 4\n"); /* SP - 4 -> SP */
e("mov ebx, [a+7*4]\n");
WriteLong(); /* PC -> (SP) */
}
switch ((op >> 3) & 7) /* timing based on control addressing mode */
{ case 2: t = 8; break; /* (An) */
case 5: t = 10; break; /* (d16,An) */
case 6: t = 14; break; /* (d8,An,Xn) */
case 7:
switch (op & 7)
{ case 0: t = 10; break; /* (xxx).W */
case 1: t = 12; break; /* (xxx).L */
case 2: t = 10; break; /* (d16,pc) */
case 3: t = 14; break; /* (d8,pc,Xn) */
} break;
}
if ((op & 0xffc0) == 0x4e80) t += 8; /* JSR */
EmitTiming(t);
InstEnd();
return 1;
}
int LEA(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned t;
if (!CheckEA(op & 0x3f, "001001111011")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
LoadControlEA(op & 0x3f);
e("mov [a+%d*4], ebx\n", (op >> 9) & 7);
switch ((op >> 3) & 7) /* timing based on control addressing mode */
{ case 2: t = 4; break; /* (An) */
case 5: t = 8; break; /* (d16,An) */
case 6: t = 12; break; /* (d8,An,Xn) */
case 7:
switch (op & 7)
{ case 0: t = 8; break; /* (xxx).W */
case 1: t = 12; break; /* (xxx).L */
case 2: t = 8; break; /* (d16,pc) */
case 3: t = 12; break; /* (d8,pc,Xn) */
} break;
}
EmitTiming(t);
InstEnd();
return 1;
}
int BTST(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned t;
if (((op >> 6) & 7) != 4 && ((op >> 6) & 7) != 0) return 0;
if (op & 0x100) /* dynamic */
{
if (!CheckEA(op & 0x3f, "101111111111")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
if (((op >> 3) & 7) == 0) /* Dn is long */
{
t = 6;
e("and edi, byte 7\n");
e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */
e("and ebx, byte 31\n"); /* same as modulo 32 */
e("bt dword [d+edi*4], ebx\n"); /* bit->CF */
}
else /* mem is byte */
{
t = 4;
LoadFromEA(op & 0x3f, BYTE_SIZE, 0);
e("mov ebx, [d+%d*4]\n", (op >> 9) & 7); /* bit # */
e("and ebx, byte 7\n"); /* same as modulo 8 */
e("bt edx, ebx\n"); /* bit->CF */
}
}
else if (!(op & 0x100)) /* static */
{
if (!CheckEA(op & 0x3f, "101111111011")) return 0;
if (!NumDecodedEA(op & 0x3f)) return 0;
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
if (((op >> 3) & 7) == 0) /* Dn is long */
{
t = 10;
e("and edi, byte 7\n"); /* isolate Dn reg # */
e("mov ebx, [esi]\n"); /* get bit # */
e("and ebx, byte 31\n"); /* same as modulo 32 */
e("add esi, byte 2\n");
e("bt dword [d+edi*4], ebx\n"); /* bit->CF */
}
else /* mem is byte */
{
t = 8;
e("mov ebx, [esi]\n"); /* get bit # */
e("and ebx, byte 7\n"); /* same as modulo 8 */
e("add esi, byte 2\n");
SaveReg("run", "ebx");
LoadFromEA(op & 0x3f, BYTE_SIZE, 0);
RestoreReg("run", "ebx");
e("bt edx, ebx\n"); /* bit->CF */
}
}
e("setnc bl\n"); /* zero status of C->DH */
e("and ah, 0xbf\n"); /* get rid of old Z */
e("shl bl, 6\n"); /* put C in Z position */
e("or ah, bl\n"); /* set the new Z */
if (((op & 0x3f) & 7) == 0) /* Dn is long */
EmitTiming(t);
else /* mem is byte */
EmitTiming(t + TimingEA(op & 0x3f, BYTE_SIZE));
InstEnd();
return 1;
}
int TST(unsigned short op, char *mnem, unsigned base_timing)
{
int size;
switch ((op >> 6) & 3) /* set valid sizes */
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
if (!NumDecodedEA(op & 0x3f)) /* check if decoded this EA */
return 0;
if (!CheckEA(op & 0x3f, "101111111000"))
return 0; /* no An,#dat,(d16,pc),(d8,pc,Xn) for 68000 */
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
if (((op & 0x3f) >> 3) == 0) /* Dn shortcut */
{
e("and edi, byte 7\n");
e("mov edx, [d+edi*4]\n");
if (size == BYTE_SIZE)
e("test dl, dl\n");
else if (size == WORD_SIZE)
e("test dx, dx\n");
else if (size == LONG_SIZE)
e("test edx, edx\n");
}
else
{
LoadFromEA(op & 0x3f, size, 0);
if (size == BYTE_SIZE)
e("test dl, dl\n");
else if (size == WORD_SIZE)
e("test dx, dx\n");
else if (size == LONG_SIZE)
e("test edx, edx\n");
}
e("lahf\n");
e("xor al, al\n");
EmitTiming(base_timing + TimingEA(op & 0x3f, size));
InstEnd();
return 1;
}
int Bxx(unsigned short op, char *mnem, unsigned base_timing)
{
if ((op & 0xff) != 0 && (op & 0xff) != 1)
return 0;
switch (op & 0xff)
{ case 0: InstBegin(op, mnem, 1); break;
case 1: InstBegin(op, mnem, 255); break;
break; }
switch (op & 0xff)
{
case 0: /* 16-bit displacement */
if ((op & 0xff00) == 0x6100)
{
e("mov edx, 2\n");
e("add edx, esi\n");
}
e("movsx ebx, word [esi]\n");
e("add esi, ebx\n");
break;
case 1: /* 8-bit displacement -- XX01-XXFF */
if ((op & 0xff00) == 0x6100)
e("mov edx, esi\n");
e("and edi, 0xff\n");
e("mov ebx, edi\n");
e("movsx ebx, bl\n");
e("add esi, ebx\n");
break;
}
if (brafetch)
{
e("sub esi, ebp\n"); /* ESI=denormalized PC */
if ((op & 0xff00) == 0x6100) /* if BSR, save EDX */
SaveReg("run", "edx");
UpdateFetchPtr();
if ((op & 0xff00) == 0x6100)
RestoreReg("run", "edx");
}
if ((op & 0xff00) == 0x6100) /* BSR */
{
e("sub dword [a+7*4], byte 4\n"); /* SP - 4 -> SP */
e("sub edx, ebp\n"); /* EDX=PC of next instruction */
e("mov ebx, [a+7*4]\n");
WriteLong(); /* PC -> (SP) */
}
EmitTiming(base_timing);
InstEnd();
return 1;
}
int Bcc(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned cond = (op >> 8) & 0xf;
char mnem[4];
char *mnem_t[] = { NULL, NULL, "BHI", "BLS",
"BCC", "BCS", "BNE", "BEQ",
"BVC", "BVS", "BPL", "BMI",
"BGE", "BLT", "BGT", "BLE" };
if (!cond || cond == 1) return 0; /* these not available for Bcc */
strcpy(mnem, mnem_t[cond]); /* set mnemonic */
switch (op & 0xff)
{
case 0: /* 16-bit displacement */
InstBegin(op, mnem, 1); break;
case 1: /* 8-bit displacement -- XX01-XXFE */
InstBegin(op, mnem, 255); break;
default:
return 0;
}
switch (cond)
{
/* EAX:0000000000000000 NZ00 000C 0000 000V */
case 2: /* HI: !C & !Z */
e("test ah, byte 0x41\n");
e("jz short .do\n");
break;
case 3: /* LS: C | Z */
e("test ah, byte 0x41\n");
e("jnz short .do\n");
break;
case 4: /* CC: !C */
e("test ah, byte 1\n");
e("jz short .do\n");
break;
case 5: /* CS: C */
e("test ah, byte 1\n");
e("jnz short .do\n");
break;
case 6: /* NE: !Z */
e("test ah, byte 0x40\n");
e("jz short .do\n");
break;
case 7: /* EQ: Z */
e("test ah, byte 0x40\n");
e("jnz short .do\n");
break;
case 8: /* VC: !V */
e("test al, byte 1\n");
e("jz short .do\n");
break;
case 9: /* VS: V */
e("test al, byte 1\n");
e("jnz short .do\n");
break;
case 0xa: /* PL: !N */
e("test ah, byte 0x80\n");
e("jz short .do\n");
break;
case 0xb: /* MI: N */
e("test ah, byte 0x80\n");
e("jnz short .do\n");
break;
case 0xc: /* GE: N & V | !N & !V (N&V || !N&!V) */
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpe short .do\n");
break;
case 0xd: /* LT: N & !V | !N & V */
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpo short .do\n");
EmitLabel(".dont_do");
break;
case 0xe: /* GT: N & V & !Z | !N & !V & !Z */
/* if Z, then it is GE and we don't take this branch */
e("test ah, byte 0x40\n");
e("jnz short .dont_do\n");
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpe short .do\n");
EmitLabel(".dont_do");
break;
case 0xf: /* LE: Z | N & !V | !N & V */
e("test ah, 0x40\n");
e("jnz short .do\n");
e("mov bl, al\n");
e("mov bh, ah\n");
e("and bh, 0xfe\n");
e("or bl, bh\n");
e("test bl, byte 0x81\n");
e("jpo short .do\n");
break;
}
/* branch not taken */
switch (op & 0xff) /* look at this to figure out timing and increment */
{
case 0: /* 16-bit */
e("add esi, byte 2\n"); EmitTiming(12); break;
case 1: /* 8-bit */
EmitTiming(8); break;
}
InstEnd();
Align(4);
EmitLabel(".do");
switch (op & 0xff)
{
case 0: /* 16-bit */
e("movsx ebx, word [esi]\n");
e("add esi, ebx\n");
break;
case 1: /* 8-bit */
e("and edi, 0xff\n");
e("mov ebx, edi\n");
e("movsx ebx, bl\n");
e("add esi, ebx\n");
break;
}
if (brafetch)
{
e("sub esi, ebp\n"); /* ESI=denormalized PC */
UpdateFetchPtr();
}
EmitTiming(10);
InstEnd();
return 1;
}
int Arithmetic(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned t, ea = op & 0x3f, opr_code = (op >> 12),
opmode = (op >> 6) & 7;
int size = UNKNOWN_SIZE;
char mnem[4];
char opr[4], *size_s[] = { "byte", "word", "dword" },
*edx[] = { "dl", "dx", "edx" };
if (opmode & 4) /* EA == destination */
{
switch (opr_code)
{
case 8: /* OR */
case 9: /* SUB */
case 0xc: /* AND */
case 0xd: if (!CheckEA(ea, "001111111000")) return 0; /* ADD */
break;
case 0xb: if (!CheckEA(ea, "101111111000")) return 0; /* EOR */
break;
default: return 0; }
}
else /* EA == source */
{
switch (opr_code)
{
case 0xd: /* ADD */
case 0xb: /* CMP */
case 9: if (!CheckEA(ea, "111111111111")) return 0; /* SUB */
if ((ea >> 3) == 1 && (opmode & 3) == 0)
return 0; /* byte access to An not allowed! */
break;
case 8: /* OR */
case 0xc: if (!CheckEA(ea, "101111111111")) return 0; /* AND */
break;
default: return 0; }
}
if (!NumDecodedEA(ea)) /* check if decoded this EA */
return 0;
switch (opmode & 3) /* set allowed sizes */
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
switch (opr_code) /* timing */
{
case 0xd: /* ADD */
case 9: /* SUB */
if (size == BYTE_SIZE || size == WORD_SIZE)
{
if ((ea >> 3) == 1) t = 8;
else if ((ea >> 3) == 0) t = 4;
else t = 8;
}
else
{
if ((ea >> 3) == 1) t = 6;
else if ((ea >> 3) == 0) t = 6;
else t = 12;
if ((ea >> 3) == 0 || (ea >> 3) == 1 || ea == 0x3c)
t += 2; /* if reg direct or immediate, timing of 6 is
increased to 8 */
} break;
case 0xb:
if (!(opmode & 4)) /* CMP */
{
if (size == BYTE_SIZE || size == WORD_SIZE)
{
if ((ea >> 3) == 1) t = 6;
else if ((ea >> 3) == 0) t = 4;
}
else
{
if ((ea >> 3) == 1) t = 6;
else if ((ea >> 3) == 0) t = 6;
}
}
else /* EOR */
{
if (size == BYTE_SIZE || size == WORD_SIZE)
{
if ((ea >> 3) == 0) t = 4;
else t = 8;
}
else
{
if ((ea >> 3) == 0) t = 8;
else t = 12;
} break;
}
case 0xc: /* AND */
if (size == BYTE_SIZE || size == WORD_SIZE)
{
if ((ea >> 3) == 0) t = 4;
else t = 8;
}
else
{
if ((ea >> 3) == 0) t = 6;
else t = 12;
if ((ea >> 3) == 0 || (ea >> 3) == 1 || ea == 0x3c)
t += 2; /* if reg direct or immediate, timing of 6 is
increased to 8 */
} break;
case 0x8: /* OR */
if (size == BYTE_SIZE || size == WORD_SIZE)
{
if ((ea >> 3) == 0) t = 4;
else t = 8;
}
else
{
if ((ea >> 3) == 0) t = 6;
else t = 12;
if ((ea >> 3) == 0 || (ea >> 3) == 1 || ea == 0x3c)
t += 2; /* if reg direct or immediate, timing of 6 is
increased to 8 */
} break;
}
/* set mnem and opr */
switch (opr_code)
{ case 0xd: strcpy(mnem, "ADD"); strcpy(opr, "add"); break;
case 0xc: strcpy(mnem, "AND"); strcpy(opr, "and"); break;
case 0xb:
if (opmode & 4) { strcpy(mnem, "EOR"); strcpy(opr, "xor"); }
else { strcpy(mnem, "CMP"); strcpy(opr, "cmp"); }
break;
case 8: strcpy(mnem, "OR"); strcpy(opr, "or"); break;
case 9: strcpy(mnem, "SUB"); strcpy(opr, "sub"); break;
default: return 0;
}
InstBegin(op, mnem, NumDecodedEA(ea));
if (opmode & 4) /* op Dn,ea */
{
if ((ea >> 3) == 0 || (ea >> 3) == 1) /* Dn, An shortcut */
{
char r[2] = { 'd', 'a' };
int i = ea >> 3;
e("and edi, byte 7\n");
e("mov edx, [d+(%d*4)]\n", (op >> 9) & 7);
e("%s %s [%c+(edi*4)], %s\n", opr, size_s[opmode & 3], r[i],
edx[opmode & 3]);
e("lahf\n");
switch ((op >> 12) & 0xf) /* flags calculations */
{ case 0xc: e("xor al, al\n"); break; /* AND */
case 0xd: e("setc byte [x]\n");
e("seto al\n"); break; /* ADD */
case 0xb:
if (opmode & 4) e("xor al, al\n"); /* EOR */
else e("seto al\n"); /* CMP */
break;
case 8: e("xor al, al\n"); break; /* OR */
case 9: e("setc byte [x]\n");
e("seto al\n"); break; /* SUB */ }
}
else
{
LoadFromEA(ea, size, 0);
e("%s %s, [d+(%d*4)]\n", opr, edx[opmode & 3], (op >> 9) & 7);
e("lahf\n");
switch ((op >> 12) & 0xf) /* flags calculations */
{ case 0xc: e("xor al, al\n"); break; /* AND */
case 0xd: e("setc byte [x]\n");
e("seto al\n"); break; /* ADD */
case 0xb:
if (opmode & 4) e("xor al, al\n"); /* EOR */
else e("seto al\n"); /* CMP */
break;
case 8: e("xor al, al\n"); break; /* OR */
case 9: e("setc byte [x]\n");
e("seto al\n"); break; /* SUB */ }
if(!(((op >> 12) & 0xf) == 0xb && !(opmode & 4))) // don't write if CMP
{
switch (size)
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break;
}
}
else
{
e("xor edi, edi\n"); // the write functions normally handle this
}
}
}
else /* op ea,Dn */
{
LoadFromEA(ea, size, 0);
e("%s [d+(%d*4)], %s\n", opr, (op >> 9) & 7, edx[opmode & 3]);
e("lahf\n");
switch ((op >> 12) & 0xf) /* flags calculations */
{ case 0xc: e("xor al, al\n"); break; /* AND */
case 0xd: e("setc byte [x]\n");
e("seto al\n"); break; /* ADD */
case 0xb:
if (opmode & 4) e("xor al, al\n"); /* EOR */
else e("seto al\n"); /* CMP */
break;
case 8: e("xor al, al\n"); break; /* OR */
case 9: e("setc byte [x]\n");
e("seto al\n"); break; /* SUB */ }
}
EmitTiming(t);
InstEnd();
return 1;
}
int ArithmeticI(unsigned short op, char *mnem_unused, unsigned base_timing)
{
unsigned t = base_timing, ea = op & 0x3f; /* base_timing = 8 */
int size = UNKNOWN_SIZE;
char mnem[5];
char opr[5], *size_s[] = { "byte", "word", "dword" },
*edx[] = { "dl", "dx", "edx" };
if (!CheckEA(ea, "101111111000"))
return 0;
if (!NumDecodedEA(ea)) /* check if decoded this EA */
return 0;
switch ((op >> 6) & 3) /* set allowed sizes */
{ case 0: size = BYTE_SIZE; break;
case 1: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
switch ((op >> 8) & 0xf) /* set operation, mnem */
{ case 2: strcpy(mnem, "ANDI"); strcpy(opr, "and"); break;
case 6: strcpy(mnem, "ADDI"); strcpy(opr, "add"); break;
case 0xc: strcpy(mnem, "CMPI"); strcpy(opr, "cmp"); break;
case 0xa: strcpy(mnem, "EORI"); strcpy(opr, "xor"); break;
case 0: strcpy(mnem, "ORI"); strcpy(opr, "or"); break;
case 4: strcpy(mnem, "SUBI"); strcpy(opr, "sub"); break;
default: return 0; }
/* all but CMPI,ANDI take 8+8 cycles for long sizes */
if (((op >> 8) & 0xf) != 0xc && ((op >> 8) & 0xf) != 2 && size == LONG_SIZE)
t += 8;
else if (((op >> 8) & 0xf) == 0xc) t += 6;
InstBegin(op, mnem, NumDecodedEA(ea));
if (((ea >> 3) & 7) == 0) /* Dn is treated differently */
{
e("and edi, byte 7\n");/* get reg # */
e("mov edx, [esi]\n"); /* get imm data */
if (size == LONG_SIZE)
e("ror edx, byte 16\n"); /* prep. the long-word */
e("%s %s [d+edi*4], %s\n", opr, size_s[(op >> 6) & 3], edx[(op >> 6) & 3]);
e("lahf\n");
switch ((op >> 8) & 0xf) /* flags calculations */
{ case 2: e("xor al, al\n"); break; /* ANDI */
case 6: e("setc byte [x]\n");
e("seto al\n"); break; /* ADDI */
case 0xc: e("seto al\n"); break; /* CMPI */
case 0xa: e("xor al, al\n"); break; /* EORI */
case 0: e("xor al, al\n"); break; /* ORI */
case 4: e("setc byte [x]\n");
e("seto al\n"); break; /* SUBI */ }
if (size != LONG_SIZE)
e("add esi, byte 2\n");
else
e("add esi, byte 4\n");
}
else
{
SaveReg("run", "esi"); /* address of immediate data */
switch (size) /* make ESI point to EA data */
{ case BYTE_SIZE:
case WORD_SIZE: e("add esi, byte 2\n"); break;
case LONG_SIZE: e("add esi, byte 4\n"); break;
}
LoadFromEA(ea, size, 0);
RestoreRegTo("run", "esi", "edi"); /* EDI=address of immediate data */
/* if byte or word, perform operation directly from memory */
if (size == BYTE_SIZE || size == WORD_SIZE)
{
e("%s %s, [edi]\n", opr, edx[(op >> 6) & 3]);
e("lahf\n");
switch ((op >> 8) & 0xf) /* flags calculations */
{ case 2: e("xor al, al\n"); break; /* ANDI */
case 6: e("setc byte [x]\n");
e("seto al\n"); break; /* ADDI */
case 0xc: e("seto al\n"); break; /* CMPI */
case 0xa: e("xor al, al\n"); break; /* EORI */
case 0: e("xor al, al\n"); break; /* ORI */
case 4: e("setc byte [x]\n");
e("seto al\n"); break; /* SUBI */ }
}
else /* long */
{
e("mov edi, [edi]\n");
e("ror edi, byte 16\n"); /* word-swap long-words... */
e("%s %s, edi\n", opr, edx[(op >> 6) & 3]);
e("lahf\n");
switch ((op >> 8) & 0xf) /* flags calculations */
{ case 2: e("xor al, al\n"); break; /* ANDI */
case 6: e("setc byte [x]\n");
e("seto al\n"); break; /* ADDI */
case 0xc: e("seto al\n"); break; /* CMPI */
case 0xa: e("xor al, al\n"); break; /* EORI */
case 0: e("xor al, al\n"); break; /* ORI */
case 4: e("setc byte [x]\n");
e("seto al\n"); break; /* SUBI */ }
}
if(((op >> 8) & 0x0F) != 0x0C)
{
switch (size) /* write back to mem */
{ case BYTE_SIZE: WriteByte(); break;
case WORD_SIZE: WriteWord(); break;
case LONG_SIZE: WriteLong(); break;
}
}
else
{
e("xor edi, edi\n");
}
}
EmitTiming(t + TimingEA(ea, size));
InstEnd();
return 1;
}
int MOVE(unsigned short op, char *mnem, unsigned base_timing)
{
unsigned src = op & 0x3f, dest = (op >> 6) & 0x3f;
int size = UNKNOWN_SIZE;
dest = ((dest & 0x7) << 3) | ((dest >> 3) & 0x7);
if (!CheckEA(src, "111111111111") || !CheckEA(dest, "101111111000"))
return 0;
if (!NumDecodedEA(src)) /* check if decoded this source EA */
return 0;
switch ((op >> 12) & 3) /* set allowed sizes */
{ case 1: size = BYTE_SIZE; break;
case 3: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
if (size == BYTE_SIZE && ((src >> 3) & 7) == 1)
return 0; /* no An byte accesses allowed */
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
LoadFromEA(src, size, 0);
switch (size)
{ case BYTE_SIZE: e("test dl, dl\n"); break;
case WORD_SIZE: e("test dx, dx\n"); break;
case LONG_SIZE: e("test edx, edx\n"); break; }
e("lahf\n");
e("xor al, al\n"); /* V=always cleared */
if (src == dest)
StoreToEA(dest, size, 1);
else
StoreToEA(dest, size, 0);
EmitTiming(base_timing + TimingEA(src, size) + TimingEA(dest, size));
InstEnd();
return 1;
}
int MOVEQ(unsigned op, char *mnem, unsigned base_timing)
{
InstBegin(op, mnem, 256); /* 256 possible immediate data combinations */
e("mov ebx, edi\n"); /* opcode is in edi */
e("movsx ebx, bl\n"); /* sign extend */
e("mov [d+%d*4], ebx\n", (op >> 9) & 7);
e("test bl, bl\n"); /* set N,Z,C appropriately */
e("lahf\n"); /* get changes */
e("xor al, al\n"); /* V is cleared */
EmitTiming(base_timing);
InstEnd();
return 1;
}
int MOVEA(unsigned short op, char *mnem, unsigned base_timing)
{
int size = UNKNOWN_SIZE;
if (!CheckEA(op & 0x3f, "111111111111")) /* check EA mode */
return 0;
if (!NumDecodedEA(op & 0x3f)) /* check if decoded this EA */
return 0;
switch ((op >> 12) & 3) /* set allowed sizes */
{ case 3: size = WORD_SIZE; break;
case 2: size = LONG_SIZE; break;
default: return 0; }
InstBegin(op, mnem, NumDecodedEA(op & 0x3f));
LoadFromEA(op & 0x3f, size, 1); /* load */
e("mov [a+%d*4], edx\n", (op >> 9) & 7); /* store */
EmitTiming(TimingEA(op & 0x3f, size) + base_timing);
InstEnd();
return 1;
}
/*****************************************************************************
* Instruction Emission */
void EmitInstructions()
{
unsigned i, j, k, l, n;
/* TST */
n = 0;
for (i = 0; i <= 2; i++)
for (j = 0; j <= 0x3f; j++)
n += TST((i << 6) + j + 0x4a00, "TST", 4);
printf("TST:\t\t%d\n", n);
/* Bcc */
n = 0;
for (i = 0; i <= 0xf; i++)
for (j = 0; j < 0xff; j++)
n += Bcc((i << 8) + j + 0x6000, "B??", 0);
printf("Bcc:\t\t%d\n", n);
/* MOVE */
n = 0;
for (i = 1; i <= 3; i++) /* size */
for (j = 0; j <= 0x3f; j++) /* dest. EA */
for (k = 0; k <= 0x3f; k++) /* source EA */
n += MOVE((i << 12) + (j << 6) + k + 0x0000, "MOVE", 4);
printf("MOVE:\t\t%d\n", n);
/* ADD, AND, CMP, EOR, OR, SUB -- "Arithmetic" */
n = 0;
for (i = 8; i <= 0xd; i++) /* operation */
for (j = 0; j <= 7; j++) /* Dn */
for (k = 0; k <= 6; k++) /* opmode */
for (l = 0; l <= 0x3f; l++) /* EA */
n += Arithmetic((i << 12) + (j << 9) + (k << 6) + l, "????", 0);
printf("ADD, AND, CMP, EOR, OR, SUB: %d\n", n);
/* ADDI, ANDI, CMPI, EORI, ORI, SUBI -- "ArithmeticI" */
n = 0;
for (i = 0; i <= 0xc; i++) /* operation */
for (j = 0; j <= 2; j++) /* size */
for (k = 0; k <= 0x3f; k++) /* EA */
n += ArithmeticI((i << 8) + (j << 6) + k + 0x0000, "????I", 8);
printf("ADDI, ANDI, CMPI, EORI, ORI, SUBI: %d\n", n);
/* DBcc */
n = 0;
for (i = 0; i <= 0xf; i++) /* condition */
n += DBcc((i << 8) + 0x50c8, "DB??", 0);
printf("DBcc:\t\t%d\n", n);
/* BTST */
n = 0;
for (i = 0; i <= 7; i++) /* register */
for (j = 0; j <= 0x3f; j++) /* EA */
n += BTST((i << 9) + j + 0x0100, "BTST", 0);
for (i = 0; i <= 0x3f; i++) /* EA */
n += BTST(i + 0x0800, "BTST", 0);
printf("BTST:\t\t%d\n", n);
/* ADDQ, SUBQ -- "ArithmeticQ" */
n = 0;
for (i = 0; i <= 7; i++) /* data */
for (j = 0; j <= 1; j++) /* ADDQ/SUBQ */
for (k = 0; k <= 2; k++) /* size */
for (l = 0; l <= 0x3f; l++) /* EA */
n += ArithmeticQ((i << 9) + (j << 8) + (k << 6) + l + 0x5000, "???Q", 4);
printf("ADDQ, SUBQ:\t%d\n", n);
/* BRA */
n = 0;
for (i = 0; i <= 0xff; i++)
n += Bxx(i + 0x6000, "BRA", 10);
printf("BRA:\t\t%d\n", n);
/* LEA */
n = 0;
for (i = 0; i <= 7; i++) /* register */
for (j = 0; j <= 0x3f; j++) /* EA */
n += LEA((i << 9) + 0x41c0 + j, "LEA", 0);
printf("LEA:\t\t%d\n", n);
/* RTS */
RTS(0x4e75, "RTS", 16);
printf("RTS:\t\t1\n");
/* MOVEA */
n = 0;
for (i = 2; i <= 3; i++) /* size */
for (j = 0; j <= 7; j++) /* dest. reg */
for (k = 0; k <= 0x3f; k++) /* source EA */
n += MOVEA((i << 12) + (j << 9) + k + 0x0040, "MOVEA", 4);
printf("MOVEA:\t\t%d\n", n);
/* ADDA */
n = 0;
for (i = 3; i <= 7; i++) /* size */
for (j = 0; j <= 7; j++) /* dest. reg */
for (k = 0; k <= 0x3f; k++) /* source EA */
n += ArithmeticA((i << 6) + (j << 9) + k + 0xd000, "ADDA", 4);
printf("ADDA:\t\t%d\n", n);
/* MOVEQ */
n = 0;
for (i = 0; i < 8; i++)
n += MOVEQ((i << 9) + 0x7000, "MOVEQ", 4);
printf("MOVEQ:\t\t%d\n", n);
/* JSR */
n = 0;
for (i = 0; i <= 0x3f; i++) /* register */
n += Jxx(i + 0x4e80, "JSR", 0);
printf("JSR:\t\t%d\n", n);
/* LSL, LSR */
n = 0;
for (i = 0; i <= 7; i++) /* count/register */
for (j = 0; j <= 1; j++) /* direction */
for (k = 0; k <= 2; k++) /* size */
for (l = 0; l <= 1; l++) /* immediate/reg */
n += LSxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe008, "LS?", 0);
for (i = 0; i <= 1; i++) /* direction */
for (j = 0; j <= 0x3f; j++) /* EA */
n += LSxMem((i << 8) + j + 0xe2c0, "LS?", 8);
printf("LSL, LSR:\t%d\n", n);
/* SWAP */
SWAP(0x4840, "SWAP", 4);
printf("SWAP:\t\t1\n");
/* BSR */
n = 0;
for (i = 0; i <= 0xff; i++)
n += Bxx(i + 0x6100, "BSR", 18);
printf("BSR:\t\t%d\n", n);
/* EXT */
n = 0;
for (i = 2; i <= 7; i++)
n += EXT((i << 6) + 0x4800, "EXT", 4);
printf("EXT:\t\t%d\n", n);
/* CLR */
n = 0;
for (i = 0; i <= 2; i++) /* size */
for (j = 0; j <= 0x3f; j++) /* EA */
n += CLR((i << 6) + j + 0x4200, "CLR", 0);
printf("CLR:\t\t%d\n", n);
/* MOVEM */
n = 0;
for (i = 0; i <= 1; i++) /* direction */
for (j = 0; j <= 1; j++) /* size */
for (k = 0; k <= 0x3f; k++) /* EA */
n += MOVEM((i << 10) + (j << 6) + k + 0x4880, "MOVEM", 0);
printf("MOVEM:\t\t%d\n", n);
/* MOVE to SR */
n = 0;
for (i = 0; i <= 0x3f; i++)
n += MOVEtoSR(i + 0x46c0, "MOVE to SR", 12);
printf("MOVE to SR:\t%d\n", n);
/* NOP */
NOP(0x4e71, "NOP", 4);
printf("NOP:\t\t1\n");
/* JMP */
n = 0;
for (i = 0; i <= 0x3f; i++) /* register */
n += Jxx(i + 0x4ec0, "JMP", 0);
printf("JMP:\t\t%d\n", n);
/* CMPA */
n = 0;
for (i = 0; i <= 7; i++) /* register */
for (j = 3; j <= 7; j++) /* opmode */
for (k = 0; k <= 0x3f; k++) /* EA */
n += CMPA((i << 9) + (j << 6) + k + 0xb000, "CMPA", 0);
printf("CMPA:\t\t%d\n", n);
/* ASL, ASR */
n = 0;
for (i = 0; i <= 7; i++) /* count/reg */
for (j = 0; j <= 1; j++) /* direction */
for (k = 0; k <= 2; k++) /* size */
for (l = 0; l <= 1; l++) /* immediate/reg */
n += ASxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe000, "AS?", 0);
for (i = 0; i <= 1; i++) /* direction */
for (j = 0; j <= 0x3f; j++) /* EA */
n += ASxMem((i << 8) + j + 0xe0c0, "AS?", 8);
printf("ASL, ASR:\t%d\n", n);
/* MULS */
n = 0;
for (i = 0; i <= 7; i++) /* register */
for (j = 0; j <= 0x3f; j++) /* EA */
n += MULS((i << 9) + j + 0xc1c0, "MULS", 70);
printf("MULS:\t\t%d\n", n);
/* RTE */
RTE(0x4e73, "RTE", 20);
printf("RTE:\t\t1\n");
/* NEG, NEGX */
n = 0;
for (i = 0; i <= 1; i++) /* NEG/NEGX */
for (j = 0; j <= 2; j++) /* size */
for (k = 0; k <= 0x3f; k++) /* EA */
n += NEGx((i << 10) + (j << 6) + k + 0x4000, "NEG?", 0);
printf("NEG, NEGX:\t%d\n", n);
/* ROL, ROR */
n = 0;
for (i = 0; i <= 7; i++) /* count/register */
for (j = 0; j <= 1; j++) /* direction */
for (k = 0; k <= 2; k++) /* size */
for (l = 0; l <= 1; l++) /* immediate/reg */
n += ROxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe018, "RO?", 0);
for (i = 0; i <= 1; i++) /* direction */
for (j = 0; j <= 0x3f; j++) /* EA */
n += ROxMem((i << 8) + j + 0xe6c0, "RO?", 8);
printf("ROL, ROR:\t%d\n", n);
/* BCLR */
n = 0;
for (i = 0; i <= 7; i++) /* register */
for (j = 0; j <= 0x3f; j++) /* EA */
n += BCLR((i << 9) + j + 0x0180, "BCLR", 0);
for (i = 0; i <= 0x3f; i++) /* EA */
n += BCLR(i + 0x0880, "BCLR", 0);
printf("BCLR:\t\t%d\n", n);
/* NOT */
n = 0;
for (i = 0; i <= 2; i++)
for (j = 0; j <= 0x3f; j++)
n += NOT((i << 6) + j + 0x4600, "NOT", 0);
printf("NOT:\t\t%d\n", n);
/* MOVE USP */
n = 0;
for (i = 0; i <= 1; i++)
n += MOVEUSP((i << 3) + 0x4e60, "MOVE USP", 4);
printf("MOVE USP:\t%d\n", n);
/* ROXL, ROXR */
n = 0;
for (i = 0; i <= 7; i++) /* count/register */
for (j = 0; j <= 1; j++) /* direction */
for (k = 0; k <= 2; k++) /* size */
for (l = 0; l <= 1; l++) /* immediate/reg */
n += ROXxReg((i << 9) + (j << 8) + (k << 6) + (l << 5) + 0xe010, "ROX?", 0);
for (i = 0; i <= 1; i++) /* direction */
for (j = 0; j <= 0x3f; j++) /* EA */
n += ROXxMem((i << 8) + j + 0xe4c0, "ROX?", 8);
printf("ROXL, ROXR:\t%d\n", n);
/* BSET */
n = 0;
for (i = 0; i <= 7; i++) /* register */
for (j = 0; j <= 0x3f; j++) /* EA */
n += BSET((i << 9) + j + 0x01c0, "BSET", 0);
for (i = 0; i <= 0x3f; i++) /* EA */
n += BSET(i + 0x08c0, "BSET", 0);
printf("BSET:\t\t%d\n", n);
/* MOVE from SR */
/*
* The 68000 and 68008 "MOVE from SR" is not privileged. But for the 68010
* and higher, it is.
*/
n = 0;
for (i = 0; i <= 0x3f; i++)
n += MOVEfromSR(i + 0x40c0, "MOVE from SR", 0);
printf("MOVE from SR:\t%d\n", n);
/* SUBA */
n = 0;
for (i = 3; i <= 7; i++) /* size */
for (j = 0; j <= 7; j++) /* dest. reg */
for (k = 0; k <= 0x3f; k++) /* source EA */
n += ArithmeticA((i << 6) + (j << 9) + k + 0x9000, "SUBA", 4);
printf("SUBA:\t\t%d\n", n);
/* DIVS */
n = 0;
for (i = 0; i <= 7; i++) /* register */
for (j = 0; j <= 0x3f; j++) /* EA */
n += DIVS((i << 9) + j + 0x81c0, "DIVS", 158);
printf("DIVS:\t\t%d\n", n);
/* MULU */
n = 0;
for (i = 0; i <= 7; i++) /* register */
for (j = 0; j <= 0x3f; j++) /* EA */
n += MULU((i << 9) + j + 0xc0c0, "MULU", 70);
printf("MULU:\t\t%d\n", n);
/* ADDX */
n = 0;
for (i = 0; i <= 7; i++) /* Rx */
for (j = 0; j <= 2; j++) /* size */
for (k = 0; k <= 1; k++) /* R/M */
n += ADDX((i << 9) + (j << 6) + (k << 3) + 0xd100, "ADDX", 0);
printf("ADDX:\t\t%d\n", n);
/* Scc */
n = 0;
for (i = 0; i <= 0xf; i++) /* condition */
for (j = 0; j <= 0x3f; j++) /* EA */
n += Scc((i << 8) + j + 0x50c0, "S??", 0);
printf("Scc:\t\t%d\n", n);
/* ORI to SR */
ORItoSR(0x007c, "ORI to SR", 20);
printf("ORI to SR:\t1\n");
/* MOVE to CCR */
n = 0;
for (i = 0; i <= 0x3f; i++)
n += MOVEtoCCR(i + 0x44c0, "MOVE to CCR", 12);
printf("MOVE to CCR:\t%d\n", n);
/* EXG */
n = 0;
for (i = 0; i <= 7; i++) /* Rx */
for (j = 0; j <= 0x11; j++) /* opmode */
n += EXG((i << 9) + (j << 3) + 0xc100, "EXG", 6);
printf("EXG:\t\t%d\n", n);
/* UNLK */
UNLK(0x4e58, "UNLK", 12);
printf("UNLK:\t\t1\n");
/* LINK */
LINK(0x4e50, "LINK", 16); /* word */
printf("LINK:\t\t1\n");
/* DIVU */
n = 0;
for (i = 0; i <= 7; i++) /* register */
for (j = 0; j <= 0x3f; j++) /* EA */
n += DIVU((i << 9) + j + 0x80c0, "DIVU", 144);
printf("DIVU:\t\t%d\n", n);
/* ANDI to CCR */
ANDItoCCR(0x023c, "ANDI to CCR", 20);
printf("ANDI to CCR:\t1\n");
/* PEA */
n = 0;
for (i = 0; i <= 0x3f; i++)
n += PEA(i + 0x4840, "PEA", 0);
printf("PEA:\t\t%d\n", n);
/* ANDI to SR */
ANDItoSR(0x027c, "ANDI to SR", 20);
printf("ANDI to SR:\t%d\n", n);
/* BCHG */
n = 0;
for (i = 0; i <= 7; i++) /* register */
for (j = 0; j <= 0x3f; j++) /* EA */
n += BCHG((i << 9) + j + 0x0140, "BCHG", 0);
for (i = 0; i <= 0x3f; i++) /* EA */
n += BCHG(i + 0x0840, "BCHG", 0);
printf("BCHG:\t\t%d\n", n);
/* ORI to CCR */
ORItoCCR(0x003c, "ORI to CCR", 20);
printf("ORI to CCR:\t1\n");
/* RTR */
RTR(0x4e77, "RTR", 20);
printf("RTR:\t\t1\n");
/* TRAP */
TRAP(0x4e40, "TRAP", 34);
printf("TRAP:\t\t1\n");
/* SBCD */
n = 0;
for (i = 0; i <= 7; i++) /* Rx */
for (j = 0; j <= 1; j++) /* R/M */
n += SBCD((i << 9) + (j << 3) + 0x8100, "SBCD", 0);
printf("SBCD:\t\t%d\n", n);
/* SUBX */
n = 0;
for (i = 0; i <= 7; i++) /* Rx */
for (j = 0; j <= 2; j++) /* size */
for (k = 0; k <= 1; k++) /* R/M */
n += SUBX((i << 9) + (j << 6) + (k << 3) + 0x9100, "SUBX", 0);
printf("SUBX:\t\t%d\n", n);
/* ABCD */
n = 0;
for (i = 0; i <= 7; i++) /* Rx */
for (j = 0; j <= 1; j++) /* R/M */
n += ABCD((i << 9) + (j << 3) + 0xc100, "ABCD", 0);
printf("ABCD:\t\t%d\n", n);
/* CMPM */
n = 0;
for (i = 0; i <= 7; i++) /* Ax */
for (j = 0; j <= 2; j++) /* size */
n += CMPM((i << 9) + (j << 6) + 0xb108, "CMPM", 0);
printf("CMPM:\t\t%d\n", n);
/* MOVEP */
n = 0;
for (i = 0; i <= 7; i++) /* Dx */
for (j = 0; j <= 7; j++) /* opmode */
n += MOVEP((i << 9) + (j << 6) + 0x0008, "MOVEP", 0);
printf("MOVEP:\t\t%d\n", n);
/*
* The following have no profile data associated with them, as far as my
* tests have shown.
*/
/* BKPT */
if (mpu == 68010)
{
n = BKPT(0x4848, "BKPT", 45);
printf("BKPT:\t\t%d\n", n);
}
/* CHK */
n = 0;
for (i = 0; i <= 7; i++) /* register */
for (j = 0; j <= 3; j++) /* size */
for (k = 0; k <= 0x3f; k++) /* EA */
n += CHK((i << 9) + (j << 7) + k + 0x4000, "CHK", 10);
printf("CHK:\t\t%d\n", n);
/* EORI to CCR */
EORItoCCR(0x0a3c, "EORI to CCR", 20);
printf("EORI to CCR:\t1\n");
/* EORI to SR */
EORItoSR(0x0a7c, "EORI to SR", 20);
printf("EORI to SR:\t1\n");
/* ILLEGAL */
InstBegin(0x4afc, "ILLEGAL", 1);
e("jmp near exception_illegal_instruction\n");
printf("ILLEGAL:\t1\n");
/* MOVEC */
if (mpu == 68010)
{
n = MOVEC(0x4e7a, "MOVEC", 0);
printf("MOVEC:\t\t%d\n", n);
}
/* MOVE from CCR -- 68010, 68020, 68030, 68040, CPU32 */
if (mpu == 68010)
{
n = 0;
for (i = 0; i <= 0x3f; i++)
n += MOVEfromCCR(i + 0x42c0, "MOVE from CCR", 0);
printf("MOVE from CCR:\t%d\n", n);
}
/* NBCD */
n = 0;
for (i = 0; i <= 0x3f; i++) /* EA */
n += NBCD(i + 0x4800, "NBCD", 0);
printf("NBCD:\t\t%d\n", n);
/* RESET */
RESET(0x4e70, "RESET", 132);
printf("RESET:\t\t1\n");
/* RTD */
if (mpu == 68010)
{
RTD(0x4e74, "RTD", 16);
printf("RTD:\t\t1\n");
}
/* STOP */
STOP(0x4e72, "STOP", 4);
printf("STOP:\t\t1\n");
/* TAS */
n = 0;
for (i = 0; i <= 0x3f; i++)
n += TAS(i + 0x4ac0, "TAS", 0);
printf("TAS:\t\t%d\n", n);
/* TRAPV */
TRAPV(0x4e76, "TRAPV", 0);
printf("TRAPV:\t\t1\n");
if (illegal) /* emulate Line 1010 and Line 1111 exceptions */
{
/* Line 1010 */
InstBegin(0xa000, "Line 1010 Emulator", 4096);
e("jmp near exception_line_1010_emulator\n");
/* Line 1111 */
InstBegin(0xf000, "Line 1111 Emulator", 4096);
e("jmp near exception_line_1111_emulator\n");
num_handlers -= 2; /* don't count these as instruction handlers */
}
/* I_Invalid: Invalid instruction */
EmitLabel("I_Invalid");
if (illegal) /* emulate illegal instruction exceptions */
e("jmp near exception_illegal_instruction\n");
else
{
e("sub esi, byte 2\n"); /* back up to the bad instruction */
e("jmp invalid_error\n"); /* invalid instruction error */
}
}
void EmitExceptions()
{
/*
* exception_chk:
* exception_divide_by_zero:
* Assume the PC points to the instruction AFTER the instruction which
* caused the trap.
*/
Align(4);
EmitLabel("exception_chk");
e("push dword 6*4\n"); /* CHK exception vector */
e("jmp short do_exception\n");
Align(4);
EmitLabel("exception_divide_by_zero");
e("push dword 5*4\n"); /* division by zero exception vector */
e("jmp short do_exception\n");
/*
* exception_privilege_violation:
* exception_illegal_instruction:
* exception_line_1010_emulator:
* exception_line_1111_emulator:
* Assume the PC points to the word AFTER the instruction which caused the
* trap. The value 2 will be subtracted from the PC in order to make them
* point AT the instruction which caused the trap, as this is how the
* exceptions work.
*/
Align(4);
EmitLabel("exception_privilege_violation");
e("push dword 8*4\n"); /* privilege violation exception vector */
e("jmp short do_exception_fix_pc\n");
Align(4);
EmitLabel("exception_illegal_instruction");
e("push dword 4*4\n"); /* illegal instruction exception vector */
e("jmp short do_exception_fix_pc\n");
Align(4);
EmitLabel("exception_line_1010_emulator");
e("push dword 10*4\n"); /* Line 1010 emulator exception vector */
e("jmp short do_exception_fix_pc\n");
Align(4);
EmitLabel("exception_line_1111_emulator");
e("push dword 11*4\n"); /* Line 1111 emulator exception vector */
e("jmp short do_exception_fix_pc\n");
EmitLabel("do_exception_fix_pc"); /* points PC at trap instruction */
e("sub esi, byte 2\n");
EmitLabel("do_exception");
e("mov edx, 0x2000\n"); /* what the SR will be */
e("xor edx, [sr]\n"); /* see if S bit is different */
e("test edx, 0x2000\n");
e("jz .no_sp_magic\n"); /* nope, don't swap SPs */
e("mov edx, [__sp]\n");
e("xchg [a+7*4], edx\n");
e("mov [__sp], edx\n");
SetSupervisorAddressSpace(); /* map in supervisor address space */
EmitLabel(".no_sp_magic");
e("mov ebx, [a+7*4]\n"); /* SP */
/*
* If 68010, we have to push the format word on the stack first.
*/
if (mpu == 68010)
{
e("sub ebx, byte 2\n");/* SP-2 */
e("mov edx, [esp]\n"); /* vector offset | 0x00000000 */
SaveReg("run", "ebx");
WriteWord();
RestoreReg("run", "ebx");
}
e("mov edx, esi\n");
e("sub edx, ebp\n"); /* PC->EDX */
e("sub ebx, byte 4\n"); /* SP-4 */
SaveReg("run", "ebx");
WriteLong();
RestoreReg("run", "ebx");
e("mov edx, [sr]\n"); /* get SR */
e("sub ebx, byte 2\n"); /* SP-2 */
SaveReg("run", "ebx");
WriteWord(); /* save SR to stack */
RestoreReg("run", "ebx");
e("or dh, 0x20\n"); /* set supervisor for exception */
e("and edx, 0xa71f\n"); /* clear unwanted bits */
e("mov [sr], edx\n"); /* set new SR, get old SR->EDX */
e("mov [a+7*4], ebx\n"); /* write back SP */
e("pop ebx\n"); /* get exception vector address */
if (mpu == 68010)
e("add ebx, [vbr]\n");
ReadLong(); /* get PC */
e("mov esi, edx\n"); /* set new PC */
UpdateFetchPtr();
e("xor edi, edi\n");
e("sub ecx, byte 40\n"); /* timing... */
e("mov di, [esi]\n");
e("add esi, byte 2\n");
e("jmp [jmptab+edi*4]\n"); /* continue execution */
}
/*****************************************************************************
* Data */
void EmitData()
{
int i;
CacheAlign();
EmitGlobalLabel(NameOfContext());
e("context_start:\n");
e("fetch dd 0\n"); /* pointer to fetch array */
e("pcfetch dd 0\n"); /* PC-relative fetch */
e("read_byte dd 0\n"); /* pointer to read_byte array */
e("read_word dd 0\n"); /* pointer to read_word array */
e("read_long dd 0\n"); /* pointer to read_long array */
e("write_byte dd 0\n"); /* pointer to write_byte array */
e("write_word dd 0\n"); /* pointer to write_word array */
e("write_long dd 0\n"); /* pointer to write_long array */
e("super_fetch dd 0\n"); /* supervisor memory map... */
e("super_pcfetch dd 0\n");
e("super_read_byte dd 0\n");
e("super_read_word dd 0\n");
e("super_read_long dd 0\n");
e("super_write_byte dd 0\n");
e("super_write_word dd 0\n");
e("super_write_long dd 0\n");
e("user_fetch dd 0\n"); /* user memory map... */
e("user_pcfetch dd 0\n");
e("user_read_byte dd 0\n");
e("user_read_word dd 0\n");
e("user_read_long dd 0\n");
e("user_write_byte dd 0\n");
e("user_write_word dd 0\n");
e("user_write_long dd 0\n");
e("intr times 8 dd 0\n"); /* intr[0-6]=vectors for interrupt
levels 1-7 (0=not pending.)
intr[7]=# of interrupts pending */
e("cycles dd 0\n"); /* cycles total to emulate */
e("remaining dd 0\n"); /* cycles remaining */
e("d times 8 dd 0\n"); /* D0-D7 */
e("a times 8 dd 0\n"); /* A0-A7 */
e("__sp dd 0\n"); /* User mode: SSP, Supervisor: USP */
e("sr dd 0\n"); /* SR */
e("pc dd 0\n"); /* PC */
if (mpu == 68010)
{
e("fc dd 0\n"); /* FC (DFC, SFC) */
e("vbr dd 0\n"); /* VBR */
}
e("status dd 0\n"); /* bit 0:1=emulating, 0=not
bit 1:1=stopped, 0=not
bit 2:1=processing ints, 0=not */
e("InterruptAcknowledge dd 0\n"); /* interrupt acknowledge callback */
e("Reset dd 0\n"); /* pointer to RESET handler */
if (mpu == 68010)
e("Bkpt dd 0\n"); /* pointer to BKPT handler */
e("Debug dd 0\n"); /* pointer to debug handler */
e("context_end:\n");
e("x dd 0\n"); /* X flag (maintained internally) */
for (i = 0; i < 7; i++)
{
char *reg[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp" };
e("memhandler_%s dd 0\n", reg[i]);
}
for (i = 0; i < 7; i++)
{
char *reg[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp" };
e("run_%s dd 0\n", reg[i]);
}
e("fetch_esi dd 0\n"); /* for UpdateFetchPtr() */
}
/*****************************************************************************
* Code */
void EmitCode()
{
int i;
/* Turbo68KInit() */
/*
* Decompresses the jump table. See main() for the compression format
*/
EmitGlobalLabel("Turbo68KInit");
e("push ecx\n");
e("push edx\n");
e("push esi\n");
e("push edi\n");
e("mov esi, compressed_jmptab\n");
e("mov edi, jmptab\n");
e("xor ecx, ecx\n"); /* we only need lower half (CX) */
EmitLabel(".l");
e("mov cx, [esi]\n"); /* get repeat information */
e("add esi, byte 2\n"); /* point at data */
e("mov edx, ecx\n");
e("and edx, 0xc000\n");
e("cmp dx, 0xc000\n"); /* #handlers * 1 each? */
e("je .r1\n");
e("cmp dx, 0x8000\n"); /* #handlers * 8 each? */
e("je .r8\n");
e("mov eax, [esi]\n"); /* get data and repeat */
e("add esi, byte 4\n");
e("cld\n");
e("rep stosd\n");
EmitLabel(".c");
e("cmp dword [esi], byte -1\n"); /* end of compressed data? */
e("jne .l\n");
e("pop edi\n");
e("pop esi\n");
e("pop edx\n");
e("pop ecx\n");
e("xor eax, eax\n");
e("ret\n");
EmitLabel(".r1");
e("and ecx, 0x3ff\n"); /* get rid of 0xc000 marker */
e("cld\n");
e("rep movsd\n"); /* move address to decomp'd jmptab */
e("jmp .c\n"); /* continue decompression */
EmitLabel(".r8");
e("and ecx, 0x7ff\n"); /* get rid of 0x8000 marker */
EmitLabel(".r8_l");
e("mov edx, ecx\n"); /* save counter temporarily */
e("mov ecx, 8\n"); /* repeat handler 8 times */
e("mov eax, [esi]\n"); /* get handler to repeat */
e("add esi, byte 4\n"); /* point to next */
e("cld\n");
e("rep stosd\n"); /* store! */
e("mov ecx, edx\n"); /* restore loop counter and loop */
e("dec ecx\n");
e("jnz .r8_l\n");
e("jmp .c\n");
/* Turbo68KReset() */
Align(4);
EmitGlobalLabel("Turbo68KReset");
e("push ebx\n");
e("push ecx\n");
e("push edx\n");
e("push esi\n");
e("push edi\n");
e("push ebp\n");
SetSupervisorAddressSpace();
EMULATING();
UNSTOP_CPU(); /* in case we're STOPped, unstop */
if (mpu == 68010) /* clear 68010 VBR */
e("mov dword [vbr], 0\n");
e("xor ecx, ecx\n"); /* no cycles executed in case error */
/*
* Read the PC vector from the fetch memory map
*/
if (!call_convention) /* stack calling convention */
e("push dword 4\n");
else /* register calling convention */
e("mov eax, 4\n");
e("call %sTurbo68KFetchPtr\n", id);
if (!call_convention)
e("add esp, byte 4\n");
e("test eax, eax\n");
e("jz near fetch_error\n");
e("mov eax, [eax]\n");
e("rol eax, byte 16\n"); /* memory is byte swapped */
e("mov dword "SR", 0x2700\n"); /* in supervisor mode at start */
e("mov dword [cycles], 0\n"); /* no cycles executed */
e("mov dword [remaining], 0\n"); /* ..ditto.. */
e("mov esi, eax\n"); /* from Turbo68KFetchPtr() */
e("xor eax, eax\n"); /* clear registers */
e("mov edi, d\n");
e("mov ecx, 16\n");
e("cld\n");
e("rep stosd\n");
e("mov edi, intr\n"); /* clear interrupt queue */
e("mov ecx, 8\n");
e("rep stosd\n");
e("xor ecx, ecx\n"); /* no cycles executed in case error */
UpdateFetchPtr(); /* this also makes sure it can be fetched */
WRITEPCTOMEM("pc");
/*
* Read the SP vector from the fetch memory map
*/
if (!call_convention) /* stack calling convention */
e("push dword 0\n");
else /* register calling convention */
e("xor eax, eax\n");
e("call %sTurbo68KFetchPtr\n", id);
if (!call_convention)
e("add esp, byte 4\n");
e("test eax, eax\n");
e("jz near fetch_error\n");
e("mov eax, [eax]\n");
e("rol eax, byte 16\n"); /* memory is byte swapped */
e("mov "A7", eax\n"); /* supervisor */
e("mov "SP", eax\n"); /* user */
STOP_EMULATING(); /* no longer running */
e("pop ebp\n");
e("pop edi\n");
e("pop esi\n");
e("pop edx\n");
e("pop ecx\n");
e("pop ebx\n");
e("xor eax, eax\n");
if (mmx) e("emms\n");
e("ret\n");
EmitLabel("invalid_error"); /* invalid instruction */
SaveCCR();
STOP_EMULATING();
STOP_RUNNING(); /* save PC and cycles remaining */
e("pop ebp\n");
e("pop edi\n");
e("pop esi\n");
e("pop edx\n");
e("pop ecx\n");
e("pop ebx\n");
e("mov eax, %d\n", TURBO68K_ERROR_INVINST);
e("ret\n");
/*
* NOTE: Only UpdateFetchPtr() and Turbo68KReset() can call this
*/
EmitLabel("fetch_error"); /* could not fetch instruction */
e("mov [remaining], ecx\n"); /* save cycles remaining */
STOP_EMULATING();
e("mov [pc], esi\n"); /* UpdateFetchPtr() didn't yet change this */
e("pop ebp\n");
e("pop edi\n");
e("pop esi\n");
e("pop edx\n");
e("pop ecx\n");
e("pop ebx\n");
e("mov eax, %d\n", TURBO68K_ERROR_FETCH);
e("ret\n");
/* Turbo68KReadPC() */
/*
* Can be used anywhere, but usage while Turbo68K is emulating may
* result in the value being offset a few bytes into the current
* instruction, but it shouldn't
*/
Align(4);
EmitGlobalLabel("Turbo68KReadPC");
e("mov eax, [pc]\n");
e("ret\n");
/* Turbo68KSetFetch() */
Align(4);
EmitGlobalLabel("Turbo68KSetFetch");
e("push eax\n");
e("push edx\n");
GetArg("eax", 0, 12);
e("sub eax, "SIZEOF_FETCHREGION"\n");
if (multiaddr)
{
GetArg("edx", 1, 16);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov [super_fetch], eax\n");
e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */
e("jz near .not_super\n");
e("mov [fetch], eax\n");
EmitLabel(".not_super");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
EmitLabel(".user");
e("mov [user_fetch], eax\n");
e("test byte [sr+1], 0x20\n"); /* if user, update fetch */
e("jnz near .not_user\n");
e("mov [fetch], eax\n");
EmitLabel(".not_user");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
else
{
e("mov [fetch], eax\n");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
/* Turbo68KGetFetch() */
Align(4);
EmitGlobalLabel("Turbo68KGetFetch");
e("push edx\n");
if (multiaddr)
{
GetArg("edx", 0, 8);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov eax, [super_fetch]\n");
e("jmp near .finish\n");
EmitLabel(".user");
e("mov eax, [user_fetch]\n");
EmitLabel(".finish");
}
else
e("mov eax, [fetch]\n");
e("pop edx\n");
e("add eax, "SIZEOF_FETCHREGION"\n");
e("ret\n");
/* Turbo68KSetPCFetch() */
if (pcfetch)
{
Align(4);
EmitGlobalLabel("Turbo68KSetPCFetch");
e("push eax\n");
e("push edx\n");
GetArg("eax", 0, 12);
e("sub eax, "SIZEOF_FETCHREGION"\n");
if (multiaddr)
{
GetArg("edx", 1, 16);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov [super_pcfetch], eax\n");
e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */
e("jz near .not_super\n");
e("mov [pcfetch], eax\n");
EmitLabel(".not_super");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
EmitLabel(".user");
e("mov [user_pcfetch], eax\n");
e("test byte [sr+1], 0x20\n"); /* if user, update fetch */
e("jnz near .not_user\n");
e("mov [pcfetch], eax\n");
EmitLabel(".not_user");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
else
{
e("mov [pcfetch], eax\n");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
/* Turbo68KGetPCFetch() */
Align(4);
EmitGlobalLabel("Turbo68KGetPCFetch");
e("push edx\n");
if (multiaddr)
{
GetArg("edx", 0, 8);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov eax, [super_pcfetch]\n");
e("jmp near .finish\n");
EmitLabel(".user");
e("mov eax, [user_pcfetch]\n");
EmitLabel(".finish");
}
else
e("mov eax, [pcfetch]\n");
e("pop edx\n");
e("add eax, "SIZEOF_FETCHREGION"\n");
e("ret\n");
}
/*
* If the memory map mode is NOT 0 (defmap), we do not add or subtract
* from the pointers because they aren't arrays, but function pointers.
*/
/* Turbo68KSetReadByte() */
Align(4);
EmitGlobalLabel("Turbo68KSetReadByte");
e("push eax\n");
e("push ebx\n");
GetArg("eax", 0, 12);
if (memmap_type == 0)
e("sub eax, "SIZEOF_DATAREGION"\n");
if (multiaddr)
{
GetArg("edx", 1, 16);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov [super_read_byte], eax\n");
e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */
e("jz near .not_super\n");
e("mov [read_byte], eax\n");
EmitLabel(".not_super");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
EmitLabel(".user");
e("mov [user_read_byte], eax\n");
e("test byte [sr+1], 0x20\n"); /* if user, update fetch */
e("jnz near .not_user\n");
e("mov [read_byte], eax\n");
EmitLabel(".not_user");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
else
{
e("mov [read_byte], eax\n");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
/* Turbo68KGetReadByte() */
Align(4);
EmitGlobalLabel("Turbo68KGetReadByte");
e("push edx\n");
if (multiaddr)
{
GetArg("edx", 0, 8);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov eax, [super_read_byte]\n");
e("jmp near .finish\n");
EmitLabel(".user");
e("mov eax, [user_read_byte]\n");
EmitLabel(".finish");
}
else
e("mov eax, [read_byte]\n");
if (memmap_type == 0)
e("add eax, "SIZEOF_DATAREGION"\n");
e("pop edx\n");
e("ret\n");
/* Turbo68KSetReadWord() */
Align(4);
EmitGlobalLabel("Turbo68KSetReadWord");
e("push eax\n");
e("push ebx\n");
GetArg("eax", 0, 12);
if (memmap_type == 0)
e("sub eax, "SIZEOF_DATAREGION"\n");
if (multiaddr)
{
GetArg("edx", 1, 16);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov [super_read_word], eax\n");
e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */
e("jz near .not_super\n");
e("mov [read_word], eax\n");
EmitLabel(".not_super");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
EmitLabel(".user");
e("mov [user_read_word], eax\n");
e("test byte [sr+1], 0x20\n"); /* if user, update fetch */
e("jnz near .not_user\n");
e("mov [read_word], eax\n");
EmitLabel(".not_user");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
else
{
e("mov [read_word], eax\n");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
/* Turbo68KGetReadWord() */
Align(4);
EmitGlobalLabel("Turbo68KGetReadWord");
e("push edx\n");
if (multiaddr)
{
GetArg("edx", 0, 8);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov eax, [super_read_word]\n");
e("jmp near .finish\n");
EmitLabel(".user");
e("mov eax, [user_read_word]\n");
EmitLabel(".finish");
}
else
e("mov eax, [read_word]\n");
if (memmap_type == 0)
e("add eax, "SIZEOF_DATAREGION"\n");
e("pop edx\n");
e("ret\n");
/* Turbo68KSetReadLong() */
Align(4);
EmitGlobalLabel("Turbo68KSetReadLong");
e("push eax\n");
e("push ebx\n");
GetArg("eax", 0, 12);
if (memmap_type == 0)
e("sub eax, "SIZEOF_DATAREGION"\n");
if (multiaddr)
{
GetArg("edx", 1, 16);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov [super_read_long], eax\n");
e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */
e("jz near .not_super\n");
e("mov [read_long], eax\n");
EmitLabel(".not_super");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
EmitLabel(".user");
e("mov [user_read_long], eax\n");
e("test byte [sr+1], 0x20\n"); /* if user, update fetch */
e("jnz near .not_user\n");
e("mov [read_long], eax\n");
EmitLabel(".not_user");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
else
{
e("mov [read_long], eax\n");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
/* Turbo68KGetReadLong() */
Align(4);
EmitGlobalLabel("Turbo68KGetReadLong");
e("push edx\n");
if (multiaddr)
{
GetArg("edx", 0, 8);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov eax, [super_read_long]\n");
e("jmp near .finish\n");
EmitLabel(".user");
e("mov eax, [user_read_long]\n");
EmitLabel(".finish");
}
else
e("mov eax, [read_long]\n");
if (memmap_type == 0)
e("add eax, "SIZEOF_DATAREGION"\n");
e("pop edx\n");
e("ret\n");
/* Turbo68KSetWriteByte() */
Align(4);
EmitGlobalLabel("Turbo68KSetWriteByte");
e("push eax\n");
e("push edx\n");
GetArg("eax", 0, 12);
if (memmap_type == 0)
e("sub eax, "SIZEOF_DATAREGION"\n");
if (multiaddr)
{
GetArg("edx", 1, 16);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov [super_write_byte], eax\n");
e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */
e("jz near .not_super\n");
e("mov [write_byte], eax\n");
EmitLabel(".not_super");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
EmitLabel(".user");
e("mov [user_write_byte], eax\n");
e("test byte [sr+1], 0x20\n"); /* if user, update fetch */
e("jnz near .not_user\n");
e("mov [write_byte], eax\n");
EmitLabel(".not_user");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
else
{
e("mov [write_byte], eax\n");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
/* Turbo68KGetWriteByte() */
Align(4);
EmitGlobalLabel("Turbo68KGetWriteByte");
e("push edx\n");
if (multiaddr)
{
GetArg("edx", 0, 8);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov eax, [super_write_byte]\n");
e("jmp near .finish\n");
EmitLabel(".user");
e("mov eax, [user_write_byte]\n");
EmitLabel(".finish");
}
else
e("mov eax, [write_byte]\n");
if (memmap_type == 0)
e("add eax, "SIZEOF_DATAREGION"\n");
e("pop edx\n");
e("ret\n");
/* Turbo68KSetWriteWord() */
Align(4);
EmitGlobalLabel("Turbo68KSetWriteWord");
e("push eax\n");
e("push edx\n");
GetArg("eax", 0, 12);
if (memmap_type == 0)
e("sub eax, "SIZEOF_DATAREGION"\n");
if (multiaddr)
{
GetArg("edx", 1, 16);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov [super_write_word], eax\n");
e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */
e("jz near .not_super\n");
e("mov [write_word], eax\n");
EmitLabel(".not_super");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
EmitLabel(".user");
e("mov [user_write_word], eax\n");
e("test byte [sr+1], 0x20\n"); /* if user, update fetch */
e("jnz near .not_user\n");
e("mov [write_word], eax\n");
EmitLabel(".not_user");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
else
{
e("mov [write_word], eax\n");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
/* Turbo68KGetWriteWord() */
Align(4);
EmitGlobalLabel("Turbo68KGetWriteWord");
e("push edx\n");
if (multiaddr)
{
GetArg("edx", 0, 8);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov eax, [super_write_word]\n");
e("jmp near .finish\n");
EmitLabel(".user");
e("mov eax, [user_write_word]\n");
EmitLabel(".finish");
}
else
e("mov eax, [write_word]\n");
if (memmap_type == 0)
e("add eax, "SIZEOF_DATAREGION"\n");
e("pop edx\n");
e("ret\n");
/* Turbo68KSetWriteLong() */
Align(4);
EmitGlobalLabel("Turbo68KSetWriteLong");
e("push eax\n");
e("push edx\n");
GetArg("eax", 0, 12);
if (memmap_type == 0)
e("sub eax, "SIZEOF_DATAREGION"\n");
if (multiaddr)
{
GetArg("edx", 1, 16);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov [super_write_long], eax\n");
e("test byte [sr+1], 0x20\n"); /* if supervisor, update fetch */
e("jz near .not_super\n");
e("mov [write_long], eax\n");
EmitLabel(".not_super");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
EmitLabel(".user");
e("mov [user_write_long], eax\n");
e("test byte [sr+1], 0x20\n"); /* if user, update fetch */
e("jnz near .not_user\n");
e("mov [write_long], eax\n");
EmitLabel(".not_user");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
else
{
e("mov [write_long], eax\n");
e("pop edx\n");
e("pop eax\n");
e("ret\n");
}
/* Turbo68KGetWriteLong() */
Align(4);
EmitGlobalLabel("Turbo68KGetWriteLong");
e("push edx\n");
if (multiaddr)
{
GetArg("edx", 0, 8);
e("cmp edx, byte %d\n", TURBO68K_SUPERVISOR);
e("jne short .user\n");
e("mov eax, [super_write_long]\n");
e("jmp near .finish\n");
EmitLabel(".user");
e("mov eax, [user_write_long]\n");
EmitLabel(".finish");
}
else
e("mov eax, [write_long]\n");
if (memmap_type == 0)
e("add eax, "SIZEOF_DATAREGION"\n");
e("pop edx\n");
e("ret\n");
/* Turbo68KFetchPtr() */
Align(4);
EmitGlobalLabel("Turbo68KFetchPtr");
e("push ebx\n");
e("push edx\n");
e("push esi\n");
e("push ebp\n");
GetArg("esi", 0, 20); /* address */
if (multiaddr)
{
e("test byte [sr+1], 0x20\n"); /* set proper address space */
e("jz short .user\n");
SetSupervisorAddressSpace();
e("jmp short .continue\n");
EmitLabel(".user");
SetUserAddressSpace();
EmitLabel(".continue");
}
AddrClip("esi");
e("mov edx, [fetch]\n");
e(".find_fetch_loop:\n");
e("add edx, byte "SIZEOF_FETCHREGION"\n");
e("cmp dword [edx], byte -1\n"); /* limit=-1? end. */
e("je short .not_found\n");
e("cmp esi, [edx]\n"); /* offset 0: base. above it? */
e("jb short .find_fetch_loop\n"); /* nope, not this region */
e("cmp esi, [edx+"OFFSET_FETCH_LIMIT"]\n"); /* limit. below it? */
e("ja short .find_fetch_loop\n"); /* nope, not this region */
e("mov ebp, [edx+"OFFSET_FETCH_PTR"]\n"); /* ebp=base ptr */
e("add esi, ebp\n"); /* +pc=pc ptr.. */
e("mov eax, esi\n");
e("pop ebp\n");
e("pop esi\n");
e("pop edx\n");
e("pop ebx\n");
e("ret\n");
EmitLabel(".not_found");
e("pop ebp\n");
e("pop esi\n");
e("pop edx\n");
e("pop ebx\n");
e("xor eax, eax\n");
e("ret\n");
/* Turbo68KReadByte() */
Align(4);
EmitGlobalLabel("Turbo68KReadByte");
e("push ebx\n");
e("push ecx\n");
e("push edx\n");
e("push esi\n");
e("push edi\n");
e("push ebp\n");
e("xor ebp, ebp\n"); /* so PC doesn't get trashed */
e("mov esi, [pc]\n");
if (multiaddr)
{
e("test byte [sr+1], 0x20\n"); /* set proper address space */
e("jz short .user\n");
SetSupervisorAddressSpace();
e("jmp short .continue\n");
EmitLabel(".user");
SetUserAddressSpace();
EmitLabel(".continue");
}
GetArg("ebx", 0, 28);
ReadByte();
e("mov eax, edx\n");
if (mmx) e("emms\n");
e("pop ebp\n");
e("pop edi\n");
e("pop esi\n");
e("pop edx\n");
e("pop ecx\n");
e("pop ebx\n");
e("ret\n");
/* Turbo68KReadWord() */
Align(4);
EmitGlobalLabel("Turbo68KReadWord");
e("push ebx\n");
e("push ecx\n");
e("push edx\n");
e("push esi\n");
e("push edi\n");
e("push ebp\n");
e("xor ebp, ebp\n"); /* so PC doesn't get trashed */
e("mov esi, [pc]\n");
if (multiaddr)
{
e("test byte [sr+1], 0x20\n"); /* set proper address space */
e("jz short .user\n");
SetSupervisorAddressSpace();
e("jmp short .continue\n");
EmitLabel(".user");
SetUserAddressSpace();
EmitLabel(".continue");
}
GetArg("ebx", 0, 28);
ReadWord();
e("mov eax, edx\n");
if (mmx) e("emms\n");
e("pop ebp\n");
e("pop edi\n");
e("pop esi\n");
e("pop edx\n");
e("pop ecx\n");
e("pop ebx\n");
e("ret\n");
/* Turbo68KReadLong() */
Align(4);
EmitGlobalLabel("Turbo68KReadLong");
e("push ebx\n");
e("push ecx\n");
e("push edx\n");
e("push esi\n");
e("push edi\n");
e("push ebp\n");
e("xor ebp, ebp\n"); /* so PC doesn't get trashed */
e("mov esi, [pc]\n");
if (multiaddr)
{
e("test byte [sr+1], 0x20\n"); /* set proper address space */
e("jz short .user\n");
SetSupervisorAddressSpace();
e("jmp short .continue\n");
EmitLabel(".user");
SetUserAddressSpace();
EmitLabel(".continue");
}
GetArg("ebx", 0, 28);
ReadLong();
e("mov eax, edx\n");
if (mmx) e("emms\n");
e("pop ebp\n");
e("pop edi\n");
e("pop esi\n");
e("pop edx\n");
e("pop ecx\n");
e("pop ebx\n");
e("ret\n");
/* Turbo68KWriteByte() */
Align(4);
EmitGlobalLabel("Turbo68KWriteByte");
e("pusha\n");
e("xor ebp, ebp\n"); /* so PC doesn't get trashed */
e("mov esi, [pc]\n");
if (multiaddr)
{
e("test byte [sr+1], 0x20\n"); /* set proper address space */
e("jz short .user\n");
SetSupervisorAddressSpace();
e("jmp short .continue\n");
EmitLabel(".user");
SetUserAddressSpace();
EmitLabel(".continue");
}
GetArg("ebx", 0, 36);
GetArg("edx", 1, 40);
WriteByte();
if (mmx) e("emms\n");
e("popa\n");
e("ret\n");
/* Turbo68KWriteWord() */
Align(4);
EmitGlobalLabel("Turbo68KWriteWord");
e("pusha\n");
e("xor ebp, ebp\n"); /* so PC doesn't get trashed */
e("mov esi, [pc]\n");
if (multiaddr)
{
e("test byte [sr+1], 0x20\n"); /* set proper address space */
e("jz short .user\n");
SetSupervisorAddressSpace();
e("jmp short .continue\n");
EmitLabel(".user");
SetUserAddressSpace();
EmitLabel(".continue");
}
GetArg("ebx", 0, 36);
GetArg("edx", 1, 40);
WriteWord();
if (mmx) e("emms\n");
e("popa\n");
e("ret\n");
/* Turbo68KWriteLong() */
Align(4);
EmitGlobalLabel("Turbo68KWriteLong");
e("pusha\n");
e("xor ebp, ebp\n"); /* so PC doesn't get trashed */
e("mov esi, [pc]\n");
if (multiaddr)
{
e("test byte [sr+1], 0x20\n"); /* set proper address space */
e("jz short .user\n");
SetSupervisorAddressSpace();
e("jmp short .continue\n");
EmitLabel(".user");
SetUserAddressSpace();
EmitLabel(".continue");
}
GetArg("ebx", 0, 36);
GetArg("edx", 1, 40);
WriteLong();
if (mmx) e("emms\n");
e("popa\n");
e("ret\n");
/* Turbo68KSet/GetContext() */
/*
* Notice the trick here, all of the memory map pointers have 16
* subtracted from them, this helps optimize the read/write handlers
* since an "add edi, 16" is possible straight off. This was NB's
* idea. The fetch area is also handled this way
*/
Align(4);
EmitGlobalLabel("Turbo68KSetContext");
e("push ecx\n");
e("push esi\n");
e("push edi\n");
GetArg("esi", 0, 16);
e("mov edi, %s%s\n", id, NameOfContext());
if (mmx && !(SizeOfContext() & 7))
{
e("mov ecx, " SIZEOF_CONTEXT " / 8\n");
EmitLabel(".l");
e("movq mm6, [esi]\n");
e("movq [edi], mm6\n");
e("add esi, byte 8\n");
e("add edi, byte 8\n");
e("dec ecx\n");
e("jnz short .l\n");
e("emms\n");
}
else
{
e("mov ecx, " SIZEOF_CONTEXT " / 4\n");
e("cld\n");
e("rep movsd\n");
}
e("sub dword [fetch], "SIZEOF_FETCHREGION"\n");
e("sub dword [pcfetch], "SIZEOF_FETCHREGION"\n");
e("sub dword [super_fetch], "SIZEOF_FETCHREGION"\n");
e("sub dword [super_pcfetch], "SIZEOF_FETCHREGION"\n");
e("sub dword [user_fetch], "SIZEOF_FETCHREGION"\n");
e("sub dword [user_pcfetch], "SIZEOF_FETCHREGION"\n");
if (memmap_type == 0)
{
e("sub dword [read_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [read_word], "SIZEOF_DATAREGION"\n");
e("sub dword [read_long], "SIZEOF_DATAREGION"\n");
e("sub dword [write_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [write_word], "SIZEOF_DATAREGION"\n");
e("sub dword [write_long], "SIZEOF_DATAREGION"\n");
e("sub dword [super_read_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [super_read_word], "SIZEOF_DATAREGION"\n");
e("sub dword [super_read_long], "SIZEOF_DATAREGION"\n");
e("sub dword [super_write_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [super_write_word], "SIZEOF_DATAREGION"\n");
e("sub dword [super_write_long], "SIZEOF_DATAREGION"\n");
e("sub dword [user_read_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [user_read_word], "SIZEOF_DATAREGION"\n");
e("sub dword [user_read_long], "SIZEOF_DATAREGION"\n");
e("sub dword [user_write_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [user_write_word], "SIZEOF_DATAREGION"\n");
e("sub dword [user_write_long], "SIZEOF_DATAREGION"\n");
}
e("pop edi\n");
e("pop esi\n");
e("pop ecx\n");
e("ret\n");
Align(4);
EmitGlobalLabel("Turbo68KGetContext");
e("push ecx\n");
e("push esi\n");
e("push edi\n");
e("add dword [fetch], "SIZEOF_FETCHREGION"\n");
e("add dword [pcfetch], "SIZEOF_FETCHREGION"\n");
e("add dword [super_fetch], "SIZEOF_FETCHREGION"\n");
e("add dword [super_pcfetch], "SIZEOF_FETCHREGION"\n");
e("add dword [user_fetch], "SIZEOF_FETCHREGION"\n");
e("add dword [user_pcfetch], "SIZEOF_FETCHREGION"\n");
if (memmap_type == 0)
{
e("add dword [read_byte], "SIZEOF_DATAREGION"\n");
e("add dword [read_word], "SIZEOF_DATAREGION"\n");
e("add dword [read_long], "SIZEOF_DATAREGION"\n");
e("add dword [write_byte], "SIZEOF_DATAREGION"\n");
e("add dword [write_word], "SIZEOF_DATAREGION"\n");
e("add dword [write_long], "SIZEOF_DATAREGION"\n");
e("add dword [super_read_byte], "SIZEOF_DATAREGION"\n");
e("add dword [super_read_word], "SIZEOF_DATAREGION"\n");
e("add dword [super_read_long], "SIZEOF_DATAREGION"\n");
e("add dword [super_write_byte], "SIZEOF_DATAREGION"\n");
e("add dword [super_write_word], "SIZEOF_DATAREGION"\n");
e("add dword [super_write_long], "SIZEOF_DATAREGION"\n");
e("add dword [user_read_byte], "SIZEOF_DATAREGION"\n");
e("add dword [user_read_word], "SIZEOF_DATAREGION"\n");
e("add dword [user_read_long], "SIZEOF_DATAREGION"\n");
e("add dword [user_write_byte], "SIZEOF_DATAREGION"\n");
e("add dword [user_write_word], "SIZEOF_DATAREGION"\n");
e("add dword [user_write_long], "SIZEOF_DATAREGION"\n");
}
GetArg("edi", 0, 16);
e("mov esi, %s%s\n", id, NameOfContext());
if (mmx && !(SizeOfContext() & 7))
{
e("mov ecx, " SIZEOF_CONTEXT " / 8\n");
EmitLabel(".l");
e("movq mm6, [esi]\n");
e("movq [edi], mm6\n");
e("add esi, byte 8\n");
e("add edi, byte 8\n");
e("dec ecx\n");
e("jnz short .l\n");
e("emms\n");
}
else
{
e("mov ecx, " SIZEOF_CONTEXT " / 4\n");
e("cld\n");
e("rep movsd\n");
}
e("sub dword [fetch], "SIZEOF_FETCHREGION"\n");
e("sub dword [pcfetch], "SIZEOF_FETCHREGION"\n");
e("sub dword [super_fetch], "SIZEOF_FETCHREGION"\n");
e("sub dword [super_pcfetch], "SIZEOF_FETCHREGION"\n");
e("sub dword [user_fetch], "SIZEOF_FETCHREGION"\n");
e("sub dword [user_pcfetch], "SIZEOF_FETCHREGION"\n");
if (memmap_type == 0)
{
e("sub dword [read_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [read_word], "SIZEOF_DATAREGION"\n");
e("sub dword [read_long], "SIZEOF_DATAREGION"\n");
e("sub dword [write_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [write_word], "SIZEOF_DATAREGION"\n");
e("sub dword [write_long], "SIZEOF_DATAREGION"\n");
e("sub dword [super_read_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [super_read_word], "SIZEOF_DATAREGION"\n");
e("sub dword [super_read_long], "SIZEOF_DATAREGION"\n");
e("sub dword [super_write_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [super_write_word], "SIZEOF_DATAREGION"\n");
e("sub dword [super_write_long], "SIZEOF_DATAREGION"\n");
e("sub dword [user_read_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [user_read_word], "SIZEOF_DATAREGION"\n");
e("sub dword [user_read_long], "SIZEOF_DATAREGION"\n");
e("sub dword [user_write_byte], "SIZEOF_DATAREGION"\n");
e("sub dword [user_write_word], "SIZEOF_DATAREGION"\n");
e("sub dword [user_write_long], "SIZEOF_DATAREGION"\n");
}
e("pop edi\n");
e("pop esi\n");
e("pop ecx\n");
e("ret\n");
/* Turbo68KGetContextSize() */
EmitGlobalLabel("Turbo68KGetContextSize");
e("mov eax, "SIZEOF_CONTEXT"\n");
e("ret\n");
/* Turbo68KClearCycles */
EmitGlobalLabel("Turbo68KClearCycles");
e("mov dword [cycles], 0\n");
e("mov dword [remaining], 0\n");
e("ret\n");
/* Turbo68KFreeTimeSlice */
EmitGlobalLabel("Turbo68KFreeTimeSlice");
e("push eax\n");
e("mov eax, [cycles]\n");
e("sub eax, [remaining]\n");
e("mov [cycles], eax\n");
e("mov dword [remaining], 0\n");
e("pop eax\n");
e("ret\n");
/* Turbo68KGetElapsedCycles */
Align(4);
EmitGlobalLabel("Turbo68KGetElapsedCycles");
e("mov eax, [cycles]\n");
e("sub eax, [remaining]\n");
e("ret\n");
/* Turbo68KProcessInterrupts() */
Align(4);
EmitGlobalLabel("Turbo68KProcessInterrupts");
e("pusha\n");
e("test byte [status], 1\n");
e("jnz near .end\n"); /* does not work while running */
e("mov ecx, [remaining]\n");
EMULATING(); /* running=1 */
e("mov esi, [pc]\n"); /* fetch PC */
UpdateFetchPtr();
e("call ProcessInterrupts\n");
STOP_RUNNING(); /* save PC and cycles remaining */
STOP_EMULATING();
EmitLabel(".end");
if (mmx) e("emms\n");
e("popa\n");
e("xor eax, eax\n");
e("ret\n");
/* Turbo68KInterrupt() */
/*
* Note: This function assumes that TURBO68K_AUTOVECTOR == 256, make
* sure the definition in TURBO68K.H reflects this
*
* If 68K is halted (STOP instruction), if an interrupt with a priority
* equal to or lower than the SR priority is requested, is the interrupt
* made pending or discarded? (ask) Are there any situations when invalid-
* priority interrupts are discarded completely?
*/
Align(4);
EmitGlobalLabel("Turbo68KInterrupt");
e("push\tebx\n");
e("push\tecx\n");
e("push\tedx\n");
e("push\tesi\n");
e("push\tedi\n");
GetArg("eax", 0, 24);
e("test eax, eax\n"); /* 0 is not a valid level */
e("jz short .inv_level\n");
e("cmp eax, byte 7\n"); /* if greater than 7, invalid level */
e("ja short .inv_level\n");
GetArg("ebx", 1, 28); /* get vector */
e("cmp ebx, byte 2\n");
e("jb short .inv_vector\n");
e("cmp ebx, 256\n");
e("ja short .inv_vector\n");
e("jne short .not_auto\n");
e("mov ebx, eax\n");
e("add ebx, byte 24\n");
EmitLabel(".not_auto");
e("dec eax\n"); /* (level-1)*4=offset into intr[] */
e("shl eax, byte 2\n");
e("cmp [intr+eax], byte 0\n");
e("jne short .pending\n"); /* already pending */
e("mov [intr+eax], ebx\n"); /* store vector into intr[] */
e("inc dword [intr+7*4]\n"); /* one interrupt added */
e("pop\tedi\n");
e("pop\tesi\n");
e("pop\tedx\n");
e("pop\tecx\n");
e("pop\tebx\n");
e("xor eax, eax\n");
e("ret\n");
EmitLabel(".inv_level");
e("mov eax, %d\n", TURBO68K_ERROR_INTLEVEL);
e("pop\tedi\n");
e("pop\tesi\n");
e("pop\tedx\n");
e("pop\tecx\n");
e("pop\tebx\n");
e("ret\n");
EmitLabel(".inv_vector");
e("mov eax, %d\n", TURBO68K_ERROR_INTVECTOR);
e("pop\tedi\n");
e("pop\tesi\n");
e("pop\tedx\n");
e("pop\tecx\n");
e("pop\tebx\n");
e("ret\n");
EmitLabel(".pending");
e("mov eax, %d\n", TURBO68K_ERROR_INTPENDING);
e("pop\tedi\n");
e("pop\tesi\n");
e("pop\tedx\n");
e("pop\tecx\n");
e("pop\tebx\n");
e("ret\n");
/* Turbo68KCancelInterrupt */
EmitGlobalLabel("Turbo68KCancelInterrupt");
e("push\tebx\n");
e("push\tecx\n");
e("push\tedx\n");
e("push\tesi\n");
e("push\tedi\n");
GetArg("eax", 0, 24);
e("test eax, eax\n"); /* 0 is not a valid level */
e("jz short .inv_level\n");
e("cmp eax, byte 7\n"); /* if greater than 7, invalid level */
e("ja short .inv_level\n");
e("test byte [status], 0x04\n"); /* processing interrupts? */
e("jnz short .busy\n"); /* yep... can't do this */
e("dec eax\n"); /* make index into intr[] */
e("cmp dword [intr+eax*4], byte 0\n");
e("je .nothing_to_cancel\n");
e("mov dword [intr+eax*4], 0\n");
e("dec dword [intr+7*4]\n"); /* removed 1 interrupt */
EmitLabel(".busy");
EmitLabel(".nothing_to_cancel");
e("pop\tedi\n");
e("pop\tesi\n");
e("pop\tedx\n");
e("pop\tecx\n");
e("pop\tebx\n");
e("xor eax, eax\n");
e("ret\n");
EmitLabel(".inv_level");
e("pop\tedi\n");
e("pop\tesi\n");
e("pop\tedx\n");
e("pop\tecx\n");
e("pop\tebx\n");
e("mov eax, %d\n", TURBO68K_ERROR_INTLEVEL);
e("ret\n");
/*
* ProcessInterrupts: Processes any pending interrupts if allowed.
* Assumes all registers have been set up as per Turbo68KRun
*/
Align(4);
EmitLabel("ProcessInterrupts");
INTERRUPT_PROCESSING();
e("cmp dword [intr+7*4], byte 0\n");
e("je near .no_int_end\n"); /* no interrupts pending */
e("push eax\n"); /* EAX=flags, save it! */
e("mov al, [sr+1]\n");
e("and eax, byte 7\n"); /* EAX=interrupt priority */
e("cmp al, 7\n");
e("jne short .no_int7\n");
e("mov edi, intr+6*4\n");
e("jmp short .l\n");
EmitLabel(".no_int7");
e("mov edi, eax\n");
e("shl edi, byte 2\n"); /* *4, index into intr[] */
e("add edi, intr\n"); /* EDI=interrupt queue */
e("inc al\n"); /* new SR priority mask */
EmitLabel(".l");
e("cmp dword [edi], byte 0\n");
e("je near .skip\n"); /* no interrupt at this level */
e("test byte [sr+1], 0x20\n"); /* see if in User mode */
e("jnz short .no_sp_magic\n"); /* nope, don't need to swap SPs */
e("mov edx, [__sp]\n");
e("xchg [a+7*4], edx\n");
e("mov [__sp], edx\n");
SetSupervisorAddressSpace();
EmitLabel(".no_sp_magic");
e("mov ebx, [a+7*4]\n"); /* SP */
/*
* If 68010, we have to push the format word on the stack.
*/
if (mpu == 68010)
{
e("mov edx, [edi]\n"); /* vector number */
e("sub ebx, byte 2\n");/* SP-2 */
e("shl edx, byte 2\n");/* vector*4 for offset */
e("push ebx\n");
e("push edi\n");
WriteWord();
e("pop edi\n");
e("pop ebx\n");
}
e("mov edx, [pc]\n"); /* get old PC */
e("sub ebx, byte 4\n"); /* SP-4 */
e("push ebx\n");
e("push edi\n");
WriteLong();
e("pop edi\n");
e("pop ebx\n");
e("mov edx, [sr]\n"); /* get SR */
e("sub ebx, byte 2\n"); /* SP-2 */
e("push edi\n");
e("push ebx\n");
WriteWord(); /* save SR to stack */
e("pop ebx\n");
e("pop edi\n");
e("mov dh, al\n"); /* new interrupt priority bits */
e("or dh, 0x20\n"); /* set supervisor for exception */
e("and edx, 0xa71f\n"); /* clear unwanted bits */
e("mov [sr], edx\n"); /* set new SR */
e("mov [a+7*4], ebx\n"); /* write back SP */
e("mov ebx, [edi]\n"); /* vector of interrupt */
e("test dword [InterruptAcknowledge], -1\n"); /* interrupt call-back */
e("jz short .no_interrupt_ack_handler\n");
e("pushad\n");
e("push ebx\n");
e("call dword [InterruptAcknowledge]\n");
e("add esp, byte 4\n");
e("popad\n");
e(".no_interrupt_ack_handler:\n");
e("shl ebx, byte 2\n"); /* vector *= 4 */
if (mpu == 68010)
e("add ebx, [vbr]\n");
e("push edi\n");
ReadLong(); /* get PC */
e("pop edi\n");
e("mov [pc], edx\n"); /* set new PC */
e("mov dword [edi], 0\n"); /* done w/ this interrupt */
e("dec dword [intr+7*4]\n"); /* processed 1 interrupt... */
e("sub ecx, byte 44\n"); /* interrupts take 44 clocks */
UNSTOP_CPU();
EmitLabel(".skip");
e("add edi, byte 4\n");
e("inc al\n"); /* next entry */
e("cmp edi, intr+7*4\n");
e("jne near .l\n");
e("mov esi, [pc]\n");
e("pop eax\n"); /* get flags back */
UpdateFetchPtr();
EmitLabel(".no_int_end");
INTERRUPT_DONE();
e("ret\n");
/* Turbo68KRun() */
Align(4);
EmitGlobalLabel("Turbo68KRun");
e("pusha\n");
GetArg("ecx", 0, 36); /* ECX = cycles */
e("mov [cycles], ecx\n");
e("mov [remaining], ecx\n");
e("test byte [sr+1], 0x20\n");
e("jz near .set_user_space\n");
SetSupervisorAddressSpace();
e("jmp near .continue\n");
EmitLabel(".set_user_space");
SetUserAddressSpace();
EmitLabel(".continue");
EMULATING(); /* running=1 */
e("mov esi, [pc]\n"); /* fetch PC */
UpdateFetchPtr();
LoadCCR(); /* get flags */
e("call ProcessInterrupts\n");
e("test byte [status], 2\n"); /* stopped? */
e("jnz short .stopped\n");
e("xor edi, edi\n");
e("mov di, [esi]\n"); /* next instruction */
e("add esi, byte 2\n");
e("jmp dword [jmptab+edi*4]\n"); /* jump... */
EmitLabel(".stopped");
e("xor ecx, ecx\n");
e("jmp Turbo68KRun_done\n");
Align(4);
EmitLabel("Turbo68KRun_done");
SaveCCR(); /* save flags */
STOP_RUNNING(); /* save PC and cycles */
STOP_EMULATING();
if (mmx) e("emms\n");
e("popa\n");
e("xor eax, eax\n"); /* everything okay... */
e("ret\n");
/*
* NOTE: Only the 68010 RTE handler can call this. PC is assumed to point
* one word after the faulty RTE.
*/
if (mpu == 68010)
{
EmitLabel("stackframe_error");
e("sub esi, byte 2\n"); /* point at faulty RTE */
SaveCCR();
STOP_EMULATING();
STOP_RUNNING(); /* save PC and cycles remaining */
e("popa\n");
e("mov eax, %d\n", TURBO68K_ERROR_STACKFRAME);
e("ret\n");
}
if (memmap_type == 2) /* low level handling? don't generate the rest */
return;
/*
* ReadXXXXPC: EBX = address
* Out: EDX = data; DL=byte, EDX=upper 32-bits trashed, DX=word,
* EDX=long-word
* EBX=address
* Notes: EDI is trashed in the process, but cleared at the end.
* These functions read from the *pcfetch areas. There are no
* SX functions, this is handled by the emitter. I did this to
* save space.
*/
if (pcfetch)
{
for (i = 0; i < 3; i++) /* 0=byte, 1=word, 2=dword */
{
CacheAlign();
switch (i)
{
case 0:
EmitLabel("ReadBytePC");
break;
case 1:
EmitLabel("ReadWordPC");
break;
case 2:
EmitLabel("ReadLongPC");
break;
}
e("mov edi, [pcfetch]\n");
SaveReg("memhandler", "ebx"); /* save address */
AddrClip("ebx");
EmitLabel(".loop");
e("add edi, byte "SIZEOF_FETCHREGION"\n");
e("cmp dword [edi], byte -1\n"); /* base=-1? end. */
e("je short .not_found\n");
e("cmp ebx, [edi]\n"); /* offset 0: base. above it? */
e("jb short .loop\n"); /* nope, not this region */
e("cmp ebx, [edi+"OFFSET_FETCH_LIMIT"]\n"); /* limit. below it? */
e("ja short .loop\n"); /* nope, not this region */
e("mov edx, ebx\n");
if (!i) /* byte accesses need this because buffer is swapped */
e("xor dl, 1\n");
e("add edx, [edi+"OFFSET_FETCH_PTR"]\n"); /* into ptr.. */
if (!i) /* byte */
e("mov dl, [edx]\n"); /* fetch */
else if (i == 1) /* word */
e("mov edx, [edx]\n");
else /* long */
{
e("mov edx, [edx]\n");
e("rol edx, byte 16\n"); /* swap words */
}
e("xor edi, edi\n");
RestoreReg("memhandler", "ebx");
e("ret\n");
Align(4);
EmitLabel(".not_found");
e("xor edi, edi\n");
e("xor edx, edx\n");
e("dec edx\n"); /* not found, return all 1s */
RestoreReg("memhandler", "ebx");
e("ret\n");
}
}
/*
* ReadXXXX: EBX = address
* Out: EDX = data; DL=byte, EDX=upper 32-bits trashed, DX=word,
* EDX=long-word
* EBX=address
* Notes: EDI is trashed in the process, but cleared at the end.
* This applies to all ReadXXX and WriteXXX handlers, some
* instructions (NEGX) rely on this behavior.
*/
if (memmap_type == 0) /* default memory mapping system */
{
for (i = 0; i < 3; i++) /* 0=byte, 1=word, 2=dword */
{
CacheAlign();
switch (i)
{
case 0:
EmitLabel("ReadByte");
e("mov edi, [read_byte]\n");
break;
case 1:
EmitLabel("ReadWord");
e("mov edi, [read_word]\n");
break;
case 2:
EmitLabel("ReadLong");
e("mov edi, [read_long]\n");
break;
}
SaveReg("memhandler", "ebx"); /* save address */
AddrClip("ebx");
EmitLabel(".loop");
e("add edi, byte "SIZEOF_DATAREGION"\n");
e("cmp dword [edi], byte -1\n"); /* base=-1? end. */
e("je short .not_found\n");
e("cmp ebx, [edi]\n"); /* offset 0: base. above it? */
e("jb short .loop\n"); /* nope, not this region */
e("cmp ebx, [edi+"OFFSET_DATA_LIMIT"]\n"); /* limit. below it? */
e("ja short .loop\n"); /* nope, not this region */
e("cmp dword [edi+"OFFSET_DATA_PTR"], byte 0\n");
e("je short .read_from_handler\n");
e("mov edx, ebx\n");
if (!i) /* byte accesses need this because buffer is swapped */
e("xor dl, 1\n");
e("add edx, [edi+"OFFSET_DATA_PTR"]\n"); /* into ptr.. */
if (!i) /* byte */
e("mov dl, [edx]\n"); /* fetch */
else if (i == 1) /* word */
e("mov edx, [edx]\n");
else /* long */
{
e("mov edx, [edx]\n");
e("rol edx, byte 16\n"); /* swap words */
}
e("xor edi, edi\n");
RestoreReg("memhandler", "ebx");
e("ret\n");
Align(4);
EmitLabel(".not_found");
e("xor edi, edi\n");
e("xor edx, edx\n");
e("dec edx\n"); /* not found, return all 1s */
RestoreReg("memhandler", "ebx");
e("ret\n");
Align(4);
EmitLabel(".read_from_handler");
SaveReg("memhandler", "eax");
SaveReg("memhandler", "ebp");
WRITEPCTOMEM("pc"); /* saves esi */
e("mov [remaining], ecx\n");
/* address */
if (!call_convention) /* stack-based */
e("push ebx\n");
else /* register-based */
e("mov eax, ebx\n");
e("call dword [edi+"OFFSET_DATA_HANDLER"]\n");
if (!call_convention)
e("add esp, byte 4\n");
e("mov edx, eax\n"); /* get the data */
e("mov esi, [pc]\n"); /* handler could have changed PC */
e("mov ecx, [remaining]\n");/* this could have been changed */
RestoreReg("memhandler", "eax");
RestoreReg("memhandler", "ebp");
e("add esi, ebp\n"); /* base+pc=pc pointer */
e("xor edi, edi\n"); /* keep clear for fetch */
RestoreReg("memhandler", "ebx");
e("ret\n");
}
}
else if (memmap_type == 1) /* high level handler */
{
for (i = 0; i < 3; i++)
{
CacheAlign();
switch (i)
{
case 0: EmitLabel("ReadByte"); break;
case 1: EmitLabel("ReadWord"); break;
case 2: EmitLabel("ReadLong"); break;
}
SaveReg("memhandler", "ebx"); /* save address */
AddrClip("ebx");
SaveReg("memhandler", "eax");
SaveReg("memhandler", "ebp");
WRITEPCTOMEM("pc"); /* saves esi */
e("mov [remaining], ecx\n");
/* address */
if (!call_convention) /* stack-based */
e("push ebx\n");
else /* register-based */
e("mov eax, ebx\n");
switch (i)
{
case 0: e("call dword [read_byte]\n"); break;
case 1: e("call dword [read_word]\n"); break;
case 2: e("call dword [read_long]\n"); break;
}
if (!call_convention)
e("add esp, byte 4\n");
e("mov edx, eax\n"); /* get the data */
e("mov esi, [pc]\n"); /* handler could have changed PC */
e("mov ecx, [remaining]\n");/* this could have been changed */
RestoreReg("memhandler", "eax");
RestoreReg("memhandler", "ebp");
e("add esi, ebp\n"); /* base+pc=pc pointer */
e("xor edi, edi\n"); /* keep clear for fetch */
RestoreReg("memhandler", "ebx");
e("ret\n");
}
}
if (memmap_type == 0)
{
/*
* ReadWordSX is the same as ReadWord except that it sign extends the
* word to 32 bits.
*/
CacheAlign();
EmitLabel("ReadWordSX");
e("mov edi, [read_word]\n");
SaveReg("memhandler", "ebx"); /* save address */
AddrClip("ebx");
EmitLabel(".loop");
e("add edi, byte "SIZEOF_DATAREGION"\n");
e("cmp dword [edi], byte -1\n");
e("je short .not_found\n");
e("cmp ebx, [edi]\n");
e("jb short .loop\n");
e("cmp ebx, [edi+"OFFSET_DATA_LIMIT"]\n");
e("ja short .loop\n");
e("cmp dword [edi+"OFFSET_DATA_PTR"], byte 0\n");
e("je short .read_from_handler\n");
e("mov edx, ebx\n");
e("add edx, [edi+"OFFSET_DATA_PTR"]\n");
e("movsx edx, word [edx]\n"); /* sign extend! */
e("xor edi, edi\n");
RestoreReg("memhandler", "ebx");
e("ret\n");
Align(4);
EmitLabel(".not_found");
e("xor edi, edi\n");
e("xor edx, edx\n");
e("dec edx\n"); /* not found, return all 1s */
RestoreReg("memhandler", "ebx");
e("ret\n");
Align(4);
EmitLabel(".read_from_handler");
SaveReg("memhandler", "eax");
SaveReg("memhandler", "ebp");
WRITEPCTOMEM("pc");
e("mov [remaining], ecx\n");
/* address */
if (!call_convention) /* stack-based */
e("push ebx\n");
else /* register-based */
e("mov eax, ebx\n");
e("call dword [edi+"OFFSET_DATA_HANDLER"]\n");
if (!call_convention)
e("add esp, byte 4\n");
e("movsx edx, ax\n"); /* sign extend! */
e("mov esi, [pc]\n");
e("mov ecx, [remaining]\n");
RestoreReg("memhandler", "eax");
RestoreReg("memhandler", "ebp");
e("add esi, ebp\n");
e("xor edi, edi\n");
RestoreReg("memhandler", "ebx");
e("ret\n");
}
else if (memmap_type == 1)
{
CacheAlign();
EmitLabel("ReadWordSX");
SaveReg("memhandler", "ebx"); /* save address */
AddrClip("ebx");
SaveReg("memhandler", "eax");
SaveReg("memhandler", "ebp");
WRITEPCTOMEM("pc"); /* saves esi */
e("mov [remaining], ecx\n");
/* address */
if (!call_convention) /* stack-based */
e("push ebx\n");
else /* register-based */
e("mov eax, ebx\n");
e("call dword [read_word]\n");
if (!call_convention)
e("add esp, byte 4\n");
e("movsx edx, ax\n"); /* sign extend */
e("mov esi, [pc]\n"); /* handler could have changed PC */
e("mov ecx, [remaining]\n");/* this could have been changed */
RestoreReg("memhandler", "eax");
RestoreReg("memhandler", "ebp");
e("add esi, ebp\n"); /* base+pc=pc pointer */
e("xor edi, edi\n"); /* keep clear for fetch */
RestoreReg("memhandler", "ebx");
e("ret\n");
}
/*
* WriteXXXX: EBX = address
* In: EDX = data; DL=byte, EDX{DX}=word,
* EDX=long-word
* Out: EBX=trashed, EDX=preserved
* Notes: EDI is trashed in the process, but cleared at the end
*/
if (memmap_type == 0)
{
for (i = 0; i < 3; i++) /* 0=byte, 1=word, 2=dword */
{
CacheAlign();
switch (i)
{
case 0:
EmitLabel("WriteByte");
e("mov edi, [write_byte]\n");
break;
case 1:
EmitLabel("WriteWord");
e("mov edi, [write_word]\n");
break;
case 2:
EmitLabel("WriteLong");
e("mov edi, [write_long]\n");
break;
}
AddrClip("ebx");
EmitLabel(".loop");
e("add edi, byte "SIZEOF_DATAREGION"\n");
e("cmp dword [edi], byte -1\n"); /* base=-1? end. */
e("je short .not_found\n");
e("cmp ebx, [edi]\n"); /* offset 0: base. above it? */
e("jb short .loop\n"); /* nope, not this region */
e("cmp ebx, [edi+"OFFSET_DATA_LIMIT"]\n"); /* limit. below it? */
e("ja short .loop\n"); /* nope, not this region */
e("cmp dword [edi+"OFFSET_DATA_PTR"], byte 0\n");
e("je short .write_with_handler\n");
if (!i) /* byte accesses need this because buffer is swapped */
e("xor bl, 1\n");
e("add ebx, [edi+"OFFSET_DATA_PTR"]\n"); /* into ptr.. */
if (!i) /* byte */
e("mov [ebx], dl\n"); /* write */
else if (i == 1) /* word */
e("mov [ebx], dx\n");
else /* long */
{
e("ror edx, byte 16\n"); /* swap words */
e("mov [ebx], edx\n");
e("ror edx, byte 16\n"); /* reswap back */
}
EmitLabel(".not_found");
e("xor edi, edi\n");
RestoreReg("memhandler", "ebx");
e("ret\n");
Align(4);
EmitLabel(".write_with_handler");
SaveReg("memhandler", "eax");
SaveReg("memhandler", "ebp");
WRITEPCTOMEM("pc"); /* saves esi */
e("mov [remaining], ecx\n");
SaveReg("memhandler", "edx"); /* save params */
/* data, address */
if (!call_convention) /* stack-based */
{
e("push edx\n");
e("push ebx\n");
}
else /* register-based */
{
e("mov eax, ebx\n");
/* don't need: e("mov edx, edx\n"); */
}
e("call dword [edi+"OFFSET_DATA_HANDLER"]\n");
if (!call_convention)
e("add esp, byte 8\n");
RestoreReg("memhandler", "edx"); /* restore params */
e("mov esi, [pc]\n");
e("mov ecx, [remaining]\n"); /* this could have been changed */
RestoreReg("memhandler", "eax");
RestoreReg("memhandler", "ebp");
e("add esi, ebp\n"); /* base+pc=pc pointer */
e("xor edi, edi\n"); /* keep clear for fetch */
e("ret\n");
}
}
else if (memmap_type == 1)
{
for (i = 0; i < 3; i++)
{
CacheAlign();
switch (i)
{
case 0: EmitLabel("WriteByte"); break;
case 1: EmitLabel("WriteWord"); break;
case 2: EmitLabel("WriteLong"); break;
}
AddrClip("ebx");
SaveReg("memhandler", "eax");
SaveReg("memhandler", "ebp");
WRITEPCTOMEM("pc"); /* saves esi */
e("mov [remaining], ecx\n");
SaveReg("memhandler", "edx"); /* save params */
/* data, address */
if (!call_convention) /* stack-based */
{
e("push edx\n");
e("push ebx\n");
}
else /* register-based */
{
e("mov eax, ebx\n");
/* don't need: e("mov edx, edx\n"); */
}
switch (i)
{
case 0: e("call dword [write_byte]\n"); break;
case 1: e("call dword [write_word]\n"); break;
case 2: e("call dword [write_long]\n"); break;
}
if (!call_convention)
e("add esp, byte 8\n");
RestoreReg("memhandler", "edx"); /* restore params */
e("mov esi, [pc]\n");
e("mov ecx, [remaining]\n"); /* this could have been changed */
RestoreReg("memhandler", "eax");
RestoreReg("memhandler", "ebp");
e("add esi, ebp\n"); /* base+pc=pc pointer */
e("xor edi, edi\n"); /* keep clear for fetch */
e("ret\n");
}
}
}
/*****************************************************************************
* main() and Friends */
int FindV(char *option, int p, int h, int u, int argc, char **argv, int m)
{
static int t[128] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
int i;
char *c;
if (m) /* mark as touched */
{
if (m < 128)
t[m] = 1;
return 0;
}
if (argc > 128)
argc = 128; /* maximum this function can handle is 128 */
if (u) /* find first untouched element */
{
for (i = 1; i < argc; i++)
{
if (!t[i]) /* 0 indicates untouched */
return i;
}
return 0;
}
if (p) /* find option and return integer value following it */
{
for (i = 1; i < argc; i++)
{
if (strcmp(argv[i], option) == 0) /* found */
{
if (i >= (argc - 1)) /* bounds! */
return 0;
t[i + 1] = t[i] = 1; /* touched */
if (!h)
return atoi(argv[i + 1]);
else
return strtoul(argv[i + 1], &c, 16);
}
}
return 0; /* no match */
}
else /* find option and return position */
{
for (i = 1; i < argc; i++)
{
if (strcmp(argv[i], option) == 0)
{
t[i] = 1;
return i; /* found! return position */
}
}
return 0;
}
return 0;
}
void ShowHelp()
{
printf("Make68K Version "VERSION" by Bart Trzynadlowski: Turbo68K Source Emitter\n");
printf("Usage: make68k <outfile> [options]\n");
printf("Options: -?,-h Show this help text\n");
printf(" -mpu <type> Processor to emulate [Default=68000]\n");
printf(" -addr <bits> Address bus width [Default=24]\n");
printf(" -illegal Emulate illegal instruction exceptions [Default]\n");
printf(" -noillegal Treat illegal instructions as errors\n");
printf(" -dummyread Emulate 68000 dummy reads [Default]\n");
printf(" -nodummyread Do not emulate dummy reads\n");
printf(" -skip Skip over idle loops\n");
printf(" -noskip Do not skip over idle loops [Default]\n");
printf(" -brafetch Update fetch pointer on Bcc, BRA, BSR, DBcc\n");
printf(" -nobrafetch Do not update fetch pointer for branches [Default]\n");
printf(" -pcfetch Special PC-relative read mode\n");
printf(" -nopcfetch Use data regions for PC-relative reading [Default]\n");
printf(" -multiaddr Address spaces for supervisor and user [Default]\n");
printf(" -singleaddr Single address space for supervisor and user\n");
printf(" -defmap Definable memory map arrays [Default]\n");
printf(" -handler Single handlers for memory access\n");
printf(" -stackcall Stack calling conventions [Default]\n");
printf(" -regcall Register calling conventions\n");
printf(" -id <string> Add string to beginning of identifiers\n");
exit(0);
}
void SetAddrMask(int num_bits)
{
addr_bits = num_bits;
if (!num_bits)
{
addr_mask = 0xffffff; /* 24-bit is default address bus width */
addr_bits = 24;
}
else
addr_mask = (unsigned) pow(2, num_bits) - 1;
}
int main(int argc, char **argv)
{
int i, j, k, l, f;
#ifdef PROFILE
for (i = 0; i < 512; i++) prof[i] = NULL;
#endif
if (argc <= 1 || FindV("-?", 0, 0, 0, argc, argv, 0) || FindV("-h", 0, 0, 0, argc, argv, 0))
ShowHelp();
mpu = FindV("-mpu", 1, 0, 0, argc, argv, 0);
switch (mpu) /* make sure a valid MPU type was specified */
{
case 0:
mpu = 68000;
break;
case 68000:
case 68010:
break;
default:
fprintf(stderr, "Make68K: Unknown or unsupported processor type: %d\n", mpu);
exit(1);
}
SetAddrMask(FindV("-addr", 1, 0, 0, argc, argv, 0));
if (FindV("-illegal", 0, 0, 0, argc, argv, 0)) illegal = 1;
if (FindV("-noillegal", 0, 0, 0, argc, argv, 0)) illegal = 0;
if (FindV("-dummyread", 0, 0, 0, argc, argv, 0)) dummyread = 1;
if (FindV("-nodummyread", 0, 0, 0, argc, argv, 0)) dummyread = 0;
if (FindV("-skip", 0, 0, 0, argc, argv, 0)) skip = 1;
if (FindV("-noskip", 0, 0, 0, argc, argv, 0)) skip = 0;
if (FindV("-brafetch", 0, 0, 0, argc, argv, 0)) brafetch = 1;
if (FindV("-nobrafetch", 0, 0, 0, argc, argv, 0)) brafetch = 0;
if (FindV("-pcfetch", 0, 0, 0, argc, argv, 0)) pcfetch = 1;
if (FindV("-nopcfetch", 0, 0, 0, argc, argv, 0)) pcfetch = 0;
if (FindV("-multiaddr", 0, 0, 0, argc, argv, 0)) multiaddr = 1;
if (FindV("-singleaddr", 0, 0, 0, argc, argv, 0)) multiaddr = 0;
if (FindV("-defmap", 0, 0, 0, argc, argv, 0)) memmap_type = 0;
if (FindV("-handler", 0, 0, 0, argc, argv, 0)) memmap_type = 1;
if (FindV("-stackcall", 0, 0, 0, argc, argv, 0)) call_convention = 0;
if (FindV("-regcall", 0, 0, 0, argc, argv, 0)) call_convention = 1;
if (FindV("-debug", 0, 0, 0, argc, argv, 0)) debug = 1;
if ((i = FindV("-id", 0, 0, 0, argc, argv, 0)))
{
if ((i + 1) < argc) /* make sure we're within argv[] bounds */
{
if (strlen(argv[i + 1]) > 16)
{
fprintf(stderr, "Make68K: Identifier string is too long (maximum is 16 characters)\n");
exit(1);
}
strncpy(id, argv[i + 1], 16);
FindV(NULL, 0, 0, 0, argc, argv, i + 1);
}
}
if (!(f = FindV(NULL, 0, 0, 1, argc, argv, 0)))
{
fprintf(stderr, "Make68K: No output file specified\n");
exit(1);
}
if ((fp = fopen(argv[f], "w")) == NULL)
{
fprintf(stderr, "Make68K: Failed to open file for writing: %s\n", argv[f]);
exit(1);
}
for (i = 0; i < 65536; i++) decoded[i] = 0;
printf("Make68K Version "VERSION" by Bart Trzynadlowski: Turbo68K Source Emitter\n");
printf("\n");
printf("Emitting file: %s\n", argv[f]);
printf("Configuration:\n");
e(";\n");
e("; Turbo68K Version "VERSION": Motorola 680X0 emulator\n");
e("; Copyright 2000-2002 Bart Trzynadlowski, see \"README.TXT\" for terms of use\n");
e("; Assemble with NASM (http://www.web-sites.co.uk/nasm) only\n");
e(";\n");
e("; Configuration:\n");
printf("- %d processor\n", mpu);
e("; - %d processor\n", mpu);
printf("- %d-bit addresses\n", addr_bits);
e("; - %d-bit addresses\n", addr_bits);
printf("- %s\n", illegal ? "Illegal instruction, Line 1010, and Line 1111 exceptions" : "Illegal instructions reported as errors");
e("; - %s\n", illegal ? "Illegal instruction, Line 1010, and Line 1111 exceptions" : "Illegal instructions reported as errors");
if (mpu == 68000)
{
printf("- Dummy reads %s\n", dummyread ? "emulated" : "not emulated");
e("; - Dummy reads %s\n", dummyread ? "emulated" : "not emulated");
}
printf("- Idle loop skipping %s\n", skip ? "enabled" : "disabled");
e("; - Idle loop skipping %s\n", skip ? "enabled" : "disabled");
printf("- Fetch pointer%supdated for Bcc, BRA, BSR, and DBcc\n", brafetch ? " " : " not ");
e("; - Fetch pointer%supdated for Bcc, BRA, BSR, and DBcc\n", brafetch ? " " : " not ");
printf("- %s regions used for PC-relative reading\n", pcfetch ? "Special PC fetch" : "Data");
e("; - %s regions used for PC-relative reading\n", pcfetch ? "Special PC fetch" : "Data");
printf("- %s for supervisor and user\n", multiaddr ? "Separate address spaces" : "Single address space");
e("; - %s for supervisor and user\n", multiaddr ? "Separate address spaces" : "Single address space");
switch (memmap_type)
{
case 0: printf("- Definable memory map arrays\n");
e("; - Definable memory map arrays\n");
break;
case 1: printf("- Single memory handlers\n");
e("; - Single memory handlers\n");
break;
case 2: printf("- Low-level memory handlers\n");
e("; - Low-level memory handlers\n");
break;
}
printf("- %s calling conventions\n", call_convention ? "Register" : "Stack");
e("; - %s calling conventions\n", call_convention ? "Register" : "Stack");
if (id[0] != '\0')
{
printf("- Identifers start with: %s\n", id);
e("; - Identifiers start with: %s\n", id);
}
if (debug)
{
printf("- Debug function called at every instruction\n");
e("; - Debug function called at every instruction\n");
}
e(";\n");
printf("\n");
e("bits 32\n");
e("section .data\n");
EmitData();
e("section .text\n");
EmitCode();
printf("Generating instruction handlers:\n");
EmitInstructions();
EmitExceptions();
e("section .bss\n");
CacheAlign();
e("jmptab resd 65536\n");
/*
* Emit the compressed jump table
*
* Format:
* dw 0x8000 + # ; # of handler addresses follow, each repeated 8 times
* ... dd # ... ; handler addresses...
* dw 0xc000 + # ; # of handler addresses follow, each repeated 1 time
* ... dd # ... ; handler addresses...
* dw 0x0000 + # ; repeat following address handler # times
* dd # ; handler address to repeat
* dd -1 ; terminator
*/
e("section .data\n");
Align(4);
e("compressed_jmptab:\n");
i = 0;
j = 0;
while (i < 65536)
{
if (!decoded[i]) /* invalid */
{
for (i = i; decoded[i] == 0 && i < 65536; i++) j++;
e("dw 0x0000 + %d\n", j);
e("dd I_Invalid\n");
j = 0;
}
else
{
if (decoded[i] == 8) /* if more handlers*8, make a big block */
{
k = 0;
l = i; /* save opcode # */
while (decoded[i] == 8)
{
i += decoded[i];
k++;
}
e("dw 0x8000 + %d\n", k); /* # of handlers w/ 8 reps */
while (k != 0) /* list handlers */
{
e("dd I%04X\n", l);
l += decoded[l];
k--;
}
}
if (decoded[i] == 1) /* if more handlers*1, make a big block */
{
k = 0;
l = i; /* save opcode # */
while (decoded[i] == 1)
{
i += decoded[i];
k++;
}
e("dw 0xc000 + %d\n", k); /* # of handlers w/ 1 reps */
while (k != 0) /* list handlers */
{
e("dd I%04X\n", l);
l += decoded[l];
k--;
}
}
else if (decoded[i] == 0) /* invalid instruction */
{
e("dw 1\n");
e("dd I_Invalid\n");
i++;
}
else /* misc. */
{
e("dw %d\n", decoded[i]); /* # of valids */
e("dd I%04X\n", i); /* opcode */
i += decoded[i];
}
}
}
e("dd -1\n");
/*
* Emit profile data
*
* Step 1: Output the names of the instructions.
* Step 2: Output the usage count variables and pointers to the strings.
*/
#ifdef PROFILE
for (i = 0; prof[i] != NULL && i < 512; i++)
e("str%s db \"%s\",0\n", prof[i], prof[i]);
e("global _t68k_prof\n");
e("_t68k_prof: dd begin_t68k_prof\n");
e("begin_t68k_prof:\n");
for (i = 0; prof[i] != NULL && i < 512; i++)
{
e("prof%s dd 0, str%s\n", prof[i], prof[i]);
free(prof[i]);
}
e("dd -1, -1\n");
#endif
fclose(fp);
printf("Total:\t%d\n", num_handlers);
printf("\nSee \"README.TXT\" for terms of use and documentation!\n");
return 0;
}
/*
*
* Developers' Notes: A Guide to the Guts of Turbo68K ;)
* -----------------------------------------------------
*
* Register Usage:
*
* EAX: ****************NZ*****C*******V
* AH: NZ*****C AL: *******V
* EBX: Address
* ECX: Cycle counter
* EDX: Data
* ESI: Current fetch (PC) pointer
* EDI: Instruction fetch/decoding (keep upper 16 bits clear!). At the
* beginning of any instruction handler, contains opcode in lower 16 bits
* EBP: Pointer to base of PC region. May be below the base if the unused PC
* bits are non-zero
* ---
* MMX optimizations are unsupported -- DO NOT USE THEM! They happen to slow
* things down
*
* MMX Register Usage:
*
* MM0: Saved EAX (memhandler_eax)
* MM1: Saved EBX (memhandler_ebx)
* MM2: Saved EBP (memhandler_ebp)
* MM3: Saved EDX (run_edx)
* MM4: Saved ESI (run_esi)
* MM5: Saved EDI (run_edi)
* MM6,MM7: Misc. usage
* ---
* Format of context.intr[]:
*
* intr[0-6] = Interrupt levels 1-7, elements contain the vector #, if 0 then
* no interrupt is pending at this level
* intr[7] = Number of interrupts pending (set to 0 at reset)
* ---
* Format of context.status:
*
* 0000 0000 0000 0000 0000 0000 0000 0ISR
*
* I=Interrupt; 1=interrupts being processed, 0=not
* S=Stop; 1=68K is stopped (STOP instruction), 0=not
* R=Running; 1=68K is running, 0=not
* ---
* Format of decoded[]:
*
* [0] = 1: 2 opcode 0 handlers
* [1] = 0: see above.. this is unused
* [2] = next handler... (0 + 2)
* ---
* SR values obtained from instructions are ANDed with 0xa71f to mask out
* unused bits. CCR values are ANDed with 0x1f to mask out the unused bits.
* This is how a real 68K behaves.
* ---
* Memory mapping types: The low-level handler mode code is present, but not
* accessible. I decided to forbid using it because of performance problems.
* ---
* The unused address bits of the PC are preserved. They are only lost if the
* PC goes out of bounds.
*/