Laying groundwork for emulation, re-implementing memory bus
This commit is contained in:
parent
f8f4005e02
commit
a98c35ee8e
140
src/core/bus.c
140
src/core/bus.c
|
@ -1,140 +0,0 @@
|
||||||
/* This file is included through vue.c and cannot be built directly. */
|
|
||||||
#ifdef VUEAPI
|
|
||||||
|
|
||||||
/* Read bytes from host memory */
|
|
||||||
static void busReadBytes(uint8_t *src, uint8_t *dest, uint32_t address,
|
|
||||||
uint32_t size, uint32_t length) {
|
|
||||||
|
|
||||||
/* There is no source data */
|
|
||||||
if (src == NULL)
|
|
||||||
while (length-- > 0)
|
|
||||||
*dest++ = 0;
|
|
||||||
|
|
||||||
/* Copy bytes from the source as a circular buffer */
|
|
||||||
else for (size--; length-- > 0;)
|
|
||||||
*dest++ = src[address++ & size];
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read a value directly from host memory */
|
|
||||||
static int32_t busReadMemory(uint8_t *data, uint32_t offset, int type) {
|
|
||||||
int32_t value; /* Value read */
|
|
||||||
|
|
||||||
/* There is no source data */
|
|
||||||
if (data == NULL)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/* Locate the value */
|
|
||||||
data = &data[offset];
|
|
||||||
|
|
||||||
/* Read the value */
|
|
||||||
switch (type) {
|
|
||||||
case VUE_S8 : value = *(int8_t *)data; break;
|
|
||||||
case VUE_U8 : value = *(uint8_t *)data; break;
|
|
||||||
case VUE_S16: value = *(int16_t *)data; break;
|
|
||||||
case VUE_U16: value = *(uint16_t *)data; break;
|
|
||||||
case VUE_S32: value = *(int32_t *)data; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Host memory is big-endian */
|
|
||||||
#ifdef VUE_BIGENDIAN
|
|
||||||
switch (type) {
|
|
||||||
case VUE_S32:
|
|
||||||
value = (value >> 16 & 0x0000FFFF) | value << 16;
|
|
||||||
/* Fallthrough */
|
|
||||||
case VUE_S16:
|
|
||||||
case VUE_U16:
|
|
||||||
value = (value >> 8 & 0x00FF00FF) | (value << 8 & 0xFF00FF00);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Read a value from the CPU bus */
|
|
||||||
static int32_t busReadValue(VUE *vue, uint32_t address, int type) {
|
|
||||||
address &= -TYPE_SIZES[type];
|
|
||||||
switch (address >> 24 & 7) {
|
|
||||||
case 1: /* Fallthrough */ /* VSU (write-only) */
|
|
||||||
case 3: /* Fallthrough */ /* Unmapped */
|
|
||||||
case 4: /* Cartridge expansion (not supported) */
|
|
||||||
return 0;
|
|
||||||
case 5: /* System WRAM */
|
|
||||||
return busReadMemory(vue->bus.wram, address & 0xFFFF, type);
|
|
||||||
case 6: return busReadMemory(vue->bus.sram, /* Cartridge RAM */
|
|
||||||
address & (vue->bus.sramSize - 1), type);
|
|
||||||
case 7: return busReadMemory(vue->bus.rom , /* Cartridge ROM */
|
|
||||||
address & (vue->bus.romSize - 1), type);
|
|
||||||
}
|
|
||||||
return 0; /* Unreachable */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Perform a system reset */
|
|
||||||
static void busReset(VUE *vue) {
|
|
||||||
int x;
|
|
||||||
for (x = 0; x < 0x10000; x++)
|
|
||||||
vue->bus.wram[x] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write bytes to host memory */
|
|
||||||
static void busWriteBytes(uint8_t *dest, uint8_t *src, uint32_t address,
|
|
||||||
uint32_t size, uint32_t length) {
|
|
||||||
if (dest != NULL)
|
|
||||||
for (size--; length-- > 0;)
|
|
||||||
dest[address++ & size] = *src++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write a value directly to host memory */
|
|
||||||
static void busWriteMemory(uint8_t *data, uint32_t offset, int type,
|
|
||||||
int32_t value) {
|
|
||||||
|
|
||||||
/* There is no destination data */
|
|
||||||
if (data == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Locate the value */
|
|
||||||
data = &data[offset];
|
|
||||||
|
|
||||||
/* Host memory is big-endian */
|
|
||||||
#ifdef VUE_BIGENDIAN
|
|
||||||
switch (type) {
|
|
||||||
case VUE_S32:
|
|
||||||
value = (value >> 16 & 0x0000FFFF) | value << 16;
|
|
||||||
/* Fallthrough */
|
|
||||||
case VUE_S16:
|
|
||||||
case VUE_U16:
|
|
||||||
value = (value >> 8 & 0x00FF00FF) | (value << 8 & 0xFF00FF00);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Write the value */
|
|
||||||
switch (type) {
|
|
||||||
case VUE_S8 : /* Fallthrough */
|
|
||||||
case VUE_U8 : *(uint8_t *)data = (uint8_t) value; break;
|
|
||||||
case VUE_S16: /* Fallthrough */
|
|
||||||
case VUE_U16: *(uint16_t *)data = (int16_t) value; break;
|
|
||||||
case VUE_S32: *(int32_t *)data = (int32_t) value; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write a value to the CPU bus */
|
|
||||||
static void busWriteValue(VUE *vue, uint32_t address, int type, int32_t value){
|
|
||||||
address &= -TYPE_SIZES[type];
|
|
||||||
switch (address >> 24 & 7) {
|
|
||||||
case 5: /* System WRAM */
|
|
||||||
busWriteMemory(vue->bus.wram, address & 0xFFFF, type, value);
|
|
||||||
break;
|
|
||||||
case 6: /* Cartridge RAM */
|
|
||||||
busWriteMemory(vue->bus.sram,
|
|
||||||
address & (vue->bus.sramSize - 1), type, value);
|
|
||||||
break;
|
|
||||||
case 7: /* Cartridge ROM */
|
|
||||||
busWriteMemory(vue->bus.rom ,
|
|
||||||
address & (vue->bus.romSize - 1), type, value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /* VUEAPI */
|
|
|
@ -124,7 +124,7 @@ static void cpuDecode(VUE_INST *inst, int32_t bits) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read a system register */
|
/* Read a system register */
|
||||||
static int32_t cpuGetSystemRegister(VUE *vue, int index) {
|
static int32_t cpuGetSystemRegister(VUE *vue, int32_t index) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case VUE_ADTRE: return vue->cpu.adtre;
|
case VUE_ADTRE: return vue->cpu.adtre;
|
||||||
case VUE_EIPC : return vue->cpu.eipc;
|
case VUE_EIPC : return vue->cpu.eipc;
|
||||||
|
@ -159,7 +159,7 @@ static int32_t cpuGetSystemRegister(VUE *vue, int index) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write a system register */
|
/* Write a system register */
|
||||||
static int32_t cpuSetSystemRegister(VUE *vue, int index, int32_t value,
|
static int32_t cpuSetSystemRegister(VUE *vue, int32_t index, int32_t value,
|
||||||
vbool debug) {
|
vbool debug) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case VUE_ADTRE: return vue->cpu.adtre = value & 0xFFFFFFFE;
|
case VUE_ADTRE: return vue->cpu.adtre = value & 0xFFFFFFFE;
|
||||||
|
@ -225,9 +225,9 @@ static int32_t cpuSetSystemRegister(VUE *vue, int index, int32_t value,
|
||||||
return 1; /* Unreachable */
|
return 1; /* Unreachable */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Perform a system reset */
|
/* System reset */
|
||||||
static void cpuReset(VUE *vue) {
|
static void cpuReset(VUE *vue) {
|
||||||
int x;
|
int32_t x;
|
||||||
|
|
||||||
/* Configure instance fields */
|
/* Configure instance fields */
|
||||||
vue->cpu.cycles = 0; /* Duration of first fetch */
|
vue->cpu.cycles = 0; /* Duration of first fetch */
|
||||||
|
@ -251,7 +251,7 @@ static void cpuReset(VUE *vue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Test a condition */
|
/* Test a condition */
|
||||||
static int8_t cpuTest(VUE *vue, int condition) {
|
static int8_t cpuTest(VUE *vue, int32_t condition) {
|
||||||
switch (condition) {
|
switch (condition) {
|
||||||
case 0: return vue->cpu.psw_ov;
|
case 0: return vue->cpu.psw_ov;
|
||||||
case 1: return vue->cpu.psw_cy;
|
case 1: return vue->cpu.psw_cy;
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
/* This file is included through vue.c and cannot be built directly. */
|
||||||
|
#ifdef VUEAPI
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* Module Functions *
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/* System reset */
|
||||||
|
static void pakReset(VUE *vue) {
|
||||||
|
vue->pak.wcr_exp1w = 0;
|
||||||
|
vue->pak.wcr_rom1w = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -159,19 +159,12 @@ typedef struct {
|
||||||
|
|
||||||
/* Emulation state */
|
/* Emulation state */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
VUE_INST inst; /* Instruction state */
|
int32_t breakCode; /* Application break code */
|
||||||
|
uint8_t wram[0x10000]; /* System memory */
|
||||||
/* Memory bus */
|
|
||||||
struct {
|
|
||||||
uint8_t *rom; /* Cartridge ROM */
|
|
||||||
uint32_t romSize; /* Number of bytes in cartridge ROM */
|
|
||||||
uint8_t *sram; /* Cartridge RAM */
|
|
||||||
uint32_t sramSize; /* Number of bytes in cartridge RAM */
|
|
||||||
uint8_t wram[0x10000]; /* System memory */
|
|
||||||
} bus;
|
|
||||||
|
|
||||||
/* CPU state */
|
/* CPU state */
|
||||||
struct {
|
struct {
|
||||||
|
VUE_INST inst; /* Instruction state */
|
||||||
uint32_t cycles; /* Cycles until next stage */
|
uint32_t cycles; /* Cycles until next stage */
|
||||||
int32_t jumpFrom[3]; /* Source PCs of most recent jumps */
|
int32_t jumpFrom[3]; /* Source PCs of most recent jumps */
|
||||||
int32_t jumpTo [3]; /* Destination PCs of most recent jumps */
|
int32_t jumpTo [3]; /* Destination PCs of most recent jumps */
|
||||||
|
@ -222,6 +215,16 @@ typedef struct {
|
||||||
uint16_t ecr_fecc; /* Fatal Error Cause Code */
|
uint16_t ecr_fecc; /* Fatal Error Cause Code */
|
||||||
} cpu;
|
} cpu;
|
||||||
|
|
||||||
|
/* Game pak */
|
||||||
|
struct {
|
||||||
|
uint8_t *ram; /* Cartridge SRAM */
|
||||||
|
uint32_t ramSize; /* Number of bytes in cartridge SRAM */
|
||||||
|
uint8_t *rom; /* Cartridge ROM */
|
||||||
|
uint32_t romSize; /* Number of bytes in cartridge ROM */
|
||||||
|
int8_t wcr_exp1w; /* Expansion one-wait mode */
|
||||||
|
int8_t wcr_rom1w; /* ROM one-wait mode */
|
||||||
|
} pak;
|
||||||
|
|
||||||
} VUE;
|
} VUE;
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,13 +233,16 @@ typedef struct {
|
||||||
* Function Prototypes *
|
* Function Prototypes *
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
VUEAPI int32_t vueGetRegister(VUE *vue, int index, vbool system);
|
VUEAPI int32_t vueEmulate (VUE *vue, int32_t maxCycles);
|
||||||
|
VUEAPI int32_t vueGetRegister(VUE *vue, int32_t index, vbool system);
|
||||||
VUEAPI void vueInitialize (VUE *vue);
|
VUEAPI void vueInitialize (VUE *vue);
|
||||||
VUEAPI vbool vueRead (VUE *vue, uint32_t address, uint8_t *dest, uint32_t length);
|
VUEAPI int32_t vueRead (VUE *vue, uint32_t address, int32_t type);
|
||||||
|
VUEAPI vbool vueReadBytes (VUE *vue, uint32_t address, uint8_t *dest, uint32_t length);
|
||||||
VUEAPI void vueReset (VUE *vue);
|
VUEAPI void vueReset (VUE *vue);
|
||||||
VUEAPI int32_t vueSetRegister(VUE *vue, int index, vbool system, int32_t value);
|
VUEAPI int32_t vueSetRegister(VUE *vue, int32_t index, vbool system, int32_t value);
|
||||||
VUEAPI vbool vueSetROM (VUE *vue, uint8_t *rom, uint32_t size);
|
VUEAPI vbool vueSetROM (VUE *vue, uint8_t *rom, uint32_t size);
|
||||||
VUEAPI vbool vueWrite (VUE *vue, uint32_t address, uint8_t *src, uint32_t length);
|
VUEAPI void vueWrite (VUE *vue, uint32_t address, int32_t type, int32_t value);
|
||||||
|
VUEAPI vbool vueWriteBytes (VUE *vue, uint32_t address, uint8_t *src, uint32_t length);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
281
src/core/vue.c
281
src/core/vue.c
|
@ -18,7 +18,7 @@
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/* Memory access type sizes */
|
/* Memory access type sizes */
|
||||||
static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
|
static const uint32_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,8 +26,132 @@ static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
|
||||||
* Component Includes *
|
* Component Includes *
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
#include "bus.c"
|
|
||||||
#include "cpu.c"
|
#include "cpu.c"
|
||||||
|
#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++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,8 +159,47 @@ static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
|
||||||
* Library Functions *
|
* Library Functions *
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
/* Retrieve the value of a register */
|
/* Retrieve the value of a register */
|
||||||
int32_t vueGetRegister(VUE *vue, int index, vbool system) {
|
int32_t vueGetRegister(VUE *vue, int32_t index, vbool system) {
|
||||||
|
|
||||||
/* Error checking */
|
/* Error checking */
|
||||||
if (vue == NULL)
|
if (vue == NULL)
|
||||||
|
@ -63,12 +226,22 @@ int32_t vueGetRegister(VUE *vue, int index, vbool system) {
|
||||||
void vueInitialize(VUE *vue) {
|
void vueInitialize(VUE *vue) {
|
||||||
if (vue == NULL)
|
if (vue == NULL)
|
||||||
return;
|
return;
|
||||||
vue->bus.rom = NULL;
|
vue->pak.ram = NULL;
|
||||||
vue->bus.sram = 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read bytes from the CPU bus */
|
/* Read bytes from the CPU bus */
|
||||||
vbool vueRead(VUE *vue, uint32_t address, uint8_t *dest, uint32_t length) {
|
vbool vueReadBytes(VUE *vue, uint32_t address, uint8_t *dest, uint32_t length){
|
||||||
uint32_t count; /* Bytes to read in one iteration */
|
uint32_t count; /* Bytes to read in one iteration */
|
||||||
|
|
||||||
/* Error checking */
|
/* Error checking */
|
||||||
|
@ -76,45 +249,46 @@ vbool vueRead(VUE *vue, uint32_t address, uint8_t *dest, uint32_t length) {
|
||||||
return VUE_FALSE;
|
return VUE_FALSE;
|
||||||
|
|
||||||
/* Perform the operation */
|
/* Perform the operation */
|
||||||
for (; length > 0; address += count, length -= count, dest += count) {
|
while (length > 0) {
|
||||||
|
|
||||||
/* Determine the maximum number of bytes to process at once */
|
|
||||||
count = 0x01000000 - (address & 0x00FFFFFF);
|
count = 0x01000000 - (address & 0x00FFFFFF);
|
||||||
if (count > length)
|
if (length < count)
|
||||||
count = length;
|
count = length;
|
||||||
|
|
||||||
/* Process by component */
|
|
||||||
switch (address >> 24 & 7) {
|
switch (address >> 24 & 7) {
|
||||||
case 1: /* Fallthrough */ /* VSU (write-only) */
|
case 5: readBytes(vue->wram , 0x10000,
|
||||||
case 3: /* Fallthrough */ /* Unmapped */
|
address, dest, count); break;
|
||||||
case 4: /* Cartridge expansion (not supported) */
|
case 6: readBytes(vue->pak.ram, vue->pak.ramSize,
|
||||||
busReadBytes(NULL, dest, address, 0, count);
|
address, dest, count); break;
|
||||||
break;
|
case 7: readBytes(vue->pak.rom, vue->pak.romSize,
|
||||||
case 5: busReadBytes(vue->bus.wram, dest, /* System WRAM */
|
address, dest, count); break;
|
||||||
address, 0x10000 , count);
|
default:
|
||||||
break;
|
for (address += count, length -= count; count--;)
|
||||||
case 6: busReadBytes(vue->bus.sram, dest, /* Cartridge RAM */
|
*dest++ = 0;
|
||||||
address, vue->bus.sramSize, count);
|
continue;
|
||||||
break;
|
|
||||||
case 7: busReadBytes(vue->bus.rom , dest, /* Cartridge ROM */
|
|
||||||
address, vue->bus.romSize , count);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
address += count;
|
||||||
};
|
length -= count;
|
||||||
|
dest += count;
|
||||||
|
}
|
||||||
return VUE_TRUE;
|
return VUE_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Initialize all system components */
|
/* Initialize all system components */
|
||||||
void vueReset(VUE *vue) {
|
void vueReset(VUE *vue) {
|
||||||
|
uint32_t x; /* Iterator */
|
||||||
|
|
||||||
|
/* Error checking */
|
||||||
if (vue == NULL)
|
if (vue == NULL)
|
||||||
return;
|
return;
|
||||||
busReset(vue);
|
|
||||||
|
/* Reset state */
|
||||||
cpuReset(vue);
|
cpuReset(vue);
|
||||||
|
pakReset(vue);
|
||||||
|
for (x = 0; x < 0x1000; x++)
|
||||||
|
vue->wram[x] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Specify a value for a register */
|
/* Specify a value for a register */
|
||||||
int32_t vueSetRegister(VUE *vue, int index, vbool system, int32_t value) {
|
int32_t vueSetRegister(VUE *vue, int32_t index, vbool system, int32_t value) {
|
||||||
return vue == NULL ? 0 :
|
return vue == NULL ? 0 :
|
||||||
index == VUE_PC && system ? vue->cpu.pc = value & 0xFFFFFFFE :
|
index == VUE_PC && system ? vue->cpu.pc = value & 0xFFFFFFFE :
|
||||||
index < 0 || index > 31 ? 0 :
|
index < 0 || index > 31 ? 0 :
|
||||||
|
@ -135,13 +309,25 @@ vbool vueSetROM(VUE *vue, uint8_t *rom, uint32_t size) {
|
||||||
) return VUE_FALSE;
|
) return VUE_FALSE;
|
||||||
|
|
||||||
/* Accept the new ROM buffer */
|
/* Accept the new ROM buffer */
|
||||||
vue->bus.rom = rom;
|
vue->pak.rom = rom;
|
||||||
vue->bus.romSize = size;
|
vue->pak.romSize = size;
|
||||||
return VUE_TRUE;
|
return VUE_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Write bytes to the CPU bus */
|
/* Write bytes to the CPU bus */
|
||||||
vbool vueWrite(VUE *vue, uint32_t address, uint8_t *src, uint32_t length) {
|
vbool vueWriteBytes(VUE *vue, uint32_t address, uint8_t *src, uint32_t length){
|
||||||
uint32_t count; /* Bytes to write in one iteration */
|
uint32_t count; /* Bytes to write in one iteration */
|
||||||
|
|
||||||
/* Error checking */
|
/* Error checking */
|
||||||
|
@ -149,26 +335,21 @@ vbool vueWrite(VUE *vue, uint32_t address, uint8_t *src, uint32_t length) {
|
||||||
return VUE_FALSE;
|
return VUE_FALSE;
|
||||||
|
|
||||||
/* Perform the operation */
|
/* Perform the operation */
|
||||||
for (; length > 0; address += count, length -= count, src += count) {
|
while (length > 0) {
|
||||||
|
|
||||||
/* Determine the maximum number of bytes to process at once */
|
|
||||||
count = 0x01000000 - (address & 0x00FFFFFF);
|
count = 0x01000000 - (address & 0x00FFFFFF);
|
||||||
if (count > length)
|
if (length < count)
|
||||||
count = length;
|
count = length;
|
||||||
|
|
||||||
/* Process by component */
|
|
||||||
switch (address >> 24 & 7) {
|
switch (address >> 24 & 7) {
|
||||||
case 5: /* System WRAM */
|
case 5: writeBytes(vue->wram , 0x10000,
|
||||||
busWriteBytes(vue->bus.wram, src, address, 0x10000, count);
|
address, src, count); break;
|
||||||
break;
|
case 6: writeBytes(vue->pak.ram, vue->pak.ramSize,
|
||||||
case 6: busWriteBytes(vue->bus.sram, src, /* Cartridge RAM */
|
address, src, count); break;
|
||||||
address, vue->bus.sramSize, count);
|
case 7: writeBytes(vue->pak.rom, vue->pak.ramSize,
|
||||||
break;
|
address, src, count); break;
|
||||||
case 7: busWriteBytes(vue->bus.rom , src, /* Cartridge ROM */
|
|
||||||
address, vue->bus.romSize , count);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
address += count;
|
||||||
};
|
length -= count;
|
||||||
|
src += count;
|
||||||
|
}
|
||||||
return VUE_TRUE;
|
return VUE_TRUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,7 @@ class DisassemblerPane extends JScrollPane {
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
// Disassemble from the current address
|
// Disassemble from the current address
|
||||||
vue.read(address, data, 0, data.length);
|
vue.readBytes(address, data, 0, data.length);
|
||||||
widths[3] = widths[4] = 0;
|
widths[3] = widths[4] = 0;
|
||||||
for (int y = 0; y < count; y++) {
|
for (int y = 0; y < count; y++) {
|
||||||
var row = y < rows.size() ? rows.get(y) : addRow();
|
var row = y < rows.size() ? rows.get(y) : addRow();
|
||||||
|
@ -248,7 +248,7 @@ class DisassemblerPane extends JScrollPane {
|
||||||
target &= 0xFFFFFFFE;
|
target &= 0xFFFFFFFE;
|
||||||
|
|
||||||
// Load enough bytes to represent every fully visible instruction
|
// Load enough bytes to represent every fully visible instruction
|
||||||
vue.read(address, data, 0, data.length);
|
vue.readBytes(address, data, 0, data.length);
|
||||||
|
|
||||||
// Iterate through instructions
|
// Iterate through instructions
|
||||||
for (int x = 0, address = this.address; x < count; x++) {
|
for (int x = 0, address = this.address; x < count; x++) {
|
||||||
|
@ -283,7 +283,7 @@ class DisassemblerPane extends JScrollPane {
|
||||||
|
|
||||||
// Load bytes starting up to 8 instructions prior to the target
|
// Load bytes starting up to 8 instructions prior to the target
|
||||||
address = target - 32;
|
address = target - 32;
|
||||||
vue.read(address, data, 0, data.length);
|
vue.readBytes(address, data, 0, data.length);
|
||||||
|
|
||||||
// Iterate through instructions
|
// Iterate through instructions
|
||||||
for (boolean found = false;;) {
|
for (boolean found = false;;) {
|
||||||
|
@ -310,7 +310,7 @@ class DisassemblerPane extends JScrollPane {
|
||||||
|
|
||||||
// Load bytes starting up to 8 instructions prior to the top row
|
// Load bytes starting up to 8 instructions prior to the top row
|
||||||
address = target - data.length + 4;
|
address = target - data.length + 4;
|
||||||
vue.read(address, data, 0, data.length);
|
vue.readBytes(address, data, 0, data.length);
|
||||||
|
|
||||||
// Iterate through instructions
|
// Iterate through instructions
|
||||||
var addresses = new int[row]; // Circular buffer
|
var addresses = new int[row]; // Circular buffer
|
||||||
|
|
|
@ -65,7 +65,8 @@ class MainWindow extends JFrame {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
pwd = Util.PWD;
|
pwd = Util.PWD;
|
||||||
vue = VUE.create(app.getUseNative());
|
vue = VUE.create(app.getUseNative());
|
||||||
System.out.println("Native: " + (vue.isNative() ? "Yes" : "No"));
|
System.out.println("Native: " +
|
||||||
|
(vue.isNative() ? VUE.getNativeID() : "No"));
|
||||||
|
|
||||||
// Configure video pane
|
// Configure video pane
|
||||||
video = new JPanel(null) {
|
video = new JPanel(null) {
|
||||||
|
|
|
@ -87,7 +87,7 @@ class MemoryWindow extends ChildWindow {
|
||||||
var data = new byte[count * 16];
|
var data = new byte[count * 16];
|
||||||
|
|
||||||
// Retrieve all visible bytes from the emulation context
|
// Retrieve all visible bytes from the emulation context
|
||||||
parent.vue.read(address, data, 0, data.length);
|
parent.vue.readBytes(address, data, 0, data.length);
|
||||||
|
|
||||||
// Update visible rows
|
// Update visible rows
|
||||||
for (int x = 0; x < count; x++) {
|
for (int x = 0; x < count; x++) {
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
package vue;
|
|
||||||
|
|
||||||
// Java imports
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
// Simulation of the CPU bus
|
|
||||||
class Bus {
|
|
||||||
|
|
||||||
// Private fields
|
|
||||||
private JavaVUE vue; // Emulation state
|
|
||||||
|
|
||||||
// Package fields
|
|
||||||
byte[] rom; // Cartridge ROM
|
|
||||||
byte[] sram; // Cartridge SRAM
|
|
||||||
byte[] wram; // System WRAM
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructors //
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Default constructor
|
|
||||||
Bus(JavaVUE vue) {
|
|
||||||
this.vue = vue;
|
|
||||||
wram = new byte[0x10000];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read bytes from host memory
|
|
||||||
void readBytes(byte[] src,byte[] dest,int address,int offset,int length) {
|
|
||||||
|
|
||||||
// There is no source data
|
|
||||||
if (src == null) {
|
|
||||||
Arrays.fill(dest, offset, offset + length, (byte) 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy bytes from the source as a circular buffer
|
|
||||||
int count = 0; // Bytes to process in one iteration
|
|
||||||
for (; length > 0; address += count, length -= count, offset += count){
|
|
||||||
int from = address & src.length - 1;
|
|
||||||
count = Math.min(length, src.length - from);
|
|
||||||
System.arraycopy(src, from, dest, offset, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a value directly from host memory
|
|
||||||
int readMemory(byte[] data, int address, int type) {
|
|
||||||
|
|
||||||
// There is no source data
|
|
||||||
if (data == null)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Processing by type
|
|
||||||
address &= data.length - 1;
|
|
||||||
switch (type) {
|
|
||||||
case VUE.S8 : return data[address];
|
|
||||||
case VUE.U8 : return data[address] & 0xFF;
|
|
||||||
case VUE.S16: return
|
|
||||||
data[address+1] << 8 | data[address ] & 0xFF;
|
|
||||||
case VUE.U16: return
|
|
||||||
(data[address+1] & 0xFF)<< 8 | data[address ] & 0xFF;
|
|
||||||
case VUE.S32: return
|
|
||||||
data[address+3] <<24 | (data[address+2] & 0xFF)<<16 |
|
|
||||||
(data[address+1] & 0xFF)<< 8 | (data[address ] & 0xFF);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0; // Unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a value from the CPU bus
|
|
||||||
int readValue(int address, int type) {
|
|
||||||
address &= ~JavaVUE.TYPE_SIZES[type] + 1;
|
|
||||||
switch (address >> 24 & 7) {
|
|
||||||
case 1: // Fallthrough, VSU (write-only)
|
|
||||||
case 3: // Fallthrough, unmapped
|
|
||||||
case 4: // Cartridge expansion (not supported)
|
|
||||||
return 0;
|
|
||||||
case 5: return
|
|
||||||
readMemory(wram, address & 0xFFFF , type);
|
|
||||||
case 6: return
|
|
||||||
readMemory(sram, address & sram.length - 1, type);
|
|
||||||
case 7: return
|
|
||||||
readMemory(rom , address & rom .length - 1, type);
|
|
||||||
}
|
|
||||||
return 0; // Unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perform a system reset
|
|
||||||
public void reset() {
|
|
||||||
Arrays.fill(wram, 0, 0x10000, (byte) 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write bytes to host memory
|
|
||||||
void writeBytes(byte[] dest,byte[] src,int address,int offset,int length) {
|
|
||||||
|
|
||||||
// There is no destination data
|
|
||||||
if (dest != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Copy bytes to the destination as a circular buffer
|
|
||||||
int count = 0; // Bytes to process in one iteration
|
|
||||||
for (; length > 0; address += count, length -= count, offset += count){
|
|
||||||
int to = address & dest.length - 1;
|
|
||||||
count = Math.min(length, dest.length - to);
|
|
||||||
System.arraycopy(src, offset, dest, to, count);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a value directly to host memory
|
|
||||||
void writeMemory(byte[] data, int address, int type, int value) {
|
|
||||||
|
|
||||||
// There is no source data
|
|
||||||
if (data == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Processing by type
|
|
||||||
address &= data.length - 1;
|
|
||||||
switch (type) {
|
|
||||||
case VUE.S32:
|
|
||||||
data[address + 3] = (byte) (value >> 24);
|
|
||||||
data[address + 2] = (byte) (value >> 16);
|
|
||||||
// Fallthrough
|
|
||||||
case VUE.S16: // Fallthrough
|
|
||||||
case VUE.U16:
|
|
||||||
data[address + 1] = (byte) (value >> 8);
|
|
||||||
// Fallthrough
|
|
||||||
case VUE.S8 : // Fallthrough
|
|
||||||
case VUE.U8 :
|
|
||||||
data[address ] = (byte) value ;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write a value to the CPU bus
|
|
||||||
void writeValue(int address, int type, int value) {
|
|
||||||
address &= ~JavaVUE.TYPE_SIZES[type] + 1;
|
|
||||||
switch (address >> 24 & 7) {
|
|
||||||
case 5: writeMemory(wram, address, type, value); break;
|
|
||||||
case 6: writeMemory(sram, address, type, value); break;
|
|
||||||
case 7: writeMemory(rom , address, type, value); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -10,11 +10,12 @@ class CPU {
|
||||||
private JavaVUE vue; // Emulation state
|
private JavaVUE vue; // Emulation state
|
||||||
|
|
||||||
// Package fields
|
// Package fields
|
||||||
int cycles; // Cycles until next stage
|
int cycles; // Cycles until next stage
|
||||||
int fetch; // Fetch unit index
|
int fetch; // Fetch unit index
|
||||||
int[] jumpFrom; // Source PCs of most recent jumps
|
Instruction inst; // Instruction state
|
||||||
int[] jumpTo; // Destination PCs of most recent jumps
|
int[] jumpFrom; // Source PCs of most recent jumps
|
||||||
int stage; // Current processing stage
|
int[] jumpTo; // Destination PCs of most recent jumps
|
||||||
|
int stage; // Current processing stage
|
||||||
|
|
||||||
// Program registers
|
// Program registers
|
||||||
int[] program;
|
int[] program;
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package vue;
|
||||||
|
|
||||||
|
// Simulation of the Game Pak
|
||||||
|
class GamePak {
|
||||||
|
|
||||||
|
// Package fields
|
||||||
|
byte[] ram; // Cartridge SRAM
|
||||||
|
byte[] rom; // Cartridge ROM
|
||||||
|
int wcr_exp1w; // Expansion one-wait mode
|
||||||
|
int wcr_rom1w; // ROM one-wait mode
|
||||||
|
|
||||||
|
// Private fields
|
||||||
|
private JavaVUE vue; // Emulation state
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructors //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Default constructor
|
||||||
|
GamePak(JavaVUE vue) {
|
||||||
|
this.vue = vue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Package Methods //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// System reset
|
||||||
|
void reset() {
|
||||||
|
wcr_exp1w = 0;
|
||||||
|
wcr_rom1w = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,14 +1,18 @@
|
||||||
package vue;
|
package vue;
|
||||||
|
|
||||||
|
// Java imports
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
// Java emulation core implementation
|
// Java emulation core implementation
|
||||||
class JavaVUE extends VUE {
|
class JavaVUE extends VUE {
|
||||||
|
|
||||||
// Instance fields
|
// State fields
|
||||||
Instruction inst; // Instruction state
|
int breakCode; // Application break code
|
||||||
|
byte[] wram; // System WRAM
|
||||||
|
|
||||||
// Components
|
// Package fields
|
||||||
Bus bus; // Memory bus
|
CPU cpu; // Processor
|
||||||
CPU cpu; // Processor
|
GamePak pak; // Game pak
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -27,9 +31,9 @@ class JavaVUE extends VUE {
|
||||||
|
|
||||||
// Default constructor
|
// Default constructor
|
||||||
JavaVUE() {
|
JavaVUE() {
|
||||||
bus = new Bus(this);
|
cpu = new CPU (this);
|
||||||
cpu = new CPU(this);
|
pak = new GamePak(this);
|
||||||
inst = new Instruction();
|
wram = new byte[0x10000];
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,6 +46,43 @@ class JavaVUE extends VUE {
|
||||||
// Release any used resources
|
// Release any used resources
|
||||||
public void dispose() { }; // No action needed
|
public void dispose() { }; // No action needed
|
||||||
|
|
||||||
|
// Process the simulation
|
||||||
|
public int emulate(int maxCycles) {
|
||||||
|
|
||||||
|
// Process up to the given number of cycles
|
||||||
|
do {
|
||||||
|
|
||||||
|
// Determine the number of cycles during which nothing will happen
|
||||||
|
int cycles = maxCycles;
|
||||||
|
//cycles = cpu .until(cycles);
|
||||||
|
//cycles = pad .until(cycles);
|
||||||
|
//cycles = link .until(cycles);
|
||||||
|
//cycles = timer.until(cycles);
|
||||||
|
//cycles = vip .until(cycles);
|
||||||
|
//cycles = vsu .until(cycles);
|
||||||
|
|
||||||
|
// Process all system components
|
||||||
|
breakCode = 0;
|
||||||
|
//pad .emulate(cycles);
|
||||||
|
//link .emulate(cycles);
|
||||||
|
//timer.emulate(cycles);
|
||||||
|
//vip .emulate(cycles);
|
||||||
|
//vsu .emulate(cycles);
|
||||||
|
//cpu .emulate(cycles);
|
||||||
|
|
||||||
|
// An application break was requested
|
||||||
|
if (breakCode != 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Update the number of cycles remaining
|
||||||
|
if (maxCycles >= 0)
|
||||||
|
maxCycles -= cycles;
|
||||||
|
} while (maxCycles != 0);
|
||||||
|
|
||||||
|
// A break condition has occurred
|
||||||
|
return maxCycles;
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve a register value
|
// Retrieve a register value
|
||||||
public int getRegister(int index, boolean system) {
|
public int getRegister(int index, boolean system) {
|
||||||
|
|
||||||
|
@ -66,12 +107,12 @@ class JavaVUE extends VUE {
|
||||||
public byte[] getROM() {
|
public byte[] getROM() {
|
||||||
|
|
||||||
// No ROM data
|
// No ROM data
|
||||||
if (bus.rom == null)
|
if (pak.rom == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Copy the ROM data
|
// Copy the ROM data
|
||||||
var ret = new byte[bus.rom.length];
|
var ret = new byte[pak.rom.length];
|
||||||
System.arraycopy(bus.rom, 0, ret, 0, ret.length);
|
System.arraycopy(pak.rom, 0, ret, 0, ret.length);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,9 +121,18 @@ class JavaVUE extends VUE {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read a value from the CPU bus
|
||||||
|
public int read(int address, int type) {
|
||||||
|
switch (address >> 24 & 7) {
|
||||||
|
case 5: return readBuffer(wram , address, type);
|
||||||
|
case 6: return readBuffer(pak.ram, address, type);
|
||||||
|
case 7: return readBuffer(pak.rom, address, type);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Read bytes from the CPU bus
|
// Read bytes from the CPU bus
|
||||||
public boolean read(int address, byte[] dest, int offset, int length) {
|
public boolean readBytes(int address, byte[] dest, int offset, int length){
|
||||||
int count = 0; // Bytes to read in one iteration
|
|
||||||
|
|
||||||
// Error checking
|
// Error checking
|
||||||
if (
|
if (
|
||||||
|
@ -93,40 +143,26 @@ class JavaVUE extends VUE {
|
||||||
) return false;
|
) return false;
|
||||||
|
|
||||||
// Perform the operation
|
// Perform the operation
|
||||||
for (; length > 0; address += count, length -= count, offset += count){
|
while (length > 0) {
|
||||||
|
int count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF));
|
||||||
// Determine the maximum number of bytes to process at once
|
|
||||||
count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF));
|
|
||||||
|
|
||||||
// Process by component
|
|
||||||
switch (address >> 24 & 7) {
|
switch (address >> 24 & 7) {
|
||||||
case 1: // VSU
|
case 5: readBytes(wram ,address,dest,offset,count); break;
|
||||||
// Will support debug reads
|
case 6: readBytes(pak.ram,address,dest,offset,count); break;
|
||||||
bus.readBytes(null, dest, address, 0 , count);
|
case 7: readBytes(pak.rom,address,dest,offset,count); break;
|
||||||
break;
|
default: Arrays.fill(dest, offset, offset + count, (byte) 0);
|
||||||
case 3: // Fallthrough, unmapped
|
|
||||||
case 4: // Cartridge expansion (not supported)
|
|
||||||
bus.readBytes(null , dest, address, 0 , count);
|
|
||||||
break;
|
|
||||||
case 5: /* System WRAM */
|
|
||||||
bus.readBytes(bus.wram, dest, address, offset, count);
|
|
||||||
break;
|
|
||||||
case 6: /* Cartridge RAM */
|
|
||||||
bus.readBytes(bus.sram, dest, address, offset, count);
|
|
||||||
break;
|
|
||||||
case 7: /* Cartridge ROM */
|
|
||||||
bus.readBytes(bus.rom , dest, address, offset, count);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
address += count;
|
||||||
};
|
length -= count;
|
||||||
|
offset += count;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize all system components
|
// Initialize all system components
|
||||||
public void reset() {
|
public void reset() {
|
||||||
bus.reset();
|
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
|
pak.reset();
|
||||||
|
Arrays.fill(wram, 0, 0x10000, (byte) 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Specify a register value
|
// Specify a register value
|
||||||
|
@ -152,14 +188,22 @@ class JavaVUE extends VUE {
|
||||||
) return false;
|
) return false;
|
||||||
|
|
||||||
// Accept the new ROM data
|
// Accept the new ROM data
|
||||||
bus.rom = new byte[length];
|
pak.rom = new byte[length];
|
||||||
System.arraycopy(data, offset, bus.rom, 0, length);
|
System.arraycopy(data, offset, pak.rom, 0, length);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Write a value to the CPU bus
|
||||||
|
public void write(int address, int type, int value) {
|
||||||
|
switch (address >> 24 & 7) {
|
||||||
|
case 5: writeBuffer(wram , address, type, value); break;
|
||||||
|
case 6: writeBuffer(pak.ram, address, type, value); break;
|
||||||
|
case 7: writeBuffer(pak.rom, address, type, value); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Write bytes to the CPU bus
|
// Write bytes to the CPU bus
|
||||||
public boolean write(int address, byte[] src, int offset, int length) {
|
public boolean writeBytes(int address, byte[] src, int offset, int length){
|
||||||
int count = 0; // Bytes to process in one iteration
|
|
||||||
|
|
||||||
// Error checking
|
// Error checking
|
||||||
if (
|
if (
|
||||||
|
@ -170,26 +214,126 @@ class JavaVUE extends VUE {
|
||||||
) return false;
|
) return false;
|
||||||
|
|
||||||
// Perform the operation
|
// Perform the operation
|
||||||
for (; length > 0; address += count, length -= count, offset += count){
|
while (length > 0) {
|
||||||
|
int count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF));
|
||||||
// Determine the maximum number of bytes to process at once
|
|
||||||
count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF));
|
|
||||||
|
|
||||||
// Process by component
|
|
||||||
switch (address >> 24 & 7) {
|
switch (address >> 24 & 7) {
|
||||||
case 5: /* System WRAM */
|
case 5: writeBytes(wram ,address,src,offset,count); break;
|
||||||
bus.writeBytes(bus.wram, src, address, offset, count);
|
case 6: writeBytes(pak.ram,address,src,offset,count); break;
|
||||||
break;
|
case 7: writeBytes(pak.rom,address,src,offset,count); break;
|
||||||
case 6: /* Cartridge RAM */
|
|
||||||
bus.writeBytes(bus.sram, src, address, offset, count);
|
|
||||||
break;
|
|
||||||
case 7: /* Cartridge ROM */
|
|
||||||
bus.writeBytes(bus.rom , src, address, offset, count);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
address += count;
|
||||||
};
|
length -= count;
|
||||||
|
offset += count;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Package Methods //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Read a value from a byte buffer
|
||||||
|
static int readBuffer(byte[] data, int address, int type) {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (data == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Processing by data type
|
||||||
|
int size = TYPE_SIZES[type];
|
||||||
|
int value = 0;
|
||||||
|
address &= ~size + 1 & data.length - 1;
|
||||||
|
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) == 1) {
|
||||||
|
size = 32 - (size << 3);
|
||||||
|
value = value << size >> size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read bytes from a byte buffer
|
||||||
|
static void readBytes(byte[] data, int address, byte[] dest, int offset,
|
||||||
|
int length) {
|
||||||
|
|
||||||
|
// The source does not exist
|
||||||
|
if (data == null) {
|
||||||
|
Arrays.fill(dest, offset, offset + length, (byte) 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer bytes from the source as a circular buffer
|
||||||
|
while (length > 0) {
|
||||||
|
address &= data.length - 1;
|
||||||
|
int count = Math.min(length, data.length - address);
|
||||||
|
System.arraycopy(data, address, dest, offset, count);
|
||||||
|
address += count;
|
||||||
|
length -= count;
|
||||||
|
offset += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a value to a byte buffer
|
||||||
|
static void writeBuffer(byte[] data, int address, int type, int value) {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (data == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Processing by data type
|
||||||
|
int size = TYPE_SIZES[type];
|
||||||
|
address &= ~size + 1 & data.length - 1;
|
||||||
|
switch (type) {
|
||||||
|
case VUE.S32:
|
||||||
|
data[address + 3] = (byte) (value >> 24);
|
||||||
|
data[address + 2] = (byte) (value >> 16);
|
||||||
|
// Fallthrough
|
||||||
|
case VUE.S16: // Fallthrough
|
||||||
|
case VUE.U16:
|
||||||
|
data[address + 1] = (byte) (value >> 8);
|
||||||
|
// Fallthrough
|
||||||
|
case VUE.S8 : // Fallthrough
|
||||||
|
case VUE.U8 : value |=
|
||||||
|
data[address ] = (byte) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write bytes to a byte buffer
|
||||||
|
static void writeBytes(byte[] data, int address, byte[] src, int offset,
|
||||||
|
int length) {
|
||||||
|
|
||||||
|
// The destination does not exist
|
||||||
|
if (data == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Transfer bytes to the destination as a circular buffer
|
||||||
|
while (length > 0) {
|
||||||
|
address &= data.length - 1;
|
||||||
|
int count = Math.min(length, data.length - address);
|
||||||
|
System.arraycopy(src, offset, data, address, count);
|
||||||
|
address += count;
|
||||||
|
length -= count;
|
||||||
|
offset += count;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,13 +75,20 @@ JNIEXPORT void JNICALL Java_vue_NativeVUE_construct
|
||||||
JNIEXPORT void JNICALL Java_vue_NativeVUE_dispose
|
JNIEXPORT void JNICALL Java_vue_NativeVUE_dispose
|
||||||
(JNIEnv *env, jobject vue) {
|
(JNIEnv *env, jobject vue) {
|
||||||
CORE *core = GetCore(env, vue);
|
CORE *core = GetCore(env, vue);
|
||||||
if (core->vue.bus.rom != NULL)
|
if (core->vue.pak.rom != NULL)
|
||||||
free(core->vue.bus.rom);
|
free(core->vue.pak.rom);
|
||||||
if (core->vue.bus.sram != NULL)
|
if (core->vue.pak.ram != NULL)
|
||||||
free(core->vue.bus.sram);
|
free(core->vue.pak.ram);
|
||||||
free(core);
|
free(core);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Process the simulation
|
||||||
|
JNIEXPORT jint JNICALL Java_vue_NativeVUE_emulate
|
||||||
|
(JNIEnv *env, jobject vue, jint maxCycles) {
|
||||||
|
CORE *core = GetCore(env, vue);
|
||||||
|
return vueEmulate(&core->vue, maxCycles);
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve a register value
|
// Retrieve a register value
|
||||||
JNIEXPORT jint JNICALL Java_vue_NativeVUE_getRegister
|
JNIEXPORT jint JNICALL Java_vue_NativeVUE_getRegister
|
||||||
(JNIEnv *env, jobject vue, jint index, jboolean system) {
|
(JNIEnv *env, jobject vue, jint index, jboolean system) {
|
||||||
|
@ -95,13 +102,13 @@ JNIEXPORT jbyteArray JNICALL Java_vue_NativeVUE_getROM
|
||||||
CORE *core = GetCore(env, vue);
|
CORE *core = GetCore(env, vue);
|
||||||
|
|
||||||
// No ROM data
|
// No ROM data
|
||||||
if (core->vue.bus.rom == NULL)
|
if (core->vue.pak.rom == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// Copy the ROM data
|
// Copy the ROM data
|
||||||
jbyteArray ret = (*env)->NewByteArray(env, (jint)core->vue.bus.romSize);
|
jbyteArray ret = (*env)->NewByteArray(env, (jint)core->vue.pak.romSize);
|
||||||
jbyte *elems = (*env)->GetByteArrayElements(env, ret, NULL);
|
jbyte *elems = (*env)->GetByteArrayElements(env, ret, NULL);
|
||||||
memcpy(elems, core->vue.bus.rom, core->vue.bus.romSize);
|
memcpy(elems, core->vue.pak.rom, core->vue.pak.romSize);
|
||||||
(*env)->ReleaseByteArrayElements(env, ret, elems, 0);
|
(*env)->ReleaseByteArrayElements(env, ret, elems, 0);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -112,8 +119,15 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_isNative
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read a value from the CPU bus
|
||||||
|
JNIEXPORT jint JNICALL Java_vue_NativeVUE_read
|
||||||
|
(JNIEnv *env, jobject vue, jint address, jint type) {
|
||||||
|
CORE *core = GetCore(env, vue);
|
||||||
|
return vueRead(&core->vue, address, type);
|
||||||
|
}
|
||||||
|
|
||||||
// Read bytes from the CPU bus
|
// Read bytes from the CPU bus
|
||||||
JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_read
|
JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_readBytes
|
||||||
(JNIEnv *env, jobject vue, jint address, jbyteArray data, jint offset,
|
(JNIEnv *env, jobject vue, jint address, jbyteArray data, jint offset,
|
||||||
jint length) {
|
jint length) {
|
||||||
|
|
||||||
|
@ -128,7 +142,7 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_read
|
||||||
// Perform the operation
|
// Perform the operation
|
||||||
CORE *core = GetCore(env, vue);
|
CORE *core = GetCore(env, vue);
|
||||||
jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL);
|
jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL);
|
||||||
vueRead(&core->vue, address, &elems[offset], length);
|
vueReadBytes(&core->vue, address, &elems[offset], length);
|
||||||
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
|
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
}
|
}
|
||||||
|
@ -161,21 +175,28 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_setROM
|
||||||
) return JNI_FALSE;
|
) return JNI_FALSE;
|
||||||
|
|
||||||
// Accept the new ROM data
|
// Accept the new ROM data
|
||||||
uint8_t *rom = calloc(length, 1);
|
uint8_t *rom = malloc(length);
|
||||||
jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL);
|
jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL);
|
||||||
memcpy(rom, &elems[offset], length);
|
memcpy(rom, &elems[offset], length);
|
||||||
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
|
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
|
||||||
|
|
||||||
// Transfer the ROM data to the emulation state
|
// Transfer the ROM data to the emulation state
|
||||||
CORE *core = GetCore(env, vue);
|
CORE *core = GetCore(env, vue);
|
||||||
if (core->vue.bus.rom != NULL)
|
if (core->vue.pak.rom != NULL)
|
||||||
free(core->vue.bus.rom);
|
free(core->vue.pak.rom);
|
||||||
vueSetROM(&core->vue, rom, length);
|
vueSetROM(&core->vue, rom, length);
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Write bytes to the CPU bus */
|
// Write a value to the CPU bus
|
||||||
JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_write
|
JNIEXPORT void JNICALL Java_vue_NativeVUE_write
|
||||||
|
(JNIEnv *env, jobject vue, jint address, jint type, jint value) {
|
||||||
|
CORE *core = GetCore(env, vue);
|
||||||
|
vueWrite(&core->vue, address, type, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write bytes to the CPU bus
|
||||||
|
JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_writeBytes
|
||||||
(JNIEnv *env, jobject vue, jint address, jbyteArray data, jint offset,
|
(JNIEnv *env, jobject vue, jint address, jbyteArray data, jint offset,
|
||||||
jint length) {
|
jint length) {
|
||||||
|
|
||||||
|
@ -190,7 +211,7 @@ JNIEXPORT jboolean JNICALL Java_vue_NativeVUE_write
|
||||||
// Perform the operation
|
// Perform the operation
|
||||||
CORE *core = GetCore(env, vue);
|
CORE *core = GetCore(env, vue);
|
||||||
jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL);
|
jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL);
|
||||||
vueWrite(&core->vue, address, &elems[offset], length);
|
vueWriteBytes(&core->vue, address, &elems[offset], length);
|
||||||
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
|
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
|
||||||
return JNI_TRUE;
|
return JNI_TRUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,9 @@ class NativeVUE extends VUE {
|
||||||
// Release any used resources
|
// Release any used resources
|
||||||
public native void dispose();
|
public native void dispose();
|
||||||
|
|
||||||
|
// Process the simulation
|
||||||
|
public native int emulate(int maxCycles);
|
||||||
|
|
||||||
// Retrieve a register value
|
// Retrieve a register value
|
||||||
public native int getRegister(int index, boolean system);
|
public native int getRegister(int index, boolean system);
|
||||||
|
|
||||||
|
@ -35,8 +38,11 @@ class NativeVUE extends VUE {
|
||||||
// Determine whether the context is native-backed
|
// Determine whether the context is native-backed
|
||||||
public native boolean isNative();
|
public native boolean isNative();
|
||||||
|
|
||||||
|
// Read a value from the CPU bus
|
||||||
|
public native int read(int address, int type);
|
||||||
|
|
||||||
// Read bytes from the CPU bus
|
// Read bytes from the CPU bus
|
||||||
public native boolean read(int address, byte[] dest, int offset,
|
public native boolean readBytes(int address, byte[] dest, int offset,
|
||||||
int length);
|
int length);
|
||||||
|
|
||||||
// Initialize all system components
|
// Initialize all system components
|
||||||
|
@ -48,8 +54,11 @@ class NativeVUE extends VUE {
|
||||||
// Provide new ROM data
|
// Provide new ROM data
|
||||||
public native boolean setROM(byte[] data, int offset, int length);
|
public native boolean setROM(byte[] data, int offset, int length);
|
||||||
|
|
||||||
|
// Write a value to the CPU bus
|
||||||
|
public native void write(int address, int type, int value);
|
||||||
|
|
||||||
// Write bytes to the CPU bus
|
// Write bytes to the CPU bus
|
||||||
public native boolean write(int address, byte[] src, int offset,
|
public native boolean writeBytes(int address, byte[] src, int offset,
|
||||||
int length);
|
int length);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -158,6 +158,9 @@ public abstract class VUE {
|
||||||
// Release any used resources
|
// Release any used resources
|
||||||
public abstract void dispose();
|
public abstract void dispose();
|
||||||
|
|
||||||
|
// Process the simulation
|
||||||
|
public abstract int emulate(int maxCycles);
|
||||||
|
|
||||||
// Retrieve a register value
|
// Retrieve a register value
|
||||||
public abstract int getRegister(int index, boolean system);
|
public abstract int getRegister(int index, boolean system);
|
||||||
|
|
||||||
|
@ -167,8 +170,11 @@ public abstract class VUE {
|
||||||
// Determine whether the context is native-backed
|
// Determine whether the context is native-backed
|
||||||
public abstract boolean isNative();
|
public abstract boolean isNative();
|
||||||
|
|
||||||
|
// Read a value from the CPU bus
|
||||||
|
public abstract int read(int address, int type);
|
||||||
|
|
||||||
// Read bytes from the CPU bus
|
// Read bytes from the CPU bus
|
||||||
public abstract boolean read(int address, byte[] dest, int offset,
|
public abstract boolean readBytes(int address, byte[] dest, int offset,
|
||||||
int length);
|
int length);
|
||||||
|
|
||||||
// Initialize all system components
|
// Initialize all system components
|
||||||
|
@ -180,8 +186,11 @@ public abstract class VUE {
|
||||||
// Provide new ROM data
|
// Provide new ROM data
|
||||||
public abstract boolean setROM(byte[] data, int offset, int length);
|
public abstract boolean setROM(byte[] data, int offset, int length);
|
||||||
|
|
||||||
|
// Write a value to the CPU bus
|
||||||
|
public abstract void write(int address, int type, int value);
|
||||||
|
|
||||||
// Write bytes to the CPU bus
|
// Write bytes to the CPU bus
|
||||||
public abstract boolean write(int address, byte[] src, int offset,
|
public abstract boolean writeBytes(int address, byte[] src, int offset,
|
||||||
int length);
|
int length);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue