2020-07-30 18:04:41 +00:00
|
|
|
#define VUEAPI
|
|
|
|
#include <vue.h>
|
2020-08-04 01:34:02 +00:00
|
|
|
|
|
|
|
|
2020-08-08 01:04:11 +00:00
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Macros *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/* Sign-extend a value */
|
|
|
|
#define SIGN_EXTEND(bits, value) \
|
|
|
|
((value) & 1 << (bits - 1) ? (value) | (~(1 << bits) + 1) : (value))
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-08-04 01:34:02 +00:00
|
|
|
/*****************************************************************************
|
|
|
|
* Constants *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/* Memory access type sizes */
|
2020-08-09 23:35:57 +00:00
|
|
|
static const uint32_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
|
2020-08-04 01:34:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Component Includes *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2020-08-06 01:40:23 +00:00
|
|
|
#include "cpu.c"
|
2020-08-09 23:35:57 +00:00
|
|
|
#include "gamepak.c"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Module Functions *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/* Read a value from a byte buffer */
|
|
|
|
static int32_t readBuffer(uint8_t *data, uint32_t datlen, uint32_t address,
|
|
|
|
int32_t type) {
|
|
|
|
uint32_t size = TYPE_SIZES[type]; /* Size of data type */
|
|
|
|
int32_t value = 0;
|
|
|
|
|
|
|
|
/* Error checking */
|
|
|
|
if (data == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Common processing */
|
|
|
|
address &= (~size + 1) & (datlen - 1);
|
|
|
|
|
|
|
|
/* The host is little-endian */
|
|
|
|
#ifndef VUE_BIGENDIAN
|
|
|
|
switch (type) {
|
|
|
|
case VUE_S8 : value = *( int8_t *)&data[address]; break;
|
|
|
|
case VUE_U8 : value = *(uint8_t *)&data[address]; break;
|
|
|
|
case VUE_S16: value = *( int16_t *)&data[address]; break;
|
|
|
|
case VUE_U16: value = *(uint16_t *)&data[address]; break;
|
|
|
|
case VUE_S32: value = *( int32_t *)&data[address]; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The host is big-endian */
|
|
|
|
#else
|
|
|
|
switch (type) {
|
|
|
|
case VUE_S32: value =
|
|
|
|
data[address + 3] << 24 |
|
|
|
|
(data[address + 2] & 0xFF) << 16;
|
|
|
|
/* Fallthrough */
|
|
|
|
case VUE_S16: /* Fallthrough */
|
|
|
|
case VUE_U16: value |=
|
|
|
|
(data[address + 1] & 0xFF) << 8;
|
|
|
|
/* Fallthrough */
|
|
|
|
case VUE_S8 : /* Fallthrough */
|
|
|
|
case VUE_U8 : value |=
|
|
|
|
data[address ] & 0xFF;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Sign-extend the value if appropriate */
|
|
|
|
if (type & 1) {
|
|
|
|
size <<= 3;
|
|
|
|
value = SIGN_EXTEND(size, value);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return value;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read bytes from a byte buffer */
|
|
|
|
static void readBytes(uint8_t *data, uint32_t datlen, uint32_t address,
|
|
|
|
uint8_t *dest, uint32_t length) {
|
|
|
|
|
|
|
|
/* The source does not exist */
|
|
|
|
if (data == NULL) {
|
|
|
|
while (length--)
|
|
|
|
*dest++ = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Transfer bytes from the source as a circular buffer */
|
|
|
|
while (length--)
|
|
|
|
*dest++ = data[address++ & (datlen - 1)];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write a value to a byte buffer */
|
|
|
|
static void writeBuffer(uint8_t *data, uint32_t datlen, uint32_t address,
|
|
|
|
int32_t type, int32_t value) {
|
|
|
|
uint32_t size = TYPE_SIZES[type]; /* Size of data type */
|
|
|
|
|
|
|
|
/* Error checking */
|
|
|
|
if (data == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Common processing */
|
|
|
|
address &= (~size + 1) & (datlen - 1);
|
|
|
|
|
|
|
|
/* The host is little-endian */
|
|
|
|
#ifndef VUE_BIGENDIAN
|
|
|
|
switch (type) {
|
|
|
|
case VUE_S8 : /* Fallthrough */
|
|
|
|
case VUE_U8 : *(int8_t *)&data[address] = (int8_t ) value; break;
|
|
|
|
case VUE_S16: /* Fallthrough */
|
|
|
|
case VUE_U16: *(int16_t *)&data[address] = (int16_t ) value; break;
|
|
|
|
case VUE_S32: *(int32_t *)&data[address] = (int32_t ) value; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The host is big-endian */
|
|
|
|
#else
|
|
|
|
switch (type) {
|
|
|
|
case VUE_S32:
|
|
|
|
data[address + 3] = (uint8_t) (value >> 24);
|
|
|
|
data[address + 2] = (uint8_t) (value >> 16);
|
|
|
|
/* Fallthrough */
|
|
|
|
case VUE_S16: /* Fallthrough */
|
|
|
|
case VUE_U16:
|
|
|
|
data[address + 1] = (uint8_t) (value >> 8);
|
|
|
|
/* Fallthrough */
|
|
|
|
case VUE_S8 : /* Fallthrough */
|
|
|
|
case VUE_U8 : value |=
|
|
|
|
data[address ] = (uint8_t) value;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Write bytes to a byte buffer */
|
|
|
|
static void writeBytes(uint8_t *data, uint32_t datlen, uint32_t address,
|
|
|
|
uint8_t *src, uint32_t length) {
|
|
|
|
|
|
|
|
/* The destination does not exist */
|
|
|
|
if (data == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
/* Transfer bytes to the destination as a circular buffer */
|
|
|
|
while (length--)
|
|
|
|
data[address++ & (datlen - 1)] = *src++;
|
|
|
|
}
|
2020-08-04 01:34:02 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
* Library Functions *
|
|
|
|
*****************************************************************************/
|
|
|
|
|
2020-08-09 23:35:57 +00:00
|
|
|
/* Process the simulation */
|
|
|
|
int32_t vueEmulate(VUE *vue, int32_t maxCycles) {
|
|
|
|
int32_t cycles; /* Number of cycles to process */
|
|
|
|
|
|
|
|
/* Process up to the given number of cycles */
|
|
|
|
do {
|
|
|
|
|
|
|
|
/* Determine the number of cycles during which nothing will happen */
|
|
|
|
cycles = maxCycles;
|
|
|
|
/*cycles = cpuUntil (vue, cycles);*/
|
|
|
|
/*cycles = padUntil (vue, cycles);*/
|
|
|
|
/*cycles = linkUntil (vue, cycles);*/
|
|
|
|
/*cycles = timerUntil(vue, cycles);*/
|
|
|
|
/*cycles = vipUntil (vue, cycles);*/
|
|
|
|
/*cycles = vsuUntil (vue, cycles);*/
|
|
|
|
|
|
|
|
/* Process all system components */
|
|
|
|
vue->breakCode = 0;
|
|
|
|
/*gamePad.emulate(cycles);*/
|
|
|
|
/*gamePak.emulate(cycles);*/
|
|
|
|
/*link .emulate(cycles);*/
|
|
|
|
/*timer .emulate(cycles);*/
|
|
|
|
/*vip .emulate(cycles);*/
|
|
|
|
/*vsu .emulate(cycles);*/
|
|
|
|
/*cpu .emulate(cycles);*/
|
|
|
|
|
|
|
|
/* An application break was requested */
|
|
|
|
if (vue->breakCode != 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Update the number of cycles remaining */
|
|
|
|
if (maxCycles >= 0)
|
|
|
|
maxCycles -= cycles;
|
|
|
|
} while (maxCycles != 0);
|
|
|
|
|
|
|
|
/* A break condition has occurred */
|
|
|
|
return maxCycles;
|
|
|
|
}
|
|
|
|
|
2020-08-11 01:24:00 +00:00
|
|
|
/* Retrieve the application break code */
|
|
|
|
int32_t vueGetBreakCode(VUE *vue) {
|
|
|
|
return vue == NULL ? 0 : vue->breakCode;
|
|
|
|
}
|
|
|
|
|
2020-08-06 01:40:23 +00:00
|
|
|
/* Retrieve the value of a register */
|
2020-08-09 23:35:57 +00:00
|
|
|
int32_t vueGetRegister(VUE *vue, int32_t index, vbool system) {
|
2020-08-07 19:21:03 +00:00
|
|
|
|
|
|
|
/* Error checking */
|
|
|
|
if (vue == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Non-indexed registers */
|
2020-08-08 02:24:09 +00:00
|
|
|
if (system && index < 0) switch (index) {
|
|
|
|
case VUE_JUMP_FROM:
|
|
|
|
return vue->cpu.jumpFrom[vue->cpu.psw_np ? 2 : vue->cpu.psw_ep];
|
|
|
|
case VUE_JUMP_TO :
|
|
|
|
return vue->cpu.jumpTo [vue->cpu.psw_np ? 2 : vue->cpu.psw_ep];
|
2020-08-07 19:21:03 +00:00
|
|
|
case VUE_PC : return vue->cpu.pc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Indexed registers */
|
|
|
|
return
|
2020-08-06 01:40:23 +00:00
|
|
|
index < 0 || index > 31 ? 0 :
|
|
|
|
system ? cpuGetSystemRegister(vue, index) :
|
|
|
|
vue->cpu.program[index]
|
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2020-08-04 01:34:02 +00:00
|
|
|
/* Prepare an emulation state context for use */
|
|
|
|
void vueInitialize(VUE *vue) {
|
2020-08-06 21:37:05 +00:00
|
|
|
if (vue == NULL)
|
|
|
|
return;
|
2020-08-09 23:35:57 +00:00
|
|
|
vue->pak.ram = NULL;
|
|
|
|
vue->pak.rom = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Read a value from the CPU bus */
|
|
|
|
int32_t vueRead(VUE *vue, uint32_t address, int32_t type) {
|
|
|
|
switch (address >> 24 & 7) {
|
|
|
|
case 5: return readBuffer(vue->wram , 0x10000,address,type);
|
|
|
|
case 6: return readBuffer(vue->pak.ram,vue->pak.ramSize,address,type);
|
|
|
|
case 7: return readBuffer(vue->pak.rom,vue->pak.romSize,address,type);
|
|
|
|
}
|
|
|
|
return 0;
|
2020-08-04 01:34:02 +00:00
|
|
|
}
|
|
|
|
|
2020-08-05 02:17:56 +00:00
|
|
|
/* Read bytes from the CPU bus */
|
2020-08-09 23:35:57 +00:00
|
|
|
vbool vueReadBytes(VUE *vue, uint32_t address, uint8_t *dest, uint32_t length){
|
2020-08-04 01:34:02 +00:00
|
|
|
uint32_t count; /* Bytes to read in one iteration */
|
|
|
|
|
|
|
|
/* Error checking */
|
2020-08-05 02:17:56 +00:00
|
|
|
if (vue == NULL || dest == NULL)
|
|
|
|
return VUE_FALSE;
|
2020-08-04 01:34:02 +00:00
|
|
|
|
|
|
|
/* Perform the operation */
|
2020-08-09 23:35:57 +00:00
|
|
|
while (length > 0) {
|
2020-08-04 01:34:02 +00:00
|
|
|
count = 0x01000000 - (address & 0x00FFFFFF);
|
2020-08-09 23:35:57 +00:00
|
|
|
if (length < count)
|
2020-08-04 01:34:02 +00:00
|
|
|
count = length;
|
|
|
|
switch (address >> 24 & 7) {
|
2020-08-09 23:35:57 +00:00
|
|
|
case 5: readBytes(vue->wram , 0x10000,
|
|
|
|
address, dest, count); break;
|
|
|
|
case 6: readBytes(vue->pak.ram, vue->pak.ramSize,
|
|
|
|
address, dest, count); break;
|
|
|
|
case 7: readBytes(vue->pak.rom, vue->pak.romSize,
|
|
|
|
address, dest, count); break;
|
|
|
|
default:
|
|
|
|
for (address += count, length -= count; count--;)
|
|
|
|
*dest++ = 0;
|
|
|
|
continue;
|
2020-08-04 01:34:02 +00:00
|
|
|
}
|
2020-08-09 23:35:57 +00:00
|
|
|
address += count;
|
|
|
|
length -= count;
|
|
|
|
dest += count;
|
|
|
|
}
|
2020-08-04 01:34:02 +00:00
|
|
|
return VUE_TRUE;
|
|
|
|
}
|
|
|
|
|
2020-08-06 21:37:05 +00:00
|
|
|
/* Initialize all system components */
|
|
|
|
void vueReset(VUE *vue) {
|
2020-08-09 23:35:57 +00:00
|
|
|
uint32_t x; /* Iterator */
|
|
|
|
|
|
|
|
/* Error checking */
|
2020-08-06 21:37:05 +00:00
|
|
|
if (vue == NULL)
|
|
|
|
return;
|
2020-08-09 23:35:57 +00:00
|
|
|
|
|
|
|
/* Reset state */
|
2020-08-06 21:37:05 +00:00
|
|
|
cpuReset(vue);
|
2020-08-09 23:35:57 +00:00
|
|
|
pakReset(vue);
|
|
|
|
for (x = 0; x < 0x1000; x++)
|
|
|
|
vue->wram[x] = 0;
|
2020-08-06 21:37:05 +00:00
|
|
|
}
|
|
|
|
|
2020-08-06 01:40:23 +00:00
|
|
|
/* Specify a value for a register */
|
2020-08-09 23:35:57 +00:00
|
|
|
int32_t vueSetRegister(VUE *vue, int32_t index, vbool system, int32_t value) {
|
2020-08-06 21:37:05 +00:00
|
|
|
return vue == NULL ? 0 :
|
2020-08-06 01:40:23 +00:00
|
|
|
index == VUE_PC && system ? vue->cpu.pc = value & 0xFFFFFFFE :
|
|
|
|
index < 0 || index > 31 ? 0 :
|
|
|
|
system ? cpuSetSystemRegister(vue, index, value, VUE_TRUE) :
|
2020-08-06 21:37:05 +00:00
|
|
|
index == 0 ? 0 : (vue->cpu.program[index] = value)
|
2020-08-06 01:40:23 +00:00
|
|
|
;
|
|
|
|
}
|
|
|
|
|
2020-08-04 01:34:02 +00:00
|
|
|
/* Specify a new ROM buffer */
|
|
|
|
vbool vueSetROM(VUE *vue, uint8_t *rom, uint32_t size) {
|
|
|
|
|
|
|
|
/* Error checking */
|
|
|
|
if (
|
|
|
|
vue == NULL ||
|
|
|
|
rom == NULL ||
|
|
|
|
size < 1024 || size > 0x01000000 ||
|
|
|
|
(size & (size - 1)) != 0
|
|
|
|
) return VUE_FALSE;
|
|
|
|
|
|
|
|
/* Accept the new ROM buffer */
|
2020-08-09 23:35:57 +00:00
|
|
|
vue->pak.rom = rom;
|
|
|
|
vue->pak.romSize = size;
|
2020-08-04 01:34:02 +00:00
|
|
|
return VUE_TRUE;
|
|
|
|
}
|
2020-08-05 02:17:56 +00:00
|
|
|
|
2020-08-09 23:35:57 +00:00
|
|
|
/* Write a value to the CPU bus */
|
|
|
|
void vueWrite(VUE *vue, uint32_t address, int32_t type, int32_t value) {
|
|
|
|
switch (address >> 24 & 7) {
|
|
|
|
case 5: writeBuffer(vue->wram , 0x1000,
|
|
|
|
address, type, value); break;
|
|
|
|
case 6: writeBuffer(vue->pak.ram, vue->pak.ramSize,
|
|
|
|
address, type, value); break;
|
|
|
|
case 7: writeBuffer(vue->pak.rom, vue->pak.romSize,
|
|
|
|
address, type, value); break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-05 02:17:56 +00:00
|
|
|
/* Write bytes to the CPU bus */
|
2020-08-09 23:35:57 +00:00
|
|
|
vbool vueWriteBytes(VUE *vue, uint32_t address, uint8_t *src, uint32_t length){
|
2020-08-05 02:17:56 +00:00
|
|
|
uint32_t count; /* Bytes to write in one iteration */
|
|
|
|
|
|
|
|
/* Error checking */
|
|
|
|
if (vue == NULL || src == NULL)
|
|
|
|
return VUE_FALSE;
|
|
|
|
|
|
|
|
/* Perform the operation */
|
2020-08-09 23:35:57 +00:00
|
|
|
while (length > 0) {
|
2020-08-05 02:17:56 +00:00
|
|
|
count = 0x01000000 - (address & 0x00FFFFFF);
|
2020-08-09 23:35:57 +00:00
|
|
|
if (length < count)
|
2020-08-05 02:17:56 +00:00
|
|
|
count = length;
|
|
|
|
switch (address >> 24 & 7) {
|
2020-08-09 23:35:57 +00:00
|
|
|
case 5: writeBytes(vue->wram , 0x10000,
|
|
|
|
address, src, count); break;
|
|
|
|
case 6: writeBytes(vue->pak.ram, vue->pak.ramSize,
|
|
|
|
address, src, count); break;
|
|
|
|
case 7: writeBytes(vue->pak.rom, vue->pak.ramSize,
|
|
|
|
address, src, count); break;
|
2020-08-05 02:17:56 +00:00
|
|
|
}
|
2020-08-09 23:35:57 +00:00
|
|
|
address += count;
|
|
|
|
length -= count;
|
|
|
|
src += count;
|
|
|
|
}
|
2020-08-05 02:17:56 +00:00
|
|
|
return VUE_TRUE;
|
|
|
|
}
|