Compare commits

..

No commits in common. "master" and "old-java" have entirely different histories.

70 changed files with 32312 additions and 2509 deletions

17
.gitattributes vendored
View File

@ -1,12 +1,11 @@
*.c text eol=lf diff=c * text=auto
*.css text eol=lf diff=css
*.h text eol=lf diff=c *.c text diff=c
*.html text eol=lf diff=html *.h text diff=c
*.java text eol=lf diff=java *.html text diff=html
*.js text eol=lf diff=js *.java text diff=java
*.txt text eol=lf *.sample text
*.txt text
*.class binary *.class binary
*.dll binary *.dll binary
*.wasm binary
*.woff2 binary

View File

@ -1,156 +0,0 @@
/* This file is included into vb.c and cannot be compiled on its own. */
#ifdef VBAPI
/********************************* Constants *********************************/
/* Memory access address masks by data type */
static const uint32_t TYPE_MASKS[] = {
0x07FFFFFF, /* S8 */
0x07FFFFFF, /* U8 */
0x07FFFFFE, /* S16 */
0x07FFFFFE, /* U16 */
0x07FFFFFC /* S32 */
};
/*************************** Sub-Module Functions ****************************/
/* Read a typed value from a buffer in host memory */
static int32_t busReadBuffer(uint8_t *data, int type) {
/* Processing by data type */
switch (type) {
/* Generic implementation */
#ifndef VB_LITTLE_ENDIAN
case VB_S8 : return ((int8_t *)data)[0];
case VB_U8 : return data [0];
case VB_S16: return (int32_t) ((int8_t *)data)[1] << 8 | data[0];
case VB_U16: return (int32_t) data [1] << 8 | data[0];
case VB_S32: return
(int32_t) data[3] << 24 | (int32_t) data[2] << 16 |
(int32_t) data[1] << 8 | data[0];
/* Little-endian host */
#else
case VB_S8 : return *(int8_t *) data;
case VB_U8 : return * data;
case VB_S16: return *(int16_t *) data;
case VB_U16: return *(uint16_t *) data;
case VB_S32: return *(int32_t *) data;
#endif
}
return 0; /* Unreachable */
}
/* Write a typed value to a buffer in host memory */
static void busWriteBuffer(uint8_t *data, int type, int32_t value) {
/* Processing by data type */
switch (type) {
/* Generic implementation */
#ifndef VB_LITTLE_ENDIAN
case VB_S32: data[3] = value >> 24;
data[2] = value >> 16; /* Fallthrough */
case VB_S16: /* Fallthrough */
case VB_U16: data[1] = value >> 8; /* Fallthrough */
case VB_S8 : /* Fallthrough */
case VB_U8 : data[0] = value;
/* Little-endian host */
#else
case VB_S8 : /* Fallthrough */
case VB_U8 : * data = value; return;
case VB_S16: /* Fallthrough */
case VB_U16: *(uint16_t *) data = value; return;
case VB_S32: *(int32_t *) data = value; return;
#endif
}
}
/***************************** Library Functions *****************************/
/* Read a typed value from the simulation bus */
static void busRead(VB *sim, uint32_t address, int type, int32_t *value) {
/* Working variables */
address &= TYPE_MASKS[type];
*value = 0;
/* Process by address range */
switch (address >> 24) {
case 0: break; /* VIP */
case 1: break; /* VSU */
case 2: break; /* Misc. I/O */
case 3: break; /* Unmapped */
case 4: break; /* Game Pak expansion */
case 5: /* WRAM */
*value = busReadBuffer(&sim->wram[address & 0x0000FFFF], type);
break;
case 6: /* Game Pak RAM */
if (sim->cart.ram != NULL) {
*value = busReadBuffer(
&sim->cart.ram[address & sim->cart.ramMask], type);
}
break;
case 7: /* Game Pak ROM */
if (sim->cart.rom != NULL) {
*value = busReadBuffer(
&sim->cart.rom[address & sim->cart.romMask], type);
}
break;
}
}
/* Write a typed value to the simulation bus */
static void busWrite(VB*sim,uint32_t address,int type,int32_t value,int debug){
/* Working variables */
address &= TYPE_MASKS[type];
/* Process by address range */
switch (address >> 24) {
case 0: break; /* VIP */
case 1: break; /* VSU */
case 2: break; /* Misc. I/O */
case 3: break; /* Unmapped */
case 4: break; /* Game Pak expansion */
case 5: /* WRAM */
busWriteBuffer(&sim->wram[address & 0x0000FFFF], type, value);
break;
case 6: /* Game Pak RAM */
if (sim->cart.ram != NULL) {
busWriteBuffer(
&sim->cart.ram[address & sim->cart.ramMask], type, value);
}
break;
case 7: /* Game Pak ROM */
if (debug && sim->cart.rom != NULL) {
busWriteBuffer(
&sim->cart.rom[address & sim->cart.romMask], type, value);
}
break;
}
}
#endif /* VBAPI */

1801
core/cpu.c

File diff suppressed because it is too large Load Diff

391
core/vb.c
View File

@ -1,391 +0,0 @@
#ifndef VBAPI
#define VBAPI
#endif
#include <float.h>
#include <vb.h>
/*********************************** Types ***********************************/
/* Simulation state */
struct VB {
/* Game Pak */
struct {
uint8_t *ram; /* Save RAM */
uint8_t *rom; /* Program ROM */
uint32_t ramMask; /* Size of SRAM - 1 */
uint32_t romMask; /* Size of ROM - 1 */
} cart;
/* CPU */
struct {
/* Cache Control Word */
struct {
uint8_t ice; /* Instruction Cache Enable */
} chcw;
/* Exception Cause Register */
struct {
uint16_t eicc; /* Exception/Interrupt Cause Code */
uint16_t fecc; /* Fatal Error Cause Code */
} ecr;
/* Program Status Word */
struct {
uint8_t ae; /* Address Trap Enable */
uint8_t cy; /* Carry */
uint8_t ep; /* Exception Pending */
uint8_t fiv; /* Floating Invalid */
uint8_t fov; /* Floating Overflow */
uint8_t fpr; /* Floading Precision */
uint8_t fro; /* Floating Reserved Operand */
uint8_t fud; /* Floading Underflow */
uint8_t fzd; /* Floating Zero Divide */
uint8_t i; /* Interrupt Level */
uint8_t id; /* Interrupt Disable */
uint8_t np; /* NMI Pending */
uint8_t ov; /* Overflow */
uint8_t s; /* Sign */
uint8_t z; /* Zero */
} psw;
/* Other registers */
uint32_t adtre; /* Address Trap Register for Execution */
uint32_t eipc; /* Exception/Interrupt PC */
uint32_t eipsw; /* Exception/Interrupt PSW */
uint32_t fepc; /* Fatal Error PC */
uint32_t fepsw; /* Fatal Error PSW */
uint32_t pc; /* Program Counter */
int32_t program[32]; /* Program registers */
uint32_t sr29; /* System register 29 */
uint32_t sr31; /* System register 31 */
/* Working data */
union {
struct {
uint32_t dest;
uint64_t src;
} bs; /* Arithmetic bit strings */
struct {
uint32_t address;
int32_t value;
} data; /* Data accesses */
} aux;
/* Other state */
uint32_t clocks; /* Master clocks to wait */
uint16_t code[2]; /* Instruction code units */
uint16_t exception; /* Exception cause code */
int halt; /* CPU is halting */
uint16_t irq; /* Interrupt request lines */
int length; /* Instruction code length */
uint32_t nextPC; /* Address of next instruction */
int operation; /* Current operation ID */
int step; /* Operation sub-task ID */
} cpu;
/* Other system state */
uint8_t wram[0x10000]; /* System RAM */
/* Application data */
vbOnException onException; /* CPU exception */
vbOnExecute onExecute; /* CPU instruction execute */
vbOnFetch onFetch; /* CPU instruction fetch */
vbOnRead onRead; /* CPU instruction read */
vbOnWrite onWrite; /* CPU instruction write */
void *tag; /* User data */
};
/***************************** Library Functions *****************************/
/* Sign-extend an integer of variable width */
static int32_t SignExtend(int32_t value, int32_t bits) {
#ifndef VB_SIGNED_PROPAGATE
value &= ~((uint32_t) 0xFFFFFFFF << bits);
bits = (int32_t) 1 << (bits - (int32_t) 1);
return (value ^ bits) - bits;
#else
return value << (32 - bits) >> (32 - bits);
#endif
}
/******************************** Sub-Modules ********************************/
#include "bus.c"
#include "cpu.c"
/***************************** Library Functions *****************************/
/* Process a simulation for a given number of clocks */
static int sysEmulate(VB *sim, uint32_t clocks) {
return
cpuEmulate(sim, clocks)
;
}
/* Determine how many clocks are guaranteed to process */
static uint32_t sysUntil(VB *sim, uint32_t clocks) {
clocks = cpuUntil(sim, clocks);
return clocks;
}
/******************************* API Commands ********************************/
/* Process one simulation */
VBAPI int vbEmulate(VB *sim, uint32_t *clocks) {
int brk; /* A callback requested a break */
uint32_t until; /* Clocks guaranteed to process */
while (*clocks != 0) {
until = sysUntil(sim, *clocks);
brk = sysEmulate(sim, until);
*clocks -= until;
if (brk)
return brk; /* TODO: return 1 */
}
return 0;
}
/* Process multiple simulations */
VBAPI int vbEmulateEx(VB **sims, int count, uint32_t *clocks) {
int brk; /* A callback requested a break */
uint32_t until; /* Clocks guaranteed to process */
int x; /* Iterator */
while (*clocks != 0) {
until = *clocks;
for (x = count - 1; x >= 0; x--)
until = sysUntil(sims[x], until);
brk = 0;
for (x = count - 1; x >= 0; x--)
brk |= sysEmulate(sims[x], until);
*clocks -= until;
if (brk)
return brk; /* TODO: return 1 */
}
return 0;
}
/* Retrieve the game pack RAM buffer */
VBAPI void* vbGetCartRAM(VB *sim, uint32_t *size) {
if (size != NULL)
*size = sim->cart.ram == NULL ? 0 : sim->cart.ramMask + 1;
return sim->cart.ram;
}
/* Retrieve the game pack ROM buffer */
VBAPI void* vbGetCartROM(VB *sim, uint32_t *size) {
if (size != NULL)
*size = sim->cart.rom == NULL ? 0 : sim->cart.romMask + 1;
return sim->cart.rom;
}
/* Retrieve the exception callback handle */
VBAPI vbOnException vbGetExceptionCallback(VB *sim) {
return sim->onException;
}
/* Retrieve the execute callback handle */
VBAPI vbOnExecute vbGetExecuteCallback(VB *sim) {
return sim->onExecute;
}
/* Retrieve the fetch callback handle */
VBAPI vbOnFetch vbGetFetchCallback(VB *sim) {
return sim->onFetch;
}
/* Retrieve the value of the program counter */
VBAPI uint32_t vbGetProgramCounter(VB *sim) {
return sim->cpu.pc;
}
/* Retrieve the value in a program register */
VBAPI int32_t vbGetProgramRegister(VB *sim, int index) {
return index < 1 || index > 31 ? 0 : sim->cpu.program[index];
}
/* Retrieve the read callback handle */
VBAPI vbOnRead vbGetReadCallback(VB *sim) {
return sim->onRead;
}
/* Retrieve the value in a system register */
VBAPI uint32_t vbGetSystemRegister(VB *sim, int index) {
return index < 0 || index > 31 ? 0 : cpuGetSystemRegister(sim, index);
}
/* Retrieve a simulation's userdata pointer */
VBAPI void* vbGetUserData(VB *sim) {
return sim->tag;
}
/* Retrieve the write callback handle */
VBAPI vbOnWrite vbGetWriteCallback(VB *sim) {
return sim->onWrite;
}
/* Initialize a simulation instance */
VBAPI VB* vbInit(VB *sim) {
sim->cart.ram = NULL;
sim->cart.rom = NULL;
sim->onExecute = NULL;
sim->onFetch = NULL;
sim->onRead = NULL;
sim->onWrite = NULL;
vbReset(sim);
return sim;
}
/* Read a value from the memory bus */
VBAPI int32_t vbRead(VB *sim, uint32_t address, int type) {
int32_t value;
if (type < 0 || type > 4)
return 0;
busRead(sim, address, type, &value);
return value;
}
/* Simulate a hardware reset */
VBAPI VB* vbReset(VB *sim) {
uint32_t x; /* Iterator */
/* WRAM (the hardware does not do this) */
for (x = 0; x < 0x10000; x++)
sim->wram[x] = 0x00;
/* CPU (normal) */
sim->cpu.exception = 0;
sim->cpu.halt = 0;
sim->cpu.irq = 0;
sim->cpu.pc = 0xFFFFFFF0;
cpuSetSystemRegister(sim, VB_ECR, 0x0000FFF0, 1);
cpuSetSystemRegister(sim, VB_PSW, 0x00008000, 1);
/* CPU (extra, hardware does not do this) */
sim->cpu.adtre = 0x00000000;
sim->cpu.eipc = 0x00000000;
sim->cpu.eipsw = 0x00000000;
sim->cpu.fepc = 0x00000000;
sim->cpu.fepsw = 0x00000000;
sim->cpu.sr29 = 0x00000000;
sim->cpu.sr31 = 0x00000000;
cpuSetSystemRegister(sim, VB_CHCW, 0x00000000, 1);
for (x = 0; x < 32; x++)
sim->cpu.program[x] = 0x00000000;
/* CPU (other) */
sim->cpu.clocks = 0;
sim->cpu.nextPC = 0xFFFFFFF0;
sim->cpu.operation = CPU_FETCH;
sim->cpu.step = 0;
return sim;
}
/* Specify a game pak RAM buffer */
VBAPI int vbSetCartRAM(VB *sim, void *sram, uint32_t size) {
if (sram != NULL) {
if (size < 16 || size > 0x1000000 || (size & (size - 1)) != 0)
return 1;
sim->cart.ramMask = size - 1;
}
sim->cart.ram = sram;
return 0;
}
/* Specify a game pak ROM buffer */
VBAPI int vbSetCartROM(VB *sim, void *rom, uint32_t size) {
if (rom != NULL) {
if (size < 16 || size > 0x1000000 || (size & (size - 1)) != 0)
return 1;
sim->cart.romMask = size - 1;
}
sim->cart.rom = rom;
return 0;
}
/* Specify a new exception callback handle */
VBAPI vbOnException vbSetExceptionCallback(VB *sim, vbOnException callback) {
vbOnException prev = sim->onException;
sim->onException = callback;
return prev;
}
/* Specify a new execute callback handle */
VBAPI vbOnExecute vbSetExecuteCallback(VB *sim, vbOnExecute callback) {
vbOnExecute prev = sim->onExecute;
sim->onExecute = callback;
return prev;
}
/* Specify a new fetch callback handle */
VBAPI vbOnFetch vbSetFetchCallback(VB *sim, vbOnFetch callback) {
vbOnFetch prev = sim->onFetch;
sim->onFetch = callback;
return prev;
}
/* Specify a new value for the program counter */
VBAPI uint32_t vbSetProgramCounter(VB *sim, uint32_t value) {
sim->cpu.operation = CPU_FETCH;
sim->cpu.pc = sim->cpu.nextPC = value & 0xFFFFFFFE;
sim->cpu.step = 0;
return sim->cpu.pc;
}
/* Specify a new value for a program register */
VBAPI int32_t vbSetProgramRegister(VB *sim, int index, int32_t value) {
return index < 1 || index > 31 ? 0 : (sim->cpu.program[index] = value);
}
/* Specify a new read callback handle */
VBAPI vbOnRead vbSetReadCallback(VB *sim, vbOnRead callback) {
vbOnRead prev = sim->onRead;
sim->onRead = callback;
return prev;
}
/* Specify a new value for a system register */
VBAPI uint32_t vbSetSystemRegister(VB *sim, int index, uint32_t value) {
return index < 0 || index > 31 ? 0 :
cpuSetSystemRegister(sim, index, value, 1);
}
/* Specify a new write callback handle */
VBAPI vbOnWrite vbSetWriteCallback(VB *sim, vbOnWrite callback) {
vbOnWrite prev = sim->onWrite;
sim->onWrite = callback;
return prev;
}
/* Determine the size of a simulation instance */
VBAPI size_t vbSizeOf() {
return sizeof (VB);
}
/* Specify a simulation's userdata pointer */
VBAPI void* vbSetUserData(VB *sim, void *tag) {
void *prev = sim->tag;
sim->tag = tag;
return prev;
}
/* Write a value to the memory bus */
VBAPI int32_t vbWrite(VB *sim, uint32_t address, int type, int32_t value) {
if (type < 0 || type > 4)
return 0;
busWrite(sim, address, type, value, 1);
return vbRead(sim, address, type);
}

103
core/vb.h
View File

@ -1,103 +0,0 @@
#ifndef VB_H_
#define VB_H_
#ifdef __cplusplus
extern "C" {
#endif
#ifndef VBAPI
#define VBAPI extern
#endif
/* Header includes */
#include <stddef.h>
#include <stdint.h>
/********************************* Constants *********************************/
/* Callback IDs */
#define VB_EXCEPTION 0
#define VB_EXECUTE 1
#define VB_FETCH 2
#define VB_FRAME 3
#define VB_READ 4
#define VB_WRITE 5
/* System registers */
#define VB_ADTRE 25
#define VB_CHCW 24
#define VB_ECR 4
#define VB_EIPC 0
#define VB_EIPSW 1
#define VB_FEPC 2
#define VB_FEPSW 3
#define VB_PIR 6
#define VB_PSW 5
#define VB_TKCW 7
/* Memory access data types */
#define VB_S8 0
#define VB_U8 1
#define VB_S16 2
#define VB_U16 3
#define VB_S32 4
/*********************************** Types ***********************************/
/* Simulation state */
typedef struct VB VB;
/* Callbacks */
typedef int (*vbOnException)(VB *sim, uint16_t *cause);
typedef int (*vbOnExecute )(VB *sim, uint32_t address, const uint16_t *code, int length);
typedef int (*vbOnFetch )(VB *sim, int fetch, uint32_t address, int32_t *value, uint32_t *cycles);
typedef int (*vbOnRead )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles);
typedef int (*vbOnWrite )(VB *sim, uint32_t address, int type, int32_t *value, uint32_t *cycles, int *cancel);
/******************************* API Commands ********************************/
VBAPI int vbEmulate (VB *sim, uint32_t *clocks);
VBAPI int vbEmulateEx (VB **sims, int count, uint32_t *clocks);
VBAPI void* vbGetCallback (VB *sim, int id);
VBAPI void* vbGetCartRAM (VB *sim, uint32_t *size);
VBAPI void* vbGetCartROM (VB *sim, uint32_t *size);
VBAPI vbOnException vbGetExceptionCallback(VB *sim);
VBAPI vbOnExecute vbGetExecuteCallback (VB *sim);
VBAPI vbOnFetch vbGetFetchCallback (VB *sim);
VBAPI uint32_t vbGetProgramCounter (VB *sim);
VBAPI int32_t vbGetProgramRegister (VB *sim, int index);
VBAPI vbOnRead vbGetReadCallback (VB *sim);
VBAPI uint32_t vbGetSystemRegister (VB *sim, int index);
VBAPI void* vbGetUserData (VB *sim);
VBAPI vbOnWrite vbGetWriteCallback (VB *sim);
VBAPI VB* vbInit (VB *sim);
VBAPI int32_t vbRead (VB *sim, uint32_t address, int type);
VBAPI VB* vbReset (VB *sim);
VBAPI int vbSetCartRAM (VB *sim, void *sram, uint32_t size);
VBAPI int vbSetCartROM (VB *sim, void *rom, uint32_t size);
VBAPI vbOnException vbSetExceptionCallback(VB *sim, vbOnException callback);
VBAPI vbOnExecute vbSetExecuteCallback (VB *sim, vbOnExecute callback);
VBAPI vbOnFetch vbSetFetchCallback (VB *sim, vbOnFetch callback);
VBAPI uint32_t vbSetProgramCounter (VB *sim, uint32_t value);
VBAPI int32_t vbSetProgramRegister (VB *sim, int index, int32_t value);
VBAPI vbOnRead vbSetReadCallback (VB *sim, vbOnRead callback);
VBAPI uint32_t vbSetSystemRegister (VB *sim, int index, uint32_t value);
VBAPI void* vbSetUserData (VB *sim, void *tag);
VBAPI vbOnWrite vbSetWriteCallback (VB *sim, vbOnWrite callback);
VBAPI size_t vbSizeOf ();
VBAPI int32_t vbWrite (VB *sim, uint32_t address, int type, int32_t value);
#ifdef __cplusplus
}
#endif
#endif /* VB_H_ */

705
expressions.html Normal file
View File

@ -0,0 +1,705 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Expression Evaluator</title>
<style>
:root {
font-family: Arial, sans-serif;
font-size : 16px;
text-align : justify;
-webkit-text-size-adjust: none;
}
a { text-decoration : underline; }
a.ext { text-decoration-style: dotted; }
body { margin : 8px; }
circle, path, rect {
fill : none;
paint-order : markers fill stroke;
stroke-width: 1.5;
}
code, pre, .mono { font-family: Consolas, monospace; }
h1 {
border-radius: 8px;
font-size : 20px;
font-weight : normal;
padding : 4px 8px;
}
h1.top {
font-weight: bold;
font-size : 24px;
text-align : center;
}
h1.bottom {
font-size : 16px;
padding : 6px 8px;
text-align: right;
}
h2 {
font-size : 18px;
font-weight: bold;
margin-top : 32px;
}
sup, .small { font-size: 10px; }
table {
border : none;
border-spacing: 0px;
}
td, th { padding-right : 12px; }
tr { vertical-align: top; }
.b {
border-style: solid;
border-width: 0;
font-size : 1px;
height : 4px;
padding : 0;
width : 16px;
}
.bb { border-bottom-width: 1px; }
.bc, .bd, .be {
font-size : 12px;
height : 32px;
position : relative;
text-align: center;
}
.bd { height: 48px; }
.be { height: 72px; }
.bh {
font-size : 10px;
padding : 2px 0;
text-align: center;
}
.bl { border-left-width : 1px; }
.br { border-right-width : 1px; }
.bs, .by, .bz {
box-sizing : border-box;
height : 17px;
left : 17px;
line-height : 17px;
margin-left : -1px;
position : absolute;
text-align : center;
top : -4px;
transform : rotate(90deg);
transform-origin: 0 0;
white-space : nowrap;
width : 41px;
}
.by {
font-size: 10px;
width : 82px;
}
.bz {
font-size: 10px;
width : 57px;
}
.bt { border-top-width : 1px; }
.center { text-align : center; }
.ednote {
border-radius: 8px;
border-style : solid;
border-width : 1px;
font-style : italic;
margin-left : 16px;
margin-right : 16px;
padding : 4px 8px;
}
.indent { margin-left : 16px; }
.middle { vertical-align: middle; }
.minor { font-size : 14px; }
.nowrap { white-space : nowrap; }
.narrow { width : 1px; }
.outdent { margin-left : -16px; }
.right { text-align : right; }
:root { color : #000000; }
a { color : #0099ff; }
a.ext { color : #00bb66; }
a.redlink { color : #ff3366; }
body { background : #ffffff; }
h1, .shade { background : #d4d4d4; }
.b { border-color: #666666; }
.ednote { border-color: #999999; color: #666666; }
.fill { fill : #ffffff; }
.stroke { stroke : #666666; }
.bordered {
border-style: solid;
border-color: #999999;
border-width: 1px 0 0 1px;
}
.bordered td {
border-style: solid;
border-color: #999999;
border-width: 0 1px 1px 0;
}
.bordered tr > :first-child, .op1 {
padding: 1px 12px 1px 12px;
text-align: center;
}
.bordered td.open { border-bottom: none; }
</style>
</head>
<body>
<h1 class="top">Expression Evaluator</h1>
<p>
The emulator is capable of evaluating expressions within breakpoint
conditions and Go To prompts. These expressions inspect the current emulation
state and can be used to dynamically process registers, variables and other
data elements that may change from one invocation to the next.
</p>
<p>
Expressions are organized into series of tokens with three main modes of
significance:
</p>
<table class="indent">
<tr>
<td class="narrow nowrap">&bull; Value</td>
<td>
Numeric value that can be used in computation. This may take the form of
a literal number provided directly in the expression, or one of the
named symbols that represent things in the CPU state, current
instruction and/or current memory access.
</td>
</tr>
<tr>
<td class="narrow nowrap">&bull; Operator</td>
<td>
Modifies one or two values through a given operation. Unary operators
modify only the value on their right, while binary operators consider the
values on their left and right and produce a new value as the result.
</td>
</tr>
<tr>
<td class="nowrap">&bull; Group</td>
<td>
Operand groups, mainly through the use of parentheses <code>()</code>,
override the default order of operations. Anything enclosed in a group
is processed before any operations adjacent to that group.
</td>
</tr>
</table>
<p>
Expressions are case-insensitive.
</p>
<h2>Values</h2>
<p>
Values come in two forms:
</p>
<table class="indent">
<tr>
<td class="narrow nowrap">&bull; Literal</td>
<td>Provided directly by the expression.</td>
</tr>
<tr>
<td class="nowrap">&bull; Symbol</td>
<td>Refers to something in the current emulation state.</td>
</tr>
</table>
<p>
Values may be one of four data types:
</p>
<table class="indent">
<tr>
<td class="narrow nowrap">&bull; Signed Word</td>
<td>
32-bit, two's complement integer.
</td>
</tr>
<tr>
<td class="nowrap">&bull; Unsigned Word</td>
<td>
32-bit, unsigned integer.
</td>
</tr>
<tr>
<td class="nowrap">&bull; Float</td>
<td>
32-bit floating short in IEEE-754 format.
</td>
</tr>
<tr>
<td class="nowrap">&bull; Boolean</td>
<td>
1-bit value representing whether a condition is true or false.
</td>
</tr>
</table>
<p>
Signed word and unsigned word are represented as sequences of character
digits. A numeric token that begins with the characters <code>0x</code>
will be interpreted as hexadecimal, allowing letters <code>A</code> through
<code>F</code> to be used as digits as well. If the given value can be
represented in the signed word data type, the token is a signed word.
Otherwise, if it can be represented in the unsigned word data type, the token
is an unsigned word. If the value cannot be represented in either data type,
a parsing error occurs.
</p>
<p>
Floats, like words, are represented as sequences of character digits. They
are distinguished from words by the presence of a dot <code>.</code>
character somewhere within the sequence. Only one dot may be present in a
float literal, and dots cannot be used in hexadecimal literals. If the given
value cannot be represented in the float data type, a parsing error occurs.
Float values in the expression evaluator are subjected to the following
restrictions: if the value of any float literal or the result of any float
operation is NaN, an infinity, a denormal number or negative zero, it will be
changed to positive zero.
</p>
<p>
Booleans are mechanically identical to signed words, but the semantics are
slightly different. When used in a numeric operation, a boolean will use the
value <code>0</code> if false or the value <code>1</code> if true. Boolean
literals are specified with the named values <code>true</code> and
<code>false</code> within the expression. In a boolean operation, any value
that is zero is considered false, and any non-zero value is considered true.
</p>
<p>
Operators exist for converting between data types (see Operators below). A
Go To expression will only be valid if its final data type is signed word or
unsigned word.
</p>
<p>
The following symbols may be used for accessing information about the current
instruction being executed:
</p>
<table class="indent">
<tr>
<td class="mono narrow">address</td>
<td>Current memory access's address.</td>
</tr>
<tr>
<td class="mono">break</td>
<td>
Identifies what type of break scenario is being considered. See below for
a list of identifiers.
</td>
</tr>
<tr>
<td class="mono">code</td>
<td>Current exception's exception code.</td>
</tr>
<tr>
<td class="mono">cond</td>
<td>
Condition code for <code>Bcond</code> and <code>SETF</code>
instructions. See below for a list of conditions.
</td>
</tr>
<tr>
<td class="mono">disp</td>
<td>Displacement offset for jumps and memory accesses.</td>
</tr>
<!--tr>
<td class="mono">fetch</td>
<td>
Data unit index during a fetch operation, or <code>-1</code> if the read
operation is not a fetch.
</td>
</tr-->
<tr>
<td class="mono">format</td>
<td>Current instruction's encoding format.</td>
</tr>
<tr>
<td class="mono">id</td>
<td>
Identifies which specific instruction is being executed, considering
<code>opcode</code> and, where applicable, <code>subopcode</code>. See
below for a list of identifiers.
</td>
</tr>
<tr>
<td class="mono">imm</td>
<td>Immediate operand.</td>
</tr>
<tr>
<td class="mono">opcode</td>
<td>Current instruction's top-level opcode.</td>
</tr>
<tr>
<td class="mono">reg1</td>
<td>Source register index.</td>
</tr>
<tr>
<td class="mono">reg2</td>
<td>Destination register index.</td>
</tr>
<tr>
<td class="mono">regid</td>
<td>
System register index in <code>LDSR</code> and <code>STSR</code>
instructions.
</td>
</tr>
<tr>
<td class="mono">size</td>
<td>Number of bytes occupied by the current instruction.</td>
</tr>
<tr>
<td class="mono">subopcode</td>
<td>Current instruction's secondary opcode.</td>
</tr>
<tr>
<td class="mono">type</td>
<td>Data type of the current memory access.</td>
</tr>
<tr>
<td class="mono">value</td>
<td>Value read or to be written by the current memory access.</td>
</tr>
<tr>
<td class="mono">vector</td>
<td>Vector for <code>TRAP</code> instructions.</td>
</tr>
</table>
<p>
The following symbols may be used in conjunction with the <code>break</code>
symbol:
</p>
<div class="indent mono" style="column-width: 75px;">
<div>exception</div><div>execute</div><div>read</div><div>write</div>
</div>
<p>
The following symbols may be used in conjunction with the <code>cond</code>
symbol:
</p>
<div class="indent mono" style="column-width: 75px;">
<div>c </div><div>e </div><div>f </div><div>ge</div><div>gt</div>
<div>h </div><div>l </div><div>le</div><div>lt</div><div>n </div>
<div>nc</div><div>ne</div><div>nh</div><div>nl</div><div>nv</div>
<div>nz</div><div>p </div><div>t </div><div>v </div><div>z </div>
</div>
<p>
The following symbols may be used in conjunction with the <code>id</code>
symbol:
</p>
<div class="indent mono" style="column-width: 75px;">
<div>illegal</div><div>add_imm</div><div>add_reg</div><div>addf.s </div>
<div>addi </div><div>and </div><div>andbsu </div><div>andi </div>
<div>andnbsu</div><div>bcond </div><div>caxi </div><div>cli </div>
<div>cmp_imm</div><div>cmp_reg</div><div>cmpf.s </div><div>cvt.sw </div>
<div>cvt.ws </div><div>div </div><div>divf.s </div><div>divu </div>
<div>halt </div><div>in.b </div><div>in.h </div><div>in.w </div>
<div>jal </div><div>jmp </div><div>jr </div><div>ld.b </div>
<div>ld.h </div><div>ld.w </div><div>ldsr </div><div>mov_imm</div>
<div>mov_reg</div><div>movbsu </div><div>movea </div><div>movhi </div>
<div>mpyhw </div><div>mul </div><div>mulf.s </div><div>mulu </div>
<div>not </div><div>notbsu </div><div>or </div><div>orbsu </div>
<div>ori </div><div>ornbsu </div><div>out.b </div><div>out.h </div>
<div>out.w </div><div>reti </div><div>rev </div><div>sar_imm</div>
<div>sar_reg</div><div>sch0bsd</div><div>sch0bsu</div><div>sch1bsd</div>
<div>sch1bsu</div><div>sei </div><div>setf </div><div>shl_imm</div>
<div>shl_reg</div><div>shr_imm</div><div>shr_reg</div><div>st.b </div>
<div>st.h </div><div>st.w </div><div>stsr </div><div>sub </div>
<div>subf.s </div><div>trap </div><div>trnc.sw</div><div>xb </div>
<div>xh </div><div>xor </div><div>xorbsu </div><div>xori </div>
<div>xornbsu</div>
</div>
<p>
The following symbols may be used to retrieve the contents of a CPU program
register:
</p>
<div class="indent mono" style="column-width: 75px;">
<div>r0 </div><div>r1 </div><div>r2 </div><div>r3 </div><div>r4 </div>
<div>r5 </div><div>r6 </div><div>r7 </div><div>r8 </div><div>r9 </div>
<div>r10</div><div>r11</div><div>r12</div><div>r13</div><div>r14</div>
<div>r15</div><div>r16</div><div>r17</div><div>r18</div><div>r19</div>
<div>r20</div><div>r21</div><div>r22</div><div>r23</div><div>r24</div>
<div>r25</div><div>r26</div><div>r27</div><div>r28</div><div>r29</div>
<div>r30</div><div>r31</div><div>gp </div><div>hp </div><div>lp </div>
<div>sp </div><div>tp </div>
</div>
<p>
The following symbols may be used to retrieve the contents of a CPU system
register:
</p>
<div class="indent mono" style="column-width: 75px;">
<div>adtre</div><div>chcw </div><div>ecr </div><div>eipc</div>
<div>eipsw</div><div>fepc </div><div>fepsw</div><div>pc </div>
<div>pir </div><div>psw </div><div>tkcw </div><div>sr29</div>
<div>sr30 </div><div>sr31 </div>
</div>
<h2>Operators</h2>
<p>
Operators may apply to one (unary) or two (binary) values. All unary
operators appear to the left of the value they modify (or another unary
operator). Binary operators appear between the values they modify.
</p>
<p>
Each operator considers the types of its operands in order to produce a new
value of the appropriate type. If the operands of a binary operator have
different types, one of the values will be converted to the other type before
performing the operation. The conversion selects the "greater" of the two
types, in the following order (higher is "greater"):
</p>
<table class="indent">
<tr><td class="center narrow">&uarr;</td><td>Float</td></tr>
<tr><td class="center"></td><td>Unsigned word</td></tr>
<tr><td class="center"></td><td>Signed word</td></tr>
<tr><td class="center">&darr;</td><td>Boolean</td></tr>
</table>
<p>
For example, if an operation contains both a signed word and a float, the
signed word value is converted to float before performing the operation.
</p>
<p>
Operators have assigned precedence that specifies the order of operations.
For example, in the expression <code>1 + 2 * 3</code>, the multiplication
happens before the addition because it has higher precedence. Parentheses
<code>()</code> may be used to encapsulate operations and guarantee that they
are evaluated first regardless of the relative precedence of the adjacent
operators. For example, in the expression <code>(1 + 2) * 3</code>, the
addition happens before the multiplication because it is enclosed in
parentheses.
</p>
<p>
The following operators may be used in expressions. Groups listed higher have
higher precedence and happen before groups listed lower. Operators within
groups have the same precedence and are processed according to the order in
which they appear in the expression: right-to-left for unary operators,
left-to-right for binary operators.
</p>
<table class="indent bordered">
<tr>
<td rowSpan="18" class="middle">Unary</td>
<td class="mono open op1">~</td>
<td class="open">Not Bitwise</td>
<td class="open">Cannot be used with a float value.</td>
</tr>
<tr>
<td class="mono open">!</td>
<td class="open">Not Logical</td>
<td class="open">Always produces a boolean.</td>
</tr>
<tr>
<td class="mono open">-</td>
<td class="open">Negate</td>
<td class="open">Cannot be used with an unsigned word value.</td>
</tr>
<tr>
<td class="mono open">bool</td>
<td class="open">Cast to Boolean</td>
<td class="open"></td>
</tr>
<tr>
<td class="mono open">ceil</td>
<td class="open">Round Up</td>
<td class="open"></td>
</tr>
<tr>
<td class="mono open">float</td>
<td class="open">Cast to Float</td>
<td class="open"></td>
</tr>
<tr>
<td class="mono open">floor</td>
<td class="open">Round Down</td>
<td class="open"></td>
</tr>
<tr>
<td class="mono open">round</td>
<td class="open">Round to Nearest</td>
<td class="open"></td>
</tr>
<tr>
<td class="open"><code>s8</code>, <code>byte</code></td>
<td class="open">Cast to Signed Byte</td>
<td class="open">Result is of type signed word.</td>
</tr>
<tr>
<td class="open"><code>s16</code>, <code>halfword</code></td>
<td class="open">Cast to Signed Halfword</td>
<td class="open">Result is of type signed word.</td>
</tr>
<tr>
<td class="open"><code>s32</code>, <code>word</code></td>
<td class="open">Cast to Signed Word</td>
<td class="open"></td>
</tr>
<tr>
<td class="mono open">trunc</td>
<td class="open">Truncate</td>
<td class="open">Removes any fraction.</td>
</tr>
<tr>
<td class="open"><code><code>u8</code>, ubyte</code></td>
<td class="open">Cast to Unsigned Byte</td>
<td class="open">Result is of type unsigned word.</td>
</tr>
<tr>
<td class="open"><code>u16</code>, <code>uhalfword</code></td>
<td class="open">Cast to Unsigned Halfword</td>
<td class="open">Result is of type unsigned word.</td>
</tr>
<tr>
<td class="open"><code>u32</code>, <code>uword</code></td>
<td class="open">Cast to Unsigned Word</td>
<td class="open"></td>
</tr>
<tr>
<td class="mono open">xfloat</td>
<td class="open">Reinterpret as Float</td>
<td class="open">The binary value is not modified.</td>
</tr>
<tr>
<td class="open"><code>xs32</code>, <code>xword</code></td>
<td class="open">Reinterpret as Signed Word</td>
<td class="open">The binary value is not modified.</td>
</tr>
<tr>
<td><code>xu32</code>, <code>xuword</code></td>
<td>Reinterpret as Unsgned Word</td>
<td>The binary value is not modified.</td>
</tr>
<tr>
<td rowSpan="20" class="middle">Binary</td>
<td class="mono open op1">/</td>
<td class="open">Divide</td>
<td class="open">Zero divisor yields zero as result.</td>
</tr>
<tr>
<td class="mono open">*</td>
<td class="open">Multiply</td>
<td class="open"></td>
</tr>
<tr>
<td class="mono">%</td>
<td>Remainder</td>
<td>Zero divisor yields zero as result.</td>
</tr>
<tr>
<td class="mono open">+</td>
<td class="open">Add</td>
<td class="open"></td>
</tr>
<tr>
<td class="mono">-</td>
<td>Subtract</td>
<td></td>
</tr>
<tr>
<td class="mono open">&lt;&lt;</td>
<td class="open">Shift Left</td>
<td class="open">Cannot be used with float values.</td>
</tr>
<tr>
<td class="mono open">&gt;&gt;</td>
<td class="open">Shift Right Arithmetic</td>
<td class="open">Cannot be used with float values.</td>
</tr>
<tr>
<td class="mono">&gt;&gt;&gt;</td>
<td>Shift Right Logical</td>
<td>Cannot be used with float values.</td>
</tr>
<tr>
<td class="mono open">&gt;</td>
<td class="open">Greater</td>
<td class="open">Always produces a boolean.</td>
</tr>
<tr>
<td class="mono open">&gt;=</td>
<td class="open">Greater or Equal</td>
<td class="open">Always produces a boolean.</td>
</tr>
<tr>
<td class="mono open">&lt;</td>
<td class="open">Less</td>
<td class="open">Always produces a boolean.</td>
</tr>
<tr>
<td class="mono">&lt;=</td>
<td>Less or Equal</td>
<td>Always produces a boolean.</td>
</tr>
<tr>
<td class="mono open">==</td>
<td class="open">Equal</td>
<td class="open">Always produces a boolean.</td>
</tr>
<tr>
<td class="mono">!=</td>
<td>Not Equal</td>
<td>Always produces a boolean.</td>
</tr>
<tr>
<td class="mono">&amp;</td>
<td>And Bitwise</td>
<td>Cannot be used with float values.</td>
</tr>
<tr>
<td class="mono">^</td>
<td>Exclusive Or Bitwise</td>
<td>Cannot be used with float values.</td>
</tr>
<tr>
<td class="mono">|</td>
<td>Or Bitwise</td>
<td>Cannot be used with float values.</td>
</tr>
<tr>
<td class="mono">&amp;&amp;</td>
<td>And Logical</td>
<td>If left is true, returns right; else returns left.</td>
</tr>
<tr>
<td class="mono">^^</td>
<td>Exclusive Or Logical</td>
<td>If only one operand is true, returns the truthy value.</td>
</tr>
<tr>
<td class="mono">||</td>
<td>Or Logical</td>
<td>If left is true, returns left; else returns right.</td>
</tr>
</table>
<h2>Memory Read</h2>
<p>
A value can be read from the memory bus of the emulation state. This is done
by enclosing the part of the expression that represents the address in square
brackets <code>[]</code>. This functions in an identical manner to
parentheses <code>()</code>, but will additionally perform the read
operation.
</p>
<p>
Under most circumstances, a signed word read is performed. Certain operators
can be placed in front of the group to alter how the read is performed:
</p>
<table class="indent">
<tr>
<td class="narrow nowrap">&bull; <code>float</code></td>
<td>Behaves like <code>xfloat</code> instead.</td>
</tr>
<tr>
<td class="nowrap">&bull; <code>s8</code>, <code>u8</code></td>
<td>Performs a byte read of the specified signedness.</td>
</tr>
<tr>
<td class="nowrap">&bull; <code>s16</code>, <code>u16</code></td>
<td>Performs a halfword read of the specified signedness.</td>
</tr>
</table>
<p>
Reads are subjected to the alginment restrictions of the Virtual Boy's CPU:
the lowest bit of the address is ignored for halfword reads, and the lowest
two bits of the address are ignored for word reads.
</p>
<h1>&nbsp;</h1>
</body>
</html>

BIN
images/app_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

View File

@ -0,0 +1,588 @@
/*
* Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef CLASSFILE_CONSTANTS_H
#define CLASSFILE_CONSTANTS_H
#ifdef __cplusplus
extern "C" {
#endif
/* Classfile version number for this information */
#define JVM_CLASSFILE_MAJOR_VERSION 59
#define JVM_CLASSFILE_MINOR_VERSION 0
/* Flags */
enum {
JVM_ACC_PUBLIC = 0x0001,
JVM_ACC_PRIVATE = 0x0002,
JVM_ACC_PROTECTED = 0x0004,
JVM_ACC_STATIC = 0x0008,
JVM_ACC_FINAL = 0x0010,
JVM_ACC_SYNCHRONIZED = 0x0020,
JVM_ACC_SUPER = 0x0020,
JVM_ACC_VOLATILE = 0x0040,
JVM_ACC_BRIDGE = 0x0040,
JVM_ACC_TRANSIENT = 0x0080,
JVM_ACC_VARARGS = 0x0080,
JVM_ACC_NATIVE = 0x0100,
JVM_ACC_INTERFACE = 0x0200,
JVM_ACC_ABSTRACT = 0x0400,
JVM_ACC_STRICT = 0x0800,
JVM_ACC_SYNTHETIC = 0x1000,
JVM_ACC_ANNOTATION = 0x2000,
JVM_ACC_ENUM = 0x4000,
JVM_ACC_MODULE = 0x8000
};
#define JVM_ACC_PUBLIC_BIT 0
#define JVM_ACC_PRIVATE_BIT 1
#define JVM_ACC_PROTECTED_BIT 2
#define JVM_ACC_STATIC_BIT 3
#define JVM_ACC_FINAL_BIT 4
#define JVM_ACC_SYNCHRONIZED_BIT 5
#define JVM_ACC_SUPER_BIT 5
#define JVM_ACC_VOLATILE_BIT 6
#define JVM_ACC_BRIDGE_BIT 6
#define JVM_ACC_TRANSIENT_BIT 7
#define JVM_ACC_VARARGS_BIT 7
#define JVM_ACC_NATIVE_BIT 8
#define JVM_ACC_INTERFACE_BIT 9
#define JVM_ACC_ABSTRACT_BIT 10
#define JVM_ACC_STRICT_BIT 11
#define JVM_ACC_SYNTHETIC_BIT 12
#define JVM_ACC_ANNOTATION_BIT 13
#define JVM_ACC_ENUM_BIT 14
/* Used in newarray instruction. */
enum {
JVM_T_BOOLEAN = 4,
JVM_T_CHAR = 5,
JVM_T_FLOAT = 6,
JVM_T_DOUBLE = 7,
JVM_T_BYTE = 8,
JVM_T_SHORT = 9,
JVM_T_INT = 10,
JVM_T_LONG = 11
};
/* Constant Pool Entries */
enum {
JVM_CONSTANT_Utf8 = 1,
JVM_CONSTANT_Unicode = 2, /* unused */
JVM_CONSTANT_Integer = 3,
JVM_CONSTANT_Float = 4,
JVM_CONSTANT_Long = 5,
JVM_CONSTANT_Double = 6,
JVM_CONSTANT_Class = 7,
JVM_CONSTANT_String = 8,
JVM_CONSTANT_Fieldref = 9,
JVM_CONSTANT_Methodref = 10,
JVM_CONSTANT_InterfaceMethodref = 11,
JVM_CONSTANT_NameAndType = 12,
JVM_CONSTANT_MethodHandle = 15, // JSR 292
JVM_CONSTANT_MethodType = 16, // JSR 292
JVM_CONSTANT_Dynamic = 17,
JVM_CONSTANT_InvokeDynamic = 18,
JVM_CONSTANT_Module = 19,
JVM_CONSTANT_Package = 20,
JVM_CONSTANT_ExternalMax = 20
};
/* JVM_CONSTANT_MethodHandle subtypes */
enum {
JVM_REF_getField = 1,
JVM_REF_getStatic = 2,
JVM_REF_putField = 3,
JVM_REF_putStatic = 4,
JVM_REF_invokeVirtual = 5,
JVM_REF_invokeStatic = 6,
JVM_REF_invokeSpecial = 7,
JVM_REF_newInvokeSpecial = 8,
JVM_REF_invokeInterface = 9
};
/* StackMapTable type item numbers */
enum {
JVM_ITEM_Top = 0,
JVM_ITEM_Integer = 1,
JVM_ITEM_Float = 2,
JVM_ITEM_Double = 3,
JVM_ITEM_Long = 4,
JVM_ITEM_Null = 5,
JVM_ITEM_UninitializedThis = 6,
JVM_ITEM_Object = 7,
JVM_ITEM_Uninitialized = 8
};
/* Type signatures */
enum {
JVM_SIGNATURE_SLASH = '/',
JVM_SIGNATURE_DOT = '.',
JVM_SIGNATURE_SPECIAL = '<',
JVM_SIGNATURE_ENDSPECIAL = '>',
JVM_SIGNATURE_ARRAY = '[',
JVM_SIGNATURE_BYTE = 'B',
JVM_SIGNATURE_CHAR = 'C',
JVM_SIGNATURE_CLASS = 'L',
JVM_SIGNATURE_ENDCLASS = ';',
JVM_SIGNATURE_ENUM = 'E',
JVM_SIGNATURE_FLOAT = 'F',
JVM_SIGNATURE_DOUBLE = 'D',
JVM_SIGNATURE_FUNC = '(',
JVM_SIGNATURE_ENDFUNC = ')',
JVM_SIGNATURE_INT = 'I',
JVM_SIGNATURE_LONG = 'J',
JVM_SIGNATURE_SHORT = 'S',
JVM_SIGNATURE_VOID = 'V',
JVM_SIGNATURE_BOOLEAN = 'Z'
};
/* Opcodes */
enum {
JVM_OPC_nop = 0,
JVM_OPC_aconst_null = 1,
JVM_OPC_iconst_m1 = 2,
JVM_OPC_iconst_0 = 3,
JVM_OPC_iconst_1 = 4,
JVM_OPC_iconst_2 = 5,
JVM_OPC_iconst_3 = 6,
JVM_OPC_iconst_4 = 7,
JVM_OPC_iconst_5 = 8,
JVM_OPC_lconst_0 = 9,
JVM_OPC_lconst_1 = 10,
JVM_OPC_fconst_0 = 11,
JVM_OPC_fconst_1 = 12,
JVM_OPC_fconst_2 = 13,
JVM_OPC_dconst_0 = 14,
JVM_OPC_dconst_1 = 15,
JVM_OPC_bipush = 16,
JVM_OPC_sipush = 17,
JVM_OPC_ldc = 18,
JVM_OPC_ldc_w = 19,
JVM_OPC_ldc2_w = 20,
JVM_OPC_iload = 21,
JVM_OPC_lload = 22,
JVM_OPC_fload = 23,
JVM_OPC_dload = 24,
JVM_OPC_aload = 25,
JVM_OPC_iload_0 = 26,
JVM_OPC_iload_1 = 27,
JVM_OPC_iload_2 = 28,
JVM_OPC_iload_3 = 29,
JVM_OPC_lload_0 = 30,
JVM_OPC_lload_1 = 31,
JVM_OPC_lload_2 = 32,
JVM_OPC_lload_3 = 33,
JVM_OPC_fload_0 = 34,
JVM_OPC_fload_1 = 35,
JVM_OPC_fload_2 = 36,
JVM_OPC_fload_3 = 37,
JVM_OPC_dload_0 = 38,
JVM_OPC_dload_1 = 39,
JVM_OPC_dload_2 = 40,
JVM_OPC_dload_3 = 41,
JVM_OPC_aload_0 = 42,
JVM_OPC_aload_1 = 43,
JVM_OPC_aload_2 = 44,
JVM_OPC_aload_3 = 45,
JVM_OPC_iaload = 46,
JVM_OPC_laload = 47,
JVM_OPC_faload = 48,
JVM_OPC_daload = 49,
JVM_OPC_aaload = 50,
JVM_OPC_baload = 51,
JVM_OPC_caload = 52,
JVM_OPC_saload = 53,
JVM_OPC_istore = 54,
JVM_OPC_lstore = 55,
JVM_OPC_fstore = 56,
JVM_OPC_dstore = 57,
JVM_OPC_astore = 58,
JVM_OPC_istore_0 = 59,
JVM_OPC_istore_1 = 60,
JVM_OPC_istore_2 = 61,
JVM_OPC_istore_3 = 62,
JVM_OPC_lstore_0 = 63,
JVM_OPC_lstore_1 = 64,
JVM_OPC_lstore_2 = 65,
JVM_OPC_lstore_3 = 66,
JVM_OPC_fstore_0 = 67,
JVM_OPC_fstore_1 = 68,
JVM_OPC_fstore_2 = 69,
JVM_OPC_fstore_3 = 70,
JVM_OPC_dstore_0 = 71,
JVM_OPC_dstore_1 = 72,
JVM_OPC_dstore_2 = 73,
JVM_OPC_dstore_3 = 74,
JVM_OPC_astore_0 = 75,
JVM_OPC_astore_1 = 76,
JVM_OPC_astore_2 = 77,
JVM_OPC_astore_3 = 78,
JVM_OPC_iastore = 79,
JVM_OPC_lastore = 80,
JVM_OPC_fastore = 81,
JVM_OPC_dastore = 82,
JVM_OPC_aastore = 83,
JVM_OPC_bastore = 84,
JVM_OPC_castore = 85,
JVM_OPC_sastore = 86,
JVM_OPC_pop = 87,
JVM_OPC_pop2 = 88,
JVM_OPC_dup = 89,
JVM_OPC_dup_x1 = 90,
JVM_OPC_dup_x2 = 91,
JVM_OPC_dup2 = 92,
JVM_OPC_dup2_x1 = 93,
JVM_OPC_dup2_x2 = 94,
JVM_OPC_swap = 95,
JVM_OPC_iadd = 96,
JVM_OPC_ladd = 97,
JVM_OPC_fadd = 98,
JVM_OPC_dadd = 99,
JVM_OPC_isub = 100,
JVM_OPC_lsub = 101,
JVM_OPC_fsub = 102,
JVM_OPC_dsub = 103,
JVM_OPC_imul = 104,
JVM_OPC_lmul = 105,
JVM_OPC_fmul = 106,
JVM_OPC_dmul = 107,
JVM_OPC_idiv = 108,
JVM_OPC_ldiv = 109,
JVM_OPC_fdiv = 110,
JVM_OPC_ddiv = 111,
JVM_OPC_irem = 112,
JVM_OPC_lrem = 113,
JVM_OPC_frem = 114,
JVM_OPC_drem = 115,
JVM_OPC_ineg = 116,
JVM_OPC_lneg = 117,
JVM_OPC_fneg = 118,
JVM_OPC_dneg = 119,
JVM_OPC_ishl = 120,
JVM_OPC_lshl = 121,
JVM_OPC_ishr = 122,
JVM_OPC_lshr = 123,
JVM_OPC_iushr = 124,
JVM_OPC_lushr = 125,
JVM_OPC_iand = 126,
JVM_OPC_land = 127,
JVM_OPC_ior = 128,
JVM_OPC_lor = 129,
JVM_OPC_ixor = 130,
JVM_OPC_lxor = 131,
JVM_OPC_iinc = 132,
JVM_OPC_i2l = 133,
JVM_OPC_i2f = 134,
JVM_OPC_i2d = 135,
JVM_OPC_l2i = 136,
JVM_OPC_l2f = 137,
JVM_OPC_l2d = 138,
JVM_OPC_f2i = 139,
JVM_OPC_f2l = 140,
JVM_OPC_f2d = 141,
JVM_OPC_d2i = 142,
JVM_OPC_d2l = 143,
JVM_OPC_d2f = 144,
JVM_OPC_i2b = 145,
JVM_OPC_i2c = 146,
JVM_OPC_i2s = 147,
JVM_OPC_lcmp = 148,
JVM_OPC_fcmpl = 149,
JVM_OPC_fcmpg = 150,
JVM_OPC_dcmpl = 151,
JVM_OPC_dcmpg = 152,
JVM_OPC_ifeq = 153,
JVM_OPC_ifne = 154,
JVM_OPC_iflt = 155,
JVM_OPC_ifge = 156,
JVM_OPC_ifgt = 157,
JVM_OPC_ifle = 158,
JVM_OPC_if_icmpeq = 159,
JVM_OPC_if_icmpne = 160,
JVM_OPC_if_icmplt = 161,
JVM_OPC_if_icmpge = 162,
JVM_OPC_if_icmpgt = 163,
JVM_OPC_if_icmple = 164,
JVM_OPC_if_acmpeq = 165,
JVM_OPC_if_acmpne = 166,
JVM_OPC_goto = 167,
JVM_OPC_jsr = 168,
JVM_OPC_ret = 169,
JVM_OPC_tableswitch = 170,
JVM_OPC_lookupswitch = 171,
JVM_OPC_ireturn = 172,
JVM_OPC_lreturn = 173,
JVM_OPC_freturn = 174,
JVM_OPC_dreturn = 175,
JVM_OPC_areturn = 176,
JVM_OPC_return = 177,
JVM_OPC_getstatic = 178,
JVM_OPC_putstatic = 179,
JVM_OPC_getfield = 180,
JVM_OPC_putfield = 181,
JVM_OPC_invokevirtual = 182,
JVM_OPC_invokespecial = 183,
JVM_OPC_invokestatic = 184,
JVM_OPC_invokeinterface = 185,
JVM_OPC_invokedynamic = 186,
JVM_OPC_new = 187,
JVM_OPC_newarray = 188,
JVM_OPC_anewarray = 189,
JVM_OPC_arraylength = 190,
JVM_OPC_athrow = 191,
JVM_OPC_checkcast = 192,
JVM_OPC_instanceof = 193,
JVM_OPC_monitorenter = 194,
JVM_OPC_monitorexit = 195,
JVM_OPC_wide = 196,
JVM_OPC_multianewarray = 197,
JVM_OPC_ifnull = 198,
JVM_OPC_ifnonnull = 199,
JVM_OPC_goto_w = 200,
JVM_OPC_jsr_w = 201,
JVM_OPC_MAX = 201
};
/* Opcode length initializer, use with something like:
* unsigned char opcode_length[JVM_OPC_MAX+1] = JVM_OPCODE_LENGTH_INITIALIZER;
*/
#define JVM_OPCODE_LENGTH_INITIALIZER { \
1, /* nop */ \
1, /* aconst_null */ \
1, /* iconst_m1 */ \
1, /* iconst_0 */ \
1, /* iconst_1 */ \
1, /* iconst_2 */ \
1, /* iconst_3 */ \
1, /* iconst_4 */ \
1, /* iconst_5 */ \
1, /* lconst_0 */ \
1, /* lconst_1 */ \
1, /* fconst_0 */ \
1, /* fconst_1 */ \
1, /* fconst_2 */ \
1, /* dconst_0 */ \
1, /* dconst_1 */ \
2, /* bipush */ \
3, /* sipush */ \
2, /* ldc */ \
3, /* ldc_w */ \
3, /* ldc2_w */ \
2, /* iload */ \
2, /* lload */ \
2, /* fload */ \
2, /* dload */ \
2, /* aload */ \
1, /* iload_0 */ \
1, /* iload_1 */ \
1, /* iload_2 */ \
1, /* iload_3 */ \
1, /* lload_0 */ \
1, /* lload_1 */ \
1, /* lload_2 */ \
1, /* lload_3 */ \
1, /* fload_0 */ \
1, /* fload_1 */ \
1, /* fload_2 */ \
1, /* fload_3 */ \
1, /* dload_0 */ \
1, /* dload_1 */ \
1, /* dload_2 */ \
1, /* dload_3 */ \
1, /* aload_0 */ \
1, /* aload_1 */ \
1, /* aload_2 */ \
1, /* aload_3 */ \
1, /* iaload */ \
1, /* laload */ \
1, /* faload */ \
1, /* daload */ \
1, /* aaload */ \
1, /* baload */ \
1, /* caload */ \
1, /* saload */ \
2, /* istore */ \
2, /* lstore */ \
2, /* fstore */ \
2, /* dstore */ \
2, /* astore */ \
1, /* istore_0 */ \
1, /* istore_1 */ \
1, /* istore_2 */ \
1, /* istore_3 */ \
1, /* lstore_0 */ \
1, /* lstore_1 */ \
1, /* lstore_2 */ \
1, /* lstore_3 */ \
1, /* fstore_0 */ \
1, /* fstore_1 */ \
1, /* fstore_2 */ \
1, /* fstore_3 */ \
1, /* dstore_0 */ \
1, /* dstore_1 */ \
1, /* dstore_2 */ \
1, /* dstore_3 */ \
1, /* astore_0 */ \
1, /* astore_1 */ \
1, /* astore_2 */ \
1, /* astore_3 */ \
1, /* iastore */ \
1, /* lastore */ \
1, /* fastore */ \
1, /* dastore */ \
1, /* aastore */ \
1, /* bastore */ \
1, /* castore */ \
1, /* sastore */ \
1, /* pop */ \
1, /* pop2 */ \
1, /* dup */ \
1, /* dup_x1 */ \
1, /* dup_x2 */ \
1, /* dup2 */ \
1, /* dup2_x1 */ \
1, /* dup2_x2 */ \
1, /* swap */ \
1, /* iadd */ \
1, /* ladd */ \
1, /* fadd */ \
1, /* dadd */ \
1, /* isub */ \
1, /* lsub */ \
1, /* fsub */ \
1, /* dsub */ \
1, /* imul */ \
1, /* lmul */ \
1, /* fmul */ \
1, /* dmul */ \
1, /* idiv */ \
1, /* ldiv */ \
1, /* fdiv */ \
1, /* ddiv */ \
1, /* irem */ \
1, /* lrem */ \
1, /* frem */ \
1, /* drem */ \
1, /* ineg */ \
1, /* lneg */ \
1, /* fneg */ \
1, /* dneg */ \
1, /* ishl */ \
1, /* lshl */ \
1, /* ishr */ \
1, /* lshr */ \
1, /* iushr */ \
1, /* lushr */ \
1, /* iand */ \
1, /* land */ \
1, /* ior */ \
1, /* lor */ \
1, /* ixor */ \
1, /* lxor */ \
3, /* iinc */ \
1, /* i2l */ \
1, /* i2f */ \
1, /* i2d */ \
1, /* l2i */ \
1, /* l2f */ \
1, /* l2d */ \
1, /* f2i */ \
1, /* f2l */ \
1, /* f2d */ \
1, /* d2i */ \
1, /* d2l */ \
1, /* d2f */ \
1, /* i2b */ \
1, /* i2c */ \
1, /* i2s */ \
1, /* lcmp */ \
1, /* fcmpl */ \
1, /* fcmpg */ \
1, /* dcmpl */ \
1, /* dcmpg */ \
3, /* ifeq */ \
3, /* ifne */ \
3, /* iflt */ \
3, /* ifge */ \
3, /* ifgt */ \
3, /* ifle */ \
3, /* if_icmpeq */ \
3, /* if_icmpne */ \
3, /* if_icmplt */ \
3, /* if_icmpge */ \
3, /* if_icmpgt */ \
3, /* if_icmple */ \
3, /* if_acmpeq */ \
3, /* if_acmpne */ \
3, /* goto */ \
3, /* jsr */ \
2, /* ret */ \
99, /* tableswitch */ \
99, /* lookupswitch */ \
1, /* ireturn */ \
1, /* lreturn */ \
1, /* freturn */ \
1, /* dreturn */ \
1, /* areturn */ \
1, /* return */ \
3, /* getstatic */ \
3, /* putstatic */ \
3, /* getfield */ \
3, /* putfield */ \
3, /* invokevirtual */ \
3, /* invokespecial */ \
3, /* invokestatic */ \
5, /* invokeinterface */ \
5, /* invokedynamic */ \
3, /* new */ \
2, /* newarray */ \
3, /* anewarray */ \
1, /* arraylength */ \
1, /* athrow */ \
3, /* checkcast */ \
3, /* instanceof */ \
1, /* monitorenter */ \
1, /* monitorexit */ \
0, /* wide */ \
4, /* multianewarray */ \
3, /* ifnull */ \
3, /* ifnonnull */ \
5, /* goto_w */ \
5 /* jsr_w */ \
}
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* CLASSFILE_CONSTANTS */

356
jni/linux/jawt.h Normal file
View File

@ -0,0 +1,356 @@
/*
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef _JAVASOFT_JAWT_H_
#define _JAVASOFT_JAWT_H_
#include "jni.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* AWT native interface.
*
* The AWT native interface allows a native C or C++ application a means
* by which to access native structures in AWT. This is to facilitate moving
* legacy C and C++ applications to Java and to target the needs of the
* developers who need to do their own native rendering to canvases
* for performance or other reasons.
*
* Conversely it also provides mechanisms for an application which already
* has a native window to provide that to AWT for AWT rendering.
*
* Since every platform may be different in its native data structures
* and APIs for windowing systems the application must necessarily
* provided per-platform source and compile and deliver per-platform
* native code to use this API.
*
* These interfaces are not part of the Java SE specification and
* a VM is not required to implement this API. However it is strongly
* recommended that all implementations which support headful AWT
* also support these interfaces.
*
*/
/*
* AWT Native Drawing Surface (JAWT_DrawingSurface).
*
* For each platform, there is a native drawing surface structure. This
* platform-specific structure can be found in jawt_md.h. It is recommended
* that additional platforms follow the same model. It is also recommended
* that VMs on all platforms support the existing structures in jawt_md.h.
*
*******************
* EXAMPLE OF USAGE:
*******************
*
* In Win32, a programmer wishes to access the HWND of a canvas to perform
* native rendering into it. The programmer has declared the paint() method
* for their canvas subclass to be native:
*
*
* MyCanvas.java:
*
* import java.awt.*;
*
* public class MyCanvas extends Canvas {
*
* static {
* System.loadLibrary("mylib");
* }
*
* public native void paint(Graphics g);
* }
*
*
* myfile.c:
*
* #include "jawt_md.h"
* #include <assert.h>
*
* JNIEXPORT void JNICALL
* Java_MyCanvas_paint(JNIEnv* env, jobject canvas, jobject graphics)
* {
* JAWT awt;
* JAWT_DrawingSurface* ds;
* JAWT_DrawingSurfaceInfo* dsi;
* JAWT_Win32DrawingSurfaceInfo* dsi_win;
* jboolean result;
* jint lock;
*
* // Get the AWT. Request version 9 to access features in that release.
* awt.version = JAWT_VERSION_9;
* result = JAWT_GetAWT(env, &awt);
* assert(result != JNI_FALSE);
*
* // Get the drawing surface
* ds = awt.GetDrawingSurface(env, canvas);
* assert(ds != NULL);
*
* // Lock the drawing surface
* lock = ds->Lock(ds);
* assert((lock & JAWT_LOCK_ERROR) == 0);
*
* // Get the drawing surface info
* dsi = ds->GetDrawingSurfaceInfo(ds);
*
* // Get the platform-specific drawing info
* dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo;
*
* //////////////////////////////
* // !!! DO PAINTING HERE !!! //
* //////////////////////////////
*
* // Free the drawing surface info
* ds->FreeDrawingSurfaceInfo(dsi);
*
* // Unlock the drawing surface
* ds->Unlock(ds);
*
* // Free the drawing surface
* awt.FreeDrawingSurface(ds);
* }
*
*/
/*
* JAWT_Rectangle
* Structure for a native rectangle.
*/
typedef struct jawt_Rectangle {
jint x;
jint y;
jint width;
jint height;
} JAWT_Rectangle;
struct jawt_DrawingSurface;
/*
* JAWT_DrawingSurfaceInfo
* Structure for containing the underlying drawing information of a component.
*/
typedef struct jawt_DrawingSurfaceInfo {
/*
* Pointer to the platform-specific information. This can be safely
* cast to a JAWT_Win32DrawingSurfaceInfo on Windows or a
* JAWT_X11DrawingSurfaceInfo on Linux and Solaris. On Mac OS X this is a
* pointer to a NSObject that conforms to the JAWT_SurfaceLayers
* protocol. See jawt_md.h for details.
*/
void* platformInfo;
/* Cached pointer to the underlying drawing surface */
struct jawt_DrawingSurface* ds;
/* Bounding rectangle of the drawing surface */
JAWT_Rectangle bounds;
/* Number of rectangles in the clip */
jint clipSize;
/* Clip rectangle array */
JAWT_Rectangle* clip;
} JAWT_DrawingSurfaceInfo;
#define JAWT_LOCK_ERROR 0x00000001
#define JAWT_LOCK_CLIP_CHANGED 0x00000002
#define JAWT_LOCK_BOUNDS_CHANGED 0x00000004
#define JAWT_LOCK_SURFACE_CHANGED 0x00000008
/*
* JAWT_DrawingSurface
* Structure for containing the underlying drawing information of a component.
* All operations on a JAWT_DrawingSurface MUST be performed from the same
* thread as the call to GetDrawingSurface.
*/
typedef struct jawt_DrawingSurface {
/*
* Cached reference to the Java environment of the calling thread.
* If Lock(), Unlock(), GetDrawingSurfaceInfo() or
* FreeDrawingSurfaceInfo() are called from a different thread,
* this data member should be set before calling those functions.
*/
JNIEnv* env;
/* Cached reference to the target object */
jobject target;
/*
* Lock the surface of the target component for native rendering.
* When finished drawing, the surface must be unlocked with
* Unlock(). This function returns a bitmask with one or more of the
* following values:
*
* JAWT_LOCK_ERROR - When an error has occurred and the surface could not
* be locked.
*
* JAWT_LOCK_CLIP_CHANGED - When the clip region has changed.
*
* JAWT_LOCK_BOUNDS_CHANGED - When the bounds of the surface have changed.
*
* JAWT_LOCK_SURFACE_CHANGED - When the surface itself has changed
*/
jint (JNICALL *Lock)
(struct jawt_DrawingSurface* ds);
/*
* Get the drawing surface info.
* The value returned may be cached, but the values may change if
* additional calls to Lock() or Unlock() are made.
* Lock() must be called before this can return a valid value.
* Returns NULL if an error has occurred.
* When finished with the returned value, FreeDrawingSurfaceInfo must be
* called.
*/
JAWT_DrawingSurfaceInfo* (JNICALL *GetDrawingSurfaceInfo)
(struct jawt_DrawingSurface* ds);
/*
* Free the drawing surface info.
*/
void (JNICALL *FreeDrawingSurfaceInfo)
(JAWT_DrawingSurfaceInfo* dsi);
/*
* Unlock the drawing surface of the target component for native rendering.
*/
void (JNICALL *Unlock)
(struct jawt_DrawingSurface* ds);
} JAWT_DrawingSurface;
/*
* JAWT
* Structure for containing native AWT functions.
*/
typedef struct jawt {
/*
* Version of this structure. This must always be set before
* calling JAWT_GetAWT(). It affects the functions returned.
* Must be one of the known pre-defined versions.
*/
jint version;
/*
* Return a drawing surface from a target jobject. This value
* may be cached.
* Returns NULL if an error has occurred.
* Target must be a java.awt.Component (should be a Canvas
* or Window for native rendering).
* FreeDrawingSurface() must be called when finished with the
* returned JAWT_DrawingSurface.
*/
JAWT_DrawingSurface* (JNICALL *GetDrawingSurface)
(JNIEnv* env, jobject target);
/*
* Free the drawing surface allocated in GetDrawingSurface.
*/
void (JNICALL *FreeDrawingSurface)
(JAWT_DrawingSurface* ds);
/*
* Since 1.4
* Locks the entire AWT for synchronization purposes
*/
void (JNICALL *Lock)(JNIEnv* env);
/*
* Since 1.4
* Unlocks the entire AWT for synchronization purposes
*/
void (JNICALL *Unlock)(JNIEnv* env);
/*
* Since 1.4
* Returns a reference to a java.awt.Component from a native
* platform handle. On Windows, this corresponds to an HWND;
* on Solaris and Linux, this is a Drawable. For other platforms,
* see the appropriate machine-dependent header file for a description.
* The reference returned by this function is a local
* reference that is only valid in this environment.
* This function returns a NULL reference if no component could be
* found with matching platform information.
*/
jobject (JNICALL *GetComponent)(JNIEnv* env, void* platformInfo);
/**
* Since 9
* Creates a java.awt.Frame placed in a native container. Container is
* referenced by the native platform handle. For example on Windows this
* corresponds to an HWND. For other platforms, see the appropriate
* machine-dependent header file for a description. The reference returned
* by this function is a local reference that is only valid in this
* environment. This function returns a NULL reference if no frame could be
* created with matching platform information.
*/
jobject (JNICALL *CreateEmbeddedFrame) (JNIEnv *env, void* platformInfo);
/**
* Since 9
* Moves and resizes the embedded frame. The new location of the top-left
* corner is specified by x and y parameters relative to the native parent
* component. The new size is specified by width and height.
*
* The embedded frame should be created by CreateEmbeddedFrame() method, or
* this function will not have any effect.
*
* java.awt.Component.setLocation() and java.awt.Component.setBounds() for
* EmbeddedFrame really don't move it within the native parent. These
* methods always locate the embedded frame at (0, 0) for backward
* compatibility. To allow moving embedded frames this method was
* introduced, and it works just the same way as setLocation() and
* setBounds() for usual, non-embedded components.
*
* Using usual get/setLocation() and get/setBounds() together with this new
* method is not recommended.
*/
void (JNICALL *SetBounds) (JNIEnv *env, jobject embeddedFrame,
jint x, jint y, jint w, jint h);
/**
* Since 9
* Synthesize a native message to activate or deactivate an EmbeddedFrame
* window depending on the value of parameter doActivate, if "true"
* activates the window; otherwise, deactivates the window.
*
* The embedded frame should be created by CreateEmbeddedFrame() method, or
* this function will not have any effect.
*/
void (JNICALL *SynthesizeWindowActivation) (JNIEnv *env,
jobject embeddedFrame, jboolean doActivate);
} JAWT;
/*
* Get the AWT native structure. This function returns JNI_FALSE if
* an error occurs.
*/
_JNI_IMPORT_OR_EXPORT_
jboolean JNICALL JAWT_GetAWT(JNIEnv* env, JAWT* awt);
/*
* Specify one of these constants as the JAWT.version
* Specifying an earlier version will limit the available functions to
* those provided in that earlier version of JAWT.
* See the "Since" note on each API. Methods with no "Since"
* may be presumed to be present in JAWT_VERSION_1_3.
*/
#define JAWT_VERSION_1_3 0x00010003
#define JAWT_VERSION_1_4 0x00010004
#define JAWT_VERSION_1_7 0x00010007
#define JAWT_VERSION_9 0x00090000
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* !_JAVASOFT_JAWT_H_ */

276
jni/linux/jdwpTransport.h Normal file
View File

@ -0,0 +1,276 @@
/*
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* Java Debug Wire Protocol Transport Service Provider Interface.
*/
#ifndef JDWPTRANSPORT_H
#define JDWPTRANSPORT_H
#include "jni.h"
enum {
JDWPTRANSPORT_VERSION_1_0 = 0x00010000,
JDWPTRANSPORT_VERSION_1_1 = 0x00010001
};
#ifdef __cplusplus
extern "C" {
#endif
struct jdwpTransportNativeInterface_;
struct _jdwpTransportEnv;
#ifdef __cplusplus
typedef _jdwpTransportEnv jdwpTransportEnv;
#else
typedef const struct jdwpTransportNativeInterface_ *jdwpTransportEnv;
#endif /* __cplusplus */
/*
* Errors. Universal errors with JVMTI/JVMDI equivalents keep the
* values the same.
*/
typedef enum {
JDWPTRANSPORT_ERROR_NONE = 0,
JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT = 103,
JDWPTRANSPORT_ERROR_OUT_OF_MEMORY = 110,
JDWPTRANSPORT_ERROR_INTERNAL = 113,
JDWPTRANSPORT_ERROR_ILLEGAL_STATE = 201,
JDWPTRANSPORT_ERROR_IO_ERROR = 202,
JDWPTRANSPORT_ERROR_TIMEOUT = 203,
JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE = 204
} jdwpTransportError;
/*
* Structure to define capabilities
*/
typedef struct {
unsigned int can_timeout_attach :1;
unsigned int can_timeout_accept :1;
unsigned int can_timeout_handshake :1;
unsigned int reserved3 :1;
unsigned int reserved4 :1;
unsigned int reserved5 :1;
unsigned int reserved6 :1;
unsigned int reserved7 :1;
unsigned int reserved8 :1;
unsigned int reserved9 :1;
unsigned int reserved10 :1;
unsigned int reserved11 :1;
unsigned int reserved12 :1;
unsigned int reserved13 :1;
unsigned int reserved14 :1;
unsigned int reserved15 :1;
} JDWPTransportCapabilities;
/*
* Structures to define packet layout.
*
* See: http://java.sun.com/j2se/1.5/docs/guide/jpda/jdwp-spec.html
*/
#define JDWP_HEADER_SIZE 11
enum {
/*
* If additional flags are added that apply to jdwpCmdPacket,
* then debugLoop.c: reader() will need to be updated to
* accept more than JDWPTRANSPORT_FLAGS_NONE.
*/
JDWPTRANSPORT_FLAGS_NONE = 0x0,
JDWPTRANSPORT_FLAGS_REPLY = 0x80
};
typedef struct {
jint len;
jint id;
jbyte flags;
jbyte cmdSet;
jbyte cmd;
jbyte *data;
} jdwpCmdPacket;
typedef struct {
jint len;
jint id;
jbyte flags;
jshort errorCode;
jbyte *data;
} jdwpReplyPacket;
typedef struct {
union {
jdwpCmdPacket cmd;
jdwpReplyPacket reply;
} type;
} jdwpPacket;
/*
* JDWP functions called by the transport.
*/
typedef struct jdwpTransportCallback {
void *(*alloc)(jint numBytes); /* Call this for all allocations */
void (*free)(void *buffer); /* Call this for all deallocations */
} jdwpTransportCallback;
typedef jint (JNICALL *jdwpTransport_OnLoad_t)(JavaVM *jvm,
jdwpTransportCallback *callback,
jint version,
jdwpTransportEnv** env);
/*
* JDWP transport configuration from the agent.
*/
typedef struct jdwpTransportConfiguration {
/* Field added in JDWPTRANSPORT_VERSION_1_1: */
const char* allowed_peers; /* Peers allowed for connection */
} jdwpTransportConfiguration;
/* Function Interface */
struct jdwpTransportNativeInterface_ {
/* 1 : RESERVED */
void *reserved1;
/* 2 : Get Capabilities */
jdwpTransportError (JNICALL *GetCapabilities)(jdwpTransportEnv* env,
JDWPTransportCapabilities *capabilities_ptr);
/* 3 : Attach */
jdwpTransportError (JNICALL *Attach)(jdwpTransportEnv* env,
const char* address,
jlong attach_timeout,
jlong handshake_timeout);
/* 4: StartListening */
jdwpTransportError (JNICALL *StartListening)(jdwpTransportEnv* env,
const char* address,
char** actual_address);
/* 5: StopListening */
jdwpTransportError (JNICALL *StopListening)(jdwpTransportEnv* env);
/* 6: Accept */
jdwpTransportError (JNICALL *Accept)(jdwpTransportEnv* env,
jlong accept_timeout,
jlong handshake_timeout);
/* 7: IsOpen */
jboolean (JNICALL *IsOpen)(jdwpTransportEnv* env);
/* 8: Close */
jdwpTransportError (JNICALL *Close)(jdwpTransportEnv* env);
/* 9: ReadPacket */
jdwpTransportError (JNICALL *ReadPacket)(jdwpTransportEnv* env,
jdwpPacket *pkt);
/* 10: Write Packet */
jdwpTransportError (JNICALL *WritePacket)(jdwpTransportEnv* env,
const jdwpPacket* pkt);
/* 11: GetLastError */
jdwpTransportError (JNICALL *GetLastError)(jdwpTransportEnv* env,
char** error);
/* 12: SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
jdwpTransportError (JNICALL *SetTransportConfiguration)(jdwpTransportEnv* env,
jdwpTransportConfiguration *config);
};
/*
* Use inlined functions so that C++ code can use syntax such as
* env->Attach("mymachine:5000", 10*1000, 0);
*
* rather than using C's :-
*
* (*env)->Attach(env, "mymachine:5000", 10*1000, 0);
*/
struct _jdwpTransportEnv {
const struct jdwpTransportNativeInterface_ *functions;
#ifdef __cplusplus
jdwpTransportError GetCapabilities(JDWPTransportCapabilities *capabilities_ptr) {
return functions->GetCapabilities(this, capabilities_ptr);
}
jdwpTransportError Attach(const char* address, jlong attach_timeout,
jlong handshake_timeout) {
return functions->Attach(this, address, attach_timeout, handshake_timeout);
}
jdwpTransportError StartListening(const char* address,
char** actual_address) {
return functions->StartListening(this, address, actual_address);
}
jdwpTransportError StopListening(void) {
return functions->StopListening(this);
}
jdwpTransportError Accept(jlong accept_timeout, jlong handshake_timeout) {
return functions->Accept(this, accept_timeout, handshake_timeout);
}
jboolean IsOpen(void) {
return functions->IsOpen(this);
}
jdwpTransportError Close(void) {
return functions->Close(this);
}
jdwpTransportError ReadPacket(jdwpPacket *pkt) {
return functions->ReadPacket(this, pkt);
}
jdwpTransportError WritePacket(const jdwpPacket* pkt) {
return functions->WritePacket(this, pkt);
}
jdwpTransportError GetLastError(char** error) {
return functions->GetLastError(this, error);
}
/* SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
jdwpTransportError SetTransportConfiguration(jdwpTransportEnv* env,
return functions->SetTransportConfiguration(this, config);
}
#endif /* __cplusplus */
};
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* JDWPTRANSPORT_H */

1987
jni/linux/jni.h Normal file

File diff suppressed because it is too large Load Diff

2625
jni/linux/jvmti.h Normal file

File diff suppressed because it is too large Load Diff

115
jni/linux/jvmticmlr.h Normal file
View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This header file defines the data structures sent by the VM
* through the JVMTI CompiledMethodLoad callback function via the
* "void * compile_info" parameter. The memory pointed to by the
* compile_info parameter may not be referenced after returning from
* the CompiledMethodLoad callback. These are VM implementation
* specific data structures that may evolve in future releases. A
* JVMTI agent should interpret a non-NULL compile_info as a pointer
* to a region of memory containing a list of records. In a typical
* usage scenario, a JVMTI agent would cast each record to a
* jvmtiCompiledMethodLoadRecordHeader, a struct that represents
* arbitrary information. This struct contains a kind field to indicate
* the kind of information being passed, and a pointer to the next
* record. If the kind field indicates inlining information, then the
* agent would cast the record to a jvmtiCompiledMethodLoadInlineRecord.
* This record contains an array of PCStackInfo structs, which indicate
* for every pc address what are the methods on the invocation stack.
* The "methods" and "bcis" fields in each PCStackInfo struct specify a
* 1-1 mapping between these inlined methods and their bytecode indices.
* This can be used to derive the proper source lines of the inlined
* methods.
*/
#ifndef _JVMTI_CMLR_H_
#define _JVMTI_CMLR_H_
enum {
JVMTI_CMLR_MAJOR_VERSION_1 = 0x00000001,
JVMTI_CMLR_MINOR_VERSION_0 = 0x00000000,
JVMTI_CMLR_MAJOR_VERSION = 0x00000001,
JVMTI_CMLR_MINOR_VERSION = 0x00000000
/*
* This comment is for the "JDK import from HotSpot" sanity check:
* version: 1.0.0
*/
};
typedef enum {
JVMTI_CMLR_DUMMY = 1,
JVMTI_CMLR_INLINE_INFO = 2
} jvmtiCMLRKind;
/*
* Record that represents arbitrary information passed through JVMTI
* CompiledMethodLoadEvent void pointer.
*/
typedef struct _jvmtiCompiledMethodLoadRecordHeader {
jvmtiCMLRKind kind; /* id for the kind of info passed in the record */
jint majorinfoversion; /* major and minor info version values. Init'ed */
jint minorinfoversion; /* to current version value in jvmtiExport.cpp. */
struct _jvmtiCompiledMethodLoadRecordHeader* next;
} jvmtiCompiledMethodLoadRecordHeader;
/*
* Record that gives information about the methods on the compile-time
* stack at a specific pc address of a compiled method. Each element in
* the methods array maps to same element in the bcis array.
*/
typedef struct _PCStackInfo {
void* pc; /* the pc address for this compiled method */
jint numstackframes; /* number of methods on the stack */
jmethodID* methods; /* array of numstackframes method ids */
jint* bcis; /* array of numstackframes bytecode indices */
} PCStackInfo;
/*
* Record that contains inlining information for each pc address of
* an nmethod.
*/
typedef struct _jvmtiCompiledMethodLoadInlineRecord {
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
jint numpcs; /* number of pc descriptors in this nmethod */
PCStackInfo* pcinfo; /* array of numpcs pc descriptors */
} jvmtiCompiledMethodLoadInlineRecord;
/*
* Dummy record used to test that we can pass records with different
* information through the void pointer provided that they can be cast
* to a jvmtiCompiledMethodLoadRecordHeader.
*/
typedef struct _jvmtiCompiledMethodLoadDummyRecord {
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
char message[50];
} jvmtiCompiledMethodLoadDummyRecord;
#endif

60
jni/linux/linux/jawt_md.h Normal file
View File

@ -0,0 +1,60 @@
/*
* Copyright (c) 1999, 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef _JAVASOFT_JAWT_MD_H_
#define _JAVASOFT_JAWT_MD_H_
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "jawt.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* X11-specific declarations for AWT native interface.
* See notes in jawt.h for an example of use.
*/
typedef struct jawt_X11DrawingSurfaceInfo {
Drawable drawable;
Display* display;
VisualID visualID;
Colormap colormapID;
int depth;
/*
* Since 1.4
* Returns a pixel value from a set of RGB values.
* This is useful for paletted color (256 color) modes.
*/
int (JNICALL *GetAWTColor)(JAWT_DrawingSurface* ds,
int r, int g, int b);
} JAWT_X11DrawingSurfaceInfo;
#ifdef __cplusplus
}
#endif
#endif /* !_JAVASOFT_JAWT_MD_H_ */

66
jni/linux/linux/jni_md.h Normal file
View File

@ -0,0 +1,66 @@
/*
* Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
#ifndef _JAVASOFT_JNI_MD_H_
#define _JAVASOFT_JNI_MD_H_
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
#ifndef JNIEXPORT
#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility)
#ifdef ARM
#define JNIEXPORT __attribute__((externally_visible,visibility("default")))
#else
#define JNIEXPORT __attribute__((visibility("default")))
#endif
#else
#define JNIEXPORT
#endif
#endif
#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility)
#ifdef ARM
#define JNIIMPORT __attribute__((externally_visible,visibility("default")))
#else
#define JNIIMPORT __attribute__((visibility("default")))
#endif
#else
#define JNIIMPORT
#endif
#define JNICALL
typedef int jint;
#ifdef _LP64
typedef long jlong;
#else
typedef long long jlong;
#endif
typedef signed char jbyte;
#endif /* !_JAVASOFT_JNI_MD_H_ */

View File

@ -0,0 +1,588 @@
/*
* Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
#ifndef CLASSFILE_CONSTANTS_H
#define CLASSFILE_CONSTANTS_H
#ifdef __cplusplus
extern "C" {
#endif
/* Classfile version number for this information */
#define JVM_CLASSFILE_MAJOR_VERSION 58
#define JVM_CLASSFILE_MINOR_VERSION 0
/* Flags */
enum {
JVM_ACC_PUBLIC = 0x0001,
JVM_ACC_PRIVATE = 0x0002,
JVM_ACC_PROTECTED = 0x0004,
JVM_ACC_STATIC = 0x0008,
JVM_ACC_FINAL = 0x0010,
JVM_ACC_SYNCHRONIZED = 0x0020,
JVM_ACC_SUPER = 0x0020,
JVM_ACC_VOLATILE = 0x0040,
JVM_ACC_BRIDGE = 0x0040,
JVM_ACC_TRANSIENT = 0x0080,
JVM_ACC_VARARGS = 0x0080,
JVM_ACC_NATIVE = 0x0100,
JVM_ACC_INTERFACE = 0x0200,
JVM_ACC_ABSTRACT = 0x0400,
JVM_ACC_STRICT = 0x0800,
JVM_ACC_SYNTHETIC = 0x1000,
JVM_ACC_ANNOTATION = 0x2000,
JVM_ACC_ENUM = 0x4000,
JVM_ACC_MODULE = 0x8000
};
#define JVM_ACC_PUBLIC_BIT 0
#define JVM_ACC_PRIVATE_BIT 1
#define JVM_ACC_PROTECTED_BIT 2
#define JVM_ACC_STATIC_BIT 3
#define JVM_ACC_FINAL_BIT 4
#define JVM_ACC_SYNCHRONIZED_BIT 5
#define JVM_ACC_SUPER_BIT 5
#define JVM_ACC_VOLATILE_BIT 6
#define JVM_ACC_BRIDGE_BIT 6
#define JVM_ACC_TRANSIENT_BIT 7
#define JVM_ACC_VARARGS_BIT 7
#define JVM_ACC_NATIVE_BIT 8
#define JVM_ACC_INTERFACE_BIT 9
#define JVM_ACC_ABSTRACT_BIT 10
#define JVM_ACC_STRICT_BIT 11
#define JVM_ACC_SYNTHETIC_BIT 12
#define JVM_ACC_ANNOTATION_BIT 13
#define JVM_ACC_ENUM_BIT 14
/* Used in newarray instruction. */
enum {
JVM_T_BOOLEAN = 4,
JVM_T_CHAR = 5,
JVM_T_FLOAT = 6,
JVM_T_DOUBLE = 7,
JVM_T_BYTE = 8,
JVM_T_SHORT = 9,
JVM_T_INT = 10,
JVM_T_LONG = 11
};
/* Constant Pool Entries */
enum {
JVM_CONSTANT_Utf8 = 1,
JVM_CONSTANT_Unicode = 2, /* unused */
JVM_CONSTANT_Integer = 3,
JVM_CONSTANT_Float = 4,
JVM_CONSTANT_Long = 5,
JVM_CONSTANT_Double = 6,
JVM_CONSTANT_Class = 7,
JVM_CONSTANT_String = 8,
JVM_CONSTANT_Fieldref = 9,
JVM_CONSTANT_Methodref = 10,
JVM_CONSTANT_InterfaceMethodref = 11,
JVM_CONSTANT_NameAndType = 12,
JVM_CONSTANT_MethodHandle = 15, // JSR 292
JVM_CONSTANT_MethodType = 16, // JSR 292
JVM_CONSTANT_Dynamic = 17,
JVM_CONSTANT_InvokeDynamic = 18,
JVM_CONSTANT_Module = 19,
JVM_CONSTANT_Package = 20,
JVM_CONSTANT_ExternalMax = 20
};
/* JVM_CONSTANT_MethodHandle subtypes */
enum {
JVM_REF_getField = 1,
JVM_REF_getStatic = 2,
JVM_REF_putField = 3,
JVM_REF_putStatic = 4,
JVM_REF_invokeVirtual = 5,
JVM_REF_invokeStatic = 6,
JVM_REF_invokeSpecial = 7,
JVM_REF_newInvokeSpecial = 8,
JVM_REF_invokeInterface = 9
};
/* StackMapTable type item numbers */
enum {
JVM_ITEM_Top = 0,
JVM_ITEM_Integer = 1,
JVM_ITEM_Float = 2,
JVM_ITEM_Double = 3,
JVM_ITEM_Long = 4,
JVM_ITEM_Null = 5,
JVM_ITEM_UninitializedThis = 6,
JVM_ITEM_Object = 7,
JVM_ITEM_Uninitialized = 8
};
/* Type signatures */
enum {
JVM_SIGNATURE_SLASH = '/',
JVM_SIGNATURE_DOT = '.',
JVM_SIGNATURE_SPECIAL = '<',
JVM_SIGNATURE_ENDSPECIAL = '>',
JVM_SIGNATURE_ARRAY = '[',
JVM_SIGNATURE_BYTE = 'B',
JVM_SIGNATURE_CHAR = 'C',
JVM_SIGNATURE_CLASS = 'L',
JVM_SIGNATURE_ENDCLASS = ';',
JVM_SIGNATURE_ENUM = 'E',
JVM_SIGNATURE_FLOAT = 'F',
JVM_SIGNATURE_DOUBLE = 'D',
JVM_SIGNATURE_FUNC = '(',
JVM_SIGNATURE_ENDFUNC = ')',
JVM_SIGNATURE_INT = 'I',
JVM_SIGNATURE_LONG = 'J',
JVM_SIGNATURE_SHORT = 'S',
JVM_SIGNATURE_VOID = 'V',
JVM_SIGNATURE_BOOLEAN = 'Z'
};
/* Opcodes */
enum {
JVM_OPC_nop = 0,
JVM_OPC_aconst_null = 1,
JVM_OPC_iconst_m1 = 2,
JVM_OPC_iconst_0 = 3,
JVM_OPC_iconst_1 = 4,
JVM_OPC_iconst_2 = 5,
JVM_OPC_iconst_3 = 6,
JVM_OPC_iconst_4 = 7,
JVM_OPC_iconst_5 = 8,
JVM_OPC_lconst_0 = 9,
JVM_OPC_lconst_1 = 10,
JVM_OPC_fconst_0 = 11,
JVM_OPC_fconst_1 = 12,
JVM_OPC_fconst_2 = 13,
JVM_OPC_dconst_0 = 14,
JVM_OPC_dconst_1 = 15,
JVM_OPC_bipush = 16,
JVM_OPC_sipush = 17,
JVM_OPC_ldc = 18,
JVM_OPC_ldc_w = 19,
JVM_OPC_ldc2_w = 20,
JVM_OPC_iload = 21,
JVM_OPC_lload = 22,
JVM_OPC_fload = 23,
JVM_OPC_dload = 24,
JVM_OPC_aload = 25,
JVM_OPC_iload_0 = 26,
JVM_OPC_iload_1 = 27,
JVM_OPC_iload_2 = 28,
JVM_OPC_iload_3 = 29,
JVM_OPC_lload_0 = 30,
JVM_OPC_lload_1 = 31,
JVM_OPC_lload_2 = 32,
JVM_OPC_lload_3 = 33,
JVM_OPC_fload_0 = 34,
JVM_OPC_fload_1 = 35,
JVM_OPC_fload_2 = 36,
JVM_OPC_fload_3 = 37,
JVM_OPC_dload_0 = 38,
JVM_OPC_dload_1 = 39,
JVM_OPC_dload_2 = 40,
JVM_OPC_dload_3 = 41,
JVM_OPC_aload_0 = 42,
JVM_OPC_aload_1 = 43,
JVM_OPC_aload_2 = 44,
JVM_OPC_aload_3 = 45,
JVM_OPC_iaload = 46,
JVM_OPC_laload = 47,
JVM_OPC_faload = 48,
JVM_OPC_daload = 49,
JVM_OPC_aaload = 50,
JVM_OPC_baload = 51,
JVM_OPC_caload = 52,
JVM_OPC_saload = 53,
JVM_OPC_istore = 54,
JVM_OPC_lstore = 55,
JVM_OPC_fstore = 56,
JVM_OPC_dstore = 57,
JVM_OPC_astore = 58,
JVM_OPC_istore_0 = 59,
JVM_OPC_istore_1 = 60,
JVM_OPC_istore_2 = 61,
JVM_OPC_istore_3 = 62,
JVM_OPC_lstore_0 = 63,
JVM_OPC_lstore_1 = 64,
JVM_OPC_lstore_2 = 65,
JVM_OPC_lstore_3 = 66,
JVM_OPC_fstore_0 = 67,
JVM_OPC_fstore_1 = 68,
JVM_OPC_fstore_2 = 69,
JVM_OPC_fstore_3 = 70,
JVM_OPC_dstore_0 = 71,
JVM_OPC_dstore_1 = 72,
JVM_OPC_dstore_2 = 73,
JVM_OPC_dstore_3 = 74,
JVM_OPC_astore_0 = 75,
JVM_OPC_astore_1 = 76,
JVM_OPC_astore_2 = 77,
JVM_OPC_astore_3 = 78,
JVM_OPC_iastore = 79,
JVM_OPC_lastore = 80,
JVM_OPC_fastore = 81,
JVM_OPC_dastore = 82,
JVM_OPC_aastore = 83,
JVM_OPC_bastore = 84,
JVM_OPC_castore = 85,
JVM_OPC_sastore = 86,
JVM_OPC_pop = 87,
JVM_OPC_pop2 = 88,
JVM_OPC_dup = 89,
JVM_OPC_dup_x1 = 90,
JVM_OPC_dup_x2 = 91,
JVM_OPC_dup2 = 92,
JVM_OPC_dup2_x1 = 93,
JVM_OPC_dup2_x2 = 94,
JVM_OPC_swap = 95,
JVM_OPC_iadd = 96,
JVM_OPC_ladd = 97,
JVM_OPC_fadd = 98,
JVM_OPC_dadd = 99,
JVM_OPC_isub = 100,
JVM_OPC_lsub = 101,
JVM_OPC_fsub = 102,
JVM_OPC_dsub = 103,
JVM_OPC_imul = 104,
JVM_OPC_lmul = 105,
JVM_OPC_fmul = 106,
JVM_OPC_dmul = 107,
JVM_OPC_idiv = 108,
JVM_OPC_ldiv = 109,
JVM_OPC_fdiv = 110,
JVM_OPC_ddiv = 111,
JVM_OPC_irem = 112,
JVM_OPC_lrem = 113,
JVM_OPC_frem = 114,
JVM_OPC_drem = 115,
JVM_OPC_ineg = 116,
JVM_OPC_lneg = 117,
JVM_OPC_fneg = 118,
JVM_OPC_dneg = 119,
JVM_OPC_ishl = 120,
JVM_OPC_lshl = 121,
JVM_OPC_ishr = 122,
JVM_OPC_lshr = 123,
JVM_OPC_iushr = 124,
JVM_OPC_lushr = 125,
JVM_OPC_iand = 126,
JVM_OPC_land = 127,
JVM_OPC_ior = 128,
JVM_OPC_lor = 129,
JVM_OPC_ixor = 130,
JVM_OPC_lxor = 131,
JVM_OPC_iinc = 132,
JVM_OPC_i2l = 133,
JVM_OPC_i2f = 134,
JVM_OPC_i2d = 135,
JVM_OPC_l2i = 136,
JVM_OPC_l2f = 137,
JVM_OPC_l2d = 138,
JVM_OPC_f2i = 139,
JVM_OPC_f2l = 140,
JVM_OPC_f2d = 141,
JVM_OPC_d2i = 142,
JVM_OPC_d2l = 143,
JVM_OPC_d2f = 144,
JVM_OPC_i2b = 145,
JVM_OPC_i2c = 146,
JVM_OPC_i2s = 147,
JVM_OPC_lcmp = 148,
JVM_OPC_fcmpl = 149,
JVM_OPC_fcmpg = 150,
JVM_OPC_dcmpl = 151,
JVM_OPC_dcmpg = 152,
JVM_OPC_ifeq = 153,
JVM_OPC_ifne = 154,
JVM_OPC_iflt = 155,
JVM_OPC_ifge = 156,
JVM_OPC_ifgt = 157,
JVM_OPC_ifle = 158,
JVM_OPC_if_icmpeq = 159,
JVM_OPC_if_icmpne = 160,
JVM_OPC_if_icmplt = 161,
JVM_OPC_if_icmpge = 162,
JVM_OPC_if_icmpgt = 163,
JVM_OPC_if_icmple = 164,
JVM_OPC_if_acmpeq = 165,
JVM_OPC_if_acmpne = 166,
JVM_OPC_goto = 167,
JVM_OPC_jsr = 168,
JVM_OPC_ret = 169,
JVM_OPC_tableswitch = 170,
JVM_OPC_lookupswitch = 171,
JVM_OPC_ireturn = 172,
JVM_OPC_lreturn = 173,
JVM_OPC_freturn = 174,
JVM_OPC_dreturn = 175,
JVM_OPC_areturn = 176,
JVM_OPC_return = 177,
JVM_OPC_getstatic = 178,
JVM_OPC_putstatic = 179,
JVM_OPC_getfield = 180,
JVM_OPC_putfield = 181,
JVM_OPC_invokevirtual = 182,
JVM_OPC_invokespecial = 183,
JVM_OPC_invokestatic = 184,
JVM_OPC_invokeinterface = 185,
JVM_OPC_invokedynamic = 186,
JVM_OPC_new = 187,
JVM_OPC_newarray = 188,
JVM_OPC_anewarray = 189,
JVM_OPC_arraylength = 190,
JVM_OPC_athrow = 191,
JVM_OPC_checkcast = 192,
JVM_OPC_instanceof = 193,
JVM_OPC_monitorenter = 194,
JVM_OPC_monitorexit = 195,
JVM_OPC_wide = 196,
JVM_OPC_multianewarray = 197,
JVM_OPC_ifnull = 198,
JVM_OPC_ifnonnull = 199,
JVM_OPC_goto_w = 200,
JVM_OPC_jsr_w = 201,
JVM_OPC_MAX = 201
};
/* Opcode length initializer, use with something like:
* unsigned char opcode_length[JVM_OPC_MAX+1] = JVM_OPCODE_LENGTH_INITIALIZER;
*/
#define JVM_OPCODE_LENGTH_INITIALIZER { \
1, /* nop */ \
1, /* aconst_null */ \
1, /* iconst_m1 */ \
1, /* iconst_0 */ \
1, /* iconst_1 */ \
1, /* iconst_2 */ \
1, /* iconst_3 */ \
1, /* iconst_4 */ \
1, /* iconst_5 */ \
1, /* lconst_0 */ \
1, /* lconst_1 */ \
1, /* fconst_0 */ \
1, /* fconst_1 */ \
1, /* fconst_2 */ \
1, /* dconst_0 */ \
1, /* dconst_1 */ \
2, /* bipush */ \
3, /* sipush */ \
2, /* ldc */ \
3, /* ldc_w */ \
3, /* ldc2_w */ \
2, /* iload */ \
2, /* lload */ \
2, /* fload */ \
2, /* dload */ \
2, /* aload */ \
1, /* iload_0 */ \
1, /* iload_1 */ \
1, /* iload_2 */ \
1, /* iload_3 */ \
1, /* lload_0 */ \
1, /* lload_1 */ \
1, /* lload_2 */ \
1, /* lload_3 */ \
1, /* fload_0 */ \
1, /* fload_1 */ \
1, /* fload_2 */ \
1, /* fload_3 */ \
1, /* dload_0 */ \
1, /* dload_1 */ \
1, /* dload_2 */ \
1, /* dload_3 */ \
1, /* aload_0 */ \
1, /* aload_1 */ \
1, /* aload_2 */ \
1, /* aload_3 */ \
1, /* iaload */ \
1, /* laload */ \
1, /* faload */ \
1, /* daload */ \
1, /* aaload */ \
1, /* baload */ \
1, /* caload */ \
1, /* saload */ \
2, /* istore */ \
2, /* lstore */ \
2, /* fstore */ \
2, /* dstore */ \
2, /* astore */ \
1, /* istore_0 */ \
1, /* istore_1 */ \
1, /* istore_2 */ \
1, /* istore_3 */ \
1, /* lstore_0 */ \
1, /* lstore_1 */ \
1, /* lstore_2 */ \
1, /* lstore_3 */ \
1, /* fstore_0 */ \
1, /* fstore_1 */ \
1, /* fstore_2 */ \
1, /* fstore_3 */ \
1, /* dstore_0 */ \
1, /* dstore_1 */ \
1, /* dstore_2 */ \
1, /* dstore_3 */ \
1, /* astore_0 */ \
1, /* astore_1 */ \
1, /* astore_2 */ \
1, /* astore_3 */ \
1, /* iastore */ \
1, /* lastore */ \
1, /* fastore */ \
1, /* dastore */ \
1, /* aastore */ \
1, /* bastore */ \
1, /* castore */ \
1, /* sastore */ \
1, /* pop */ \
1, /* pop2 */ \
1, /* dup */ \
1, /* dup_x1 */ \
1, /* dup_x2 */ \
1, /* dup2 */ \
1, /* dup2_x1 */ \
1, /* dup2_x2 */ \
1, /* swap */ \
1, /* iadd */ \
1, /* ladd */ \
1, /* fadd */ \
1, /* dadd */ \
1, /* isub */ \
1, /* lsub */ \
1, /* fsub */ \
1, /* dsub */ \
1, /* imul */ \
1, /* lmul */ \
1, /* fmul */ \
1, /* dmul */ \
1, /* idiv */ \
1, /* ldiv */ \
1, /* fdiv */ \
1, /* ddiv */ \
1, /* irem */ \
1, /* lrem */ \
1, /* frem */ \
1, /* drem */ \
1, /* ineg */ \
1, /* lneg */ \
1, /* fneg */ \
1, /* dneg */ \
1, /* ishl */ \
1, /* lshl */ \
1, /* ishr */ \
1, /* lshr */ \
1, /* iushr */ \
1, /* lushr */ \
1, /* iand */ \
1, /* land */ \
1, /* ior */ \
1, /* lor */ \
1, /* ixor */ \
1, /* lxor */ \
3, /* iinc */ \
1, /* i2l */ \
1, /* i2f */ \
1, /* i2d */ \
1, /* l2i */ \
1, /* l2f */ \
1, /* l2d */ \
1, /* f2i */ \
1, /* f2l */ \
1, /* f2d */ \
1, /* d2i */ \
1, /* d2l */ \
1, /* d2f */ \
1, /* i2b */ \
1, /* i2c */ \
1, /* i2s */ \
1, /* lcmp */ \
1, /* fcmpl */ \
1, /* fcmpg */ \
1, /* dcmpl */ \
1, /* dcmpg */ \
3, /* ifeq */ \
3, /* ifne */ \
3, /* iflt */ \
3, /* ifge */ \
3, /* ifgt */ \
3, /* ifle */ \
3, /* if_icmpeq */ \
3, /* if_icmpne */ \
3, /* if_icmplt */ \
3, /* if_icmpge */ \
3, /* if_icmpgt */ \
3, /* if_icmple */ \
3, /* if_acmpeq */ \
3, /* if_acmpne */ \
3, /* goto */ \
3, /* jsr */ \
2, /* ret */ \
99, /* tableswitch */ \
99, /* lookupswitch */ \
1, /* ireturn */ \
1, /* lreturn */ \
1, /* freturn */ \
1, /* dreturn */ \
1, /* areturn */ \
1, /* return */ \
3, /* getstatic */ \
3, /* putstatic */ \
3, /* getfield */ \
3, /* putfield */ \
3, /* invokevirtual */ \
3, /* invokespecial */ \
3, /* invokestatic */ \
5, /* invokeinterface */ \
5, /* invokedynamic */ \
3, /* new */ \
2, /* newarray */ \
3, /* anewarray */ \
1, /* arraylength */ \
1, /* athrow */ \
3, /* checkcast */ \
3, /* instanceof */ \
1, /* monitorenter */ \
1, /* monitorexit */ \
0, /* wide */ \
4, /* multianewarray */ \
3, /* ifnull */ \
3, /* ifnonnull */ \
5, /* goto_w */ \
5 /* jsr_w */ \
}
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* CLASSFILE_CONSTANTS */

356
jni/windows/jawt.h Normal file
View File

@ -0,0 +1,356 @@
/*
* Copyright (c) 1999, 2017, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
#ifndef _JAVASOFT_JAWT_H_
#define _JAVASOFT_JAWT_H_
#include "jni.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* AWT native interface.
*
* The AWT native interface allows a native C or C++ application a means
* by which to access native structures in AWT. This is to facilitate moving
* legacy C and C++ applications to Java and to target the needs of the
* developers who need to do their own native rendering to canvases
* for performance or other reasons.
*
* Conversely it also provides mechanisms for an application which already
* has a native window to provide that to AWT for AWT rendering.
*
* Since every platform may be different in its native data structures
* and APIs for windowing systems the application must necessarily
* provided per-platform source and compile and deliver per-platform
* native code to use this API.
*
* These interfaces are not part of the Java SE specification and
* a VM is not required to implement this API. However it is strongly
* recommended that all implementations which support headful AWT
* also support these interfaces.
*
*/
/*
* AWT Native Drawing Surface (JAWT_DrawingSurface).
*
* For each platform, there is a native drawing surface structure. This
* platform-specific structure can be found in jawt_md.h. It is recommended
* that additional platforms follow the same model. It is also recommended
* that VMs on all platforms support the existing structures in jawt_md.h.
*
*******************
* EXAMPLE OF USAGE:
*******************
*
* In Win32, a programmer wishes to access the HWND of a canvas to perform
* native rendering into it. The programmer has declared the paint() method
* for their canvas subclass to be native:
*
*
* MyCanvas.java:
*
* import java.awt.*;
*
* public class MyCanvas extends Canvas {
*
* static {
* System.loadLibrary("mylib");
* }
*
* public native void paint(Graphics g);
* }
*
*
* myfile.c:
*
* #include "jawt_md.h"
* #include <assert.h>
*
* JNIEXPORT void JNICALL
* Java_MyCanvas_paint(JNIEnv* env, jobject canvas, jobject graphics)
* {
* JAWT awt;
* JAWT_DrawingSurface* ds;
* JAWT_DrawingSurfaceInfo* dsi;
* JAWT_Win32DrawingSurfaceInfo* dsi_win;
* jboolean result;
* jint lock;
*
* // Get the AWT. Request version 9 to access features in that release.
* awt.version = JAWT_VERSION_9;
* result = JAWT_GetAWT(env, &awt);
* assert(result != JNI_FALSE);
*
* // Get the drawing surface
* ds = awt.GetDrawingSurface(env, canvas);
* assert(ds != NULL);
*
* // Lock the drawing surface
* lock = ds->Lock(ds);
* assert((lock & JAWT_LOCK_ERROR) == 0);
*
* // Get the drawing surface info
* dsi = ds->GetDrawingSurfaceInfo(ds);
*
* // Get the platform-specific drawing info
* dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo;
*
* //////////////////////////////
* // !!! DO PAINTING HERE !!! //
* //////////////////////////////
*
* // Free the drawing surface info
* ds->FreeDrawingSurfaceInfo(dsi);
*
* // Unlock the drawing surface
* ds->Unlock(ds);
*
* // Free the drawing surface
* awt.FreeDrawingSurface(ds);
* }
*
*/
/*
* JAWT_Rectangle
* Structure for a native rectangle.
*/
typedef struct jawt_Rectangle {
jint x;
jint y;
jint width;
jint height;
} JAWT_Rectangle;
struct jawt_DrawingSurface;
/*
* JAWT_DrawingSurfaceInfo
* Structure for containing the underlying drawing information of a component.
*/
typedef struct jawt_DrawingSurfaceInfo {
/*
* Pointer to the platform-specific information. This can be safely
* cast to a JAWT_Win32DrawingSurfaceInfo on Windows or a
* JAWT_X11DrawingSurfaceInfo on Linux and Solaris. On Mac OS X this is a
* pointer to a NSObject that conforms to the JAWT_SurfaceLayers
* protocol. See jawt_md.h for details.
*/
void* platformInfo;
/* Cached pointer to the underlying drawing surface */
struct jawt_DrawingSurface* ds;
/* Bounding rectangle of the drawing surface */
JAWT_Rectangle bounds;
/* Number of rectangles in the clip */
jint clipSize;
/* Clip rectangle array */
JAWT_Rectangle* clip;
} JAWT_DrawingSurfaceInfo;
#define JAWT_LOCK_ERROR 0x00000001
#define JAWT_LOCK_CLIP_CHANGED 0x00000002
#define JAWT_LOCK_BOUNDS_CHANGED 0x00000004
#define JAWT_LOCK_SURFACE_CHANGED 0x00000008
/*
* JAWT_DrawingSurface
* Structure for containing the underlying drawing information of a component.
* All operations on a JAWT_DrawingSurface MUST be performed from the same
* thread as the call to GetDrawingSurface.
*/
typedef struct jawt_DrawingSurface {
/*
* Cached reference to the Java environment of the calling thread.
* If Lock(), Unlock(), GetDrawingSurfaceInfo() or
* FreeDrawingSurfaceInfo() are called from a different thread,
* this data member should be set before calling those functions.
*/
JNIEnv* env;
/* Cached reference to the target object */
jobject target;
/*
* Lock the surface of the target component for native rendering.
* When finished drawing, the surface must be unlocked with
* Unlock(). This function returns a bitmask with one or more of the
* following values:
*
* JAWT_LOCK_ERROR - When an error has occurred and the surface could not
* be locked.
*
* JAWT_LOCK_CLIP_CHANGED - When the clip region has changed.
*
* JAWT_LOCK_BOUNDS_CHANGED - When the bounds of the surface have changed.
*
* JAWT_LOCK_SURFACE_CHANGED - When the surface itself has changed
*/
jint (JNICALL *Lock)
(struct jawt_DrawingSurface* ds);
/*
* Get the drawing surface info.
* The value returned may be cached, but the values may change if
* additional calls to Lock() or Unlock() are made.
* Lock() must be called before this can return a valid value.
* Returns NULL if an error has occurred.
* When finished with the returned value, FreeDrawingSurfaceInfo must be
* called.
*/
JAWT_DrawingSurfaceInfo* (JNICALL *GetDrawingSurfaceInfo)
(struct jawt_DrawingSurface* ds);
/*
* Free the drawing surface info.
*/
void (JNICALL *FreeDrawingSurfaceInfo)
(JAWT_DrawingSurfaceInfo* dsi);
/*
* Unlock the drawing surface of the target component for native rendering.
*/
void (JNICALL *Unlock)
(struct jawt_DrawingSurface* ds);
} JAWT_DrawingSurface;
/*
* JAWT
* Structure for containing native AWT functions.
*/
typedef struct jawt {
/*
* Version of this structure. This must always be set before
* calling JAWT_GetAWT(). It affects the functions returned.
* Must be one of the known pre-defined versions.
*/
jint version;
/*
* Return a drawing surface from a target jobject. This value
* may be cached.
* Returns NULL if an error has occurred.
* Target must be a java.awt.Component (should be a Canvas
* or Window for native rendering).
* FreeDrawingSurface() must be called when finished with the
* returned JAWT_DrawingSurface.
*/
JAWT_DrawingSurface* (JNICALL *GetDrawingSurface)
(JNIEnv* env, jobject target);
/*
* Free the drawing surface allocated in GetDrawingSurface.
*/
void (JNICALL *FreeDrawingSurface)
(JAWT_DrawingSurface* ds);
/*
* Since 1.4
* Locks the entire AWT for synchronization purposes
*/
void (JNICALL *Lock)(JNIEnv* env);
/*
* Since 1.4
* Unlocks the entire AWT for synchronization purposes
*/
void (JNICALL *Unlock)(JNIEnv* env);
/*
* Since 1.4
* Returns a reference to a java.awt.Component from a native
* platform handle. On Windows, this corresponds to an HWND;
* on Solaris and Linux, this is a Drawable. For other platforms,
* see the appropriate machine-dependent header file for a description.
* The reference returned by this function is a local
* reference that is only valid in this environment.
* This function returns a NULL reference if no component could be
* found with matching platform information.
*/
jobject (JNICALL *GetComponent)(JNIEnv* env, void* platformInfo);
/**
* Since 9
* Creates a java.awt.Frame placed in a native container. Container is
* referenced by the native platform handle. For example on Windows this
* corresponds to an HWND. For other platforms, see the appropriate
* machine-dependent header file for a description. The reference returned
* by this function is a local reference that is only valid in this
* environment. This function returns a NULL reference if no frame could be
* created with matching platform information.
*/
jobject (JNICALL *CreateEmbeddedFrame) (JNIEnv *env, void* platformInfo);
/**
* Since 9
* Moves and resizes the embedded frame. The new location of the top-left
* corner is specified by x and y parameters relative to the native parent
* component. The new size is specified by width and height.
*
* The embedded frame should be created by CreateEmbeddedFrame() method, or
* this function will not have any effect.
*
* java.awt.Component.setLocation() and java.awt.Component.setBounds() for
* EmbeddedFrame really don't move it within the native parent. These
* methods always locate the embedded frame at (0, 0) for backward
* compatibility. To allow moving embedded frames this method was
* introduced, and it works just the same way as setLocation() and
* setBounds() for usual, non-embedded components.
*
* Using usual get/setLocation() and get/setBounds() together with this new
* method is not recommended.
*/
void (JNICALL *SetBounds) (JNIEnv *env, jobject embeddedFrame,
jint x, jint y, jint w, jint h);
/**
* Since 9
* Synthesize a native message to activate or deactivate an EmbeddedFrame
* window depending on the value of parameter doActivate, if "true"
* activates the window; otherwise, deactivates the window.
*
* The embedded frame should be created by CreateEmbeddedFrame() method, or
* this function will not have any effect.
*/
void (JNICALL *SynthesizeWindowActivation) (JNIEnv *env,
jobject embeddedFrame, jboolean doActivate);
} JAWT;
/*
* Get the AWT native structure. This function returns JNI_FALSE if
* an error occurs.
*/
_JNI_IMPORT_OR_EXPORT_
jboolean JNICALL JAWT_GetAWT(JNIEnv* env, JAWT* awt);
/*
* Specify one of these constants as the JAWT.version
* Specifying an earlier version will limit the available functions to
* those provided in that earlier version of JAWT.
* See the "Since" note on each API. Methods with no "Since"
* may be presumed to be present in JAWT_VERSION_1_3.
*/
#define JAWT_VERSION_1_3 0x00010003
#define JAWT_VERSION_1_4 0x00010004
#define JAWT_VERSION_1_7 0x00010007
#define JAWT_VERSION_9 0x00090000
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* !_JAVASOFT_JAWT_H_ */

276
jni/windows/jdwpTransport.h Normal file
View File

@ -0,0 +1,276 @@
/*
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
* Java Debug Wire Protocol Transport Service Provider Interface.
*/
#ifndef JDWPTRANSPORT_H
#define JDWPTRANSPORT_H
#include "jni.h"
enum {
JDWPTRANSPORT_VERSION_1_0 = 0x00010000,
JDWPTRANSPORT_VERSION_1_1 = 0x00010001
};
#ifdef __cplusplus
extern "C" {
#endif
struct jdwpTransportNativeInterface_;
struct _jdwpTransportEnv;
#ifdef __cplusplus
typedef _jdwpTransportEnv jdwpTransportEnv;
#else
typedef const struct jdwpTransportNativeInterface_ *jdwpTransportEnv;
#endif /* __cplusplus */
/*
* Errors. Universal errors with JVMTI/JVMDI equivalents keep the
* values the same.
*/
typedef enum {
JDWPTRANSPORT_ERROR_NONE = 0,
JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT = 103,
JDWPTRANSPORT_ERROR_OUT_OF_MEMORY = 110,
JDWPTRANSPORT_ERROR_INTERNAL = 113,
JDWPTRANSPORT_ERROR_ILLEGAL_STATE = 201,
JDWPTRANSPORT_ERROR_IO_ERROR = 202,
JDWPTRANSPORT_ERROR_TIMEOUT = 203,
JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE = 204
} jdwpTransportError;
/*
* Structure to define capabilities
*/
typedef struct {
unsigned int can_timeout_attach :1;
unsigned int can_timeout_accept :1;
unsigned int can_timeout_handshake :1;
unsigned int reserved3 :1;
unsigned int reserved4 :1;
unsigned int reserved5 :1;
unsigned int reserved6 :1;
unsigned int reserved7 :1;
unsigned int reserved8 :1;
unsigned int reserved9 :1;
unsigned int reserved10 :1;
unsigned int reserved11 :1;
unsigned int reserved12 :1;
unsigned int reserved13 :1;
unsigned int reserved14 :1;
unsigned int reserved15 :1;
} JDWPTransportCapabilities;
/*
* Structures to define packet layout.
*
* See: http://java.sun.com/j2se/1.5/docs/guide/jpda/jdwp-spec.html
*/
#define JDWP_HEADER_SIZE 11
enum {
/*
* If additional flags are added that apply to jdwpCmdPacket,
* then debugLoop.c: reader() will need to be updated to
* accept more than JDWPTRANSPORT_FLAGS_NONE.
*/
JDWPTRANSPORT_FLAGS_NONE = 0x0,
JDWPTRANSPORT_FLAGS_REPLY = 0x80
};
typedef struct {
jint len;
jint id;
jbyte flags;
jbyte cmdSet;
jbyte cmd;
jbyte *data;
} jdwpCmdPacket;
typedef struct {
jint len;
jint id;
jbyte flags;
jshort errorCode;
jbyte *data;
} jdwpReplyPacket;
typedef struct {
union {
jdwpCmdPacket cmd;
jdwpReplyPacket reply;
} type;
} jdwpPacket;
/*
* JDWP functions called by the transport.
*/
typedef struct jdwpTransportCallback {
void *(*alloc)(jint numBytes); /* Call this for all allocations */
void (*free)(void *buffer); /* Call this for all deallocations */
} jdwpTransportCallback;
typedef jint (JNICALL *jdwpTransport_OnLoad_t)(JavaVM *jvm,
jdwpTransportCallback *callback,
jint version,
jdwpTransportEnv** env);
/*
* JDWP transport configuration from the agent.
*/
typedef struct jdwpTransportConfiguration {
/* Field added in JDWPTRANSPORT_VERSION_1_1: */
const char* allowed_peers; /* Peers allowed for connection */
} jdwpTransportConfiguration;
/* Function Interface */
struct jdwpTransportNativeInterface_ {
/* 1 : RESERVED */
void *reserved1;
/* 2 : Get Capabilities */
jdwpTransportError (JNICALL *GetCapabilities)(jdwpTransportEnv* env,
JDWPTransportCapabilities *capabilities_ptr);
/* 3 : Attach */
jdwpTransportError (JNICALL *Attach)(jdwpTransportEnv* env,
const char* address,
jlong attach_timeout,
jlong handshake_timeout);
/* 4: StartListening */
jdwpTransportError (JNICALL *StartListening)(jdwpTransportEnv* env,
const char* address,
char** actual_address);
/* 5: StopListening */
jdwpTransportError (JNICALL *StopListening)(jdwpTransportEnv* env);
/* 6: Accept */
jdwpTransportError (JNICALL *Accept)(jdwpTransportEnv* env,
jlong accept_timeout,
jlong handshake_timeout);
/* 7: IsOpen */
jboolean (JNICALL *IsOpen)(jdwpTransportEnv* env);
/* 8: Close */
jdwpTransportError (JNICALL *Close)(jdwpTransportEnv* env);
/* 9: ReadPacket */
jdwpTransportError (JNICALL *ReadPacket)(jdwpTransportEnv* env,
jdwpPacket *pkt);
/* 10: Write Packet */
jdwpTransportError (JNICALL *WritePacket)(jdwpTransportEnv* env,
const jdwpPacket* pkt);
/* 11: GetLastError */
jdwpTransportError (JNICALL *GetLastError)(jdwpTransportEnv* env,
char** error);
/* 12: SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
jdwpTransportError (JNICALL *SetTransportConfiguration)(jdwpTransportEnv* env,
jdwpTransportConfiguration *config);
};
/*
* Use inlined functions so that C++ code can use syntax such as
* env->Attach("mymachine:5000", 10*1000, 0);
*
* rather than using C's :-
*
* (*env)->Attach(env, "mymachine:5000", 10*1000, 0);
*/
struct _jdwpTransportEnv {
const struct jdwpTransportNativeInterface_ *functions;
#ifdef __cplusplus
jdwpTransportError GetCapabilities(JDWPTransportCapabilities *capabilities_ptr) {
return functions->GetCapabilities(this, capabilities_ptr);
}
jdwpTransportError Attach(const char* address, jlong attach_timeout,
jlong handshake_timeout) {
return functions->Attach(this, address, attach_timeout, handshake_timeout);
}
jdwpTransportError StartListening(const char* address,
char** actual_address) {
return functions->StartListening(this, address, actual_address);
}
jdwpTransportError StopListening(void) {
return functions->StopListening(this);
}
jdwpTransportError Accept(jlong accept_timeout, jlong handshake_timeout) {
return functions->Accept(this, accept_timeout, handshake_timeout);
}
jboolean IsOpen(void) {
return functions->IsOpen(this);
}
jdwpTransportError Close(void) {
return functions->Close(this);
}
jdwpTransportError ReadPacket(jdwpPacket *pkt) {
return functions->ReadPacket(this, pkt);
}
jdwpTransportError WritePacket(const jdwpPacket* pkt) {
return functions->WritePacket(this, pkt);
}
jdwpTransportError GetLastError(char** error) {
return functions->GetLastError(this, error);
}
/* SetTransportConfiguration added in JDWPTRANSPORT_VERSION_1_1 */
jdwpTransportError SetTransportConfiguration(jdwpTransportEnv* env,
return functions->SetTransportConfiguration(this, config);
}
#endif /* __cplusplus */
};
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */
#endif /* JDWPTRANSPORT_H */

1987
jni/windows/jni.h Normal file

File diff suppressed because it is too large Load Diff

2624
jni/windows/jvmti.h Normal file

File diff suppressed because it is too large Load Diff

115
jni/windows/jvmticmlr.h Normal file
View File

@ -0,0 +1,115 @@
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
* This header file defines the data structures sent by the VM
* through the JVMTI CompiledMethodLoad callback function via the
* "void * compile_info" parameter. The memory pointed to by the
* compile_info parameter may not be referenced after returning from
* the CompiledMethodLoad callback. These are VM implementation
* specific data structures that may evolve in future releases. A
* JVMTI agent should interpret a non-NULL compile_info as a pointer
* to a region of memory containing a list of records. In a typical
* usage scenario, a JVMTI agent would cast each record to a
* jvmtiCompiledMethodLoadRecordHeader, a struct that represents
* arbitrary information. This struct contains a kind field to indicate
* the kind of information being passed, and a pointer to the next
* record. If the kind field indicates inlining information, then the
* agent would cast the record to a jvmtiCompiledMethodLoadInlineRecord.
* This record contains an array of PCStackInfo structs, which indicate
* for every pc address what are the methods on the invocation stack.
* The "methods" and "bcis" fields in each PCStackInfo struct specify a
* 1-1 mapping between these inlined methods and their bytecode indices.
* This can be used to derive the proper source lines of the inlined
* methods.
*/
#ifndef _JVMTI_CMLR_H_
#define _JVMTI_CMLR_H_
enum {
JVMTI_CMLR_MAJOR_VERSION_1 = 0x00000001,
JVMTI_CMLR_MINOR_VERSION_0 = 0x00000000,
JVMTI_CMLR_MAJOR_VERSION = 0x00000001,
JVMTI_CMLR_MINOR_VERSION = 0x00000000
/*
* This comment is for the "JDK import from HotSpot" sanity check:
* version: 1.0.0
*/
};
typedef enum {
JVMTI_CMLR_DUMMY = 1,
JVMTI_CMLR_INLINE_INFO = 2
} jvmtiCMLRKind;
/*
* Record that represents arbitrary information passed through JVMTI
* CompiledMethodLoadEvent void pointer.
*/
typedef struct _jvmtiCompiledMethodLoadRecordHeader {
jvmtiCMLRKind kind; /* id for the kind of info passed in the record */
jint majorinfoversion; /* major and minor info version values. Init'ed */
jint minorinfoversion; /* to current version value in jvmtiExport.cpp. */
struct _jvmtiCompiledMethodLoadRecordHeader* next;
} jvmtiCompiledMethodLoadRecordHeader;
/*
* Record that gives information about the methods on the compile-time
* stack at a specific pc address of a compiled method. Each element in
* the methods array maps to same element in the bcis array.
*/
typedef struct _PCStackInfo {
void* pc; /* the pc address for this compiled method */
jint numstackframes; /* number of methods on the stack */
jmethodID* methods; /* array of numstackframes method ids */
jint* bcis; /* array of numstackframes bytecode indices */
} PCStackInfo;
/*
* Record that contains inlining information for each pc address of
* an nmethod.
*/
typedef struct _jvmtiCompiledMethodLoadInlineRecord {
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
jint numpcs; /* number of pc descriptors in this nmethod */
PCStackInfo* pcinfo; /* array of numpcs pc descriptors */
} jvmtiCompiledMethodLoadInlineRecord;
/*
* Dummy record used to test that we can pass records with different
* information through the void pointer provided that they can be cast
* to a jvmtiCompiledMethodLoadRecordHeader.
*/
typedef struct _jvmtiCompiledMethodLoadDummyRecord {
jvmtiCompiledMethodLoadRecordHeader header; /* common header for casting */
char message[50];
} jvmtiCompiledMethodLoadDummyRecord;
#endif

View File

@ -0,0 +1,92 @@
/*
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/*
* Header file defining callback typedefs for Windows routines
* which are called from Java (responding to events, etc.).
*/
#ifndef __AccessBridgeCallbacks_H__
#define __AccessBridgeCallbacks_H__
#include <jni.h>
#include "AccessBridgePackages.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef void (*AccessBridge_PropertyChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
wchar_t *property, wchar_t *oldValue, wchar_t *newValue);
typedef void (*AccessBridge_JavaShutdownFP) (long vmID);
typedef void (*AccessBridge_JavaShutdownFP) (long vmID);
typedef void (*AccessBridge_FocusGainedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_FocusLostFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_CaretUpdateFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_MouseClickedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_MouseEnteredFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_MouseExitedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_MousePressedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_MouseReleasedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_MenuCanceledFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_MenuDeselectedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_MenuSelectedFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_PopupMenuCanceledFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_PopupMenuWillBecomeInvisibleFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_PopupMenuWillBecomeVisibleFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_PropertyNameChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
wchar_t *oldName, wchar_t *newName);
typedef void (*AccessBridge_PropertyDescriptionChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
wchar_t *oldDescription, wchar_t *newDescription);
typedef void (*AccessBridge_PropertyStateChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
wchar_t *oldState, wchar_t *newState);
typedef void (*AccessBridge_PropertyValueChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
wchar_t *oldValue, wchar_t *newValue);
typedef void (*AccessBridge_PropertySelectionChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_PropertyTextChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_PropertyCaretChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
int oldPosition, int newPosition);
typedef void (*AccessBridge_PropertyVisibleDataChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source);
typedef void (*AccessBridge_PropertyChildChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 source,
JOBJECT64 oldChild, JOBJECT64 newChild);
typedef void (*AccessBridge_PropertyActiveDescendentChangeFP) (long vmID, JOBJECT64 event,
JOBJECT64 source,
JOBJECT64 oldActiveDescendent,
JOBJECT64 newActiveDescendent);
typedef void (*AccessBridge_PropertyTableModelChangeFP) (long vmID, JOBJECT64 event, JOBJECT64 src,
wchar_t *oldValue, wchar_t *newValue);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,725 @@
/*
* Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
/* Note: In addition to this header file AccessBridgeCalls.c must be compiled and
* linked to your application. AccessBridgeCalls.c implements the Java Access
* Bridge API and also hides the complexities associated with interfacing to the
* associated Java Access Bridge DLL which is installed when Java is installed.
*
* AccessBridgeCalls.c is available for download from the OpenJDK repository using
* the following link:
*
* http://hg.openjdk.java.net/jdk9/jdk9/jdk/raw-file/tip/src/jdk.accessibility/windows/native/bridge/AccessBridgeCalls.c
*
* Also note that the API is used in the jaccessinspector and jaccesswalker tools.
* The source for those tools is available in the OpenJDK repository at these links:
*
* http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/tip/src/jdk.accessibility/windows/native/jaccessinspector/jaccessinspector.cpp
* http://hg.openjdk.java.net/jdk9/jdk9/jdk/file/tip/src/jdk.accessibility/windows/native/jaccesswalker/jaccesswalker.cpp
*
*
*/
/*
* Wrapper functions around calls to the AccessBridge DLL
*/
#include <windows.h>
#include <jni.h>
#include "AccessBridgeCallbacks.h"
#include "AccessBridgePackages.h"
#ifdef __cplusplus
extern "C" {
#endif
#define null NULL
typedef JOBJECT64 AccessibleContext;
typedef JOBJECT64 AccessibleText;
typedef JOBJECT64 AccessibleValue;
typedef JOBJECT64 AccessibleSelection;
typedef JOBJECT64 Java_Object;
typedef JOBJECT64 PropertyChangeEvent;
typedef JOBJECT64 FocusEvent;
typedef JOBJECT64 CaretEvent;
typedef JOBJECT64 MouseEvent;
typedef JOBJECT64 MenuEvent;
typedef JOBJECT64 AccessibleTable;
typedef JOBJECT64 AccessibleHyperlink;
typedef JOBJECT64 AccessibleHypertext;
typedef void (*Windows_runFP) ();
typedef void (*SetPropertyChangeFP) (AccessBridge_PropertyChangeFP fp);
typedef void (*SetJavaShutdownFP) (AccessBridge_JavaShutdownFP fp);
typedef void (*SetFocusGainedFP) (AccessBridge_FocusGainedFP fp);
typedef void (*SetFocusLostFP) (AccessBridge_FocusLostFP fp);
typedef void (*SetCaretUpdateFP) (AccessBridge_CaretUpdateFP fp);
typedef void (*SetMouseClickedFP) (AccessBridge_MouseClickedFP fp);
typedef void (*SetMouseEnteredFP) (AccessBridge_MouseEnteredFP fp);
typedef void (*SetMouseExitedFP) (AccessBridge_MouseExitedFP fp);
typedef void (*SetMousePressedFP) (AccessBridge_MousePressedFP fp);
typedef void (*SetMouseReleasedFP) (AccessBridge_MouseReleasedFP fp);
typedef void (*SetMenuCanceledFP) (AccessBridge_MenuCanceledFP fp);
typedef void (*SetMenuDeselectedFP) (AccessBridge_MenuDeselectedFP fp);
typedef void (*SetMenuSelectedFP) (AccessBridge_MenuSelectedFP fp);
typedef void (*SetPopupMenuCanceledFP) (AccessBridge_PopupMenuCanceledFP fp);
typedef void (*SetPopupMenuWillBecomeInvisibleFP) (AccessBridge_PopupMenuWillBecomeInvisibleFP fp);
typedef void (*SetPopupMenuWillBecomeVisibleFP) (AccessBridge_PopupMenuWillBecomeVisibleFP fp);
typedef void (*SetPropertyNameChangeFP) (AccessBridge_PropertyNameChangeFP fp);
typedef void (*SetPropertyDescriptionChangeFP) (AccessBridge_PropertyDescriptionChangeFP fp);
typedef void (*SetPropertyStateChangeFP) (AccessBridge_PropertyStateChangeFP fp);
typedef void (*SetPropertyValueChangeFP) (AccessBridge_PropertyValueChangeFP fp);
typedef void (*SetPropertySelectionChangeFP) (AccessBridge_PropertySelectionChangeFP fp);
typedef void (*SetPropertyTextChangeFP) (AccessBridge_PropertyTextChangeFP fp);
typedef void (*SetPropertyCaretChangeFP) (AccessBridge_PropertyCaretChangeFP fp);
typedef void (*SetPropertyVisibleDataChangeFP) (AccessBridge_PropertyVisibleDataChangeFP fp);
typedef void (*SetPropertyChildChangeFP) (AccessBridge_PropertyChildChangeFP fp);
typedef void (*SetPropertyActiveDescendentChangeFP) (AccessBridge_PropertyActiveDescendentChangeFP fp);
typedef void (*SetPropertyTableModelChangeFP) (AccessBridge_PropertyTableModelChangeFP fp);
typedef void (*ReleaseJavaObjectFP) (long vmID, Java_Object object);
typedef BOOL (*GetVersionInfoFP) (long vmID, AccessBridgeVersionInfo *info);
typedef BOOL (*IsJavaWindowFP) (HWND window);
typedef BOOL (*IsSameObjectFP) (long vmID, JOBJECT64 obj1, JOBJECT64 obj2);
typedef BOOL (*GetAccessibleContextFromHWNDFP) (HWND window, long *vmID, AccessibleContext *ac);
typedef HWND (*getHWNDFromAccessibleContextFP) (long vmID, AccessibleContext ac);
typedef BOOL (*GetAccessibleContextAtFP) (long vmID, AccessibleContext acParent,
jint x, jint y, AccessibleContext *ac);
typedef BOOL (*GetAccessibleContextWithFocusFP) (HWND window, long *vmID, AccessibleContext *ac);
typedef BOOL (*GetAccessibleContextInfoFP) (long vmID, AccessibleContext ac, AccessibleContextInfo *info);
typedef AccessibleContext (*GetAccessibleChildFromContextFP) (long vmID, AccessibleContext ac, jint i);
typedef AccessibleContext (*GetAccessibleParentFromContextFP) (long vmID, AccessibleContext ac);
/* begin AccessibleTable */
typedef BOOL (*getAccessibleTableInfoFP) (long vmID, AccessibleContext ac, AccessibleTableInfo *tableInfo);
typedef BOOL (*getAccessibleTableCellInfoFP) (long vmID, AccessibleTable accessibleTable,
jint row, jint column, AccessibleTableCellInfo *tableCellInfo);
typedef BOOL (*getAccessibleTableRowHeaderFP) (long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
typedef BOOL (*getAccessibleTableColumnHeaderFP) (long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
typedef AccessibleContext (*getAccessibleTableRowDescriptionFP) (long vmID, AccessibleContext acParent, jint row);
typedef AccessibleContext (*getAccessibleTableColumnDescriptionFP) (long vmID, AccessibleContext acParent, jint column);
typedef jint (*getAccessibleTableRowSelectionCountFP) (long vmID, AccessibleTable table);
typedef BOOL (*isAccessibleTableRowSelectedFP) (long vmID, AccessibleTable table, jint row);
typedef BOOL (*getAccessibleTableRowSelectionsFP) (long vmID, AccessibleTable table, jint count,
jint *selections);
typedef jint (*getAccessibleTableColumnSelectionCountFP) (long vmID, AccessibleTable table);
typedef BOOL (*isAccessibleTableColumnSelectedFP) (long vmID, AccessibleTable table, jint column);
typedef BOOL (*getAccessibleTableColumnSelectionsFP) (long vmID, AccessibleTable table, jint count,
jint *selections);
typedef jint (*getAccessibleTableRowFP) (long vmID, AccessibleTable table, jint index);
typedef jint (*getAccessibleTableColumnFP) (long vmID, AccessibleTable table, jint index);
typedef jint (*getAccessibleTableIndexFP) (long vmID, AccessibleTable table, jint row, jint column);
/* end AccessibleTable */
/* AccessibleRelationSet */
typedef BOOL (*getAccessibleRelationSetFP) (long vmID, AccessibleContext accessibleContext,
AccessibleRelationSetInfo *relationSetInfo);
/* AccessibleHypertext */
typedef BOOL (*getAccessibleHypertextFP)(long vmID, AccessibleContext accessibleContext,
AccessibleHypertextInfo *hypertextInfo);
typedef BOOL (*activateAccessibleHyperlinkFP)(long vmID, AccessibleContext accessibleContext,
AccessibleHyperlink accessibleHyperlink);
typedef jint (*getAccessibleHyperlinkCountFP)(const long vmID,
const AccessibleContext accessibleContext);
typedef BOOL (*getAccessibleHypertextExtFP) (const long vmID,
const AccessibleContext accessibleContext,
const jint nStartIndex,
AccessibleHypertextInfo *hypertextInfo);
typedef jint (*getAccessibleHypertextLinkIndexFP)(const long vmID,
const AccessibleHypertext hypertext,
const jint nIndex);
typedef BOOL (*getAccessibleHyperlinkFP)(const long vmID,
const AccessibleHypertext hypertext,
const jint nIndex,
AccessibleHyperlinkInfo *hyperlinkInfo);
/* Accessible KeyBindings, Icons and Actions */
typedef BOOL (*getAccessibleKeyBindingsFP)(long vmID, AccessibleContext accessibleContext,
AccessibleKeyBindings *keyBindings);
typedef BOOL (*getAccessibleIconsFP)(long vmID, AccessibleContext accessibleContext,
AccessibleIcons *icons);
typedef BOOL (*getAccessibleActionsFP)(long vmID, AccessibleContext accessibleContext,
AccessibleActions *actions);
typedef BOOL (*doAccessibleActionsFP)(long vmID, AccessibleContext accessibleContext,
AccessibleActionsToDo *actionsToDo, jint *failure);
/* AccessibleText */
typedef BOOL (*GetAccessibleTextInfoFP) (long vmID, AccessibleText at, AccessibleTextInfo *textInfo, jint x, jint y);
typedef BOOL (*GetAccessibleTextItemsFP) (long vmID, AccessibleText at, AccessibleTextItemsInfo *textItems, jint index);
typedef BOOL (*GetAccessibleTextSelectionInfoFP) (long vmID, AccessibleText at, AccessibleTextSelectionInfo *textSelection);
typedef BOOL (*GetAccessibleTextAttributesFP) (long vmID, AccessibleText at, jint index, AccessibleTextAttributesInfo *attributes);
typedef BOOL (*GetAccessibleTextRectFP) (long vmID, AccessibleText at, AccessibleTextRectInfo *rectInfo, jint index);
typedef BOOL (*GetAccessibleTextLineBoundsFP) (long vmID, AccessibleText at, jint index, jint *startIndex, jint *endIndex);
typedef BOOL (*GetAccessibleTextRangeFP) (long vmID, AccessibleText at, jint start, jint end, wchar_t *text, short len);
typedef BOOL (*GetCurrentAccessibleValueFromContextFP) (long vmID, AccessibleValue av, wchar_t *value, short len);
typedef BOOL (*GetMaximumAccessibleValueFromContextFP) (long vmID, AccessibleValue av, wchar_t *value, short len);
typedef BOOL (*GetMinimumAccessibleValueFromContextFP) (long vmID, AccessibleValue av, wchar_t *value, short len);
typedef void (*AddAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as, int i);
typedef void (*ClearAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as);
typedef JOBJECT64 (*GetAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as, int i);
typedef int (*GetAccessibleSelectionCountFromContextFP) (long vmID, AccessibleSelection as);
typedef BOOL (*IsAccessibleChildSelectedFromContextFP) (long vmID, AccessibleSelection as, int i);
typedef void (*RemoveAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as, int i);
typedef void (*SelectAllAccessibleSelectionFromContextFP) (long vmID, AccessibleSelection as);
/* Utility methods */
typedef BOOL (*setTextContentsFP) (const long vmID, const AccessibleContext ac, const wchar_t *text);
typedef AccessibleContext (*getParentWithRoleFP) (const long vmID, const AccessibleContext ac, const wchar_t *role);
typedef AccessibleContext (*getParentWithRoleElseRootFP) (const long vmID, const AccessibleContext ac, const wchar_t *role);
typedef AccessibleContext (*getTopLevelObjectFP) (const long vmID, const AccessibleContext ac);
typedef int (*getObjectDepthFP) (const long vmID, const AccessibleContext ac);
typedef AccessibleContext (*getActiveDescendentFP) (const long vmID, const AccessibleContext ac);
typedef BOOL (*getVirtualAccessibleNameFP) (const long vmID, const AccessibleContext accessibleContext,
wchar_t *name, int len);
typedef BOOL (*requestFocusFP) (const long vmID, const AccessibleContext accessibleContext);
typedef BOOL (*selectTextRangeFP) (const long vmID, const AccessibleContext accessibleContext,
const int startIndex, const int endIndex);
typedef BOOL (*getTextAttributesInRangeFP) (const long vmID, const AccessibleContext accessibleContext,
const int startIndex, const int endIndex,
AccessibleTextAttributesInfo *attributes, short *len);
typedef int (*getVisibleChildrenCountFP) (const long vmID, const AccessibleContext accessibleContext);
typedef BOOL (*getVisibleChildrenFP) (const long vmID, const AccessibleContext accessibleContext,
const int startIndex, VisibleChildrenInfo *children);
typedef BOOL (*setCaretPositionFP) (const long vmID, const AccessibleContext accessibleContext, const int position);
typedef BOOL (*getCaretLocationFP) (long vmID, AccessibleContext ac, AccessibleTextRectInfo *rectInfo, jint index);
typedef int (*getEventsWaitingFP) ();
typedef struct AccessBridgeFPsTag {
Windows_runFP Windows_run;
SetPropertyChangeFP SetPropertyChange;
SetJavaShutdownFP SetJavaShutdown;
SetFocusGainedFP SetFocusGained;
SetFocusLostFP SetFocusLost;
SetCaretUpdateFP SetCaretUpdate;
SetMouseClickedFP SetMouseClicked;
SetMouseEnteredFP SetMouseEntered;
SetMouseExitedFP SetMouseExited;
SetMousePressedFP SetMousePressed;
SetMouseReleasedFP SetMouseReleased;
SetMenuCanceledFP SetMenuCanceled;
SetMenuDeselectedFP SetMenuDeselected;
SetMenuSelectedFP SetMenuSelected;
SetPopupMenuCanceledFP SetPopupMenuCanceled;
SetPopupMenuWillBecomeInvisibleFP SetPopupMenuWillBecomeInvisible;
SetPopupMenuWillBecomeVisibleFP SetPopupMenuWillBecomeVisible;
SetPropertyNameChangeFP SetPropertyNameChange;
SetPropertyDescriptionChangeFP SetPropertyDescriptionChange;
SetPropertyStateChangeFP SetPropertyStateChange;
SetPropertyValueChangeFP SetPropertyValueChange;
SetPropertySelectionChangeFP SetPropertySelectionChange;
SetPropertyTextChangeFP SetPropertyTextChange;
SetPropertyCaretChangeFP SetPropertyCaretChange;
SetPropertyVisibleDataChangeFP SetPropertyVisibleDataChange;
SetPropertyChildChangeFP SetPropertyChildChange;
SetPropertyActiveDescendentChangeFP SetPropertyActiveDescendentChange;
SetPropertyTableModelChangeFP SetPropertyTableModelChange;
ReleaseJavaObjectFP ReleaseJavaObject;
GetVersionInfoFP GetVersionInfo;
IsJavaWindowFP IsJavaWindow;
IsSameObjectFP IsSameObject;
GetAccessibleContextFromHWNDFP GetAccessibleContextFromHWND;
getHWNDFromAccessibleContextFP getHWNDFromAccessibleContext;
GetAccessibleContextAtFP GetAccessibleContextAt;
GetAccessibleContextWithFocusFP GetAccessibleContextWithFocus;
GetAccessibleContextInfoFP GetAccessibleContextInfo;
GetAccessibleChildFromContextFP GetAccessibleChildFromContext;
GetAccessibleParentFromContextFP GetAccessibleParentFromContext;
getAccessibleTableInfoFP getAccessibleTableInfo;
getAccessibleTableCellInfoFP getAccessibleTableCellInfo;
getAccessibleTableRowHeaderFP getAccessibleTableRowHeader;
getAccessibleTableColumnHeaderFP getAccessibleTableColumnHeader;
getAccessibleTableRowDescriptionFP getAccessibleTableRowDescription;
getAccessibleTableColumnDescriptionFP getAccessibleTableColumnDescription;
getAccessibleTableRowSelectionCountFP getAccessibleTableRowSelectionCount;
isAccessibleTableRowSelectedFP isAccessibleTableRowSelected;
getAccessibleTableRowSelectionsFP getAccessibleTableRowSelections;
getAccessibleTableColumnSelectionCountFP getAccessibleTableColumnSelectionCount;
isAccessibleTableColumnSelectedFP isAccessibleTableColumnSelected;
getAccessibleTableColumnSelectionsFP getAccessibleTableColumnSelections;
getAccessibleTableRowFP getAccessibleTableRow;
getAccessibleTableColumnFP getAccessibleTableColumn;
getAccessibleTableIndexFP getAccessibleTableIndex;
getAccessibleRelationSetFP getAccessibleRelationSet;
getAccessibleHypertextFP getAccessibleHypertext;
activateAccessibleHyperlinkFP activateAccessibleHyperlink;
getAccessibleHyperlinkCountFP getAccessibleHyperlinkCount;
getAccessibleHypertextExtFP getAccessibleHypertextExt;
getAccessibleHypertextLinkIndexFP getAccessibleHypertextLinkIndex;
getAccessibleHyperlinkFP getAccessibleHyperlink;
getAccessibleKeyBindingsFP getAccessibleKeyBindings;
getAccessibleIconsFP getAccessibleIcons;
getAccessibleActionsFP getAccessibleActions;
doAccessibleActionsFP doAccessibleActions;
GetAccessibleTextInfoFP GetAccessibleTextInfo;
GetAccessibleTextItemsFP GetAccessibleTextItems;
GetAccessibleTextSelectionInfoFP GetAccessibleTextSelectionInfo;
GetAccessibleTextAttributesFP GetAccessibleTextAttributes;
GetAccessibleTextRectFP GetAccessibleTextRect;
GetAccessibleTextLineBoundsFP GetAccessibleTextLineBounds;
GetAccessibleTextRangeFP GetAccessibleTextRange;
GetCurrentAccessibleValueFromContextFP GetCurrentAccessibleValueFromContext;
GetMaximumAccessibleValueFromContextFP GetMaximumAccessibleValueFromContext;
GetMinimumAccessibleValueFromContextFP GetMinimumAccessibleValueFromContext;
AddAccessibleSelectionFromContextFP AddAccessibleSelectionFromContext;
ClearAccessibleSelectionFromContextFP ClearAccessibleSelectionFromContext;
GetAccessibleSelectionFromContextFP GetAccessibleSelectionFromContext;
GetAccessibleSelectionCountFromContextFP GetAccessibleSelectionCountFromContext;
IsAccessibleChildSelectedFromContextFP IsAccessibleChildSelectedFromContext;
RemoveAccessibleSelectionFromContextFP RemoveAccessibleSelectionFromContext;
SelectAllAccessibleSelectionFromContextFP SelectAllAccessibleSelectionFromContext;
setTextContentsFP setTextContents;
getParentWithRoleFP getParentWithRole;
getTopLevelObjectFP getTopLevelObject;
getParentWithRoleElseRootFP getParentWithRoleElseRoot;
getObjectDepthFP getObjectDepth;
getActiveDescendentFP getActiveDescendent;
getVirtualAccessibleNameFP getVirtualAccessibleName;
requestFocusFP requestFocus;
selectTextRangeFP selectTextRange;
getTextAttributesInRangeFP getTextAttributesInRange;
getVisibleChildrenCountFP getVisibleChildrenCount;
getVisibleChildrenFP getVisibleChildren;
setCaretPositionFP setCaretPosition;
getCaretLocationFP getCaretLocation;
getEventsWaitingFP getEventsWaiting;
} AccessBridgeFPs;
/**
* Initialize the world
*/
BOOL initializeAccessBridge();
BOOL shutdownAccessBridge();
/**
* Window routines
*/
BOOL IsJavaWindow(HWND window);
// Returns the virtual machine ID and AccessibleContext for a top-level window
BOOL GetAccessibleContextFromHWND(HWND target, long *vmID, AccessibleContext *ac);
// Returns the HWND from the AccessibleContext of a top-level window
HWND getHWNDFromAccessibleContext(long vmID, AccessibleContext ac);
/**
* Event handling routines
*/
void SetJavaShutdown(AccessBridge_JavaShutdownFP fp);
void SetFocusGained(AccessBridge_FocusGainedFP fp);
void SetFocusLost(AccessBridge_FocusLostFP fp);
void SetCaretUpdate(AccessBridge_CaretUpdateFP fp);
void SetMouseClicked(AccessBridge_MouseClickedFP fp);
void SetMouseEntered(AccessBridge_MouseEnteredFP fp);
void SetMouseExited(AccessBridge_MouseExitedFP fp);
void SetMousePressed(AccessBridge_MousePressedFP fp);
void SetMouseReleased(AccessBridge_MouseReleasedFP fp);
void SetMenuCanceled(AccessBridge_MenuCanceledFP fp);
void SetMenuDeselected(AccessBridge_MenuDeselectedFP fp);
void SetMenuSelected(AccessBridge_MenuSelectedFP fp);
void SetPopupMenuCanceled(AccessBridge_PopupMenuCanceledFP fp);
void SetPopupMenuWillBecomeInvisible(AccessBridge_PopupMenuWillBecomeInvisibleFP fp);
void SetPopupMenuWillBecomeVisible(AccessBridge_PopupMenuWillBecomeVisibleFP fp);
void SetPropertyNameChange(AccessBridge_PropertyNameChangeFP fp);
void SetPropertyDescriptionChange(AccessBridge_PropertyDescriptionChangeFP fp);
void SetPropertyStateChange(AccessBridge_PropertyStateChangeFP fp);
void SetPropertyValueChange(AccessBridge_PropertyValueChangeFP fp);
void SetPropertySelectionChange(AccessBridge_PropertySelectionChangeFP fp);
void SetPropertyTextChange(AccessBridge_PropertyTextChangeFP fp);
void SetPropertyCaretChange(AccessBridge_PropertyCaretChangeFP fp);
void SetPropertyVisibleDataChange(AccessBridge_PropertyVisibleDataChangeFP fp);
void SetPropertyChildChange(AccessBridge_PropertyChildChangeFP fp);
void SetPropertyActiveDescendentChange(AccessBridge_PropertyActiveDescendentChangeFP fp);
void SetPropertyTableModelChange(AccessBridge_PropertyTableModelChangeFP fp);
/**
* General routines
*/
void ReleaseJavaObject(long vmID, Java_Object object);
BOOL GetVersionInfo(long vmID, AccessBridgeVersionInfo *info);
HWND GetHWNDFromAccessibleContext(long vmID, JOBJECT64 accesibleContext);
/**
* Accessible Context routines
*/
BOOL GetAccessibleContextAt(long vmID, AccessibleContext acParent,
jint x, jint y, AccessibleContext *ac);
BOOL GetAccessibleContextWithFocus(HWND window, long *vmID, AccessibleContext *ac);
BOOL GetAccessibleContextInfo(long vmID, AccessibleContext ac, AccessibleContextInfo *info);
AccessibleContext GetAccessibleChildFromContext(long vmID, AccessibleContext ac, jint index);
AccessibleContext GetAccessibleParentFromContext(long vmID, AccessibleContext ac);
/**
* Accessible Text routines
*/
BOOL GetAccessibleTextInfo(long vmID, AccessibleText at, AccessibleTextInfo *textInfo, jint x, jint y);
BOOL GetAccessibleTextItems(long vmID, AccessibleText at, AccessibleTextItemsInfo *textItems, jint index);
BOOL GetAccessibleTextSelectionInfo(long vmID, AccessibleText at, AccessibleTextSelectionInfo *textSelection);
BOOL GetAccessibleTextAttributes(long vmID, AccessibleText at, jint index, AccessibleTextAttributesInfo *attributes);
BOOL GetAccessibleTextRect(long vmID, AccessibleText at, AccessibleTextRectInfo *rectInfo, jint index);
BOOL GetAccessibleTextLineBounds(long vmID, AccessibleText at, jint index, jint *startIndex, jint *endIndex);
BOOL GetAccessibleTextRange(long vmID, AccessibleText at, jint start, jint end, wchar_t *text, short len);
/* begin AccessibleTable routines */
BOOL getAccessibleTableInfo(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
BOOL getAccessibleTableCellInfo(long vmID, AccessibleTable accessibleTable, jint row, jint column,
AccessibleTableCellInfo *tableCellInfo);
BOOL getAccessibleTableRowHeader(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
BOOL getAccessibleTableColumnHeader(long vmID, AccessibleContext acParent, AccessibleTableInfo *tableInfo);
AccessibleContext getAccessibleTableRowDescription(long vmID, AccessibleContext acParent, jint row);
AccessibleContext getAccessibleTableColumnDescription(long vmID, AccessibleContext acParent, jint column);
jint getAccessibleTableRowSelectionCount(long vmID, AccessibleTable table);
BOOL isAccessibleTableRowSelected(long vmID, AccessibleTable table, jint row);
BOOL getAccessibleTableRowSelections(long vmID, AccessibleTable table, jint count, jint *selections);
jint getAccessibleTableColumnSelectionCount(long vmID, AccessibleTable table);
BOOL isAccessibleTableColumnSelected(long vmID, AccessibleTable table, jint column);
BOOL getAccessibleTableColumnSelections(long vmID, AccessibleTable table, jint count, jint *selections);
jint getAccessibleTableRow(long vmID, AccessibleTable table, jint index);
jint getAccessibleTableColumn(long vmID, AccessibleTable table, jint index);
jint getAccessibleTableIndex(long vmID, AccessibleTable table, jint row, jint column);
/* end AccessibleTable */
/* ----- AccessibleRelationSet routines */
BOOL getAccessibleRelationSet(long vmID, AccessibleContext accessibleContext,
AccessibleRelationSetInfo *relationSetInfo);
/* ----- AccessibleHypertext routines */
/*
* Returns hypertext information associated with a component.
*/
BOOL getAccessibleHypertext(long vmID, AccessibleContext accessibleContext,
AccessibleHypertextInfo *hypertextInfo);
/*
* Requests that a hyperlink be activated.
*/
BOOL activateAccessibleHyperlink(long vmID, AccessibleContext accessibleContext,
AccessibleHyperlink accessibleHyperlink);
/*
* Returns the number of hyperlinks in a component
* Maps to AccessibleHypertext.getLinkCount.
* Returns -1 on error.
*/
jint getAccessibleHyperlinkCount(const long vmID,
const AccessibleHypertext hypertext);
/*
* This method is used to iterate through the hyperlinks in a component. It
* returns hypertext information for a component starting at hyperlink index
* nStartIndex. No more than MAX_HYPERLINKS AccessibleHypertextInfo objects will
* be returned for each call to this method.
* Returns FALSE on error.
*/
BOOL getAccessibleHypertextExt(const long vmID,
const AccessibleContext accessibleContext,
const jint nStartIndex,
/* OUT */ AccessibleHypertextInfo *hypertextInfo);
/*
* Returns the index into an array of hyperlinks that is associated with
* a character index in document; maps to AccessibleHypertext.getLinkIndex
* Returns -1 on error.
*/
jint getAccessibleHypertextLinkIndex(const long vmID,
const AccessibleHypertext hypertext,
const jint nIndex);
/*
* Returns the nth hyperlink in a document
* Maps to AccessibleHypertext.getLink.
* Returns FALSE on error
*/
BOOL getAccessibleHyperlink(const long vmID,
const AccessibleHypertext hypertext,
const jint nIndex,
/* OUT */ AccessibleHyperlinkInfo *hyperlinkInfo);
/* Accessible KeyBindings, Icons and Actions */
/*
* Returns a list of key bindings associated with a component.
*/
BOOL getAccessibleKeyBindings(long vmID, AccessibleContext accessibleContext,
AccessibleKeyBindings *keyBindings);
/*
* Returns a list of icons associate with a component.
*/
BOOL getAccessibleIcons(long vmID, AccessibleContext accessibleContext,
AccessibleIcons *icons);
/*
* Returns a list of actions that a component can perform.
*/
BOOL getAccessibleActions(long vmID, AccessibleContext accessibleContext,
AccessibleActions *actions);
/*
* Request that a list of AccessibleActions be performed by a component.
* Returns TRUE if all actions are performed. Returns FALSE
* when the first requested action fails in which case "failure"
* contains the index of the action that failed.
*/
BOOL doAccessibleActions(long vmID, AccessibleContext accessibleContext,
AccessibleActionsToDo *actionsToDo, jint *failure);
/* Additional utility methods */
/*
* Returns whether two object references refer to the same object.
*/
BOOL IsSameObject(long vmID, JOBJECT64 obj1, JOBJECT64 obj2);
/**
* Sets editable text contents. The AccessibleContext must implement AccessibleEditableText and
* be editable. The maximum text length that can be set is MAX_STRING_SIZE - 1.
* Returns whether successful
*/
BOOL setTextContents (const long vmID, const AccessibleContext accessibleContext, const wchar_t *text);
/**
* Returns the Accessible Context with the specified role that is the
* ancestor of a given object. The role is one of the role strings
* defined in AccessBridgePackages.h
* If there is no ancestor object that has the specified role,
* returns (AccessibleContext)0.
*/
AccessibleContext getParentWithRole (const long vmID, const AccessibleContext accessibleContext,
const wchar_t *role);
/**
* Returns the Accessible Context with the specified role that is the
* ancestor of a given object. The role is one of the role strings
* defined in AccessBridgePackages.h. If an object with the specified
* role does not exist, returns the top level object for the Java Window.
* Returns (AccessibleContext)0 on error.
*/
AccessibleContext getParentWithRoleElseRoot (const long vmID, const AccessibleContext accessibleContext,
const wchar_t *role);
/**
* Returns the Accessible Context for the top level object in
* a Java Window. This is same Accessible Context that is obtained
* from GetAccessibleContextFromHWND for that window. Returns
* (AccessibleContext)0 on error.
*/
AccessibleContext getTopLevelObject (const long vmID, const AccessibleContext accessibleContext);
/**
* Returns how deep in the object hierarchy a given object is.
* The top most object in the object hierarchy has an object depth of 0.
* Returns -1 on error.
*/
int getObjectDepth (const long vmID, const AccessibleContext accessibleContext);
/**
* Returns the Accessible Context of the current ActiveDescendent of an object.
* This method assumes the ActiveDescendent is the component that is currently
* selected in a container object.
* Returns (AccessibleContext)0 on error or if there is no selection.
*/
AccessibleContext getActiveDescendent (const long vmID, const AccessibleContext accessibleContext);
/**
/**
* Accessible Value routines
*/
BOOL GetCurrentAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len);
BOOL GetMaximumAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len);
BOOL GetMinimumAccessibleValueFromContext(long vmID, AccessibleValue av, wchar_t *value, short len);
/**
* Accessible Selection routines
*/
void AddAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i);
void ClearAccessibleSelectionFromContext(long vmID, AccessibleSelection as);
JOBJECT64 GetAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i);
int GetAccessibleSelectionCountFromContext(long vmID, AccessibleSelection as);
BOOL IsAccessibleChildSelectedFromContext(long vmID, AccessibleSelection as, int i);
void RemoveAccessibleSelectionFromContext(long vmID, AccessibleSelection as, int i);
void SelectAllAccessibleSelectionFromContext(long vmID, AccessibleSelection as);
/**
* Additional methods for Teton
*/
/**
* Gets the AccessibleName for a component based upon the JAWS algorithm. Returns
* whether successful.
*
* Bug ID 4916682 - Implement JAWS AccessibleName policy
*/
BOOL getVirtualAccessibleName(const long vmID, const AccessibleContext accessibleContext,
wchar_t *name, int len);
/**
* Request focus for a component. Returns whether successful.
*
* Bug ID 4944757 - requestFocus method needed
*/
BOOL requestFocus(const long vmID, const AccessibleContext accessibleContext);
/**
* Selects text between two indices. Selection includes the text at the start index
* and the text at the end index. Returns whether successful.
*
* Bug ID 4944758 - selectTextRange method needed
*/
BOOL selectTextRange(const long vmID, const AccessibleContext accessibleContext, const int startIndex,
const int endIndex);
/**
* Get text attributes between two indices. The attribute list includes the text at the
* start index and the text at the end index. Returns whether successful;
*
* Bug ID 4944761 - getTextAttributes between two indices method needed
*/
BOOL getTextAttributesInRange(const long vmID, const AccessibleContext accessibleContext,
const int startIndex, const int endIndex,
AccessibleTextAttributesInfo *attributes, short *len);
/**
* Returns the number of visible children of a component. Returns -1 on error.
*
* Bug ID 4944762- getVisibleChildren for list-like components needed
*/
int getVisibleChildrenCount(const long vmID, const AccessibleContext accessibleContext);
/**
* Gets the visible children of an AccessibleContext. Returns whether successful.
*
* Bug ID 4944762- getVisibleChildren for list-like components needed
*/
BOOL getVisibleChildren(const long vmID, const AccessibleContext accessibleContext,
const int startIndex,
VisibleChildrenInfo *visibleChildrenInfo);
/**
* Set the caret to a text position. Returns whether successful.
*
* Bug ID 4944770 - setCaretPosition method needed
*/
BOOL setCaretPosition(const long vmID, const AccessibleContext accessibleContext,
const int position);
/**
* Gets the text caret location
*/
BOOL getCaretLocation(long vmID, AccessibleContext ac,
AccessibleTextRectInfo *rectInfo, jint index);
/**
* Gets the number of events waiting to fire
*/
int getEventsWaiting();
#ifdef __cplusplus
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) 1999, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
#ifndef _JAVASOFT_JAWT_MD_H_
#define _JAVASOFT_JAWT_MD_H_
#include <windows.h>
#include "jawt.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
* Win32-specific declarations for AWT native interface.
* See notes in jawt.h for an example of use.
*/
typedef struct jawt_Win32DrawingSurfaceInfo {
/* Native window, DDB, or DIB handle */
union {
HWND hwnd;
HBITMAP hbitmap;
void* pbits;
};
/*
* This HDC should always be used instead of the HDC returned from
* BeginPaint() or any calls to GetDC().
*/
HDC hdc;
HPALETTE hpalette;
} JAWT_Win32DrawingSurfaceInfo;
#ifdef __cplusplus
}
#endif
#endif /* !_JAVASOFT_JAWT_MD_H_ */

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 1996, 1998, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
#ifndef _JAVASOFT_JNI_MD_H_
#define _JAVASOFT_JNI_MD_H_
#define JNIEXPORT __declspec(dllexport)
#define JNIIMPORT __declspec(dllimport)
#define JNICALL __stdcall
// 'long' is always 32 bit on windows so this matches what jdk expects
typedef long jint;
typedef __int64 jlong;
typedef signed char jbyte;
#endif /* !_JAVASOFT_JNI_MD_H_ */

View File

@ -1,4 +1,4 @@
Copyright (C) 2024 Guy Perfect Copyright (C) 2020 Planet Virtual Boy
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

151
locale/en-US.txt Normal file
View File

@ -0,0 +1,151 @@
# Locale
locale {
id en-US
name English (United States)
}
# Main window
app {
debug {
(menu) Debug
breakpoints Breakpoints
console Console
cpu CPU
memory Memory
# VIP section
bg_maps BG Maps
characters Characters
frame_buffers Frame buffers
objects Objects
worlds Worlds
}
file {
(menu) File
debug_mode Debug mode
exit Exit
game_mode Game mode
load_rom Load ROM...
new_window New window
}
title {
default PVB Emulator
mixed {ctrl.number} {ctrl.filename} - {app.title.default}
number {ctrl.number} {app.title.default}
rom {ctrl.filename} - {app.title.default}
}
}
# BG Maps window
bg_maps {
address Address
cell Cell
character Character
generic Generic
grid Grid
hflip H-Flip
index Index
map Map
palette Palette
scale Scale
title BG Maps
vflip V-Flip
}
# Breakpoints window
breakpoints {
address Address
condition Condition
default_name New breakpoint
delete Delete
enabled Enabled
exception Exception
execute Execute
name Name
new New
read Read
title Breakpoints
type Type
write Write
error {
badliteral_a Error:{err.position}: Unable to process address "{err.text}"
badliteral_c Error:{err.position}: Unable to process literal "{err.text}"
badoperand Error:{err.position}: Invalid operand to operator "{err.text}"
badtoken Error:{err.position}: Unrecognized token "{err.text}"
earlyeof Error:{err.position}: Unexpected end of input
invalid Error:{err.position}: Token "{err.text}" cannot appear here
nesting Error:{err.position}: Expected "{err.other}", but found "{err.text}"
unexpected Error:{err.position}: Unexpected "{err.text}"
}
}
# Characters window
characters {
address Address
grid Grid
index Index
mirror Mirror
Palette Palette
scale Scale
title Characters
wide Wide
}
# Console window
console {
title Console
}
# Emulation core
core {
java Java
linux-x86 Linux (32-bit)
linux-x86_64 Linux (64-bit)
native Native
windows-x86 Windows (32-bit)
windows-x86_64 Windows (64-bit)
}
# CPU window
cpu {
float Float
hex Hex
jump_from From
jump_to To
signed Signed
title CPU
unsigned Unsigned
}
# File dialog
dialog {
ext_isx ISX modules (*.isx)
ext_vb Virtual Boy ROMs (*.vb)
load Load
load_rom Load ROM
load_rom_error Unable to load the selected ROM file.
load_rom_notvb The selected file does not appear to be a Virtual Boy ROM.
}
# Memory window
memory {
title Memory
}
# Palettes
palette {
Generic Generic
gplt0 BG 0
gplt1 BG 1
gplt2 BG 2
gplt3 BG 3
jplt0 OBJ 0
jplt1 OBJ 1
jplt2 OBJ 2
jplt3 OBJ 3
}

202
makefile
View File

@ -1,61 +1,167 @@
.PHONY: help # Java include directory pathnames
help: # The Windows files need to be copied from a Windows JDK installation
include_linux = jni/linux
include_windows = jni/windows
# Default goal
.PHONY: default
default:
@echo $(include_linux)
@echo "Planet Virtual Boy Emulator"
@echo " https://www.planetvb.com/"
@echo " December 26, 2020"
@echo @echo
@echo "Virtual Boy Emulator - October 10, 2024" @echo "Intended build environment: Debian i386 or amd64"
@echo " gcc-multilib"
@echo " mingw-w64"
@echo " openjdk-15-jdk"
@echo @echo
@echo "Target build environment is any Debian with the following packages:" @echo "Usage: make <recipe>"
@echo " emscripten" @echo " Package recipes:"
@echo " gcc" @echo " build Compiles the native modules and desktop application"
@echo " openjdk-17-jdk" @echo " clean Deletes all output files"
@echo @echo " core Check the native core library for style errors"
@echo "Available make targets:" @echo " desktop Compiles the Java desktop application"
@echo " build Perform all build commands" @echo " native Builds all native modules"
@echo " bundle Package the repository for HTML distribution" @echo " pack Bundles everything in a .jar file"
@echo " clean Remove output files" @echo " release Produces a .jar and deletes all intermediate files"
@echo " core Check the C core source for compiler warnings" @echo " Native recipes:"
@echo " wasm Build the WebAssembly core module" @echo " lin32 Builds native module linux_x86"
@echo " lin64 Builds native module linux_x86-64"
@echo " win32 Builds native module windows_x86"
@echo " win64 Builds native module windows_x86-64"
@echo @echo
###############################################################################
# Package Recipes #
###############################################################################
# Perform a full build process of the native modules and desktop application
.PHONY: build .PHONY: build
build: build:
@echo " Check C core library for style issues"
@make -s core @make -s core
@echo " Build WebAssembly core module" @make -s desktop
@make -s wasm @make -s native
@echo " Bundle directory tree into HTML file"
@make -s bundle
.PHONY: bundle
bundle:
@java web/Bundle.java vbemu
# Delete all output files
.PHONY: clean .PHONY: clean
clean: clean: clean_most
@rm -f vbemu_*.html web/core/core.wasm @rm -f pvbemu_*.jar
# Check the native core library for style errors
.PHONY: core .PHONY: core
core: core:
# GCC generic @echo " Checking native core library for style errors"
@gcc core/vb.c -I core -c -o /dev/null \ $(eval coreargs = -c -Isrc/core/include -Werror -Wall -Wextra -Wpedantic \
-Werror -std=c90 -Wall -Wextra -Wpedantic -fno-strict-aliasing -fsyntax-only src/core/vue.c)
# GCC compilation control @gcc -std=c90 $(coreargs)
@gcc core/vb.c -I core -c -o /dev/null \ @gcc -std=c90 -D VUE_BIGENDIAN $(coreargs)
-Werror -std=c90 -Wall -Wextra -Wpedantic \ @gcc -std=c99 $(coreargs)
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE -D VB_DIV_GENERIC @gcc -std=c99 -D VUE_BIGENDIAN $(coreargs)
# Clang generic @gcc -std=c11 $(coreargs)
@emcc core/vb.c -I core -c -o /dev/null \ @gcc -std=c11 -D VUE_BIGENDIAN $(coreargs)
-Werror -std=c90 -Wall -Wextra -Wpedantic
# Clang compilation control
@emcc core/vb.c -I core -c -o /dev/null \
-Werror -std=c90 -Wall -Wextra -Wpedantic \
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE -D VB_DIV_GENERIC
.PHONY: wasm # Compile the Java desktop application
wasm: .PHONY: desktop
@emcc -o web/core/core.wasm web/core/wasm.c core/vb.c -Icore \ desktop: clean_desktop
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE \ @echo " Compiling Java desktop application"
-D "VBAPI=__attribute__((used))" \ @javac -sourcepath src/desktop --release 10 -Xlint:unchecked \
--no-entry -O2 -flto -s WASM=1 \ -Xlint:deprecation -h src/desktop/vue -d . src/desktop/Main.java
-s EXPORTED_RUNTIME_METHODS=[] -s ALLOW_MEMORY_GROWTH \
-s MAXIMUM_MEMORY=4GB -fno-strict-aliasing # Build all native modules
@rm -f web/core/*.wasm.tmp* .PHONY: native
native:
@make -s lin32
@make -s lin64
@make -s win32
@make -s win64
# Package the release into a .jar file
.PHONY: pack
pack:
$(eval jarname = "pvbemu_`date +%Y%m%d`.jar")
@echo " Bundling into $(jarname)"
@jar -cfe $(jarname) Main *.class license.txt \
app images locale native util vue
# Performs a full build and packages it into a .jar
.PHONY: release
release:
@make -s build
@make -s pack
@echo " Removing temporary files"
@make -s clean_most
# Delete only Java .class files
.PHONY: clean_desktop
clean_desktop:
@rm -r -f *.class app util vue
# Delete everything but the .jar
.PHONY: clean_most
clean_most: clean_desktop
@rm -f src/desktop/vue/vue_NativeVue.h native/*.dll native/*.so
###############################################################################
# Native Recipes #
###############################################################################
# JNI header file
src/desktop/vue/vue_NativeVue.h: src/desktop/vue/NativeVue.java
@javac -h src/desktop/vue -sourcepath src/desktop -d . \
src/desktop/vue/NativeVue.java
@sleep 3
# linux_x86
.PHONY: lin32_pre
lin32_pre: src/desktop/vue/vue_NativeVue.h
$(eval name = linux_x86)
$(eval prefix = `uname -m`-linux-gnu-)
$(eval include = -I$(include_linux) -I$(include_linux)/linux)
$(eval gccargs = -m32 -lm)
$(eval ext = .so)
.PHONY: lin32
lin32: lin32_pre native_common
# linux_x86-64
.PHONY: lin64_pre
lin64_pre: src/desktop/vue/vue_NativeVue.h
$(eval name = linux_x86-64)
$(eval prefix = `uname -m`-linux-gnu-)
$(eval include = -I$(include_linux) -I$(include_linux)/linux)
$(eval gccargs = -m64 -lm)
$(eval ext = .so)
.PHONY: lin64
lin64: lin64_pre native_common
# windows_x86
.PHONY: win32_pre
win32_pre: src/desktop/vue/vue_NativeVue.h
$(eval name = windows_x86)
$(eval prefix = i686-w64-mingw32-)
$(eval include = -I$(include_windows) -I$(include_windows)/win32)
$(eval ext = .dll)
.PHONY: win32
win32: win32_pre native_common
# windows_x86-64
.PHONY: win64_pre
win64_pre: src/desktop/vue/vue_NativeVue.h
$(eval name = windows_x86-64)
$(eval prefix = x86_64-w64-mingw32-)
$(eval include = -I$(include_windows) -I$(include_windows)/win32)
$(eval ext = .dll)
.PHONY: win64
win64: win64_pre native_common
# Common recipe for building native modules
.PHONY: native_common
native_common:
@echo " Building native module $(name)"
@$(prefix)gcc $(include) -Isrc/core/include $(gccargs) -s -shared -O2 \
-fno-strict-aliasing -fPIC -Werror \
-o native/$(name)$(ext) src/desktop/vue/NativeVue.c src/core/vue.c

0
native/.gitignore vendored Normal file
View File

1261
src/core/cpu.c Normal file

File diff suppressed because it is too large Load Diff

14
src/core/gamepak.c Normal file
View File

@ -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

295
src/core/include/vue.h Normal file
View File

@ -0,0 +1,295 @@
#ifndef __VUE_H__
#define __VUE_H__
#ifdef __cplusplus
extern "C" {
#endif
/* API management */
#ifndef VUEAPI
#define VUEAPI extern
#endif
/* Header includes */
#include <stddef.h>
#include <stdint.h>
/*****************************************************************************
* Constants *
*****************************************************************************/
/* Boolean values */
#define VUE_FALSE 0
#define VUE_TRUE 1
/* Memory access types */
#define VUE_S8 0
#define VUE_U8 1
#define VUE_S16 2
#define VUE_U16 3
#define VUE_S32 4
#define VUE_CANCEL 5
/* System register indexes */
#define VUE_ADTRE 25
#define VUE_CHCW 24
#define VUE_ECR 4
#define VUE_EIPC 0
#define VUE_EIPSW 1
#define VUE_FEPC 2
#define VUE_FEPSW 3
#define VUE_PIR 6
#define VUE_PSW 5
#define VUE_TKCW 7
/* Non-standard register indexes */
#define VUE_PC -1
#define VUE_JUMP_FROM -2
#define VUE_JUMP_TO -3
/* Program register indexes */
#define VUE_GP 4
#define VUE_HP 2
#define VUE_LP 31
#define VUE_SP 3
#define VUE_TP 5
/* Instruction IDs */
#define VUE_ILLEGAL -1
#define VUE_ADD_IMM 0
#define VUE_ADD_REG 1
#define VUE_ADDF_S 2
#define VUE_ADDI 3
#define VUE_AND 4
#define VUE_ANDBSU 5
#define VUE_ANDI 6
#define VUE_ANDNBSU 7
#define VUE_BCOND 8
#define VUE_CAXI 9
#define VUE_CLI 10
#define VUE_CMP_IMM 11
#define VUE_CMP_REG 12
#define VUE_CMPF_S 13
#define VUE_CVT_SW 14
#define VUE_CVT_WS 15
#define VUE_DIV 16
#define VUE_DIVF_S 17
#define VUE_DIVU 18
#define VUE_HALT 19
#define VUE_IN_B 20
#define VUE_IN_H 21
#define VUE_IN_W 22
#define VUE_JAL 23
#define VUE_JMP 24
#define VUE_JR 25
#define VUE_LD_B 26
#define VUE_LD_H 27
#define VUE_LD_W 28
#define VUE_LDSR 29
#define VUE_MOV_IMM 30
#define VUE_MOV_REG 31
#define VUE_MOVBSU 32
#define VUE_MOVEA 33
#define VUE_MOVHI 34
#define VUE_MPYHW 35
#define VUE_MUL 36
#define VUE_MULF_S 37
#define VUE_MULU 38
#define VUE_NOT 39
#define VUE_NOTBSU 40
#define VUE_OR 41
#define VUE_ORBSU 42
#define VUE_ORI 43
#define VUE_ORNBSU 44
#define VUE_OUT_B 45
#define VUE_OUT_H 46
#define VUE_OUT_W 47
#define VUE_RETI 48
#define VUE_REV 49
#define VUE_SAR_IMM 50
#define VUE_SAR_REG 51
#define VUE_SCH0BSD 52
#define VUE_SCH0BSU 53
#define VUE_SCH1BSD 54
#define VUE_SCH1BSU 55
#define VUE_SEI 56
#define VUE_SETF 57
#define VUE_SHL_IMM 58
#define VUE_SHL_REG 59
#define VUE_SHR_IMM 60
#define VUE_SHR_REG 61
#define VUE_ST_B 62
#define VUE_ST_H 63
#define VUE_ST_W 64
#define VUE_STSR 65
#define VUE_SUB 66
#define VUE_SUBF_S 67
#define VUE_TRAP 68
#define VUE_TRNC_SW 69
#define VUE_XB 70
#define VUE_XH 71
#define VUE_XOR 72
#define VUE_XORBSU 73
#define VUE_XORI 74
#define VUE_XORNBSU 75
/*****************************************************************************
* Types *
*****************************************************************************/
/* Forward references */
typedef struct Vue Vue;
/* Boolean */
typedef int vbool;
/* Access state */
typedef struct {
uint32_t address; /* CPU bus address */
int32_t value; /* Value read/to write */
int8_t fetch; /* Index of machine code unit */
int8_t type; /* Data type */
} VueAccess;
/* Instruction state */
typedef struct {
int32_t bits; /* Binary encoding */
int32_t disp; /* Displacement for jumps and branches */
int32_t imm; /* Immediate operand */
uint8_t cond; /* Condition for Bcond */
int8_t format; /* Binary format */
int8_t id; /* Library-specific identifier */
uint8_t opcode; /* Instruction opcode */
uint8_t reg1; /* Source/right register */
uint8_t reg2; /* Destination/left register */
uint8_t size; /* Number of bytes in encoding */
uint8_t subopcode; /* Instruction subopcode */
} VueInstruction;
/* Breakpoint handler callbacks */
typedef int32_t (*VueOnAccess )(Vue *, VueAccess *);
typedef int32_t (*VueOnException)(Vue *, uint16_t );
typedef int32_t (*VueOnExecute )(Vue *, VueInstruction *);
typedef int32_t (*VueOnFrame )(Vue * );
/* Emulation state */
struct Vue {
void *tag; /* Application reference */
int32_t breakCode; /* Application break code */
uint8_t wram[0x10000]; /* System memory */
/* Breakpoint handlers */
VueOnException onException;
VueOnExecute onExecute;
VueOnFrame onFrame;
VueOnAccess onRead;
VueOnAccess onWrite;
/* CPU state */
struct {
VueAccess access; /* Access state */
VueInstruction inst; /* Instruction state */
uint32_t cycles; /* Cycles until next stage */
int32_t jumpFrom[3]; /* Source PCs of most recent jumps */
int32_t jumpTo [3]; /* Destination PCs of most recent jumps */
uint16_t exception; /* Exception code */
uint16_t irq; /* Interrupt lines */
int fetch; /* Fetch unit index */
int stage; /* Current processing stage */
/* Program registers */
int32_t program[32];
/* System registers */
int32_t adtre; /* Address Trap Register for Execution */
int32_t eipc; /* Exception/interrupt PC */
int32_t eipsw; /* Exception/interrupt PSW */
int32_t fepc; /* Duplexed exception PC */
int32_t fepsw; /* Duplexed exception PSW */
int32_t pc; /* Program Counter */
int32_t sr29; /* System register 29 */
int32_t sr31; /* System register 31 */
/* Program Status Word */
int8_t psw_ae; /* Address Trap Enable */
int8_t psw_ep; /* Exception Pending */
int8_t psw_id; /* Interrupt Disable */
int8_t psw_cy; /* Carry */
int8_t psw_fiv; /* Floating Reserved Operand */
int8_t psw_fov; /* Floating Overflow */
int8_t psw_fpr; /* Floating Precision */
int8_t psw_fro; /* Floating Reserved Operand */
int8_t psw_fud; /* Floating Underflow */
int8_t psw_fzd; /* Floating Zero Divide */
int8_t psw_i; /* Interrupt Level */
int8_t psw_np; /* NMI Pending */
int8_t psw_ov; /* Overflow */
int8_t psw_s; /* Sign */
int8_t psw_z; /* Zero */
/* Cache Control Word */
uint16_t chcw_cec; /* Clear Entry Count */
uint16_t chcw_cen; /* Clear Entry Number */
int8_t chcw_icc; /* Instruction Cache Clear */
int8_t chcw_icd; /* Instruction Cache Dump */
int8_t chcw_ice; /* Instruction Cache Enable */
int8_t chcw_icr; /* Instruction Cache Restore */
int32_t chcw_sa; /* Spill-Out Base Address */
/* Exception Cause Register */
uint16_t ecr_eicc; /* Exception/Interrupt Cause Code */
uint16_t ecr_fecc; /* Fatal Error Cause Code */
} cpu;
/* Game pak state */
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;
/* VIP state */
struct {
uint8_t vram[0x40000]; /* Video memory */
} vip;
};
/*****************************************************************************
* Function Prototypes *
*****************************************************************************/
VUEAPI uint32_t vueEmulate (Vue *vue, uint32_t maxCycles);
VUEAPI int32_t vueGetBreakCode (Vue *vue);
VUEAPI uint16_t vueGetExceptionCode (Vue *vue);
VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system);
VUEAPI void* vueGetTag (Vue *vue);
VUEAPI void vueInitialize (Vue *vue);
VUEAPI VueOnException vueOnException (Vue *vue, VueOnException callback);
VUEAPI VueOnExecute vueOnExecute (Vue *vue, VueOnExecute callback);
VUEAPI VueOnFrame vueOnFrame (Vue *vue, VueOnFrame callback);
VUEAPI VueOnAccess vueOnRead (Vue *vue, VueOnAccess callback);
VUEAPI VueOnAccess vueOnWrite (Vue *vue, VueOnAccess callback);
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 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 void* vueSetTag (Vue *vue, void *tag);
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);
#ifdef __cplusplus
}
#endif
#endif /* __VUE_H__ */

298
src/core/vip.c Normal file
View File

@ -0,0 +1,298 @@
/* This file is included through vue.c and cannot be built directly. */
#ifdef VUEAPI
/*****************************************************************************
* Internal Functions *
******************************************************************************/
/* Process the simulation */
static void vipEmulate(Vue *vue, uint32_t cycles) {
vue = vue;
cycles = cycles;
}
/* Read an I/O register */
static int32_t vipReadRegister(Vue *vue, int32_t address) {
vue=vue;
/* Process by register */
switch (address & ~1) {
case 0x0005F800: break; /* INTPND */
case 0x0005F802: break; /* INTENB */
case 0x0005F804: break; /* INTCLR */
case 0x0005F820: break; /* DPSTTS */
case 0x0005F822: break; /* DPCTRL */
case 0x0005F824: break; /* BRTA */
case 0x0005F826: break; /* BRTB */
case 0x0005F828: break; /* BRTC */
case 0x0005F82A: break; /* REST */
case 0x0005F82E: break; /* FRMCYC */
case 0x0005F830: break; /* CTA */
case 0x0005F840: break; /* XPSTTS */
case 0x0005F842: break; /* XPCTRL */
case 0x0005F844: break; /* VER */
case 0x0005F848: break; /* SPT0 */
case 0x0005F84A: break; /* SPT1 */
case 0x0005F84C: break; /* SPT2 */
case 0x0005F84E: break; /* SPT3 */
case 0x0005F860: break; /* GPLT0 */
case 0x0005F862: break; /* GPLT1 */
case 0x0005F864: break; /* GPLT2 */
case 0x0005F866: break; /* GPLT3 */
case 0x0005F868: break; /* JPLT0 */
case 0x0005F86A: break; /* JPLT1 */
case 0x0005F86C: break; /* JPLT2 */
case 0x0005F86E: break; /* JPLT3 */
case 0x0005F870: break; /* BKCOL */
}
/* Unmapped */
return 0;
}
/* Read an I/O register */
static void vipWriteRegister(Vue *vue, int32_t address, int32_t value) {
vue=vue;
value=value;
/* Process by register */
switch (address & ~1) {
case 0x0005F800: break; /* INTPND */
case 0x0005F802: break; /* INTENB */
case 0x0005F804: break; /* INTCLR */
case 0x0005F820: break; /* DPSTTS */
case 0x0005F822: break; /* DPCTRL */
case 0x0005F824: break; /* BRTA */
case 0x0005F826: break; /* BRTB */
case 0x0005F828: break; /* BRTC */
case 0x0005F82A: break; /* REST */
case 0x0005F82E: break; /* FRMCYC */
case 0x0005F830: break; /* CTA */
case 0x0005F840: break; /* XPSTTS */
case 0x0005F842: break; /* XPCTRL */
case 0x0005F844: break; /* VER */
case 0x0005F848: break; /* SPT0 */
case 0x0005F84A: break; /* SPT1 */
case 0x0005F84C: break; /* SPT2 */
case 0x0005F84E: break; /* SPT3 */
case 0x0005F860: break; /* GPLT0 */
case 0x0005F862: break; /* GPLT1 */
case 0x0005F864: break; /* GPLT2 */
case 0x0005F866: break; /* GPLT3 */
case 0x0005F868: break; /* JPLT0 */
case 0x0005F86A: break; /* JPLT1 */
case 0x0005F86C: break; /* JPLT2 */
case 0x0005F86E: break; /* JPLT3 */
case 0x0005F870: break; /* BKCOL */
}
}
/* Read a value from the CPU bus */
static int32_t vipRead(Vue *vue, int32_t address, int32_t type) {
int32_t value; /* Register value */
address &= 0x0007FFFF;
/* VRAM */
if (address < 0x00040000)
return readBuffer(vue->vip.vram, 0x40000, address, type);
/* Mirrors of character memory */
if (address >= 0x00078000)
return readBuffer(vue->vip.vram, 0x40000,
(address - 0x00078000) >> 13 << 15 | 0x00006000 |
(address & 0x00001FFF),
type);
/* I/O register or unmapped */
value = vipReadRegister(vue, address);
if (type < 2 && (address & 1) == 1)
value >>= 8;
switch (type) {
case VUE_S8 : return SIGN_EXTEND(value, 8);
case VUE_U8 : return value & 0x000000FF;
case VUE_S16: return SIGN_EXTEND(value, 16);
case VUE_S32: return value | vipReadRegister(vue, address + 2) << 16;
}
return value; /* U16 */
}
/* Read bytes from the CPU bus */
static void vipReadBytes(Vue *vue, int address, uint8_t *dest, int length) {
int32_t count, size, value; /* Working variables */
address &= 0x0007FFFF;
/* Perform the operation */
while (length > 0) {
/* VRAM */
if (address < 0x00040000) {
count = 0x00040000 - address;
if (length < count)
count = length;
readBytes(vue->vip.vram, 0x40000, address, dest, count);
}
/* Mirrors of character memory */
else if (address >= 0x00078000) {
count = 0x2000 - (address & 0x1FFF);
if (length < count)
count = length;
readBytes(vue->vip.vram, 0x40000,
(address - 0x00078000) >> 13 << 15 | 0x00006000 |
(address & 0x00001FFF),
dest, count);
}
/* I/O register or unmapped */
else {
count = 0x00078000 - address;
if (length < count)
count = length;
/* Read all registers in the range */
while (count > 0) {
value = vipReadRegister(vue, address);
/* Odd address */
if ((address & 1) == 1) {
*dest = value >> 8;
address++;
count --;
length --;
dest ++;
continue;
}
/* Even address */
size = count == 1 ? 1 : 2;
*dest = value;
if (size == 2)
dest[1] = value >> 8;
address += size;
count -= size;
length -= size;
dest += size;
}
continue;
}
/* Advance to the next region */
address += count;
length -= count;
dest += count;
}
}
/* System reset */
static void vipReset(Vue *vue) {
vue = vue;
}
/* Determine the number of CPU cycles until a breakpoint could trigger */
static uint32_t vipUntil(Vue *vue, uint32_t cycles) {
vue = vue;
return cycles;
}
/* Write a value to the CPU bus */
static void vipWrite(Vue *vue, int32_t address, int32_t type, int32_t value) {
address &= 0x0007FFFF;
/* VRAM */
if (address < 0x00040000) {
writeBuffer(vue->vip.vram, 0x40000, address, type, value);
return;
}
/* Mirrors of character memory */
if (address >= 0x00078000) {
writeBuffer(vue->vip.vram, 0x40000,
(address - 0x00078000) >> 13 << 15 | 0x00006000 |
(address & 0x00001FFF),
type, value);
return;
}
/* I/O register or unmapped */
if (type < 2 && (address & 1) == 1)
value <<= 8;
vipWriteRegister(vue, address, value);
if (type == VUE_S32)
vipWriteRegister(vue, address + 2, value >> 16);
}
/* Write bytes to the CPU bus */
static void vipWriteBytes(Vue *vue, int address, uint8_t *src, int length) {
int32_t count, size, value; /* Working variables */
address &= 0x0007FFFF;
/* Perform the operation */
while (length > 0) {
/* VRAM */
if (address < 0x00040000) {
count = 0x00040000 - address;
if (length < count)
count = length;
writeBytes(vue->vip.vram, 0x40000, address, src, count);
}
/* Mirrors of character memory */
else if (address >= 0x00078000) {
count = 0x2000 - (address & 0x1FFF);
if (length < count)
count = length;
writeBytes(vue->vip.vram, 0x40000,
(address - 0x00078000) >> 13 << 15 | 0x00006000 |
(address & 0x00001FFF),
src, count);
}
/* I/O register or unmapped */
else {
count = 0x00078000 - address;
if (length < count)
count = length;
/* Read all registers in the range */
while (count > 0) {
value = *src;
/* Odd address */
if ((address & 1) == 1) {
vipWriteRegister(vue, address, value << 8);
address++;
count --;
length --;
src ++;
continue;
}
/* Even address */
size = count == 1 ? 1 : 2;
if (size == 2)
value |= src[1] << 8;
vipWriteRegister(vue, address, value);
address += size;
count -= size;
length -= size;
src += size;
}
continue;
}
/* Advance to the next region */
address += count;
length -= count;
src += count;
}
}
#endif

458
src/core/vue.c Normal file
View File

@ -0,0 +1,458 @@
#define VUEAPI
#include <vue.h>
/*****************************************************************************
* Macros *
*****************************************************************************/
/* Sign-extend a value */
#define SIGN_EXTEND(bits, value) \
((value) & 1 << (bits - 1) ? (value) | (~(1 << bits) + 1) : (value))
/*****************************************************************************
* Constants *
*****************************************************************************/
/* Memory access type sizes */
static const uint32_t TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
/*****************************************************************************
* 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) == 0) {
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 big-endian */
#ifdef VUE_BIGENDIAN
switch (type) {
case VUE_S32:
value = (value >> 16 & 0x0000FFFF) | value << 16;
/* Fallthrough */
case VUE_S16: /* Fallthrough */
case VUE_U16:
value = (value >> 8 & 0x00FF00FF) | (value << 8 & 0xFF00FF00);
}
#endif
/* Processing by data type */
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;
}
}
/* 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++;
}
/*****************************************************************************
* Component Includes *
*****************************************************************************/
#include "cpu.c"
#include "gamepak.c"
#include "vip.c"
/*****************************************************************************
* Library Functions *
*****************************************************************************/
/* Process the simulation */
uint32_t vueEmulate(Vue *vue, uint32_t maxCycles) {
uint32_t cycles; /* Number of cycles to process */
/* Process up to the given number of cycles */
do {
/* Determine the number of cycles where no breakpoint will occur */
cycles = maxCycles; /* min(maxCycles, nextFrameCycles) */
cycles = cpuUntil (vue, cycles);
/*cycles = padUntil (vue, cycles);*/
/*cycles = linkUntil (vue, cycles);*/
/*cycles = timerUntil(vue, cycles);*/
cycles = vipUntil (vue, cycles);
/* Process all system components */
vue->breakCode = 0;
cpuEmulate (vue, cycles);
/*padEmulate (vue, cycles);*/
/*pakEmulate (vue, cycles);*/
/*linkEmulate (vue, cycles);*/
/*timerEmulate(vue, cycles);*/
vipEmulate (vue, cycles);
/*vsuEmulate (vue, cycles);*/
maxCycles -= cycles;
} while (vue->breakCode == 0 && maxCycles != 0);
/* A break condition has occurred */
return maxCycles;
}
/* Retrieve the most recent applicaiton break code */
int32_t vueGetBreakCode(Vue *vue) {
return vue == NULL ? 0 : vue->breakCode;
}
/* Retrieve the most recent exception code */
uint16_t vueGetExceptionCode(Vue *vue) {
return vue == NULL ? 0 : vue->cpu.exception;
}
/* Retrieve the value of a register */
int32_t vueGetRegister(Vue *vue, int32_t index, vbool system) {
/* Error checking */
if (vue == NULL)
return 0;
/* Non-indexed registers */
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];
case VUE_PC : return vue->cpu.pc;
}
/* Indexed registers */
return
index < 0 || index > 31 ? 0 :
system ? cpuGetSystemRegister(vue, index) :
vue->cpu.program[index]
;
}
/* Retrieve the application reference */
void* vueGetTag(Vue *vue) {
return vue == NULL ? NULL : vue->tag;
}
/* Prepare an emulation state context for use */
void vueInitialize(Vue *vue) {
if (vue == NULL)
return;
vue->onException = NULL;
vue->onExecute = NULL;
vue->onFrame = NULL;
vue->onRead = NULL;
vue->onWrite = NULL;
vue->pak.ram = NULL;
vue->pak.rom = NULL;
vueReset(vue);
}
/* Specify an exception breakpoint callback */
VueOnException vueOnException(Vue *vue, VueOnException callback) {
VueOnException ret;
if (vue == NULL)
return NULL;
ret = vue->onException;
vue->onException = callback;
return ret;
}
/* Specify an execute breakpoint callback */
VueOnExecute vueOnExecute(Vue *vue, VueOnExecute callback) {
VueOnExecute ret;
if (vue == NULL)
return NULL;
ret = vue->onExecute;
vue->onExecute = callback;
return ret;
}
/* Specify a frame breakpoint callback */
VueOnFrame vueOnFrame(Vue *vue, VueOnFrame callback) {
VueOnFrame ret;
if (vue == NULL)
return NULL;
ret = vue->onFrame;
vue->onFrame = callback;
return ret;
}
/* Specify a read breakpoint callback */
VueOnAccess vueOnRead(Vue *vue, VueOnAccess callback) {
VueOnAccess ret;
if (vue == NULL)
return NULL;
ret = vue->onRead;
vue->onRead = callback;
return ret;
}
/* Specify a write breakpoint callback */
VueOnAccess vueOnWrite(Vue *vue, VueOnAccess callback) {
VueOnAccess ret;
if (vue == NULL)
return NULL;
ret = vue->onWrite;
vue->onWrite = callback;
return ret;
}
/* Read a value from the CPU bus */
int32_t vueRead(Vue *vue, uint32_t address, int32_t type) {
/* Error checking */
if (vue == NULL || type < 0 || type > 4)
return 0;
/* Perform the operation */
switch (address >> 24 & 7) {
case 0: return vipRead (vue , address,type);
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 */
vbool vueReadBytes(Vue *vue, uint32_t address, uint8_t *dest, uint32_t length){
uint32_t count; /* Bytes to read in one iteration */
/* Error checking */
if (vue == NULL || dest == NULL)
return VUE_FALSE;
/* Perform the operation */
while (length > 0) {
count = 0x01000000 - (address & 0x00FFFFFF);
if (length < count)
count = length;
switch (address >> 24 & 7) {
case 0: vipReadBytes(vue ,
address, dest, count); break;
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;
}
address += count;
length -= count;
dest += count;
}
return VUE_TRUE;
}
/* Initialize all system components */
void vueReset(Vue *vue) {
uint32_t x; /* Iterator */
/* Error checking */
if (vue == NULL)
return;
/* Reset state */
cpuReset(vue);
pakReset(vue);
for (x = 0; x < 0x10000; x++)
vue->wram[x] = 0;
}
/* Specify a value for a register */
int32_t vueSetRegister(Vue *vue, int32_t index, vbool system, int32_t value) {
/* Error checking */
if (vue == NULL)
return 0;
/* PC */
if (index == VUE_PC && system) {
if (vue->cpu.stage == CPU_EXECUTE || vue->cpu.stage == CPU_FETCH) {
vue->cpu.fetch = -1;
vue->cpu.stage = CPU_FETCH;
}
return vue->cpu.pc = value & 0xFFFFFFFE;
}
/* Other */
return
index < 0 || index > 31 ? 0 :
system ? cpuSetSystemRegister(vue, index, value, VUE_TRUE) :
index == 0 ? 0 : (vue->cpu.program[index] = value)
;
}
/* 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 */
vue->pak.rom = rom;
vue->pak.romSize = size;
return VUE_TRUE;
}
/* Specify a new application reference */
void* vueSetTag(Vue *vue, void *tag) {
void *ret;
if (vue == NULL)
return NULL;
ret = vue->tag;
vue->tag = tag;
return ret;
}
/* Write a value to the CPU bus */
void vueWrite(Vue *vue, uint32_t address, int32_t type, int32_t value) {
/* Error checking */
if (vue == NULL)
return;
/* Perform the operation */
switch (address >> 24 & 7) {
case 0: vipWrite(vue ,
address, type, value); break;
case 5: writeBuffer(vue->wram , 0x10000,
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 */
vbool vueWriteBytes(Vue *vue, uint32_t address, uint8_t *src, uint32_t length){
uint32_t count; /* Bytes to write in one iteration */
/* Error checking */
if (vue == NULL || src == NULL)
return VUE_FALSE;
/* Perform the operation */
while (length > 0) {
count = 0x01000000 - (address & 0x00FFFFFF);
if (length < count)
count = length;
switch (address >> 24 & 7) {
case 0: vipWriteBytes(vue ,
address, src, count); break;
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;
}
address += count;
length -= count;
src += count;
}
return VUE_TRUE;
}

55
src/desktop/Main.java Normal file
View File

@ -0,0 +1,55 @@
// Java imports
import java.io.*;
// Project imports
import app.*;
import util.*;
import vue.*;
// Desktop application primary class
public class Main {
// Program entry point
public static void main(String[] args) {
// Configure Swing look-and-feel
Util.setSystemLAF();
// Load the native module, if available
for (String filename : Util.listFiles("native")) {
File file = null;
// Skip any file that isn't a shared object library
if (!filename.endsWith(".dll") && !filename.endsWith(".so"))
continue;
// Write the contents of the native object file
FileOutputStream stream = null;
try {
file = File.createTempFile("native", "." + filename);
stream = new FileOutputStream(file);
stream.write(Util.fileRead("native/" + filename));
} catch (Exception e) { file = null; }
try { stream.close(); } catch (Exception e) { }
// Load the native object file into the JVM
try {
System.load(file.getAbsolutePath());
Vue.setNativeID(filename.substring(0,
filename.lastIndexOf(".")));
break;
}
catch (Error e) { }
}
// Use the native module
boolean useNative = false;
for (String arg : args)
if (arg.trim().toLowerCase().equals("native"))
useNative = true;
// Begin application operations
new App(useNative);
}
}

174
src/desktop/app/App.java Normal file
View File

@ -0,0 +1,174 @@
package app;
// Java imports
import java.awt.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
import vue.*;
// Top-level software state manager
public class App {
// Instance fields
private boolean useNative; // Produce native core contexts
private Localizer.Locale[] locales; // Language translations
private ArrayList<MainWindow> windows; // Application windows
// Configuration fields
Util.Font fntDialog; // Dialog font
Util.Font fntMono; // Monospaced font
int hexDigitWidth; // Width in pixels of one hex digit
Localizer localizer; // UI localization manager
int[][] rgbBase; // Base anaglyph filter colors
int rgbClear; // Transparent pixel color
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Anaglyph presets
static final int[] RED = { 0xFF, 0x00, 0x00 };
static final int[] CYAN = { 0x00, 0xC6, 0xF0 };
static final int[] GREEN = { 0x00, 0xB4, 0x00 };
static final int[] MAGENTA = { 0xC8, 0x00, 0xFF };
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
public App(boolean useNative) {
// Configure instance fields
localizer = new Localizer();
windows = new ArrayList<MainWindow>();
// Configure config fields
fntDialog = new Util.Font(new JLabel().getFont());
fntMono = new Util.Font(new Font(Util.fontFamily(new String[]
{ "Consolas", Font.MONOSPACED } ), Font.PLAIN, 14));
hexDigitWidth = hexDigitWidth();
rgbBase = new int[][] {
Arrays.copyOf(GREEN , 4),
Arrays.copyOf(MAGENTA, 4),
Arrays.copyOf(RED , 4)
};
rgbClear = 0x555555;
// Additional processing
setUseNative(useNative);
initLocales();
addWindow();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Add a new program window
void addWindow() {
windows.add(new MainWindow(this));
windowsChanged();
}
// Determine whether the native module is in use
boolean getUseNative() {
return useNative;
}
// Retrieve a list of registered locales
Localizer.Locale[] listLocales() {
var ret = new Localizer.Locale[locales.length];
System.arraycopy(locales, 0, ret, 0, locales.length);
return ret;
}
// Determine the number of a window in the collection
int numberOf(MainWindow window) {
return windows.indexOf(window) + 1;
}
// Remove a program window
void removeWindow(MainWindow window) {
windows.remove(window);
windowsChanged();
}
// Specify whether using the native module
boolean setUseNative(boolean useNative) {
return this.useNative = useNative && Vue.getNativeID() != null;
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Calculate the maximum hexadecimal digit width
private int hexDigitWidth() {
int width = 0;
for (var digit : "0123456789ABCDEFabcdef".toCharArray())
width = Math.max(width, fntMono.metrics.charWidth(digit));
return width;
}
// Load and parse all locale translations
private void initLocales() {
// Process all locale files
var locales = new HashMap<String, Localizer.Locale>();
for (String file : Util.listFiles("locale")) {
// Process the file
try {
var locale = Localizer.parse(Util.textRead("locale/" + file));
String key = locale.id.toLowerCase();
// Register the locale
if (locales.containsKey(key)) throw new RuntimeException(
"Locale with ID '" + locale.id + "' already registered");
locales.put(key, locale);
// The locale matches the one in the config
if (key.equals("en-us"))
localizer.setLocale(locale);
}
// Could not process the file
catch (Exception e) {
System.err.println("Error parsing locale " +
file + ": " + e.getMessage());
}
}
// Produce the list of registered locales
this.locales = locales.values().toArray(
new Localizer.Locale[locales.size()]);
Arrays.sort(this.locales);
// Select a default locale
if (localizer.getLocale() != null || this.locales.length == 0)
return;
var locale = locales.get("en-us");
localizer.setLocale(locale != null ? locale : this.locales[0]);
}
// A window has been added to or removed from the program state
private void windowsChanged() {
int count = windows.size();
for (int x = 0; x < count; x++)
windows.get(x).windowsChanged(x + 1, count == 1);
}
}

View File

@ -0,0 +1,512 @@
package app;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
// Project imports
import util.*;
import vue.*;
// VIP backgrounds window
class BGMapsWindow extends ChildWindow {
// Instance fields
private int cellIndex; // Current cell index
private int character; // Cell character index
private Point dragging; // Most recent pattern mouse position
private boolean generic; // Use the generic palette
private boolean grid; // Draw a grid around characters
private boolean hFlip; // Cell is flipped horizontally
private int mapIndex; // Current BG map index
private int palette; // Palette index
private int[] pix; // Image buffer
private int scale; // Display scale
private boolean vFlip; // Cell is flipped vertically
private BufferedImage image; // BG map graphic
// UI components
private JCheckBox chkGeneric; // Generic colors check box
private JCheckBox chkGrid; // Grid check box
private JCheckBox chkHFlip; // H-flip check box
private JCheckBox chkVFlip; // V-flip check box
private UComboBox cmbPalette; // Palette drop-down
private UPanel client; // Client area
private UPanel panCell; // Cell panel
private UPanel panMap; // BG map panel
private JScrollPane scrControls; // Controls panel
private JScrollPane scrMap; // BG map container
private JSlider sldScale; // Scale slider
private JSpinner spnCellIndex; // Cell index spinner
private JSpinner spnCharacter; // Character spinner
private JSpinner spnMapIndex; // BG map index spinner
private JTextField txtCellAddress; // Cell address text box
private JTextField txtMapAddress; // BG map address text box
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
BGMapsWindow(MainWindow parent) {
super(parent, "bg_maps.title");
// Configure instance fields
generic = false;
image = new BufferedImage(512, 512, BufferedImage.TYPE_INT_RGB);
pix = new int[512 * 512];
scale = 2;
// Configure client area
client = new UPanel(new BorderLayout());
client.setBackground(SystemColor.control);
client.setFocusable(true);
client.setPreferredSize(new Dimension(480, 360));
client.addComponentListener(Util.onResize(e->onResize()));
// Configure controls panel
var ctrls = new UPanel(new GridBagLayout());
ctrls.setBackground(SystemColor.control);
ctrls.addMouseListener(
Util.onMouse(e->client.requestFocus(), null));
scrControls = new JScrollPane(ctrls,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrControls.setBorder(null);
scrControls.getVerticalScrollBar().setUnitIncrement(20);
client.add(scrControls, BorderLayout.WEST);
// BG Map controls
label(ctrls, "bg_maps.map", true);
spnMapIndex = spinner(ctrls, 0, 15, 0, true);
spnMapIndex.addChangeListener(e->
setMap((Integer) spnMapIndex.getValue()));
label(ctrls, "bg_maps.address", false);
txtMapAddress = textBox(ctrls);
txtMapAddress.addFocusListener(Util.onFocus(null,
e->onAddress(txtMapAddress)));
// Cell controls container
var cell = new UPanel(new GridBagLayout());
cell.setBorder(new TitledBorder(""));
parent.app.localizer.add(cell, "bg_maps.cell");
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 2, 2, 2);
ctrls.add(cell, gbc);
// Cell controls
label(cell, "bg_maps.index", true);
spnCellIndex = spinner(cell, 0, 4095, 0, true);
spnCellIndex.addChangeListener(e->
setCell((Integer) spnCellIndex.getValue()));
label(cell, "bg_maps.address", false);
txtCellAddress = textBox(cell);
txtCellAddress.addFocusListener(Util.onFocus(null,
e->onAddress(txtCellAddress)));
label(cell, "bg_maps.character", false);
spnCharacter = spinner(cell, 0, 2047, 0, false);
spnCharacter.addChangeListener(e->onEdit());
label(cell, "bg_maps.palette", false);
cmbPalette = select(cell, new String[] {
"palette.gplt0", "palette.gplt1", "palette.gplt2", "palette.gplt3"
});
cmbPalette.addActionListener(e->onEdit());
label(cell, "bg_maps.hflip", false);
chkHFlip = checkBox(cell);
chkHFlip.addActionListener(e->onEdit());
label(cell, "bg_maps.vflip", false);
chkVFlip = checkBox(cell);
chkVFlip.addActionListener(e->onEdit());
// Cell panel
panCell = new UPanel();
panCell.setOpaque(false);
panCell.setPreferredSize(new Dimension(96, 96));
panCell.addPaintListener((g,w,h)->onPaintCell(g, w, h));
gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.CENTER;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(2, 2, 2, 2);
cell.add(panCell, gbc);
terminator(cell);
// Fill the extra space above the view controls
var spacer = new UPanel();
spacer.setOpaque(false);
gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weighty = 1;
ctrls.add(spacer, gbc);
// View controls
label(ctrls, "bg_maps.grid", false);
chkGrid = checkBox(ctrls);
chkGrid.addActionListener(e->{
grid = chkGrid.isSelected(); onView(); });
label(ctrls, "bg_maps.generic", false);
chkGeneric = checkBox(ctrls);
chkGeneric.setSelected(generic);
chkGeneric.addActionListener(e->{
generic = chkGeneric.isSelected(); refresh(); });
label(ctrls, "bg_maps.scale", false);
sldScale = slider(ctrls, 1, 10, scale);
sldScale.addChangeListener(e->{
scale = sldScale.getValue(); onView(); });
terminator(ctrls);
// Configure BG map panel
panMap = new UPanel();
panMap.setBackground(SystemColor.control);
panMap.addMouseListener(
Util.onMouse(e->onMouse(e), e->onMouse(e)));
panMap.addMouseMotionListener(
Util.onMouseMove(null, e->onMouse(e)));
panMap.addPaintListener((g,w,h)->onPaintMap(g, w, h));
scrMap = new JScrollPane(panMap);
client.add(scrMap, BorderLayout.CENTER);
// Configure component
setContentPane(client);
setCell(0);
onView();
pack();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Update the display
void refresh() {
// Update BG map graphic
int src = 0x00020000 | mapIndex << 13;
for (int index = 0; index < 4096; index++, src += 2) {
int bits = parent.vram[src] & 0xFF | parent.vram[src + 1] << 8;
var pal = parent.palettes[generic ? MainWindow.GENERIC :
MainWindow.GPLT0 + (bits >> 14 & 3)][MainWindow.RED];
parent.drawCharacter(
bits & 0x07FF, (bits & 0x2000) != 0, (bits & 0x1000) != 0, pal,
pix, index >> 6 << 12 | (index & 63) << 3, 512
);
}
image.setRGB(0, 0, 512, 512, pix, 0, 512);
// Update display;
panCell.repaint();
panMap .repaint();
}
///////////////////////////////////////////////////////////////////////////
// Event Handlers //
///////////////////////////////////////////////////////////////////////////
// Address text boxes commit
private void onAddress(JTextField src) {
int address;
// Parse the given address
try { address = (int) Long.parseLong(src.getText(), 16); }
catch (Exception e) {
setMap(mapIndex);
return;
}
// Restrict address range
if ((address >> 24 & 7) != 0) {
setMap(mapIndex);
return;
}
address &= 0x0007FFFF;
// Check if the address is in BG map memory
if (address < 0x00020000 || address >= 0x00040000) {
setMap(mapIndex);
return;
}
// Update the map and character indexes
address -= 0x00020000;
mapIndex = address >> 13;
setCell((address & 0x00001FFE) >> 1);
}
// A cell value has changed
private void onEdit() {
// Process cell properties
character = (Integer) spnCharacter.getValue();
hFlip = chkHFlip.isSelected();
palette = cmbPalette.getSelectedIndex();
vFlip = chkVFlip.isSelected();
// Update VRAM state
parent.vue.write(
0x00020000 | mapIndex << 13 | cellIndex << 1, Vue.U16,
palette << 14 | (hFlip ? 0x2000 : 0) |
(vFlip ? 0x1000 : 0) | character
);
parent.refreshDebug(false);
}
// BG map panel mouse
private void onMouse(MouseEvent e) {
int id = e.getID();
// Mouse release
if (id == MouseEvent.MOUSE_RELEASED) {
if (e.getButton() == MouseEvent.BUTTON1)
dragging = null;
return;
}
// Mouse press
if (id == MouseEvent.MOUSE_PRESSED) {
client.requestFocus();
if (e.getButton() != MouseEvent.BUTTON1)
return;
dragging = e.getPoint();
}
// Not dragging
if (dragging == null)
return;
// Working variables
int grid = this.grid ? 1 : 0;
int size = 8 * scale + grid;
int col = e.getX() / size;
int row = e.getY() / size;
// The pixel is not within a cell
if (col >= 64 || row >= 64)
return;
// Calculate the index of the selected cell
setCell(row << 6 | col);
}
// Cell panel paint
private void onPaintCell(Graphics2D g, int width, int height) {
int scale = Math.max(1, Math.min(width >> 3, height >> 3));
int x = (width - (scale << 3) + 1) >> 1;
int y = (height - (scale << 3) + 1) >> 1;
int ix = (cellIndex & 63) << 3;
int iy = cellIndex >> 6 << 3;
g.drawImage(image,
x, y, x + scale * 8, y + scale * 8,
ix, iy, ix + 8 , iy + 8 ,
null);
}
// BG map panel paint
private void onPaintMap(Graphics2D g, int width, int height) {
// Drawing with a grid
if (grid) {
int scale = 8 * this.scale;
for (int y = 0, z = 0; y < 64; y++)
for (int x = 0; x < 64; x++, z++) {
int ix = (z & 63) << 3;
int iy = z >> 6 << 3;
int ox = x * (scale + 1);
int oy = y * (scale + 1);
g.drawImage(image,
ox, oy, ox + scale, oy + scale,
ix, iy, ix + 8 , iy + 8 ,
null);
}
}
// Not drawing with a grid
else g.drawImage(image, 0, 0, 512 * scale, 512 * scale, null);
// Highlight the selected cell
int size = 8 * scale + (grid ? 1 : 0);
int X = (cellIndex & 63) * size;
int Y = (cellIndex >> 6) * size;
int light = SystemColor.textHighlight.getRGB() & 0x00FFFFFF;
g.setColor(new Color(0xD0000000 | light, true));
g.drawRect(X , Y , 8 * scale - 1, 8 * scale - 1);
g.setColor(new Color(0x90000000 | light, true));
g.drawRect(X + 1, Y + 1, 8 * scale - 3, 8 * scale - 3);
g.setColor(new Color(0x50000000 | light, true));
g.fillRect(X + 2, Y + 2, 8 * scale - 4, 8 * scale - 4);
}
// Window resize
private void onResize() {
var viewport = scrControls.getViewport();
int inner = viewport.getView().getPreferredSize().width;
int outer = viewport.getExtentSize().width;
// Size the controls container to match the inner component
if (inner != outer) {
scrControls.setPreferredSize(new Dimension(
scrControls.getPreferredSize().width + inner - outer, 0));
scrControls.revalidate();
scrControls.repaint();
}
}
// View control changed
private void onView() {
int grid = this.grid ? 1 : 0;
int size = 8 * scale + grid;
int pref = size * 64 - grid;
scrMap.getVerticalScrollBar().setUnitIncrement(8 * scale + grid);
panMap.setPreferredSize(new Dimension(pref, pref));
panMap.revalidate();
panMap.repaint();
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Add a check box to the controls panel
private JCheckBox checkBox(UPanel panel) {
var chk = new JCheckBox();
chk.setBorder(null);
chk.setFocusable(false);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 2, 2);
panel.add(chk, gbc);
return chk;
}
// Add a label to the controls panel
private void label(UPanel panel, String key, boolean top) {
var lbl = new JLabel();
parent.app.localizer.add(lbl, key);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(top ? 2 : 0, 2, 2, 2);
panel.add(lbl, gbc);
}
// Update controls
private void refreshLite() {
int mapAddr = mapIndex << 13 | 0x00020000;
int cellAddr = mapAddr | cellIndex << 1;
int bits = parent.vram[cellAddr] & 0xFF | parent.vram[cellAddr+1] << 8;
character = bits & 0x07FF;
hFlip = (bits & 0x2000) != 0;
palette = bits >> 14 & 3;
vFlip = (bits & 0x1000) != 0;
chkHFlip .setSelected(hFlip);
chkVFlip .setSelected(vFlip);
cmbPalette .setSelectedIndex(palette);
spnCellIndex .setValue(cellIndex);
spnCharacter .setValue(character);
spnMapIndex .setValue(mapIndex);
txtMapAddress .setText(String.format("%08X", mapAddr ));
txtCellAddress.setText(String.format("%08X", cellAddr));
panCell.repaint();
panMap .repaint();
}
// Add a combo box to the controls panel
private UComboBox select(UPanel panel, String[] options) {
var cmb = new UComboBox();
parent.app.localizer.add(cmb, options);
cmb.setSelectedIndex(0);
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 2, 2);
panel.add(cmb, gbc);
return cmb;
}
// Specify the current cell index
private void setCell(int index) {
cellIndex = index;
refreshLite();
}
// Specify the current BG map index
private void setMap(int index) {
mapIndex = index;
refreshLite();
}
// Add a slider to the controls panel
private JSlider slider(UPanel panel, int min, int max, int value) {
var sld = new JSlider(min, max, value);
sld.setFocusable(false);
sld.setPreferredSize(new Dimension(0, sld.getPreferredSize().height));
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 1, 2);
panel.add(sld, gbc);
return sld;
}
// Add a spinner to the controls panel
private JSpinner spinner(UPanel panel, int min, int max, int value,
boolean top) {
var spn = new JSpinner(new SpinnerNumberModel(value, min, max, 1));
var txt = new JSpinner.NumberEditor(spn, "#");
txt.getTextField().addActionListener(e->client.requestFocus());
spn.setEditor(txt);
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(top ? 2 : 0, 0, 2, 2);
gbc.weightx = 1;
panel.add(spn, gbc);
return spn;
}
// Terminate a controls container
private void terminator(UPanel panel) {
var spacer = new UPanel();
spacer.setOpaque(false);
spacer.setPreferredSize(new Dimension(0, 0));
var gbc = new GridBagConstraints();
gbc.gridheight = GridBagConstraints.REMAINDER;
gbc.gridwidth = GridBagConstraints.REMAINDER;
panel.add(spacer, gbc);
}
// Add a text box to the controls panel
private JTextField textBox(UPanel panel) {
var txt = new JTextField();
txt.setFont(parent.app.fntMono);
txt.addActionListener(e->client.requestFocus());
var size = txt.getPreferredSize();
txt.setPreferredSize(new Dimension(
parent.app.hexDigitWidth * 8 + 2 + size.width, size.height));
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 2, 2);
panel.add(txt, gbc);
return txt;
}
}

View File

@ -0,0 +1,613 @@
package app;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
import vue.*;
// Memory viewer and hex editor window
class BreakpointsWindow extends ChildWindow {
// Private fields
private ArrayList<Item> items; // List items
private int selectedIndex; // Selected list item
// UI components
private JButton btnDelete; // Delete button
private JButton btnNew; // New button
private JCheckBox chkEnabled; // Enabled check box
private JCheckBox chkException; // Exception check box
private JCheckBox chkExecute; // Execute check box
private JCheckBox chkRead; // Read check box
private JCheckBox chkWrite; // Write check box
private UPanel client; // Client area
private JLabel errAddress; // Address error text
private JLabel errCondition; // Condition error text
private UPanel lstBreakpoints; // Breakpoint list
private UPanel spacer; // List termination spacer
private JTextField txtAddress; // Address text box
private JTextField txtCondition; // Condition text box
private JTextField txtName; // Name text box
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Address error keys
private static final String[] KEYS_ADDRESS = {
null , // NONE
"breakpoints.error.badliteral_a", // BADLITERAL
null , // BADOPERAND
null , // BADTOKEN
"breakpoints.error.earlyeof" , // EARLYEOF
null , // INVALID
null , // NESTING
"breakpoints.error.unexpected" // UNEXPECTED
};
// Condition error keys
private static final String[] KEYS_CONDITION = {
null , // NONE
"breakpoints.error.badliteral_c", // BADLITERAL
"breakpoints.error.badoperand" , // BADOPERAND
"breakpoints.error.badtoken" , // BADTOKEN
"breakpoints.error.earlyeof" , // EARLYEOF
"breakpoints.error.invalid" , // INVALID
"breakpoints.error.nesting" , // NESTING
"breakpoints.error.unexpected" // UNEXPECTED
};
// List constraints
private static final GridBagConstraints ENABLED;
private static final GridBagConstraints NAME;
private static final GridBagConstraints SPACER;
private static final GridBagConstraints STATUS;
// Static initializer
static {
ENABLED = new GridBagConstraints();
ENABLED.anchor = GridBagConstraints.CENTER;
ENABLED.insets = new Insets(1, 2, 1, 2);
NAME = new GridBagConstraints();
NAME.fill = GridBagConstraints.HORIZONTAL;
NAME.gridwidth = GridBagConstraints.REMAINDER;
NAME.insets = new Insets(1, 0, 1, 2);
NAME.weightx = 1;
SPACER = new GridBagConstraints();
SPACER.fill = GridBagConstraints.BOTH;
SPACER.gridheight = GridBagConstraints.REMAINDER;
SPACER.gridwidth = GridBagConstraints.REMAINDER;
SPACER.weighty = 1;
STATUS = new GridBagConstraints();
STATUS.fill = GridBagConstraints.BOTH;
STATUS.insets = new Insets(0, 0, 0, 2);
}
///////////////////////////////////////////////////////////////////////////
// Classes //
///////////////////////////////////////////////////////////////////////////
// Text box commit
private interface Commit {
void run();
}
// List item
private class Item {
Breakpoint breakpoint;
JCheckBox chkEnabled;
JLabel lblName;
JLabel lblStatus;
// Constructor
Item(Breakpoint brk) {
var that = this;
breakpoint = brk;
brk.setFetch(false);
chkEnabled = new JCheckBox();
chkEnabled.setBorder(null);
chkEnabled.setFocusable(false);
chkEnabled.addActionListener(e->onEnabled(that));
parent.app.localizer.add(chkEnabled,"null","breakpoints.enabled");
lblName = new JLabel(brk.getName());
lblStatus = new JLabel("", SwingConstants.CENTER);
refresh(null, null);
}
// Update the display
void refresh(Instruction inst, Access acc) {
if (
breakpoint.getAddressError ().code != Breakpoint.NONE ||
breakpoint.getConditionError().code != Breakpoint.NONE
) lblStatus.setText("\u00d7"); // Times symbol
else if (breakpoint.any() && breakpoint.evaluate(inst, acc))
lblStatus.setText("\u2605"); // Star
else lblStatus.setText(" ");
int lineHeight = parent.app.fntDialog.metrics.getHeight();
lblStatus.setPreferredSize(new Dimension(lineHeight, lineHeight));
}
// Specify whether or not the item has focus
void setFocused(boolean focused) {
var color = focused ? SystemColor.textHighlightText :
SystemColor.windowText;
lblStatus.setForeground(color);
lblName .setForeground(color);
}
}
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
BreakpointsWindow(MainWindow parent) {
super(parent, "breakpoints.title");
// Configure instance fields
items = new ArrayList<Item>();
selectedIndex = -1;
// Configure client area
client = new UPanel(new BorderLayout());
client.setBackground(SystemColor.control);
client.setFocusable(true);
client.setPreferredSize(new Dimension(320, 240));
// Configure breakpoint list
lstBreakpoints = new UPanel(new GridBagLayout());
lstBreakpoints.setFocusable(true);
lstBreakpoints.setBackground(SystemColor.window);
lstBreakpoints.addFocusListener(Util.onFocus(
e->lstBreakpoints.repaint(), e->lstBreakpoints.repaint()));
lstBreakpoints.addMouseListener(Util.onMouse(e->onMouseDown(e), null));
lstBreakpoints.addPaintListener((g,w,h)->onPaint(g, w, h));
var scr = new JScrollPane(lstBreakpoints);
scr.getVerticalScrollBar().setUnitIncrement(
parent.app.fntDialog.metrics.getHeight());
client.add(scr, BorderLayout.CENTER);
spacer = new UPanel();
spacer.setOpaque(false);
lstBreakpoints.add(spacer, SPACER);
// Configure properties pane
var props = new UPanel(new GridBagLayout());
props.setBackground(SystemColor.control);
props.addMouseListener(
Util.onMouse(e->client.requestFocus(), null));
client.add(props, BorderLayout.SOUTH);
// Configure properties
label(props, "breakpoints.name", 2);
txtName = textBox(props, false, 2, ()->onName());
//label(props, "breakpoints.enabled", 0);
//chkEnabled = checkBox(props, null, true);
chkEnabled = new JCheckBox();
chkEnabled.addActionListener(e->onEnabled(null));
label(props, "breakpoints.type", 0);
chkExecute = checkBox(props, "breakpoints.execute" , false);
chkExecute .addActionListener(e->onType(chkExecute ));
chkRead = checkBox(props, "breakpoints.read" , false);
chkRead .addActionListener(e->onType(chkRead ));
chkWrite = checkBox(props, "breakpoints.write" , false);
chkWrite .addActionListener(e->onType(chkWrite ));
chkException = checkBox(props, "breakpoints.exception", true );
chkException.addActionListener(e->onType(chkException));
label(props, "breakpoints.address", 0);
txtAddress = textBox(props, true, 0, ()->onAddress());
errAddress = error(props);
label(props, "breakpoints.condition", 0);
txtCondition = textBox(props, true, 0, ()->onCondition());
errCondition = error(props);
// Configure buttons
var buttons = new UPanel(new GridBagLayout());
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
props.add(buttons, gbc);
var fill = new UPanel();
fill.setOpaque(false);
fill.addMouseListener(Util.onMouse(e->onDebug(e), null));
gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.weightx = 1;
buttons.add(fill, gbc);
btnDelete = button(buttons, "breakpoints.delete", false);
btnDelete.setEnabled(false);
btnDelete.addActionListener(e->onDelete());
btnNew = button(buttons, "breakpoints.new", true);
btnNew.addActionListener(e->onNew());
// Configure component
setContentPane(client);
pack();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Update the display
void refresh() {
var inst = new Instruction(parent.vue.read(
parent.vue.getRegister(Vue.PC, true), Vue.S32));
var acc = inst.access(parent.vue);
for (var item : items)
item.refresh(inst, acc);
lstBreakpoints.revalidate();
lstBreakpoints.repaint();
}
///////////////////////////////////////////////////////////////////////////
// Event Handlers //
///////////////////////////////////////////////////////////////////////////
// Address text box commit
private void onAddress() {
if (selectedIndex == -1)
return;
var item = items.get(selectedIndex);
item.breakpoint.setAddresses(txtAddress.getText());
checkErrors(item);
item.refresh(null, null);
lstBreakpoints.revalidate();
lstBreakpoints.repaint();
}
// Condition text box commit
private void onCondition() {
if (selectedIndex == -1)
return;
var item = items.get(selectedIndex);
item.breakpoint.setCondition(txtCondition.getText());
checkErrors(item);
item.refresh(null, null);
lstBreakpoints.revalidate();
lstBreakpoints.repaint();
}
// Debug interaction
private void onDebug(MouseEvent e) {
client.requestFocus();
if (
selectedIndex == -1 ||
e.getButton() != MouseEvent.BUTTON1 ||
(e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) == 0
) return;
var brk = items.get(selectedIndex).breakpoint;
System.out.println("Address");
System.out.println(brk.getAddresses());
System.out.println(brk.debugRanges());
System.out.println("Condition");
System.out.println(brk.getCondition());
System.out.println(brk.debugTokens());
System.out.println();
}
// Delete button click
private void onDelete() {
var item = items.remove(selectedIndex);
parent.vue.remove(item.breakpoint);
lstBreakpoints.remove(item.chkEnabled);
lstBreakpoints.remove(item.lblName);
lstBreakpoints.remove(item.lblStatus);
lstBreakpoints.revalidate();
selectedIndex = -1;
select(-1);
}
// Enabled check box toggle
private void onEnabled(Item item) {
boolean enabled;
// Clicked from the properties pane
if (item == null) {
enabled = chkEnabled.isSelected();
item = items.get(selectedIndex);
item.chkEnabled.setSelected(enabled);
client.requestFocus();
}
// Clicked from the breakpoints list
else {
enabled = item.chkEnabled.isSelected();
if (items.indexOf(item) == selectedIndex)
chkEnabled.setSelected(enabled);
lstBreakpoints.requestFocus();
}
// Common processing
item.breakpoint.setEnabled(enabled);
item.refresh(null, null);
lstBreakpoints.revalidate();
lstBreakpoints.repaint();
}
// List mouse press
private void onMouseDown(MouseEvent e) {
int count = items.size();
// Left clicking
if (count > 0 && e.getButton() == MouseEvent.BUTTON1) {
int newIndex = e.getY() / items.get(0).lblStatus.getHeight();
if (newIndex < count)
select(newIndex);
}
// Update layout
lstBreakpoints.requestFocus();
lstBreakpoints.repaint();
}
// Name text box commit
private void onName() {
if (selectedIndex == -1)
return;
var item = items.get(selectedIndex);
item.lblName.setText(item.breakpoint.setName(txtName.getText()));
lstBreakpoints.revalidate();
lstBreakpoints.repaint();
item.refresh(null, null);
lstBreakpoints.revalidate();
lstBreakpoints.repaint();
}
// New button click
private void onNew() {
var brk = parent.vue.breakpoint();
brk.setEnabled(true);
brk.setName (parent.app.localizer.get("breakpoints.default_name"));
var item = new Item(brk);
item.chkEnabled.setSelected(true);
item.lblName .setText(brk.getName());
items.add(item);
int count = items.size();
boolean first = count == 1;
lstBreakpoints.remove(spacer);
lstBreakpoints.add(item.chkEnabled, ENABLED);
lstBreakpoints.add(item.lblStatus , STATUS );
lstBreakpoints.add(item.lblName , NAME );
lstBreakpoints.add(spacer , SPACER );
select(count - 1);
lstBreakpoints.requestFocus();
lstBreakpoints.revalidate();
}
// List paint
private void onPaint(Graphics2D g, int width, int height) {
// There is no selected item
if (selectedIndex == -1)
return;
// Select the display colors
var item = items.get(selectedIndex);
if (lstBreakpoints.isFocusOwner()) {
g.setColor(SystemColor.textHighlight);
item.setFocused(true);
} else {
g.setColor(SystemColor.control);
item.setFocused(false);
}
// Draw the selection background
height = item.lblStatus.getHeight();
g.fillRect(0, selectedIndex * height, width, height);
}
// Type check box toggle
private void onType(JCheckBox chk) {
var item = items.get(selectedIndex);
var brk = item.breakpoint;
boolean selected = chk.isSelected();
// Configure controls
if (chk == chkException)
brk.setException(selected);
else if (chk == chkExecute )
brk.setExecute (selected);
else if (chk == chkRead )
brk.setRead (selected);
else if (chk == chkWrite )
brk.setWrite (selected);
// Common processing
item.refresh(null, null);
client.requestFocus();
lstBreakpoints.revalidate();
lstBreakpoints.repaint();
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Add a button to the form
private JButton button(UPanel panel, String key, boolean last) {
var btn = new JButton();
if (key != null)
parent.app.localizer.add(btn, key);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(0, 0, 2, 2);
if (last)
gbc.gridwidth = GridBagConstraints.REMAINDER;
panel.add(btn, gbc);
return btn;
}
// Add a check box to the form
private JCheckBox checkBox(UPanel panel, String key, boolean last) {
var chk = new JCheckBox();
if (key != null)
parent.app.localizer.add(chk, key);
chk.setBorder(null);
chk.setEnabled(false);
chk.setFocusable(false);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(0, 0, 2, last ? 2 : 6);
if (last)
gbc.gridwidth = GridBagConstraints.REMAINDER;
panel.add(chk, gbc);
return chk;
}
// Check for errors in the selected breakpoint
private void checkErrors(Item item) {
var brk = item.breakpoint;
var loc = parent.app.localizer;
// Check for address errors
var err = brk.getAddressError();
if (err.code != Breakpoint.NONE) {
loc.add(errAddress, KEYS_ADDRESS[err.code]);
loc.put(errAddress, "err.position",
Integer.toString(err.position));
loc.put(errAddress, "err.text", err.text);
errAddress.setVisible(true);
} else errAddress.setVisible(false);
// Check for condition errors
err = brk.getConditionError();
if (err.code != Breakpoint.NONE) {
loc.add(errCondition, KEYS_CONDITION[err.code]);
loc.put(errCondition, "err.position",
Integer.toString(err.position));
loc.put(errCondition, "err.text", err.text);
if (err.code == Breakpoint.NESTING)
loc.put(errCondition, "err.other",
err.text.equals(")") ? "]" : ")");
errCondition.setVisible(true);
} else errCondition.setVisible(false);
}
// Add an error label to the form
private JLabel error(UPanel panel) {
var lbl = new JLabel();
lbl.setForeground(Color.red);
lbl.setVisible(false);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.BOTH;
gbc.gridx = 1;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 2, 2);
panel.add(lbl, gbc);
return lbl;
}
// Add a label to the form
private void label(UPanel panel, String key, int top) {
var lbl = new JLabel();
if (key != null)
parent.app.localizer.add(lbl, key);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(top, 2, 2, 4);
panel.add(lbl, gbc);
}
// Select a particular list item
private void select(int newIndex) {
boolean enabled = newIndex != -1;
// The existing selection was de-selected
if (selectedIndex != -1 && newIndex != selectedIndex)
items.get(selectedIndex).setFocused(false);
// Selecting a new list item
if (newIndex != -1)
items.get(newIndex).setFocused(true);
// Configure instance fields
selectedIndex = newIndex;
// Update control properties
if (enabled) {
var brk = items.get(selectedIndex).breakpoint;
chkEnabled .setSelected(brk.isEnabled ());
chkExecute .setSelected(brk.getExecute ());
chkRead .setSelected(brk.getRead ());
chkWrite .setSelected(brk.getWrite ());
chkException.setSelected(brk.getException());
txtAddress .setText (brk.getAddresses());
txtCondition.setText (brk.getCondition());
txtName .setText (brk.getName ());
}
// Clear control properties
else {
chkEnabled .setSelected(false);
chkExecute .setSelected(false);
chkRead .setSelected(false);
chkWrite .setSelected(false);
chkException.setSelected(false);
errAddress .setText(""); errAddress .setVisible(false);
errCondition.setText(""); errCondition.setVisible(false);
txtAddress .setText("");
txtCondition.setText("");
txtName .setText("");
}
// Enable or disable controls
btnDelete .setEnabled(enabled);
chkEnabled .setEnabled(enabled);
chkExecute .setEnabled(enabled);
chkException.setEnabled(enabled);
chkRead .setEnabled(enabled);
chkWrite .setEnabled(enabled);
txtAddress .setEnabled(enabled);
txtCondition.setEnabled(enabled);
txtName .setEnabled(enabled);
// Common processing
if (selectedIndex != -1)
checkErrors(items.get(selectedIndex));
lstBreakpoints.repaint();
}
// Add a text box to the form
private JTextField textBox(UPanel panel, boolean mono, int top,
Commit handler) {
var txt = new JTextField();
txt.setEnabled(false);
if (mono)
txt.setFont(parent.app.fntMono);
txt.addActionListener(e->client.requestFocus());
txt.addFocusListener (Util.onFocus(null, e->handler.run()));
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.CENTER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(top, 0, 2, 2);
gbc.weightx = 1;
panel.add(txt, gbc);
return txt;
}
}

View File

@ -0,0 +1,71 @@
package app;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
import vue.*;
// CPU window
class CPUWindow extends ChildWindow {
// Instance fields
MainWindow parent; // Containing window
// UI components
private DisassemblerPane panDasm; // Disassembler client
private RegisterList lstProgram; // Program register list
private RegisterList lstSystem; // System register list
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
CPUWindow(MainWindow parent) {
super(parent, "cpu.title");
// Configure instance fields
this.parent = parent;
// Configure child panes
panDasm = new DisassemblerPane(this);
lstSystem = new RegisterList(this, true);
lstProgram = new RegisterList(this, false);
// Configure layout
var inner = Util.splitPane(JSplitPane.VERTICAL_SPLIT);
inner.setTopComponent(lstSystem);
inner.setBottomComponent(lstProgram);
var outer = Util.splitPane(JSplitPane.HORIZONTAL_SPLIT);
outer.setLeftComponent(panDasm);
outer.setRightComponent(inner);
outer.setResizeWeight(1);
// Configure component
var client = getContentPane();
client.add(outer);
client.setPreferredSize(new Dimension(480, 300));
pack();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Update the display
void refresh(boolean seekToPC) {
panDasm .refresh(seekToPC);
lstSystem .refresh();
lstProgram.refresh();
}
}

View File

@ -0,0 +1,612 @@
package app;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
// VIP characters window
class CharactersWindow extends ChildWindow {
// Instance fields
private int color; // Selected color index
private Point dragging; // Most recent pattern mouse position
private int index; // Current character index
private boolean grid; // Draw a grid around characters
private int palette; // Palette index
private int[] pix; // Image buffer
private int scale; // Display scale
private int wide; // Number of characters per row
private BufferedImage image; // Character graphics
// UI components
private JCheckBox chkGrid; // Grid check box
private UPanel client; // Client area
private UComboBox cmbPalette; // Palette drop-down
private UPanel panCharacters; // Characters panel
private UPanel panPalette; // Palette panel
private UPanel panPattern; // Pattern panel
private JScrollPane scrCharacters; // Characters container
private JScrollPane scrControls; // Controls panel
private JScrollBar scrWidth; // Width template scroll bar
private JSlider sldScale; // Scale slider
private JSpinner spnIndex; // Index spinner
private JSpinner spnWide; // Wide spinner
private JTextField txtAddress; // Address text box
private JTextField txtMirror; // Mirror text box
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
CharactersWindow(MainWindow parent) {
super(parent, "characters.title");
// Configure instance fields
color = 0;
grid = true;
image = new BufferedImage(256, 512, BufferedImage.TYPE_INT_RGB);
pix = new int[256 * 512];
scale = 3;
// Template scroll bar to check control width in the current LAF
scrWidth = new JScrollBar(JScrollBar.VERTICAL);
// Configure client area
client = new UPanel(new BorderLayout());
client.setBackground(SystemColor.control);
client.setFocusable(true);
client.setPreferredSize(new Dimension(480, 360));
client.addComponentListener(Util.onResize(e->onResize()));
// Configure controls panel
var ctrls = new UPanel(new GridBagLayout());
ctrls.setBackground(SystemColor.control);
ctrls.addMouseListener(
Util.onMouse(e->client.requestFocus(), null));
scrControls = new JScrollPane(ctrls,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scrControls.setBorder(null);
scrControls.getVerticalScrollBar().setUnitIncrement(20);
client.add(scrControls, BorderLayout.WEST);
// Character controls
label(ctrls, "characters.index", true);
spnIndex = spinner(ctrls, 0, 2047, 0, true);
spnIndex.addChangeListener(e->
setIndex((Integer) spnIndex.getValue()));
label(ctrls, "characters.address", false);
txtAddress = textBox(ctrls);
txtAddress.addFocusListener(Util.onFocus(null,
e->onAddress(txtAddress)));
label(ctrls, "characters.mirror", false);
txtMirror = textBox(ctrls);
txtMirror.addFocusListener(Util.onFocus(null,
e->onAddress(txtMirror)));
// Pattern panel
panPattern = new UPanel();
panPattern.setOpaque(false);
panPattern.setPreferredSize(new Dimension(96, 96));
panPattern.addMouseListener(
Util.onMouse(e->onMousePattern(e), e->onMousePattern(e)));
panPattern.addMouseMotionListener(
Util.onMouseMove(null, e->onMousePattern(e)));
panPattern.addPaintListener((g,w,h)->onPaintPattern(g, w, h));
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.CENTER;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(4, 2, 2, 2);
ctrls.add(panPattern, gbc);
// Palette panel
panPalette = new UPanel();
panPalette.setOpaque(false);
panPalette.setPreferredSize(new Dimension(0, 20 + 6));
panPalette.addMouseListener(
Util.onMouse(e->onMouseDownPalette(e), null));
panPalette.addPaintListener((g,w,h)->onPaintPalette(g, w, h));
gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 2, 4, 2);
ctrls.add(panPalette, gbc);
// Fill the extra space above the view controls
var spacer = new UPanel();
spacer.setOpaque(false);
gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weighty = 1;
ctrls.add(spacer, gbc);
// View controls
label(ctrls, "characters.grid", false);
chkGrid = checkBox(ctrls);
chkGrid.setSelected(grid);
chkGrid.addActionListener(e->{
grid = chkGrid.isSelected(); onView(); });
label(ctrls, "characters.wide", false);
spnWide = spinner(ctrls, 0, 2048, 0, false);
spnWide.addChangeListener(e->{
wide = (Integer) spnWide.getValue(); onView(); });
label(ctrls, "characters.palette", false);
cmbPalette = select(ctrls, new String[] { "palette.generic",
"palette.gplt0", "palette.gplt1", "palette.gplt2", "palette.gplt3",
"palette.jplt0", "palette.jplt1", "palette.jplt2", "palette.jplt3"
});
cmbPalette.addActionListener(e->{
palette = cmbPalette.getSelectedIndex(); repaint(); });
label(ctrls, "characters.scale", false);
sldScale = slider(ctrls, 1, 10, scale);
sldScale.addChangeListener(e->{
scale = sldScale.getValue(); onView(); });
// Terminate the list of controls
spacer = new UPanel();
spacer.setOpaque(false);
spacer.setPreferredSize(new Dimension(0, 0));
gbc = new GridBagConstraints();
gbc.gridheight = GridBagConstraints.REMAINDER;
gbc.gridwidth = GridBagConstraints.REMAINDER;
ctrls.add(spacer, gbc);
// Configure characters panel
panCharacters = new UPanel();
panCharacters.setBackground(SystemColor.control);
panCharacters.addMouseListener(
Util.onMouse(e->onMouseCharacters(e), e->onMouseCharacters(e)));
panCharacters.addMouseMotionListener(
Util.onMouseMove(null, e->onMouseCharacters(e)));
panCharacters.addPaintListener((g,w,h)->onPaintCharacters(g, w, h));
scrCharacters = new JScrollPane(panCharacters);
client.add(scrCharacters, BorderLayout.CENTER);
// Configure component
setContentPane(client);
setIndex(0);
onView();
pack();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Update the display
void refresh() {
// Update characters graphic
var pal = parent.palettes[palette][MainWindow.RED];
for (int index = 0; index < 2048; index++)
parent.drawCharacter(index, false, false, pal, pix,
index >> 5 << 11 | (index & 31) << 3, 256);
image.setRGB(0, 0, 256, 512, pix, 0, 256);
// Update display;
panCharacters.repaint();
panPattern .repaint();
}
///////////////////////////////////////////////////////////////////////////
// Event Handlers //
///////////////////////////////////////////////////////////////////////////
// Address and Mirror text box commit
private void onAddress(JTextField src) {
int address;
// Parse the given address
try { address = (int) Long.parseLong(src.getText(), 16); }
catch (Exception e) {
setIndex(index);
return;
}
// Restrict address range
if ((address >> 24 & 7) != 0) {
setIndex(index);
return;
}
address &= 0x0007FFFF;
// Character memory
if ((address & 0x00066000) == 0x00006000)
index = address >> 15 << 9 | address >> 4 & 511;
// Mirror of character memory
else if (address >= 0x00078000)
index = address - 0x00078000 >> 4;
// Common processing
setIndex(index);
}
// Characters mouse
private void onMouseCharacters(MouseEvent e) {
int id = e.getID();
// Mouse release
if (id == MouseEvent.MOUSE_RELEASED) {
if (e.getButton() == MouseEvent.BUTTON1)
dragging = null;
return;
}
// Mouse press
if (id == MouseEvent.MOUSE_PRESSED) {
client.requestFocus();
if (e.getButton() != MouseEvent.BUTTON1)
return;
dragging = e.getPoint();
}
// Not dragging
if (dragging == null)
return;
// Working variables
int grid = this.grid ? 1 : 0;
int size = 8 * scale + grid;
int wide = this.wide != 0 ? this.wide :
Math.max(1, (panCharacters.getWidth() + grid) / size);
int col = e.getX() / size;
int row = e.getY() / size;
// The pixel is not within a character
if (col >= wide || row >= (2047 + wide) / wide)
return;
// Calculate the index of the selected character
int index = row * wide + col;
if (index < 2048)
setIndex(index);
}
// Palette mouse button press
private void onMouseDownPalette(MouseEvent e) {
// Common processing
client.requestFocus();
// Only consider left clicks
if (e.getButton() != MouseEvent.BUTTON1)
return;
// Working variables
int height = panPalette.getHeight();
int width = panPalette.getWidth();
int size = Math.max(1, Math.min((width - 18) / 4, height - 6));
int left = (width - size * 4 - 18) / 2;
int top = (height - size - 6) / 2;
int x = e.getX() - left - 3;
int y = e.getY() - top - 3;
// The click was not on top of a color
if (
x < 0 || x >= (size + 4) * 4 ||
x % (size + 4) >= size ||
y < 0 || y >= size
) return;
// Select the clicked color
color = x / (size + 4);
panPalette.repaint();
}
// Pattern mouse
private void onMousePattern(MouseEvent e) {
int id = e.getID();
int scale = Math.max(1, Math.min(
panPattern.getWidth () >> 3,
panPattern.getHeight() >> 3
));
// Mouse release
if (id == MouseEvent.MOUSE_RELEASED) {
if (e.getButton() == MouseEvent.BUTTON1)
dragging = null;
return;
}
// Mouse press
if (id == MouseEvent.MOUSE_PRESSED) {
client.requestFocus();
if (e.getButton() != MouseEvent.BUTTON1)
return;
dragging = e.getPoint();
dragging.x = dragging.x / scale - (dragging.x < 0 ? 1 : 0);
dragging.y = dragging.y / scale - (dragging.y < 0 ? 1 : 0);
}
// Not dragging
if (dragging == null)
return;
// Retrieve the current point
var pos = e.getPoint();
pos.x = pos.x / scale - (pos.x < 0 ? 1 : 0);
pos.y = pos.y / scale - (pos.y < 0 ? 1 : 0);
// Draw a line segment (adapted from Wikipedia)
int dx = Math.abs(pos.x - dragging.x);
int sx = dragging.x < pos.x ? 1 : -1;
int dy = -Math.abs(pos.y - dragging.y);
int sy = dragging.y < pos.y ? 1 : -1;
int err = dx + dy;
int addr = index >> 9 << 15 | 0x00006000 | (index & 511) << 4;
for (;;) {
// Draw the current pixel
if ((dragging.x&7) == dragging.x && (dragging.y&7) == dragging.y) {
int b = addr | dragging.y << 1 | dragging.x >> 2;
int bit = (dragging.x & 3) << 1;
parent.vram[b] = (byte)
(parent.vram[b] & ~(3 << bit) | color << bit);
}
// The last pixel has been drawn
if (dragging.x == pos.x && dragging.y == pos.y)
break;
// Advance to the next pixel
int e2 = err << 1;
if (e2 >= dy)
{ err += dy; dragging.x += sx; }
if (e2 <= dx)
{ err += dx; dragging.y += sy; }
}
// Update VRAM state
parent.vue.writeBytes(addr, parent.vram, addr, 16);
parent.refreshDebug(false);
}
// Characters paint
private void onPaintCharacters(Graphics2D g, int width, int height) {
int grid = this.grid ? 1 : 0;
int size = scale * 8 + grid;
int wide = this.wide != 0 ? this.wide :
Math.max(1, (width + grid) / size);
int tall = (2047 + wide) / wide;
// Draw all characters
for (int Y = 0, y = 0, z = 0; y < tall; y++, Y += size)
for (int X = 0, x = 0; x < wide; x++, z++, X += size) {
if (z == 2048)
break;
int ix = (z & 31) << 3;
int iy = z >> 5 << 3;
g.drawImage(image,
X, Y, X + size - grid, Y + size - grid,
ix, iy, ix + 8 , iy + 8 ,
null);
}
// Highlight the selected character
int X = index % wide * size;
int Y = index / wide * size;
int light = SystemColor.textHighlight.getRGB() & 0x00FFFFFF;
g.setColor(new Color(0xD0000000 | light, true));
g.drawRect(X , Y , 8 * scale - 1, 8 * scale - 1);
g.setColor(new Color(0x90000000 | light, true));
g.drawRect(X + 1, Y + 1, 8 * scale - 3, 8 * scale - 3);
g.setColor(new Color(0x50000000 | light, true));
g.fillRect(X + 2, Y + 2, 8 * scale - 4, 8 * scale - 4);
}
// Palette paint
private void onPaintPalette(Graphics2D g, int width, int height) {
int size = Math.max(1, Math.min((width - 18) / 4, height - 6));
int left = (width - size * 4 - 18) / 2;
var pal = parent.palettes[palette][MainWindow.RED];
int top = (height - size - 6) / 2;
// Draw the color picker
for (int x = 0; x < 4; x++, left += size + 4) {
// The current color is selected
if (x == color) {
g.setColor(SystemColor.textHighlight);
g.fillRect(left , top , size + 6, size + 6);
g.setColor(SystemColor.control);
g.fillRect(left + 2, top + 2, size + 2, size + 2);
}
// Draw the color area
g.setColor(new Color(pal[x]));
g.fillRect(left + 3, top + 3, size , size );
}
}
// Pattern paint
private void onPaintPattern(Graphics2D g, int width, int height) {
int scale = Math.max(1, Math.min(width >> 3, height >> 3));
int x = (width - (scale << 3) + 1) >> 1;
int y = (height - (scale << 3) + 1) >> 1;
int ix = (index & 31) << 3;
int iy = index >> 5 << 3;
g.drawImage(image,
x, y, x + scale * 8, y + scale * 8,
ix, iy, ix + 8 , iy + 8 ,
null);
}
// Window resize
private void onResize() {
var viewport = scrControls.getViewport();
int inner = viewport.getView().getPreferredSize().width;
int outer = viewport.getExtentSize().width;
// Size the controls container to match the inner component
if (inner != outer) {
scrControls.setPreferredSize(new Dimension(
scrControls.getPreferredSize().width + inner - outer, 0));
scrControls.revalidate();
scrControls.repaint();
}
// The number of characters per row is dynamic
if (wide == 0) {
var bar = scrWidth.getPreferredSize().width;
var border = scrCharacters.getBorder();
int grid = this.grid ? 1 : 0;
var insets = border == null ? new Insets(0, 0, 0, 0) :
border.getBorderInsets(scrCharacters);
var size = new Dimension(
scrCharacters.getWidth () - insets.left - insets.right,
scrCharacters.getHeight() - insets.top - insets.bottom
);
// Calculate the dimensions without the vertical scroll bar
int wide = Math.max(1, (size.width + grid) / (8 * scale + grid));
int height = (2047 + wide) / wide * (8 * scale + grid) - grid;
// Calculate the dimensions with the vertical scroll bar
if (height > size.height) {
wide = Math.max(1, (size.width-bar+grid) / (8 * scale + grid));
height = (2047 + wide) / wide * (8 * scale + grid) - grid;
}
// Configure the component
panCharacters.setPreferredSize(
new Dimension(wide * (8 * scale + grid) - grid, height));
panCharacters.revalidate();
panCharacters.repaint();
}
}
// View control changed
private void onView() {
// Number of characters per row is dynamic: defer to resize processing
if (wide == 0) {
onResize();
return;
}
// Calculate the space occupied by the character images
int grid = this.grid ? 1 : 0;
panCharacters.setPreferredSize(new Dimension(
wide * (scale * 8 + grid) - grid,
(2047 + wide) / wide * (scale * 8 + grid) - grid
));
scrCharacters.getVerticalScrollBar().setUnitIncrement(8*scale+grid);
panCharacters.revalidate();
panCharacters.repaint();
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Add a check box to the controls panel
private JCheckBox checkBox(UPanel panel) {
var chk = new JCheckBox();
chk.setBorder(null);
chk.setFocusable(false);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 2, 2);
panel.add(chk, gbc);
return chk;
}
// Add a label to the controls panel
private void label(UPanel panel, String key, boolean top) {
var lbl = new JLabel();
parent.app.localizer.add(lbl, key);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
gbc.insets = new Insets(top ? 2 : 0, 2, 2, 2);
panel.add(lbl, gbc);
}
// Add a combo box to the controls panel
private UComboBox select(UPanel panel, String[] options) {
var cmb = new UComboBox();
parent.app.localizer.add(cmb, options);
cmb.setSelectedIndex(0);
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 2, 2);
panel.add(cmb, gbc);
return cmb;
}
// Specify the current character index
private void setIndex(int index) {
this.index = index;
spnIndex.setValue(index);
txtAddress.setText(String.format("%08X",
index >> 9 << 15 | 0x00006000 | (index & 511) << 4));
txtMirror .setText(String.format("%08X",
index << 4 | 0x00078000));
repaint();
}
// Add a slider to the controls panel
private JSlider slider(UPanel panel, int min, int max, int value) {
var sld = new JSlider(min, max, value);
sld.setFocusable(false);
sld.setPreferredSize(new Dimension(0, sld.getPreferredSize().height));
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 1, 2);
panel.add(sld, gbc);
return sld;
}
// Add a spinner to the controls panel
private JSpinner spinner(UPanel panel, int min, int max, int value,
boolean top) {
var spn = new JSpinner(new SpinnerNumberModel(value, min, max, 1));
var txt = new JSpinner.NumberEditor(spn, "#");
txt.getTextField().addActionListener(e->client.requestFocus());
spn.setEditor(txt);
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(top ? 2 : 0, 0, 2, 2);
gbc.weightx = 1;
panel.add(spn, gbc);
return spn;
}
// Add a text box to the controls panel
private JTextField textBox(UPanel panel) {
var txt = new JTextField();
txt.setFont(parent.app.fntMono);
txt.addActionListener(e->client.requestFocus());
var size = txt.getPreferredSize();
txt.setPreferredSize(new Dimension(
parent.app.hexDigitWidth * 8 + 2 + size.width, size.height));
var gbc = new GridBagConstraints();
gbc.fill = GridBagConstraints.BOTH;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 0, 2, 2);
panel.add(txt, gbc);
return txt;
}
}

View File

@ -0,0 +1,71 @@
package app;
// Java imports
import java.awt.*;
import javax.swing.*;
// Project imports
import util.*;
// Child window
class ChildWindow extends JInternalFrame {
// Instance fields
MainWindow parent; // Parent application window
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
ChildWindow(MainWindow parent, String key) {
super();
// Configure instanece fields
this.parent = parent;
// Configure component
parent.app.localizer.add(this, key);
addInternalFrameListener(Util.onClose2(e->setVisible(false)));
setClosable(true);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setMaximizable(true);
setResizable(true);
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Show or hide the component
public void setVisible(boolean visible) {
// Making visible
if (visible) {
// Not currently visible: center in parent
if (!isVisible()) {
var parent = getParent().getParent();
setLocation(
Math.max(0, (parent.getWidth () - getWidth ()) / 2),
Math.max(0, (parent.getHeight() - getHeight()) / 2)
);
}
// Already visible: bring to front
else {
moveToFront();
try { setSelected(true); } catch (Exception e) { }
}
}
// Change visibility
super.setVisible(visible);
}
}

View File

@ -0,0 +1,38 @@
package app;
// Java imports
import java.awt.*;
import javax.swing.*;
// Console window
class ConsoleWindow extends ChildWindow {
// Instance fields
private boolean shown; // Component has been visible
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
ConsoleWindow(MainWindow parent) {
super(parent, "console.title");
getContentPane().setPreferredSize(new Dimension(384, 224));
pack();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Show the component on the first transition to debug mode
void firstShow() {
if (!shown)
setVisible(shown = true);
}
}

View File

@ -0,0 +1,386 @@
package app;
// Project imports
import vue.*;
// Instruction disassembler
class Disassembler {
private Disassembler() { } // Cannot be instantiated
static { setDefaults(); } // Initialize to default settings
///////////////////////////////////////////////////////////////////////////
// Global Settings //
///////////////////////////////////////////////////////////////////////////
static boolean bcondCombine; // Merge Bcond condition into mnemonic
static boolean bcondNames; // Use symbolic condition names for Bcond
static boolean condCaps; // Uppercase condition mnemonics
static boolean destLast; // Destination register last
static boolean dispDest; // Resolve jump destination addresses
static boolean dispInside; // Load/store displacement inside brackets
static boolean hexCaps; // Upercase hexadecimal
static int hexMode; // Hexadecimal decoration
static boolean immNumber; // Use number sign with immediate values
static boolean jmpBrackets; // Square brackets around JMP
static boolean jumpAddress; // Use jump destination addresses
static boolean lower; // Use L instead of C in conditions
static boolean mnemonicCaps; // Uppercase mnemonics
static boolean programCaps; // Uppercase program register names
static boolean programNames; // Use symbolic program register names
static boolean setfCombine; // Merge SETF condition into mnemonic
static boolean setfNames; // Use symbolic condition names for SETF
static boolean systemCaps; // Uppercase system register names
static boolean systemNames; // Use symbolic system register names
static boolean zero; // Use Z instead of E in conditions
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Hexadecimal decorations
static final int DOLLAR = 0;
static final int H = 1;
static final int ZEROX = 2;
// Condition mneonics
static final String[] CONDITIONS = {
"V" , "C" , "E" , "NH", "N", "T", "LT", "LE",
"NV", "NC", "NE", "H" , "P", "F", "GE", "GT"
};
// Mnemonics
static final String[] MNEMONICS = {
"---" , "ADD" , "ADD" , "ADDF.S", "ADDI" , "AND" ,
"ANDBSU" , "ANDI" , "ANDNBSU", "Bcond" , "CAXI" , "CLI" ,
"CMP" , "CMP" , "CMPF.S" , "CVT.SW", "CVT.WS" , "DIV" ,
"DIVF.S" , "DIVU" , "HALT" , "IN.B" , "IN.H" , "IN.W" ,
"JAL" , "JMP" , "JR" , "LD.B" , "LD.H" , "LD.W" ,
"LDSR" , "MOV" , "MOV" , "MOVBSU", "MOVEA" , "MOVHI" ,
"MPYHW" , "MUL" , "MULF.S" , "MULU" , "NOT" , "NOTBSU" ,
"OR" , "ORBSU" , "ORI" , "ORNBSU", "OUT.B" , "OUT.H" ,
"OUT.W" , "RETI" , "REV" , "SAR" , "SAR" , "SCH0BSD",
"SCH0BSU", "SCH1BSD", "SCH1BSU", "SEI" , "SETF" , "SHL" ,
"SHL" , "SHR" , "SHR" , "ST.B" , "ST.H" , "ST.W" ,
"STSR" , "SUB" , "SUBF.S" , "TRAP" , "TRNC.SW", "XB" ,
"XH" , "XOR" , "XORBSU" , "XORI" , "XORNBSU"
};
// Program register names
private static final String[] PROGRAMS = {
null, null, "hp", "sp", "gp", "tp", null, null,
null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, "lp"
};
// System register names
private static final String[] SYSTEMS = {
"EIPC", "EIPSW", "FEPC", "FEPSW", "ECR", "PSW", "PIR", "TKCW",
null , null , null , null , null , null , null , null ,
null , null , null , null , null , null , null , null ,
"CHCW", "ADTRE", null , null , null , null , null , null
};
///////////////////////////////////////////////////////////////////////////
// Classes //
///////////////////////////////////////////////////////////////////////////
// One row of output
static class Row {
String address; // Bus address
String bytes; // Encoded bytes in bus order
String mnemonic; // Instruction mnemonic
String operands; // Instruction operands, if any
}
///////////////////////////////////////////////////////////////////////////
// Static Methods //
///////////////////////////////////////////////////////////////////////////
// Reset all settings to their default values
static void setDefaults() {
bcondCombine = true;
bcondNames = true;
condCaps = true;
destLast = true;
dispDest = true;
dispInside = false;
hexCaps = true;
hexMode = ZEROX;
immNumber = false;
jmpBrackets = true;
jumpAddress = true;
lower = true;
mnemonicCaps = true;
programCaps = false;
programNames = true;
setfCombine = false;
setfNames = true;
systemCaps = true;
systemNames = true;
zero = false;
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Represent an instruction as text fields
static void disassemble(int address, Instruction inst, Row row) {
// Address
String x = hexCaps ? "X" : "x";
row.address = String.format("%08" + x, address);
// Bytes
x = "%02" + x;
row.bytes = inst.size == 2 ?
String.format(x + " " + x,
inst.bits >> 16 & 0xFF,
inst.bits >> 24 & 0xFF
) : String.format(x + " " + x + " " + x + " " + x,
inst.bits >> 16 & 0xFF,
inst.bits >> 24 & 0xFF,
inst.bits & 0xFF,
inst.bits >> 8 & 0xFF
)
;
// Bcond mnemonic
if (inst.id == Vue.BCOND && bcondCombine) {
row.mnemonic = "B" + CONDITIONS[inst.cond];
if (lower && (inst.cond & 7) == 1)
row.mnemonic = inst.cond == 1 ? "BL" : "BNL";
if (zero && (inst.cond & 7) == 2)
row.mnemonic = inst.cond == 2 ? "BZ" : "BNZ";
if ( (inst.cond & 7) == 5)
row.mnemonic = inst.cond == 5 ? "BR" : "NOP";
}
// SETF mnemonic
else if (inst.id == Vue.SETF && setfCombine)
row.mnemonic = "SETF" + CONDITIONS[inst.imm];
// All other mnemonics
else row.mnemonic = MNEMONICS[inst.id + 1];
// Adjust mnemonic case
if (!mnemonicCaps)
row.mnemonic = row.mnemonic.toLowerCase();
// Operands by format
row.operands = null;
if (inst.id != Vue.ILLEGAL) switch (inst.format) {
case 1: // Fallthrough
case 7: row.operands = formatI_VII(inst ); break;
case 2: row.operands = formatII (inst ); break;
case 3: row.operands = formatIII (inst, address); break;
case 4: row.operands = destination(inst, address); break;
case 5: row.operands = formatV (inst ); break;
case 6: row.operands = formatVI (inst ); break;
}
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Format the destination of a branch or jump
private static String destination(Instruction inst, int address) {
return !dispDest ? toHex(inst.disp, 1, false) :
String.format("%08" + (hexCaps ? "X" : "x"), address + inst.disp);
}
// Operands for Format I and Format VII
private static String formatI_VII(Instruction inst) {
String reg1 = program(inst.reg1);
String reg2 = program(inst.reg2);
// One-operand instructions
switch (inst.id) {
case Vue.JMP:
return String.format(jmpBrackets ? "[%s]" : "%s", reg1);
case Vue.XB: // Fallthrough
case Vue.XH:
return reg2;
}
// Generic
return String.format("%s, %s",
destLast ? reg1 : reg2,
destLast ? reg2 : reg1
);
}
// Operands for Format II
private static String formatII(Instruction inst) {
// Bit string
if (inst.opcode == 0b011111)
return null;
// Zero- or one-operand instructions
switch (inst.id) {
// Zero-operand
case Vue.CLI : // Fallthrough
case Vue.HALT: // Fallthrough
case Vue.RETI: // Fallthrough
case Vue.SEI :
return null;
// One-operand
case Vue.TRAP:
return Integer.toString(inst.imm);
}
// Combined SETF
String reg2 = program(inst.reg2);
if (inst.id == Vue.SETF && setfCombine)
return reg2;
// Two-operand instructions
String imm = String.format("%s%d", immNumber ? "#" : "", inst.imm);
switch (inst.id) {
case Vue.LDSR: // Fallthrough
case Vue.STSR:
if (!systemNames || SYSTEMS[inst.imm] == null)
break;
imm = SYSTEMS[inst.imm];
if (!systemCaps)
imm = imm.toLowerCase();
break;
case Vue.SETF:
if (!setfNames)
break;
imm = CONDITIONS[inst.imm];
if (!condCaps)
imm = imm.toLowerCase();
break;
}
// LDSR
if (inst.id == Vue.LDSR) {
String temp = imm;
imm = reg2;
reg2 = temp;
}
// Generic
return String.format("%s, %s",
destLast ? imm : reg2,
destLast ? reg2 : imm
);
}
// Operands for Format III
private static String formatIII(Instruction inst, int address) {
// NOP
if (bcondCombine && inst.cond == 13)
return null;
// Format destination
String dest = destination(inst, address);
// One-operand notation
if (bcondCombine)
return dest;
// Two-operand notation
String cond = bcondNames ?
CONDITIONS[inst.cond] : Integer.toString(inst.cond);
if (bcondNames && !condCaps)
cond = cond.toLowerCase();
return String.format("%s, %s", cond, dest);
}
// Operands for Format V
private static String formatV(Instruction inst) {
String reg1 = program(inst.reg1);
String reg2 = program(inst.reg2);
String imm = toHex(
inst.id == Vue.MOVEA ? inst.imm & 0xFFFF : inst.imm,
inst.id == Vue.ADDI ? 1 : 4,
immNumber
);
return String.format("%s, %s, %s",
destLast ? imm : reg2,
reg1,
destLast ? reg2 : imm
);
}
// Operands for Format VI
private static String formatVI(Instruction inst) {
String src = program(inst.reg1);
String dest = program(inst.reg2);
// Data operand
src = inst.disp == 0 ? String.format("[%s]", src) :
dispInside ? String.format(
"[%s %s %s]",
src,
inst.disp < 0 ? "-" : "+",
toHex(Math.abs(inst.disp), 1, false)
) : String.format(
"%s[%s]",
toHex(inst.disp, 1, false),
src
)
;
// Write instruction
switch (inst.id) {
case Vue.OUT_B: // Fallthrough
case Vue.OUT_H: // Fallthrough
case Vue.OUT_W: // Fallthrough
case Vue.ST_B : // Fallthrough
case Vue.ST_H : // Fallthrough
case Vue.ST_W :
String temp = src;
src = dest;
dest = temp;
}
// Format operands
return String.format("%s, %s",
destLast ? src : dest,
destLast ? dest : src
);
}
// Format a program register
private static String program(int index) {
String ret = programNames ? PROGRAMS[index] : null;
if (ret == null)
ret = "r" + index;
return programCaps ? ret.toUpperCase() : ret;
}
// Represent an immediate value as hexadecimal
private static String toHex(int value, int digits, boolean number) {
return String.format(
"%s%s%s%0" + digits + (hexCaps ? "X" : "x") + "%s",
number ? "#" : "",
value < 0 ? "-" : "",
hexMode == DOLLAR ? "$" : hexMode == ZEROX ? "0x" : "",
value < 0 ? -value : value,
hexMode == H ? "h" : ""
);
}
}

View File

@ -0,0 +1,405 @@
package app;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
import vue.*;
// Disassembler UI
class DisassemblerPane extends JScrollPane {
// Instance fields
private int address; // Address of top row of output
private CPUWindow parent; // Containing CPU window
private boolean showBytes; // Display the bytes column
private boolean shown; // Component has been shown
private int[] widths; // Column widths
// UI components
private UPanel client; // Client area
private ArrayList<Row> rows; // Disassembler output
///////////////////////////////////////////////////////////////////////////
// Classes //
///////////////////////////////////////////////////////////////////////////
// One row of disassembler output
private class Row extends Disassembler.Row {
Instruction inst; // Decoded instruction
JLabel[] labels; // Display text
}
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
DisassemblerPane(CPUWindow parent) {
super(VERTICAL_SCROLLBAR_NEVER, HORIZONTAL_SCROLLBAR_AS_NEEDED);
// Configure instance fields
this.parent = parent;
rows = new ArrayList<Row>();
showBytes = true;
widths = new int[5];
// Configure client area
client = new UPanel();
client.setBackground(SystemColor.window);
client.setFocusable(true);
client.addFocusListener(
Util.onFocus(e->client.repaint(), e->client.repaint()));
client.addKeyListener(Util.onKey(e->onKeyDown(e), null));
client.addMouseListener(Util.onMouse(e->client.requestFocus(), null));
client.addMouseWheelListener(e->onMouseWheel(e));
client.addPaintListener((g,w,h)->onPaint(g, w, h));
// Configure component
setViewportView(client);
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Update the display
void refresh(boolean seekToPC) {
if (seekToPC) {
int pc = parent.parent.vue.getRegister(Vue.PC, true);
if (!isVisible(pc))
seek(pc, tall(false) / 3);
}
client.repaint();
}
///////////////////////////////////////////////////////////////////////////
// Event Handlers //
///////////////////////////////////////////////////////////////////////////
// Key down
private void onKeyDown(KeyEvent e) {
int code = e.getKeyCode();
int count = tall(false);
int mods = e.getModifiersEx();
var vue = parent.parent.vue;
boolean alt = (mods & InputEvent.ALT_DOWN_MASK ) != 0;
boolean ctrl = (mods & InputEvent.CTRL_DOWN_MASK) != 0;
// No Alt combinations
if (alt) return;
// Goto
if (ctrl && code == KeyEvent.VK_G) {
String text = JOptionPane.showInputDialog(
this, "Goto:", "Goto", JOptionPane.PLAIN_MESSAGE);
Object eval = vue.evaluate(text);
int addr = 0;
if (eval instanceof Integer)
addr = (Integer) eval;
else if (eval instanceof Long)
addr = (int) (long) (Long) eval;
else return;
if (!isVisible(addr))
seek(addr, count / 3);
return;
}
// Processing by key code
int pc = vue.getRegister(Vue.PC, true);
var step = parent.parent.brkStep;
switch (code) {
case KeyEvent.VK_UP : seek(address, 1); break;
case KeyEvent.VK_DOWN : seek(address, -1); break;
case KeyEvent.VK_PAGE_UP : seek(address, count); break;
case KeyEvent.VK_PAGE_DOWN: seek(address, -count); break;
// Single Step
case KeyEvent.VK_F11:
step.setCondition("!fetch&&pc!=" + pc);
step.setEnabled(true);
vue.emulate(20000000);
if (step.evaluate())
step.setEnabled(false);
parent.parent.refreshDebug(true);
break;
// Run to Next
case KeyEvent.VK_F12:
step.setCondition("!fetch&&pc==" +
(pc + Instruction.size(vue.read(pc, Vue.U16) >> 10)));
step.setEnabled(true);
vue.emulate(20000000);
if (step.evaluate())
step.setEnabled(false);
parent.parent.refreshDebug(true);
break;
}
}
// Mouse wheel
private void onMouseWheel(MouseWheelEvent e) {
seek(address, -e.getUnitsToScroll());
}
// Client paint
private void onPaint(Graphics2D g, int width, int height) {
var vue = parent.parent.vue;
int pc = vue.getRegister(Vue.PC, true);
// The view is being shown for the first time
if (!shown) {
shown = true;
seek(pc, tall(false) / 3);
return;
}
// Configure working variables
int address = this.address;
int count = tall(true);
var data = new byte[count * 4];
int lineHeight = parent.parent.app.fntMono.metrics.getHeight();
int offset = 0;
// Disassemble from the current address
vue.readBytes(address, data, 0, data.length);
widths[3] = widths[4] = 0;
for (int y = 0; y < count; y++) {
var row = y < rows.size() ? rows.get(y) : addRow();
var color = SystemColor.windowText;
// Disassemble the instruction
row.inst.decode(data, offset);
Disassembler.disassemble(address, row.inst, row);
updateRow(row);
// Highlight PC
if (address == pc) {
Color bg = SystemColor.control;
if (client.isFocusOwner()) {
bg = SystemColor.textHighlight;
color = SystemColor.textHighlightText;
}
g.setColor(bg);
g.fillRect(
0, y * lineHeight,
width, lineHeight
);
}
// Configure label text color
for (var label : row.labels)
label.setForeground(color);
// Advance to the next instruction
int size = address + 2 == pc ? 2 : row.inst.size;
address += size;
offset += size;
}
// Configure all rows
for (int y = 0; y < rows.size(); y++) {
var row = rows.get(y);
int top = y * lineHeight;
// Configure all labels
for (int z = 0, x = 0; z < 5; z++) {
var label = row.labels[z];
// The label is not part of the output
if (y >= count || widths[z] == 0) {
label.setVisible(false);
continue;
}
// Configure the label
label.setLocation(x, top);
label.setSize(label.getPreferredSize());
x += widths[z] + lineHeight;
}
}
// Update the client's size
var size = client.getPreferredSize();
width = -lineHeight + 1;
for (int x = 0; x < 5; x++)
if (widths[x] != 0 && (x != 2 || showBytes))
width += lineHeight + widths[x];
if (width == size.width)
return;
client.setPreferredSize(new Dimension(width, 0));
revalidate();
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Create a new row object
private Row addRow() {
var row = new Row();
row.inst = new Instruction();
row.labels = new JLabel[5];
// Initialize columns
for (int x = 0; x < 5; x++) {
var label = row.labels[x] = new JLabel();
if (x != 4)
label.setFont(parent.parent.app.fntMono);
client.add(label);
}
rows.add(row);
return row;
}
// Determine whether the instruction at an address is in the client view
private boolean isVisible(int target) {
int count = Math.max(1, tall(false));
var data = new byte[count * 4];
int offset = 0;
var vue = parent.parent.vue;
int pc = vue.getRegister(Vue.PC, true);
target &= 0xFFFFFFFE;
// Load enough bytes to represent every fully visible instruction
vue.readBytes(address, data, 0, data.length);
// Iterate through instructions
for (int x = 0, address = this.address; x < count; x++) {
// Determine the instruction's size
int size = address + 2 == pc ? 2 :
Instruction.size(data[offset + 1] >> 2 & 0x3F);
// The current instruction is the target
if (address == target || size == 4 && address + 2 == target)
return true;
// Advance to the next instruction
address += size;
offset += size;
}
// The instruction is not visible in the view
return false;
}
// Determine the address of the top row of output
private void seek(int target, int row) {
var data = new byte[(Math.abs(row) + 9) * 4];
int offset = 0;
var vue = parent.parent.vue;
int pc = vue.getRegister(Vue.PC, true);
target &= 0xFFFFFFFE;
// Scrolling down
if (row <= 0) {
// Load bytes starting up to 8 instructions prior to the target
address = target - 32;
vue.readBytes(address, data, 0, data.length);
// Iterate through instructions
for (boolean found = false;;) {
// Determine the instruction's size
int size = address + 2 == pc ? 2 :
Instruction.size(data[offset + 1] >> 2 & 0x3F);
// The current instruction is the target
if (address == target || size == 4 && address + 2 == target)
found = true;
if (found && row++ == 0)
break;
// Advance to the next instruction
address += size;
offset += size;
}
}
// Scrolling up
else {
// Load bytes starting up to 8 instructions prior to the top row
address = target - data.length + 4;
vue.readBytes(address, data, 0, data.length);
// Iterate through instructions
var addresses = new int[row]; // Circular buffer
for (int index = 0;;) {
// Determine the instruction's size
int size = pc == address + 2 ? 2 :
Instruction.size(data[offset + 1] >> 2 & 63);
// The current instruction is the target
if (address == target || size == 4 && address + 2 == target) {
address = addresses[index];
break;
}
// Advance to the next instruction
addresses[index] = address;
if (++index == row)
index = 0;
address += size;
offset += size;
}
}
// Common processing
client.repaint();
}
// Determine how many rows of output are visible
private int tall(boolean partial) {
int lineHeight = parent.parent.app.fntMono.metrics.getHeight();
return Math.max(1, (client.getHeight() +
(partial ? lineHeight - 1 : 0)
) / lineHeight);
}
// Update a row with its text and measure the column widths
private void updateRow(Row row) {
for (int x = 0; x < 5; x++) {
var label = row.labels[x];
String text = null;
switch (x) {
case 0: text = row.address ; break;
case 1: text = row.bytes ; break;
case 2: text = row.mnemonic; break;
case 3: text = row.operands; break;
}
if (text != null) {
label.setText(text);
label.setVisible(x == 1 ? showBytes : true);
widths[x] = Math.max(widths[x],label.getPreferredSize().width);
} else label.setVisible(false);
}
}
}

View File

@ -0,0 +1,477 @@
package app;
// Java imports
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.swing.*;
import javax.swing.filechooser.*;
// Project imports
import util.*;
import vue.*;
// Main application window
class MainWindow extends JFrame {
// Instance fields
App app; // Containing application
Breakpoint brkStep; // Single step internal breakpoint
int[][][] palettes; // Raster palettes
byte[] vram; // Snapshot of VIP memory
Vue vue; // Emulation core context
// Private fields
private boolean debugMode; // Window is in debug mode
private int number; // Window number within application
private boolean only; // This is the only application window
private File pwd; // Most recent working directory
private ROM rom; // Currently loaded ROM
private File romFile; // Currently loaded ROM file
// UI components
private UPanel client; // Common client container
private JDesktopPane desktop; // Container for child windows
private JMenu mnuDebug; // Debug menu
private UPanel video; // Video output
private JMenuItem mnuFileDebugMode; // File -> Debug mode
private JMenuItem mnuFileGameMode; // File -> Game mode
// Child windows
private BGMapsWindow bgMaps;
private BreakpointsWindow breakpoints;
private CharactersWindow characters;
private ConsoleWindow console;
private CPUWindow cpu;
private MemoryWindow memory;
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Palette indexes
static final int GENERIC = 0;
static final int GPLT0 = 1;
static final int GPLT1 = 2;
static final int GPLT2 = 3;
static final int GPLT3 = 4;
static final int JPLT0 = 5;
static final int JPLT1 = 6;
static final int JPLT2 = 7;
static final int JPLT3 = 8;
static final int LEFT = 0;
static final int RIGHT = 1;
static final int RED = 2;
// Application icon
private static final BufferedImage APPICON;
// Static initializer
static {
APPICON = Util.imageRead("images/app_icon.png");
}
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
MainWindow(App app) {
super();
// Configure instance fields
this.app = app;
palettes = new int[9][3][4];
pwd = Util.PWD;
vram = new byte[0x40000];
vue = Vue.create(app.getUseNative());
System.out.println("Native: " +
(vue.isNative() ? Vue.getNativeID() : "No"));
// Configure video pane
video = new UPanel();
video.setPreferredSize(new Dimension(384, 224));
video.setFocusable(true);
video.addPaintListener((g,w,h)->onPaintVideo(g, w, h));
// Configure client area
client = new UPanel(new BorderLayout());
client.add(video, BorderLayout.CENTER);
// Configure window
addWindowListener(Util.onClose(e->onClose()));
setContentPane(client);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setIconImage(APPICON);
setJMenuBar(initMenus());
app.localizer.add(this, "app.title.default");
// Configure child windows
desktop = new JDesktopPane();
desktop.setBackground(SystemColor.controlShadow);
desktop.add(bgMaps = new BGMapsWindow (this));
desktop.add(breakpoints = new BreakpointsWindow(this));
desktop.add(characters = new CharactersWindow (this));
desktop.add(console = new ConsoleWindow (this));
desktop.add(cpu = new CPUWindow (this));
desktop.add(memory = new MemoryWindow (this));
// Configure internal breakpoints
brkStep = vue.breakpoint();
brkStep.setRead(true);
// Display window
refreshDebug(true);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
///////////////////////////////////////////////////////////////////////////
// Menu Constructors //
///////////////////////////////////////////////////////////////////////////
// Produce the window's menu bar
private JMenuBar initMenus() {
var bar = new JMenuBar();
var loc = app.localizer;
bar.add(initMenuFile (loc));
bar.add(initMenuDebug(loc));
return bar;
}
// Initialize the Debug menu
private JMenu initMenuDebug(Localizer loc) {
mnuDebug = new JMenu();
loc.add(mnuDebug, "app.debug.(menu)");
mnuDebug.setVisible(false);
var mnuDebugBreakpoints = new JMenuItem();
loc.add(mnuDebugBreakpoints, "app.debug.breakpoints");
mnuDebugBreakpoints.addActionListener(e->breakpoints.setVisible(true));
mnuDebug.add(mnuDebugBreakpoints);
var mnuDebugConsole = new JMenuItem();
loc.add(mnuDebugConsole, "app.debug.console");
mnuDebugConsole.addActionListener(e->console.setVisible(true));
mnuDebug.add(mnuDebugConsole);
var mnuDebugCPU = new JMenuItem();
loc.add(mnuDebugCPU, "app.debug.cpu");
mnuDebugCPU.addActionListener(e->cpu.setVisible(true));
mnuDebug.add(mnuDebugCPU);
var mnuDebugMemory = new JMenuItem();
loc.add(mnuDebugMemory, "app.debug.memory");
mnuDebugMemory.addActionListener(e->memory.setVisible(true));
mnuDebug.add(mnuDebugMemory);
mnuDebug.addSeparator();
var mnuDebugBGMaps = new JMenuItem();
loc.add(mnuDebugBGMaps, "app.debug.bg_maps");
mnuDebugBGMaps.addActionListener(e->bgMaps.setVisible(true));
mnuDebug.add(mnuDebugBGMaps);
var mnuDebugCharacters = new JMenuItem();
loc.add(mnuDebugCharacters, "app.debug.characters");
mnuDebugCharacters.addActionListener(e->characters.setVisible(true));
mnuDebug.add(mnuDebugCharacters);
var mnuDebugFrameBuffers = new JMenuItem();
mnuDebugFrameBuffers.setEnabled(false);
loc.add(mnuDebugFrameBuffers, "app.debug.frame_buffers");
mnuDebug.add(mnuDebugFrameBuffers);
var mnuDebugObjects = new JMenuItem();
mnuDebugObjects.setEnabled(false);
loc.add(mnuDebugObjects, "app.debug.objects");
mnuDebug.add(mnuDebugObjects);
var mnuDebugWorlds = new JMenuItem();
mnuDebugWorlds.setEnabled(false);
loc.add(mnuDebugWorlds, "app.debug.worlds");
mnuDebug.add(mnuDebugWorlds);
return mnuDebug;
}
// Initialize the File menu
private JMenu initMenuFile(Localizer loc) {
var mnuFile = new JMenu();
loc.add(mnuFile, "app.file.(menu)");
var mnuFileLoadRom = new JMenuItem();
loc.add(mnuFileLoadRom, "app.file.load_rom");
mnuFileLoadRom.addActionListener(e->onLoadROM());
mnuFile.add(mnuFileLoadRom);
mnuFileDebugMode = new JMenuItem();
loc.add(mnuFileDebugMode, "app.file.debug_mode");
mnuFileDebugMode.addActionListener(e->onDebugMode(true));
mnuFile.add(mnuFileDebugMode);
mnuFileGameMode = new JMenuItem();
loc.add(mnuFileGameMode, "app.file.game_mode");
mnuFileGameMode.addActionListener(e->onDebugMode(false));
mnuFileGameMode.setVisible(false);
mnuFile.add(mnuFileGameMode);
mnuFile.addSeparator();
var mnuFileNewWindow = new JMenuItem();
loc.add(mnuFileNewWindow, "app.file.new_window");
mnuFileNewWindow.addActionListener(e->onNewWindow());
mnuFile.add(mnuFileNewWindow);
var mnuFileExit = new JMenuItem();
loc.add(mnuFileExit, "app.file.exit");
mnuFileExit.addActionListener(e->onClose());
mnuFile.add(mnuFileExit);
return mnuFile;
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Draw a character into a pixel buffer
void drawCharacter(int index, boolean hFlip, boolean vFlip, int[] palette,
int[] buffer, int offset, int stride) {
int addr = index >> 9 << 15 | 0x00006000 | (index & 511) << 4;
for (int y = 0; y < 8; y++, offset += stride - 8)
for (int x = 0; x < 8; x++, offset += 1 ) {
int ix = hFlip ? 7 - x : x;
int iy = vFlip ? 7 - y : y;
int b = addr | iy << 1 | ix >> 2;
int bit = (ix & 3) << 1;
buffer[offset] = palette[vram[b] >> bit & 3];
}
}
// Refresh all debug views
void refreshDebug(boolean seekToPC) {
vue.readBytes(0x00000000, vram, 0, vram.length);
refreshPalettes();
refreshDebugLite(seekToPC);
}
// Refresh all debug views without retrieving video memory
void refreshDebugLite(boolean seekToPC) {
bgMaps .refresh();
breakpoints.refresh();
characters .refresh();
cpu .refresh(seekToPC);
memory .refresh();
}
// A window has been added to or removed from the program state
void windowsChanged(int number, boolean only) {
this.number = number;
this.only = only;
app.localizer.put(this, "ctrl.number", "" + number);
updateTitle();
}
///////////////////////////////////////////////////////////////////////////
// Event Handlers //
///////////////////////////////////////////////////////////////////////////
// Window close, File -> Exit
private void onClose() {
app.removeWindow(this);
cpu.dispose();
dispose();
vue.dispose();
}
// File -> Debug mode, File -> Game mode
private void onDebugMode(boolean debugMode) {
this.debugMode = debugMode;
mnuFileDebugMode.setVisible(!debugMode);
mnuFileGameMode .setVisible( debugMode);
// Transition to debug mode
if (debugMode) {
client.remove(video);
client.add(desktop, BorderLayout.CENTER);
console.setContentPane(video);
console.firstShow();
mnuDebug.setVisible(true);
}
// Transition to game mode
else {
client.remove(desktop);
client.add(video, BorderLayout.CENTER);
mnuDebug.setVisible(false);
}
client.revalidate();
client.repaint();
}
// File -> Load ROM
private void onLoadROM() {
var loc = app.localizer;
// Prompt the user to select a file
var dlgFile = new JFileChooser(pwd);
dlgFile.addChoosableFileFilter(new FileNameExtensionFilter(
loc.get("dialog.ext_vb"), "vb"));
dlgFile.addChoosableFileFilter(new FileNameExtensionFilter(
loc.get("dialog.ext_isx"), "isx"));
dlgFile.setAcceptAllFileFilterUsed(true);
dlgFile.setDialogTitle(loc.get("dialog.load_rom"));
int option = dlgFile.showDialog(this, loc.get("dialog.load"));
// The user did not select a file
var file = dlgFile.getSelectedFile();
if (option != JFileChooser.APPROVE_OPTION || file == null)
return;
// Update the current directory
pwd = file.getParentFile();
// Read the file
var data = Util.fileRead(file);
if (data == null) {
JOptionPane.showMessageDialog(this,
loc.get("dialog.load_rom_error"),
loc.get("dialog.load_rom"),
JOptionPane.ERROR_MESSAGE
);
return;
}
// Process the ROM
ROM rom = null;
try { rom = new ROM(data); }
catch (Exception e) {
JOptionPane.showMessageDialog(this,
loc.get("dialog.load_rom_notvb"),
loc.get("dialog.load_rom"),
JOptionPane.ERROR_MESSAGE
);
return;
}
// Update instance fields
this.rom = rom;
romFile = file;
loc.put(this, "ctrl.filename", file.getName());
updateTitle();
// Update the emulation state
var bytes = rom.toByteArray();
// Pause emulation
vue.setROM(bytes, 0, bytes.length);
vue.reset();
refreshDebug(true);
// Resume emulation
}
// File -> New window
private void onNewWindow() {
app.addWindow();
}
// Video paint
private void onPaintVideo(Graphics2D g, int width, int height) {
int scale = Math.max(1, Math.min(width / 384, height / 224));
g.translate(
Math.max(0, (width - scale * 384) / 2),
Math.max(0, (height - scale * 224) / 2)
);
g.scale(scale, scale);
g.setColor(Color.black);
g.fillRect(0, 0, 384, 224);
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Update the palette composites
private void refreshPalettes() {
// Process brightness levels
int[] brt = { 0,
vue.read(0x0005F824, Vue.U8),
vue.read(0x0005F826, Vue.U8),
vue.read(0x0005F828, Vue.U8)
};
brt[3] += brt[0] + brt[1];
for (int x = 1; x < 4; x++)
brt[x] = (Math.min(127, brt[x]) * 510 + 127) / 254 << 1;
// Process all palettes
var pal = new int[4];
for (int x = 0; x < 9; x++) {
// Generic palette
if (x == GENERIC) {
pal[1] = 0x55 << 1;
pal[2] = 0xAA << 1;
pal[3] = 0xFF << 1;
}
// Palette from emulation state
else {
int bits = vue.read(0x0005F860 + (x - 1 << 1), Vue.U8);
pal[1] = brt[bits >> 2 & 3];
pal[2] = brt[bits >> 4 & 3];
pal[3] = brt[bits >> 6 ];
}
// Process colors
for (int y = 0; y < 3; y++) {
var base = app.rgbBase[y];
var dest = palettes[x][y];
dest[0] = 0xFF000000 | app.rgbClear;
for (int z = 1; z < 4; z++) {
dest[z] = 0xFF000000;
for (int w = 0, bits = 16; w < 3; w++, bits -= 8)
dest[z] |= (pal[z] * base[w] + 255) / 510 << bits;
}
}
}
}
// Separate the RGB components of a color
private static int[] split(int rgb) {
return new int[] { rgb >> 16 & 0xFF, rgb >> 8 & 0xFF, rgb & 0xFF };
}
// Update the window title
private void updateTitle() {
app.localizer.add(this,
only ? romFile != null ?
"app.title.rom" :
"app.title.default"
: romFile != null ?
"app.title.mixed" :
"app.title.number"
);
}
}

View File

@ -0,0 +1,248 @@
package app;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
// Memory viewer and hex editor window
class MemoryWindow extends ChildWindow {
// Private fields
private int address; // Address of top row
// UI components
private UPanel client; // Client area
private ArrayList<Row> rows; // Rows of text
///////////////////////////////////////////////////////////////////////////
// Classes //
///////////////////////////////////////////////////////////////////////////
// One row of output
private class Row {
JLabel address; // Address (row header)
JLabel[] bytes; // Hexadecimal bytes
}
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
MemoryWindow(MainWindow parent) {
super(parent, "memory.title");
// Configure instance fields
address = 0x00000000;
rows = new ArrayList<Row>();
// Configure client area
client = new UPanel();
client.addComponentListener(Util.onResize(e->onResize()));
client.addKeyListener(Util.onKey(e->onKeyDown(e), null));
client.addMouseWheelListener(e->onWheel(e));
client.setBackground(SystemColor.window);
client.setFocusable(true);
client.setPreferredSize(new Dimension(480, 360));
// Configure component
var content = new UPanel(new BorderLayout());
content.setBorder(new JScrollPane().getBorder());
content.add(client, BorderLayout.CENTER);
setContentPane(content);
pack();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Update the display
void refresh() {
// The element is not ready
if (client == null)
return;
// Configure working variables
int height = client.getHeight();
int lineHeight = parent.app.fntMono.metrics.getHeight();
int count = (height + lineHeight - 1) / lineHeight;
var data = new byte[count * 16];
// Retrieve all visible bytes from the emulation context
parent.vue.readBytes(address, data, 0, data.length);
// Update visible rows
for (int x = 0; x < count; x++) {
Row row;
if (x < rows.size())
row = rows.get(x); // Retrieve row from collection
else row = createRow(); // Produce a new row
update(row, x * lineHeight, address + x * 16, data, x * 16);
setVisible(row, true);
}
// Hide any rows that are not visible
for (int x = count; x < rows.size(); x++)
setVisible(rows.get(x), false);
// Finalize layout
client.revalidate();
client.repaint();
}
///////////////////////////////////////////////////////////////////////////
// Event Handlers //
///////////////////////////////////////////////////////////////////////////
// Key down
private void onKeyDown(KeyEvent e) {
int code = e.getKeyCode();
int mods = e.getModifiersEx();
boolean alt = (mods & InputEvent.ALT_DOWN_MASK ) != 0;
boolean ctrl = (mods & InputEvent.CTRL_DOWN_MASK) != 0;
int tall = Math.max(1, tall(false));
// No Alt combinations
if (alt) return;
// Goto
if (ctrl && code == KeyEvent.VK_G) {
String text = JOptionPane.showInputDialog(
this, "Goto:", "Goto", JOptionPane.PLAIN_MESSAGE);
Object eval = parent.vue.evaluate(text);
int addr = 0;
if (eval instanceof Integer)
addr = (Integer) eval;
else if (eval instanceof Long)
addr = (int) (long) (Long) eval;
else return;
setAddress(addr);
return;
}
// Seek
switch (code) {
case KeyEvent.VK_UP : setAddress(address - 16); break;
case KeyEvent.VK_DOWN : setAddress(address + 16); break;
case KeyEvent.VK_PAGE_UP : setAddress(address - tall * 16); break;
case KeyEvent.VK_PAGE_DOWN: setAddress(address + tall * 16); break;
}
}
// Client resize
private void onResize() {
refresh();
}
// Mouse wheel
private void onWheel(MouseWheelEvent e) {
int amount = e.getUnitsToScroll();
int mods = e.getModifiersEx();
boolean alt = (mods & InputEvent.ALT_DOWN_MASK ) != 0;
boolean ctrl = (mods & InputEvent.CTRL_DOWN_MASK) != 0;
// No Alt or Ctrl combinations
if (amount == 0 || alt || ctrl)
return;
// Seek
setAddress(address + 16 * amount);
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Add a new row of output
private Row createRow() {
var font = parent.app.fntMono;
var row = new Row();
// Address label
row.address = new JLabel();
row.address.setFont(font);
row.address.setForeground(SystemColor.windowText);
row.address.setVisible(false);
client.add(row.address);
// Byte labels
row.bytes = new JLabel[16];
for (int x = 0; x < row.bytes.length; x++) {
var label = row.bytes[x] = new JLabel();
label.setFont(font);
label.setForeground(SystemColor.windowText);
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setVisible(false);
client.add(label);
}
// Add the row to the collection
rows.add(row);
return row;
}
// Measure the minimum column widths for a row
private void measure(Row row, int[] widths) {
widths[0] = Math.max(widths[0], row.address.getPreferredSize().width);
for (int x = 0; x < 16; x++)
widths[1] = Math.max(widths[1],
row.bytes[x].getPreferredSize().width);
}
// Specify the address of the top row of output
private void setAddress(int address) {
this.address = address & 0xFFFFFFF0;
refresh();
}
// Show or hide a row
private void setVisible(Row row, boolean visible) {
row.address.setVisible(visible);
for (var label : row.bytes)
label.setVisible(visible);
}
// Determine how many rows of output are visible
private int tall(boolean partial) {
int lineHeight = parent.app.fntMono.metrics.getHeight();
return (client.getHeight() + (partial?lineHeight-1:0)) / lineHeight;
}
// Update the text of a row
private void update(Row row, int y, int address, byte[] data, int offset) {
int hexDigitWidth = parent.app.hexDigitWidth;
int lineHeight = parent.app.fntMono.metrics.getHeight();
// Update address
row.address.setBounds(0, y, 8 * hexDigitWidth, lineHeight);
row.address.setText(String.format("%08X", address));
// Update bytes
for (int z = 0, x = 10 * hexDigitWidth; z < 16; z++) {
var label = row.bytes[z];
label.setBounds(x, y, 2 * hexDigitWidth, lineHeight);
label.setText(String.format("%02X", data[offset++] & 0xFF));
x += hexDigitWidth * (z == 7 ? 4 : 3);
}
}
}

254
src/desktop/app/ROM.java Normal file
View File

@ -0,0 +1,254 @@
package app;
// Java imports
import java.nio.charset.*;
import java.util.*;
// Cartridge ROM module
public class ROM {
// Instance fields
private byte[] data; // Binary data
private int format; // File format of loaded file
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Formats
public static final int RAW = 0;
public static final int ISX = 1;
///////////////////////////////////////////////////////////////////////////
// Classes //
///////////////////////////////////////////////////////////////////////////
// ISX code segments
private static class Code {
int address; // CPU address
int offset; // Position within file
int size; // Number of bytes
Code(int o, int a, int s) { address = a; offset = o; size = s; }
}
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
ROM(byte[] data) {
// Attempt to decode as ISX
try { isxDecode(data); return; } catch (Exception e) { }
// Attempt to decode as raw
try { rawDecode(data); return; } catch (Exception e) { }
// Unable to identify the file contents
throw new RuntimeException();
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Retrieve the game code from the ROM header
public String getGameCode() {
var bytes = new byte[4];
System.arraycopy(data, data.length - 517, bytes, 0, 4);
return new String(bytes, StandardCharsets.ISO_8859_1);
}
// Retrieve the file format of the loaded ROM file
public int getFormat() {
return format;
}
// Retrieve the maker code from the ROM header
public String getMaker() {
var bytes = new byte[2];
System.arraycopy(data, data.length - 519, bytes, 0, 2);
return new String(bytes, StandardCharsets.ISO_8859_1);
}
// Retrieve the number of bytes in the ROM data
public int getSize() {
return data.length;
}
// Retrieve the game title from the ROM header
public String getTitle() {
return ShiftJIS.decode(data, data.length - 544, 20).trim();
}
// Retrieve the version number from the ROM header
public int getVersion() {
return data[data.length - 513] & 0xFF;
}
// Produce a byte array containing the ROM contents
public byte[] toByteArray() {
var ret = new byte[data.length];
System.arraycopy(data, 0, ret, 0, data.length);
return ret;
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Decode the file data as ISX
private void isxDecode(byte[] data) {
var codes = isxParse(data); // ISX code segments
int head = -1; // Latest address in bottom half of ROM address space
int tail = -1; // Earliest address in upper half of ROM address space
// Process all code segments
for (var code : codes) {
int end = code.address + code.size - 1;
int start = code.address;
boolean isHead = end >= 0; // Lower half of CPU address range
boolean isTail = start < 0; // Upper half of CPU address range
// The segment spans the middle of the ROM address space
if (isHead && isTail) {
head = 0x7FFFFF;
tail = 0x800000;
break;
}
// Track the segment's position within the ROM address space
end &= 0x00FFFFFF;
start &= 0x00FFFFFF;
if (isHead && (head == -1 || head < end )) head = end;
if (isTail && (tail == -1 || tail > start)) tail = start;
}
// Determine the required ROM size
int size = 1024;
int minSize = (head == -1 ? 0 : head + 1) +
(tail == -1 ? 0 : 0x01000000 - tail);
if (minSize == 0 || tail == -1 &&
(minSize < 1024 || (minSize - 1 & minSize) != 0))
throw new RuntimeException();
for (; size < minSize; size <<= 1);
// Transfer the code segments into the ROM buffer
this.data = new byte[size];
for (var code : codes)
System.arraycopy(
data , code.offset,
this.data, code.address & size - 1,
code.size
);
// The ROM is valid
format = ISX;
}
// Parse the code records from the ISX data
private Code[] isxParse(byte[] data) {
int count;
int offset = 0;
var ret = new ArrayList<Code>();
// Check for an extended ISX header
if (readInt(data, 0, 3) == 0x585349) // ASCII "ISX"
offset = 32;
// Process records
while (offset != data.length)
switch (data[offset++] & 0xFF) {
// SNES code
case 0x01:
if ((data[offset++] & 0xFF) >= 0x80) // Bank
offset++; // BankHigh
offset += 2; // Address
offset += 2 + readInt(data, offset, 2); // Data
break;
// SNES range
case 0x03:
// Count, { Bank, StartAddress, EndAddress, Type }
offset += 2 + readInt(data, offset, 2) * 6;
break;
// SNES symbol
case 0x04:
count = readInt(data, offset, 2); offset += 2;
for (; count > 0; count--) // Name, Flags, Bank, Address
offset += 5 + readInt(data, offset, 1);
break;
// Virtual Boy code
case 0x11:
int address = readInt(data, offset, 4); offset += 4;
int size = readInt(data, offset, 4); offset += 4;
if ((address & 0x07000000) != 0x07000000 || size < 0 ||
(address & 0x00FFFFFF) + size > 0x01000000)
throw new RuntimeException();
ret.add(new Code(offset, address, size));
offset += size;
break;
// Virtual Boy range
case 0x13:
// Count, { StartAddress, EndAddress, Type }
offset += 2 + readInt(data, offset, 2) * 9;
break;
// Virtual Boy symbol
case 0x14:
count = readInt(data, offset, 2); offset += 2;
for (; count > 0; count--) // Name, Flags, Address
offset += 7 + readInt(data, offset, 1);
break;
// System
case 0x20: case 0x21: case 0x22:
offset += 4 + readInt(data, offset, 4); // Debug, undocumented
break;
// Invalid record type
default:
throw new RuntimeException();
}
return ret.toArray(new Code[ret.size()]);
}
// Decode the file as raw
private void rawDecode(byte[] data) {
// Validate length
if (
data.length < 1024 || // Exception handlers and ROM header
data.length > 0x00FFFFFF || // 24-bit bus width
(data.length & data.length - 1) != 0 // Must be a power of 2
) throw new RuntimeException();
// The ROM is valid
this.data = data;
format = RAW;
}
// Read an integer from a byte array
private static int readInt(byte[] data, int offset, int size) {
int ret = 0;
for (size--; size >= 0; size--)
ret = ret << 8 | data[offset + size] & 0xFF;
return ret;
}
}

View File

@ -0,0 +1,521 @@
package app;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
import vue.*;
// Register list item
class Register {
// Instance fields
private boolean expandable; // The expansion area can be shown
private boolean expanded; // The expanded area is being shown
private int index; // Register index
private int mode; // Display mode for program registers
private String name; // Register name
private RegisterList parent; // Containing register list
private int type; // Expansion controls type
private int value; // Current register value
// UI components
UPanel expansion; // Expansion container
JLabel btnExpand; // Expand button
UPanel indent; // Expansion area indentation
JLabel lblName; // Register name
JTextField txtValue; // Register value
ArrayList<JComponent> controls; // Expansion controls
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Modes
static final int FLOAT = 3;
static final int HEX = 0;
static final int SIGNED = 1;
static final int UNSIGNED = 2;
// Types
static final int PLAIN = -2;
static final int PROGRAM = -3;
// Expand button labels
static final String COLLAPSE = "-";
static final String EXPAND = "+";
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
Register(RegisterList parent, String name, int index, int type) {
parent.add(index, this);
// Configure instance fields
controls = new ArrayList<JComponent>();
expandable = type != PLAIN && index != 0;
this.index = index;
mode = HEX;
this.name = name;
this.parent = parent;
this.type = type;
// Click handler for expand and name controls
MouseListener expand = !expandable ? null : Util.onMouse(e->{
if (e.getButton() == 1) setExpanded(!expanded); }, null);
// Expand button
btnExpand = new JLabel(expandable ? EXPAND : "");
btnExpand.setHorizontalAlignment(SwingConstants.CENTER);
if (expandable)
btnExpand.addMouseListener(expand);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTH;
gbc.fill = GridBagConstraints.HORIZONTAL;
parent.add(gbc, btnExpand);
// Name label
lblName = new JLabel(" ");
if (expandable)
lblName.addMouseListener(expand);
gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTH;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.weightx = 1;
parent.add(gbc, lblName);
// Value text box
txtValue = new JTextField();
txtValue.setBorder(null);
txtValue.setOpaque(false);
txtValue.setText("00000000");
gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 4, 0, 0);
parent.add(gbc, txtValue);
// Value changed
txtValue.addActionListener(e->parent.requestFocus());
txtValue.addFocusListener(Util.onFocus(null, e->{
String text = txtValue.getText();
int val = value;
try { switch (mode) {
case HEX : val = (int) Long.parseLong(text, 16); break;
case SIGNED : // Fallthrough
case UNSIGNED: val = (int) Long.parseLong(text, 10); break;
case FLOAT : val =
Float.floatToIntBits(Float.parseFloat(text) ); break;
}} catch (Exception x) { }
setValue(val);
}));
// Expansion controls
switch (type) {
case PROGRAM : initProgram(); break;
case Vue.CHCW: initCHCW (); break;
case Vue.ECR : initECR (); break;
case Vue.PC : initPC (); break;
case Vue.PIR : initPIR (); break;
case Vue.PSW : initPSW (); break;
case Vue.TKCW: initTKCW (); break;
default: configure(); return;
}
// Expansion indentation
if (index != Vue.PC) {
indent = new UPanel();
indent.setOpaque(false);
indent.setPreferredSize(new Dimension(0, 0));
indent.setVisible(false);
gbc = new GridBagConstraints();
parent.add(gbc, indent);
}
// Expansion area
expansion.setOpaque(false);
expansion.setVisible(false);
gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.WEST;
if (index == Vue.PC)
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 4, 0, 0);
gbc.weightx = 1;
parent.add(gbc, expansion);
// Handling for PSW
if (index == Vue.PSW && type == Vue.PSW)
setExpanded(true);
// Apply application settings
configure();
}
///////////////////////////////////////////////////////////////////////////
// Expansion Constructors //
///////////////////////////////////////////////////////////////////////////
// Expansion controls for CHCW
private void initCHCW() {
expansion = new UPanel(new GridBagLayout());
addCheckBox("ICE", 1, false, true);
}
// Expansion controls for ECR
private void initECR() {
expansion = new UPanel(new GridBagLayout());
addTextBox("EICC", 0, 16, false, true);
addTextBox("FECC", 16, 16, false, true);
}
// Expansion controls for program registers
private void initProgram() {
expansion = new UPanel(new GridBagLayout());
var group = new ButtonGroup();
group.add(addRadioButton("cpu.hex" , HEX ));
group.add(addRadioButton("cpu.signed" , SIGNED ));
group.add(addRadioButton("cpu.unsigned", UNSIGNED));
group.add(addRadioButton("cpu.float" , FLOAT ));
}
// Expansion controls for PC
private void initPC() {
expansion = new UPanel(new GridBagLayout());
// Configure controls
for (int x = 0; x < 2; x++) {
// Indentation
indent = new UPanel();
indent.setOpaque(false);
indent.setPreferredSize(new Dimension(0, 0));
var gbc = new GridBagConstraints();
gbc.weightx = 1;
expansion.add(indent, gbc);
// Name label
var label = new JLabel();
parent.parent.parent.app.localizer.add(label,
x == 0 ? "cpu.jump_from" : "cpu.jump_to" );
gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTHWEST;
expansion.add(label, gbc);
controls.add(label);
// Value text box
var txt = new JTextField();
txt.addActionListener(e->parent.requestFocus());
txt.addFocusListener(Util.onFocus(null,
e->txt.setText((String) txt.getClientProperty("text"))));
txt.putClientProperty("index",
x == 0 ? Vue.JUMP_FROM : Vue.JUMP_TO);
txt.setBorder(null);
txt.setOpaque(false);
gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 4, 0, 0);
expansion.add(txt, gbc);
controls.add(txt);
}
}
// Expansion controls for PSW
private void initPSW() {
expansion = new UPanel(new GridBagLayout());
addCheckBox("Z" , 0, false, false);
addCheckBox("FRO", 9, false, true );
addCheckBox("S" , 1, false, false);
addCheckBox("FIV", 8, false, true );
addCheckBox("OV" , 2, false, false);
addCheckBox("FZD", 7, false, true );
addCheckBox("CY" , 3, false, false);
addCheckBox("FOV", 6, false, true );
addCheckBox("EP" , 14, false, false);
addCheckBox("FUD", 5, false, true );
addCheckBox("NP" , 15, false, false);
addCheckBox("FPR", 4, false, true );
addCheckBox("AE" , 13, false, true );
addCheckBox("ID" , 12, false, false);
addTextBox ("I" , 16, 4, false, false);
}
// Expansion controls for PIR
private void initPIR() {
expansion = new UPanel(new GridBagLayout());
addTextBox("PT", 0, 16, true, true);
}
// Expansion controls for TKCW
private void initTKCW() {
expansion = new UPanel(new GridBagLayout());
addCheckBox("OTM", 8, true, false);
addCheckBox("FVT", 5, true, true );
addCheckBox("FIT", 7, true, false);
addCheckBox("FUT", 4, true, true );
addCheckBox("FZT", 6, true, false);
addCheckBox("FPT", 3, true, true );
addCheckBox("RDI", 2, true, false);
addTextBox ("RD", 0, 2, true, false);
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Apply configuration settings
void configure() {
String name = null;
// System register
if (type != PROGRAM) {
name = this.name;
if (!Disassembler.systemCaps)
name = name.toLowerCase();
}
// Program register
else {
if (Disassembler.programNames)
name = this.name;
if (name == null)
name = "r" + index;
if (Disassembler.programCaps)
name = name.toUpperCase();
}
// Name label
lblName.setText(name);
// Expand button
var size = btnExpand.getPreferredSize();
var metrics = parent.parent.parent.app.fntDialog.metrics;
size.width = 4 + Math.max(
metrics.stringWidth(EXPAND), metrics.stringWidth(COLLAPSE));
btnExpand.setPreferredSize(size);
// Value text box
var fntMono = parent.parent.parent.app.fntMono;
int hexDigitWidth = parent.parent.parent.app.hexDigitWidth;
txtValue.setFont(fntMono);
txtValue.setPreferredSize(new Dimension(
hexDigitWidth * 8 + 4, fntMono.metrics.getHeight()));
// Expansion controls
for (var ctrl : controls) {
if (!(ctrl instanceof JTextField))
continue;
if (type == Vue.PC || (Boolean) ctrl.getClientProperty("hex"))
((JTextField) ctrl).setFont(fntMono);
int digits = type == Vue.PC ? 8 :
(Integer) ctrl.getClientProperty("digits");
size = ctrl.getPreferredSize();
size.width = digits * hexDigitWidth + 4;
ctrl.setPreferredSize(size);
}
}
// Refresh controls
void refresh() {
var vue = parent.parent.parent.vue;
// Value text box
value = vue.getRegister(index, type != PROGRAM);
txtValue.setText(
type != PROGRAM || mode == HEX ?
String.format("%08X", value) :
mode == SIGNED ? Integer.toString(value) :
mode == UNSIGNED ? Long.toString(value & 0xFFFFFFFFL) :
Float.toString(Float.intBitsToFloat(value))
);
// Expansion controls
for (var control : controls) {
// Check box
if (control instanceof JCheckBox) {
var ctrl = (JCheckBox) control;
int bit = (Integer) ctrl.getClientProperty("bit");
ctrl.setSelected((value & 1 << bit) != 0);
}
// Text box
if (control instanceof JTextField) {
var ctrl = (JTextField) control;
int digits; // Maximum digits that can be displayed
boolean hex; // The value is hexadecimal
int val; // The value to be displayed
// Jump history
if (type == Vue.PC) {
digits = 8;
hex = true;
val = vue.getRegister((Integer)
ctrl.getClientProperty("index"), true);
}
// All other values
else {
int bit = (Integer) ctrl.getClientProperty("bit" );
digits = (Integer) ctrl.getClientProperty("digits");
hex = (Boolean) ctrl.getClientProperty("hex");
int width = (Integer) ctrl.getClientProperty("width");
val = value >> bit & (1 << width) - 1;
}
// Update the text
String text = !hex ? Integer.toString(val) : String.format(
"%0" + digits + (Disassembler.hexCaps ? "X" : "x"), val);
ctrl.putClientProperty("text", text);
ctrl.setText(text);
}
}
}
// Specify whether the expansion area is expanded
void setExpanded(boolean expanded) {
// Error checking
if (!expandable)
return;
// Update controls
this.expanded = expanded;
btnExpand.setText(expanded ? "-" : "+");
if (type != Vue.PC)
indent.setVisible(expanded);
expansion.setVisible(expanded);
parent.revalidate();
}
// Change the display mode of a program register
void setMode(int mode) {
this.mode = mode;
txtValue.setFont(mode!=HEX ? null : parent.parent.parent.app.fntMono);
refresh();
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Add a check box to the expansion area
private void addCheckBox(String name, int bit, boolean readOnly,
boolean last) {
int mask = 1 << bit;
// Configure control
var ctrl = new JCheckBox(name);
ctrl.putClientProperty("bit", bit);
ctrl.setBorder(null);
ctrl.setEnabled(!readOnly);
ctrl.setFocusable(false);
ctrl.setOpaque(false);
controls.add(ctrl);
// Event handler
ctrl.addItemListener(e->setValue(
e.getStateChange() == ItemEvent.SELECTED ?
value | mask : value & ~mask
));
// Configure expansion area
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.gridwidth = last ? GridBagConstraints.REMAINDER : 2;
gbc.insets = new Insets(0, 4, 0, 4);
expansion.add(ctrl, gbc);
}
// Add a radio button to the expansion area
private JRadioButton addRadioButton(String key, int mode) {
// Configure control
var ctrl = new JRadioButton();
parent.parent.parent.app.localizer.add(ctrl, key);
ctrl.setBorder(null);
ctrl.setFocusable(false);
ctrl.setOpaque(false);
ctrl.setSelected(mode == HEX);
controls.add(ctrl);
// Event handler
ctrl.addItemListener(e->{
if (e.getStateChange() == ItemEvent.SELECTED) setMode(mode); });
// Configure expansion area
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 4, 0, 0);
expansion.add(ctrl, gbc);
return ctrl;
}
// Add a text box to the expansion area
private void addTextBox(String name, int bit, int width, boolean readOnly,
boolean hex) {
int mask = (1 << width) - 1;
// Configure control
var ctrl = new JTextField();
ctrl.putClientProperty("bit", bit);
ctrl.putClientProperty("digits",
Integer.toString(mask, hex ? 16 : 10).length());
ctrl.putClientProperty("hex", hex);
ctrl.putClientProperty("width", width);
ctrl.setBorder(null);
ctrl.setEnabled(!readOnly);
ctrl.setOpaque(false);
controls.add(ctrl);
// Event handlers
ctrl.addActionListener(e->parent.requestFocus());
ctrl.addFocusListener(Util.onFocus(null, e->{
int val = value >> bit & mask;
try { val = Integer.parseInt(ctrl.getText(), hex ? 16 : 10); }
catch (Exception x) { }
setValue(value & ~(mask << bit) | (val & mask) << bit);
}));
// Configure expansion area
var label = new JLabel(name);
label.setEnabled(!readOnly);
var gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTHWEST;
gbc.insets = new Insets(0, 4, 0, 4);
expansion.add(label, gbc);
gbc = new GridBagConstraints();
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.insets = new Insets(0, 4, 0, 4);
expansion.add(ctrl , gbc);
}
// Update the register value
private void setValue(int value) {
parent.parent.parent.vue.setRegister(index, type != PROGRAM, value);
refresh();
if (index == Vue.PSW && type == Vue.PSW)
parent.registers.get(Vue.PC).refresh();
}
}

View File

@ -0,0 +1,203 @@
package app;
// Java imports
import java.awt.*;
import java.util.*;
import javax.swing.*;
// Project imports
import util.*;
import vue.*;
// List of CPU registers
class RegisterList extends JScrollPane {
// Package fields
CPUWindow parent; // Containing CPU window
HashMap<Integer, Register> registers; // Register items
// Private fields
private boolean shown; // Component has been shown
// UI components
private UPanel client; // Client area
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
RegisterList(CPUWindow parent, boolean system) {
super(VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_AS_NEEDED);
// Configure instance fields
this.parent = parent;
registers = new HashMap<Integer, Register>();
shown = true;
// Configure client area
client = new UPanel(new GridBagLayout()) {
public Dimension getPreferredSize() {
var ret = super.getPreferredSize();
if (!shown) ret.height = system ? getInitialHeight() : 0;
return ret;
}
public void paintComponent(Graphics g) {
shown = true;
super.paintComponent(g);
}
};
client.addMouseListener(Util.onMouse(e->client.requestFocus(), null));
client.setBackground(SystemColor.window);
client.setFocusable(true);
// Initialize system registers
if (system) {
new Register(this, "PC" , Vue.PC , Vue.PC );
new Register(this, "PSW" , Vue.PSW , Vue.PSW );
new Register(this, "EIPC" , Vue.EIPC , Register.PLAIN);
new Register(this, "EIPSW", Vue.EIPSW, Vue.PSW );
new Register(this, "FEPC" , Vue.FEPC , Register.PLAIN);
new Register(this, "FEPSW", Vue.FEPSW, Vue.PSW );
new Register(this, "ECR" , Vue.ECR , Vue.ECR );
new Register(this, "ADTRE", Vue.ADTRE, Register.PLAIN);
new Register(this, "CHCW" , Vue.CHCW , Vue.CHCW );
new Register(this, "PIR" , Vue.PIR , Vue.PIR );
new Register(this, "TKCW" , Vue.TKCW , Vue.TKCW );
new Register(this, "29" , 29, Register.PLAIN);
new Register(this, "30" , 30, Register.PLAIN);
new Register(this, "31" , 31, Register.PLAIN);
}
// Initialize program registers
else for (int x = 0; x < 32; x++) {
String name = null;
switch (x) {
case Vue.GP: name = "gp"; break;
case Vue.HP: name = "hp"; break;
case Vue.LP: name = "lp"; break;
case Vue.SP: name = "sp"; break;
case Vue.TP: name = "tp"; break;
}
new Register(this, name, x, Register.PROGRAM);
}
// List terminator
var spacer = new UPanel();
spacer.setOpaque(false);
spacer.setPreferredSize(new Dimension(0, 0));
var gbc = new GridBagConstraints();
gbc.gridheight = GridBagConstraints.REMAINDER;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.weighty = 1;
client.add(spacer, gbc);
// Configure component
shown = false;
setViewportView(client);
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Draw the component
public void paintComponent(Graphics g) {
if (!shown) {
shown = true;
revalidate();
}
super.paintComponent(g);
}
// Transfer focus to the client area
public void requestFocus() {
client.requestFocus();
}
// Recalculate layout
public void revalidate() {
if (client != null)
client.revalidate();
super.revalidate();
repaint();
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Add a register component to the client area
void add(GridBagConstraints gbc, JComponent comp) {
client.add(comp, gbc);
}
// Associate a register with its index
void add(int index, Register register) {
registers.put(index, register);
}
// Apply configuration settings
void configure2() {
// Configure registers
for (var reg : registers.values())
reg.configure();
// Configure component
getVerticalScrollBar().setUnitIncrement(
parent.parent.app.fntMono.metrics.getHeight());
}
// Update the display
void refresh() {
for (var reg : registers.values())
reg.refresh();
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Determine the initial height upon first show
private int getInitialHeight() {
int height = 0;
var layout = (GridBagLayout) client.getLayout();
int ret = 0;
int row = 0;
// Process controls until the target row is found
for (var control : client.getComponents()) {
var ctrl = (JComponent) control;
// Track the tallest control on the row
if (ctrl.isVisible())
height = Math.max(height, ctrl.getPreferredSize().height);
// This is not the last control on the row
if (layout.getConstraints(control).gridwidth !=
GridBagConstraints.REMAINDER)
continue;
// Advance to the next row
ret += height;
height = 0;
row++;
// The target row has been reached
if (row == 4)
break;
}
return ret;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,585 @@
package util;
// Java imports
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import javax.sound.sampled.*;
// Background timer using either a realtime clock or audio for timing
public class FrameTimer {
// Instance fields
private Callback callback; // Event handler
private double rate; // Frame rate in seconds
private int source; // Timing source
private int state; // Operation state
// Audio fields
private int block; // Current byte buffer index
private byte[][] blocks; // Reusable byte buffers
private double buffer; // Buffer length in seconds
private int channels; // Number channels
private double delay; // Delay in seconds
private int frames; // Audio frames per timer frame
private SourceDataLine line; // Output line
private int sampleRate; // Rate in samples per second
private boolean written; // Samples have been written this frame
private LinkedBlockingQueue<byte[]> queue; // Sample queue
// Threading fields
private Thread audio; // Background audio processing
private CyclicBarrier cycAudio; // Synchronization with audio thread
private CyclicBarrier cycTimer; // Synchronization with timer thread
private ReentrantLock lock; // Allows callback to control the timer
private Thread timer; // Background timer processing
// Type for timer event handler
public interface Callback {
void onFrame(FrameTimer source);
}
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Sources
public static final int CLOCK = 0;
public static final int AUDIO = 1;
// States
public static final int STOPPED = 0;
public static final int RUNNING = 1;
public static final int PAUSED = 2;
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
public FrameTimer() {
buffer = 0.05;
cycAudio = new CyclicBarrier(2);
cycTimer = new CyclicBarrier(2);
channels = 2;
delay = 0.1;
lock = new ReentrantLock();
queue = new LinkedBlockingQueue<byte[]>();
rate = 1;
sampleRate = 44100;
state = STOPPED;
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Retrieve the audio buffer size
public synchronized double getBuffer() {
return buffer;
}
// Retrieve the number of audio channels
public synchronized int getChannels() {
return channels;
}
// Retrieve the audio delay
public synchronized double getDelay() {
return delay;
}
// Calculate the number of audio frames per timer frame
public synchronized int getFrames() {
return audioFrames(rate);
}
// Retrieve the frame rate
public synchronized double getRate() {
return rate;
}
// Retrieve the audio sampling rate
public synchronized int getSampleRate() {
return sampleRate;
}
// Retrieve the timing source
public synchronized int getSource() {
return source;
}
// Retrieve the operation state
public synchronized int getState() {
return state;
}
// Pause the timer
public synchronized boolean pause() {
boolean isTimer = Thread.currentThread() == timer;
// The timer thread already owns the lock
if (isTimer && lock.isHeldByCurrentThread()) {
if (state == RUNNING)
state = PAUSED;
return state == PAUSED;
}
// Obtain the lock
lock.lock();
// Invalid state
if (state != RUNNING) {
lock.unlock();
return state == PAUSED;
}
// Configure state
state = PAUSED;
// Pause timer thread
if (!isTimer) try {
timer.interrupt();
lock.unlock();
cycTimer.await();
} catch (Exception e) { }
return true;
}
// Resume running the paused timer
public synchronized boolean resume() {
boolean isTimer = Thread.currentThread() == timer;
// The timer thread already owns the lock
if (isTimer && lock.isHeldByCurrentThread()) {
if (state == PAUSED)
state = RUNNING;
return state == RUNNING;
}
// Obtain the lock
lock.lock();
// Invalid state
if (state != PAUSED) {
lock.unlock();
return state == RUNNING;
}
// Configure state
state = RUNNING;
// Resume timer thread
if (!isTimer) try {
lock.unlock();
cycTimer.await();
} catch (Exception e) { }
return true;
}
// Specify the audio buffer length
public synchronized double setBuffer(double buffer) {
return this.buffer = state == STOPPED && buffer > 0 ?
buffer : this.buffer;
}
// Specify the callback handler
public synchronized boolean setCallback(Callback callback) {
if (state != STOPPED)
return false;
this.callback = callback;
return true;
}
// Specify the number of audio channels
public synchronized int setChannels(int channels) {
return this.channels = state == STOPPED && (channels - 1 & ~1) != 0 ?
channels : this.channels;
}
// Specify the audio delay
public synchronized double setDelay(double delay) {
return this.delay = state == STOPPED && delay > 0 ? delay : this.delay;
}
// Specify the frame rate
public synchronized double setRate(double rate) {
return this.rate = state == STOPPED && rate > 0 ? rate : this.rate;
}
// Specify the audio sampling rate
public synchronized int setSampleRate(int sampleRate) {
return this.sampleRate = state == STOPPED && sampleRate > 0 ?
sampleRate : this.sampleRate;
}
// Begin timer operations
public synchronized boolean start(int source) {
// Error checking
if (source != CLOCK && source != AUDIO || state != STOPPED ||
callback == null || Thread.currentThread() == timer ||
source == AUDIO && buffer > delay) {
return false;
}
// Audio processing
if (source == AUDIO) try {
// Open the output line
AudioFormat fmt =
new AudioFormat(sampleRate, 16, channels, true, false);
line = AudioSystem.getSourceDataLine(fmt);
line.open(fmt);
// Configure audio fields
frames = audioFrames(rate);
block = 0;
blocks = new byte[(int) Math.ceil(delay / rate)]
[frames * channels * 2];
}
// Could not open the audio line
catch (Exception e) {
line = null;
return false;
}
// Configure state
this.source = source;
state = RUNNING;
// Spawn and start timer thread
cycTimer.reset();
timer = new Thread(()->timer());
timer.setDaemon(true);
timer.start();
// Spawn and start audio thread
if (source == AUDIO) {
cycAudio.reset();
audio = new Thread(()->audio());
audio.setDaemon(true);
audio.start();
}
// Synchronize with timer thread
try { cycTimer.await(); } catch (Exception e) { }
return true;
}
// End timer operations
public synchronized boolean stop() {
boolean isTimer = Thread.currentThread() == timer;
// The timer thread already owns the lock
if (isTimer && lock.isHeldByCurrentThread())
return true;
// Obtain the lock
lock.lock();
// Invalid state
if (state == STOPPED) {
lock.unlock();
return true;
}
// Configure state
boolean paused = state == PAUSED;
state = STOPPED;
// Stop timer thread
if (!isTimer) try {
if (!paused) timer.interrupt();
lock.unlock();
if ( paused) cycTimer.await();
cycTimer.await();
} catch (Exception e) { }
return true;
}
// Write audio samples as bytes to output
public boolean write(byte[] samples, int offset) {
int size = frames * channels * 2;
// Error checking
if (Thread.currentThread() != timer || state == STOPPED || written ||
samples == null || offset < 0 || samples.length < offset + size)
return false;
// Add a new sample block to the queue
block = (block + 1) % blocks.length;
byte[] block = blocks[this.block];
System.arraycopy(samples, offset, block, 0, size);
queue.offer(block);
return written = true;
}
// Write audio samples as shorts to output
public boolean write(short[] samples, int offset) {
int size = frames * channels;
// Error checking
if (Thread.currentThread() != timer || state == STOPPED || written ||
samples == null || offset < 0 || samples.length < offset + size)
return false;
// Encode the samples as bytes
block = (block + 1) % blocks.length;
byte[] block = blocks[this.block];
for (int src = 0, dest = 0; src < size; src++) {
short sample = samples[offset + src];
block[dest++] = (byte) sample;
block[dest++] = (byte) (sample >> 8);
}
queue.offer(block);
return written = true;
}
// Write audio samples as floats to output (range -1 to +1)
public boolean write(float[] samples, int offset) {
int size = frames * channels;
// Error checking
if (Thread.currentThread() != timer || state == STOPPED || written ||
samples == null || offset < 0 || samples.length < offset + size)
return false;
// Encode the samples as bytes
block = (block + 1) % blocks.length;
byte[] block = blocks[this.block];
for (int src = 0, dest = 0; src < size; src++) {
short sample = (short) Math.round(32767 *
Math.min(1, Math.max(-1, samples[offset + src])) );
block[dest++] = (byte) sample;
block[dest++] = (byte) (sample >> 8);
}
queue.offer(block);
return written = true;
}
///////////////////////////////////////////////////////////////////////////
// Private Methds //
///////////////////////////////////////////////////////////////////////////
// Calculate the number of audio sampling frames in some number of seconds
private int audioFrames(double seconds) {
return Math.max(1, (int) Math.round(seconds * sampleRate));
}
// Handler for pause operations -- invoked by timer thread
private long onPause(long reference, boolean isCallback) {
// Track the current time
if (source == CLOCK)
reference = System.nanoTime() - reference;
// Pause audio thread
else try {
audio.interrupt();
line.stop();
cycAudio.await();
} catch (Exception e) { }
// Synchronization
try {
if (!isCallback)
cycTimer.await(); // Synchronize with invoking thread
else lock.unlock();
cycTimer.await(); // Wait for resume() or stop()
} catch (Exception e) { }
// Calculate a new reference time
if (source == CLOCK)
reference = System.nanoTime() - reference;
// Unpause audio thread
else try { cycAudio.await(); } catch (Exception e) { }
return reference;
}
// Handler for stop operations -- invoked by timer thread
private int onStop(boolean paused, boolean isCallback) {
// Stop the audio thread
if (source == AUDIO) try {
audio.interrupt();
line.stop();
cycAudio.await();
} catch (Exception e) { }
// Synchronization
try {
if (!isCallback || paused)
cycTimer.await(); // Synchronize with invoking thread
else lock.unlock();
} catch (Exception e) { }
// Cleanup
blocks = null;
timer = null;
return 0;
}
///////////////////////////////////////////////////////////////////////////
// Thread Methods //
///////////////////////////////////////////////////////////////////////////
// Audio thread entry point
private void audio() {
// Synchronize with timer thread
try { cycAudio.await(); } catch (Exception e) { }
// Initialize working variables
byte[] block = new byte[audioFrames(delay ) * channels * 2];
byte[] buffer = new byte[audioFrames(this.buffer) * channels * 2];
int blockPos = 0;
int bufferPos = -buffer.length; // Less than 0 means not full
// Audio processing
line.start();
for (;;) {
// Fill the buffer with samples, blocking until full
while (bufferPos < 0) {
// Load bytes from the current block
if (blockPos < block.length) {
int size = Math.min(block.length - blockPos, -bufferPos);
System.arraycopy(block, blockPos, buffer,
bufferPos + buffer.length, size);
blockPos += size;
bufferPos += size;
}
// Fetch a new sample block, blocking until one is available
else try {
block = queue.take();
blockPos = 0;
}
// The timer state has changed
catch (Exception e) {
audio.interrupt(); // take() clears interrupt status
break;
}
}
// Send samples to the output, blocking until sent
if (!audio.isInterrupted()) {
bufferPos +=
line.write(buffer, bufferPos, buffer.length - bufferPos);
if (bufferPos == buffer.length)
bufferPos = -buffer.length;
}
// Check for changes to the timer state
if (state == RUNNING)
continue;
Thread.interrupted();
// Timer has paused
if (state == PAUSED) try {
cycAudio.await(); // Synchronize with timer thread
cycAudio.await(); // Wait for resume() or stop()
} catch (Exception e) { }
// Timer has stopped
if (state == STOPPED) {
try { line.close(); } catch (Exception e) { }
try { cycAudio.await(); } catch (Exception e) { }
audio = null;
return;
}
// Resume playback
line.start();
}
}
// Timer thread entry point
private int timer() {
// Synchronize with other threads
try {
if (source == AUDIO)
cycAudio.await(); // Synchronize with audio thread
cycTimer.await(); // Synchronize with parent thread
} catch (Exception e) { }
// Initialize working variables
byte[] empty = new byte[frames * channels * 2];
long reference = source == AUDIO ? 0 : System.nanoTime();
long target = source == AUDIO ? audioFrames(rate) :
Math.max(1, (long) Math.round(rate * 1000000000L));
// Timer processing
for (boolean first = true;; first = false) {
long current = source == CLOCK ? System.nanoTime() :
line.getLongFramePosition();
long remain = target - current + reference;
// Processing on all frames but the first
if (!first) {
// Wait until the next frame interval
if (remain > 0) try {
if (source == AUDIO)
remain = remain * 1000000000L / sampleRate;
remain = Math.max(1000000, remain);
Thread.sleep(remain / 1000000, (int) (remain % 1000000));
continue;
} catch (Exception e) { timer.interrupt(); }
// Another thread configured the timer state
if (Thread.interrupted()) {
if (state == PAUSED)
reference = onPause(reference, false);
if (state == STOPPED)
return onStop(false, false);
continue;
}
}
// Invoke the event callback
written = false;
callback.onFrame(this);
if (source == AUDIO && !written)
queue.offer(empty);
// The callback configured the timer state
if (lock.isHeldByCurrentThread()) {
boolean paused = state == PAUSED;
if (state == PAUSED)
reference = onPause(reference, true);
if (state == STOPPED)
return onStop(paused, true);
continue;
}
// Track processed frame time
reference = current + remain;
}
}
}

View File

@ -0,0 +1,249 @@
package util;
// Java imports
import java.io.*;
// Little-endian implementation of DataInputStream
public class LEDataInputStream extends FilterInputStream {
// Instance fields
private DataInputStream data; // Data-decoding stream
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
public LEDataInputStream(InputStream stream) {
super(stream = stream instanceof LEDataInputStream ?
((LEDataInputStream) stream).in : stream);
data = stream instanceof DataInputStream ?
(DataInputStream) stream : new DataInputStream(stream);
}
// Byte array constructor
public LEDataInputStream(byte[] data) {
this(new ByteArrayInputStream(data));
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Reads a boolean from the underlying input stream
public boolean readBoolean() throws IOException {
return data.readByte() != 0;
}
// Reads an array of booleans from the underlying input stream
public boolean[] readBooleans(int count) throws IOException {
return readBooleans(new boolean[count], 0, count);
}
// Reads an array of booleans from the underlying input stream
public boolean[] readBooleans(boolean[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
v[offset + x] = readBoolean();
return v;
}
// Reads a byte from the underlying input stream
public byte readByte() throws IOException {
return data.readByte();
}
// Reads an array of bytes from the underlying input stream
public byte[] readBytes(int count) throws IOException {
return readBytes(new byte[count], 0, count);
}
// Reads an array of bytes from the underlying input stream
public byte[] readBytes(byte[] v, int offset, int length)
throws IOException {
data.read(v, offset, length);
return v;
}
// Reads a character from the underlying input stream
public char readChar() throws IOException {
return (char) Short.reverseBytes(data.readShort());
}
// Reads an array of characters from the underlying input stream
public char[] readChars(int count) throws IOException {
return readChars(new char[count], 0, count);
}
// Reads an array of characters from the underlying input stream
public char[] readChars(char[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
v[offset + x] = readChar();
return v;
}
// Reads a double from the underlying input stream
public double readDouble() throws IOException {
return readDouble(false);
}
// Reads a double from the underlying input stream
public double readDouble(boolean finite) throws IOException {
long bits = readLong();
double ret = Double.longBitsToDouble(bits);
if (finite && !Double.isFinite(ret)) throw new RuntimeException(
String.format("Non-finite double value 0x%016X", bits));
return ret;
}
// Reads an array of doubles from the underlying input stream
public double[] readDoubles(int count) throws IOException {
return readDoubles(new double[count], 0, count, false);
}
// Reads an array of doubles from the underlying input stream
public double[] readDoubles(int count, boolean finite) throws IOException {
return readDoubles(new double[count], 0, count, finite);
}
// Reads an array of doubles from the underlying input stream
public double[] readDoubles(double[] v, int offset, int length)
throws IOException {
return readDoubles(v, offset, length, false);
}
// Reads an array of doubles from the underlying input stream
public double[] readDoubles(double[] v, int offset, int length,
boolean finite) throws IOException {
for (int x = 0; x < length; x++)
v[offset + x] = readDouble(finite);
return v;
}
// Reads a float from the underlying input stream
public float readFloat() throws IOException {
return readFloat(false);
}
// Reads a float from the underlying input stream
public float readFloat(boolean finite) throws IOException {
int bits = readInt();
float ret = Float.intBitsToFloat(bits);
if (finite && !Float.isFinite(ret)) throw new RuntimeException(
String.format("Non-finite float value 0x%08X", bits));
return ret;
}
// Reads an array of floats from the underlying input stream
public float[] readFloats(int count) throws IOException {
return readFloats(new float[count], 0, count, false);
}
// Reads an array of floats from the underlying input stream
public float[] readFloats(int count, boolean finite) throws IOException {
return readFloats(new float[count], 0, count, finite);
}
// Reads an array of floats from the underlying input stream
public float[] readFloats(float[] v, int offset, int length)
throws IOException {
return readFloats(v, offset, length, false);
}
// Reads an array of floats from the underlying input stream
public float[] readFloats(float[] v, int offset, int length,
boolean finite) throws IOException {
for (int x = 0; x < length; x++)
v[offset + x] = readFloat(finite);
return v;
}
// Reads an int from the underlying input stream
public int readInt() throws IOException {
return Integer.reverseBytes(data.readInt());
}
// Reads an array of ints from the underlying input stream
public int[] readInts(int count) throws IOException {
return readInts(new int[count], 0, count);
}
// Reads an array of ints from the underlying input stream
public int[] readInts(int[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
v[offset + x] = readInt();
return v;
}
// Reads a long from the underlying input stream
public long readLong() throws IOException {
return Long.reverseBytes(data.readLong());
}
// Reads an array of longs from the underlying input stream
public long[] readLongs(int count) throws IOException {
return readLongs(new long[count], 0, count);
}
// Reads an array of longs from the underlying input stream
public long[] readLongs(long[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
v[offset + x] = readLong();
return v;
}
// Reads a 24-bit integer from the underlying input stream
public int readMiddle() throws IOException {
int v = readShort() & 0xFFFF;
return readByte() << 16 | v;
}
// Reads an array of 24-bit integers from the underlying input stream
public int[] readMiddles(int count) throws IOException {
return readMiddles(new int[count], 0, count);
}
// Reads an array of 24-bit integers from the underlying input stream
public int[] readMiddles(int[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
v[offset + x] = readMiddle();
return v;
}
// Reads a short from the underlying input stream
public short readShort() throws IOException {
return Short.reverseBytes(data.readShort());
}
// Reads an array of shorts from the underlying input stream
public short[] readShorts(int count) throws IOException {
return readShorts(new short[count], 0, count);
}
// Reads an array of shorts from the underlying input stream
public short[] readShorts(short[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
v[offset + x] = readShort();
return v;
}
// Reads from the stream in a modified UTF-8 string
public String readUTF() throws IOException {
return data.readUTF();
}
// Advances forward in the stream some number of bytes
public int skipBytes(int n) throws IOException {
return data.skipBytes(n);
}
}

View File

@ -0,0 +1,218 @@
package util;
// Java imports
import java.io.*;
import java.nio.charset.*;
// Little-endian implementation of DataOutputStream
public class LEDataOutputStream extends FilterOutputStream {
// Instance fields
private DataOutputStream data; // Data-encoding stream
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
public LEDataOutputStream() {
this(new ByteArrayOutputStream());
}
// Stream constructor
public LEDataOutputStream(OutputStream stream) {
super(stream = stream instanceof LEDataOutputStream ?
((LEDataOutputStream) stream).out : stream);
data = stream instanceof DataOutputStream ?
(DataOutputStream) stream : new DataOutputStream(stream);
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Returns the current value of the underlying buffer
public int size() {
return out instanceof ByteArrayOutputStream ?
((ByteArrayOutputStream) out).size() : -1;
}
// Produce a byte array containing this stream's data
public byte[] toByteArray() throws IOException {
return out instanceof ByteArrayOutputStream ?
((ByteArrayOutputStream) out).toByteArray() : null;
}
// Writes a boolean to the underlying output stream as a 1-byte value
public void writeBoolean(boolean v) throws IOException {
data.writeBoolean(v);
}
// Writes an array of booleans to the underlying output stream
public void writeBooleans(boolean[] v) throws IOException {
writeBooleans(v, 0, v.length);
}
// Writes an array of booleans to the underlying output stream
public void writeBooleans(boolean[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
writeBoolean(v[offset + x]);
}
// Writes out a byte to the underlying output stream as a 1-byte value
public void writeByte(int v) throws IOException {
data.writeByte(v);
}
// Writes out the string to the underlying output stream
public void writeBytes(String s) throws IOException {
data.writeBytes(s);
}
// Writes an array of bytes to the underlying output stream
public void writeBytes(byte[] v) throws IOException {
writeBytes(v, 0, v.length);
}
// Writes an array of bytes to the underlying output stream
public void writeBytes(byte[] v, int offset, int length)
throws IOException {
data.write(v, offset, length);
}
// Writes a character to the underlying output stream as a 2-byte value
public void writeChar(int v) throws IOException {
data.writeChar(Short.reverseBytes((short) v));
}
// Writes a string to the underlying output stream
public void writeChars(String s) throws IOException {
writeChars(s.toCharArray());
}
// Writes an array of characters to the underlying output stream
public void writeChars(char[] v) throws IOException {
writeChars(v, 0, v.length);
}
// Writes an array of characters to the underlying output stream
public void writeChars(char[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
writeChar(v[offset + x]);
}
// Converts the double argument to a long using doubleToLongBits
public void writeDouble(double v) throws IOException {
data.writeLong(Long.reverseBytes(Double.doubleToLongBits(v)));
}
// Writes an array of doubles to the underlying output stream
public void writeDoubles(double[] v) throws IOException {
writeDoubles(v, 0, v.length);
}
// Writes an array of doubles to the underlying output stream
public void writeDoubles(double[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
writeDouble(v[offset + x]);
}
// Converts the float argument to an int using floatToIntBits
public void writeFloat(float v) throws IOException {
data.writeInt(Integer.reverseBytes(Float.floatToIntBits(v)));
}
// Writes an array of floats to the underlying output stream
public void writeFloats(float[] v) throws IOException {
writeFloats(v, 0, v.length);
}
// Writes an array of floats to the underlying output stream
public void writeFloats(float[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
writeFloat(v[offset + x]);
}
// Writes an int to the underlying output stream as four bytes
public void writeInt(int v) throws IOException {
data.writeInt(Integer.reverseBytes(v));
}
// Writes an array of ints to the underlying output stream
public void writeInts(int[] v) throws IOException {
writeInts(v, 0, v.length);
}
// Writes an array of ints to the underlying output stream
public void writeInts(int[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
writeInt(v[offset + x]);
}
// Writes an int to the underlying output stream as three bytes
public void writeMiddle(int v) throws IOException {
writeShort((short) v );
writeByte ((byte ) (v >> 16));
}
// Writes an array of 24-bit integers to the underlying output stream
public void writeMiddles(int[] v) throws IOException {
writeMiddles(v, 0, v.length);
}
// Writes an array of 24-bit integers to the underlying output stream
public void writeMiddles(int[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
writeMiddle(v[offset + x]);
}
// Writes a long to the underlying output stream as eight bytes
public void writeLong(long v) throws IOException {
data.writeLong(Long.reverseBytes(v));
}
// Writes an array of longs to the underlying output stream
public void writeLong(long[] v) throws IOException {
writeLongs(v, 0, v.length);
}
// Writes an array of longs to the underlying output stream
public void writeLongs(long[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
writeLong(v[offset + x]);
}
// Writes a short to the underlying output stream as two bytes
public void writeShort(int v) throws IOException {
data.writeShort(Short.reverseBytes((short) v));
}
// Writes an array of shorts to the underlying output stream
public void writeShorts(short[] v) throws IOException {
writeShorts(v, 0, v.length);
}
// Writes an array of shorts to the underlying output stream
public void writeShorts(short[] v, int offset, int length)
throws IOException {
for (int x = 0; x < length; x++)
writeShort(v[offset + x]);
}
// Writes a string to the underlying output stream using modified UTF-8
public void writeUTF(String str) throws IOException {
data.writeUTF(str);
}
}

View File

@ -0,0 +1,538 @@
package util;
// Java imports
import java.util.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.text.*;
// UI display text manager
public class Localizer {
// Instance fields
private HashMap<Object, Control> controls; // Control mapping
private Locale locale; // Current message store
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Class reference for JComboBox<String>
JComboBox<String> JCOMBOBOX = new JComboBox<String>();
///////////////////////////////////////////////////////////////////////////
// Classes //
///////////////////////////////////////////////////////////////////////////
// Control settings
private static class Control {
String key; // Single-string dictionary key
String[] keys; // Multiple-string dictionary key
String tipKey; // Tooltip dictionary key
HashMap<String, String> tags; // Message overrides
Control(String tipKey) {
tags = new HashMap<String, String>();
this.tipKey = tipKey;
}
}
// Locale container
public static class Locale implements Comparable<Locale> {
// Public fields
public final String id; // Unique identifier
public final String name; // Display name
// Private fields
private HashMap<String, String> messages; // Message dictionary
// Constructor
private Locale(HashMap<String, String> messages) {
id = messages.get("locale.id");
this.messages = messages;
name = messages.get("locale.name");
}
// Comparator
public int compareTo(Locale o) {
return id.compareTo(o.id);
}
// Represent this object as a string
public String toString() {
return id + " - " + name;
}
}
///////////////////////////////////////////////////////////////////////////
// Static Methods //
///////////////////////////////////////////////////////////////////////////
// Parse a text file to produce a Locale object
public static Locale parse(String text) {
var chars = text.replaceAll("\\r\\n?", "\n").toCharArray();
var ret = new HashMap<String, String>(); // Output dictionary
int start = 0; // Position of first character in key or value
var stack = new Stack<String>(); // Nested key chain
int state = 0; // Current parsing context
// Process all characters
for (int x = 0, line = 1, col = 1; x < chars.length; x++, col++) {
char c = chars[x];
boolean end = x == chars.length - 1;
String pos = line + ":" + col + ": ";
boolean white = c == ' ' || c == '\t';
// Processing for newline
if (c == '\n') {
col = 0;
line++;
}
// Comment
if (state == -1) {
if (c == '\n' || end)
state = 0;
}
// Pre-key
else if (state == 0) {
// Ignore leading whitespace
if (white)
continue;
// The line is a comment
if (c == '#') {
state = -1;
continue;
}
// End of input has been reached
if (end)
break;
// The line is empty
if (c == '\n')
continue;
// Proceed to key
start = x;
state = 1;
}
// Key
else if (state == 1) {
// Any non-whitespace character is valid in a key
if (!white && c != '\n' && !end)
continue;
// Produce the key as a string
String key = new String(chars, start, x - start).trim();
// Close a key group
if (key.equals("}")) {
// Nesting error
if (stack.size() == 0) throw new RuntimeException(
pos + "Unexpected '}'");
// Syntax error
outer: for (int y = x; y < chars.length; y++) {
char h = chars[y];
if (h == '\n') break;
if (h != ' ' && h != '\t') throw new RuntimeException(
pos + "Newline expected");
}
// Proceed to key
state = 0;
stack.pop();
continue;
}
// Curly braces are not valid in keys
if (key.contains("{") || key.contains("}"))
throw new RuntimeException(
line + ": Curly braces are not allowed in keys");
// Proceed to interim
stack.push(key);
state = 2;
}
// Post-key, pre-value
if (state == 2) {
// Ignore any whitespace between key and value
if (white)
continue;
// No value is present
if (c == '\n') throw new RuntimeException(
pos + "Unexpected newline");
if (end) throw new RuntimeException(
pos + "Unexpected end of input");
// Proceed to value
start = x;
state = 3;
}
// Value
if (state == 3) {
// Escape sequence
if (c == '\\') {
// EOF
if (x == chars.length - 1)
throw new RuntimeException(
pos + "Unexpected end of input");
// Cannot escape a newline
if (chars[x + 1] == '\n')
throw new RuntimeException(
pos + "Unexpected newline");
// Skip the next character
x++;
continue;
}
// The end of the value has not yet been reached
if (c != '\n' && !end)
continue;
// Produce the value as a string
if (end)
x++;
String value = new String(chars, start, x - start).trim();
state = 0;
// Open a key group
if (value.equals("{")) {
state = 0;
continue;
}
// Check for nesting errors
int depth = 0;
for (int y = start; y < x; y++) {
switch (chars[y]) {
case '\\': y++; continue;
case '{' : depth++; continue;
case '}' : if (--depth == -1)
throw new RuntimeException(
(line - 1) + ": Unexpected '}'"
);
continue;
}
}
if (depth != 0) throw new RuntimeException(
pos + "'}' expected");
// Produce the full key as a string
var pkey = new StringBuilder();
for (String item : stack)
pkey.append((pkey.length() != 0 ? "." : "") + item);
String key = pkey.toString();
// Check for duplicate keys
String lkey = key.toLowerCase();
if (ret.get(lkey) != null) throw new RuntimeException(
(line-1)+ ": Key '" + key + "' has already been defined.");
// Add the pair to the dictionary
ret.put(lkey, value);
stack.pop();
}
}
// Perform post-processing error checks
if (state != 0 || !stack.empty()) throw new RuntimeException(
"Unexpected end of input");
if (!ret.containsKey("locale.id")) throw new RuntimeException(
"Required key not found: 'locale.id'");
if (!ret.containsKey("locale.name")) throw new RuntimeException(
"Required key not found: 'locale.name'");
ret.put("null", "");
return new Locale(ret);
}
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
public Localizer() {
controls = new HashMap<Object, Control>();
}
// Parsing constructor
public Localizer(String text) {
this();
setLocale(parse(text));
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Add a control to the collection
public boolean add(Object control, Object key) {
return add(control, key, null);
}
// Add a control with a tooltip to the collection
public boolean add(Object control, Object key, String tipKey) {
var ctrl = controls.get(control);
if (ctrl == null)
ctrl = new Control(tipKey);
// Error checking
if (control == null || key == null)
return false;
// Control takes a single string
if (key instanceof String) {
// Type validation
if (!(
control instanceof AbstractButton ||
control instanceof JFrame ||
control instanceof JInternalFrame ||
control instanceof JLabel ||
control instanceof JPanel || // TitledBorder
control instanceof JTextComponent
)) return false;
// Configure key
ctrl.key = (String) key;
}
// Control takes an array of strings
else if (key instanceof String[]) {
// Type validation
if (!(
JCOMBOBOX.getClass().isAssignableFrom(control.getClass())
)) return false;
// Configure keys
String[] keys = (String[]) key;
ctrl.keys = new String[keys.length];
System.arraycopy(keys, 0, ctrl.keys, 0, keys.length);
}
// Invalid control type
else return false;
// Add the control to the collection
controls.put(control, ctrl);
update(control);
return true;
}
// Remove all controls from being managed
public void clearControls() {
controls.clear();
}
// Evaluate the message for a given key
public String get(String key) {
return key == null ? null : evaluate(null, key);
}
// Retrieve the currently loaded locale
public Locale getLocale() {
return locale;
}
// Configure a control tag
public String put(Object control, String key, String value) {
var ctrl = controls.get(control);
// Error checking
if (ctrl == null || key == null)
return null;
// Update the control's tags
key = key.toLowerCase();
String ret = value == null ?
ctrl.tags.remove(key) :
ctrl.tags.put(key, value)
;
// Refresh the text on the control
update(control);
return ret;
}
// Remove a control from the collection
public boolean remove(Object control) {
return controls.remove(control) != null;
}
// Specify a message dictionary
public void setLocale(Locale locale) {
this.locale = locale;
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Process substitutions and escapes on a message for a given key
private String evaluate(Control control, String key) {
// No locale is loaded
if (locale == null)
return key;
// Check that the key exists
String ret = locale.messages.get(key.toLowerCase());
if (ret == null)
return key;
// Perform all substitutions
outer: for (;;) {
var chars = ret.toCharArray();
int start = 0;
// Look for the first complete substitution
for (int x = 0; x < chars.length; x++) {
char c = chars[x];
// Escape sequence
if (c == '\\') {
x++;
continue;
}
// Begin substitution
if (c == '{')
start = x + 1;
// Substitution has not yet ended
if (c != '}')
continue;
// Determine the substitution
key = new String(chars, start, x - start);
String lkey = key.toLowerCase();
String value = control == null ? null : control.tags.get(lkey);
if (value == null)
value = locale.messages.get(lkey);
if (value == null)
value = "\\{" + key + "\\}";
// Apply the substitution to the message
ret =
new String(chars, 0, start - 1) +
value +
new String(chars, x + 1, chars.length - x - 1)
;
continue outer;
}
// No further substitutions
break;
}
// Unescape all escape sequences
var chars = ret.toCharArray();
int length = 0;
for (int x = 0; x < chars.length; x++, length++) {
char c = chars[x];
if (c == '\\') switch (c = chars[++x]) {
case 'n': c = '\n'; break;
case 't': c = '\t'; break;
}
chars[length] = c;
}
return new String(chars, 0, length);
}
// Update the text for all controls
private void update() {
for (var control : controls.keySet())
update(control);
}
// Update the text for a control
private void update(Object control) {
var ctrl = controls.get(control);
String[] keys = ctrl.key==null ? ctrl.keys : new String[]{ctrl.key};
String[] values = new String[keys.length];
// Evaluate all messages
for (int x = 0; x < keys.length; x++)
values[x] = evaluate(ctrl, keys[x]);
// Update the control's text
if (control instanceof AbstractButton)
((AbstractButton) control).setText (values[0]);
if (control instanceof JFrame)
((JFrame ) control).setTitle(values[0]);
if (control instanceof JInternalFrame)
((JInternalFrame) control).setTitle(values[0]);
if (control instanceof JLabel )
((JLabel ) control).setText (values[0]);
if (control instanceof JTextComponent)
((JTextComponent) control).setText (values[0]);
// JPanel must be wrapped in a TitledBorder
if (control instanceof JPanel) {
var border = ((JPanel) control).getBorder();
if (border instanceof TitledBorder)
((TitledBorder) border).setTitle(values[0]);
}
// Replace the contents of a JComboBox without firing events
if (JCOMBOBOX.getClass().isAssignableFrom(control.getClass())) {
// The type is explicitly verified above
@SuppressWarnings("unchecked")
var box = (JComboBox<String>) control;
// Configure working variables
var action = box.getActionListeners();
int index = box.getSelectedIndex();
var item = box.getItemListeners();
// Remove event listeners
for (var lst : action) box.removeActionListener(lst);
for (var lst : item ) box.removeItemListener (lst);
// Update contents
box.setModel(new DefaultComboBoxModel<String>(values));
box.setSelectedIndex(index);
// Restore event listeners
for (var lst : action) box.addActionListener(lst);
for (var lst : item ) box.addItemListener (lst);
}
// Update the control's tooltip text
if (control instanceof JComponent)
((JComponent) control).setToolTipText(
ctrl.tipKey == null ? null : evaluate(ctrl, ctrl.tipKey));
}
}

View File

@ -0,0 +1,95 @@
package util;
// Java imports
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
// Wrapper around JComboBox<String> to enforce desired behaviors
public class UComboBox extends JComboBox<String> {
// Instance fields
private HashMap<ActionListener, ActionListener> actionListeners;
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Empty item set
private static final String[] EMPTY = new String[0];
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
public UComboBox() {
this(null);
}
// List constructor
public UComboBox(String[] items) {
super();
actionListeners = new HashMap<ActionListener, ActionListener>();
setItems(items);
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Add a listener to receive action events
public void addActionListener(ActionListener l) {
// Error checking
if (l == null)
return;
// Wrap the event listener in a guard
ActionListener L = e->{
// Search the stack trace for an invocation not from Java itself
var trace = Thread.currentThread().getStackTrace();
for (int x = 2; x < trace.length; x++)
if (trace[x].getModuleName() == null)
return;
// Call the event handler
l.actionPerformed(e);
};
// Manage the collections
actionListeners.put(l, L);
super.addActionListener(L);
}
// Retrieve a list of the current action listeners
public ActionListener[] getActionListeners() {
return actionListeners.keySet().toArray(
new ActionListener[actionListeners.size()]);
}
// Remove an action listener from the collection
public void removeActionListener(ActionListener l) {
var L = actionListeners.get(l);
if (L == null)
return;
actionListeners.remove(l);
super.removeActionListener(L);
}
// Update the items in the list
public void setItems(String[] items) {
int index = getSelectedIndex();
setModel(new DefaultComboBoxModel<String>(
items != null ? items : EMPTY));
setSelectedIndex(index);
}
}

View File

@ -0,0 +1,93 @@
package util;
// Java imports
import java.awt.*;
import java.util.*;
import javax.swing.*;
// Wrapper around JPanel to work around a bug in GridBagLayout
public class UPanel extends JPanel {
// Instance fields
private HashSet<PaintListener> paintListeners;
///////////////////////////////////////////////////////////////////////////
// Classes //
///////////////////////////////////////////////////////////////////////////
// Functional interface for paint events
public interface PaintListener {
void paint(Graphics2D g, int width, int height);
}
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
public UPanel() {
this(null);
}
// Layout manager constructor
public UPanel(LayoutManager layout) {
super(layout);
paintListeners = new HashSet<PaintListener>();
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Add a listener to receive paint events
public void addPaintListener(PaintListener l) {
if (l != null)
paintListeners.add(l);
}
// Retrieve a list of the current paint listeners
public PaintListener[] getPaintListeners() {
return paintListeners.toArray(
new PaintListener[paintListeners.size()]);
}
// Retrieve the maximum size of the component
public Dimension getMaximumSize() {
var ret = super.getMaximumSize();
return ret != null ? ret : new Dimension();
}
// Retrieve the minimum size of the component
public Dimension getMinimumSize() {
var ret = super.getMinimumSize();
return ret != null ? ret : new Dimension();
}
// Retrieve the preferred size of the component
public Dimension getPreferredSize() {
var ret = super.getPreferredSize();
return ret != null ? ret : new Dimension();
}
// Draw the component
public void paintComponent(Graphics g) {
super.paintComponent(g);
var G = (Graphics2D) g;
int width = getWidth();
int height = getHeight();
for (var lst : paintListeners)
lst.paint(G, width, height);
}
// Remove a paint listener from the collection
public void removePaintListener(PaintListener l) {
paintListeners.remove(l);
}
}

395
src/desktop/util/Util.java Normal file
View File

@ -0,0 +1,395 @@
package util;
// Java imports
import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import java.io.*;
import java.net.*;
import java.nio.charset.*;
import java.nio.file.*;
import java.util.*;
import javax.imageio.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.plaf.basic.*;
// General utility methods
public final class Util {
// This class cannot be instantiated
private Util() { }
///////////////////////////////////////////////////////////////////////////
// Classes //
///////////////////////////////////////////////////////////////////////////
// Event listener interfaces
public interface OnClose { void call(WindowEvent e); }
public interface OnClose2 { void call(InternalFrameEvent e); }
public interface OnFocus { void call(FocusEvent e); }
public interface OnKey { void call(KeyEvent e); }
public interface OnMouse { void call(MouseEvent e); }
public interface OnResize { void call(ComponentEvent e); }
public interface OnVisible { void call(AncestorEvent e); }
// Data class for byte-order marks
private static class BOM {
byte[] mark;
Charset set;
BOM(byte[] m, Charset s) { mark = m; set = s; }
}
// Font metrics wrapper
public static class Font extends java.awt.Font {
public final FontMetrics metrics;
public Font(java.awt.Font font) {
super(font);
metrics = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)
.getGraphics().getFontMetrics(this);
}
}
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Directory from which the JVM was initialized
public static final File PWD = new File(System.getProperty("user.dir"));
// Text file byte-order marks
private static final BOM[] BOMS = {
new BOM(new byte[] { -17, -69, -65 }, StandardCharsets.UTF_8 ),
new BOM(new byte[] { - 2, - 1 }, StandardCharsets.UTF_16BE),
new BOM(new byte[] { - 1, - 2 }, StandardCharsets.UTF_16LE),
};
// Available font families
private static final String[] FONTS;
// Filesystem state manager for the current .jar (if any)
private static final FileSystem JARFS;
// Static initializer
static {
// Font families
var fonts = GraphicsEnvironment
.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
Arrays.sort(fonts, String.CASE_INSENSITIVE_ORDER);
FONTS = fonts;
// .jar filesystem
FileSystem fs = null;
try {
fs = FileSystems.newFileSystem(
new URI("jar:" + Util.class.getProtectionDomain()
.getCodeSource().getLocation().toString()),
new HashMap<String, String>(),
Util.class.getClassLoader()
);
} catch (Exception e) { }
JARFS = fs;
}
///////////////////////////////////////////////////////////////////////////
// Static Methods //
///////////////////////////////////////////////////////////////////////////
// Apply an alpha value to a Color object
public static Color alpha(Color color, float alpha) {
int a = (int) Math.round(alpha * 255);
return color == null || a < 0 || a > 255 ? null :
new Color(color.getRGB() & 0x00FFFFFF | a << 24, true);
}
// Blend one color into another
public static Color blend(Color front, Color back, float alpha) {
// Error checking
if (front == null || back == null ||
!Float.isFinite(alpha) || alpha < 0 || alpha > 1)
return null;
// Decompose components
var colors = new Color[] { front, back };
var argb = new float[3][4];
for (int x = 0; x < 2; x++) {
int bits = colors[x].getRGB();
for (int y = 3; y >= 0; y++, bits >>= 8)
argb[x][y] = (bits & 0xFF) / 255.0f;
}
// Combine the colors
argb[2][0] = argb[0][0] + argb[1][0] * (1 - argb[0][0]);
for (int x = 1; x < 4; x++)
argb[2][x] = (
argb[0][x] * argb[0][0] +
argb[1][x] * argb[1][0] * (1 - argb[0][0])
) / argb[2][0];
// Produce the resulting Color object
int bits = 0;
for (int x = 0; x < 4; x++)
bits = bits << 8 | (int) Math.round(argb[2][x] * 255);
return new Color(bits, true);
}
// Read a file from disk
public static byte[] fileRead(File file) {
FileInputStream stream = null;
byte[] data = null;
try {
stream = new FileInputStream(file);
data = stream.readAllBytes();
} catch (Exception e) { }
try { stream.close(); } catch (Exception e) { }
return data;
}
// Read a file, first from disk, then from .jar
public static byte[] fileRead(String filename) {
InputStream stream = null;
// Open the file on disk
try { stream = new FileInputStream(filename); }
// File on disk could not be opened, so get resource from .jar
catch (Exception e) {
stream = Util.class.getResourceAsStream("/" + filename);
if (stream == null)
return null; // Resource in .jar could not be found
}
// Read the file data into memory
byte[] data = null;
try { data = stream.readAllBytes(); } catch (Exception e) { }
try { stream.close(); } catch (Exception e) { }
return data;
}
// Select a font family from a list, if avaiable
public static String fontFamily(String[] families) {
for (String family : families)
if (Arrays.binarySearch(FONTS, family,
String.CASE_INSENSITIVE_ORDER) >= 0)
return family;
return null;
}
// Read an image file as an icon
public static Icon iconRead(String filename) {
BufferedImage img = imageRead(filename);
return img == null ? null : new ImageIcon(img);
}
// Read an image file by filename
public static BufferedImage imageRead(String filename) {
try { return
ImageIO.read(new ByteArrayInputStream(fileRead(filename)));
} catch (Exception e) { return null; }
}
// Read an image file by handle
public static BufferedImage imageRead(File file) {
try { return imageRead(file.getAbsolutePath()); }
catch (Exception e) { return null; }
}
// Produce a list of files contained in a directory
// If the directory is not found on disk, looks for it in the .jar
public static String[] listFiles(String path) {
// Check for the directory on disk
var file = new File(path);
if (file.exists() && file.isDirectory())
return file.list();
// Not executing out of a .jar
if (JARFS == null)
return null;
// Check for the directory in the .jar
try {
var list = Files.list(JARFS.getPath("/" + path)).toArray();
var ret = new String[list.length];
for (int x = 0; x < list.length; x++) {
path = "/" + ((Path) list[x]).toString();
ret[x] = path.substring(path.lastIndexOf("/") + 1);
}
return ret;
} catch (Exception e) { }
// The directory was not found
return null;
}
// Produce a list of available font family names
public static String[] listFonts() {
var ret = new String[FONTS.length];
System.arraycopy(FONTS, 0, ret, 0, FONTS.length);
return ret;
}
// Produce a WindowListener with a functional interface
public static WindowListener onClose(OnClose close) {
return new WindowListener() {
public void windowActivated (WindowEvent e) { }
public void windowClosed (WindowEvent e) { }
public void windowDeactivated(WindowEvent e) { }
public void windowDeiconified(WindowEvent e) { }
public void windowIconified (WindowEvent e) { }
public void windowOpened (WindowEvent e) { }
public void windowClosing (WindowEvent e)
{ if (close != null) close.call(e); }
};
}
// Produce an InternalFrameListener with a functional interface
public static InternalFrameListener onClose2(OnClose2 close) {
return new InternalFrameListener() {
public void internalFrameActivated (InternalFrameEvent e) { }
public void internalFrameClosed (InternalFrameEvent e) { }
public void internalFrameDeactivated(InternalFrameEvent e) { }
public void internalFrameDeiconified(InternalFrameEvent e) { }
public void internalFrameIconified (InternalFrameEvent e) { }
public void internalFrameOpened (InternalFrameEvent e) { }
public void internalFrameClosing (InternalFrameEvent e)
{ if (close != null) close.call(e); }
};
}
// Produce a FocusListener with functional interfaces
public static FocusListener onFocus(OnFocus focus, OnFocus blur) {
return new FocusListener() {
public void focusGained(FocusEvent e)
{ if (focus != null) focus.call(e); }
public void focusLost (FocusEvent e)
{ if (blur != null) blur .call(e); }
};
}
// Produce a KeyListener with functional interfaces
public static KeyListener onKey(OnKey down, OnKey up) {
return new KeyListener() {
public void keyTyped (KeyEvent e) { }
public void keyPressed (KeyEvent e)
{ if (down != null) down.call(e); }
public void keyReleased(KeyEvent e)
{ if (up != null) up .call(e); }
};
}
// Produce a MouseMotionListener with a functional interface
public static MouseMotionListener onMouseMove(OnMouse move, OnMouse drag) {
return new MouseMotionListener() {
public void mouseMoved (MouseEvent e)
{ if (move != null) move.call(e); }
public void mouseDragged(MouseEvent e)
{ if (drag != null) drag.call(e); }
};
}
// Produce a MouseListener with functional interfaces
public static MouseListener onMouse(OnMouse down, OnMouse up) {
return new MouseListener() {
public void mouseClicked (MouseEvent e) { }
public void mouseEntered (MouseEvent e) { }
public void mouseExited (MouseEvent e) { }
public void mousePressed (MouseEvent e)
{ if (down != null) down.call(e); }
public void mouseReleased(MouseEvent e)
{ if (up != null) up .call(e); }
};
}
// Produce a ComponentListener with a functional interface
public static ComponentListener onResize(OnResize resize) {
return new ComponentListener() {
public void componentHidden (ComponentEvent e) { }
public void componentMoved (ComponentEvent e) { }
public void componentShown (ComponentEvent e) { }
public void componentResized(ComponentEvent e)
{ if (resize != null) resize.call(e); }
};
}
// Produce an AncestorListener using functional interfaces
public static AncestorListener onVisible(OnVisible show, OnVisible hide) {
return new AncestorListener() {
public void ancestorMoved (AncestorEvent e) { }
public void ancestorAdded (AncestorEvent e)
{ if (show != null) show.call(e); }
public void ancestorRemoved(AncestorEvent e)
{ if (hide != null) hide.call(e); }
};
}
// Configure the Swing look-and-feel with the system theme
public static boolean setSystemLAF() {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
return true;
} catch (Exception e) { return false; }
}
// Produce a JSplitPane with an un-styled divider
public static JSplitPane splitPane(int orientation) {
var split = new JSplitPane(orientation, null, null);
split.setContinuousLayout(true);
split.setDividerSize(2);
split.setUI(new BasicSplitPaneUI() {
public BasicSplitPaneDivider createDefaultDivider() {
return new BasicSplitPaneDivider(this) {
public void setBorder(Border b) { }
};
}
});
split.setBorder(null);
return split;
}
// Read a text file, accounting for BOMs
public static String textRead(String filename) {
// Read the file
byte[] data = fileRead(filename);
if (data == null)
return null;
// Configure working variables
byte[] mark = null;
Charset set = StandardCharsets.ISO_8859_1; // Default charset
// Check for a byte-order mark
outer: for (BOM bom : BOMS) {
if (data.length < bom.mark.length)
continue;
for (int x = 0; x < bom.mark.length; x++)
if (data[x] != bom.mark[x])
continue outer;
mark = bom.mark;
set = bom.set;
break;
}
// Remove the byte-order mark from the data
if (mark != null) {
byte[] temp = new byte[data.length - mark.length];
System.arraycopy(data, mark.length, temp, 0, temp.length);
data = temp;
}
// Produce a string
return new String(data, set);
}
}

137
src/desktop/util/XML.java Normal file
View File

@ -0,0 +1,137 @@
package util;
// Java imports
import java.io.*;
import java.util.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
// Utility methods for managing XML documents
public class XML {
// This class cannot be instantiated
private XML() { }
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Parsing instance
private static final DocumentBuilder XML_PARSER;
// Static initializer
static {
DocumentBuilder parser = null;
try { parser =
DocumentBuilderFactory.newInstance().newDocumentBuilder();
} catch (Exception e) { }
XML_PARSER = parser;
}
///////////////////////////////////////////////////////////////////////////
// Static Methods //
///////////////////////////////////////////////////////////////////////////
// Retrieve an attribute value from an XML element
public static String attribute(Element element, String attribute) {
return element.hasAttribute(attribute) ?
element.getAttribute(attribute) : null;
}
// Retrieve the child elements of an XML node
public static Element[] children(Node node) {
var nodes = node.getChildNodes();
int count = nodes.getLength();
var ret = new ArrayList<Element>();
for (int x = 0; x < count; x++) {
node = nodes.item(x);
if (node.getNodeType() == Node.ELEMENT_NODE)
ret.add((Element) node);
}
return ret.toArray(new Element[ret.size()]);
}
// Retrieve the first node matching the given hierarchy
public static Element element(Node node, String hierarchy) {
// Error checking
if (node == null || hierarchy == null)
return null;
// Propagate down the hierarchy
outer: for (String name : hierarchy.split("\\.")) {
var nodes = node.getChildNodes();
int count = nodes.getLength();
for (int x = 0; x < count; x++) {
node = nodes.item(x);
if (
node.getNodeType() == Node.ELEMENT_NODE &&
node.getNodeName().equals(name)
) continue outer;
}
return null;
}
return (Element) node;
}
// Retrieve all nodes matching the endpoint of a given hierarchy
public static Element[] elements(Node node, String hierarchy) {
// Find the first matching node
node = element(node, hierarchy);
if (node == null)
return new Element[0];
// Working variables
var names = hierarchy.split("\\.");
String name = names[names.length - 1];
var ret = new ArrayList<Element>();
// Include all siblings with the same tag name
ret.add((Element) node);
for (;;) {
node = node.getNextSibling();
if (node == null)
break;
if (
node.getNodeType() == Node.ELEMENT_NODE &&
node.getNodeName().equals(name)
) ret.add((Element) node);
}
return ret.toArray(new Element[ret.size()]);
}
// Read and parse an XML file
public static Node read(String filename) {
try { return XML_PARSER.parse(
new ByteArrayInputStream(Util.fileRead(filename)));
} catch (Exception e) { return null; }
}
// Retrieve the text content of an element
public static String text(Node node) {
// Error checking
if (node == null)
return null;
// Find the child node called #text
var nodes = node.getChildNodes();
int count = nodes.getLength();
for (int x = 0; x < count; x++) {
node = nodes.item(x);
if (node.getNodeType() != Node.TEXT_NODE)
continue;
String text = node.getNodeValue();
return text == null ? "" : text.trim();
}
return "";
}
}

View File

@ -0,0 +1,21 @@
package vue;
// Access state
public class Access {
// Instance fields
public int address; // CPU bus address
public int fetch; // Index of machine code unit
public int type; // Data type
public int value; // Value read/to write
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
Access() { }
}

View File

@ -0,0 +1,576 @@
// This file is included through NativeVue.c and cannot be built directly. */
#ifdef NATIVEVUE
///////////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////////
// Hook flags
#define BRK_EXCEPTION 0x00000001
#define BRK_EXECUTE 0x00000002
#define BRK_FRAME 0x00000004
#define BRK_READ 0x00000008
#define BRK_WRITE 0x00000010
// Token types
#define BRK_BOOL 0 /* Value types */
#define BRK_SIGNED 1
#define BRK_UNSIGNED 2
#define BRK_FLOAT 3
#define BRK_SYMBOL 4 /* Symbol types */
#define BRK_PROREG 5
#define BRK_SYSREG 6
#define BRK_UNARY 7 /* Operator types */
#define BRK_BINARY 8
// Functional symbol IDs
#define BRK_ADDRESS 0
#define BRK_BREAK 1
#define BRK_CODE 2
#define BRK_COND 3
#define BRK_DISP 4
#define BRK_FETCH 5
#define BRK_FORMAT 6
#define BRK_ID 7
#define BRK_IMM 8
#define BRK_OPCODE 9
#define BRK_REG1 10
#define BRK_REG2 11
#define BRK_REGID 12
#define BRK_SIZE 13
#define BRK_SUBOPCODE 14
#define BRK_TYPE 15
#define BRK_VALUE 16
#define BRK_VECTOR 17
// Evaluation operator IDs
#define BRK_XS32 1 /* xs32, xword */
#define BRK_XU32 2 /* xu32, xuword */
#define BRK_XFLOAT 3 /* xfloat */
#define BRK_READ8 4 /* [] */
#define BRK_READ16 5 /* [] */
#define BRK_READ32 6 /* [] */
#define BRK_BOOL_ - 2 /* bool */
#define BRK_S32 - 3 /* s32, word */
#define BRK_U32 - 4 /* u32, uword */
#define BRK_FLOAT_ - 5 /* float */
#define BRK_ADD - 6 /* + */
#define BRK_AND_B - 7 /* & */
#define BRK_AND_L - 8 /* && */
#define BRK_CEIL - 9 /* ceil */
#define BRK_DIVIDE -10 /* / */
#define BRK_EQUAL -11 /* == */
#define BRK_FLOOR -12 /* floor */
#define BRK_GREATER -13 /* > */
#define BRK_GREQUAL -14 /* >= */
#define BRK_LEFT_L -15 /* << */
#define BRK_LEQUAL -16 /* <= */
#define BRK_LESS -17 /* < */
#define BRK_MULTIPLY -18 /* * */
#define BRK_NEQUAL -19 /* != */
#define BRK_NOT_B -20 /* ~ */
#define BRK_NOT_L -21 /* ! */
#define BRK_NEGATE -22 /* - */
#define BRK_OR_B -23 /* | */
#define BRK_OR_L -24 /* || */
#define BRK_REMAINDER -25 /* % */
#define BRK_RIGHT_A -26 /* >> */
#define BRK_RIGHT_L -27 /* >>> */
#define BRK_ROUND -28 /* round */
#define BRK_S8 -29 /* s8, byte */
#define BRK_S16 -30 /* s16, halfword */
#define BRK_SUBTRACT -31 /* - */
#define BRK_TRUNC -32 /* trunc */
#define BRK_U8 -33 /* u8, ubyte */
#define BRK_U16 -34 /* u16, uhalfword */
#define BRK_XOR_B -36 /* ^ */
#define BRK_XOR_L -37 /* ^^ */
///////////////////////////////////////////////////////////////////////////////
// Internal Functions //
///////////////////////////////////////////////////////////////////////////////
// Adjust a float value as needed
static int brkAdjust(float value) {
int32_t bits = *(int32_t *)&value;
int exp = bits & 0x7F800000;
return
(bits & 0x7FFFFFFF) == 0 || // Zero
exp == 0x7F800000 || // Indefinite
exp == 0 // Denormal
? 0 : bits;
}
// Evaluate a binary operator
static int32_t brkEvalBinary(int32_t id, int32_t left, int32_t right) {
// Processing by ID
switch (id) {
// Add
case -BRK_ADD * 4 + BRK_BOOL:
case -BRK_ADD * 4 + BRK_SIGNED:
case -BRK_ADD * 4 + BRK_UNSIGNED:
return left + right;
case -BRK_ADD * 4 + BRK_FLOAT:
return brkAdjust(*(float *)&left + *(float *)&right);
// And Bitwise
case -BRK_AND_B * 4 + BRK_BOOL:
case -BRK_AND_B * 4 + BRK_FLOAT: // Prevented by parser
case -BRK_AND_B * 4 + BRK_SIGNED:
case -BRK_AND_B * 4 + BRK_UNSIGNED:
return left & right;
// And Logical
case -BRK_AND_L * 4 + BRK_BOOL:
case -BRK_AND_L * 4 + BRK_FLOAT:
case -BRK_AND_L * 4 + BRK_SIGNED:
case -BRK_AND_L * 4 + BRK_UNSIGNED:
return left == 0 ? left : right;
// Divide
case -BRK_DIVIDE * 4 + BRK_BOOL:
case -BRK_DIVIDE * 4 + BRK_SIGNED:
return right == 0 ? 0 : left / right;
case -BRK_DIVIDE * 4 + BRK_UNSIGNED:
return right == 0 ? 0 : (uint32_t) left / right;
case -BRK_DIVIDE * 4 + BRK_FLOAT:
return right == 0 ? 0 :
brkAdjust(*(float *)&left / *(float *)&right);
// Equal
case -BRK_EQUAL * 4 + BRK_BOOL:
case -BRK_EQUAL * 4 + BRK_FLOAT:
case -BRK_EQUAL * 4 + BRK_SIGNED:
case -BRK_EQUAL * 4 + BRK_UNSIGNED:
return left == right ? 1 : 0;
// Greater
case -BRK_GREATER * 4 + BRK_BOOL:
case -BRK_GREATER * 4 + BRK_SIGNED:
return left > right ? 1 : 0;
case -BRK_GREATER * 4 + BRK_UNSIGNED:
return (uint32_t) left > right ? 1 : 0;
case -BRK_GREATER * 4 + BRK_FLOAT:
return *(float *)&left > *(float *)&right ? 1 : 0;
// Greater or Equal
case -BRK_GREQUAL * 4 + BRK_BOOL:
case -BRK_GREQUAL * 4 + BRK_SIGNED:
return left >= right ? 1 : 0;
case -BRK_GREQUAL * 4 + BRK_UNSIGNED:
return (uint32_t) left >= right ? 1 : 0;
case -BRK_GREQUAL * 4 + BRK_FLOAT:
return *(float *)&left >= *(float *)&right ? 1 : 0;
// Shift Left
case -BRK_LEFT_L * 4 + BRK_BOOL:
case -BRK_LEFT_L * 4 + BRK_FLOAT: // Prevented by parser
case -BRK_LEFT_L * 4 + BRK_SIGNED:
case -BRK_LEFT_L * 4 + BRK_UNSIGNED:
return left << (right & 31);
// Less or Equal
case -BRK_LEQUAL * 4 + BRK_BOOL:
case -BRK_LEQUAL * 4 + BRK_SIGNED:
return left <= right ? 1 : 0;
case -BRK_LEQUAL * 4 + BRK_UNSIGNED:
return (uint32_t) left <= right ? 1 : 0;
case -BRK_LEQUAL * 4 + BRK_FLOAT:
return *(float *)&left <= *(float *)&right ? 1 : 0;
// Less
case -BRK_LESS * 4 + BRK_BOOL:
case -BRK_LESS * 4 + BRK_SIGNED:
return left < right ? 1 : 0;
case -BRK_LESS * 4 + BRK_UNSIGNED:
return (uint32_t) left < right ? 1 : 0;
case -BRK_LESS * 4 + BRK_FLOAT:
return *(float *)&left < *(float *)&right ? 1 : 0;
// Multiply
case -BRK_MULTIPLY * 4 + BRK_BOOL:
case -BRK_MULTIPLY * 4 + BRK_SIGNED:
return left * right;
case -BRK_MULTIPLY * 4 + BRK_UNSIGNED:
return (uint32_t) left * right;
case -BRK_MULTIPLY * 4 + BRK_FLOAT:
return brkAdjust(*(float *)&left * *(float *)&right);
// Not Equal
case -BRK_NEQUAL * 4 + BRK_BOOL:
case -BRK_NEQUAL * 4 + BRK_FLOAT:
case -BRK_NEQUAL * 4 + BRK_SIGNED:
case -BRK_NEQUAL * 4 + BRK_UNSIGNED:
return left != right ? 1 : 0;
// Or Bitwise
case -BRK_OR_B * 4 + BRK_BOOL:
case -BRK_OR_B * 4 + BRK_FLOAT: // Prevented by parser
case -BRK_OR_B * 4 + BRK_SIGNED:
case -BRK_OR_B * 4 + BRK_UNSIGNED:
return left | right;
// Or Logical
case -BRK_OR_L * 4 + BRK_BOOL:
case -BRK_OR_L * 4 + BRK_FLOAT:
case -BRK_OR_L * 4 + BRK_SIGNED:
case -BRK_OR_L * 4 + BRK_UNSIGNED:
return left != 0 ? left : right;
// Remainder
case -BRK_REMAINDER * 4 + BRK_BOOL:
case -BRK_REMAINDER * 4 + BRK_SIGNED:
return right == 0 ? 0 : left % right;
case -BRK_REMAINDER * 4 + BRK_UNSIGNED:
return right == 0 ? 0 : (uint32_t) left % right;
case -BRK_REMAINDER * 4 + BRK_FLOAT:
return right == 0 ? 0 :
brkAdjust(fmodf(*(float *)&left, *(float *)&right));
// Shift Right Arithmetic
case -BRK_RIGHT_A * 4 + BRK_BOOL:
case -BRK_RIGHT_A * 4 + BRK_FLOAT: // Prevented by parser
case -BRK_RIGHT_A * 4 + BRK_SIGNED:
case -BRK_RIGHT_A * 4 + BRK_UNSIGNED:
return SIGN_EXTEND(left >> (right & 31), 32 - (right & 31));
// Shift Right Logical
case -BRK_RIGHT_L * 4 + BRK_BOOL:
case -BRK_RIGHT_L * 4 + BRK_FLOAT: // Prevented by parser
case -BRK_RIGHT_L * 4 + BRK_SIGNED:
case -BRK_RIGHT_L * 4 + BRK_UNSIGNED:
return left >> (right & 31) & ((int32_t) -1 << (right & 31));
// Subtract
case -BRK_SUBTRACT * 4 + BRK_BOOL:
case -BRK_SUBTRACT * 4 + BRK_SIGNED:
case -BRK_SUBTRACT * 4 + BRK_UNSIGNED:
return left - right;
case -BRK_SUBTRACT * 4 + BRK_FLOAT:
return brkAdjust(*(float *)&left - *(float *)&right);
// Exclusive Or Bitwise
case -BRK_XOR_B * 4 + BRK_BOOL:
case -BRK_XOR_B * 4 + BRK_FLOAT: // Prevented by parser
case -BRK_XOR_B * 4 + BRK_SIGNED:
case -BRK_XOR_B * 4 + BRK_UNSIGNED:
return left ^ right;
// Exclusive Or Logical
case -BRK_XOR_L * 4 + BRK_BOOL:
case -BRK_XOR_L * 4 + BRK_FLOAT:
case -BRK_XOR_L * 4 + BRK_SIGNED:
case -BRK_XOR_L * 4 + BRK_UNSIGNED:
return left != 0 && right != 0 ? 0 : left != 0 ? left : right;
}
// Unreachable
return 0;
}
// Evaluate a unary operator
static int32_t brkEvalUnary(Vue *vue, int32_t id, int32_t operand) {
float val; // Working variable
// Processing by ID
switch (id) {
// Cast to Boolean
case -BRK_BOOL_ * 4 + BRK_BOOL: // Removed by parser
case -BRK_BOOL_ * 4 + BRK_FLOAT:
case -BRK_BOOL_ * 4 + BRK_SIGNED:
case -BRK_BOOL_ * 4 + BRK_UNSIGNED:
return operand == 0 ? 0 : 1;
// Round up
case -BRK_CEIL * 4 + BRK_BOOL: // Removed by parser
case -BRK_CEIL * 4 + BRK_FLOAT:
case -BRK_CEIL * 4 + BRK_SIGNED: // Removed by parser
case -BRK_CEIL * 4 + BRK_UNSIGNED: // Removed by parser
return brkAdjust(ceilf(*(float *)&operand));
// Cast to Float
case -BRK_FLOAT_ * 4 + BRK_BOOL:
case -BRK_FLOAT_ * 4 + BRK_FLOAT: // Removed by parser
case -BRK_FLOAT_ * 4 + BRK_SIGNED:
return brkAdjust(operand);
case -BRK_FLOAT_ * 4 + BRK_UNSIGNED:
return brkAdjust((uint32_t) operand);
// Round down
case -BRK_FLOOR * 4 + BRK_BOOL: // Removed by parser
case -BRK_FLOOR * 4 + BRK_FLOAT:
case -BRK_FLOOR * 4 + BRK_SIGNED: // Removed by parser
case -BRK_FLOOR * 4 + BRK_UNSIGNED: // Removed by parser
return brkAdjust(floorf(*(float *)&operand));
// Bitwise Not
case -BRK_NOT_B * 4 + BRK_BOOL:
case -BRK_NOT_B * 4 + BRK_FLOAT: // Prevented by parser
case -BRK_NOT_B * 4 + BRK_SIGNED:
case -BRK_NOT_B * 4 + BRK_UNSIGNED:
return ~operand;
// Logical Not
case -BRK_NOT_L * 4 + BRK_BOOL:
case -BRK_NOT_L * 4 + BRK_FLOAT:
case -BRK_NOT_L * 4 + BRK_SIGNED:
case -BRK_NOT_L * 4 + BRK_UNSIGNED:
return operand == 0 ? 1 : 0;
// Negate
case -BRK_NEGATE * 4 + BRK_BOOL:
case -BRK_NEGATE * 4 + BRK_SIGNED:
case -BRK_NEGATE * 4 + BRK_UNSIGNED: // Removed by parser
return -operand;
case -BRK_NEGATE * 4 + BRK_FLOAT:
return brkAdjust(-*(float *)&operand);
// Read Byte
case BRK_READ8:
return vueRead(vue, operand, VUE_S8);
// Read Halfword
case BRK_READ16:
return vueRead(vue, operand, VUE_S16);
// Read Word
case BRK_READ32:
return vueRead(vue, operand, VUE_S32);
// Round to Nearest
case -BRK_ROUND * 4 + BRK_BOOL: // Removed by parser
case -BRK_ROUND * 4 + BRK_FLOAT:
case -BRK_ROUND * 4 + BRK_SIGNED: // Removed by parser
case -BRK_ROUND * 4 + BRK_UNSIGNED: // Removed by parser
return brkAdjust(roundf(*(float *)&operand));
// Cast to Signed Byte
case -BRK_S8 * 4 + BRK_FLOAT:
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
// Fallthrough
case -BRK_S8 * 4 + BRK_BOOL:
case -BRK_S8 * 4 + BRK_SIGNED:
case -BRK_S8 * 4 + BRK_UNSIGNED:
return SIGN_EXTEND(operand, 8);
// Cast to Signed Halfword
case -BRK_S16 * 4 + BRK_FLOAT:
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
// Fallthrough
case -BRK_S16 * 4 + BRK_BOOL:
case -BRK_S16 * 4 + BRK_SIGNED:
case -BRK_S16 * 4 + BRK_UNSIGNED:
return SIGN_EXTEND(operand, 16);
// Cast to Signed Word
case -BRK_S32 * 4 + BRK_BOOL:
case -BRK_S32 * 4 + BRK_SIGNED: // Removed by parser
case -BRK_S32 * 4 + BRK_UNSIGNED:
return operand;
case -BRK_S32 * 4 + BRK_FLOAT:
val = *(float *)&operand;
return val >= -MAX_WORD && val < MAX_WORD ? (int32_t) val : 0;
// Truncate
case -BRK_TRUNC * 4 + BRK_BOOL: // Removed by parser
case -BRK_TRUNC * 4 + BRK_FLOAT:
case -BRK_TRUNC * 4 + BRK_SIGNED: // Removed by parser
case -BRK_TRUNC * 4 + BRK_UNSIGNED: // Removed by parser
return brkAdjust(truncf(*(float *)&operand));
// Cast to Unsigned Byte
case -BRK_U8 * 4 + BRK_FLOAT:
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
case -BRK_U8 * 4 + BRK_BOOL:
case -BRK_U8 * 4 + BRK_SIGNED:
case -BRK_U8 * 4 + BRK_UNSIGNED:
return operand & 0xFF;
// Cast to Unsigned Halfword
case -BRK_U16 * 4 + BRK_FLOAT:
operand = brkEvalUnary(vue, -BRK_S32 * 4 + BRK_FLOAT, operand);
case -BRK_U16 * 4 + BRK_BOOL:
case -BRK_U16 * 4 + BRK_SIGNED:
case -BRK_U16 * 4 + BRK_UNSIGNED:
return operand & 0xFFFF;
// Cast to Unsigned Word
case -BRK_U32 * 4 + BRK_BOOL:
case -BRK_U32 * 4 + BRK_SIGNED:
case -BRK_U32 * 4 + BRK_UNSIGNED: // Removed by parser
return operand;
case -BRK_U32 * 4 + BRK_FLOAT:
val = *(float *)&operand;
return val >= 0 && val < MAX_UWORD ? (uint32_t) val : 0;
// Reinterpret as Float
case BRK_XFLOAT:
return brkAdjust(*(float *)&operand);
// Reinterpret as Signed Word, Reinterpret as Unsigned Word
//case BRK_XS32: // Removed by parser
//case BRK_XU32: // Removed by parser
}
// Unreachable
return 0;
}
// Evaluate the condition, returning only the computed value
static int32_t brkEvaluate(Core *core, Breakpoint *brk) {
int32_t *stack = core->stack;
Vue *vue = &core->vue;
// Process all tokens
int size = 0;
Token *tok;
int32_t x, sym; // Working variables
for (tok = &brk->tokens[x=0]; x < brk->numTokens; tok = &brk->tokens[++x])
switch (tok->type) {
// Binary operator
case BRK_BINARY:
stack[size - 2] =
brkEvalBinary(tok->id, stack[size - 2], stack[size - 1]);
size--;
break;
// Literal
case BRK_BOOL:
case BRK_FLOAT:
case BRK_SIGNED:
case BRK_UNSIGNED:
stack[size++] = tok->id;
break;
// CPU register
case BRK_PROREG:
case BRK_SYSREG:
stack[size++] = vueGetRegister(vue,tok->id,tok->type==BRK_SYSREG);
break;
// Unary operator
case BRK_UNARY:
stack[size - 1] = brkEvalUnary(vue, tok->id, stack[size - 1]);
break;
// Symbol
case BRK_SYMBOL:
sym = vue->cpu.inst.imm; // IMM, REGID, VECTOR
switch (tok->id) {
case BRK_ADDRESS : sym = vue->cpu.access.address; break;
case BRK_BREAK : sym = core->breakType ; break;
case BRK_CODE : sym = vue->cpu.exception ; break;
case BRK_COND : sym = vue->cpu.inst.cond ; break;
case BRK_DISP : sym = vue->cpu.inst.disp ; break;
case BRK_FETCH : sym = vue->cpu.fetch ; break;
case BRK_FORMAT : sym = vue->cpu.inst.format ; break;
case BRK_ID : sym = vue->cpu.inst.id ; break;
case BRK_OPCODE : sym = vue->cpu.inst.opcode ; break;
case BRK_REG1 : sym = vue->cpu.inst.reg1 ; break;
case BRK_REG2 : sym = vue->cpu.inst.reg2 ; break;
case BRK_SIZE : sym = vue->cpu.inst.size ; break;
case BRK_SUBOPCODE: sym = vue->cpu.inst.subopcode; break;
case BRK_TYPE : sym = vue->cpu.access.type ; break;
case BRK_VALUE : sym = vue->cpu.access.value ; break;
}
stack[size++] = sym;
break;
}
// Return the remaining stack value
return stack[0];
}
// Determine whether the breakpoint applies to a given address range
static vbool brkInRange(Breakpoint *brk, uint32_t start, uint32_t end) {
// Implicit inclusion
if (brk->numRanges == 0)
return VUE_TRUE;
// Check all ranges
for (int x = 0; x < brk->numRanges; x++) {
uint32_t *range = &brk->ranges[x * 2];
if (
start - range[0] <= range[1] - range[0] ||
range[0] - start <= end - start
) return VUE_TRUE;
}
return VUE_FALSE;
}
// Check for breakpoints
static int32_t brkOnBreakpoint(Vue *vue, int32_t breakType) {
Core *core = (Core *) vue->tag;
uint32_t end = 0;
vbool ranged = VUE_FALSE;
uint32_t start = 0;
core->breakType = breakType;
// Processing for Execute
if (breakType == BRK_EXECUTE) {
ranged = VUE_TRUE;
start = vue->cpu.pc;
end = start + vue->cpu.inst.size - 1;
}
// Processing for Read and Write
else if (breakType == BRK_READ || breakType == BRK_WRITE) {
ranged = VUE_TRUE;
start = vue->cpu.access.address;
end = start + TYPE_SIZES[vue->cpu.access.type] - 1;
}
// Check all breakpoints
int32_t fetch = breakType == BRK_READ && vue->cpu.fetch != -1 ? 1 : 0;
for (int x = 0; x < core->numBreakpoints; x++) {
Breakpoint *brk = &core->breakpoints[x];
if (
brk->enabled && (brk->fetch || !fetch) &&
(breakType == 0 || (breakType & brk->hooks) != 0) &&
(!ranged || brkInRange(brk, start, end)) &&
(brk->numTokens == 0 || brkEvaluate(core, brk) != 0)
) return x + 1;
}
return 0;
}
// Check for exception breakpoints
static int32_t brkOnException(Vue *vue, uint16_t code) {
return brkOnBreakpoint(vue, BRK_EXCEPTION);
}
// Check for execute breakpoints
static int32_t brkOnExecute(Vue *vue, VueInstruction *inst) {
return brkOnBreakpoint(vue, BRK_EXECUTE);
}
// Check for frame breakpoints
static int32_t brkOnFrame(Vue *vue) {
return brkOnBreakpoint(vue, BRK_FRAME);
}
// Check for read breakpoints
static int32_t brkOnRead(Vue *vue, VueAccess *acc) {
return brkOnBreakpoint(vue, BRK_READ);
}
// Check for write breakpoints
static int32_t brkOnWrite(Vue *vue, VueAccess *acc) {
return brkOnBreakpoint(vue, BRK_WRITE);
}
#endif // NATIVEVUE

File diff suppressed because it is too large Load Diff

1139
src/desktop/vue/CPU.java Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,228 @@
package vue;
// Instruction state and decoder
public class Instruction {
// Instruction fields
public int bits; // Binary encoding
public int cond; // Condition for Bcond
public int disp; // Displacement for jumps and branches
public int format; // Binary format
public int id; // Library-specific identifier
public int imm; // Immediate operand
public int opcode; // Instruction opcode
public int reg1; // Source/right register
public int reg2; // Destination/left register
public int size; // Number of bytes in encoding
public int subopcode; // Instruction subopcode
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Intermediate instruction IDs
private static final int BITSTRING = -2;
private static final int FLOATENDO = -3;
// Opcode lookup table
private static final byte[] LOOKUP_OPCODE = {
Vue.MOV_REG, 1, Vue.ADD_REG, 1, Vue.SUB , 1, Vue.CMP_REG, 1,
Vue.SHL_REG, 1, Vue.SHR_REG, 1, Vue.JMP , 1, Vue.SAR_REG, 1,
Vue.MUL , 1, Vue.DIV , 1, Vue.MULU , 1, Vue.DIVU , 1,
Vue.OR , 1, Vue.AND , 1, Vue.XOR , 1, Vue.NOT , 1,
Vue.MOV_IMM,-2, Vue.ADD_IMM,-2, Vue.SETF , 2, Vue.CMP_IMM,-2,
Vue.SHL_IMM, 2, Vue.SHR_IMM, 2, Vue.CLI , 2, Vue.SAR_IMM, 2,
Vue.TRAP , 2, Vue.RETI , 2, Vue.HALT , 2, Vue.ILLEGAL, 0,
Vue.LDSR , 2, Vue.STSR , 2, Vue.SEI , 2, BITSTRING , 2,
Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3,
Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3, Vue.BCOND , 3,
Vue.MOVEA ,-5, Vue.ADDI ,-5, Vue.JR , 4, Vue.JAL , 4,
Vue.ORI , 5, Vue.ANDI , 5, Vue.XORI , 5, Vue.MOVHI , 5,
Vue.LD_B , 6, Vue.LD_H , 6, Vue.ILLEGAL, 0, Vue.LD_W , 6,
Vue.ST_B , 6, Vue.ST_H , 6, Vue.ILLEGAL, 0, Vue.ST_W , 6,
Vue.IN_B , 6, Vue.IN_H , 6, Vue.CAXI , 6, Vue.IN_W , 6,
Vue.OUT_B , 6, Vue.OUT_H , 6, FLOATENDO , 7, Vue.OUT_W , 6
};
// Bit string lookup table
private static final byte[] LOOKUP_BITSTRING = {
Vue.SCH0BSU, Vue.SCH0BSD, Vue.SCH1BSU, Vue.SCH1BSD,
Vue.ILLEGAL, Vue.ILLEGAL, Vue.ILLEGAL, Vue.ILLEGAL,
Vue.ORBSU , Vue.ANDBSU , Vue.XORBSU , Vue.MOVBSU ,
Vue.ORNBSU , Vue.ANDNBSU, Vue.XORNBSU, Vue.XORNBSU
};
// Floating-point and Nintendo lookup table
private static final byte[] LOOKUP_FLOATENDO = {
Vue.CMPF_S , Vue.ILLEGAL, Vue.CVT_WS , Vue.CVT_SW ,
Vue.ADDF_S , Vue.SUBF_S , Vue.MULF_S , Vue.DIVF_S ,
Vue.XB , Vue.XH , Vue.REV , Vue.TRNC_SW,
Vue.MPYHW , Vue.ILLEGAL, Vue.ILLEGAL, Vue.ILLEGAL
};
///////////////////////////////////////////////////////////////////////////
// Static Methods //
///////////////////////////////////////////////////////////////////////////
// Determine the size of an instruction given its opcode
public static int size(int opcode) {
return Math.abs(LOOKUP_OPCODE[opcode << 1 | 1]) < 4 ? 2 : 4;
}
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
public Instruction() { }
// Decoding constructor
public Instruction(int bits) {
decode(bits);
}
// Cloning constructor
Instruction(Instruction o) {
this();
bits = o.bits;
cond = o.cond;
disp = o.disp;
format = o.format;
id = o.id;
imm = o.imm;
opcode = o.opcode;
reg1 = o.reg1;
reg2 = o.reg2;
size = o.size;
subopcode = o.subopcode;
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Produce an access descriptor from the current instruction state
public Access access(Vue vue) {
boolean read = false;
var ret = new Access();
ret.address = vue.getRegister(reg1, false) + disp;
ret.fetch = vue.getFetch();
ret.type = Vue.S32;
// Configure descriptor by ID
switch (id) {
case Vue.CAXI : read = true; break;
case Vue.IN_B : ret.type = Vue.U8 ; read = true; break;
case Vue.IN_H : ret.type = Vue.U16; read = true; break;
case Vue.IN_W : read = true; break;
case Vue.LD_B : ret.type = Vue.S8 ; read = true; break;
case Vue.LD_H : ret.type = Vue.S16; read = true; break;
case Vue.LD_W : read = true; break;
case Vue.OUT_B: ret.type = Vue.U8 ; break;
case Vue.OUT_H: ret.type = Vue.U16; break;
case Vue.OUT_W: break;
case Vue.ST_B : ret.type = Vue.S8 ; break;
case Vue.ST_H : ret.type = Vue.S16; break;
case Vue.ST_W : break;
// TODO: Bit strings
default: return null;
}
// Read the value currently on the bus
if (read)
ret.value = vue.read(ret.address, ret.type);
// Write a register to the bus
else if (id != Vue.CAXI)
vue.getRegister(reg2, false);
// CAXI processing
else {
int value = vue.read(ret.address, Vue.S32);
int compare = vue.getRegister(reg2, false);
int exchange = vue.getRegister( 30, false);
ret.value = value == compare ? value : exchange;
}
return ret;
}
// Decode an instruction from a byte array
public Instruction decode(byte[] data, int offset) {
return decode(
(data[offset + 0] & 0xFF) |
(data[offset + 1] & 0xFF) << 8 |
(data[offset + 2] & 0xFF) << 16 |
(data[offset + 3] & 0xFF) << 24
);
}
// Decode an instruction from its binary encoding (swapped halfwords)
public Instruction decode(int bits) {
byte extend; // Sign-extend the immediate operand
int x; // Working variable
// Configure instance fields
this.bits = bits = bits << 16 | bits >>> 16;
opcode = bits >> 26 & 63;
x = opcode << 1;
id = LOOKUP_OPCODE[x ];
extend = LOOKUP_OPCODE[x + 1];
format = extend < 0 ? -extend : extend;
size = format < 4 ? 2 : 4;
// Decode by format
switch (format) {
case 1:
reg2 = bits >> 21 & 31;
reg1 = bits >> 16 & 31;
break;
case 2:
reg2 = bits >> 21 & 31;
imm = extend < 0 ? bits << 11 >> 27 : bits >> 16 & 31;
if (id == BITSTRING) {
id = imm >= 16 ? Vue.ILLEGAL : LOOKUP_BITSTRING[imm];
subopcode = imm;
}
if (id == Vue.SETF)
cond = imm & 15;
break;
case 3:
opcode = 0x20;
cond = bits >> 25 & 15;
disp = bits << 7 >> 23;
break;
case 4:
disp = bits << 6 >> 6;
break;
case 5:
reg2 = bits >> 21 & 31;
reg1 = bits >> 16 & 31;
imm = extend < 0 ? bits << 16 >> 16 : bits & 0xFFFF;
break;
case 6:
reg2 = bits >> 21 & 31;
reg1 = bits >> 16 & 31;
disp = bits << 16 >> 16;
break;
case 7:
reg2 = bits >> 21 & 31;
reg1 = bits >> 16 & 31;
subopcode = bits >> 10 & 63;
id = subopcode >= 16 ? Vue.ILLEGAL :
LOOKUP_FLOATENDO[subopcode];
break;
}
return this;
}
}

View File

@ -0,0 +1,427 @@
package vue;
// Java imports
import java.util.*;
// Java emulation core implementation
class JavaVue extends Vue {
// Package fields
int breakCode; // Application breakpoint code
int breakType; // Most recent breakpoint scenario
int[] stack; // Breakpoint condition evaluation stack
byte[] wram; // System WRAM
// System components
CPU cpu; // Processor
GamePak pak; // Game pak
VIP vip; // Video unit
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Memory access type sizes
static final int[] TYPE_SIZES = { 1, 1, 2, 2, 4 };
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
JavaVue() {
// Configure instance fields
stack = new int[0];
wram = new byte[0x10000];
// Configure system components
cpu = new CPU (this);
pak = new GamePak(this);
vip = new VIP (this);
// Initialize state
reset();
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Release any used resources
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 where no breakpoint will occur
int cycles = maxCycles; // min(maxCycles, nextFrameCycles)
cycles = cpu .until(cycles);
//cycles = pad .until(cycles);
//cycles = link .until(cycles);
//cycles = timer.until(cycles);
cycles = vip .until(cycles);
// Process all system components
breakCode = 0;
cpu .emulate(cycles);
//pad .emulate(cycles);
//link .emulate(cycles);
//timer.emulate(cycles);
vip .emulate(cycles);
//vsu .emulate(cycles);
maxCycles -= cycles;
} while (breakCode == 0 && maxCycles != 0);
// A break condition has occurred
return maxCycles;
}
// Retrieve the most recent applicaiton break code
public int getBreakCode() {
return breakCode;
}
// Retrieve the most recent exception code
public int getExceptionCode() {
return cpu.exception;
}
// Retrieve a register value
public int getRegister(int index, boolean system) {
// Non-indexed registers
if (system) switch (index) {
case Vue.JUMP_FROM: return
cpu.jumpFrom[cpu.psw_np != 0 ? 2 : cpu.psw_ep];
case Vue.JUMP_TO : return
cpu.jumpTo [cpu.psw_np != 0 ? 2 : cpu.psw_ep];
case Vue.PC : return cpu.pc;
}
// Indexed registers
return
index < 0 || index > 31 ? 0 :
system ? cpu.getSystemRegister(index) :
cpu.program[index]
;
}
// Retrieve a copy of the ROM data
public byte[] getROM() {
// No ROM data
if (pak.rom == null)
return null;
// Copy the ROM data
var ret = new byte[pak.rom.length];
System.arraycopy(pak.rom, 0, ret, 0, ret.length);
return ret;
}
// Determine whether the context is native-backed
public boolean isNative() {
return false;
}
// Read a value from the CPU bus
public int read(int address, int type) {
// Error checking
if (type < 0 || type > 4)
return 0;
// Perform the operation
switch (address >> 24 & 7) {
case 0: return vip.read ( address, type);
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
public boolean readBytes(int address, byte[] dest, int offset, int length){
// Error checking
if (
dest == null ||
offset < 0 ||
length < 0 ||
offset + length > dest.length
) return false;
// Perform the operation
while (length > 0) {
int count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF));
switch (address >> 24 & 7) {
case 0: vip.readBytes( address,dest,offset,count); break;
case 5: readBytes(wram ,address,dest,offset,count); break;
case 6: readBytes(pak.ram,address,dest,offset,count); break;
case 7: readBytes(pak.rom,address,dest,offset,count); break;
default: Arrays.fill(dest, offset, offset + count, (byte) 0);
}
address += count;
length -= count;
offset += count;
}
return true;
}
// Initialize all system components
public void reset() {
cpu.reset();
pak.reset();
Arrays.fill(wram, 0, 0x10000, (byte) 0);
}
// Specify a register value
public int setRegister(int index, boolean system, int value) {
// PC
if (index == Vue.PC && system) {
if (cpu.stage == CPU.EXECUTE || cpu.stage == CPU.FETCH) {
cpu.fetch = -1;
cpu.stage = CPU.FETCH;
}
return cpu.pc = value & 0xFFFFFFFE;
}
// Other
return
index < 0 || index > 31 ? 0 :
system ? cpu.setSystemRegister(index, value, true) :
index == 0 ? 0 : (cpu.program[index] = value)
;
}
// Provide new ROM data
public boolean setROM(byte[] data, int offset, int length) {
// Error checking
if (
data == null ||
offset < 0 ||
length < 1024 || length > 0x01000000 ||
(length & length - 1) != 0 ||
offset + length > data.length
) return false;
// Accept the new ROM data
pak.rom = new byte[length];
System.arraycopy(data, offset, pak.rom, 0, length);
return true;
}
// Write a value to the CPU bus
public void write(int address, int type, int value) {
switch (address >> 24 & 7) {
case 0: vip.write ( address, type, value); break;
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
public boolean writeBytes(int address, byte[] src, int offset, int length){
// Error checking
if (
src == null ||
offset < 0 ||
length < 0 ||
offset + length > src.length
) return false;
// Perform the operation
while (length > 0) {
int count = Math.min(length, 0x01000000 - (address & 0x00FFFFFF));
switch (address >> 24 & 7) {
case 0: vip.writeBytes( address,src,offset,count); break;
case 5: writeBytes(wram ,address,src,offset,count); break;
case 6: writeBytes(pak.ram,address,src,offset,count); break;
case 7: writeBytes(pak.rom,address,src,offset,count); break;
}
address += count;
length -= count;
offset += count;
}
return true;
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Retrieve the current state's breakpoint scenario
int getBreakType() {
return breakType;
}
// Retrieve the current instruction fetch index
int getFetch() {
return cpu.fetch;
}
// Check for breakpoints
boolean onBreakpoint(int breakType) {
int end = 0;
boolean ranged = false;
int start = 0;
this.breakType = breakType;
// Processing for Execute
if (breakType == Breakpoint.EXECUTE) {
ranged = true;
start = cpu.pc;
end = start + cpu.inst.size - 1;
}
// Processing for Read and Write
else if (breakType==Breakpoint.READ || breakType==Breakpoint.WRITE) {
ranged = true;
start = cpu.access.address;
end = start + TYPE_SIZES[cpu.access.type] - 1;
}
// Check all breakpoints
int count = breakpoints.size();
boolean fetch = breakType == Breakpoint.READ && cpu.fetch != -1;
for (int x = 0; breakCode == 0 && x < count; x++) {
var brk = breakpoints.get(x);
if (
brk.appliesTo(breakType, fetch) &&
(!ranged || brk.inRange(start, end)) &&
brk.isTrue(stack, breakType, cpu.inst, cpu.access)
) breakCode = x + 1;
}
return breakCode != 0;
}
// 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) == 0) {
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;
}
}
// A breakpoint's condition tokens have changed
void updateTokens(Breakpoint brk) {
super.updateTokens(brk);
int depth = 0;
for (var bre : breakpoints)
depth = Math.max(depth, bre.getDepth());
if (depth != stack.length)
stack = new int[depth];
}
// Write a value to a byte buffer
static void writeBuffer(byte[] data, int address, int type, int value) {
// The destination does not exist
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;
}
}
}

348
src/desktop/vue/NativeVue.c Normal file
View File

@ -0,0 +1,348 @@
// Native-backed emulation core implementation
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <jni.h>
#include <vue.h>
#include "vue_NativeVue.h"
///////////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////////
// Memory access type sizes
static const int TYPE_SIZES[] = { 1, 1, 2, 2, 4 };
// Word data type limits
#define MAX_WORD 2147483648.0f
#define MAX_UWORD 4294967296.0f
///////////////////////////////////////////////////////////////////////////////
// Types //
///////////////////////////////////////////////////////////////////////////////
// Breakpoint condition token
typedef struct {
int32_t type; // Token category
int32_t id; // Operator or symbol ID, or literal value
} Token;
// Precompiled breakpoint
typedef struct {
int32_t enabled; // The breakpoint is enabled
int32_t fetch; // Breakpoint traps fetch reads
int32_t hooks; // Events hooked by the breakpoint
int32_t numRanges; // Number of address ranges
int32_t numTokens; // Number of condition tokens
uint32_t *ranges; // Address ranges
Token *tokens; // Condition tokens in RPN order
} Breakpoint;
// Core context state type
typedef struct {
Vue vue; // Context into the generic C library
int32_t breakType; // Most recent breakpoint scenario
int32_t numBreakpoints; // Number of breakpoints
Breakpoint *breakpoints; // Application breakpoints
int32_t *stack; // Expression stack for breakpoints
} Core;
///////////////////////////////////////////////////////////////////////////////
// Macros //
///////////////////////////////////////////////////////////////////////////////
/* Sign-extend a value */
#define SIGN_EXTEND(bits, value) \
((value) & 1 << (bits - 1) ? (value) | (~(1 << bits) + 1) : (value))
///////////////////////////////////////////////////////////////////////////////
// Component Includes //
///////////////////////////////////////////////////////////////////////////////
#define NATIVEVUE
#include "Breakpoint.c"
///////////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////////
// Native constructor
JNIEXPORT jlong JNICALL Java_vue_NativeVue_construct
(JNIEnv *env, jobject uve) {
Core *mem[] = { NULL, NULL };
Core *core = mem[0] = calloc(sizeof (Core), 1);
Vue *vue = &core->vue;
vueInitialize (vue);
vueOnException(vue, &brkOnException);
vueOnExecute (vue, &brkOnExecute );
vueOnFrame (vue, &brkOnFrame );
vueOnRead (vue, &brkOnRead );
vueOnWrite (vue, &brkOnWrite );
vueReset (vue);
vueSetTag (vue, core);
return *(jlong *)&core;
}
///////////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////////
// Produce a new breakpoint and add it to the collection
JNIEXPORT void JNICALL Java_vue_NativeVue_breakpoint
(JNIEnv *env, jobject vue, jlong handle) {
Core *core = *(Core **)&handle;
core->numBreakpoints++;
core->breakpoints =
realloc(core->breakpoints,core->numBreakpoints * sizeof (Breakpoint));
Breakpoint *brk = &core->breakpoints[core->numBreakpoints - 1];
memset(brk, 0 ,sizeof (Breakpoint));
brk->fetch = VUE_TRUE;
}
// Release any used resources
JNIEXPORT void JNICALL Java_vue_NativeVue_dispose
(JNIEnv *env, jobject vue, jlong handle) {
Core *core = *(Core **)&handle;
Breakpoint *brk; // Handle to breakpoint
int x; // Iterator
// Delete breakpoints
for (x = 0; x < core->numBreakpoints; x++) {
brk = &core->breakpoints[x];
free(brk->ranges);
free(brk->tokens);
}
// Delete core
free(core->breakpoints);
free(core->stack );
free(core->vue.pak.rom);
free(core->vue.pak.ram);
free(core);
}
// Process the simulation
JNIEXPORT jint JNICALL Java_vue_NativeVue_emulate
(JNIEnv *env, jobject vue, jlong handle, jint maxCycles) {
Core *core = *(Core **)&handle;
return vueEmulate(&core->vue, maxCycles);
}
// Retrieve the most recent exception code
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode
(JNIEnv *env, jobject vue, jlong handle) {
Core *core = *(Core **)&handle;
return vueGetBreakCode(&core->vue);
}
// Retrieve the most recent exception code
JNIEXPORT jint JNICALL Java_vue_NativeVue_getExceptionCode
(JNIEnv *env, jobject vue, jlong handle) {
Core *core = *(Core **)&handle;
return vueGetExceptionCode(&core->vue);
}
// Retrieve a register value
JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) {
Core *core = *(Core **)&handle;
return vueGetRegister(&core->vue, index, system);
}
// Retrieve a copy of the ROM data
JNIEXPORT jbyteArray JNICALL Java_vue_NativeVue_getROM
(JNIEnv *env, jobject vue, jlong handle) {
Core *core = *(Core **)&handle;
// No ROM data
if (core->vue.pak.rom == NULL)
return NULL;
// Copy the ROM data
jbyteArray ret = (*env)->NewByteArray(env, (jint)core->vue.pak.romSize);
jbyte *elems = (*env)->GetByteArrayElements(env, ret, NULL);
memcpy(elems, core->vue.pak.rom, core->vue.pak.romSize);
(*env)->ReleaseByteArrayElements(env, ret, elems, 0);
return ret;
}
// Read a value from the CPU bus
JNIEXPORT jint JNICALL Java_vue_NativeVue_read
(JNIEnv *env, jobject vue, jlong handle, jint address, jint type) {
Core *core = *(Core **)&handle;
return vueRead(&core->vue, address, type);
}
// Read bytes from the CPU bus
JNIEXPORT jboolean JNICALL Java_vue_NativeVue_readBytes
(JNIEnv *env, jobject vue, jlong handle, jint address, jbyteArray data,
jint offset, jint length) {
// Error checking
if (
data == NULL ||
offset < 0 ||
length < 0 ||
offset + length > (*env)->GetArrayLength(env, data)
) return JNI_FALSE;
// Perform the operation
Core *core = *(Core **)&handle;
jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL);
vueReadBytes(&core->vue, address, &elems[offset], length);
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
return JNI_TRUE;
}
// Remove a breakpoint from the collection
JNIEXPORT void JNICALL Java_vue_NativeVue_remove
(JNIEnv *env, jobject vue, jlong handle, jint index) {
Core *core = *(Core **)&handle;
Breakpoint *brk = &core->breakpoints[index];
free(brk->ranges);
free(brk->tokens);
memmove(brk, &core->breakpoints[index + 1],
(core->numBreakpoints - index - 1) * sizeof (Breakpoint));
core->numBreakpoints--;
}
// Initialize all system components
JNIEXPORT void JNICALL Java_vue_NativeVue_reset
(JNIEnv *env, jobject vue, jlong handle) {
Core *core = *(Core **)&handle;
vueReset(&core->vue);
}
// Specify a register value
JNIEXPORT jint JNICALL Java_vue_NativeVue_setRegister
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system,
jint value) {
Core *core = *(Core **)&handle;
return vueSetRegister(&core->vue, index, system, value);
}
// Provide new ROM data
JNIEXPORT jboolean JNICALL Java_vue_NativeVue_setROM
(JNIEnv *env, jobject vue, jlong handle, jbyteArray data, jint offset,
jint length) {
// Error checking
if (
data == NULL ||
offset < 0 ||
length < 1024 || length > 0x01000000 ||
(length & length - 1) != 0 ||
offset + length > (*env)->GetArrayLength(env, data)
) return JNI_FALSE;
// Accept the new ROM data
uint8_t *rom = malloc(length);
jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL);
memcpy(rom, &elems[offset], length);
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
// Transfer the ROM data to the emulation state
Core *core = *(Core **)&handle;
free(core->vue.pak.rom);
vueSetROM(&core->vue, rom, length);
return JNI_TRUE;
}
// Write a value to the CPU bus
JNIEXPORT void JNICALL Java_vue_NativeVue_write
(JNIEnv *env, jobject vue, jlong handle, jint address, jint type,
jint value) {
Core *core = *(Core **)&handle;
vueWrite(&core->vue, address, type, value);
}
// Write bytes to the CPU bus
JNIEXPORT jboolean JNICALL Java_vue_NativeVue_writeBytes
(JNIEnv *env, jobject vue, jlong handle, jint address, jbyteArray data,
jint offset, jint length) {
// Error checking
if (
data == NULL ||
offset < 0 ||
length < 0 ||
offset + length > (*env)->GetArrayLength(env, data)
) return JNI_FALSE;
// Perform the operation
Core *core = *(Core **)&handle;
jbyte *elems = (*env)->GetByteArrayElements(env, data, NULL);
vueWriteBytes(&core->vue, address, &elems[offset], length);
(*env)->ReleaseByteArrayElements(env, data, elems, 0);
return JNI_TRUE;
}
///////////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////////
// Retrieve the current state's breakpoint scenario
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakType
(JNIEnv *env, jobject vue, jlong handle) {
Core *core = *(Core **)&handle;
return core->breakType;
}
// Retrieve the current instruction fetch index
JNIEXPORT jint JNICALL Java_vue_NativeVue_getFetch
(JNIEnv *env, jobject vue, jlong handle) {
Core *core = *(Core **)&handle;
return core->vue.cpu.fetch;
}
// A breakpoint's address ranges have changed
JNIEXPORT void JNICALL Java_vue_NativeVue_updateRanges
(JNIEnv *env, jobject vue, jlong handle, jint index, jintArray ranges) {
Core *core = *(Core **)&handle;
Breakpoint *brk = &core->breakpoints[index];
brk->numRanges = (*env)->GetArrayLength(env, ranges);
brk->ranges = realloc(brk->ranges, brk->numRanges * 8);
jint *elems = (*env)->GetIntArrayElements(env, ranges, NULL);
memcpy(brk->ranges, elems, brk->numRanges * 8);
(*env)->ReleaseIntArrayElements(env, ranges, elems, 0);
}
// A breakpoint's enabled/hook state has changed
JNIEXPORT void JNICALL Java_vue_NativeVue_updateState
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean enabled,
jboolean fetch, jint hooks) {
Core *core = *(Core **)&handle;
Breakpoint *brk = &core->breakpoints[index];
brk->enabled = enabled;
brk->fetch = fetch;
brk->hooks = hooks;
}
// A breakpoint's condition tokens have changed
JNIEXPORT void JNICALL Java_vue_NativeVue_updateTokens
(JNIEnv *env, jobject vue, jlong handle, jint index, jintArray tokens,
jint depth, jint maxDepth) {
Core *core = *(Core **)&handle;
core->stack = realloc(core->stack, maxDepth * 4);
Breakpoint *brk = &core->breakpoints[index];
brk->numTokens = (*env)->GetArrayLength(env, tokens) / 2;
brk->tokens = realloc(brk->tokens, brk->numTokens * sizeof (Token));
jint *elems = (*env)->GetIntArrayElements(env, tokens, NULL);
memcpy(brk->tokens, elems, brk->numTokens * sizeof (Token));
(*env)->ReleaseIntArrayElements(env, tokens, elems, 0);
}

View File

@ -0,0 +1,170 @@
package vue;
// Native-backed emulation core implementation
class NativeVue extends Vue {
// Instance fields
private long handle; // Context address in native memory
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
private native long construct();
NativeVue() {
handle = construct();
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Produce a new breakpoint and add it to the collection
private native void breakpoint(long handle);
public Breakpoint breakpoint() {
var brk = super.breakpoint();
breakpoint(handle);
return brk;
}
// Release any used resources
private native void dispose(long handle);
public void dispose()
{ dispose(handle); }
// Process the simulation
private native int emulate(long handle, int maxCycles);
public int emulate(int maxCycles)
{ return emulate(handle, maxCycles); }
// Retrieve the most recent application break code
private native int getBreakCode(long handle);
public int getBreakCode() { return getBreakCode(handle); }
// Retrieve the most recent exception code
private native int getExceptionCode(long handle);
public int getExceptionCode() {
return getExceptionCode(handle);
}
// Retrieve a register value
private native int getRegister(long handle, int index, boolean system);
public int getRegister(int index, boolean system)
{ return getRegister(handle, index, system); }
// Retrieve a copy of the ROM data
private native byte[] getROM(long handle);
public byte[] getROM() { return getROM(handle); }
// Determine whether the context is native-backed
public boolean isNative() {
return true;
}
// Read a value from the CPU bus
private native int read(long handle, int address, int type);
public int read(int address, int type)
{ return read(handle, address, type); }
// Read bytes from the CPU bus
private native boolean readBytes(long handle, int address, byte[] dest,
int offset, int length);
public boolean readBytes(int address, byte[] dest, int offset, int length)
{ return readBytes(handle, address, dest, offset, length); }
// Remove a breakpoint from the collection
private native void remove(long handle, int index);
public boolean remove(Breakpoint brk) {
int index = breakpoints.indexOf(brk);
if (!super.remove(brk))
return false;
remove(handle, index);
return true;
}
// Initialize all system components
private native void reset(long handle);
public void reset()
{ reset(handle); }
// Specify a register value
private native int setRegister(long handle, int index, boolean system,
int value);
public int setRegister(int index, boolean system, int value)
{ return setRegister(handle, index, system, value); }
// Provide new ROM data
private native boolean setROM(long handle, byte[] data, int offset,
int length);
public boolean setROM(byte[] data, int offset, int length)
{ return setROM(handle, data, offset, length); }
// Write a value to the CPU bus
private native void write(long handle, int address, int type, int value);
public void write(int address, int type, int value)
{ write(handle, address, type, value); }
// Write bytes to the CPU bus
private native boolean writeBytes(long handle, int address, byte[] src,
int offset, int length);
public boolean writeBytes(int address, byte[] src, int offset, int length)
{ return writeBytes(handle, address, src, offset, length); }
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Retrieve the current state's breakpoint scenario
private native int getBreakType(long handle);
int getBreakType() {
return getBreakType(handle);
}
// Retrieve the current instruction fetch index
private native int getFetch(long handle);
int getFetch() {
return getFetch(handle);
}
// A breakpoint's address ranges have changed
private native void updateRanges(long handle, int index, int[] ranges);
void updateRanges(Breakpoint brk) {
super.updateRanges(brk);
int index = breakpoints.indexOf(brk);
if (index >= 0)
updateRanges(handle, index, brk.flattenRanges());
}
// A breakpoint's enabled/hook state has changed
private native void updateState(long handle, int index, boolean enabled,
boolean fetch, int hooks);
void updateState(Breakpoint brk) {
super.updateState(brk);
int index = breakpoints.indexOf(brk);
updateState(handle, index,
brk.isEnabled(), brk.getFetch(), brk.getHooks());
}
// A breakpoint's condition tokens have changed
private native void updateTokens(long handle, int index, int[] tokens,
int depth, int maxDepth);
void updateTokens(Breakpoint brk) {
super.updateTokens(brk);
int index = breakpoints.indexOf(brk);
if (index < 0)
return;
int maxDepth = 0;
for (var bre : breakpoints)
maxDepth = Math.max(maxDepth, bre.getDepth());
updateTokens(handle, index, brk.flattenTokens(),
brk.getDepth(), maxDepth);
}
}

309
src/desktop/vue/VIP.java Normal file
View File

@ -0,0 +1,309 @@
package vue;
// Java imports
import java.util.*;
// VIP state
class VIP {
// Instance fields
byte[] vram; // Video memory
// Private fields
private JavaVue vue; // Emulation state
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
VIP(JavaVue vue) {
vram = new byte[0x40000];
this.vue = vue;
}
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Process the simulation
void emulate(int cycles) {
}
// Read a value from the CPU bus
int read(int address, int type) {
address &= 0x0007FFFF;
// VRAM
if (address < 0x00040000)
return JavaVue.readBuffer(vram, address, type);
// Mirrors of character memory
if (address >= 0x00078000)
return JavaVue.readBuffer(vram,
address - 0x00078000 >> 13 << 15 | 0x00006000 |
address & 0x00001FFF,
type);
// I/O register or unmapped
int value = readRegister(address);
if (type < 2 && (address & 1) == 1)
value >>= 8;
switch (type) {
case Vue.S8 : return value << 24 >> 24;
case Vue.U8 : return value & 0x000000FF;
case Vue.S16: return value << 16 >> 16;
case Vue.S32: return value | readRegister(address + 2) << 16;
}
return value; // U16
}
// Read bytes from the CPU bus
void readBytes(int address, byte[] dest, int offset, int length) {
address &= 0x0007FFFF;
// Perform the operation
while (length > 0) {
int count;
// VRAM
if (address < 0x00040000) {
count = Math.min(length, 0x00040000 - address);
JavaVue.readBytes(vram, address, dest, offset, count);
}
// Mirrors of character memory
else if (address >= 0x00078000) {
count = Math.min(length, 0x2000 - (address & 0x1FFF));
JavaVue.readBytes(vram,
address - 0x00078000 >> 13 << 15 | 0x00006000 |
address & 0x00001FFF,
dest, offset, count);
}
// I/O register or unmapped
else {
count = Math.min(length, 0x00078000 - address);
// Read all registers in the range
while (count > 0) {
int value = readRegister(address);
// Odd address
if ((address & 1) == 1) {
dest[offset] = (byte) (value >> 8);
address++;
count --;
length --;
offset ++;
continue;
}
// Even address
int size = count == 1 ? 1 : 2;
dest[offset] = (byte) value;
if (size == 2)
dest[offset + 1] = (byte) (value >> 8);
address += size;
count -= size;
length -= size;
offset += size;
}
continue;
}
// Advance to the next region
address += count;
length -= count;
offset += count;
}
}
// System reset
void reset() {
}
// Determine the number of CPU cycles until a breakpoint could trigger
int until(int cycles) {
return cycles;
}
// Write a value to the CPU bus
void write(int address, int type, int value) {
address &= 0x0007FFFF;
// VRAM
if (address < 0x00040000) {
JavaVue.writeBuffer(vram, address, type, value);
return;
}
// Mirrors of character memory
if (address >= 0x00078000) {
JavaVue.writeBuffer(vram,
address - 0x00078000 >> 13 << 15 | 0x00006000 |
address & 0x00001FFF,
type, value);
return;
}
// I/O register or unmapped
if (type < 2 && (address & 1) == 1)
value <<= 8;
writeRegister(address, value);
if (type == Vue.S32)
writeRegister(address + 2, value >> 16);
}
// Write bytes to the CPU bus
void writeBytes(int address, byte[] src, int offset, int length) {
address &= 0x0007FFFF;
// Perform the operation
while (length > 0) {
int count;
// VRAM
if (address < 0x00040000) {
count = Math.min(length, 0x00040000 - address);
JavaVue.writeBytes(vram, address, src, offset, count);
}
// Mirrors of character memory
else if (address >= 0x00078000) {
count = Math.min(length, 0x2000 - (address & 0x1FFF));
JavaVue.writeBytes(vram,
address - 0x00078000 >> 13 << 15 | 0x00006000 |
address & 0x00001FFF,
src, offset, count);
}
// I/O register or unmapped
else {
count = Math.min(length, 0x00078000 - address);
// Write all registers in the range
while (count > 0) {
int value = src[offset] & 0xFF;
// Odd address
if ((address & 1) == 1) {
writeRegister(address, value << 8);
address++;
count --;
length --;
offset ++;
continue;
}
// Even address
int size = count == 1 ? 1 : 2;
if (size == 2)
value |= src[offset + 1] << 8;
writeRegister(address, value);
address += size;
count -= size;
length -= size;
offset += size;
}
continue;
}
// Advance to the next region
address += count;
length -= count;
offset += count;
}
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Read an I/O register
private int readRegister(int address) {
// Process by register
switch (address & ~1) {
case 0x0005F800: break; // INTPND
case 0x0005F802: break; // INTENB
case 0x0005F804: break; // INTCLR
case 0x0005F820: break; // DPSTTS
case 0x0005F822: break; // DPCTRL
case 0x0005F824: break; // BRTA
case 0x0005F826: break; // BRTB
case 0x0005F828: break; // BRTC
case 0x0005F82A: break; // REST
case 0x0005F82E: break; // FRMCYC
case 0x0005F830: break; // CTA
case 0x0005F840: break; // XPSTTS
case 0x0005F842: break; // XPCTRL
case 0x0005F844: break; // VER
case 0x0005F848: break; // SPT0
case 0x0005F84A: break; // SPT1
case 0x0005F84C: break; // SPT2
case 0x0005F84E: break; // SPT3
case 0x0005F860: break; // GPLT0
case 0x0005F862: break; // GPLT1
case 0x0005F864: break; // GPLT2
case 0x0005F866: break; // GPLT3
case 0x0005F868: break; // JPLT0
case 0x0005F86A: break; // JPLT1
case 0x0005F86C: break; // JPLT2
case 0x0005F86E: break; // JPLT3
case 0x0005F870: break; // BKCOL
}
// Unmapped
return 0;
}
// Write an I/O register
private void writeRegister(int address, int value) {
// Process by register
switch (address & ~1) {
case 0x0005F800: break; // INTPND
case 0x0005F802: break; // INTENB
case 0x0005F804: break; // INTCLR
case 0x0005F820: break; // DPSTTS
case 0x0005F822: break; // DPCTRL
case 0x0005F824: break; // BRTA
case 0x0005F826: break; // BRTB
case 0x0005F828: break; // BRTC
case 0x0005F82A: break; // REST
case 0x0005F82E: break; // FRMCYC
case 0x0005F830: break; // CTA
case 0x0005F840: break; // XPSTTS
case 0x0005F842: break; // XPCTRL
case 0x0005F844: break; // VER
case 0x0005F848: break; // SPT0
case 0x0005F84A: break; // SPT1
case 0x0005F84C: break; // SPT2
case 0x0005F84E: break; // SPT3
case 0x0005F860: break; // GPLT0
case 0x0005F862: break; // GPLT1
case 0x0005F864: break; // GPLT2
case 0x0005F866: break; // GPLT3
case 0x0005F868: break; // JPLT0
case 0x0005F86A: break; // JPLT1
case 0x0005F86C: break; // JPLT2
case 0x0005F86E: break; // JPLT3
case 0x0005F870: break; // BKCOL
}
}
}

266
src/desktop/vue/Vue.java Normal file
View File

@ -0,0 +1,266 @@
package vue;
// Java imports
import java.util.*;
// Template for emulation core implementations
public abstract class Vue {
// Instance fields
ArrayList<Breakpoint> breakpoints; // Current breakpoints
// Static fields
private static String nativeID = null; // ID of loaded native library
///////////////////////////////////////////////////////////////////////////
// Constants //
///////////////////////////////////////////////////////////////////////////
// Memory access types
public static final int S8 = 0;
public static final int U8 = 1;
public static final int S16 = 2;
public static final int U16 = 3;
public static final int S32 = 4;
public static final int CANCEL = 5;
// System register indexes
public static final int ADTRE = 25;
public static final int CHCW = 24;
public static final int ECR = 4;
public static final int EIPC = 0;
public static final int EIPSW = 1;
public static final int FEPC = 2;
public static final int FEPSW = 3;
public static final int PIR = 6;
public static final int PSW = 5;
public static final int TKCW = 7;
// Non-standard register indexes
public static final int PC = -1;
public static final int JUMP_FROM = -2;
public static final int JUMP_TO = -3;
// Program register indexes
public static final int GP = 4;
public static final int HP = 2;
public static final int LP = 31;
public static final int SP = 3;
public static final int TP = 5;
// Instruction IDs
public static final int ILLEGAL = -1;
public static final int ADD_IMM = 0;
public static final int ADD_REG = 1;
public static final int ADDF_S = 2;
public static final int ADDI = 3;
public static final int AND = 4;
public static final int ANDBSU = 5;
public static final int ANDI = 6;
public static final int ANDNBSU = 7;
public static final int BCOND = 8;
public static final int CAXI = 9;
public static final int CLI = 10;
public static final int CMP_IMM = 11;
public static final int CMP_REG = 12;
public static final int CMPF_S = 13;
public static final int CVT_SW = 14;
public static final int CVT_WS = 15;
public static final int DIV = 16;
public static final int DIVF_S = 17;
public static final int DIVU = 18;
public static final int HALT = 19;
public static final int IN_B = 20;
public static final int IN_H = 21;
public static final int IN_W = 22;
public static final int JAL = 23;
public static final int JMP = 24;
public static final int JR = 25;
public static final int LD_B = 26;
public static final int LD_H = 27;
public static final int LD_W = 28;
public static final int LDSR = 29;
public static final int MOV_IMM = 30;
public static final int MOV_REG = 31;
public static final int MOVBSU = 32;
public static final int MOVEA = 33;
public static final int MOVHI = 34;
public static final int MPYHW = 35;
public static final int MUL = 36;
public static final int MULF_S = 37;
public static final int MULU = 38;
public static final int NOT = 39;
public static final int NOTBSU = 40;
public static final int OR = 41;
public static final int ORBSU = 42;
public static final int ORI = 43;
public static final int ORNBSU = 44;
public static final int OUT_B = 45;
public static final int OUT_H = 46;
public static final int OUT_W = 47;
public static final int RETI = 48;
public static final int REV = 49;
public static final int SAR_IMM = 50;
public static final int SAR_REG = 51;
public static final int SCH0BSD = 52;
public static final int SCH0BSU = 53;
public static final int SCH1BSD = 54;
public static final int SCH1BSU = 55;
public static final int SEI = 56;
public static final int SETF = 57;
public static final int SHL_IMM = 58;
public static final int SHL_REG = 59;
public static final int SHR_IMM = 60;
public static final int SHR_REG = 61;
public static final int ST_B = 62;
public static final int ST_H = 63;
public static final int ST_W = 64;
public static final int STSR = 65;
public static final int SUB = 66;
public static final int SUBF_S = 67;
public static final int TRAP = 68;
public static final int TRNC_SW = 69;
public static final int XB = 70;
public static final int XH = 71;
public static final int XOR = 72;
public static final int XORBSU = 73;
public static final int XORI = 74;
public static final int XORNBSU = 75;
///////////////////////////////////////////////////////////////////////////
// Static Methods //
///////////////////////////////////////////////////////////////////////////
// Produce an emulation core context
public static Vue create(boolean useNative) {
return !useNative ? new JavaVue() :
nativeID != null ? new NativeVue() : null;
}
// Retrieve the ID of the loaded native library, if any
public static String getNativeID() {
return nativeID;
}
// Specify the ID of the loaded native library
public static void setNativeID(String nativeID) {
Vue.nativeID = nativeID;
}
///////////////////////////////////////////////////////////////////////////
// Constructors //
///////////////////////////////////////////////////////////////////////////
// Default constructor
Vue() {
breakpoints = new ArrayList<Breakpoint>();
}
///////////////////////////////////////////////////////////////////////////
// Public Methods //
///////////////////////////////////////////////////////////////////////////
// Produce a new breakpoint and add it to the collection
public Breakpoint breakpoint() {
var brk = new Breakpoint(this);
breakpoints.add(brk);
return brk;
}
// Release any used resources
public abstract void dispose();
// Process the simulation
public abstract int emulate(int maxCycles);
// Evaluate an expression
public Object evaluate(String expression) {
var inst = new Instruction(read(getRegister(PC, true), S32));
var brk = new Breakpoint(this);
brk.setCondition(expression);
brk.setEnabled (true);
return brk.evaluateTyped(
new int[brk.getDepth()], 0, inst, inst.access(this));
}
// Retrieve the most recent applicaiton break code
public abstract int getBreakCode();
// Retrieve the most recent exception code
public abstract int getExceptionCode();
// Retrieve a register value
public abstract int getRegister(int index, boolean system);
// Retrieve a copy of the ROM data
public abstract byte[] getROM();
// Determine whether the context is native-backed
public abstract boolean isNative();
// Produce an array of the current breakpoint collection
public Breakpoint[] listBreakpoints() {
return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
}
// Read a value from the CPU bus
public abstract int read(int address, int type);
// Read bytes from the CPU bus
public abstract boolean readBytes(int address, byte[] dest, int offset,
int length);
// Remove a breakpoint from the collection
public boolean remove(Breakpoint brk) {
if (!breakpoints.remove(brk))
return false;
brk.remove();
return true;
}
// Initialize all system components
public abstract void reset();
// Specify a register value
public abstract int setRegister(int index, boolean system, int value);
// Provide new ROM data
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
public abstract boolean writeBytes(int address, byte[] src, int offset,
int length);
///////////////////////////////////////////////////////////////////////////
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Retrieve the current state's breakpoint scenario
abstract int getBreakType();
// Retrieve the current instruction fetch index
abstract int getFetch();
// A breakpoint's address ranges have changed
void updateRanges(Breakpoint brk) { }
// A breakpoint's enabled/hook state has changed
void updateState(Breakpoint brk) { }
// A breakpoint's condition tokens have changed
void updateTokens(Breakpoint brk) { }
}