mirror of
				https://github.com/RetroDECK/Supermodel.git
				synced 2025-04-10 19:15:14 +00:00 
			
		
		
		
	 c73cb830dc
			
		
	
	
		c73cb830dc
		
	
	
	
	
		
			
			- Added a 68K interface, CPU/68K/M68K.*. - Moved Turbo68K source files to CPU/68K/Turbo68K. It is no longer used but is being retained in the source tree for now. The LSR instruction has not been fixed in this update. - Changed sound board code to use the new 68K interface (and hence, Musashi). - Changed version string from 0.2-WIP to 0.2a-WIP. This is still an alpha version. - Above changes have broken the 68K debugger.
		
			
				
	
	
		
			8987 lines
		
	
	
		
			308 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			8987 lines
		
	
	
		
			308 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * 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.
 | |
|  */
 |