mpw/cpu/CpuModule_Exceptions.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;
}
}