mirror of
https://github.com/ksherlock/mpw.git
synced 2024-10-05 14:56:17 +00:00
374 lines
11 KiB
C
374 lines
11 KiB
C
/*=========================================================================*/
|
|
/* Fellow */
|
|
/* CPU 68k exception handling functions */
|
|
/* */
|
|
/* Author: Petter Schau */
|
|
/* */
|
|
/* */
|
|
/* Copyright (C) 1991, 1992, 1996 Free Software Foundation, Inc. */
|
|
/* */
|
|
/* This program is free software; you can redistribute it and/or modify */
|
|
/* it under the terms of the GNU General Public License as published by */
|
|
/* the Free Software Foundation; either version 2, or (at your option) */
|
|
/* any later version. */
|
|
/* */
|
|
/* This program is distributed in the hope that it will be useful, */
|
|
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
|
|
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
|
|
/* GNU General Public License for more details. */
|
|
/* */
|
|
/* You should have received a copy of the GNU General Public License */
|
|
/* along with this program; if not, write to the Free Software Foundation, */
|
|
/* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
|
/*=========================================================================*/
|
|
|
|
#include "defs.h"
|
|
#include "CpuModule_Memory.h"
|
|
|
|
#include "CpuModule.h"
|
|
#include "CpuModule_Internal.h"
|
|
|
|
|
|
// MPW
|
|
static cpuLineExceptionFunc cpu_a_line_exception_func = NULL;
|
|
static cpuLineExceptionFunc cpu_f_line_exception_func = NULL;
|
|
|
|
void cpuSetALineExceptionFunc(cpuLineExceptionFunc func)
|
|
{
|
|
cpu_a_line_exception_func = func;
|
|
}
|
|
|
|
void cpuSetFLineExceptionFunc(cpuLineExceptionFunc func)
|
|
{
|
|
cpu_f_line_exception_func = func;
|
|
}
|
|
|
|
|
|
/* Function for exiting from mid-instruction exceptions */
|
|
static cpuMidInstructionExceptionFunc cpu_mid_instruction_exception_func;
|
|
|
|
static void cpuCallMidInstructionExceptionFunc(void)
|
|
{
|
|
cpu_mid_instruction_exception_func();
|
|
}
|
|
|
|
void cpuSetMidInstructionExceptionFunc(cpuMidInstructionExceptionFunc func)
|
|
{
|
|
cpu_mid_instruction_exception_func = func;
|
|
}
|
|
|
|
/* Function for notifying the emulator about a reset */
|
|
static cpuResetExceptionFunc cpu_reset_exception_func;
|
|
|
|
void cpuCallResetExceptionFunc(void)
|
|
{
|
|
cpu_reset_exception_func();
|
|
}
|
|
|
|
void cpuSetResetExceptionFunc(cpuResetExceptionFunc func)
|
|
{
|
|
cpu_reset_exception_func = func;
|
|
}
|
|
|
|
static char *cpuGetExceptionName(uint32_t vector_offset)
|
|
{
|
|
char *name;
|
|
|
|
if (vector_offset == 0x8)
|
|
name = "Exception: 2 - Bus error";
|
|
else if (vector_offset == 0xc)
|
|
name = "Exception: 3 - Address error";
|
|
else if (vector_offset == 0x10)
|
|
name = "Exception: 4 - Illegal Instruction";
|
|
else if (vector_offset == 0x14)
|
|
name = "Exception: 5 - Integer division by zero";
|
|
else if (vector_offset == 0x18)
|
|
name = "Exception: 6 - CHK, CHK2";
|
|
else if (vector_offset == 0x1c)
|
|
name = "Exception: 7 - FTRAPcc, TRAPcc, TRAPV";
|
|
else if (vector_offset == 0x20)
|
|
name = "Exception: 8 - Privilege Violation";
|
|
else if (vector_offset == 0x24)
|
|
name = "Exception: 9 - Trace";
|
|
else if (vector_offset == 0x28)
|
|
name = "Exception: 10 - A-Line";
|
|
else if (vector_offset == 0x2c)
|
|
name = "Exception: 11 - F-Line";
|
|
else if (vector_offset == 0x38)
|
|
name = "Exception: 14 - Format error";
|
|
else if (vector_offset >= 0x80 && vector_offset <= 0xbc)
|
|
name = "Exception: TRAP";
|
|
else
|
|
name = "Exception: Unknown";
|
|
|
|
return name;
|
|
}
|
|
|
|
/*===============================================
|
|
Sets up an exception
|
|
===============================================*/
|
|
|
|
void cpuExceptionFail(BOOLE executejmp)
|
|
{
|
|
// Avoid endless loop that will crash the emulator.
|
|
// The (odd) address error exception vector contained an odd address.
|
|
cpuCallResetExceptionFunc();
|
|
cpuHardReset();
|
|
cpuSetInstructionTime(132);
|
|
if (executejmp)
|
|
{
|
|
cpuCallMidInstructionExceptionFunc(); // Supposed to be doing setjmp/longjmp back to machine emulator code
|
|
}
|
|
}
|
|
|
|
void cpuThrowException(uint32_t vector_offset, uint32_t pc, BOOLE executejmp)
|
|
{
|
|
uint32_t vector_address;
|
|
BOOLE is_address_error_on_sub_020 = (cpuGetModelMajor() < 2 && vector_offset == 0xc);
|
|
BOOLE stack_is_even = !(cpuGetAReg(7) & 1);
|
|
BOOLE vbr_is_even = !(cpuGetVbr() & 1);
|
|
|
|
if ((is_address_error_on_sub_020 && !stack_is_even) || !vbr_is_even)
|
|
{
|
|
cpuExceptionFail(executejmp);
|
|
return;
|
|
}
|
|
|
|
#ifdef CPU_INSTRUCTION_LOGGING
|
|
cpuCallExceptionLoggingFunc(cpuGetExceptionName(vector_offset), cpuGetOriginalPC(), cpuGetCurrentOpcode());
|
|
#endif
|
|
|
|
cpuActivateSSP();
|
|
|
|
stack_is_even = !(cpuGetAReg(7) & 1);
|
|
|
|
if (is_address_error_on_sub_020 && !stack_is_even)
|
|
{
|
|
cpuExceptionFail(executejmp);
|
|
return;
|
|
}
|
|
|
|
cpuStackFrameGenerate((uint16_t) vector_offset, pc);
|
|
|
|
// read a memory position
|
|
vector_address = memoryReadLong(cpuGetVbr() + vector_offset);
|
|
if (is_address_error_on_sub_020 && vector_address & 1)
|
|
{
|
|
cpuExceptionFail(executejmp);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// set supervisor modus
|
|
cpuSetSR(cpuGetSR() | 0x2000);
|
|
cpuSetSR(cpuGetSR() & 0x3fff);
|
|
|
|
// restart cpu, if needed
|
|
cpuSetStop(FALSE);
|
|
|
|
cpuInitializeFromNewPC(vector_address);
|
|
|
|
uint32_t exceptionCycles = 0;
|
|
|
|
switch (vector_offset)
|
|
{
|
|
case 0x08: exceptionCycles = 50; break; // Bus
|
|
case 0x0c: exceptionCycles = 50; break; // Address
|
|
case 0x10: exceptionCycles = 34; break; // Illegal
|
|
case 0x14: exceptionCycles = 42; break; // Division by zero
|
|
case 0x18: exceptionCycles = 28; break; // Chk
|
|
case 0x1c: exceptionCycles = 34; break; // Trapcc/trapv
|
|
case 0x20: exceptionCycles = 34; break; // Privilege
|
|
case 0x24: exceptionCycles = 34; break; // Trace
|
|
case 0x28: exceptionCycles = 34; break; // Line A
|
|
case 0x2c: exceptionCycles = 34; break; // Line F
|
|
case 0x80:
|
|
case 0x84:
|
|
case 0x88:
|
|
case 0x8c:
|
|
case 0x90:
|
|
case 0x94:
|
|
case 0x98:
|
|
case 0x9c:
|
|
case 0xa0:
|
|
case 0xa4:
|
|
case 0xa8:
|
|
case 0xac:
|
|
case 0xb0:
|
|
case 0xb4:
|
|
case 0xb8:
|
|
case 0xbc: exceptionCycles = 34; break; // TRAP
|
|
default: exceptionCycles = 4; break; // Should not come here
|
|
}
|
|
cpuSetInstructionTime(exceptionCycles);
|
|
}
|
|
|
|
// If the exception happened mid-instruction...
|
|
if (executejmp)
|
|
{
|
|
cpuCallMidInstructionExceptionFunc(); // Supposed to be doing setjmp/longjmp back to machine emulator code
|
|
}
|
|
}
|
|
|
|
void cpuThrowPrivilegeViolationException(void)
|
|
{
|
|
cpuSetInstructionAborted(true);
|
|
// The saved pc points to the instruction causing the violation
|
|
// (And the kickstart excpects pc in the stack frame to be the opcode PC.)
|
|
cpuThrowException(0x20, cpuGetOriginalPC(), FALSE);
|
|
}
|
|
|
|
void cpuThrowIllegalInstructionException(BOOLE executejmp)
|
|
{
|
|
cpuSetInstructionAborted(true);
|
|
// The saved pc points to the illegal instruction
|
|
cpuThrowException(0x10, cpuGetOriginalPC(), executejmp);
|
|
}
|
|
|
|
void cpuThrowIllegalInstructionExceptionFromBreakpoint(void)
|
|
{
|
|
cpuSetInstructionAborted(true);
|
|
// The saved pc points to the illegal instruction
|
|
cpuThrowException(0x10, cpuGetPC(), FALSE);
|
|
}
|
|
|
|
void cpuThrowALineException(void)
|
|
{
|
|
// MPW
|
|
if (cpu_a_line_exception_func)
|
|
{
|
|
uint16_t opcode = memoryReadWord(cpuGetPC() - 2);
|
|
cpu_a_line_exception_func(opcode);
|
|
cpuInitializeFromNewPC(cpuGetPC());
|
|
cpuSetInstructionTime(512);
|
|
return;
|
|
}
|
|
|
|
cpuSetInstructionAborted(true);
|
|
// The saved pc points to the a-line instruction
|
|
cpuThrowException(0x28, cpuGetOriginalPC(), FALSE);
|
|
}
|
|
|
|
void cpuThrowFLineException(void)
|
|
{
|
|
// MPW
|
|
if (cpu_f_line_exception_func)
|
|
{
|
|
uint16_t opcode = memoryReadWord(cpuGetPC() - 2);
|
|
cpu_f_line_exception_func(opcode);
|
|
cpuInitializeFromNewPC(cpuGetPC());
|
|
cpuSetInstructionTime(512);
|
|
return;
|
|
}
|
|
|
|
cpuSetInstructionAborted(true);
|
|
// The saved pc points to the f-line instruction
|
|
cpuThrowException(0x2c, cpuGetOriginalPC(), FALSE);
|
|
}
|
|
|
|
void cpuThrowTrapVException(void)
|
|
{
|
|
// The saved pc points to the next instruction, which is now in pc
|
|
cpuThrowException(0x1c, cpuGetPC(), FALSE);
|
|
}
|
|
|
|
void cpuThrowDivisionByZeroException(void)
|
|
{
|
|
// The saved pc points to the next instruction, which is now in pc
|
|
cpuThrowException(0x14, cpuGetPC(), FALSE);
|
|
}
|
|
|
|
void cpuThrowTrapException(uint32_t vector_no)
|
|
{
|
|
// The saved pc points to the next instruction, which is now in pc
|
|
cpuThrowException(0x80 + vector_no*4, cpuGetPC(), FALSE);
|
|
}
|
|
|
|
void cpuThrowChkException(void)
|
|
{
|
|
// The saved pc points to the next instruction, which is now in pc
|
|
cpuThrowException(0x18, cpuGetPC(), FALSE);
|
|
}
|
|
|
|
void cpuThrowTraceException(void)
|
|
{
|
|
// The saved pc points to the next instruction, which is now in pc
|
|
cpuThrowException(0x24, cpuGetPC(), FALSE);
|
|
}
|
|
|
|
void cpuThrowAddressErrorException(void)
|
|
{
|
|
cpuSetInstructionAborted(true);
|
|
cpuThrowException(0xc, cpuGetPC() - 2, TRUE);
|
|
}
|
|
|
|
/*=================*/
|
|
/* Reset exception */
|
|
/*=================*/
|
|
|
|
static void cpuThrowResetException000(void)
|
|
{
|
|
cpuSetSR(cpuGetSR() & 0x271f); /* T = 0 */
|
|
cpuSetSR(cpuGetSR() | 0x2700); /* S = 1, ilvl = 7 */
|
|
cpuSetVbr(0);
|
|
cpuSetSspDirect(cpuGetInitialSP()); /* ssp = fake vector 0 */
|
|
cpuInitializeFromNewPC(cpuGetInitialPC()); /* pc = fake vector 1 */
|
|
}
|
|
|
|
static void cpuThrowResetException010(void)
|
|
{
|
|
cpuSetSR(cpuGetSR() & 0x271f); /* T = 0 */
|
|
cpuSetSR(cpuGetSR() | 0x2700); /* S = 1, ilvl = 7 */
|
|
cpuSetVbr(0);
|
|
cpuSetSspDirect(cpuGetInitialSP()); /* ssp = fake vector 0 */
|
|
cpuInitializeFromNewPC(cpuGetInitialPC()); /* pc = fake vector 1 */
|
|
}
|
|
|
|
static void cpuThrowResetException020(void)
|
|
{
|
|
cpuSetSR(cpuGetSR() & 0x271f); /* T1T0 = 0, M = 0 */
|
|
cpuSetSR(cpuGetSR() | 0x2700); /* S = 1, ilvl = 7 */
|
|
cpuSetVbr(0);
|
|
cpuSetCacr(0); /* E = 0, F = 0 */
|
|
cpuSetCaar(0);
|
|
/* Invalidate cache, we don't have one */
|
|
cpuSetSspDirect(cpuGetInitialSP()); /* ssp = fake vector 0 */
|
|
cpuInitializeFromNewPC(cpuGetInitialPC()); /* pc = fake vector 1 */
|
|
}
|
|
|
|
static void cpuThrowResetException030(void)
|
|
{
|
|
cpuSetSR(cpuGetSR() & 0x271f); /* T1T0 = 0, M = 0 */
|
|
cpuSetSR(cpuGetSR() | 0x2700); /* S = 1, ilvl = 7 */
|
|
cpuSetVbr(0);
|
|
cpuSetCacr(0); /* E = 0, F = 0 */
|
|
cpuSetCaar(0);
|
|
/* Invalidate cache, we don't have one */
|
|
cpuSetSspDirect(cpuGetInitialSP()); /* ssp = fake vector 0 */
|
|
cpuInitializeFromNewPC(cpuGetInitialPC()); /* pc = fake vector 1 */
|
|
}
|
|
|
|
/*============================*/
|
|
/* Performs a Reset exception */
|
|
/*============================*/
|
|
|
|
void cpuThrowResetException(void)
|
|
{
|
|
cpuSetStop(FALSE);
|
|
switch (cpuGetModelMajor())
|
|
{
|
|
case 0:
|
|
cpuThrowResetException000();
|
|
break;
|
|
case 1:
|
|
cpuThrowResetException010();
|
|
break;
|
|
case 2:
|
|
cpuThrowResetException020();
|
|
break;
|
|
case 3:
|
|
cpuThrowResetException030();
|
|
break;
|
|
}
|
|
}
|