]> Shamusworld >> Repos - virtualjaguar/blobdiff - src/m68000/m68kinterface.c
Added new 68000 cpu core based on UAE's 68000. Here be dragons. ;-)
[virtualjaguar] / src / m68000 / m68kinterface.c
diff --git a/src/m68000/m68kinterface.c b/src/m68000/m68kinterface.c
new file mode 100644 (file)
index 0000000..5292ca5
--- /dev/null
@@ -0,0 +1,631 @@
+//
+// m68kinterface.c: Code interface to the UAE 68000 core and support code
+//
+// by James L. Hammons
+// (C) 2011 Underground Software
+//
+// JLH = James L. Hammons <jlhamm@acm.org>
+//
+// Who  When        What
+// ---  ----------  -------------------------------------------------------------
+// JLH  10/28/2011  Created this file ;-)
+//
+
+#include "m68kinterface.h"
+#include "cpudefs.h"
+#include "inlines.h"
+#include "cpuextra.h"
+#include "readcpu.h"
+
+
+// Exception Vectors handled by emulation
+#define EXCEPTION_BUS_ERROR                2 /* This one is not emulated! */
+#define EXCEPTION_ADDRESS_ERROR            3 /* This one is partially emulated (doesn't stack a proper frame yet) */
+#define EXCEPTION_ILLEGAL_INSTRUCTION      4
+#define EXCEPTION_ZERO_DIVIDE              5
+#define EXCEPTION_CHK                      6
+#define EXCEPTION_TRAPV                    7
+#define EXCEPTION_PRIVILEGE_VIOLATION      8
+#define EXCEPTION_TRACE                    9
+#define EXCEPTION_1010                    10
+#define EXCEPTION_1111                    11
+#define EXCEPTION_FORMAT_ERROR            14
+#define EXCEPTION_UNINITIALIZED_INTERRUPT 15
+#define EXCEPTION_SPURIOUS_INTERRUPT      24
+#define EXCEPTION_INTERRUPT_AUTOVECTOR    24
+#define EXCEPTION_TRAP_BASE               32
+
+// These are found in obj/cpustbl.c (generated by gencpu)
+
+//extern const struct cputbl op_smalltbl_0_ff[];       /* 68040 */
+//extern const struct cputbl op_smalltbl_1_ff[];       /* 68020 + 68881 */
+//extern const struct cputbl op_smalltbl_2_ff[];       /* 68020 */
+//extern const struct cputbl op_smalltbl_3_ff[];       /* 68010 */
+extern const struct cputbl op_smalltbl_4_ff[]; /* 68000 */
+extern const struct cputbl op_smalltbl_5_ff[]; /* 68000 slow but compatible.  */
+
+// Externs, supplied by the user...
+extern int irq_ack_handler(int);
+
+// Function prototypes...
+STATIC_INLINE void m68ki_check_interrupts(void);
+void m68ki_exception_interrupt(uint intLevel);
+STATIC_INLINE uint32_t m68ki_init_exception(void);
+STATIC_INLINE void m68ki_stack_frame_3word(uint pc, uint sr);
+unsigned long IllegalOpcode(uint32_t opcode);
+void BuildCPUFunctionTable(void);
+
+// Local "Global" vars
+static int32_t initialCycles;
+cpuop_func * cpuFunctionTable[65536];
+
+
+#if 0
+#define ADD_CYCLES(A)    m68ki_remaining_cycles += (A)
+#define USE_CYCLES(A)    m68ki_remaining_cycles -= (A)
+#define SET_CYCLES(A)    m68ki_remaining_cycles = A
+#define GET_CYCLES()     m68ki_remaining_cycles
+#define USE_ALL_CYCLES() m68ki_remaining_cycles = 0
+
+#define CPU_INT_LEVEL    m68ki_cpu.int_level /* ASG: changed from CPU_INTS_PENDING */
+#define CPU_INT_CYCLES   m68ki_cpu.int_cycles /* ASG */
+#define CPU_STOPPED      m68ki_cpu.stopped
+#define CPU_PREF_ADDR    m68ki_cpu.pref_addr
+#define CPU_PREF_DATA    m68ki_cpu.pref_data
+#define CPU_ADDRESS_MASK m68ki_cpu.address_mask
+#define CPU_SR_MASK      m68ki_cpu.sr_mask
+#endif
+
+#define CPU_DEBUG
+void Dasm(uint32_t offset, uint32_t qt)
+{
+#ifdef CPU_DEBUG
+       static char buffer[2048];//, mem[64];
+       int pc = offset, oldpc;
+       uint32_t i;
+
+       for(i=0; i<qt; i++)
+       {
+/*             oldpc = pc;
+               for(int j=0; j<64; j++)
+                       mem[j^0x01] = jaguar_byte_read(pc + j);
+
+               pc += Dasm68000((char *)mem, buffer, 0);
+               WriteLog("%08X: %s\n", oldpc, buffer);//*/
+               oldpc = pc;
+               pc += m68k_disassemble(buffer, pc, 0);//M68K_CPU_TYPE_68000);
+//             WriteLog("%08X: %s\n", oldpc, buffer);//*/
+               printf("%08X: %s\n", oldpc, buffer);//*/
+       }
+#endif
+}
+
+
+void m68k_set_cpu_type(unsigned int type)
+{
+}
+
+// Pulse the RESET line on the CPU
+void m68k_pulse_reset(void)
+{
+       static uint emulation_initialized = 0;
+
+       // The first call to this function initializes the opcode handler jump table
+       if (!emulation_initialized)
+       {
+#if 0
+               m68ki_build_opcode_table();
+               m68k_set_int_ack_callback(NULL);
+               m68k_set_bkpt_ack_callback(NULL);
+               m68k_set_reset_instr_callback(NULL);
+               m68k_set_pc_changed_callback(NULL);
+               m68k_set_fc_callback(NULL);
+               m68k_set_instr_hook_callback(NULL);
+#else
+               // Build opcode handler table here...
+               read_table68k();
+               do_merges();
+               BuildCPUFunctionTable();
+#endif
+               emulation_initialized = 1;
+       }
+
+//     if (CPU_TYPE == 0)      /* KW 990319 */
+//             m68k_set_cpu_type(M68K_CPU_TYPE_68000);
+
+#if 0
+       /* Clear all stop levels and eat up all remaining cycles */
+       CPU_STOPPED = 0;
+       SET_CYCLES(0);
+
+       /* Turn off tracing */
+       FLAG_T1 = FLAG_T0 = 0;
+       m68ki_clear_trace();
+       /* Interrupt mask to level 7 */
+       FLAG_INT_MASK = 0x0700;
+       /* Reset VBR */
+       REG_VBR = 0;
+       /* Go to supervisor mode */
+       m68ki_set_sm_flag(SFLAG_SET | MFLAG_CLEAR);
+
+       /* Invalidate the prefetch queue */
+#if M68K_EMULATE_PREFETCH
+       /* Set to arbitrary number since our first fetch is from 0 */
+       CPU_PREF_ADDR = 0x1000;
+#endif /* M68K_EMULATE_PREFETCH */
+
+       /* Read the initial stack pointer and program counter */
+       m68ki_jump(0);
+       REG_SP = m68ki_read_imm_32();
+       REG_PC = m68ki_read_imm_32();
+       m68ki_jump(REG_PC);
+#else
+       regs.stopped = 0;
+       regs.remainingCycles = 0;
+       
+       regs.intmask = 0x07;
+       regs.s = 1;                                                             // Supervisor mode ON
+
+       // Read initial SP and PC
+       m68k_areg(regs, 7) = m68k_read_memory_32(0);
+       m68k_setpc(m68k_read_memory_32(4));
+       refill_prefetch(m68k_getpc(), 0);
+#endif
+}
+
+int m68k_execute(int num_cycles)
+{
+#if 0
+       /* Make sure we're not stopped */
+       if (CPU_STOPPED)
+       {
+               /* We get here if the CPU is stopped or halted */
+               SET_CYCLES(0);
+               CPU_INT_CYCLES = 0;
+
+               return num_cycles;
+       }
+#else
+       if (regs.stopped)
+       {
+               regs.remainingCycles = 0;       // int32_t
+               regs.interruptCycles = 0;       // uint32_t
+
+               return num_cycles;
+       }
+#endif
+
+#if 0
+       /* Set our pool of clock cycles available */
+       SET_CYCLES(num_cycles);
+       m68ki_initial_cycles = num_cycles;
+
+       /* ASG: update cycles */
+       USE_CYCLES(CPU_INT_CYCLES);
+       CPU_INT_CYCLES = 0;
+
+       /* Return point if we had an address error */
+       m68ki_set_address_error_trap(); /* auto-disable (see m68kcpu.h) */
+#else
+       regs.remainingCycles = num_cycles;
+       /*int32_t*/ initialCycles = num_cycles;
+       
+       regs.remainingCycles -= regs.interruptCycles;
+       regs.interruptCycles = 0;
+#endif
+
+       /* Main loop.  Keep going until we run out of clock cycles */
+       do
+       {
+#if 0
+               /* Set tracing accodring to T1. (T0 is done inside instruction) */
+               m68ki_trace_t1(); /* auto-disable (see m68kcpu.h) */
+
+               /* Set the address space for reads */
+               m68ki_use_data_space(); /* auto-disable (see m68kcpu.h) */
+
+               /* Call external hook to peek at CPU */
+               m68ki_instr_hook(); /* auto-disable (see m68kcpu.h) */
+
+               /* Record previous program counter */
+               REG_PPC = REG_PC;
+
+               /* Read an instruction and call its handler */
+               REG_IR = m68ki_read_imm_16();
+               m68ki_instruction_jump_table[REG_IR]();
+               USE_CYCLES(CYC_INSTRUCTION[REG_IR]);
+
+               /* Trace m68k_exception, if necessary */
+               m68ki_exception_if_trace(); /* auto-disable (see m68kcpu.h) */
+#else
+//Testing Hover Strike...
+#if 0
+//Dasm(regs.pc, 1);
+static int hitCount = 0;
+static int inRoutine = 0;
+static int instSeen;
+
+//if (regs.pc == 0x80340A)
+if (regs.pc == 0x803416)
+{
+       hitCount++;
+       inRoutine = 1;
+       instSeen = 0;
+       printf("%i: $80340A start. A0=%08X, A1=%08X ", hitCount, regs.regs[8], regs.regs[9]);
+}
+else if (regs.pc == 0x803422)
+{
+       inRoutine = 0;
+       printf("(%i instructions)\n", instSeen);
+}
+
+if (inRoutine)
+       instSeen++;
+#endif
+               uint32_t opcode = get_iword(0);
+               int32_t cycles = (int32_t)(*cpuFunctionTable[opcode])(opcode);
+               regs.remainingCycles -= cycles;
+//printf("Executed opcode $%04X (%i cycles)...\n", opcode, cycles);
+#endif
+       }
+#if 0
+       while (GET_CYCLES() > 0);
+#else
+       while (regs.remainingCycles > 0);
+#endif
+
+#if 0
+       /* set previous PC to current PC for the next entry into the loop */
+       REG_PPC = REG_PC;
+
+       /* ASG: update cycles */
+       USE_CYCLES(CPU_INT_CYCLES);
+       CPU_INT_CYCLES = 0;
+
+       /* return how many clocks we used */
+       return m68ki_initial_cycles - GET_CYCLES();
+#else
+       regs.remainingCycles -= regs.interruptCycles;
+       regs.interruptCycles = 0;
+
+       // Return # of clock cycles used
+       return initialCycles - regs.remainingCycles;
+#endif
+}
+
+/* ASG: rewrote so that the int_level is a mask of the IPL0/IPL1/IPL2 bits */
+void m68k_set_irq(unsigned int intLevel)
+{
+#if 0
+       uint old_level = CPU_INT_LEVEL;
+       CPU_INT_LEVEL = int_level << 8;
+
+       /* A transition from < 7 to 7 always interrupts (NMI) */
+       /* Note: Level 7 can also level trigger like a normal IRQ */
+       if(old_level != 0x0700 && CPU_INT_LEVEL == 0x0700)
+               m68ki_exception_interrupt(7); /* Edge triggered level 7 (NMI) */
+       else
+               m68ki_check_interrupts(); /* Level triggered (IRQ) */
+#else
+       int oldLevel = regs.intLevel;
+       regs.intLevel = intLevel;
+
+       // A transition from < 7 to 7 always interrupts (NMI)
+       // Note: Level 7 can also level trigger like a normal IRQ
+       if (oldLevel != 0x07 && regs.intLevel == 0x07)
+               m68ki_exception_interrupt(7);           // Edge triggered level 7 (NMI)
+       else
+               m68ki_check_interrupts();                       // Level triggered (IRQ)
+#endif
+}
+
+// Check for interrupts
+STATIC_INLINE void m68ki_check_interrupts(void)
+{
+#if 0
+       if(CPU_INT_LEVEL > FLAG_INT_MASK)
+               m68ki_exception_interrupt(CPU_INT_LEVEL>>8);
+#else
+       if (regs.intLevel > regs.intmask)
+               m68ki_exception_interrupt(regs.intLevel);
+#endif
+}
+
+// Service an interrupt request and start exception processing
+void m68ki_exception_interrupt(uint intLevel)
+{
+#if 0
+       uint vector;
+       uint sr;
+       uint new_pc;
+
+       /* Turn off the stopped state */
+       CPU_STOPPED &= ~STOP_LEVEL_STOP;
+
+       /* If we are halted, don't do anything */
+       if(CPU_STOPPED)
+               return;
+
+       /* Acknowledge the interrupt */
+       vector = m68ki_int_ack(int_level);
+
+       /* Get the interrupt vector */
+       if(vector == M68K_INT_ACK_AUTOVECTOR)
+               /* Use the autovectors.  This is the most commonly used implementation */
+               vector = EXCEPTION_INTERRUPT_AUTOVECTOR+int_level;
+       else if(vector == M68K_INT_ACK_SPURIOUS)
+               /* Called if no devices respond to the interrupt acknowledge */
+               vector = EXCEPTION_SPURIOUS_INTERRUPT;
+       else if(vector > 255)
+       {
+               M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: Interrupt acknowledge returned invalid vector $%x\n",
+                        m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC), vector));
+               return;
+       }
+
+       /* Start exception processing */
+       sr = m68ki_init_exception();
+
+       /* Set the interrupt mask to the level of the one being serviced */
+       FLAG_INT_MASK = int_level<<8;
+
+       /* Get the new PC */
+       new_pc = m68ki_read_data_32((vector<<2) + REG_VBR);
+
+       /* If vector is uninitialized, call the uninitialized interrupt vector */
+       if(new_pc == 0)
+               new_pc = m68ki_read_data_32((EXCEPTION_UNINITIALIZED_INTERRUPT<<2) + REG_VBR);
+
+       /* Generate a stack frame */
+       m68ki_stack_frame_0000(REG_PC, sr, vector);
+
+       if(FLAG_M && CPU_TYPE_IS_EC020_PLUS(CPU_TYPE))
+       {
+               /* Create throwaway frame */
+               m68ki_set_sm_flag(FLAG_S);      /* clear M */
+               sr |= 0x2000; /* Same as SR in master stack frame except S is forced high */
+               m68ki_stack_frame_0001(REG_PC, sr, vector);
+       }
+
+       m68ki_jump(new_pc);
+
+       /* Defer cycle counting until later */
+       CPU_INT_CYCLES += CYC_EXCEPTION[vector];
+
+#if !M68K_EMULATE_INT_ACK
+       /* Automatically clear IRQ if we are not using an acknowledge scheme */
+       CPU_INT_LEVEL = 0;
+#endif /* M68K_EMULATE_INT_ACK */
+#else
+       // Turn off the stopped state
+       regs.stopped = 0;
+
+//JLH: need to add halt state?
+       // If we are halted, don't do anything
+//     if (CPU_STOPPED)
+//             return;
+
+       // Acknowledge the interrupt (NOTE: This is a user supplied function!)
+       uint32_t vector = irq_ack_handler(intLevel);
+
+       // Get the interrupt vector
+       if (vector == M68K_INT_ACK_AUTOVECTOR)
+               // Use the autovectors.  This is the most commonly used implementation
+               vector = EXCEPTION_INTERRUPT_AUTOVECTOR + intLevel;
+       else if (vector == M68K_INT_ACK_SPURIOUS)
+               // Called if no devices respond to the interrupt acknowledge
+               vector = EXCEPTION_SPURIOUS_INTERRUPT;
+       else if (vector > 255)
+       {
+//             M68K_DO_LOG_EMU((M68K_LOG_FILEHANDLE "%s at %08x: Interrupt acknowledge returned invalid vector $%x\n",
+//                      m68ki_cpu_names[CPU_TYPE], ADDRESS_68K(REG_PC), vector));
+               return;
+       }
+
+       // Start exception processing
+       uint32_t sr = m68ki_init_exception();
+
+       // Set the interrupt mask to the level of the one being serviced
+       regs.intmask = intLevel;
+
+       // Get the new PC
+       uint32_t newPC = m68k_read_memory_32(vector << 2);
+
+       // If vector is uninitialized, call the uninitialized interrupt vector
+       if (newPC == 0)
+               newPC = m68k_read_memory_32(EXCEPTION_UNINITIALIZED_INTERRUPT << 2);
+
+       // Generate a stack frame
+       m68ki_stack_frame_3word(regs.pc, sr);
+
+       m68k_setpc(newPC);
+
+       // Defer cycle counting until later
+       regs.interruptCycles += 56;     // NOT ACCURATE-- !!! FIX !!!
+//     CPU_INT_CYCLES += CYC_EXCEPTION[vector];
+#endif
+}
+
+// Initiate exception processing
+STATIC_INLINE uint32_t m68ki_init_exception(void)
+{
+#if 0
+       /* Save the old status register */
+       uint sr = m68ki_get_sr();
+
+       /* Turn off trace flag, clear pending traces */
+       FLAG_T1 = FLAG_T0 = 0;
+       m68ki_clear_trace();
+       /* Enter supervisor mode */
+       m68ki_set_s_flag(SFLAG_SET);
+
+       return sr;
+#else
+       MakeSR();
+       uint32_t sr = regs.sr;                                  // Save old status register
+       regs.s = 1;                                                             // Set supervisor mode
+
+       return sr;
+#endif
+}
+
+// 3 word stack frame (68000 only)
+STATIC_INLINE void m68ki_stack_frame_3word(uint32_t pc, uint32_t sr)
+{
+#if 0
+       m68ki_push_32(pc);
+       m68ki_push_16(sr);
+#else
+       // Push PC on stack:
+       m68k_areg(regs, 7) -= 4;
+       m68k_write_memory_32(m68k_areg(regs, 7), pc);
+       // Push SR on stack:
+       m68k_areg(regs, 7) -= 2;
+       m68k_write_memory_16(m68k_areg(regs, 7), sr);
+#endif
+}
+
+unsigned int m68k_get_reg(void * context, m68k_register_t reg)
+{
+       if (reg <= M68K_REG_A7)
+               return regs.regs[reg];
+       else if (reg == M68K_REG_PC)
+               return regs.pc;
+       else if (reg == M68K_REG_SR)
+       {
+               MakeSR();
+               return regs.sr;
+       }
+       else if (reg == M68K_REG_SP)
+               return regs.regs[15];
+
+       return 0;
+}
+
+void m68k_set_reg(m68k_register_t reg, unsigned int value)
+{
+       if (reg <= M68K_REG_A7)
+               regs.regs[reg] = value;
+       else if (reg == M68K_REG_PC)
+               regs.pc = value;
+       else if (reg == M68K_REG_SR)
+       {
+               regs.sr = value;
+               MakeFromSR();
+       }
+       else if (reg == M68K_REG_SP)
+               regs.regs[15] = value;
+}
+
+//
+// Check if the instruction is a valid one
+//
+unsigned int m68k_is_valid_instruction(unsigned int instruction, unsigned int cpu_type)
+{
+       instruction &= 0xFFFF;
+
+       if (cpuFunctionTable[instruction] == IllegalOpcode)
+               return 0;
+
+       return 1;
+}
+
+// Dummy functions, for now, until we prove the concept here. :-)
+
+// Temp, while we're using the Musashi disassembler...
+#if 0
+unsigned int m68k_disassemble(char * str_buff, unsigned int pc, unsigned int cpu_type)
+{
+       return 0;
+}
+#endif
+
+int m68k_cycles_run(void) {}              /* Number of cycles run so far */
+int m68k_cycles_remaining(void) {}        /* Number of cycles left */
+void m68k_modify_timeslice(int cycles) {} /* Modify cycles left */
+//void m68k_end_timeslice(void) {}          /* End timeslice now */
+
+void m68k_end_timeslice(void)
+{
+#if 0
+       m68ki_initial_cycles = GET_CYCLES();
+       SET_CYCLES(0);
+#else
+       initialCycles = regs.remainingCycles;
+       regs.remainingCycles = 0;
+#endif
+}
+
+
+unsigned long IllegalOpcode(uint32_t opcode)
+{
+#if 0
+       uint32_t pc = m68k_getpc ();
+#endif
+       if ((opcode & 0xF000) == 0xF000)
+       {
+               Exception(0x0B, 0, M68000_EXC_SRC_CPU); // LineF exception...
+               return 4;
+       }
+       else if ((opcode & 0xF000) == 0xA000)
+       {
+               Exception(0x0A, 0, M68000_EXC_SRC_CPU); // LineA exception...
+               return 4;
+       }
+
+#if 0
+       write_log ("Illegal instruction: %04x at %08lx\n", opcode, (long)pc);
+#endif
+
+       Exception(0x04, 0, M68000_EXC_SRC_CPU);         // Illegal opcode exception...
+       return 4;
+}
+
+
+void BuildCPUFunctionTable(void)
+{
+       int i;
+       unsigned long opcode;
+
+       // We're only using the "fast" 68000 emulation here, not the "compatible"
+#if 0
+       const struct cputbl * tbl = (currprefs.cpu_compatible
+               ? op_smalltbl_5_ff : op_smalltbl_4_ff);
+#else
+       const struct cputbl * tbl = op_smalltbl_4_ff;
+#endif
+
+//     Log_Printf(LOG_DEBUG, "Building CPU function table (%d %d %d).\n",
+//             currprefs.cpu_level, currprefs.cpu_compatible, currprefs.address_space_24);
+
+       // Set all instructions to Illegal...
+       for(opcode=0; opcode<65536; opcode++)
+               cpuFunctionTable[opcode] = IllegalOpcode;
+
+       // Move functions from compact table into our full function table...
+       for(i=0; tbl[i].handler!=NULL; i++)
+               cpuFunctionTable[tbl[i].opcode] = tbl[i].handler;
+
+//JLH: According to readcpu.c, handler is set to -1 and never changes.
+// Actually, it does read this crap in readcpu.c, do_merges() does it... :-P
+// Again, seems like a build time thing could be done here...
+#if 1
+       for(opcode=0; opcode<65536; opcode++)
+       {
+//             if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > currprefs.cpu_level)
+               if (table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > 0)
+                       continue;
+
+               if (table68k[opcode].handler != -1)
+               {
+//printf("Relocate: $%04X->$%04X\n", table68k[opcode].handler, opcode);
+                       cpuop_func * f = cpuFunctionTable[table68k[opcode].handler];
+
+                       if (f == IllegalOpcode)
+                               abort();
+
+                       cpuFunctionTable[opcode] = f;
+               }
+       }
+#endif
+}