524 lines
13 KiB
C++
524 lines
13 KiB
C++
//
|
|
// JAGUAR.CPP
|
|
//
|
|
// Originally by David Raingeard (Cal2)
|
|
// GCC/SDL port by Niels Wagenaar (Linux/WIN32) and Carwin Jones (BeOS)
|
|
// Cleanups and endian wrongness amelioration by James Hammons
|
|
// Note: Endian wrongness probably stems from the MAME origins of this emu and
|
|
// the braindead way in which MAME handled memory when this was written. :-)
|
|
//
|
|
// JLH = James Hammons
|
|
//
|
|
// WHO WHEN WHAT
|
|
// --- ---------- -----------------------------------------------------------
|
|
// JLH 11/25/2009 Major rewrite of memory subsystem and handlers
|
|
//
|
|
|
|
#include "jaguar.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <string.h>
|
|
#include "blitter.h"
|
|
#include "cdhle.h"
|
|
#include "cdrom.h"
|
|
#include "dac.h"
|
|
#include "dsp.h"
|
|
#include "eeprom.h"
|
|
#include "event.h"
|
|
#include "gpu.h"
|
|
#include "jerry.h"
|
|
#include "joystick.h"
|
|
#include "m68000/m68kinterface.h"
|
|
#include "memtrack.h"
|
|
#include "settings.h"
|
|
#include "tom.h"
|
|
|
|
// Private function prototypes
|
|
|
|
unsigned jaguar_unknown_readbyte(unsigned address, uint32_t who = UNKNOWN);
|
|
unsigned jaguar_unknown_readword(unsigned address, uint32_t who = UNKNOWN);
|
|
void jaguar_unknown_writebyte(unsigned address, unsigned data, uint32_t who = UNKNOWN);
|
|
void jaguar_unknown_writeword(unsigned address, unsigned data, uint32_t who = UNKNOWN);
|
|
|
|
// External variables
|
|
|
|
// Really, need to include memory.h for this, but it might interfere with some stuff...
|
|
extern uint8_t jagMemSpace[];
|
|
|
|
// Internal variables
|
|
|
|
uint32_t jaguarMainROMCRC32, jaguarROMSize, jaguarRunAddress;
|
|
bool jaguarCartInserted = false;
|
|
bool lowerField = false;
|
|
bool jaguarCdInserted = false;
|
|
|
|
void M68KInstructionHook(void)
|
|
{
|
|
if (jaguarCdInserted)
|
|
{
|
|
uint32_t pc = m68k_get_reg(M68K_REG_PC);
|
|
if (pc >= 0x3000 && pc <= 0x306C)
|
|
{
|
|
CDHLEHook((pc - 0x3000) / 6);
|
|
// return
|
|
uint32_t sp = m68k_get_reg(M68K_REG_SP);
|
|
m68k_set_reg(M68K_REG_PC, m68k_read_memory_32(sp));
|
|
m68k_set_reg(M68K_REG_SP, sp + 4);
|
|
}
|
|
}
|
|
|
|
if (__builtin_expect(!!CPUTraceCallback, false))
|
|
{
|
|
uint32_t regs[18];
|
|
for (uint32_t i = 0; i < 18; i++)
|
|
{
|
|
regs[i] = m68k_get_reg((m68k_register_t)i);
|
|
}
|
|
CPUTraceCallback(regs);
|
|
}
|
|
|
|
MAYBE_CALLBACK(ExecuteCallback, m68k_get_reg(M68K_REG_PC));
|
|
}
|
|
|
|
//
|
|
// Custom UAE 68000 read/write/IRQ functions
|
|
//
|
|
|
|
int irq_ack_handler(int level)
|
|
{
|
|
if (level == 2)
|
|
{
|
|
m68k_set_irq(0);
|
|
return 64;
|
|
}
|
|
|
|
return M68K_INT_ACK_AUTOVECTOR;
|
|
}
|
|
|
|
unsigned int m68k_read_memory_8(unsigned int address)
|
|
{
|
|
MAYBE_CALLBACK(ReadCallback, address);
|
|
|
|
address &= 0x00FFFFFF;
|
|
|
|
unsigned int retVal = 0;
|
|
|
|
if ((address >= 0x000000) && (address <= 0x1FFFFF))
|
|
retVal = jaguarMainRAM[address];
|
|
else if ((address >= 0x800000) && (address <= 0xDFFEFF))
|
|
retVal = jaguarMainROM[address - 0x800000];
|
|
else if ((address >= 0xE00000) && (address <= 0xE3FFFF))
|
|
retVal = jagMemSpace[address];
|
|
else if ((address >= 0xDFFF00) && (address <= 0xDFFFFF))
|
|
retVal = CDROMReadByte(address);
|
|
else if ((address >= 0xF00000) && (address <= 0xF0FFFF))
|
|
retVal = TOMReadByte(address, M68K);
|
|
else if ((address >= 0xF10000) && (address <= 0xF1FFFF))
|
|
retVal = JERRYReadByte(address, M68K);
|
|
else
|
|
retVal = jaguar_unknown_readbyte(address, M68K);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
unsigned int m68k_read_memory_16(unsigned int address)
|
|
{
|
|
MAYBE_CALLBACK(ReadCallback, address);
|
|
|
|
address &= 0x00FFFFFF;
|
|
|
|
unsigned int retVal = 0;
|
|
|
|
if ((address >= 0x000000) && (address <= 0x1FFFFE))
|
|
retVal = GET16(jaguarMainRAM, address);
|
|
else if ((address >= 0x800000) && (address <= 0xDFFEFE))
|
|
{
|
|
if (((TOMGetMEMCON1() & 0x0006) == (2 << 1)) && (jaguarMainROMCRC32 == 0xFDF37F47))
|
|
{
|
|
retVal = MTReadWord(address);
|
|
}
|
|
else
|
|
retVal = (jaguarMainROM[address - 0x800000] << 8)
|
|
| jaguarMainROM[address - 0x800000 + 1];
|
|
}
|
|
else if ((address >= 0xE00000) && (address <= 0xE3FFFE))
|
|
retVal = (jagMemSpace[address] << 8) | jagMemSpace[address + 1];
|
|
else if ((address >= 0xDFFF00) && (address <= 0xDFFFFE))
|
|
retVal = CDROMReadWord(address, M68K);
|
|
else if ((address >= 0xF00000) && (address <= 0xF0FFFE))
|
|
retVal = TOMReadWord(address, M68K);
|
|
else if ((address >= 0xF10000) && (address <= 0xF1FFFE))
|
|
retVal = JERRYReadWord(address, M68K);
|
|
else
|
|
retVal = jaguar_unknown_readword(address, M68K);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
unsigned int m68k_read_memory_32(unsigned int address)
|
|
{
|
|
MAYBE_CALLBACK(ReadCallback, address);
|
|
|
|
address &= 0x00FFFFFF;
|
|
|
|
uint32_t retVal = 0;
|
|
|
|
if ((address >= 0x800000) && (address <= 0xDFFEFE))
|
|
{
|
|
if (((TOMGetMEMCON1() & 0x0006) == (2 << 1)) && (jaguarMainROMCRC32 == 0xFDF37F47))
|
|
retVal = MTReadLong(address);
|
|
else
|
|
retVal = GET32(jaguarMainROM, address - 0x800000);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
return (m68k_read_memory_16(address) << 16) | m68k_read_memory_16(address + 2);
|
|
}
|
|
|
|
void m68k_write_memory_8(unsigned int address, unsigned int value)
|
|
{
|
|
MAYBE_CALLBACK(WriteCallback, address);
|
|
|
|
address &= 0x00FFFFFF;
|
|
|
|
if ((address >= 0x000000) && (address <= 0x1FFFFF))
|
|
jaguarMainRAM[address] = value;
|
|
else if ((address >= 0xDFFF00) && (address <= 0xDFFFFF))
|
|
CDROMWriteByte(address, value, M68K);
|
|
else if ((address >= 0xF00000) && (address <= 0xF0FFFF))
|
|
TOMWriteByte(address, value, M68K);
|
|
else if ((address >= 0xF10000) && (address <= 0xF1FFFF))
|
|
JERRYWriteByte(address, value, M68K);
|
|
else
|
|
jaguar_unknown_writebyte(address, value, M68K);
|
|
}
|
|
|
|
void m68k_write_memory_16(unsigned int address, unsigned int value)
|
|
{
|
|
MAYBE_CALLBACK(WriteCallback, address);
|
|
|
|
address &= 0x00FFFFFF;
|
|
|
|
if ((address >= 0x000000) && (address <= 0x1FFFFE))
|
|
{
|
|
SET16(jaguarMainRAM, address, value);
|
|
}
|
|
else if ((address >= 0x800000) && (address <= 0x87FFFE))
|
|
{
|
|
if (((TOMGetMEMCON1() & 0x0006) == (2 << 1)) && (jaguarMainROMCRC32 == 0xFDF37F47))
|
|
MTWriteWord(address, value);
|
|
}
|
|
else if ((address >= 0xDFFF00) && (address <= 0xDFFFFE))
|
|
CDROMWriteWord(address, value, M68K);
|
|
else if ((address >= 0xF00000) && (address <= 0xF0FFFE))
|
|
TOMWriteWord(address, value, M68K);
|
|
else if ((address >= 0xF10000) && (address <= 0xF1FFFE))
|
|
JERRYWriteWord(address, value, M68K);
|
|
else
|
|
{
|
|
jaguar_unknown_writeword(address, value, M68K);
|
|
}
|
|
}
|
|
|
|
void m68k_write_memory_32(unsigned int address, unsigned int value)
|
|
{
|
|
address &= 0x00FFFFFF;
|
|
|
|
m68k_write_memory_16(address, value >> 16);
|
|
m68k_write_memory_16(address + 2, value & 0xFFFF);
|
|
}
|
|
|
|
uint32_t JaguarGetHandler(uint32_t i)
|
|
{
|
|
return JaguarReadLong(i * 4);
|
|
}
|
|
|
|
//
|
|
// Unknown read/write byte/word routines
|
|
//
|
|
|
|
void jaguar_unknown_writebyte(unsigned address, unsigned data, uint32_t who)
|
|
{
|
|
}
|
|
|
|
void jaguar_unknown_writeword(unsigned address, unsigned data, uint32_t who)
|
|
{
|
|
}
|
|
|
|
unsigned jaguar_unknown_readbyte(unsigned address, uint32_t who)
|
|
{
|
|
return 0xFF;
|
|
}
|
|
|
|
unsigned jaguar_unknown_readword(unsigned address, uint32_t who)
|
|
{
|
|
return 0xFFFF;
|
|
}
|
|
|
|
//
|
|
// Disassemble M68K instructions at the given offset
|
|
//
|
|
|
|
unsigned int m68k_read_disassembler_8(unsigned int address)
|
|
{
|
|
return m68k_read_memory_8(address);
|
|
}
|
|
|
|
unsigned int m68k_read_disassembler_16(unsigned int address)
|
|
{
|
|
return m68k_read_memory_16(address);
|
|
}
|
|
|
|
unsigned int m68k_read_disassembler_32(unsigned int address)
|
|
{
|
|
return m68k_read_memory_32(address);
|
|
}
|
|
|
|
uint8_t JaguarReadByte(uint32_t offset, uint32_t who)
|
|
{
|
|
uint8_t data = 0x00;
|
|
offset &= 0xFFFFFF;
|
|
|
|
if (offset < 0x800000)
|
|
data = jaguarMainRAM[offset & 0x1FFFFF];
|
|
else if ((offset >= 0x800000) && (offset < 0xDFFF00))
|
|
data = jaguarMainROM[offset - 0x800000];
|
|
else if ((offset >= 0xDFFF00) && (offset <= 0xDFFFFF))
|
|
data = CDROMReadByte(offset, who);
|
|
else if ((offset >= 0xE00000) && (offset < 0xE40000))
|
|
data = jagMemSpace[offset];
|
|
else if ((offset >= 0xF00000) && (offset < 0xF10000))
|
|
data = TOMReadByte(offset, who);
|
|
else if ((offset >= 0xF10000) && (offset < 0xF20000))
|
|
data = JERRYReadByte(offset, who);
|
|
else
|
|
data = jaguar_unknown_readbyte(offset, who);
|
|
|
|
return data;
|
|
}
|
|
|
|
uint16_t JaguarReadWord(uint32_t offset, uint32_t who)
|
|
{
|
|
offset &= 0xFFFFFF;
|
|
|
|
if (offset < 0x800000)
|
|
{
|
|
return (jaguarMainRAM[(offset+0) & 0x1FFFFF] << 8) | jaguarMainRAM[(offset+1) & 0x1FFFFF];
|
|
}
|
|
else if ((offset >= 0x800000) && (offset < 0xDFFF00))
|
|
{
|
|
offset -= 0x800000;
|
|
return (jaguarMainROM[offset+0] << 8) | jaguarMainROM[offset+1];
|
|
}
|
|
else if ((offset >= 0xDFFF00) && (offset <= 0xDFFFFE))
|
|
return CDROMReadWord(offset, who);
|
|
else if ((offset >= 0xE00000) && (offset <= 0xE3FFFE))
|
|
return (jagMemSpace[offset + 0] << 8) | jagMemSpace[offset + 1];
|
|
else if ((offset >= 0xF00000) && (offset <= 0xF0FFFE))
|
|
return TOMReadWord(offset, who);
|
|
else if ((offset >= 0xF10000) && (offset <= 0xF1FFFE))
|
|
return JERRYReadWord(offset, who);
|
|
|
|
return jaguar_unknown_readword(offset, who);
|
|
}
|
|
|
|
void JaguarWriteByte(uint32_t offset, uint8_t data, uint32_t who)
|
|
{
|
|
offset &= 0xFFFFFF;
|
|
|
|
if (offset < 0x800000)
|
|
{
|
|
jaguarMainRAM[offset & 0x1FFFFF] = data;
|
|
return;
|
|
}
|
|
else if ((offset >= 0xDFFF00) && (offset <= 0xDFFFFF))
|
|
{
|
|
CDROMWriteByte(offset, data, who);
|
|
return;
|
|
}
|
|
else if ((offset >= 0xF00000) && (offset <= 0xF0FFFF))
|
|
{
|
|
TOMWriteByte(offset, data, who);
|
|
return;
|
|
}
|
|
else if ((offset >= 0xF10000) && (offset <= 0xF1FFFF))
|
|
{
|
|
JERRYWriteByte(offset, data, who);
|
|
return;
|
|
}
|
|
|
|
jaguar_unknown_writebyte(offset, data, who);
|
|
}
|
|
|
|
void JaguarWriteWord(uint32_t offset, uint16_t data, uint32_t who)
|
|
{
|
|
offset &= 0xFFFFFF;
|
|
|
|
if (offset <= 0x7FFFFE)
|
|
{
|
|
jaguarMainRAM[(offset+0) & 0x1FFFFF] = data >> 8;
|
|
jaguarMainRAM[(offset+1) & 0x1FFFFF] = data & 0xFF;
|
|
return;
|
|
}
|
|
else if (offset >= 0xDFFF00 && offset <= 0xDFFFFE)
|
|
{
|
|
CDROMWriteWord(offset, data, who);
|
|
return;
|
|
}
|
|
else if (offset >= 0xF00000 && offset <= 0xF0FFFE)
|
|
{
|
|
TOMWriteWord(offset, data, who);
|
|
return;
|
|
}
|
|
else if (offset >= 0xF10000 && offset <= 0xF1FFFE)
|
|
{
|
|
JERRYWriteWord(offset, data, who);
|
|
return;
|
|
}
|
|
else if (offset >= 0x800000 && offset <= 0xEFFFFF)
|
|
return;
|
|
|
|
jaguar_unknown_writeword(offset, data, who);
|
|
}
|
|
|
|
uint32_t JaguarReadLong(uint32_t offset, uint32_t who)
|
|
{
|
|
return (JaguarReadWord(offset, who) << 16) | JaguarReadWord(offset+2, who);
|
|
}
|
|
|
|
void JaguarWriteLong(uint32_t offset, uint32_t data, uint32_t who)
|
|
{
|
|
JaguarWriteWord(offset, data >> 16, who);
|
|
JaguarWriteWord(offset+2, data & 0xFFFF, who);
|
|
}
|
|
|
|
//
|
|
// Jaguar console initialization
|
|
//
|
|
void JaguarInit(void)
|
|
{
|
|
srand(time(NULL));
|
|
|
|
for(uint32_t i=0; i<0x200000; i+=4)
|
|
*((uint32_t *)(&jaguarMainRAM[i])) = rand();
|
|
|
|
lowerField = false;
|
|
memset(jaguarMainRAM + 0x804, 0xFF, 4);
|
|
|
|
m68k_pulse_reset();
|
|
GPUInit();
|
|
DSPInit();
|
|
TOMInit();
|
|
JERRYInit();
|
|
CDROMInit();
|
|
CDHLEInit();
|
|
}
|
|
|
|
void HalflineCallback(void);
|
|
void RenderCallback(void);
|
|
|
|
void JaguarReset(void)
|
|
{
|
|
for(uint32_t i=8; i<0x200000; i+=4)
|
|
*((uint32_t *)(&jaguarMainRAM[i])) = rand();
|
|
|
|
InitializeEventList();
|
|
|
|
if (vjs.useJaguarBIOS && jaguarCartInserted && !vjs.hardwareTypeAlpine)
|
|
memcpy(jaguarMainRAM, jagMemSpace + 0xE00000, 8);
|
|
else
|
|
SET32(jaguarMainRAM, 4, jaguarRunAddress);
|
|
|
|
TOMReset();
|
|
JERRYReset();
|
|
GPUReset();
|
|
DSPReset();
|
|
CDROMReset();
|
|
CDHLEReset();
|
|
m68k_pulse_reset();
|
|
|
|
lowerField = false;
|
|
SetCallbackTime(HalflineCallback, (vjs.hardwareTypeNTSC ? 31.777777777 : 32.0));
|
|
}
|
|
|
|
//
|
|
// Jaguar execution stack
|
|
// This executes 1 frame's worth of code.
|
|
//
|
|
bool frameDone;
|
|
void JaguarAdvance(void)
|
|
{
|
|
frameDone = false;
|
|
TOMStartFrame();
|
|
|
|
do
|
|
{
|
|
double timeToNextEvent = GetTimeToNextEvent();
|
|
|
|
m68k_execute(USEC_TO_M68K_CYCLES(timeToNextEvent));
|
|
GPUExec(USEC_TO_RISC_CYCLES(timeToNextEvent));
|
|
DSPExec(USEC_TO_RISC_CYCLES(timeToNextEvent));
|
|
|
|
HandleNextEvent();
|
|
}
|
|
while (!frameDone);
|
|
}
|
|
|
|
//
|
|
// The thing to keep in mind is that the VC is advanced every HALF line,
|
|
// regardless of whether the display is interlaced or not. The only difference
|
|
// with an interlaced display is that the high bit of VC will be set when the
|
|
// lower field is being rendered. (NB: The high bit of VC is ALWAYS set on the
|
|
// lower field, regardless of whether it's in interlace mode or not.
|
|
// NB2: Seems it doesn't always, not sure what the constraint is...)
|
|
//
|
|
// Normally, TVs will render a full frame in 1/30s (NTSC) or 1/25s (PAL) by
|
|
// rendering two fields that are slighty vertically offset from each other.
|
|
// Each field is created in 1/60s (NTSC) or 1/50s (PAL), and every other line
|
|
// is rendered in this mode so that each field, when overlaid on each other,
|
|
// will yield the final picture at the full resolution for the full frame.
|
|
//
|
|
// We execute a half frame in each timeslice (1/60s NTSC, 1/50s PAL).
|
|
// Since the number of lines in a FULL frame is 525 for NTSC, 625 for PAL,
|
|
// it will be half this number for a half frame. BUT, since we're counting
|
|
// HALF lines, we double this number and we're back at 525 for NTSC, 625 for
|
|
// PAL.
|
|
//
|
|
// Scanline times are 63.5555... μs in NTSC and 64 μs in PAL
|
|
// Half line times are, naturally, half of this. :-P
|
|
//
|
|
void HalflineCallback(void)
|
|
{
|
|
uint16_t vc = TOMReadWord(0xF00006, JAGUAR);
|
|
uint16_t vp = TOMReadWord(0xF0003E, JAGUAR) + 1;
|
|
uint16_t vi = TOMReadWord(0xF0004E, JAGUAR);
|
|
vc++;
|
|
|
|
uint16_t numHalfLines = ((vjs.hardwareTypeNTSC ? 525 : 625) * 2) / 2;
|
|
|
|
if ((vc & 0x7FF) >= numHalfLines)
|
|
{
|
|
lowerField = !lowerField;
|
|
vc = (lowerField ? 0x0800 : 0x0000);
|
|
}
|
|
|
|
TOMWriteWord(0xF00006, vc, JAGUAR);
|
|
|
|
if ((vc & 0x7FF) == vi && (vc & 0x7FF) > 0 && TOMIRQEnabled(IRQ_VIDEO))
|
|
{
|
|
TOMSetPendingVideoInt();
|
|
m68k_set_irq(2);
|
|
}
|
|
|
|
TOMExecHalfline(vc);
|
|
|
|
if ((vc & 0x7FF) == 0)
|
|
{
|
|
frameDone = true;
|
|
}
|
|
|
|
SetCallbackTime(HalflineCallback, (vjs.hardwareTypeNTSC ? 31.777777777 : 32.0));
|
|
}
|