BizHawk/waterbox/virtualjaguar/src/jaguar.cpp

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));
}