Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77c256f2e0 | ||
|
|
386f4a904f | ||
|
|
1cc268e5d0 | ||
|
|
b77f2a5ab1 | ||
|
|
265aeed8cd | ||
|
|
133a5e7e82 | ||
|
|
17277bb354 | ||
|
|
7d31c55391 | ||
|
|
3cf006ba13 | ||
|
|
fccb799a9c | ||
|
|
a7985bad6e | ||
|
|
b33a6c3055 | ||
|
|
89feaaa3e9 | ||
|
|
573f72e46f | ||
|
|
94486ecf02 | ||
|
|
70f0f2a318 | ||
|
|
85cc0f5754 | ||
|
|
af5fc0c4fc | ||
|
|
ff07e1907f | ||
|
|
3dc8b93f94 | ||
|
|
5a02e7e52d | ||
|
|
b2adf1f9dc | ||
|
|
f753f9f59b | ||
|
|
dd066e0bbb | ||
|
|
0927a42107 | ||
|
|
4b3852a1d2 |
@@ -1,11 +1,12 @@
|
|||||||
* text=auto
|
*.c text eol=lf diff=c
|
||||||
|
*.css text eol=lf diff=css
|
||||||
*.c text diff=c
|
*.h text eol=lf diff=c
|
||||||
*.h text diff=c
|
*.html text eol=lf diff=html
|
||||||
*.html text diff=html
|
*.java text eol=lf diff=java
|
||||||
*.java text diff=java
|
*.js text eol=lf diff=js
|
||||||
*.sample text
|
*.txt text eol=lf
|
||||||
*.txt text
|
|
||||||
|
|
||||||
*.class binary
|
*.class binary
|
||||||
*.dll binary
|
*.dll binary
|
||||||
|
*.wasm binary
|
||||||
|
*.woff2 binary
|
||||||
|
|||||||
@@ -0,0 +1,162 @@
|
|||||||
|
/* This file is included into vb.c and cannot be compiled on its own. */
|
||||||
|
#ifdef VBAPI
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Utility Functions *****************************/
|
||||||
|
|
||||||
|
/* Read a typed value from a buffer in host memory */
|
||||||
|
static int32_t busReadBuffer(
|
||||||
|
uint8_t *buffer, uint32_t size, uint32_t offset, int type) {
|
||||||
|
|
||||||
|
/* There is no data */
|
||||||
|
if (buffer == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Mirror the buffer across its address range */
|
||||||
|
offset &= size - 1;
|
||||||
|
|
||||||
|
/* Processing by type */
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
/* Generic implementation */
|
||||||
|
#ifndef VB_LITTLE_ENDIAN
|
||||||
|
case VB_S8 :
|
||||||
|
return (int8_t) buffer[offset];
|
||||||
|
case VB_U8:
|
||||||
|
return buffer[offset];
|
||||||
|
case VB_S16:
|
||||||
|
return (int32_t) (int8_t)
|
||||||
|
buffer[offset + 1] << 8 | buffer[offset];
|
||||||
|
case VB_U16:
|
||||||
|
offset &= 0xFFFFFFFE;
|
||||||
|
return (int32_t) (int8_t)
|
||||||
|
buffer[offset + 1] << 8 | buffer[offset];
|
||||||
|
case VB_S32:
|
||||||
|
offset &= 0xFFFFFFFC;
|
||||||
|
return
|
||||||
|
(int32_t) buffer[offset + 3] << 24 |
|
||||||
|
(int32_t) buffer[offset + 2] << 16 |
|
||||||
|
(int32_t) buffer[offset + 1] << 8 |
|
||||||
|
buffer[offset ]
|
||||||
|
;
|
||||||
|
|
||||||
|
/* Little-endian implementation */
|
||||||
|
#else
|
||||||
|
case VB_S8 : return *(int8_t *)&buffer[offset ];
|
||||||
|
case VB_U8 : return buffer[offset ];
|
||||||
|
case VB_S16: return *(int16_t *)&buffer[offset & 0xFFFFFFFE];
|
||||||
|
case VB_U16: return *(uint16_t *)&buffer[offset & 0xFFFFFFFE];
|
||||||
|
case VB_S32: return *(int32_t *)&buffer[offset & 0xFFFFFFFC];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Invalid type */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write a typed value to a buffer in host memory */
|
||||||
|
static void busWriteBuffer(
|
||||||
|
uint8_t *buffer, uint32_t size, uint32_t offset, int type, int32_t value) {
|
||||||
|
|
||||||
|
/* There is no data */
|
||||||
|
if (buffer == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Mirror the buffer across its address range */
|
||||||
|
offset &= size - 1;
|
||||||
|
|
||||||
|
/* Processing by type */
|
||||||
|
switch (type) {
|
||||||
|
|
||||||
|
/* Generic implementation */
|
||||||
|
#ifndef VB_LITTLE_ENDIAN
|
||||||
|
case VB_S32:
|
||||||
|
offset &= 0xFFFFFFFC;
|
||||||
|
buffer[offset ] = value;
|
||||||
|
buffer[offset + 1] = value >> 8;
|
||||||
|
buffer[offset + 2] = value >> 16;
|
||||||
|
buffer[offset + 3] = value >> 24;
|
||||||
|
break;
|
||||||
|
case VB_S16:
|
||||||
|
case VB_U16:
|
||||||
|
offset &= 0xFFFFFFFE;
|
||||||
|
buffer[offset ] = value;
|
||||||
|
buffer[offset + 1] = value >> 8;
|
||||||
|
break;
|
||||||
|
case VB_S8 :
|
||||||
|
case VB_U8 :
|
||||||
|
buffer[offset ] = value;
|
||||||
|
|
||||||
|
/* Little-endian implementation */
|
||||||
|
#else
|
||||||
|
case VB_S8 :
|
||||||
|
case VB_U8 :
|
||||||
|
buffer[offset ] = value;
|
||||||
|
break;
|
||||||
|
case VB_S16:
|
||||||
|
case VB_U16:
|
||||||
|
*(int16_t *)&buffer[offset & 0xFFFFFFFE] = value;
|
||||||
|
break;
|
||||||
|
case VB_S32:
|
||||||
|
*(int32_t *)&buffer[offset & 0xFFFFFFFC] = value;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Module Functions ******************************/
|
||||||
|
|
||||||
|
/* Read a typed value from the simulation bus */
|
||||||
|
static int32_t busRead(VB *sim, uint32_t address, int type) {
|
||||||
|
|
||||||
|
/* Processing by address region */
|
||||||
|
switch (address >> 24 & 7) {
|
||||||
|
case 0: return 0; /* VIP */
|
||||||
|
case 1: return 0; /* VSU */
|
||||||
|
case 2: return 0; /* Misc. hardware */
|
||||||
|
case 3: return 0; /* Unmapped */
|
||||||
|
case 4: return 0; /* Game pak expansion */
|
||||||
|
case 5: return /* WRAM */
|
||||||
|
busReadBuffer(sim->wram , 0x10000 , address, type);
|
||||||
|
case 6: return /* Game pak RAM */
|
||||||
|
busReadBuffer(sim->cart.ram, sim->cart.ramSize, address, type);
|
||||||
|
case 7: return /* Game pak ROM */
|
||||||
|
busReadBuffer(sim->cart.rom, sim->cart.romSize, address, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Unreachable */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write a typed value to the simulation bus */
|
||||||
|
static void busWrite(VB*sim,uint32_t address,int type,int32_t value,int debug){
|
||||||
|
(void) debug;
|
||||||
|
|
||||||
|
/* Processing by address region */
|
||||||
|
switch (address >> 24 & 7) {
|
||||||
|
case 0: break; /* VIP */
|
||||||
|
case 1: break; /* VSU */
|
||||||
|
case 2: break; /* Misc. hardware */
|
||||||
|
case 3: break; /* Unmapped */
|
||||||
|
case 4: break; /* Game pak expansion */
|
||||||
|
case 5: /* WRAM */
|
||||||
|
busWriteBuffer(sim->wram ,0x10000 ,address,type,value);
|
||||||
|
break;
|
||||||
|
case 6: /* Game pak RAM */
|
||||||
|
busWriteBuffer(sim->cart.ram,sim->cart.ramSize,address,type,value);
|
||||||
|
break;
|
||||||
|
case 7: /* Game pak ROM */
|
||||||
|
busWriteBuffer(sim->cart.rom,sim->cart.romSize,address,type,value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* VBAPI */
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
#ifndef VBAPI
|
||||||
|
#define VBAPI
|
||||||
|
#endif
|
||||||
|
#include <vb.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Utility Functions *****************************/
|
||||||
|
|
||||||
|
/* Select the lesser of two unsigned numbers */
|
||||||
|
static uint32_t Min(uint32_t a, uint32_t b) {
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sign-extend an integer, masking upper bits if positive */
|
||||||
|
#ifndef VB_SIGNED_PROPAGATE
|
||||||
|
/* Generic implementation */
|
||||||
|
static int32_t SignExtend(int32_t value, int bits) {
|
||||||
|
return value & 1 << (bits - 1) ?
|
||||||
|
value | ~0 << bits :
|
||||||
|
value & ((1 << bits) - 1)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Sign-propagating implementation */
|
||||||
|
#define SignExtend(v, b) ((int32_t) (v) << (32 - (b)) >> (32 - (b)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**************************** Sub-Module Imports *****************************/
|
||||||
|
|
||||||
|
#include "bus.c"
|
||||||
|
#include "cpu.c"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/***************************** Module 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 can be simulated without a breakpoint */
|
||||||
|
static uint32_t sysUntil(VB *sim, uint32_t clocks) {
|
||||||
|
clocks = cpuUntil(sim, clocks);
|
||||||
|
return clocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************ API ************************************/
|
||||||
|
|
||||||
|
/* Process a simulation */
|
||||||
|
int vbEmulate(VB *sim, uint32_t *clocks) {
|
||||||
|
int brk; /* A break was requested */
|
||||||
|
uint32_t until; /* Number of clocks during which no break will occur */
|
||||||
|
|
||||||
|
/* Process all clocks */
|
||||||
|
for (brk = 0; *clocks != 0 && !brk; *clocks -= until) {
|
||||||
|
until = sysUntil (sim, *clocks);
|
||||||
|
brk = sysEmulate(sim, until );
|
||||||
|
}
|
||||||
|
|
||||||
|
return brk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Process multiple simulations */
|
||||||
|
int vbEmulateEx(VB **sims, int count, uint32_t *clocks) {
|
||||||
|
int brk; /* A break was requested */
|
||||||
|
uint32_t until; /* Number of clocks during which no break will occur */
|
||||||
|
int x; /* Iterator */
|
||||||
|
|
||||||
|
/* Process all clocks */
|
||||||
|
for (brk = 0; *clocks != 0 && !brk; *clocks -= until) {
|
||||||
|
until = *clocks;
|
||||||
|
for (x = 0; x < count; x++)
|
||||||
|
until = sysUntil (sims[x], until);
|
||||||
|
for (x = 0; x < count; x++)
|
||||||
|
brk |= sysEmulate(sims[x], until);
|
||||||
|
}
|
||||||
|
|
||||||
|
return brk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve a current breakpoint handler */
|
||||||
|
void* vbGetCallback(VB *sim, int id) {
|
||||||
|
switch (id) {
|
||||||
|
case VB_ONEXCEPTION: return *(void **)&sim->onException;
|
||||||
|
case VB_ONEXECUTE : return *(void **)&sim->onExecute;
|
||||||
|
case VB_ONFETCH : return *(void **)&sim->onFetch;
|
||||||
|
case VB_ONREAD : return *(void **)&sim->onRead;
|
||||||
|
case VB_ONWRITE : return *(void **)&sim->onWrite;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve the value of a register */
|
||||||
|
int32_t vbGetRegister(VB *sim, int type, int id) {
|
||||||
|
switch (type) {
|
||||||
|
case VB_PROGRAM:
|
||||||
|
return id < 0 || id > 31 ? 0 : sim->cpu.program[id];
|
||||||
|
case VB_SYSTEM:
|
||||||
|
return cpuGetSystemRegister(sim, id);
|
||||||
|
case VB_OTHER:
|
||||||
|
switch (id) {
|
||||||
|
case VB_PC: return sim->cpu.pc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0; /* Invalid type */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve a handle to the current cartridge ROM data */
|
||||||
|
uint8_t* vbGetROM(VB *sim, uint32_t *size) {
|
||||||
|
if (size != NULL)
|
||||||
|
*size = sim->cart.romSize;
|
||||||
|
return sim->cart.rom;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Retrieve a handle to the current cartridge RAM data */
|
||||||
|
uint8_t* vbGetSRAM(VB *sim, uint32_t *size) {
|
||||||
|
if (size != NULL)
|
||||||
|
*size = sim->cart.ramSize;
|
||||||
|
return sim->cart.ram;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepare a simulation instance for use */
|
||||||
|
void vbInit(VB *sim) {
|
||||||
|
|
||||||
|
/* Breakpoint handlers */
|
||||||
|
sim->onException = NULL;
|
||||||
|
sim->onExecute = NULL;
|
||||||
|
sim->onFetch = NULL;
|
||||||
|
sim->onRead = NULL;
|
||||||
|
sim->onWrite = NULL;
|
||||||
|
|
||||||
|
/* Game pak */
|
||||||
|
sim->cart.ram = NULL;
|
||||||
|
sim->cart.ramSize = 0;
|
||||||
|
sim->cart.rom = NULL;
|
||||||
|
sim->cart.romSize = 0;
|
||||||
|
|
||||||
|
/* Hardware reset */
|
||||||
|
vbReset(sim);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read a value from memory */
|
||||||
|
int32_t vbRead(VB *sim, uint32_t address, int type) {
|
||||||
|
return busRead(sim, address, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Read multiple bytes from memory */
|
||||||
|
void vbReadEx(VB *sim, uint32_t address, uint8_t *buffer, uint32_t length) {
|
||||||
|
while (length--) *buffer++ = busRead(sim, address++, VB_U8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Simulate a hardware reset */
|
||||||
|
void vbReset(VB *sim) {
|
||||||
|
int x; /* Iterator */
|
||||||
|
|
||||||
|
/* Reset WRAM (the hardware does not do this) */
|
||||||
|
for (x = 0; x < 0x10000; x++)
|
||||||
|
sim->wram[x] = 0x00;
|
||||||
|
|
||||||
|
/* CPU (normal) */
|
||||||
|
sim->cpu.pc = 0xFFFFFFF0;
|
||||||
|
cpuSetSystemRegister(sim, VB_ECR, 0x0000FFF0, 1);
|
||||||
|
cpuSetSystemRegister(sim, VB_PSW, 0x00008000, 1);
|
||||||
|
for (x = 0; x < 5; x++)
|
||||||
|
sim->cpu.irq[x] = 0;
|
||||||
|
|
||||||
|
/* CPU (extra, hardware doesn't 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 (internal) */
|
||||||
|
sim->cpu.bitstring = 0;
|
||||||
|
sim->cpu.clocks = 0;
|
||||||
|
sim->cpu.exception = 0;
|
||||||
|
sim->cpu.stage = CPU_FETCH;
|
||||||
|
sim->cpu.step = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify a breakpoint handler */
|
||||||
|
void* vbSetCallback(VB *sim, int id, void *proc) {
|
||||||
|
void *prev = vbGetCallback(sim, id);
|
||||||
|
switch (id) {
|
||||||
|
case VB_ONEXCEPTION: *(void **)&sim->onException = proc; break;
|
||||||
|
case VB_ONEXECUTE : *(void **)&sim->onExecute = proc; break;
|
||||||
|
case VB_ONFETCH : *(void **)&sim->onFetch = proc; break;
|
||||||
|
case VB_ONREAD : *(void **)&sim->onRead = proc; break;
|
||||||
|
case VB_ONWRITE : *(void **)&sim->onWrite = proc; break;
|
||||||
|
}
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify a value for a register */
|
||||||
|
int32_t vbSetRegister(VB *sim, int type, int id, int32_t value) {
|
||||||
|
switch (type) {
|
||||||
|
case VB_PROGRAM:
|
||||||
|
return id < 1 || id > 31 ? 0 : (sim->cpu.program[id] = value);
|
||||||
|
case VB_SYSTEM:
|
||||||
|
return cpuSetSystemRegister(sim, id, value, 1);
|
||||||
|
case VB_OTHER:
|
||||||
|
switch (id) {
|
||||||
|
case VB_PC:
|
||||||
|
sim->cpu.bitstring = 0;
|
||||||
|
sim->cpu.clocks = 0;
|
||||||
|
sim->cpu.exception = 0;
|
||||||
|
sim->cpu.stage = CPU_FETCH;
|
||||||
|
return sim->cpu.pc = value & 0xFFFFFFFE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0; /* Invalid type or ID */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify a cartridge ROM buffer */
|
||||||
|
int vbSetROM(VB *sim, uint8_t *data, uint32_t size) {
|
||||||
|
|
||||||
|
/* Specifying no ROM */
|
||||||
|
if (data == NULL) {
|
||||||
|
sim->cart.rom = NULL;
|
||||||
|
sim->cart.romSize = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error checking */
|
||||||
|
if (
|
||||||
|
size < 4 ||
|
||||||
|
size > 0x1000000 ||
|
||||||
|
(size & (size - 1)) /* Power of 2 */
|
||||||
|
) return 1;
|
||||||
|
|
||||||
|
/* Register the ROM data */
|
||||||
|
sim->cart.rom = data;
|
||||||
|
sim->cart.romSize = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify a cartridge RAM buffer */
|
||||||
|
int vbSetSRAM(VB *sim, uint8_t *data, uint32_t size) {
|
||||||
|
|
||||||
|
/* Specifying no SRAM */
|
||||||
|
if (data == NULL) {
|
||||||
|
sim->cart.ram = NULL;
|
||||||
|
sim->cart.ramSize = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error checking */
|
||||||
|
if (
|
||||||
|
size < 4 ||
|
||||||
|
size > 0x1000000 ||
|
||||||
|
(size & (size - 1)) /* Power of 2 */
|
||||||
|
) return 1;
|
||||||
|
|
||||||
|
/* Register the SRAM data */
|
||||||
|
sim->cart.ram = data;
|
||||||
|
sim->cart.ramSize = size;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write a value to memory */
|
||||||
|
void vbWrite(VB *sim, uint32_t address, int type, int32_t value) {
|
||||||
|
busWrite(sim, address, type, value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Write multiple values to memory */
|
||||||
|
void vbWriteEx(VB *sim, uint32_t address, uint8_t *buffer, uint32_t length) {
|
||||||
|
while (length--) busWrite(sim, address++, VB_U8, *buffer++, 1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,221 @@
|
|||||||
|
#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>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**************************** Compilation Control ****************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
VB_LITTLE_ENDIAN
|
||||||
|
|
||||||
|
Accelerates simulated memory reads and writes on hosts with little-endian
|
||||||
|
byte ordering. If left undefined, a generic implementation is used instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
VB_SIGNED_PROPAGATE
|
||||||
|
|
||||||
|
Accelerates sign extension by assuming the >> operator propagates the sign
|
||||||
|
bit for signed types and does not for unsigned types. If left undefined, a
|
||||||
|
generic implementation is used instead.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************* Constants *********************************/
|
||||||
|
|
||||||
|
/* Data types */
|
||||||
|
#define VB_S8 0
|
||||||
|
#define VB_U8 1
|
||||||
|
#define VB_S16 2
|
||||||
|
#define VB_U16 3
|
||||||
|
#define VB_S32 4
|
||||||
|
|
||||||
|
/* Register types */
|
||||||
|
#define VB_PROGRAM 0
|
||||||
|
#define VB_SYSTEM 1
|
||||||
|
#define VB_OTHER 2
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
|
||||||
|
/* Other registers */
|
||||||
|
#define VB_PC 0
|
||||||
|
|
||||||
|
/* Callbacks */
|
||||||
|
#define VB_ONEXCEPTION 0
|
||||||
|
#define VB_ONEXECUTE 1
|
||||||
|
#define VB_ONFETCH 2
|
||||||
|
#define VB_ONREAD 3
|
||||||
|
#define VB_ONWRITE 4
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** Types ***********************************/
|
||||||
|
|
||||||
|
/* Forward references */
|
||||||
|
typedef struct VB VB;
|
||||||
|
|
||||||
|
/* Memory access descriptor */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t address; /* Memory address */
|
||||||
|
uint32_t clocks; /* Number of clocks taken */
|
||||||
|
int32_t value; /* Data loaded/to store */
|
||||||
|
uint8_t type; /* Data type */
|
||||||
|
} VBAccess;
|
||||||
|
|
||||||
|
/* Exception descriptor */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t address; /* Memory address of handler routine */
|
||||||
|
uint16_t code; /* Cause code */
|
||||||
|
uint8_t cancel; /* Set to cancel the exception */
|
||||||
|
} VBException;
|
||||||
|
|
||||||
|
/* Instruction descriptor */
|
||||||
|
typedef struct {
|
||||||
|
uint32_t address; /* Memory address */
|
||||||
|
uint16_t code[2]; /* Fetched code units */
|
||||||
|
uint8_t size; /* Size in halfwords */
|
||||||
|
} VBInstruction;
|
||||||
|
|
||||||
|
/* Breakpoint handlers */
|
||||||
|
typedef int (*VBExceptionProc)(VB *, VBException *);
|
||||||
|
typedef int (*VBExecuteProc )(VB *, VBInstruction *);
|
||||||
|
typedef int (*VBFetchProc )(VB *, int, VBAccess *);
|
||||||
|
typedef int (*VBMemoryProc )(VB *, VBAccess *);
|
||||||
|
|
||||||
|
/* Simulation state */
|
||||||
|
struct VB {
|
||||||
|
|
||||||
|
/* Game pak */
|
||||||
|
struct {
|
||||||
|
uint8_t *ram; /* (S)RAM data */
|
||||||
|
uint8_t *rom; /* ROM data */
|
||||||
|
uint32_t ramSize; /* Size of RAM in bytes */
|
||||||
|
uint32_t romSize; /* Size of ROM in bytes */
|
||||||
|
} cart;
|
||||||
|
|
||||||
|
/* CPU */
|
||||||
|
struct {
|
||||||
|
|
||||||
|
/* Main registers */
|
||||||
|
int32_t program[32]; /* Program registers */
|
||||||
|
uint32_t pc; /* Program counter */
|
||||||
|
|
||||||
|
/* System registers */
|
||||||
|
uint32_t adtre; /* Address trap target address */
|
||||||
|
uint32_t eipc; /* Exception return PC */
|
||||||
|
uint32_t eipsw; /* Exception return PSW */
|
||||||
|
uint32_t fepc; /* Duplexed exception return PC */
|
||||||
|
uint32_t fepsw; /* Duplexed exception return PSW */
|
||||||
|
uint32_t sr29; /* System register 29 */
|
||||||
|
uint32_t sr31; /* System register 31 */
|
||||||
|
|
||||||
|
/* Exception cause register */
|
||||||
|
struct {
|
||||||
|
uint16_t eicc; /* Exception/interrupt cause code */
|
||||||
|
uint16_t fecc; /* Duplexed exception cause code */
|
||||||
|
} ecr;
|
||||||
|
|
||||||
|
/* Cache control word */
|
||||||
|
struct {
|
||||||
|
uint8_t ice; /* Instruction cache enable */
|
||||||
|
} chcw;
|
||||||
|
|
||||||
|
/* Program status word */
|
||||||
|
struct {
|
||||||
|
uint8_t ae; /* Address trap enable */
|
||||||
|
uint8_t cy; /* Carry */
|
||||||
|
uint8_t ep; /* Exception pending */
|
||||||
|
uint8_t fiv; /* Floating-point invalid operation */
|
||||||
|
uint8_t fov; /* Floating-point overflow */
|
||||||
|
uint8_t fpr; /* Floating point precision degradation */
|
||||||
|
uint8_t fro; /* Floating-point reserved operand */
|
||||||
|
uint8_t fud; /* Floating-point underflow */
|
||||||
|
uint8_t fzd; /* Floating-point zero division */
|
||||||
|
uint8_t i; /* Interrupt level */
|
||||||
|
uint8_t id; /* Interrupt disable */
|
||||||
|
uint8_t np; /* Duplexed exception pending */
|
||||||
|
uint8_t ov; /* Overflow */
|
||||||
|
uint8_t s; /* Sign */
|
||||||
|
uint8_t z; /* Zero */
|
||||||
|
} psw;
|
||||||
|
|
||||||
|
/* Instruction */
|
||||||
|
struct {
|
||||||
|
int32_t aux[9]; /* Auxiliary storage */
|
||||||
|
void *def; /* Operation descriptor */
|
||||||
|
uint16_t code[2]; /* Fetched code units */
|
||||||
|
uint8_t size; /* Size in bytes */
|
||||||
|
} inst;
|
||||||
|
|
||||||
|
/* Other state */
|
||||||
|
uint32_t clocks; /* Clocks until next activity */
|
||||||
|
uint32_t fpFlags; /* Floating-point exception flags */
|
||||||
|
uint16_t exception; /* Current exception cause code */
|
||||||
|
uint8_t irq[5]; /* Interrupt requests */
|
||||||
|
uint8_t bitstring; /* Processing a bit string instruction */
|
||||||
|
uint8_t stage; /* Pipeline stage handler */
|
||||||
|
uint8_t step; /* General processing step index */
|
||||||
|
} cpu;
|
||||||
|
|
||||||
|
/* Breakpoint hooks */
|
||||||
|
VBExceptionProc onException; /* Exception raised */
|
||||||
|
VBExecuteProc onExecute; /* Instruction execute */
|
||||||
|
VBFetchProc onFetch; /* Bus read (fetch) */
|
||||||
|
VBMemoryProc onRead; /* Bus read (execute) */
|
||||||
|
VBMemoryProc onWrite; /* Bus write */
|
||||||
|
|
||||||
|
/* Other state */
|
||||||
|
uint8_t wram[0x10000]; /* System memory */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/************************************ API ************************************/
|
||||||
|
|
||||||
|
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 int32_t vbGetRegister(VB *sim, int type, int id);
|
||||||
|
VBAPI uint8_t* vbGetROM (VB *sim, uint32_t *size);
|
||||||
|
VBAPI uint8_t* vbGetSRAM (VB *sim, uint32_t *size);
|
||||||
|
VBAPI void vbInit (VB *sim);
|
||||||
|
VBAPI int32_t vbRead (VB *sim, uint32_t address, int type);
|
||||||
|
VBAPI void vbReadEx (VB *sim, uint32_t address, uint8_t *buffer, uint32_t length);
|
||||||
|
VBAPI void vbReset (VB *sim);
|
||||||
|
VBAPI void* vbSetCallback(VB *sim, int id, void *proc);
|
||||||
|
VBAPI int32_t vbSetRegister(VB *sim, int type, int id, int32_t value);
|
||||||
|
VBAPI int vbSetROM (VB *sim, uint8_t *data, uint32_t size);
|
||||||
|
VBAPI int vbSetSRAM (VB *sim, uint8_t *data, uint32_t size);
|
||||||
|
VBAPI void vbWrite (VB *sim, uint32_t address, int type, int32_t value);
|
||||||
|
VBAPI void vbWriteEx (VB *sim, uint32_t address, uint8_t *buffer, uint32_t length);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* VB_H_ */
|
||||||
@@ -1,705 +0,0 @@
|
|||||||
<!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">• 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">• 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">• 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">• Literal</td>
|
|
||||||
<td>Provided directly by the expression.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="nowrap">• 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">• Signed Word</td>
|
|
||||||
<td>
|
|
||||||
32-bit, two's complement integer.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="nowrap">• Unsigned Word</td>
|
|
||||||
<td>
|
|
||||||
32-bit, unsigned integer.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="nowrap">• Float</td>
|
|
||||||
<td>
|
|
||||||
32-bit floating short in IEEE-754 format.
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="nowrap">• 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">↑</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">↓</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"><<</td>
|
|
||||||
<td class="open">Shift Left</td>
|
|
||||||
<td class="open">Cannot be used with float values.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="mono open">>></td>
|
|
||||||
<td class="open">Shift Right Arithmetic</td>
|
|
||||||
<td class="open">Cannot be used with float values.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="mono">>>></td>
|
|
||||||
<td>Shift Right Logical</td>
|
|
||||||
<td>Cannot be used with float values.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="mono open">></td>
|
|
||||||
<td class="open">Greater</td>
|
|
||||||
<td class="open">Always produces a boolean.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="mono open">>=</td>
|
|
||||||
<td class="open">Greater or Equal</td>
|
|
||||||
<td class="open">Always produces a boolean.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="mono open"><</td>
|
|
||||||
<td class="open">Less</td>
|
|
||||||
<td class="open">Always produces a boolean.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="mono"><=</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">&</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">&&</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">• <code>float</code></td>
|
|
||||||
<td>Behaves like <code>xfloat</code> instead.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="nowrap">• <code>s8</code>, <code>u8</code></td>
|
|
||||||
<td>Performs a byte read of the specified signedness.</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="nowrap">• <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> </h1>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
Before Width: | Height: | Size: 220 B |
@@ -1,588 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 */
|
|
||||||
@@ -1,356 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_ */
|
|
||||||
@@ -1,276 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 */
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
@@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_ */
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_ */
|
|
||||||
@@ -1,588 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 */
|
|
||||||
@@ -1,356 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_ */
|
|
||||||
@@ -1,276 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 */
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
@@ -1,725 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_ */
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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_ */
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
Copyright (C) 2020 Planet Virtual Boy
|
Copyright (C) 2023 Guy Perfect
|
||||||
|
|
||||||
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
|
||||||
|
|||||||
@@ -1,151 +0,0 @@
|
|||||||
# 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
|
|
||||||
}
|
|
||||||
@@ -1,167 +1,61 @@
|
|||||||
# Java include directory pathnames
|
.PHONY: help
|
||||||
# The Windows files need to be copied from a Windows JDK installation
|
help:
|
||||||
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 "Intended build environment: Debian i386 or amd64"
|
@echo "Virtual Boy Emulator - March 12, 2023"
|
||||||
@echo " gcc-multilib"
|
|
||||||
@echo " mingw-w64"
|
|
||||||
@echo " openjdk-15-jdk"
|
|
||||||
@echo
|
@echo
|
||||||
@echo "Usage: make <recipe>"
|
@echo "Target build environment is any Debian with the following packages:"
|
||||||
@echo " Package recipes:"
|
@echo " emscripten"
|
||||||
@echo " build Compiles the native modules and desktop application"
|
@echo " gcc"
|
||||||
@echo " clean Deletes all output files"
|
@echo " openjdk-17-jdk"
|
||||||
@echo " core Check the native core library for style errors"
|
@echo
|
||||||
@echo " desktop Compiles the Java desktop application"
|
@echo "Available make targets:"
|
||||||
@echo " native Builds all native modules"
|
@echo " build Perform all build commands"
|
||||||
@echo " pack Bundles everything in a .jar file"
|
@echo " bundle Package the repository for HTML distribution"
|
||||||
@echo " release Produces a .jar and deletes all intermediate files"
|
@echo " clean Remove output files"
|
||||||
@echo " Native recipes:"
|
@echo " core Check the C core source for compiler warnings"
|
||||||
@echo " lin32 Builds native module linux_x86"
|
@echo " wasm Build the WebAssembly core module"
|
||||||
@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
|
||||||
@make -s desktop
|
@echo " Build WebAssembly core module"
|
||||||
@make -s native
|
@make -s wasm
|
||||||
|
@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_most
|
clean:
|
||||||
@rm -f pvbemu_*.jar
|
@rm -f vbemu_*.html web/core/core.wasm
|
||||||
|
|
||||||
# Check the native core library for style errors
|
|
||||||
.PHONY: core
|
.PHONY: core
|
||||||
core:
|
core:
|
||||||
@echo " Checking native core library for style errors"
|
# GCC generic
|
||||||
$(eval coreargs = -c -Isrc/core/include -Werror -Wall -Wextra -Wpedantic \
|
@gcc core/vb.c -I core -c -o /dev/null \
|
||||||
-fno-strict-aliasing -fsyntax-only src/core/vue.c)
|
-Werror -std=c90 -Wall -Wextra -Wpedantic
|
||||||
@gcc -std=c90 $(coreargs)
|
# GCC compilation control
|
||||||
@gcc -std=c90 -D VUE_BIGENDIAN $(coreargs)
|
@gcc core/vb.c -I core -c -o /dev/null \
|
||||||
@gcc -std=c99 $(coreargs)
|
-Werror -std=c90 -Wall -Wextra -Wpedantic \
|
||||||
@gcc -std=c99 -D VUE_BIGENDIAN $(coreargs)
|
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE
|
||||||
@gcc -std=c11 $(coreargs)
|
# Clang generic
|
||||||
@gcc -std=c11 -D VUE_BIGENDIAN $(coreargs)
|
@emcc core/vb.c -I core -c -o /dev/null \
|
||||||
|
-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
|
||||||
|
|
||||||
# Compile the Java desktop application
|
.PHONY: wasm
|
||||||
.PHONY: desktop
|
wasm:
|
||||||
desktop: clean_desktop
|
@emcc -o web/core/core.wasm web/core/wasm.c core/vb.c -Icore \
|
||||||
@echo " Compiling Java desktop application"
|
-D VB_LITTLE_ENDIAN -D VB_SIGNED_PROPAGATE \
|
||||||
@javac -sourcepath src/desktop --release 10 -Xlint:unchecked \
|
-D "VBAPI=__attribute__((used))" \
|
||||||
-Xlint:deprecation -h src/desktop/vue -d . src/desktop/Main.java
|
--no-entry -O2 -flto -s WASM=1 \
|
||||||
|
-s EXPORTED_RUNTIME_METHODS=[] -s ALLOW_MEMORY_GROWTH \
|
||||||
# Build all native modules
|
-s MAXIMUM_MEMORY=4GB -fno-strict-aliasing
|
||||||
.PHONY: native
|
@rm -f web/core/*.wasm.tmp*
|
||||||
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
|
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
/* 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
|
|
||||||
@@ -1,295 +0,0 @@
|
|||||||
#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__ */
|
|
||||||
@@ -1,298 +0,0 @@
|
|||||||
/* 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
|
|
||||||
@@ -1,458 +0,0 @@
|
|||||||
#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;
|
|
||||||
}
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,174 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,512 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,613 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,612 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,386 +0,0 @@
|
|||||||
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" : ""
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,405 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,477 +0,0 @@
|
|||||||
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"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,521 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,203 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,585 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,249 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,218 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,538 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,395 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
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 "";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
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() { }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,576 +0,0 @@
|
|||||||
// 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
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
package vue;
|
|
||||||
|
|
||||||
// Simulation of the Game Pak
|
|
||||||
class GamePak {
|
|
||||||
|
|
||||||
// Package fields
|
|
||||||
byte[] ram; // Cartridge SRAM
|
|
||||||
byte[] rom; // Cartridge ROM
|
|
||||||
int wcr_exp1w; // Expansion one-wait mode
|
|
||||||
int wcr_rom1w; // ROM one-wait mode
|
|
||||||
|
|
||||||
// Private fields
|
|
||||||
private JavaVue vue; // Emulation state
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructors //
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Default constructor
|
|
||||||
GamePak(JavaVue vue) {
|
|
||||||
this.vue = vue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Package Methods //
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// System reset
|
|
||||||
void reset() {
|
|
||||||
wcr_exp1w = 0;
|
|
||||||
wcr_rom1w = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,228 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,427 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
// 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);
|
|
||||||
}
|
|
||||||
@@ -1,170 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,309 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,266 +0,0 @@
|
|||||||
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) { }
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,691 @@
|
|||||||
|
import { Core } from /**/"./core/Core.js";
|
||||||
|
import { Debugger } from /**/"./debugger/Debugger.js";
|
||||||
|
import { Disassembler } from /**/"./core/Disassembler.js";
|
||||||
|
import { Toolkit } from /**/"./toolkit/Toolkit.js";
|
||||||
|
|
||||||
|
// Front-end emulator application
|
||||||
|
class App extends Toolkit.App {
|
||||||
|
|
||||||
|
///////////////////////// Initialization Methods //////////////////////////
|
||||||
|
|
||||||
|
constructor(bundle) {
|
||||||
|
super({
|
||||||
|
style: {
|
||||||
|
display : "grid",
|
||||||
|
gridTemplateRows: "max-content auto"
|
||||||
|
},
|
||||||
|
visibility: true,
|
||||||
|
visible : false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
this.bundle = bundle;
|
||||||
|
this.debugMode = true;
|
||||||
|
this.dualMode = false;
|
||||||
|
this.text = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
|
||||||
|
// Theme
|
||||||
|
Object.assign(document.body.style, { margin:"0", overflow:"hidden" });
|
||||||
|
this.stylesheet(/**/"web/theme/kiosk.css", false);
|
||||||
|
this.stylesheet(/**/"web/theme/vbemu.css", false);
|
||||||
|
this._theme = "auto";
|
||||||
|
this.themes = {
|
||||||
|
dark : this.stylesheet(/**/"web/theme/dark.css" ),
|
||||||
|
light : this.stylesheet(/**/"web/theme/light.css" ),
|
||||||
|
virtual: this.stylesheet(/**/"web/theme/virtual.css")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Watch for dark mode preference changes
|
||||||
|
this.isDark = window.matchMedia("(prefers-color-scheme: dark)");
|
||||||
|
this.isDark.addEventListener("change", e=>this.onDark());
|
||||||
|
this.onDark();
|
||||||
|
|
||||||
|
// Locales
|
||||||
|
await this.addLocale(/**/"web/locale/en-US.json");
|
||||||
|
for (let id of [].concat(navigator.languages, ["en-US"])) {
|
||||||
|
if (this.setLocale(id))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this.setTitle("{app.title}", true);
|
||||||
|
|
||||||
|
// Element
|
||||||
|
document.body.append(this.element);
|
||||||
|
window.addEventListener("resize", e=>{
|
||||||
|
this.element.style.height = window.innerHeight + "px";
|
||||||
|
this.element.style.width = window.innerWidth + "px";
|
||||||
|
});
|
||||||
|
window.dispatchEvent(new Event("resize"));
|
||||||
|
this.addEventListener("keydown", e=>this.onKeyDown(e));
|
||||||
|
|
||||||
|
// Menus
|
||||||
|
this.menuBar = new Toolkit.MenuBar(this);
|
||||||
|
this.menuBar.setLabel("{menu._}", true);
|
||||||
|
this.add(this.menuBar);
|
||||||
|
this.initFileMenu ();
|
||||||
|
this.initEmulationMenu();
|
||||||
|
this.initDebugMenu (0, this.debugMode);
|
||||||
|
this.initDebugMenu (1, this.debugMode && this.dualMode);
|
||||||
|
this.initThemeMenu ();
|
||||||
|
|
||||||
|
// Fallback for bubbled key events
|
||||||
|
document.body.addEventListener("focusout", e=>this.onBlur(e));
|
||||||
|
window .addEventListener("keydown" , e=>this.onKey (e));
|
||||||
|
window .addEventListener("keyup" , e=>this.onKey (e));
|
||||||
|
|
||||||
|
// Temporary: Faux game mode display
|
||||||
|
this.display = new Toolkit.Component(this, {
|
||||||
|
class : "tk display",
|
||||||
|
style : { position: "relative" },
|
||||||
|
visible: !this.debugMode
|
||||||
|
});
|
||||||
|
this.image1 = new Toolkit.Component(this, { style: {
|
||||||
|
background: "#000000",
|
||||||
|
position : "absolute"
|
||||||
|
}});
|
||||||
|
this.display.add(this.image1);
|
||||||
|
this.image2 = new Toolkit.Component(this, { style: {
|
||||||
|
background: "#000000",
|
||||||
|
position : "absolute"
|
||||||
|
}});
|
||||||
|
this.display.add(this.image2);
|
||||||
|
this.display.addEventListener("resize", e=>this.onDisplay());
|
||||||
|
this.add(this.display);
|
||||||
|
|
||||||
|
// Temporary: Faux debug mode display
|
||||||
|
this.desktop = new Toolkit.Desktop(this, {
|
||||||
|
visible: this.debugMode
|
||||||
|
});
|
||||||
|
this.add(this.desktop);
|
||||||
|
|
||||||
|
// Emulation core
|
||||||
|
this.core = await new Core().init();
|
||||||
|
let sims = (await this.core.create(2)).sims;
|
||||||
|
this.core.onsubscription = (k,m)=>this.onSubscription(k, m);
|
||||||
|
|
||||||
|
// Debugging managers
|
||||||
|
this.dasm = new Disassembler();
|
||||||
|
this.debug = new Array(sims.length);
|
||||||
|
for (let x = 0; x < sims.length; x++) {
|
||||||
|
let dbg = this.debug[x] = new Debugger(this, sims[x], x);
|
||||||
|
if (x == 0 && !this.dualMode) {
|
||||||
|
dbg.cpu .substitute("#", "");
|
||||||
|
dbg.memory.substitute("#", "");
|
||||||
|
}
|
||||||
|
this.desktop.add(dbg.cpu);
|
||||||
|
this.desktop.add(dbg.memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reveal the application
|
||||||
|
this.visible = true;
|
||||||
|
this.restoreFocus();
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"CPU window shortcuts:\n" +
|
||||||
|
" F11 Single step\n" +
|
||||||
|
" F10 Run to next\n" +
|
||||||
|
" Ctrl+B Toggle bytes column\n" +
|
||||||
|
" Ctrl+F Fit columns\n" +
|
||||||
|
" Ctrl+G Goto"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize File menu
|
||||||
|
initFileMenu() {
|
||||||
|
let bar = this.menuBar;
|
||||||
|
let item = bar.file = new Toolkit.MenuItem(this);
|
||||||
|
item.setText("{menu.file._}");
|
||||||
|
bar.add(item);
|
||||||
|
|
||||||
|
let menu = item.menu = new Toolkit.Menu(this);
|
||||||
|
|
||||||
|
item = bar.file.loadROM0 = new Toolkit.MenuItem(this);
|
||||||
|
item.setText("{menu.file.loadROM}");
|
||||||
|
item.substitute("#", this.dualMode ? " 1" : "", false);
|
||||||
|
item.addEventListener("action", e=>this.onLoadROM(0));
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = bar.file.loadROM1 = new Toolkit.MenuItem(this,
|
||||||
|
{ visible: this.dualMode });
|
||||||
|
item.setText("{menu.file.loadROM}");
|
||||||
|
item.substitute("#", " 2", false);
|
||||||
|
item.addEventListener("action", e=>this.onLoadROM(1));
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = bar.file.dualMode = new Toolkit.MenuItem(this,
|
||||||
|
{ checked: this.dualMode, type: "checkbox" });
|
||||||
|
item.setText("{menu.file.dualMode}");
|
||||||
|
item.addEventListener("action", e=>this.onDualMode());
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = bar.file.debugMode = new Toolkit.MenuItem(this,
|
||||||
|
{ checked: this.debugMode, disabled: true, type: "checkbox" });
|
||||||
|
item.setText("{menu.file.debugMode}");
|
||||||
|
item.addEventListener("action", e=>this.onDebugMode());
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
|
item = new Toolkit.MenuItem(this);
|
||||||
|
item.setText("Export source...", false);
|
||||||
|
item.addEventListener("action", ()=>this.bundle.save());
|
||||||
|
menu.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Emulation menu
|
||||||
|
initEmulationMenu() {
|
||||||
|
let bar = this.menuBar;
|
||||||
|
let item = bar.emulation = new Toolkit.MenuItem(this);
|
||||||
|
item.setText("{menu.emulation._}");
|
||||||
|
bar.add(item);
|
||||||
|
|
||||||
|
let menu = item.menu = new Toolkit.Menu(this);
|
||||||
|
|
||||||
|
item = bar.emulation.run =
|
||||||
|
new Toolkit.MenuItem(this, { disabled: true });
|
||||||
|
item.setText("{menu.emulation.run}");
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = bar.emulation.reset0 = new Toolkit.MenuItem(this);
|
||||||
|
item.setText("{menu.emulation.reset}");
|
||||||
|
item.substitute("#", this.dualMode ? " 1" : "", false);
|
||||||
|
item.addEventListener("action", e=>this.onReset(0));
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = bar.emulation.reset1 = new Toolkit.MenuItem(this,
|
||||||
|
{ visible: this.dualMode });
|
||||||
|
item.setText("{menu.emulation.reset}");
|
||||||
|
item.substitute("#", " 2", false);
|
||||||
|
item.addEventListener("action", e=>this.onReset(1));
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = bar.emulation.linkSims = new Toolkit.MenuItem(this,
|
||||||
|
{ disabled: true, type: "checkbox", visible: this.dualMode });
|
||||||
|
item.setText("{menu.emulation.linkSims}");
|
||||||
|
menu.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Debug menu
|
||||||
|
initDebugMenu(index, visible) {
|
||||||
|
let bar = this.menuBar;
|
||||||
|
let item = bar["debug" + index] = new Toolkit.MenuItem(this,
|
||||||
|
{ visible: visible }), top = item;
|
||||||
|
item.setText("{menu.debug._}");
|
||||||
|
item.substitute("#",
|
||||||
|
index == 0 ? this.dualMode ? "1" : "" : " 2", false);
|
||||||
|
bar.add(item);
|
||||||
|
|
||||||
|
let menu = item.menu = new Toolkit.Menu(this);
|
||||||
|
|
||||||
|
item = top.console = new Toolkit.MenuItem(this, { disabled: true });
|
||||||
|
item.setText("{menu.debug.console}");
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = top.memory = new Toolkit.MenuItem(this);
|
||||||
|
item.setText("{menu.debug.memory}");
|
||||||
|
item.addEventListener("action",
|
||||||
|
e=>this.showWindow(this.debug[index].memory));
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = top.cpu = new Toolkit.MenuItem(this);
|
||||||
|
item.setText("{menu.debug.cpu}");
|
||||||
|
item.addEventListener("action",
|
||||||
|
e=>this.showWindow(this.debug[index].cpu));
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item=top.breakpoints = new Toolkit.MenuItem(this, { disabled: true });
|
||||||
|
item.setText("{menu.debug.breakpoints}");
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
menu.addSeparator();
|
||||||
|
|
||||||
|
item = top.palettes = new Toolkit.MenuItem(this, { disabled: true });
|
||||||
|
item.setText("{menu.debug.palettes}");
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = top.characters = new Toolkit.MenuItem(this, { disabled: true });
|
||||||
|
item.setText("{menu.debug.characters}");
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = top.bgMaps = new Toolkit.MenuItem(this, { disabled: true });
|
||||||
|
item.setText("{menu.debug.bgMaps}");
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = top.backgrounds = new Toolkit.MenuItem(this, { disabled:true });
|
||||||
|
item.setText("{menu.debug.backgrounds}");
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = top.objects = new Toolkit.MenuItem(this, { disabled: true });
|
||||||
|
item.setText("{menu.debug.objects}");
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = top.frameBuffers = new Toolkit.MenuItem(this, {disabled: true});
|
||||||
|
item.setText("{menu.debug.frameBuffers}");
|
||||||
|
menu.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Theme menu
|
||||||
|
initThemeMenu() {
|
||||||
|
let bar = this.menuBar;
|
||||||
|
let item = bar.theme = new Toolkit.MenuItem(this);
|
||||||
|
item.setText("{menu.theme._}");
|
||||||
|
bar.add(item);
|
||||||
|
|
||||||
|
let menu = item.menu = new Toolkit.Menu(this);
|
||||||
|
|
||||||
|
item = bar.theme.auto = new Toolkit.MenuItem(this,
|
||||||
|
{ checked: true, type: "checkbox" });
|
||||||
|
item.setText("{menu.theme.auto}");
|
||||||
|
item.theme = "auto";
|
||||||
|
item.addEventListener("action", e=>this.theme = "auto");
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = bar.theme.light = new Toolkit.MenuItem(this,
|
||||||
|
{ checked: false, type: "checkbox" });
|
||||||
|
item.setText("{menu.theme.light}");
|
||||||
|
item.theme = "light";
|
||||||
|
item.addEventListener("action", e=>this.theme = "light");
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = bar.theme.dark = new Toolkit.MenuItem(this,
|
||||||
|
{ checked: false, type: "checkbox" });
|
||||||
|
item.setText("{menu.theme.dark}");
|
||||||
|
item.theme = "dark";
|
||||||
|
item.addEventListener("action", e=>this.theme = "dark");
|
||||||
|
menu.add(item);
|
||||||
|
|
||||||
|
item = bar.theme.light = new Toolkit.MenuItem(this,
|
||||||
|
{ checked: false, type: "checkbox" });
|
||||||
|
item.setText("{menu.theme.virtual}");
|
||||||
|
item.theme = "virtual";
|
||||||
|
item.addEventListener("action", e=>this.theme = "virtual");
|
||||||
|
menu.add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Event Handlers //////////////////////////////
|
||||||
|
|
||||||
|
// All elements have lost focus
|
||||||
|
onBlur(e) {
|
||||||
|
if (
|
||||||
|
e.relatedTarget == null ||
|
||||||
|
e.relatedTarget == document.body
|
||||||
|
) this.restoreFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dark mode preference changed
|
||||||
|
onDark() {
|
||||||
|
if (this._theme != "auto")
|
||||||
|
return;
|
||||||
|
let isDark = this.isDark.matches;
|
||||||
|
this.themes.light.disabled = isDark;
|
||||||
|
this.themes.dark .disabled = !isDark;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Game mode display resized
|
||||||
|
onDisplay() {
|
||||||
|
let bounds = this.display.element.getBoundingClientRect();
|
||||||
|
let width = Math.max(1, bounds.width);
|
||||||
|
let height = Math.max(1, bounds.height);
|
||||||
|
let scale, x1, y1, x2, y2;
|
||||||
|
|
||||||
|
// Single mode
|
||||||
|
if (!this.dualMode) {
|
||||||
|
this.image2.visible = false;
|
||||||
|
scale = Math.max(1, Math.min(
|
||||||
|
Math.floor(width / 384),
|
||||||
|
Math.floor(height / 224)
|
||||||
|
));
|
||||||
|
x1 = Math.max(0, Math.floor((width - 384 * scale) / 2));
|
||||||
|
y1 = Math.max(0, Math.floor((height - 224 * scale) / 2));
|
||||||
|
x2 = y2 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dual mode
|
||||||
|
else {
|
||||||
|
this.image2.visible = true;
|
||||||
|
|
||||||
|
// Horizontal orientation
|
||||||
|
if (true) {
|
||||||
|
scale = Math.max(1, Math.min(
|
||||||
|
Math.floor(width / 768),
|
||||||
|
Math.floor(height / 224)
|
||||||
|
));
|
||||||
|
let gap = Math.max(0, width - 768 * scale);
|
||||||
|
gap = gap < 0 ? 0 : Math.floor(gap / 3) + (gap%3==2 ? 1 : 0);
|
||||||
|
x1 = gap;
|
||||||
|
x2 = Math.max(384 * scale, width - 384 * scale - gap);
|
||||||
|
y1 = y2 = Math.max(0, Math.floor((height - 224 * scale) / 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical orientation
|
||||||
|
else {
|
||||||
|
scale = Math.max(1, Math.min(
|
||||||
|
Math.floor(width / 384),
|
||||||
|
Math.floor(height / 448)
|
||||||
|
));
|
||||||
|
let gap = Math.max(0, height - 448 * scale);
|
||||||
|
gap = gap < 0 ? 0 : Math.floor(gap / 3) + (gap%3==2 ? 1 : 0);
|
||||||
|
x1 = x2 = Math.max(0, Math.floor((width - 384 * scale) / 2));
|
||||||
|
y1 = gap;
|
||||||
|
y2 = Math.max(224 * scale, height - 224 * scale - gap);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
width = 384 * scale + "px";
|
||||||
|
height = 224 * scale + "px";
|
||||||
|
Object.assign(this.image1.element.style,
|
||||||
|
{ left: x1+"px", top: y1+"px", width: width, height: height });
|
||||||
|
Object.assign(this.image2.element.style,
|
||||||
|
{ left: x2+"px", top: y2+"px", width: width, height: height });
|
||||||
|
}
|
||||||
|
|
||||||
|
// File -> Debug mode
|
||||||
|
onDebugMode() {
|
||||||
|
this.debugMode =!this.debugMode;
|
||||||
|
this.display.visible =!this.debugMode;
|
||||||
|
this.desktop.visible = this.debugMode;
|
||||||
|
this.configMenus();
|
||||||
|
this.onDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emulation -> Dual mode
|
||||||
|
onDualMode() {
|
||||||
|
this.setDualMode(!this.dualMode);
|
||||||
|
this.configMenus();
|
||||||
|
this.onDisplay();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key press
|
||||||
|
onKeyDown(e) {
|
||||||
|
|
||||||
|
// Take no action
|
||||||
|
if (!e.altKey || e.key != "F10" ||
|
||||||
|
this.menuBar.contains(document.activeElement))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Move focus into the menu bar
|
||||||
|
this.menuBar.focus();
|
||||||
|
Toolkit.handle(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// File -> Load ROM
|
||||||
|
async onLoadROM(index) {
|
||||||
|
|
||||||
|
// Add a file picker to the document
|
||||||
|
let file = document.createElement("input");
|
||||||
|
file.type = "file";
|
||||||
|
file.style.position = "absolute";
|
||||||
|
file.style.visibility = "hidden";
|
||||||
|
document.body.appendChild(file);
|
||||||
|
|
||||||
|
// Prompt the user to select a file
|
||||||
|
await new Promise(resolve=>{
|
||||||
|
file.addEventListener("input", resolve);
|
||||||
|
file.click();
|
||||||
|
});
|
||||||
|
file.remove();
|
||||||
|
|
||||||
|
// No file was selected
|
||||||
|
file = file.files[0];
|
||||||
|
if (!file)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Load the file
|
||||||
|
let data = null;
|
||||||
|
try { data = new Uint8Array(await file.arrayBuffer()); }
|
||||||
|
catch {
|
||||||
|
alert(this.localize("{menu.file.loadROMError}"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to process the file as an ISX binary
|
||||||
|
try { data = Debugger.isx(data).toROM(); } catch { }
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (
|
||||||
|
data.length < 1024 ||
|
||||||
|
data.length > 0x1000000 ||
|
||||||
|
(data.length & data.length - 1)
|
||||||
|
) {
|
||||||
|
alert(this.localize("{menu.file.loadROMInvalid}"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load the ROM into simulation memory
|
||||||
|
let rep = await this.core.setROM(this.debug[index].sim, data,
|
||||||
|
{ refresh: true });
|
||||||
|
if (!rep.success) {
|
||||||
|
alert(this.localize("{menu.file.loadROMError}"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.debug[index].followPC(0xFFFFFFF0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emulation -> Reset
|
||||||
|
async onReset(index) {
|
||||||
|
await this.core.reset(this.debug[index].sim, { refresh: true });
|
||||||
|
this.debug[index].followPC(0xFFFFFFF0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Core subscription
|
||||||
|
onSubscription(key, msg) {
|
||||||
|
let target = this.debug; // Handler object
|
||||||
|
for (let x = 1; x < key.length - 1; x++)
|
||||||
|
target = target[key[x]];
|
||||||
|
target[key[key.length - 1]].call(target, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Public Methods //////////////////////////////
|
||||||
|
|
||||||
|
// Specify document title
|
||||||
|
setTitle(title, localize) {
|
||||||
|
this.setString("text", title, localize);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify the color theme
|
||||||
|
get theme() { return this._theme; }
|
||||||
|
set theme(theme) {
|
||||||
|
switch (theme) {
|
||||||
|
case "light": case "dark": case "virtual":
|
||||||
|
this._theme = theme;
|
||||||
|
for (let entry of Object.entries(this.themes))
|
||||||
|
entry[1].disabled = entry[0] != theme;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this._theme = "auto";
|
||||||
|
this.themes["virtual"].disabled = true;
|
||||||
|
this.onDark();
|
||||||
|
}
|
||||||
|
for (let item of this.menuBar.theme.menu.children)
|
||||||
|
item.checked = item.theme == theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Package Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Configure components for automatic localization, or localize a message
|
||||||
|
localize(a, b) {
|
||||||
|
|
||||||
|
// Default behavior
|
||||||
|
if (a && a != this)
|
||||||
|
return super.localize(a, b);
|
||||||
|
|
||||||
|
// Update localization strings
|
||||||
|
if (this.text != null) {
|
||||||
|
let text = this.text;
|
||||||
|
document.title = !text[1] ? text[0] :
|
||||||
|
this.localize(text[0], this.substitutions);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return focus to the most recent focused element
|
||||||
|
restoreFocus() {
|
||||||
|
|
||||||
|
// Focus was successfully restored
|
||||||
|
if (super.restoreFocus())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Select the foremost visible window
|
||||||
|
let wnd = this.desktop.getActiveWindow();
|
||||||
|
if (wnd) {
|
||||||
|
wnd.focus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select the menu bar
|
||||||
|
this.menuBar.focus();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a Run Next command on one of the simulations
|
||||||
|
runToNext(index, options) {
|
||||||
|
let debugs = [ this.debug[index] ];
|
||||||
|
if (this.dualMode)
|
||||||
|
debugs.push(this.debug[index ^ 1]);
|
||||||
|
|
||||||
|
let ret = this.core.runToNext(debugs.map(d=>d.sim), options);
|
||||||
|
|
||||||
|
if (ret instanceof Promise) ret.then(msg=>{
|
||||||
|
for (let x = 0; x < debugs.length; x++)
|
||||||
|
debugs[x].followPC(msg.pcs[x]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform a Single Step command on one of the simulations
|
||||||
|
singleStep(index, options) {
|
||||||
|
let debugs = [ this.debug[index] ];
|
||||||
|
if (this.dualMode)
|
||||||
|
debugs.push(this.debug[index ^ 1]);
|
||||||
|
|
||||||
|
let ret = this.core.singleStep(debugs.map(d=>d.sim), options);
|
||||||
|
|
||||||
|
if (ret instanceof Promise) ret.then(msg=>{
|
||||||
|
for (let x = 0; x < debugs.length; x++)
|
||||||
|
debugs[x].followPC(msg.pcs[x]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Private Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Configure menu item visibility
|
||||||
|
configMenus() {
|
||||||
|
let bar = this.menuBar;
|
||||||
|
bar.file.debugMode .checked = this.debugMode;
|
||||||
|
bar.file.loadROM1 .visible = this.dualMode;
|
||||||
|
bar.emulation.reset1 .visible = this.dualMode;
|
||||||
|
bar.emulation.linkSims.visible = this.dualMode;
|
||||||
|
bar.debug0 .visible = this.debugMode;
|
||||||
|
bar.debug1 .visible = this.debugMode && this.dualMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify whether dual mode is active
|
||||||
|
setDualMode(dualMode) {
|
||||||
|
|
||||||
|
// Update state
|
||||||
|
if (dualMode == this.dualMode)
|
||||||
|
return;
|
||||||
|
this.dualMode = dualMode;
|
||||||
|
|
||||||
|
// Working variables
|
||||||
|
let index = dualMode ? " 1" : "";
|
||||||
|
|
||||||
|
// Update menus
|
||||||
|
let bar = this.menuBar;
|
||||||
|
bar.file.loadROM0 .substitute("#", index, false);
|
||||||
|
bar.debug0 .substitute("#", index, false);
|
||||||
|
bar.emulation.reset0.substitute("#", index, false);
|
||||||
|
bar.file.dualMode .checked = this.dualMode;
|
||||||
|
|
||||||
|
// Update sim 1 debug windows
|
||||||
|
let dbg = this.debug[0];
|
||||||
|
dbg.cpu .substitute("#", index);
|
||||||
|
dbg.memory.substitute("#", index);
|
||||||
|
|
||||||
|
// Re-show any sim 2 debug windows that were previously visible
|
||||||
|
if (dualMode) {
|
||||||
|
for (let wnd of this.desktop.children) {
|
||||||
|
if (wnd.index == 1 && wnd.wasVisible)
|
||||||
|
wnd.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide any visible sim 2 debug windows
|
||||||
|
else for (let wnd of this.desktop.children) {
|
||||||
|
if (wnd.index == 0)
|
||||||
|
continue;
|
||||||
|
wnd.wasVisible = wnd.visible;
|
||||||
|
wnd.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure a debugger window is visible
|
||||||
|
showWindow(wnd) {
|
||||||
|
let adjust = false;
|
||||||
|
|
||||||
|
// The window is already visible
|
||||||
|
if (wnd.visible) {
|
||||||
|
|
||||||
|
// The window is already in the foreground
|
||||||
|
if (wnd == this.desktop.getActiveWindow())
|
||||||
|
;//adjust = true;
|
||||||
|
|
||||||
|
// Bring the window to the foreground
|
||||||
|
else this.desktop.bringToFront(wnd);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// The window is not visible
|
||||||
|
else {
|
||||||
|
adjust = !wnd.shown;
|
||||||
|
if (adjust && wnd.firstShow)
|
||||||
|
wnd.firstShow();
|
||||||
|
wnd.visible = true;
|
||||||
|
this.desktop.bringToFront(wnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust the window position
|
||||||
|
if (adjust) {
|
||||||
|
let bounds = this.desktop.element.getBoundingClientRect();
|
||||||
|
wnd.left = Math.max(0, (bounds.width - wnd.outerWidth ) / 2);
|
||||||
|
wnd.top = Math.max(0, (bounds.height - wnd.outerHeight) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer focus into the window
|
||||||
|
wnd.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install a stylesheet. Returns the resulting <link> element
|
||||||
|
stylesheet(filename, disabled = true) {
|
||||||
|
let ret = document.createElement("link");
|
||||||
|
ret.href = filename;
|
||||||
|
ret.rel = "stylesheet";
|
||||||
|
ret.type = "text/css";
|
||||||
|
ret.disabled = disabled;
|
||||||
|
document.head.append(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Program Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Program entry point
|
||||||
|
static main(bundle) {
|
||||||
|
new App(bundle).init();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { App };
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
import java.awt.image.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.nio.charset.*;
|
||||||
|
import java.util.*;
|
||||||
|
import javax.imageio.*;
|
||||||
|
|
||||||
|
public class Bundle {
|
||||||
|
|
||||||
|
/////////////////////////////// BundledFile ///////////////////////////////
|
||||||
|
|
||||||
|
// Individual packaged resource file
|
||||||
|
static class BundledFile implements Comparable<BundledFile> {
|
||||||
|
|
||||||
|
// Instance fields
|
||||||
|
byte[] data; // File data loaded from disk
|
||||||
|
File file; // Source file
|
||||||
|
String filename; // Logical filename
|
||||||
|
|
||||||
|
// Constructor
|
||||||
|
BundledFile(BundledFile parent, File file) {
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
this.file = file;
|
||||||
|
filename = parent == null ? "" : parent.filename + file.getName();
|
||||||
|
|
||||||
|
// Load file data if file
|
||||||
|
if (file.isFile()) {
|
||||||
|
try (var stream = new FileInputStream(file)) {
|
||||||
|
data = stream.readAllBytes();
|
||||||
|
} catch (Exception e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update filename if directory
|
||||||
|
else if (parent != null) filename += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparator
|
||||||
|
public int compareTo(BundledFile o) {
|
||||||
|
return
|
||||||
|
filename.equals("web/_boot.js") ? -1 :
|
||||||
|
o.filename.equals("web/_boot.js") ? +1 :
|
||||||
|
filename.compareTo(o.filename)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce a list of child files or directories
|
||||||
|
BundledFile[] listFiles(String name, boolean isDirectory) {
|
||||||
|
|
||||||
|
// Produce a filtered list of files
|
||||||
|
var files = this.file.listFiles(f->{
|
||||||
|
|
||||||
|
// Does not satisfy the directory requirement
|
||||||
|
if (f.isDirectory() != isDirectory)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Current directory is not root
|
||||||
|
if (!filename.equals(""))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Filter specific files from being bundled
|
||||||
|
String filename = f.getName();
|
||||||
|
return !(
|
||||||
|
filename.startsWith(".git" ) ||
|
||||||
|
filename.startsWith(name + "_") &&
|
||||||
|
filename.endsWith (".html" )
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Process all files for bundling
|
||||||
|
var ret = new BundledFile[files.length];
|
||||||
|
for (int x = 0; x < files.length; x++)
|
||||||
|
ret[x] = new BundledFile(this, files[x]);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Program Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Program entry point
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String name = name(args[0]);
|
||||||
|
var files = listFiles(args[0]);
|
||||||
|
var prepend = prepend(name, files);
|
||||||
|
var bundle = bundle(prepend, files);
|
||||||
|
var image = image(bundle);
|
||||||
|
var url = url(image);
|
||||||
|
patch(name, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce a buffer of the bundled files
|
||||||
|
static byte[] bundle(byte[] prepend, BundledFile[] files) {
|
||||||
|
try (var stream = new ByteArrayOutputStream()) {
|
||||||
|
stream.write(prepend);
|
||||||
|
stream.write(files[0].data); // web/_boot.js
|
||||||
|
stream.write(0);
|
||||||
|
for (int x = 1; x < files.length; x++)
|
||||||
|
stream.write(files[x].data);
|
||||||
|
return stream.toByteArray();
|
||||||
|
} catch (Exception e) { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a bundle buffer into a PNG-encoded image buffer
|
||||||
|
static byte[] image(byte[] bundle) {
|
||||||
|
int width = (int) Math.ceil(Math.sqrt(bundle.length));
|
||||||
|
int height = (bundle.length + width - 1) / width;
|
||||||
|
var pixels = new int[width * height];
|
||||||
|
|
||||||
|
// Encode the buffer as a pixel array
|
||||||
|
for (int x = 0; x < bundle.length; x++) {
|
||||||
|
int b = bundle[x] & 0xFF;
|
||||||
|
pixels[x] = 0xFF000000 | b << 16 | b << 8 | b;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce an image using the pixels
|
||||||
|
var image = new BufferedImage(
|
||||||
|
width, height, BufferedImage.TYPE_INT_RGB);
|
||||||
|
image.setRGB(0, 0, width, height, pixels, 0, width);
|
||||||
|
|
||||||
|
// Encode the image as a PNG buffer
|
||||||
|
try (var stream = new ByteArrayOutputStream()) {
|
||||||
|
ImageIO.write(image, "png", stream);
|
||||||
|
return stream.toByteArray();
|
||||||
|
} catch (Exception e) { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// List all files
|
||||||
|
static BundledFile[] listFiles(String name) {
|
||||||
|
var dirs = new ArrayList<BundledFile>();
|
||||||
|
var files = new ArrayList<BundledFile>();
|
||||||
|
|
||||||
|
// Propagate down the file system tree
|
||||||
|
dirs.add(new BundledFile(null, new File(".")));
|
||||||
|
while (!dirs.isEmpty()) {
|
||||||
|
var dir = dirs.remove(0);
|
||||||
|
for (var sub : dir.listFiles(name, true ))
|
||||||
|
dirs.add(sub );
|
||||||
|
for (var file : dir.listFiles(name, false))
|
||||||
|
files.add(file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the set of files as a sorted array
|
||||||
|
Collections.sort(files);
|
||||||
|
return files.toArray(new BundledFile[files.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a filename for the bundle
|
||||||
|
static String name(String name) {
|
||||||
|
var calendar = Calendar.getInstance();
|
||||||
|
return String.format("%s_%04d%02d%02d",
|
||||||
|
name,
|
||||||
|
calendar.get(Calendar.YEAR),
|
||||||
|
calendar.get(Calendar.MONTH) + 1,
|
||||||
|
calendar.get(Calendar.DAY_OF_MONTH)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce the output HTML from the template
|
||||||
|
static void patch(String name, String url) {
|
||||||
|
String markup = null;
|
||||||
|
try (var stream = new FileInputStream("web/template.html")) {
|
||||||
|
markup = new String(stream.readAllBytes(), StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) { }
|
||||||
|
markup = markup.replace("src=\"\"", "src=\"" + url + "\"");
|
||||||
|
try (var stream = new FileOutputStream(name + ".html")) {
|
||||||
|
stream.write(markup.getBytes(StandardCharsets.UTF_8));
|
||||||
|
} catch (Exception e) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce source data to prepend to web/_boot.js
|
||||||
|
static byte[] prepend(String name, BundledFile[] files) {
|
||||||
|
var ret = new StringBuilder();
|
||||||
|
|
||||||
|
// Arguments
|
||||||
|
ret.append("let " +
|
||||||
|
"buffer=arguments[0]," +
|
||||||
|
"image=arguments[1]," +
|
||||||
|
"name=\"" + name + "\"" +
|
||||||
|
";");
|
||||||
|
|
||||||
|
// Bundle manifest
|
||||||
|
ret.append("let manifest=[");
|
||||||
|
for (var file : files) {
|
||||||
|
if (file != files[0])
|
||||||
|
ret.append(",");
|
||||||
|
ret.append("[\"" +
|
||||||
|
file.filename + "\", " + file.data.length + "]");
|
||||||
|
}
|
||||||
|
ret.append("];");
|
||||||
|
|
||||||
|
// Convert to byte array
|
||||||
|
return ret.toString().getBytes(StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert an image buffer to a data URL
|
||||||
|
static String url(byte[] image) {
|
||||||
|
return "data:image/png;base64," +
|
||||||
|
Base64.getMimeEncoder(0, new byte[0]).encodeToString(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,310 @@
|
|||||||
|
// Running as an async function
|
||||||
|
// Prepended by Bundle.java: buffer, image, manifest, name
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Bundle //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Resource manager for bundled files
|
||||||
|
class Bundle extends Array {
|
||||||
|
|
||||||
|
//////////////////////////////// Constants ////////////////////////////////
|
||||||
|
|
||||||
|
// .ZIP support
|
||||||
|
static CRC_LOOKUP = new Uint32Array(256);
|
||||||
|
|
||||||
|
// Text processing
|
||||||
|
static DECODER = new TextDecoder();
|
||||||
|
static ENCODER = new TextEncoder();
|
||||||
|
|
||||||
|
static initializer() {
|
||||||
|
|
||||||
|
// Generate the CRC32 lookup table
|
||||||
|
for (let x = 0; x <= 255; x++) {
|
||||||
|
let l = x;
|
||||||
|
for (let j = 7; j >= 0; j--)
|
||||||
|
l = ((l >>> 1) ^ (0xEDB88320 & -(l & 1)));
|
||||||
|
this.CRC_LOOKUP[x] = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////// Initialization Methods //////////////////////////
|
||||||
|
|
||||||
|
constructor(name, url, settings, isDebug) {
|
||||||
|
super();
|
||||||
|
this.isDebug = isDebug;
|
||||||
|
this.name = name;
|
||||||
|
this.settings = settings;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Public Methods //////////////////////////////
|
||||||
|
|
||||||
|
// Export all bundled resources to a .ZIP file
|
||||||
|
save() {
|
||||||
|
let centrals = new Array(this.length);
|
||||||
|
let locals = new Array(this.length);
|
||||||
|
let offset = 0;
|
||||||
|
let size = 0;
|
||||||
|
|
||||||
|
// Encode file and directory entries
|
||||||
|
for (let x = 0; x < this.length; x++) {
|
||||||
|
let file = this[x];
|
||||||
|
let sum = Bundle.crc32(file.data);
|
||||||
|
locals [x] = file.toZipHeader(sum);
|
||||||
|
centrals[x] = file.toZipHeader(sum, offset);
|
||||||
|
offset += locals [x].length;
|
||||||
|
size += centrals[x].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode end of central directory
|
||||||
|
let end = [];
|
||||||
|
Bundle.writeInt(end, 4, 0x06054B50); // Signature
|
||||||
|
Bundle.writeInt(end, 2, 0); // Disk number
|
||||||
|
Bundle.writeInt(end, 2, 0); // Central dir start disk
|
||||||
|
Bundle.writeInt(end, 2, this.length); // # central dir this disk
|
||||||
|
Bundle.writeInt(end, 2, this.length); // # central dir total
|
||||||
|
Bundle.writeInt(end, 4, size); // Size of central dir
|
||||||
|
Bundle.writeInt(end, 4, offset); // Offset of central dir
|
||||||
|
Bundle.writeInt(end, 2, 0); // .ZIP comment length
|
||||||
|
|
||||||
|
// Prompt the user to save the resulting file
|
||||||
|
let a = document.createElement("a");
|
||||||
|
a.download = this.name + ".zip";
|
||||||
|
a.href = URL.createObjectURL(new Blob(
|
||||||
|
locals.concat(centrals).concat([Uint8Array.from(end)]),
|
||||||
|
{ type: "application/zip" }
|
||||||
|
));
|
||||||
|
a.style.visibility = "hidden";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Package Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Add a BundledFile to the collection
|
||||||
|
add(file) {
|
||||||
|
file.bundle = this;
|
||||||
|
this.push(this[file.name] = file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a byte array into an output buffer
|
||||||
|
static writeBytes(data, bytes) {
|
||||||
|
for (let b of bytes)
|
||||||
|
data.push(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write an integer into an output buffer
|
||||||
|
static writeInt(data, size, value) {
|
||||||
|
for (; size > 0; size--, value >>= 8)
|
||||||
|
data.push(value & 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write a string of text as bytes into an output buffer
|
||||||
|
static writeString(data, text) {
|
||||||
|
this.writeBytes(data, this.ENCODER.encode(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Private Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Calculate the CRC32 checksum for a byte array
|
||||||
|
static crc32(data) {
|
||||||
|
let c = 0xFFFFFFFF;
|
||||||
|
for (let x = 0; x < data.length; x++)
|
||||||
|
c = ((c >>> 8) ^ this.CRC_LOOKUP[(c ^ data[x]) & 0xFF]);
|
||||||
|
return ~c & 0xFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Bundle.initializer();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// BundledFile //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Individual file in the bundled data
|
||||||
|
class BundledFile {
|
||||||
|
|
||||||
|
//////////////////////////////// Constants ////////////////////////////////
|
||||||
|
|
||||||
|
// MIME types
|
||||||
|
static MIMES = {
|
||||||
|
".css" : "text/css;charset=UTF-8",
|
||||||
|
".frag" : "text/plain;charset=UTF-8",
|
||||||
|
".js" : "text/javascript;charset=UTF-8",
|
||||||
|
".json" : "application/json;charset=UTF-8",
|
||||||
|
".png" : "image/png",
|
||||||
|
".svg" : "image/svg+xml;charset=UTF-8",
|
||||||
|
".txt" : "text/plain;charset=UTF-8",
|
||||||
|
".vert" : "text/plain;charset=UTF-8",
|
||||||
|
".wasm" : "application/wasm",
|
||||||
|
".woff2": "font/woff2"
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////// Initialization Methods //////////////////////////
|
||||||
|
|
||||||
|
constructor(name, buffer, offset, length) {
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
this.data = buffer.slice(offset, offset + length);
|
||||||
|
this.name = name;
|
||||||
|
|
||||||
|
// Resolve the MIME type
|
||||||
|
let index = name.lastIndexOf(".");
|
||||||
|
this.mime = index != -1 && BundledFile.MIMES[name.substring(index)] ||
|
||||||
|
"application/octet-stream";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Public Methods //////////////////////////////
|
||||||
|
|
||||||
|
// Represent the file with a blob URL
|
||||||
|
toBlobURL() {
|
||||||
|
return this.blobURL || (this.blobURL = URL.createObjectURL(
|
||||||
|
new Blob([ this.data ], { type: this.mime })));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode the file data as a data URL
|
||||||
|
toDataURL() {
|
||||||
|
return "data:" + this.mime + ";base64," + btoa(
|
||||||
|
Array.from(this.data).map(b=>String.fromCharCode(b)).join(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-process URLs in a bundled file's contents
|
||||||
|
toProcURL(asDataURL = false) {
|
||||||
|
|
||||||
|
// The URL has already been computed
|
||||||
|
if (this.url)
|
||||||
|
return this.url;
|
||||||
|
|
||||||
|
// Working variables
|
||||||
|
let content = this.toString();
|
||||||
|
let pattern = /\/\*\*?\*\//g;
|
||||||
|
let parts = content.split(pattern);
|
||||||
|
let ret = [ parts.shift() ];
|
||||||
|
|
||||||
|
// Process all URLs prefixed with /**/ or /***/
|
||||||
|
for (let part of parts) {
|
||||||
|
let start = part.indexOf("\"");
|
||||||
|
let end = part.indexOf("\"", start + 1);
|
||||||
|
let filename = part.substring(start + 1, end);
|
||||||
|
let asData = pattern.exec(content)[0] == "/***/";
|
||||||
|
|
||||||
|
// Relative to current file
|
||||||
|
if (filename.startsWith(".")) {
|
||||||
|
let path = this.name.split("/");
|
||||||
|
path.pop(); // Current filename
|
||||||
|
|
||||||
|
// Navigate to the path of the target file
|
||||||
|
for (let dir of filename.split("/")) {
|
||||||
|
switch (dir) {
|
||||||
|
case "..": path.pop(); // Fallthrough
|
||||||
|
case "." : break;
|
||||||
|
default : path.push(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce the fully-qualified filename
|
||||||
|
filename = path.join("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the file as a data URL
|
||||||
|
let file = this.bundle[filename];
|
||||||
|
ret.push(
|
||||||
|
part.substring(0, start + 1),
|
||||||
|
file[
|
||||||
|
file.mime.startsWith("text/javascript") ||
|
||||||
|
file.mime.startsWith("text/css") ?
|
||||||
|
"toProcURL" : asData ? "toDataURL" : "toBlobURL"
|
||||||
|
](asData),
|
||||||
|
part.substring(end)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Represent the transformed source as a URL
|
||||||
|
return this.url = asDataURL ?
|
||||||
|
"data:" + this.mime + ";base64," + btoa(ret.join("")) :
|
||||||
|
URL.createObjectURL(new Blob(ret, { type: this.mime }))
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decode the file data as a UTF-8 string
|
||||||
|
toString() {
|
||||||
|
return Bundle.DECODER.decode(this.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Package Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Produce a .ZIP header for export
|
||||||
|
toZipHeader(crc32, offset) {
|
||||||
|
let central = offset !== undefined;
|
||||||
|
let ret = [];
|
||||||
|
if (central) {
|
||||||
|
Bundle.writeInt (ret, 4, 0x02014B50); // Signature
|
||||||
|
Bundle.writeInt (ret, 2, 20); // Version created by
|
||||||
|
} else
|
||||||
|
Bundle.writeInt (ret, 4, 0x04034B50); // Signature
|
||||||
|
Bundle.writeInt (ret, 2, 20); // Version required
|
||||||
|
Bundle.writeInt (ret, 2, 0); // Bit flags
|
||||||
|
Bundle.writeInt (ret, 2, 0); // Compression method
|
||||||
|
Bundle.writeInt (ret, 2, 0); // Modified time
|
||||||
|
Bundle.writeInt (ret, 2, 0); // Modified date
|
||||||
|
Bundle.writeInt (ret, 4, crc32); // Checksum
|
||||||
|
Bundle.writeInt (ret, 4, this.data.length); // Compressed size
|
||||||
|
Bundle.writeInt (ret, 4, this.data.length); // Uncompressed size
|
||||||
|
Bundle.writeInt (ret, 2, this.name.length); // Filename length
|
||||||
|
Bundle.writeInt (ret, 2, 0); // Extra field length
|
||||||
|
if (central) {
|
||||||
|
Bundle.writeInt (ret, 2, 0); // File comment length
|
||||||
|
Bundle.writeInt (ret, 2, 0); // Disk number start
|
||||||
|
Bundle.writeInt (ret, 2, 0); // Internal attributes
|
||||||
|
Bundle.writeInt (ret, 4, 0); // External attributes
|
||||||
|
Bundle.writeInt (ret, 4, offset); // Relative offset
|
||||||
|
}
|
||||||
|
Bundle.writeString (ret, this.name); // Filename
|
||||||
|
if (!central)
|
||||||
|
Bundle.writeBytes(ret, this.data); // File data
|
||||||
|
return Uint8Array.from(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Boot Program //
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Produce the application bundle
|
||||||
|
let bundle = new Bundle(name, image.src, image.getAttribute("settings") || "",
|
||||||
|
location.protocol != "file:" && location.hash == "#debug");
|
||||||
|
for (let x=0,offset=buffer.indexOf(0)-manifest[0][1]; x<manifest.length; x++) {
|
||||||
|
let entry = manifest[x];
|
||||||
|
bundle.add(new BundledFile(entry[0], buffer, offset, entry[1]));
|
||||||
|
offset += entry[1] + (x == 0 ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Begin program operations
|
||||||
|
(await import(bundle.isDebug ?
|
||||||
|
"./web/App.js" :
|
||||||
|
bundle["web/App.js"].toProcURL()
|
||||||
|
)).App.main(bundle);
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Dedicated audio output thread
|
||||||
|
class AudioThread extends AudioWorkletProcessor {
|
||||||
|
|
||||||
|
///////////////////////// Initialization Methods //////////////////////////
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
this.buffers = []; // Input sample buffer queue
|
||||||
|
this.offset = 0; // Offset into oldest buffer
|
||||||
|
|
||||||
|
// Wait for initializer message from parent thread
|
||||||
|
this.port.onmessage = m=>this.init(m.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(main) {
|
||||||
|
|
||||||
|
// Configure message ports
|
||||||
|
this.core = this.port;
|
||||||
|
this.core.onmessage = m=>this.onCore(m.data);
|
||||||
|
this.main = main;
|
||||||
|
this.main.onmessage = m=>this.onMain(m.data);
|
||||||
|
|
||||||
|
// Notify main thread
|
||||||
|
this.port.postMessage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Public Methods //////////////////////////////
|
||||||
|
|
||||||
|
// Produce output samples (called by the user agent)
|
||||||
|
process(inputs, outputs, parameters) {
|
||||||
|
let output = outputs[0];
|
||||||
|
let length = output [0].length;
|
||||||
|
let empty = null;
|
||||||
|
|
||||||
|
// Process all samples
|
||||||
|
for (let x = 0; x < length;) {
|
||||||
|
|
||||||
|
// No bufferfed samples are available
|
||||||
|
if (this.buffers.length == 0) {
|
||||||
|
for (; x < length; x++)
|
||||||
|
output[0] = output[1] = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer samples from the oldest buffer
|
||||||
|
let y, buffer = this.buffers[0];
|
||||||
|
for (y = this.offset; x < length && y < buffer.length; x++, y+=2) {
|
||||||
|
output[0][x] = buffer[y ];
|
||||||
|
output[1][x] = buffer[y + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to the next buffer
|
||||||
|
if ((this.offset = y) == buffer.length) {
|
||||||
|
if (empty == null)
|
||||||
|
empty = [];
|
||||||
|
empty.push(this.buffers.shift());
|
||||||
|
this.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return emptied sample buffers to the core thread
|
||||||
|
if (empty != null)
|
||||||
|
this.core.postMessage(empty, empty.map(e=>e.buffer));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Message Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Message received from core thread
|
||||||
|
onCore(msg) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message received from main thread
|
||||||
|
onMain(msg) {
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
registerProcessor("AudioThread", AudioThread);
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
// Interface between application and WebAssembly worker thread
|
||||||
|
class Core {
|
||||||
|
|
||||||
|
//////////////////////////////// Constants ////////////////////////////////
|
||||||
|
|
||||||
|
/* Register types */
|
||||||
|
static VB_PROGRAM = 0;
|
||||||
|
static VB_SYSTEM = 1;
|
||||||
|
static VB_OTHER = 2;
|
||||||
|
|
||||||
|
/* System registers */
|
||||||
|
static VB_ADTRE = 25;
|
||||||
|
static VB_CHCW = 24;
|
||||||
|
static VB_ECR = 4;
|
||||||
|
static VB_EIPC = 0;
|
||||||
|
static VB_EIPSW = 1;
|
||||||
|
static VB_FEPC = 2;
|
||||||
|
static VB_FEPSW = 3;
|
||||||
|
static VB_PIR = 6;
|
||||||
|
static VB_PSW = 5;
|
||||||
|
static VB_TKCW = 7;
|
||||||
|
|
||||||
|
/* Other registers */
|
||||||
|
static VB_PC = 0;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////// Initialization Methods //////////////////////////
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
this.promises = [];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(coreUrl, wasmUrl, audioUrl) {
|
||||||
|
|
||||||
|
// Open audio output stream
|
||||||
|
this.audio = new AudioContext({
|
||||||
|
latencyHint: "interactive",
|
||||||
|
sampleRate : 41700
|
||||||
|
});
|
||||||
|
await this.audio.suspend();
|
||||||
|
|
||||||
|
// Launch the audio thread
|
||||||
|
await this.audio.audioWorklet.addModule(
|
||||||
|
Core.url(audioUrl, "AudioThread.js", /***/"./AudioThread.js"));
|
||||||
|
let node = new AudioWorkletNode(this.audio, "AudioThread", {
|
||||||
|
numberOfInputs : 0,
|
||||||
|
numberOfOutputs : 1,
|
||||||
|
outputChannelCount: [2]
|
||||||
|
});
|
||||||
|
node.connect(this.audio.destination);
|
||||||
|
|
||||||
|
// Attach a second MessagePort to the audio thread
|
||||||
|
let channel = new MessageChannel();
|
||||||
|
this.audio.port = channel.port1;
|
||||||
|
await new Promise(resolve=>{
|
||||||
|
node.port.onmessage = resolve;
|
||||||
|
node.port.postMessage(channel.port2, [channel.port2]);
|
||||||
|
});
|
||||||
|
this.audio.port.onmessage = m=>this.onAudio(m.data);
|
||||||
|
|
||||||
|
// Launch the core thread
|
||||||
|
this.core = new Worker(
|
||||||
|
Core.url(wasmUrl, "CoreThread.js", /***/"./CoreThread.js"));
|
||||||
|
await new Promise(resolve=>{
|
||||||
|
this.core.onmessage = resolve;
|
||||||
|
this.core.postMessage({
|
||||||
|
audio : node.port,
|
||||||
|
wasmUrl: Core.url(wasmUrl, "core.wasm", /***/"./core.wasm")
|
||||||
|
}, [node.port]);
|
||||||
|
});
|
||||||
|
this.core.onmessage = m=>this.onCore(m.data);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Static Methods //////////////////////////////
|
||||||
|
|
||||||
|
// Select a URL in the same path as the current script
|
||||||
|
static url(arg, name, bundled) {
|
||||||
|
|
||||||
|
// The input argument was provided
|
||||||
|
if (arg)
|
||||||
|
return arg;
|
||||||
|
|
||||||
|
// Running from a bundle distribution
|
||||||
|
if (bundled.startsWith("blob:") || bundled.startsWith("data:"))
|
||||||
|
return bundled;
|
||||||
|
|
||||||
|
// Compute the URL for the given filename
|
||||||
|
let url = new URL(import.meta.url).pathname;
|
||||||
|
return url.substring(0, url.lastIndexOf("/") + 1) + name;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Event Handlers //////////////////////////////
|
||||||
|
|
||||||
|
// Message received from audio thread
|
||||||
|
onAudio(msg) {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message received from core thread
|
||||||
|
onCore(msg) {
|
||||||
|
|
||||||
|
// Process subscriptions
|
||||||
|
if (msg.subscriptions && this.onsubscription instanceof Function) {
|
||||||
|
for (let sub of msg.subscriptions) {
|
||||||
|
let key = sub.subscription;
|
||||||
|
delete sub.subscription;
|
||||||
|
this.onsubscription(key, sub, this);
|
||||||
|
}
|
||||||
|
delete msg.subscriptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The main thread is waiting on a reply
|
||||||
|
if (msg.isReply) {
|
||||||
|
delete msg.isReply;
|
||||||
|
|
||||||
|
// For "create", produce sim objects
|
||||||
|
if (msg.isCreate) {
|
||||||
|
delete msg.isCreate;
|
||||||
|
msg.sims = msg.sims.map(s=>({ pointer: s }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify the caller
|
||||||
|
this.promises.shift()(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Public Methods //////////////////////////////
|
||||||
|
|
||||||
|
// Create and initialize simulations
|
||||||
|
create(count, options) {
|
||||||
|
return this.message({
|
||||||
|
command: "create",
|
||||||
|
count : count
|
||||||
|
}, [], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a simulation
|
||||||
|
delete(sim, options) {
|
||||||
|
return this.message({
|
||||||
|
command: "delete",
|
||||||
|
sim : sim.pointer
|
||||||
|
}, [], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the value of all CPU registers
|
||||||
|
getAllRegisters(sim, options) {
|
||||||
|
return this.message({
|
||||||
|
command: "getAllRegisters",
|
||||||
|
sim : sim.pointer
|
||||||
|
}, [], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the value of a register
|
||||||
|
getRegister(sim, type, id, options) {
|
||||||
|
return this.message({
|
||||||
|
command: "getRegister",
|
||||||
|
id : id,
|
||||||
|
sim : sim.pointer,
|
||||||
|
type : type
|
||||||
|
}, [], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read multiple bytes from memory
|
||||||
|
read(sim, address, length, options) {
|
||||||
|
return this.message({
|
||||||
|
command: "read",
|
||||||
|
address: address,
|
||||||
|
length : length,
|
||||||
|
sim : sim.pointer
|
||||||
|
}, [], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh subscriptions
|
||||||
|
refresh(subscriptions = null, options) {
|
||||||
|
return this.message({
|
||||||
|
command : "refresh",
|
||||||
|
subscriptions: subscriptions
|
||||||
|
}, [], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate a hardware reset
|
||||||
|
reset(sim, options) {
|
||||||
|
return this.message({
|
||||||
|
command: "reset",
|
||||||
|
sim : sim.pointer
|
||||||
|
}, [], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute until the next current instruction
|
||||||
|
runToNext(sims, options) {
|
||||||
|
return this.message({
|
||||||
|
command: "runToNext",
|
||||||
|
sims : Array.isArray(sims) ?
|
||||||
|
sims.map(s=>s.pointer) : [ sims.pointer ]
|
||||||
|
}, [], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify a value for a register
|
||||||
|
setRegister(sim, type, id, value, options) {
|
||||||
|
return this.message({
|
||||||
|
command: "setRegister",
|
||||||
|
id : id,
|
||||||
|
sim : sim.pointer,
|
||||||
|
type : type,
|
||||||
|
value : value
|
||||||
|
}, [], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify a cartridge ROM buffer
|
||||||
|
setROM(sim, data, options = {}) {
|
||||||
|
data = data.slice();
|
||||||
|
return this.message({
|
||||||
|
command: "setROM",
|
||||||
|
data : data,
|
||||||
|
reset : !("reset" in options) || !!options.reset,
|
||||||
|
sim : sim.pointer
|
||||||
|
}, [data.buffer], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the current instruction
|
||||||
|
singleStep(sims, options) {
|
||||||
|
return this.message({
|
||||||
|
command: "singleStep",
|
||||||
|
sims : Array.isArray(sims) ?
|
||||||
|
sims.map(s=>s.pointer) : [ sims.pointer ]
|
||||||
|
}, [], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cancel a subscription
|
||||||
|
unsubscribe(subscription, options) {
|
||||||
|
return this.message({
|
||||||
|
command : "unsubscribe",
|
||||||
|
subscription: subscription
|
||||||
|
}, [], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write multiple bytes to memory
|
||||||
|
write(sim, address, data, options) {
|
||||||
|
data = data.slice();
|
||||||
|
return this.message({
|
||||||
|
address: address,
|
||||||
|
command: "write",
|
||||||
|
data : data,
|
||||||
|
sim : sim.pointer
|
||||||
|
}, [data.buffer], options);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Private Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Send a message to the core thread
|
||||||
|
message(msg, transfers, options = {}) {
|
||||||
|
|
||||||
|
// Configure options
|
||||||
|
if (!(options instanceof Object))
|
||||||
|
options = { reply: options };
|
||||||
|
if (!("reply" in options) || options.reply)
|
||||||
|
msg.reply = true;
|
||||||
|
if ("refresh" in options)
|
||||||
|
msg.refresh = options.refresh;
|
||||||
|
if ("subscription" in options)
|
||||||
|
msg.subscription = options.subscription;
|
||||||
|
if ("tag" in options)
|
||||||
|
msg.tag = options.tag;
|
||||||
|
|
||||||
|
// Send the command to the core thread
|
||||||
|
return msg.reply ?
|
||||||
|
new Promise(resolve=>{
|
||||||
|
this.promises.push(resolve);
|
||||||
|
this.core.postMessage(msg, transfers);
|
||||||
|
}) :
|
||||||
|
this.core.postMessage(msg, transfers);
|
||||||
|
;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Core };
|
||||||
@@ -0,0 +1,338 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
/* Register types */
|
||||||
|
const VB_PROGRAM = 0;
|
||||||
|
const VB_SYSTEM = 1;
|
||||||
|
const VB_OTHER = 2;
|
||||||
|
|
||||||
|
/* System registers */
|
||||||
|
const VB_ADTRE = 25;
|
||||||
|
const VB_CHCW = 24;
|
||||||
|
const VB_ECR = 4;
|
||||||
|
const VB_EIPC = 0;
|
||||||
|
const VB_EIPSW = 1;
|
||||||
|
const VB_FEPC = 2;
|
||||||
|
const VB_FEPSW = 3;
|
||||||
|
const VB_PIR = 6;
|
||||||
|
const VB_PSW = 5;
|
||||||
|
const VB_TKCW = 7;
|
||||||
|
|
||||||
|
/* Other registers */
|
||||||
|
const VB_PC = 0;
|
||||||
|
|
||||||
|
// Dedicated emulation thread
|
||||||
|
class CoreThread {
|
||||||
|
|
||||||
|
///////////////////////// Initialization Methods //////////////////////////
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
this.subscriptions = new Map();
|
||||||
|
|
||||||
|
// Wait for initializer message from parent thread
|
||||||
|
onmessage = m=>this.init(m.data.audio, m.data.wasmUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
async init(audio, wasmUrl) {
|
||||||
|
|
||||||
|
// Configure message ports
|
||||||
|
this.audio = audio;
|
||||||
|
this.audio.onmessage = m=>this.onAudio (m.data);
|
||||||
|
this.main = globalThis;
|
||||||
|
this.main .onmessage = m=>this.onMessage(m.data);
|
||||||
|
|
||||||
|
// Load and instantiate the WebAssembly module
|
||||||
|
this.wasm = (await WebAssembly.instantiateStreaming(
|
||||||
|
fetch(wasmUrl), {
|
||||||
|
env: { emscripten_notify_memory_growth: ()=>this.onGrowth() }
|
||||||
|
})).instance;
|
||||||
|
this.onGrowth();
|
||||||
|
this.pointerSize = this.PointerSize();
|
||||||
|
this.pointerType = this.pointerSize == 8 ? Uint64Array : Uint32Array;
|
||||||
|
|
||||||
|
// Notify main thread
|
||||||
|
this.main.postMessage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Event Handlers //////////////////////////////
|
||||||
|
|
||||||
|
// Message received from audio thread
|
||||||
|
onAudio(frames) {
|
||||||
|
|
||||||
|
// Audio processing was suspended
|
||||||
|
if (frames == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for more frames
|
||||||
|
this.audio.postMessage(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emscripten has grown the linear memory
|
||||||
|
onGrowth() {
|
||||||
|
Object.assign(this, this.wasm.exports);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Message received from main thread
|
||||||
|
onMessage(msg) {
|
||||||
|
|
||||||
|
// Subscribe to the command
|
||||||
|
if (msg.subscription && msg.command != "refresh")
|
||||||
|
this.subscriptions.set(CoreThread.key(msg.subscription), msg);
|
||||||
|
|
||||||
|
// Process the command
|
||||||
|
let rep = this[msg.command](msg);
|
||||||
|
|
||||||
|
// Do not send a reply
|
||||||
|
if (!msg.reply)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Configure the reply
|
||||||
|
if (!rep)
|
||||||
|
rep = {};
|
||||||
|
if (msg.reply)
|
||||||
|
rep.isReply = true;
|
||||||
|
if ("tag" in msg)
|
||||||
|
rep.tag = msg.tag;
|
||||||
|
|
||||||
|
// Send the reply to the main thread
|
||||||
|
let transfers = rep.transfers;
|
||||||
|
if (transfers)
|
||||||
|
delete rep.transfers;
|
||||||
|
this.main.postMessage(rep, transfers || []);
|
||||||
|
|
||||||
|
// Refresh subscriptions
|
||||||
|
if (msg.refresh && msg.command != "refresh") {
|
||||||
|
let subs = {};
|
||||||
|
if (Array.isArray(msg.refresh))
|
||||||
|
subs.subscriptions = msg.refresh;
|
||||||
|
this.refresh(subs);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////// Commands /////////////////////////////////
|
||||||
|
|
||||||
|
// Create and initialize a new simulation
|
||||||
|
create(msg) {
|
||||||
|
let sims = new Array(msg.count);
|
||||||
|
for (let x = 0; x < msg.count; x++)
|
||||||
|
sims[x] = this.Create();
|
||||||
|
return {
|
||||||
|
isCreate: true,
|
||||||
|
sims : sims
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all memory used by a simulation
|
||||||
|
delete(msg) {
|
||||||
|
this.Delete(msg.sim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the values of all CPU registers
|
||||||
|
getAllRegisters(msg) {
|
||||||
|
let program = new Int32Array (32);
|
||||||
|
let system = new Uint32Array(32);
|
||||||
|
for (let x = 0; x < 32; x++) {
|
||||||
|
program[x] = this.vbGetRegister(msg.sim, 0, x);
|
||||||
|
system [x] = this.vbGetRegister(msg.sim, 1, x);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
pc : this.vbGetRegister(msg.sim, 2, 0) >>> 0,
|
||||||
|
program : program,
|
||||||
|
system : system,
|
||||||
|
transfers: [ program.buffer, system.buffer ]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the value of a register
|
||||||
|
getRegister(msg) {
|
||||||
|
let value = this.vbGetRegister(msg.sim, msg.type, msg.id);
|
||||||
|
if (msg.type != VB_PROGRAM)
|
||||||
|
value >>>= 0;
|
||||||
|
return { value: value };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read multiple bytes from memory
|
||||||
|
read(msg) {
|
||||||
|
let buffer = this.malloc(msg.length);
|
||||||
|
this.vbReadEx(msg.sim, msg.address, buffer.pointer, msg.length);
|
||||||
|
let data = buffer.slice();
|
||||||
|
this.free(buffer);
|
||||||
|
return {
|
||||||
|
address : msg.address,
|
||||||
|
data : data,
|
||||||
|
transfers: [data.buffer]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process subscriptions
|
||||||
|
refresh(msg) {
|
||||||
|
let subscriptions = [];
|
||||||
|
let transfers = [];
|
||||||
|
|
||||||
|
// Select the key set to refresh
|
||||||
|
let keys = Array.isArray(msg.subscriptions) ?
|
||||||
|
msg.subscriptions.map(s=>CoreThread.key(s)) :
|
||||||
|
this.subscriptions.keys()
|
||||||
|
;
|
||||||
|
|
||||||
|
// Process all subscriptions
|
||||||
|
for (let key of keys) {
|
||||||
|
|
||||||
|
// Process the subscription
|
||||||
|
let sub = this.subscriptions.get(key);
|
||||||
|
let rep = this[sub.command](sub);
|
||||||
|
|
||||||
|
// There is no result
|
||||||
|
if (!rep)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Add the result to the response
|
||||||
|
rep.subscription = sub.subscription;
|
||||||
|
if ("tag" in sub)
|
||||||
|
rep.tag = sub.tag;
|
||||||
|
subscriptions.push(rep);
|
||||||
|
|
||||||
|
// Add the transfers to the response
|
||||||
|
if (!rep.transfers)
|
||||||
|
continue;
|
||||||
|
transfers = transfers.concat(rep.transfers);
|
||||||
|
delete rep.transfers;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the response to the main thread
|
||||||
|
if (subscriptions.length == 0)
|
||||||
|
return;
|
||||||
|
this.main.postMessage({
|
||||||
|
subscriptions: subscriptions.sort(CoreThread.REFRESH_ORDER)
|
||||||
|
}, transfers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate a hardware reset
|
||||||
|
reset(msg) {
|
||||||
|
this.vbReset(msg.sim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute until the next current instruction
|
||||||
|
runToNext(msg) {
|
||||||
|
let sims = this.malloc(msg.sims.length, true);
|
||||||
|
for (let x = 0; x < msg.sims.length; x++)
|
||||||
|
sims[x] = msg.sims[x];
|
||||||
|
this.RunToNext(sims.pointer, msg.sims.length);
|
||||||
|
this.free(sims);
|
||||||
|
|
||||||
|
let pcs = new Array(msg.sims.length);
|
||||||
|
for (let x = 0; x < msg.sims.length; x++)
|
||||||
|
pcs[x] = this.vbGetRegister(msg.sims[x], 2, 0) >>> 0;
|
||||||
|
|
||||||
|
return { pcs: pcs };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify a value for a register
|
||||||
|
setRegister(msg) {
|
||||||
|
let value = this.vbSetRegister(msg.sim, msg.type, msg.id, msg.value);
|
||||||
|
if (msg.type != VB_PROGRAM)
|
||||||
|
value >>>= 0;
|
||||||
|
return { value: value };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify a cartridge ROM buffer
|
||||||
|
setROM(msg) {
|
||||||
|
let prev = this.vbGetROM(msg.sim, 0);
|
||||||
|
let success = true;
|
||||||
|
|
||||||
|
// Specify a new ROM
|
||||||
|
if (msg.data != null) {
|
||||||
|
let data = this.malloc(msg.data.length);
|
||||||
|
for (let x = 0; x < data.length; x++)
|
||||||
|
data[x] = msg.data[x];
|
||||||
|
success = !this.vbSetROM(msg.sim, data.pointer, data.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Operation was successful
|
||||||
|
if (success) {
|
||||||
|
|
||||||
|
// Delete the previous ROM
|
||||||
|
this.Free(prev);
|
||||||
|
|
||||||
|
// Reset the simulation
|
||||||
|
if (msg.reset)
|
||||||
|
this.vbReset(msg.sim);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: success };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the current instruction
|
||||||
|
singleStep(msg) {
|
||||||
|
let sims = this.malloc(msg.sims.length, true);
|
||||||
|
for (let x = 0; x < msg.sims.length; x++)
|
||||||
|
sims[x] = msg.sims[x];
|
||||||
|
this.SingleStep(sims.pointer, msg.sims.length);
|
||||||
|
this.free(sims);
|
||||||
|
|
||||||
|
let pcs = new Array(msg.sims.length);
|
||||||
|
for (let x = 0; x < msg.sims.length; x++)
|
||||||
|
pcs[x] = this.vbGetRegister(msg.sims[x], 2, 0) >>> 0;
|
||||||
|
|
||||||
|
return { pcs: pcs };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete a subscription
|
||||||
|
unsubscribe(msg) {
|
||||||
|
this.subscriptions.delete(CoreThread.key(msg.subscription));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write multiple bytes to memory
|
||||||
|
write(msg) {
|
||||||
|
let data = this.malloc(msg.data.length);
|
||||||
|
for (let x = 0; x < data.length; x++)
|
||||||
|
data[x] = msg.data[x];
|
||||||
|
this.vbWriteEx(msg.sim, msg.address, data.pointer, data.length);
|
||||||
|
this.free(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Private Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Delete a byte array in WebAssembly memory
|
||||||
|
free(buffer) {
|
||||||
|
this.Free(buffer.pointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a subscription key as a string
|
||||||
|
static key(subscription) {
|
||||||
|
return subscription.map(k=>k.toString()).join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allocate a byte array in WebAssembly memory
|
||||||
|
malloc(length, pointers = false) {
|
||||||
|
let size = pointers ? length * this.pointerSize : length;
|
||||||
|
return this.map(this.Malloc(size), length, pointers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map a typed array into WebAssembly memory
|
||||||
|
map(address, length, pointers = false) {
|
||||||
|
let ret = new (pointers ? this.pointerType : Uint8Array)
|
||||||
|
(this.memory.buffer, address, length);
|
||||||
|
ret.pointer = address;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comparator for subscriptions within the refresh command
|
||||||
|
static REFRESH_ORDER(a, b) {
|
||||||
|
a = a.subscription[0];
|
||||||
|
b = b.subscription[0];
|
||||||
|
return a < b ? -1 : a > b ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
new CoreThread();
|
||||||
@@ -0,0 +1,546 @@
|
|||||||
|
// Machine code to human readable text converter
|
||||||
|
class Disassembler {
|
||||||
|
|
||||||
|
//////////////////////////////// Constants ////////////////////////////////
|
||||||
|
|
||||||
|
// Default settings
|
||||||
|
static DEFAULTS = {
|
||||||
|
condCL : "L", // Use C/NC or L/NL for conditions
|
||||||
|
condEZ : "E", // Use E/NE or Z/NZ for conditions
|
||||||
|
condNames : true, // Use condition names
|
||||||
|
condUppercase: false, // Condition names uppercase
|
||||||
|
hexPrefix : "0x", // Hexadecimal prefix
|
||||||
|
hexSuffix : "", // Hexadecimal suffix
|
||||||
|
hexUppercase : true, // Hexadecimal uppercase
|
||||||
|
instUppercase: true, // Mnemonics uppercase
|
||||||
|
jumpAddress : true, // Jump/branch shows target address
|
||||||
|
memInside : false, // Use [reg1 + disp] notation
|
||||||
|
opDestFirst : false, // Destination operand first
|
||||||
|
proNames : true, // Use program register names
|
||||||
|
proUppercase : false, // Program register names uppercase
|
||||||
|
splitBcond : false, // BCOND condition as an operand
|
||||||
|
splitSetf : true, // SETF condition as an operand
|
||||||
|
sysNames : true, // Use system register names
|
||||||
|
sysUppercase : false // System register names uppercase
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////// Disassembly Lookup ////////////////////////////
|
||||||
|
|
||||||
|
// Opcode descriptors
|
||||||
|
static OPDEFS = [
|
||||||
|
[ "MOV" , [ "opReg1" , "opReg2" ] ], // 000000
|
||||||
|
[ "ADD" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "SUB" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "CMP" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "SHL" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "SHR" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "JMP" , [ "opReg1Ind" ] ],
|
||||||
|
[ "SAR" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "MUL" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "DIV" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "MULU" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "DIVU" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "OR" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "AND" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "XOR" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "NOT" , [ "opReg1" , "opReg2" ] ],
|
||||||
|
[ "MOV" , [ "opImm5S", "opReg2" ] ], // 010000
|
||||||
|
[ "ADD" , [ "opImm5S", "opReg2" ] ],
|
||||||
|
null, // SETF: special
|
||||||
|
[ "CMP" , [ "opImm5S", "opReg2" ] ],
|
||||||
|
[ "SHL" , [ "opImm5U", "opReg2" ] ],
|
||||||
|
[ "SHR" , [ "opImm5U", "opReg2" ] ],
|
||||||
|
[ "CLI" , [ ] ],
|
||||||
|
[ "SAR" , [ "opImm5U", "opReg2" ] ],
|
||||||
|
[ "TRAP" , [ "opImm5U" ] ],
|
||||||
|
[ "RETI" , [ ] ],
|
||||||
|
[ "HALT" , [ ] ],
|
||||||
|
null, // Invalid
|
||||||
|
[ "LDSR" , [ "opReg2" , "opSys" ] ],
|
||||||
|
[ "STSR" , [ "opSys" , "opReg2" ] ],
|
||||||
|
[ "SEI" , [ ] ],
|
||||||
|
null, // Bit string: special
|
||||||
|
null, // BCOND: special // 100000
|
||||||
|
null, // BCOND: special
|
||||||
|
null, // BCOND: special
|
||||||
|
null, // BCOND: special
|
||||||
|
null, // BCOND: special
|
||||||
|
null, // BCOND: special
|
||||||
|
null, // BCOND: special
|
||||||
|
null, // BCOND: special
|
||||||
|
[ "MOVEA", [ "opImm16U" , "opReg1", "opReg2" ] ],
|
||||||
|
[ "ADDI" , [ "opImm16S" , "opReg1", "opReg2" ] ],
|
||||||
|
[ "JR" , [ "opDisp26" ] ],
|
||||||
|
[ "JAL" , [ "opDisp26" ] ],
|
||||||
|
[ "ORI" , [ "opImm16U" , "opReg1", "opReg2" ] ],
|
||||||
|
[ "ANDI" , [ "opImm16U" , "opReg1", "opReg2" ] ],
|
||||||
|
[ "XORI" , [ "opImm16U" , "opReg1", "opReg2" ] ],
|
||||||
|
[ "MOVHI", [ "opImm16U" , "opReg1", "opReg2" ] ],
|
||||||
|
[ "LD.B" , [ "opReg1Disp", "opReg2" ] ], // 110000
|
||||||
|
[ "LD.H" , [ "opReg1Disp", "opReg2" ] ],
|
||||||
|
null, // Invalid
|
||||||
|
[ "LD.W" , [ "opReg1Disp", "opReg2" ] ],
|
||||||
|
[ "ST.B" , [ "opReg2" , "opReg1Disp" ] ],
|
||||||
|
[ "ST.H" , [ "opReg2" , "opReg1Disp" ] ],
|
||||||
|
null, // Invalid
|
||||||
|
[ "ST.W" , [ "opReg2" , "opReg1Disp" ] ],
|
||||||
|
[ "IN.B" , [ "opReg1Disp", "opReg2" ] ],
|
||||||
|
[ "IN.H" , [ "opReg1Disp", "opReg2" ] ],
|
||||||
|
[ "CAXI" , [ "opReg1Disp", "opReg2" ] ],
|
||||||
|
[ "IN.W" , [ "opReg1Disp", "opReg2" ] ],
|
||||||
|
[ "OUT.B", [ "opReg2" , "opReg1Disp" ] ],
|
||||||
|
[ "OUT.H", [ "opReg2" , "opReg1Disp" ] ],
|
||||||
|
null, // Floating-point/Nintendo: special
|
||||||
|
[ "OUT.W", [ "opReg2" , "opReg1Disp" ] ]
|
||||||
|
];
|
||||||
|
|
||||||
|
// Bit string sub-opcode descriptors
|
||||||
|
static BITSTRING = [
|
||||||
|
"SCH0BSU", "SCH0BSD", "SCH1BSU", "SCH1BSD",
|
||||||
|
null , null , null , null ,
|
||||||
|
"ORBSU" , "ANDBSU" , "XORBSU" , "MOVBSU" ,
|
||||||
|
"ORNBSU" , "ANDNBSU", "XORNBSU", "NOTBSU" ,
|
||||||
|
null , null , null , null ,
|
||||||
|
null , null , null , null ,
|
||||||
|
null , null , null , null ,
|
||||||
|
null , null , null , null
|
||||||
|
];
|
||||||
|
|
||||||
|
// Floating-point/Nintendo sub-opcode descriptors
|
||||||
|
static FLOATENDO = [
|
||||||
|
[ "CMPF.S" , [ "opReg1", "opReg2" ] ],
|
||||||
|
null, // Invalid
|
||||||
|
[ "CVT.WS" , [ "opReg1", "opReg2" ] ],
|
||||||
|
[ "CVT.SW" , [ "opReg1", "opReg2" ] ],
|
||||||
|
[ "ADDF.S" , [ "opReg1", "opReg2" ] ],
|
||||||
|
[ "SUBF.S" , [ "opReg1", "opReg2" ] ],
|
||||||
|
[ "MULF.S" , [ "opReg1", "opReg2" ] ],
|
||||||
|
[ "DIVF.S" , [ "opReg1", "opReg2" ] ],
|
||||||
|
[ "XB" , [ "opReg2" ] ],
|
||||||
|
[ "XH" , [ "opReg2" ] ],
|
||||||
|
[ "REV" , [ "opReg1", "opReg2" ] ],
|
||||||
|
[ "TRNC.SW", [ "opReg1", "opReg2" ] ],
|
||||||
|
[ "MPYHW" , [ "opReg1", "opReg2" ] ],
|
||||||
|
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, 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, null
|
||||||
|
];
|
||||||
|
|
||||||
|
// Condition mnemonics
|
||||||
|
static CONDITIONS = [
|
||||||
|
"V" , "C" , "E" , "NH", "N", "T", "LT", "LE",
|
||||||
|
"NV", "NC", "NE", "H" , "P", "F", "GE", "GT"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Program register names
|
||||||
|
static REG_PROGRAM = [
|
||||||
|
"r0" , "r1" , "hp" , "sp" , "gp" , "tp" , "r6" , "r7" ,
|
||||||
|
"r8" , "r9" , "r10", "r11", "r12", "r13", "r14", "r15",
|
||||||
|
"r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23",
|
||||||
|
"r24", "r25", "r26", "r27", "r28", "r29", "r30", "lp"
|
||||||
|
];
|
||||||
|
|
||||||
|
// System register names
|
||||||
|
static REG_SYSTEM = [
|
||||||
|
"EIPC", "EIPSW", "FEPC", "FEPSW", "ECR", "PSW", "PIR", "TKCW",
|
||||||
|
"8" , "9" , "10" , "11" , "12" , "13" , "14" , "15" ,
|
||||||
|
"16" , "17" , "18" , "19" , "20" , "21" , "22" , "23" ,
|
||||||
|
"CHCW", "ADTRE", "26" , "27" , "28" , "29" , "30" , "31"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Other register names
|
||||||
|
static REG_OTHER = [ "PC", "PSW" ];
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Static Methods //////////////////////////////
|
||||||
|
|
||||||
|
// Determine the bounds of a data buffer to represent all lines of output
|
||||||
|
static dataBounds(address, line, length) {
|
||||||
|
let before = 10; // Number of lines before the first line of output
|
||||||
|
let max = 4; // Maximum number of bytes that can appear on a line
|
||||||
|
|
||||||
|
// The reference line is before the preferred earliest line
|
||||||
|
if (line < -before) {
|
||||||
|
length = (length - line) * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The reference line is before the first line
|
||||||
|
else if (line < 0) {
|
||||||
|
address -= (line + before) * max;
|
||||||
|
length = (length + before) * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The reference line is at or after the first line
|
||||||
|
else {
|
||||||
|
address -= (line + before) * max;
|
||||||
|
length = (Math.max(length, line) + before) * max;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
address: (address & ~1) >>> 0,
|
||||||
|
length : length
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////// Initialization Methods //////////////////////////
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
Object.assign(this, Disassembler.DEFAULTS);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Public Methods //////////////////////////////
|
||||||
|
|
||||||
|
// Disassemble a region of memory
|
||||||
|
disassemble(data, dataAddress, refAddress, refLine, length, pc = null) {
|
||||||
|
let pcOffset = pc === null ? -1 : pc - dataAddress >>> 0;
|
||||||
|
|
||||||
|
// Locate the offset of the first line of output in the buffer
|
||||||
|
let offset = 0;
|
||||||
|
for (let
|
||||||
|
addr = dataAddress,
|
||||||
|
circle = refLine > 0 ? new Array(refLine) : null,
|
||||||
|
index = 0,
|
||||||
|
more = [],
|
||||||
|
remain = null
|
||||||
|
;;) {
|
||||||
|
|
||||||
|
// Determine the size of the current line
|
||||||
|
if (more.length == 0)
|
||||||
|
this.more(more, data, offset);
|
||||||
|
let size = more.shift();
|
||||||
|
|
||||||
|
// The current line contains the reference address
|
||||||
|
if (refAddress - addr >>> 0 < size) {
|
||||||
|
|
||||||
|
// The next item in the buffer is the first line of output
|
||||||
|
if (refLine > 0) {
|
||||||
|
offset = circle[index];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This line is the first line of output
|
||||||
|
if (refLine == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Count more lines for the first line of output
|
||||||
|
remain = refLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record the offset of the current instruction
|
||||||
|
if (refLine > 0) {
|
||||||
|
circle[index] = offset;
|
||||||
|
index = (index + 1) % circle.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to the next line
|
||||||
|
let sizeToPC = pcOffset - offset >>> 0;
|
||||||
|
if (offset != pcOffset && sizeToPC < size) {
|
||||||
|
size = sizeToPC;
|
||||||
|
more.splice();
|
||||||
|
}
|
||||||
|
addr = addr + size >>> 0;
|
||||||
|
offset += size;
|
||||||
|
if (remain !== null && ++remain == 0)
|
||||||
|
break; // The next line is the first line of output
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all lines of output
|
||||||
|
let lines = new Array(length);
|
||||||
|
for (let
|
||||||
|
addr = dataAddress + offset,
|
||||||
|
more = [],
|
||||||
|
x = 0;
|
||||||
|
x < length; x++
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Determine the size of the current line
|
||||||
|
if (more.length == 0)
|
||||||
|
this.more(more, data, offset, pcOffset);
|
||||||
|
let size = more.shift();
|
||||||
|
|
||||||
|
// Add the line to the response
|
||||||
|
lines[x] = this.format({
|
||||||
|
rawAddress: addr,
|
||||||
|
rawBytes : data.slice(offset, offset + size)
|
||||||
|
});
|
||||||
|
|
||||||
|
// Advance to the next line
|
||||||
|
let sizeToPC = pcOffset - offset >>> 0;
|
||||||
|
if (offset != pcOffset && sizeToPC < size) {
|
||||||
|
size = sizeToPC;
|
||||||
|
more.splice();
|
||||||
|
}
|
||||||
|
addr = addr + size >>> 0;
|
||||||
|
offset += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////// Formatting Methods ////////////////////////////
|
||||||
|
|
||||||
|
// Format a line as human-readable text
|
||||||
|
format(line) {
|
||||||
|
let canReverse = true;
|
||||||
|
let opcode = line.rawBytes[1] >>> 2;
|
||||||
|
let opdef;
|
||||||
|
let code = [
|
||||||
|
line.rawBytes[1] << 8 | line.rawBytes[0],
|
||||||
|
line.rawBytes.length == 2 ? null :
|
||||||
|
line.rawBytes[3] << 8 | line.rawBytes[2]
|
||||||
|
];
|
||||||
|
|
||||||
|
// BCOND
|
||||||
|
if ((opcode & 0b111000) == 0b100000) {
|
||||||
|
let cond = code[0] >>> 9 & 15;
|
||||||
|
opdef =
|
||||||
|
cond == 13 ? [ "NOP", [ ] ] :
|
||||||
|
this.splitBcond ? [ "BCOND", [ "opBCond", "opDisp9" ] ] :
|
||||||
|
[
|
||||||
|
cond == 5 ? "BR" : "B" + this.condition(cond, true),
|
||||||
|
[ "opDisp9" ]
|
||||||
|
]
|
||||||
|
;
|
||||||
|
canReverse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing by opcode
|
||||||
|
else switch (opcode) {
|
||||||
|
|
||||||
|
// SETF
|
||||||
|
case 0b010010:
|
||||||
|
opdef = !this.splitSetf ?
|
||||||
|
[
|
||||||
|
"SETF" + Disassembler.CONDITIONS[code[0] & 15],
|
||||||
|
[ "opReg2" ]
|
||||||
|
] :
|
||||||
|
[ "SETF", [ "opCond", "opReg2" ] ]
|
||||||
|
;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Bit string
|
||||||
|
case 0b011111:
|
||||||
|
opdef = Disassembler.BITSTRING[code[0] & 31];
|
||||||
|
if (opdef != null)
|
||||||
|
opdef = [ opdef, [] ];
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Floating-point/Nintendo
|
||||||
|
case 0b111110:
|
||||||
|
opdef = Disassembler.FLOATENDO[code[1] >>> 10];
|
||||||
|
break;
|
||||||
|
|
||||||
|
// All others
|
||||||
|
default: opdef = Disassembler.OPDEFS[opcode];
|
||||||
|
}
|
||||||
|
|
||||||
|
// The opcode is undefined
|
||||||
|
if (opdef == null)
|
||||||
|
opdef = [ "---", [] ];
|
||||||
|
|
||||||
|
// Format the line's display text
|
||||||
|
line.address = this.hex(line.rawAddress, 8, false);
|
||||||
|
line.bytes = new Array(line.rawBytes.length);
|
||||||
|
line.mnemonic = this.instUppercase ? opdef[0] : opdef[0].toLowerCase();
|
||||||
|
line.operands = new Array(opdef[1].length);
|
||||||
|
for (let x = 0; x < line.bytes.length; x++)
|
||||||
|
line.bytes[x] = this.hex(line.rawBytes[x], 2, false);
|
||||||
|
for (let x = 0; x < line.operands.length; x++)
|
||||||
|
line.operands[x] = this[opdef[1][x]](line, code);
|
||||||
|
if (this.opDestFirst && canReverse)
|
||||||
|
line.operands.reverse();
|
||||||
|
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a condition operand in a BCOND instruction
|
||||||
|
opBCond(line, code) {
|
||||||
|
return this.condition(code[0] >>> 9 & 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a condition operand in a SETF instruction
|
||||||
|
opCond(line, code) {
|
||||||
|
return this.condition(code[0] & 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a 9-bit displacement operand
|
||||||
|
opDisp9(line, code) {
|
||||||
|
let disp = code[0] << 23 >> 23;
|
||||||
|
return this.jump(line.rawAddress, disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a 26-bit displacement operand
|
||||||
|
opDisp26(line, code) {
|
||||||
|
let disp = (code[0] << 16 | code[1]) << 6 >> 6;
|
||||||
|
return this.jump(line.rawAddress, disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a 5-bit signed immediate operand
|
||||||
|
opImm5S(line, code) {
|
||||||
|
return (code[0] & 31) << 27 >> 27;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a 5-bit unsigned immediate operand
|
||||||
|
opImm5U(line, code) {
|
||||||
|
return code[0] & 31;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a 16-bit signed immediate operand
|
||||||
|
opImm16S(line, code) {
|
||||||
|
let ret = code[1] << 16 >> 16;
|
||||||
|
return (
|
||||||
|
ret < -256 ? "-" + this.hex(-ret) :
|
||||||
|
ret > 256 ? this.hex( ret) :
|
||||||
|
ret
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a 16-bit unsigned immediate operand
|
||||||
|
opImm16U(line, code) {
|
||||||
|
return this.hex(code[1], 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a Reg1 operand
|
||||||
|
opReg1(line, code) {
|
||||||
|
return this.programRegister(code[0] & 31);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a disp[reg1] operand
|
||||||
|
opReg1Disp(line, code) {
|
||||||
|
let disp = code[1] << 16 >> 16;
|
||||||
|
let reg1 = this.programRegister(code[0] & 31);
|
||||||
|
|
||||||
|
// Do not print the displacement
|
||||||
|
if (disp == 0)
|
||||||
|
return "[" + reg1 + "]";
|
||||||
|
|
||||||
|
// Format the displacement amount
|
||||||
|
disp =
|
||||||
|
disp < -256 ? "-" + this.hex(-disp) :
|
||||||
|
disp > 256 ? this.hex( disp) :
|
||||||
|
disp.toString()
|
||||||
|
;
|
||||||
|
|
||||||
|
// [reg1 + disp] notation
|
||||||
|
if (this.memInside) {
|
||||||
|
return "[" + reg1 + (disp.startsWith("-") ?
|
||||||
|
" - " + disp.substring(1) :
|
||||||
|
" + " + disp
|
||||||
|
) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// disp[reg1] notation
|
||||||
|
return disp + "[" + reg1 + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a [Reg1] operand
|
||||||
|
opReg1Ind(line, code) {
|
||||||
|
return "[" + this.programRegister(code[0] & 31) + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a Reg2 operand
|
||||||
|
opReg2(line, code) {
|
||||||
|
return this.programRegister(code[0] >> 5 & 31);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a system register operand
|
||||||
|
opSys(line, code) {
|
||||||
|
return this.systemRegister(code[0] & 31);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Private Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Select the mnemonic for a condition
|
||||||
|
condition(index, forceUppercase = false) {
|
||||||
|
if (!this.condNames)
|
||||||
|
return index.toString();
|
||||||
|
let ret =
|
||||||
|
index == 1 ? this.condCL :
|
||||||
|
index == 2 ? this.condEZ :
|
||||||
|
index == 9 ? "N" + this.condCL :
|
||||||
|
index == 10 ? "N" + this.condEZ :
|
||||||
|
Disassembler.CONDITIONS[index]
|
||||||
|
;
|
||||||
|
if (!forceUppercase && !this.condUppercase)
|
||||||
|
ret = ret.toLowerCase();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a number as a hexadecimal string
|
||||||
|
hex(value, digits = null, decorated = true) {
|
||||||
|
value = value.toString(16);
|
||||||
|
if (this.hexUppercase)
|
||||||
|
value = value.toUpperCase();
|
||||||
|
if (digits != null)
|
||||||
|
value = value.padStart(digits, "0");
|
||||||
|
if (decorated) {
|
||||||
|
value = this.hexPrefix + value + this.hexSuffix;
|
||||||
|
if (this.hexPrefix == "" && "0123456789".indexOf(value[0]) == -1)
|
||||||
|
value = "0" + value;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a jump or branch destination
|
||||||
|
jump(address, disp) {
|
||||||
|
return (
|
||||||
|
this.jumpAddress ?
|
||||||
|
this.hex(address + disp >>> 0, 8, false) :
|
||||||
|
disp < -256 ? "-" + this.hex(-disp) :
|
||||||
|
disp > 256 ? "+" + this.hex( disp) :
|
||||||
|
disp.toString()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the number of bytes in the next line(s) of disassembly
|
||||||
|
more(more, data, offset) {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (offset + 1 >= data.length)
|
||||||
|
throw new Error("Disassembly error: Unexpected EoF");
|
||||||
|
|
||||||
|
// Determine the instruction's size from its opcode
|
||||||
|
let opcode = data[offset + 1] >>> 2;
|
||||||
|
more.push(
|
||||||
|
opcode < 0b101000 || // 16-bit instruction
|
||||||
|
opcode == 0b110010 || // Illegal opcode
|
||||||
|
opcode == 0b110110 // Illegal opcode
|
||||||
|
? 2 : 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a program register
|
||||||
|
programRegister(index) {
|
||||||
|
let ret = this.proNames ?
|
||||||
|
Disassembler.REG_PROGRAM[index] : "r" + index;
|
||||||
|
if (this.proUppercase)
|
||||||
|
ret = ret.toUpperCase();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a system register
|
||||||
|
systemRegister(index) {
|
||||||
|
let ret = this.sysNames ?
|
||||||
|
Disassembler.REG_SYSTEM[index] : index.toString();
|
||||||
|
if (!this.sysUppercase && this.sysNames)
|
||||||
|
ret = ret.toLowerCase();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { Disassembler };
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
#undef VBAPI
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <emscripten/emscripten.h>
|
||||||
|
#include <vb.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////// Module Commands ///////////////////////////////
|
||||||
|
|
||||||
|
// Create and initialize a new simulation
|
||||||
|
EMSCRIPTEN_KEEPALIVE VB* Create() {
|
||||||
|
VB *sim = malloc(sizeof (VB));
|
||||||
|
vbInit(sim);
|
||||||
|
return sim;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete all memory used by a simulation
|
||||||
|
EMSCRIPTEN_KEEPALIVE void Delete(VB *sim) {
|
||||||
|
free(sim->cart.ram);
|
||||||
|
free(sim->cart.rom);
|
||||||
|
free(sim);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proxy for free()
|
||||||
|
EMSCRIPTEN_KEEPALIVE void Free(void *ptr) {
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proxy for malloc()
|
||||||
|
EMSCRIPTEN_KEEPALIVE void* Malloc(int size) {
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size in bytes of a pointer
|
||||||
|
EMSCRIPTEN_KEEPALIVE int PointerSize() {
|
||||||
|
return sizeof (void *);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////// Debugger Commands //////////////////////////////
|
||||||
|
|
||||||
|
// Execute until the following instruction
|
||||||
|
static uint32_t RunToNextAddress;
|
||||||
|
static int RunToNextFetch(VB *sim, int fetch, VBAccess *access) {
|
||||||
|
return access->address == RunToNextAddress;
|
||||||
|
}
|
||||||
|
static int RunToNextExecute(VB *sim, VBInstruction *inst) {
|
||||||
|
RunToNextAddress = inst->address + inst->size;
|
||||||
|
vbSetCallback(sim, VB_ONEXECUTE, NULL);
|
||||||
|
vbSetCallback(sim, VB_ONFETCH, &RunToNextFetch);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EMSCRIPTEN_KEEPALIVE void RunToNext(VB **sims, int count) {
|
||||||
|
uint32_t clocks = 20000000; // 1s
|
||||||
|
vbSetCallback(sims[0], VB_ONEXECUTE, &RunToNextExecute);
|
||||||
|
vbEmulateEx (sims, count, &clocks);
|
||||||
|
vbSetCallback(sims[0], VB_ONEXECUTE, NULL);
|
||||||
|
vbSetCallback(sims[0], VB_ONFETCH , NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute the current instruction
|
||||||
|
static int SingleStepBreak;
|
||||||
|
static int SingleStepFetch(VB *sim, int fetch, VBAccess *access) {
|
||||||
|
if (fetch != 0)
|
||||||
|
return 0;
|
||||||
|
if (SingleStepBreak == 1)
|
||||||
|
return 1;
|
||||||
|
SingleStepBreak = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EMSCRIPTEN_KEEPALIVE void SingleStep(VB **sims, int count) {
|
||||||
|
uint32_t clocks = 20000000; // 1s
|
||||||
|
SingleStepBreak = sims[0]->cpu.stage == 0 ? 0 : 1;
|
||||||
|
vbSetCallback(sims[0], VB_ONFETCH, &SingleStepFetch);
|
||||||
|
vbEmulateEx (sims, count, &clocks);
|
||||||
|
vbSetCallback(sims[0], VB_ONEXECUTE, NULL);
|
||||||
|
vbSetCallback(sims[0], VB_ONFETCH , NULL);
|
||||||
|
}
|
||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import { ISX } from /**/"./ISX.js";
|
||||||
|
|
||||||
|
// Debug mode UI manager
|
||||||
|
class Debugger {
|
||||||
|
|
||||||
|
///////////////////////////// Static Methods //////////////////////////////
|
||||||
|
|
||||||
|
// Data type conversions
|
||||||
|
static F32 = new Float32Array(1);
|
||||||
|
static U32 = new Uint32Array(this.F32.buffer);
|
||||||
|
|
||||||
|
// Reinterpret a float32 as a u32
|
||||||
|
static fxi(x) {
|
||||||
|
this.F32[0] = x;
|
||||||
|
return this.U32[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process file data as ISM
|
||||||
|
static isx(data) {
|
||||||
|
return new ISX(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reinterpret a u32 as a float32
|
||||||
|
static ixf(x) {
|
||||||
|
this.U32[0] = x;
|
||||||
|
return this.F32[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the number of lines scrolled by a WheelEvent
|
||||||
|
static linesScrolled(e, lineHeight, pageLines, delta) {
|
||||||
|
let ret = {
|
||||||
|
delta: delta,
|
||||||
|
lines: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
// No scrolling occurred
|
||||||
|
if (e.deltaY == 0);
|
||||||
|
|
||||||
|
// Scrolling by pixel
|
||||||
|
else if (e.deltaMode == WheelEvent.DOM_DELTA_PIXEL) {
|
||||||
|
ret.delta += e.deltaY;
|
||||||
|
ret.lines = Math.sign(ret.delta) *
|
||||||
|
Math.floor(Math.abs(ret.delta) / lineHeight);
|
||||||
|
ret.delta -= ret.lines * lineHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scrolling by line
|
||||||
|
else if (e.deltaMode == WheelEvent.DOM_DELTA_LINE)
|
||||||
|
ret.lines = Math.trunc(e.deltaY);
|
||||||
|
|
||||||
|
// Scrolling by page
|
||||||
|
else if (e.deltaMode == WheelEvent.DOM_DELTA_PAGE)
|
||||||
|
ret.lines = Math.trunc(e.deltaY) * pageLines;
|
||||||
|
|
||||||
|
// Unknown scrolling mode
|
||||||
|
else ret.lines = 3 * Math.sign(e.deltaY);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////// Initialization Methods //////////////////////////
|
||||||
|
|
||||||
|
constructor(app, sim, index) {
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
this.app = app;
|
||||||
|
this.core = app.core;
|
||||||
|
this.dasm = app.dasm;
|
||||||
|
this.sim = sim;
|
||||||
|
|
||||||
|
// Configure debugger windows
|
||||||
|
this.cpu = new Debugger.CPU (this, index);
|
||||||
|
this.memory = new Debugger.Memory(this, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Package Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Disassembler configuration has changed
|
||||||
|
dasmConfigured() {
|
||||||
|
this.cpu .dasmConfigured();
|
||||||
|
this.memory.dasmConfigured();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure PC is visible in the disassembler
|
||||||
|
followPC(pc = null) {
|
||||||
|
this.cpu.disassembler.followPC(pc);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format a number as hexadecimal
|
||||||
|
hex(value, digits = null, decorated = true) {
|
||||||
|
return this.dasm.hex(value, digits, decorated);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register component classes
|
||||||
|
(await import(/**/"./CPU.js" )).register(Debugger);
|
||||||
|
(await import(/**/"./Memory.js")).register(Debugger);
|
||||||
|
|
||||||
|
export { Debugger };
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
// Debug manager for Intelligent Systems binaries
|
||||||
|
class ISX {
|
||||||
|
|
||||||
|
///////////////////////// Initialization Methods //////////////////////////
|
||||||
|
|
||||||
|
// Throws on decoding error
|
||||||
|
constructor(data) {
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
this.data = data;
|
||||||
|
this.offset = 0;
|
||||||
|
this.ranges = [];
|
||||||
|
this.symbols = [];
|
||||||
|
this.codes = [];
|
||||||
|
|
||||||
|
// Skip any header that may be present
|
||||||
|
if (data.length >= 32 && this.readInt(3) == 0x585349)
|
||||||
|
this.offset = 32;
|
||||||
|
else this.offset = 0;
|
||||||
|
|
||||||
|
// Process all records
|
||||||
|
while (this.offset < this.data.length) {
|
||||||
|
switch (this.readInt(1)) {
|
||||||
|
|
||||||
|
// Virtual Boy records
|
||||||
|
case 0x11: this.code (); break;
|
||||||
|
case 0x13: this.range (); break;
|
||||||
|
case 0x14: this.symbol(); break;
|
||||||
|
|
||||||
|
// System records
|
||||||
|
case 0x20:
|
||||||
|
case 0x21:
|
||||||
|
case 0x22:
|
||||||
|
let length = this.readInt(4);
|
||||||
|
this.offset += length;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Other records
|
||||||
|
default: throw "ISX decode error";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cleanup instance fields
|
||||||
|
delete this.data;
|
||||||
|
delete this.decoder;
|
||||||
|
delete this.offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Public Methods //////////////////////////////
|
||||||
|
|
||||||
|
// Produce a .vb format ROM file from the ISX code segments
|
||||||
|
toROM() {
|
||||||
|
let head = 0x00000000;
|
||||||
|
let tail = 0x01000000;
|
||||||
|
|
||||||
|
// Inspect all code segments
|
||||||
|
for (let code of this.codes) {
|
||||||
|
let start = code.address & 0x00FFFFFF;
|
||||||
|
let end = start + code.data.length;
|
||||||
|
|
||||||
|
// Segment begins in the first half of ROM
|
||||||
|
if (start < 0x00800000) {
|
||||||
|
|
||||||
|
// Segment ends in the second half of ROM
|
||||||
|
if (end > 0x00800000) {
|
||||||
|
head = tail = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Segment ends in the first half of ROM
|
||||||
|
else if (end > head)
|
||||||
|
head = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Segment begins in the second half of ROM
|
||||||
|
else if (start < tail)
|
||||||
|
tail = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the output buffer
|
||||||
|
let min = head + 0x01000000 - tail;
|
||||||
|
let size = 1;
|
||||||
|
for (; size < min; size <<= 1);
|
||||||
|
let rom = new Uint8Array(size);
|
||||||
|
|
||||||
|
// Output all code segments
|
||||||
|
for (let code of this.codes) {
|
||||||
|
let dest = code.address & rom.length - 1;
|
||||||
|
for (let src = 0; src < code.data.length; src++, dest++)
|
||||||
|
rom[dest] = code.data[src];
|
||||||
|
}
|
||||||
|
|
||||||
|
return rom;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Private Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Process a code record
|
||||||
|
code() {
|
||||||
|
let address = this.readInt(4);
|
||||||
|
let length = this.readInt(4);
|
||||||
|
let data = this.readBytes(length);
|
||||||
|
if (
|
||||||
|
length == 0 ||
|
||||||
|
length > 0x01000000 ||
|
||||||
|
(address & 0x07000000) != 0x07000000 ||
|
||||||
|
(address & 0x07000000) + length > 0x08000000
|
||||||
|
) throw "ISX decode error";
|
||||||
|
this.codes.push({
|
||||||
|
address: address,
|
||||||
|
data : data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process a range record
|
||||||
|
range() {
|
||||||
|
let count = this.readInt(2);
|
||||||
|
while (count--) {
|
||||||
|
let start = this.readInt(4);
|
||||||
|
let end = this.readInt(4);
|
||||||
|
let type = this.readInt(1);
|
||||||
|
this.ranges.push({
|
||||||
|
end : end,
|
||||||
|
start: start,
|
||||||
|
type : type
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process a symbol record
|
||||||
|
symbol() {
|
||||||
|
let count = this.readInt(2);
|
||||||
|
while (count--) {
|
||||||
|
let length = this.readInt(1);
|
||||||
|
let name = this.readString(length);
|
||||||
|
let flags = this.readInt(2);
|
||||||
|
let address = this.readInt(4);
|
||||||
|
this.symbols.push({
|
||||||
|
address: address,
|
||||||
|
flags : flags,
|
||||||
|
name : name
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a byte buffer
|
||||||
|
readBytes(size) {
|
||||||
|
if (this.offset + size > this.data.length)
|
||||||
|
throw "ISX decode error";
|
||||||
|
let ret = this.data.slice(this.offset, this.offset + size);
|
||||||
|
this.offset += size;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read an integer
|
||||||
|
readInt(size) {
|
||||||
|
if (this.offset + size > this.data.length)
|
||||||
|
throw "ISX decode error";
|
||||||
|
let ret = new Uint32Array(1);
|
||||||
|
for (let shift = 0; size > 0; size--, shift += 8)
|
||||||
|
ret[0] |= this.data[this.offset++] << shift;
|
||||||
|
return ret[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read a text string
|
||||||
|
readString(size) {
|
||||||
|
return (this.decoder = this.decoder || new TextDecoder()
|
||||||
|
).decode(this.readBytes(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { ISX };
|
||||||
@@ -0,0 +1,574 @@
|
|||||||
|
import { Toolkit } from /**/"../toolkit/Toolkit.js";
|
||||||
|
let register = Debugger => Debugger.Memory =
|
||||||
|
|
||||||
|
// Debugger memory window
|
||||||
|
class Memory extends Toolkit.Window {
|
||||||
|
|
||||||
|
///////////////////////// Initialization Methods //////////////////////////
|
||||||
|
|
||||||
|
constructor(debug, index) {
|
||||||
|
super(debug.app, {
|
||||||
|
class: "tk window memory"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
this.data = null,
|
||||||
|
this.dataAddress = null,
|
||||||
|
this.debug = debug;
|
||||||
|
this.delta = 0;
|
||||||
|
this.editDigit = null;
|
||||||
|
this.height = 300;
|
||||||
|
this.index = index;
|
||||||
|
this.lines = [];
|
||||||
|
this.pending = false;
|
||||||
|
this.shown = false;
|
||||||
|
this.subscription = [ 0, index, "memory", "refresh" ];
|
||||||
|
this.width = 400;
|
||||||
|
|
||||||
|
// Available buses
|
||||||
|
this.buses = [
|
||||||
|
{
|
||||||
|
editAddress: 0x05000000,
|
||||||
|
viewAddress: 0x05000000
|
||||||
|
}
|
||||||
|
];
|
||||||
|
this.bus = this.buses[0];
|
||||||
|
|
||||||
|
// Window
|
||||||
|
this.setTitle("{debug.memory._}", true);
|
||||||
|
this.substitute("#", " " + (index + 1));
|
||||||
|
if (index == 1)
|
||||||
|
this.element.classList.add("two");
|
||||||
|
this.addEventListener("close" , e=>this.visible = false);
|
||||||
|
this.addEventListener("visibility", e=>this.onVisibility(e));
|
||||||
|
|
||||||
|
// Client area
|
||||||
|
Object.assign(this.client.style, {
|
||||||
|
display : "grid",
|
||||||
|
gridTemplateRows: "max-content auto"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bus drop-down
|
||||||
|
this.drpBus = new Toolkit.DropDown(debug.app);
|
||||||
|
this.drpBus.setLabel("{debug.memory.bus}", true);
|
||||||
|
this.drpBus.setTitle("{debug.memory.bus}", true);
|
||||||
|
this.drpBus.add("{debug.memory.busMemory}", true, this.buses[0]);
|
||||||
|
this.drpBus.addEventListener("input", e=>this.busInput());
|
||||||
|
this.add(this.drpBus);
|
||||||
|
|
||||||
|
// Hex editor
|
||||||
|
this.hexEditor = new Toolkit.Component(debug.app, {
|
||||||
|
class : "tk mono hex-editor",
|
||||||
|
role : "application",
|
||||||
|
tabIndex: "0",
|
||||||
|
style : {
|
||||||
|
display : "grid",
|
||||||
|
gridTemplateColumns: "repeat(17, max-content)",
|
||||||
|
height : "100%",
|
||||||
|
minWidth : "100%",
|
||||||
|
overflow : "hidden",
|
||||||
|
position : "relative",
|
||||||
|
width : "max-content"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.hexEditor.localize = ()=>{
|
||||||
|
this.hexEditor.localizeRoleDescription();
|
||||||
|
this.hexEditor.localizeLabel();
|
||||||
|
};
|
||||||
|
this.hexEditor.setLabel("{debug.memory.hexEditor}", true);
|
||||||
|
this.hexEditor.setRoleDescription("{debug.memory.hexEditor}", true);
|
||||||
|
this.hexEditor.addEventListener("focusout", e=>this.commit ( ));
|
||||||
|
this.hexEditor.addEventListener("keydown" , e=>this.hexKeyDown(e));
|
||||||
|
this.hexEditor.addEventListener("resize" , e=>this.hexResize ( ));
|
||||||
|
this.hexEditor.addEventListener("wheel" , e=>this.hexWheel (e));
|
||||||
|
this.hexEditor.addEventListener(
|
||||||
|
"pointerdown", e=>this.hexPointerDown(e));
|
||||||
|
this.lastFocus = this.hexEditor;
|
||||||
|
|
||||||
|
// Label for measuring text dimensions
|
||||||
|
this.sizer = new Toolkit.Label(debug.app, {
|
||||||
|
class : "tk label mono",
|
||||||
|
visible : false,
|
||||||
|
visibility: true,
|
||||||
|
style: {
|
||||||
|
position: "absolute"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.sizer.setText("\u00a0", false); //
|
||||||
|
this.hexEditor.append(this.sizer);
|
||||||
|
|
||||||
|
// Hex editor scroll pane
|
||||||
|
this.scrHex = new Toolkit.ScrollPane(debug.app, {
|
||||||
|
overflowX: "auto",
|
||||||
|
overflowY: "hidden",
|
||||||
|
view : this.hexEditor
|
||||||
|
});
|
||||||
|
this.add(this.scrHex);
|
||||||
|
|
||||||
|
// Hide the bus drop-down: Virtual Boy only has one bus
|
||||||
|
this.drpBus.visible = false;
|
||||||
|
this.client.style.gridTemplateRows = "auto";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Event Handlers //////////////////////////////
|
||||||
|
|
||||||
|
// Bus drop-down selection
|
||||||
|
busInput() {
|
||||||
|
|
||||||
|
// An edit is in progress
|
||||||
|
if (this.editDigit !== null)
|
||||||
|
this.commit(false);
|
||||||
|
|
||||||
|
// Switch to the new bus
|
||||||
|
this.bus = this.drpBus.value;
|
||||||
|
this.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hex editor key press
|
||||||
|
hexKeyDown(e) {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (e.altKey || e.ctrlKey)
|
||||||
|
|
||||||
|
// Processing by key, scroll lock off
|
||||||
|
if (!e.getModifierState("ScrollLock")) switch (e.key) {
|
||||||
|
case "ArrowDown":
|
||||||
|
this.commit();
|
||||||
|
this.setEditAddress(this.bus.editAddress + 16);
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
case "ArrowLeft":
|
||||||
|
this.commit();
|
||||||
|
this.setEditAddress(this.bus.editAddress - 1);
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
case "ArrowRight":
|
||||||
|
this.commit();
|
||||||
|
this.setEditAddress(this.bus.editAddress + 1);
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
case "ArrowUp":
|
||||||
|
this.commit();
|
||||||
|
this.setEditAddress(this.bus.editAddress - 16);
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
case "PageDown":
|
||||||
|
this.commit();
|
||||||
|
this.setEditAddress(this.bus.editAddress + this.tall(true)*16);
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
case "PageUp":
|
||||||
|
this.commit();
|
||||||
|
this.setEditAddress(this.bus.editAddress - this.tall(true)*16);
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing by key, scroll lock on
|
||||||
|
else switch (e.key) {
|
||||||
|
case "ArrowDown":
|
||||||
|
this.setViewAddress(this.bus.viewAddress + 16);
|
||||||
|
this.fetch();
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
case "ArrowLeft":
|
||||||
|
this.scrHex.scrollLeft -= this.scrHex.hscroll.unitIncrement;
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
case "ArrowRight":
|
||||||
|
this.scrHex.scrollLeft += this.scrHex.hscroll.unitIncrement;
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
case "ArrowUp":
|
||||||
|
this.setViewAddress(this.bus.viewAddress - 16);
|
||||||
|
this.fetch();
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
case "PageDown":
|
||||||
|
this.setViewAddress(this.bus.viewAddress + this.tall(true)*16);
|
||||||
|
this.fetch();
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
case "PageUp":
|
||||||
|
this.setViewAddress(this.bus.viewAddress - this.tall(true)*16);
|
||||||
|
this.fetch();
|
||||||
|
Toolkit.handle(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing by key, editing
|
||||||
|
switch (e.key) {
|
||||||
|
|
||||||
|
case "0": case "1": case "2": case "3": case "4":
|
||||||
|
case "5": case "6": case "7": case "8": case "9":
|
||||||
|
case "a": case "A": case "b": case "B": case "c":
|
||||||
|
case "C": case "d": case "D": case "e": case "E":
|
||||||
|
case "f": case "F":
|
||||||
|
let digit = parseInt(e.key, 16);
|
||||||
|
if (this.editDigit === null) {
|
||||||
|
this.editDigit = digit;
|
||||||
|
this.setEditAddress(this.bus.editAddress);
|
||||||
|
} else {
|
||||||
|
this.editDigit = this.editDigit << 4 | digit;
|
||||||
|
this.commit();
|
||||||
|
this.setEditAddress(this.bus.editAddress + 1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Commit the current edit
|
||||||
|
case "Enter":
|
||||||
|
if (this.editDigit === null)
|
||||||
|
break;
|
||||||
|
this.commit();
|
||||||
|
this.setEditAddress(this.bus.editAddress + 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Cancel the current edit
|
||||||
|
case "Escape":
|
||||||
|
if (this.editDigit === null)
|
||||||
|
return;
|
||||||
|
this.editDigit = null;
|
||||||
|
this.setEditAddress(this.bus.editAddress);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toolkit.handle(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hex editor pointer down
|
||||||
|
hexPointerDown(e) {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (e.button != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Working variables
|
||||||
|
let cols = this.lines[0].lblBytes.map(l=>l.getBoundingClientRect());
|
||||||
|
let y = Math.max(0, Math.floor((e.clientY-cols[0].y)/cols[0].height));
|
||||||
|
let x = 15;
|
||||||
|
|
||||||
|
// Determine which column is closest to the touch point
|
||||||
|
if (e.clientX < cols[15].right) {
|
||||||
|
for (let l = 0; l < 15; l++) {
|
||||||
|
if (e.clientX > (cols[l].right + cols[l + 1].x) / 2)
|
||||||
|
continue;
|
||||||
|
x = l;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the selection address
|
||||||
|
let address = this.toAddress(this.bus.viewAddress + y * 16 + x);
|
||||||
|
if (this.editDigit !== null && address != this.bus.editAddress)
|
||||||
|
this.commit();
|
||||||
|
this.setEditAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hex editor resized
|
||||||
|
hexResize() {
|
||||||
|
let tall = this.tall(false);
|
||||||
|
let grew = this.lines.length < tall;
|
||||||
|
|
||||||
|
// Process all visible lines
|
||||||
|
for (let y = this.lines.length; y < tall; y++) {
|
||||||
|
let line = {
|
||||||
|
lblAddress: document.createElement("div"),
|
||||||
|
lblBytes : []
|
||||||
|
};
|
||||||
|
|
||||||
|
// Address label
|
||||||
|
line.lblAddress.className = "addr" + (y == 0 ? " first" : "");
|
||||||
|
this.hexEditor.append(line.lblAddress);
|
||||||
|
|
||||||
|
// Byte labels
|
||||||
|
for (let x = 0; x < 16; x++) {
|
||||||
|
let lbl = line.lblBytes[x] = document.createElement("div");
|
||||||
|
lbl.className = "byte b" + x + (y == 0 ? " first" : "");
|
||||||
|
this.hexEditor.append(lbl);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.lines.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove lines that are no longer visible
|
||||||
|
while (tall < this.lines.length) {
|
||||||
|
let line = this.lines[tall];
|
||||||
|
line.lblAddress.remove();
|
||||||
|
for (let lbl of line.lblBytes)
|
||||||
|
lbl.remove();
|
||||||
|
this.lines.splice(tall, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure components
|
||||||
|
let lineHeight = this.sizer.element.getBoundingClientRect().height;
|
||||||
|
this.scrHex.hscroll.unitIncrement = lineHeight;
|
||||||
|
this.hexEditor.element.style.gridAutoRows = lineHeight + "px";
|
||||||
|
|
||||||
|
// Update components
|
||||||
|
if (grew)
|
||||||
|
this.fetch();
|
||||||
|
else this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hex editor mouse wheel
|
||||||
|
hexWheel(e) {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (e.altKey || e.ctrlKey || e.shiftKey)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Always handle the event
|
||||||
|
Toolkit.handle(e);
|
||||||
|
|
||||||
|
// Determine how many full lines were scrolled
|
||||||
|
let scr = Debugger.linesScrolled(e,
|
||||||
|
this.sizer.element.getBoundingClientRect().height,
|
||||||
|
this.tall(true),
|
||||||
|
this.delta
|
||||||
|
);
|
||||||
|
this.delta = scr.delta;
|
||||||
|
scr.lines = Math.max(-3, Math.min(3, scr.lines));
|
||||||
|
|
||||||
|
// No lines were scrolled
|
||||||
|
if (scr.lines == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Scroll the view
|
||||||
|
this.setViewAddress(this.bus.viewAddress + scr.lines * 16);
|
||||||
|
this.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window key press
|
||||||
|
onKeyDown(e) {
|
||||||
|
super.onKeyDown(e);
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (e.altKey || !e.ctrlKey || e.shiftKey)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Processing by key
|
||||||
|
switch (e.key) {
|
||||||
|
case "g": case "G": this.goto(); break;
|
||||||
|
default: return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Toolkit.handle(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Window visibility
|
||||||
|
onVisibility(e) {
|
||||||
|
this.shown = this.shown || e.visible;
|
||||||
|
if (!e.visible)
|
||||||
|
this.debug.core.unsubscribe(this.subscription, false);
|
||||||
|
else this.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Package Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Disassembler configuration has changed
|
||||||
|
dasmConfigured() {
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt the user to navigate to a new editing address
|
||||||
|
goto() {
|
||||||
|
|
||||||
|
// Retrieve the value from the user
|
||||||
|
let addr = prompt(this.app.localize("{debug.memory.goto}"));
|
||||||
|
if (addr === null)
|
||||||
|
return;
|
||||||
|
addr = parseInt(addr.trim(), 16);
|
||||||
|
if (
|
||||||
|
!Number.isInteger(addr) ||
|
||||||
|
addr < 0 ||
|
||||||
|
addr > 4294967295
|
||||||
|
) return;
|
||||||
|
|
||||||
|
// Commit an outstanding edit
|
||||||
|
if (this.editDigit !== null && this.bus.editAddress != addr)
|
||||||
|
this.commit();
|
||||||
|
|
||||||
|
// Navigate to the given address
|
||||||
|
this.hexEditor.focus();
|
||||||
|
this.setEditAddress(addr, 1/3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Private Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Write the edited value to the simulation state
|
||||||
|
commit(refresh = true) {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (this.editDigit === null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// The edited value is in the bus's data buffer
|
||||||
|
if (this.data != null) {
|
||||||
|
let offset = this.toAddress(this.bus.editAddress-this.dataAddress);
|
||||||
|
if (offset < this.data.length)
|
||||||
|
this.data[offset] = this.editDigit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write one byte to the simulation state
|
||||||
|
let data = new Uint8Array(1);
|
||||||
|
data[0] = this.editDigit;
|
||||||
|
this.editDigit = null;
|
||||||
|
this.debug.core.write(this.debug.sim, this.bus.editAddress,
|
||||||
|
data, { refresh: refresh });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve data from the simulation state
|
||||||
|
async fetch() {
|
||||||
|
|
||||||
|
// Select the parameters for the simulation fetch
|
||||||
|
let params = {
|
||||||
|
address: this.toAddress(this.bus.viewAddress - 10 * 16),
|
||||||
|
length : (this.tall(false) + 20) * 16
|
||||||
|
};
|
||||||
|
|
||||||
|
// A communication with the core thread is already underway
|
||||||
|
if (this.pending) {
|
||||||
|
this.pending = params;
|
||||||
|
this.refresh();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve data from the simulation state
|
||||||
|
this.pending = params;
|
||||||
|
for (let data=null, promise=null; this.pending instanceof Object;) {
|
||||||
|
|
||||||
|
// Wait for a transaction to complete
|
||||||
|
if (promise != null) {
|
||||||
|
this.pending = true;
|
||||||
|
data = await promise;
|
||||||
|
promise = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate a new transaction
|
||||||
|
if (this.pending instanceof Object) {
|
||||||
|
params = this.pending;
|
||||||
|
let options = {};
|
||||||
|
if (this.isVisible())
|
||||||
|
options.subscription = this.subscription;
|
||||||
|
promise = this.debug.core.read(this.debug.sim,
|
||||||
|
params.address, params.length, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the result of a transaction
|
||||||
|
if (data != null) {
|
||||||
|
this.refresh(data);
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
this.pending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update hex editor
|
||||||
|
refresh(msg = null) {
|
||||||
|
|
||||||
|
// Receiving data from the simulation state
|
||||||
|
if (msg != null) {
|
||||||
|
this.data = msg.data;
|
||||||
|
this.dataAddress = msg.address;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all lines
|
||||||
|
for (let y = 0; y < this.lines.length; y++) {
|
||||||
|
let address = this.toAddress(this.bus.viewAddress + y * 16);
|
||||||
|
let line = this.lines[y];
|
||||||
|
|
||||||
|
// Address label
|
||||||
|
line.lblAddress.innerText = this.debug.hex(address, 8, false);
|
||||||
|
|
||||||
|
// Process all bytes
|
||||||
|
for (let x = 0; x < 16; x++) {
|
||||||
|
let label = line.lblBytes[x];
|
||||||
|
let text = "--";
|
||||||
|
|
||||||
|
// Currently editing this byte
|
||||||
|
if (address+x==this.bus.editAddress && this.editDigit!==null) {
|
||||||
|
text = this.debug.hex(this.editDigit, 1, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bus data exists
|
||||||
|
else if (this.data != null) {
|
||||||
|
let offset = this.toAddress(address-this.dataAddress+x);
|
||||||
|
|
||||||
|
// The byte is contained in the bus data buffer
|
||||||
|
if (offset >= 0 && offset < this.data.length)
|
||||||
|
text = this.debug.hex(this.data[offset], 2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
label.innerText = text;
|
||||||
|
label.classList[address + x == this.bus.editAddress ?
|
||||||
|
"add" : "remove"]("edit");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify the address of the hex editor's selection
|
||||||
|
setEditAddress(address, auto = false) {
|
||||||
|
let col = this.lines[0].lblBytes[address&15].getBoundingClientRect();
|
||||||
|
let port = this.scrHex.viewport.element.getBoundingClientRect();
|
||||||
|
let row = this.toAddress(address & ~15);
|
||||||
|
let scr = this.scrHex.scrollLeft;
|
||||||
|
let tall = this.tall(true, 0);
|
||||||
|
|
||||||
|
// Ensure the data row is fully visible
|
||||||
|
if (this.toAddress(row - this.bus.viewAddress) >= tall * 16) {
|
||||||
|
if (!auto) {
|
||||||
|
this.setViewAddress(
|
||||||
|
this.toAddress(this.bus.viewAddress - row) <=
|
||||||
|
this.toAddress(row - (this.bus.viewAddress + tall * 16))
|
||||||
|
? row : this.toAddress(row - (tall - 1) * 16));
|
||||||
|
} else this.setViewAddress(row - Math.floor(tall * auto) * 16);
|
||||||
|
this.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the column is fully visible
|
||||||
|
this.scrHex.scrollLeft =
|
||||||
|
Math.min(
|
||||||
|
Math.max(
|
||||||
|
scr,
|
||||||
|
scr + col.right - port.right
|
||||||
|
),
|
||||||
|
scr - port.x + col.x
|
||||||
|
)
|
||||||
|
;
|
||||||
|
|
||||||
|
// Refresh the display;
|
||||||
|
this.bus.editAddress = this.toAddress(address);
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify the address of the hex editor's view
|
||||||
|
setViewAddress(address) {
|
||||||
|
this.bus.viewAddress = this.toAddress(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Measure the number of lines visible in the view
|
||||||
|
tall(fully = null, plus = 1) {
|
||||||
|
return Math.max(1, Math[fully===null ? "abs" : fully?"floor":"ceil"](
|
||||||
|
this.scrHex.viewport.element.getBoundingClientRect().height /
|
||||||
|
this.sizer .element.getBoundingClientRect().height
|
||||||
|
)) + plus;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure an address is in the proper range
|
||||||
|
toAddress(address) {
|
||||||
|
return address >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { register };
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
{
|
||||||
|
"id" : "en-US",
|
||||||
|
"name": "English (US)",
|
||||||
|
|
||||||
|
"app": {
|
||||||
|
"title": "Virtual Boy Emulator"
|
||||||
|
},
|
||||||
|
|
||||||
|
"menu._": "Main menu",
|
||||||
|
|
||||||
|
"menu.file": {
|
||||||
|
"_" : "File",
|
||||||
|
"loadROM" : "Load ROM{#}...",
|
||||||
|
"loadROMError" : "Error loading ROM file",
|
||||||
|
"loadROMInvalid": "The selected file is not a Virtual Boy ROM.",
|
||||||
|
"dualMode" : "Dual mode",
|
||||||
|
"debugMode" : "Debug mode"
|
||||||
|
},
|
||||||
|
|
||||||
|
"menu.emulation": {
|
||||||
|
"_" : "Emulation",
|
||||||
|
"run" : "Run",
|
||||||
|
"pause" : "Pause",
|
||||||
|
"reset" : "Reset{#}",
|
||||||
|
"linkSims": "Link sims"
|
||||||
|
},
|
||||||
|
|
||||||
|
"menu.debug": {
|
||||||
|
"_" : "Debug{#}",
|
||||||
|
"backgrounds" : "Backgrounds",
|
||||||
|
"bgMaps" : "BG maps",
|
||||||
|
"breakpoints" : "Breakpoints",
|
||||||
|
"characters" : "Characters",
|
||||||
|
"console" : "Console",
|
||||||
|
"cpu" : "CPU",
|
||||||
|
"frameBuffers": "Frame buffers",
|
||||||
|
"memory" : "Memory",
|
||||||
|
"objects" : "Objects",
|
||||||
|
"palettes" : "Palettes"
|
||||||
|
},
|
||||||
|
|
||||||
|
"menu.theme": {
|
||||||
|
"_" : "Theme",
|
||||||
|
"auto" : "Auto",
|
||||||
|
"dark" : "Dark",
|
||||||
|
"light" : "Light",
|
||||||
|
"virtual": "Virtual"
|
||||||
|
},
|
||||||
|
|
||||||
|
"window": {
|
||||||
|
"close": "Close"
|
||||||
|
},
|
||||||
|
|
||||||
|
"debug.cpu": {
|
||||||
|
"_" : "CPU{#}",
|
||||||
|
"disassembler" : "Disassembler",
|
||||||
|
"float" : "Float",
|
||||||
|
"format" : "Format",
|
||||||
|
"goto" : "Enter the address to seek to:",
|
||||||
|
"hex" : "Hex",
|
||||||
|
"infinity" : "Infinity",
|
||||||
|
"programRegisters": "Program registers",
|
||||||
|
"signed" : "Signed",
|
||||||
|
"systemRegisters" : "System registers",
|
||||||
|
"unsigned" : "Unsigned",
|
||||||
|
"value" : "Value"
|
||||||
|
},
|
||||||
|
|
||||||
|
"debug.memory": {
|
||||||
|
"_" : "Memory{#}",
|
||||||
|
"bus" : "Bus",
|
||||||
|
"busMemory": "Memory",
|
||||||
|
"goto" : "Enter the address to seek to:",
|
||||||
|
"hexEditor": "Hex editor"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><link rel="icon"href="data:;base64,iVBORw0KGgo="><title>Virtual Boy Emulator</title></head><body><img style="display:none;"onload="let a=this,b=a.width,c=a.height,d=document.createElement('canvas');a.remove();d.width=b;d.height=c;d=d.getContext('2d');d.drawImage(a,0,0);d=d.getImageData(0,0,b,c).data.filter((e,f)=>!(f&3));new(async()=>{}).constructor(Array.from(d.slice(0,d.indexOf(0))).map(e=>String.fromCharCode(e)).join(''))(d,a)"src=""></body></html>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 2.6458 2.6458" version="1.1">
|
||||||
|
<g>
|
||||||
|
<path style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 1.05832,1.653625 1.3229,-1.3229 v 0.66145 l -1.3229,1.3229 -0.79374,-0.79374 v -0.66145 z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 459 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 2.6458 2.6458" version="1.1">
|
||||||
|
<path style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.13229;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none" d="m 1.05832,1.653625 1.3229,-1.3229 v 0.66145 l -1.3229,1.3229 -0.79374,-0.79374 v -0.66145 z" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 484 B |
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 2.9104166 2.9104167" version="1.1">
|
||||||
|
<g>
|
||||||
|
<path style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 0.52916666,0.52916665 0.396875,0 0.52916664,0.52916665 0.5291667,-0.52916665 0.396875,0 0,0.396875 L 1.8520833,1.4552083 2.38125,1.984375 l 0,0.396875 -0.396875,0 L 1.4552083,1.8520834 0.92604166,2.38125 l -0.396875,0 0,-0.396875 L 1.0583333,1.4552083 0.52916666,0.92604165 Z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 652 B |
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 2.9104166 2.9104168">
|
||||||
|
<g>
|
||||||
|
<rect style="opacity:1;fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="1.3229167" height="0.26458332" x="0.79375005" y="1.3229167" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 367 B |
@@ -0,0 +1,28 @@
|
|||||||
|
:root {
|
||||||
|
--tk-control : #333333;
|
||||||
|
--tk-control-active : #555555;
|
||||||
|
--tk-control-border : #cccccc;
|
||||||
|
--tk-control-highlight : #444444;
|
||||||
|
--tk-control-shadow : #9b9b9b;
|
||||||
|
--tk-control-text : #cccccc;
|
||||||
|
--tk-desktop : #111111;
|
||||||
|
--tk-selected : #008542;
|
||||||
|
--tk-selected-blur : #325342;
|
||||||
|
--tk-selected-blur-text : #ffffff;
|
||||||
|
--tk-selected-text : #ffffff;
|
||||||
|
--tk-splitter-focus : #008542c0;
|
||||||
|
--tk-window : #222222;
|
||||||
|
--tk-window-blur-close : #d9aeae;
|
||||||
|
--tk-window-blur-close-text : #eeeeee;
|
||||||
|
--tk-window-blur-title : #9fafb9;
|
||||||
|
--tk-window-blur-title2 : #c0b0a0;
|
||||||
|
--tk-window-blur-title-text : #444444;
|
||||||
|
--tk-window-close : #ee9999;
|
||||||
|
--tk-window-close-focus : #99ee99;
|
||||||
|
--tk-window-close-focus-text: #333333;
|
||||||
|
--tk-window-close-text : #ffffff;
|
||||||
|
--tk-window-text : #cccccc;
|
||||||
|
--tk-window-title : #80ccff;
|
||||||
|
--tk-window-title2 : #ffb894;
|
||||||
|
--tk-window-title-text : #000000;
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 2.9104166 2.9104168">
|
||||||
|
<g>
|
||||||
|
<rect style="opacity:1;fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="0.26458332" height="1.3229167" x="1.3229167" y="0.79375005" />
|
||||||
|
<rect style="opacity:1;fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="1.3229167" height="0.26458332" x="0.79375005" y="1.3229167" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 522 B |
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 2.9104166 2.9104168" version="1.1">
|
||||||
|
<rect style="fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="0.26458332" height="1.3229167" x="1.8555599" y="-1.1941016" transform="rotate(60)" />
|
||||||
|
<rect style="fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="1.3229167" height="0.26458332" x="1.3263932" y="0.40035158" transform="rotate(30)" />
|
||||||
|
<rect style="fill:#000000;stroke-width:0.264583;stroke-linecap:square" width="0.26458332" height="1.3229167" x="-1.5875" y="0.79374999" transform="scale(-1,1)" />
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 710 B |
@@ -0,0 +1,554 @@
|
|||||||
|
:root {
|
||||||
|
--tk-font-dialog : "Roboto", sans-serif;
|
||||||
|
--tk-font-mono : "Inconsolata SemiExpanded Medium", monospace;
|
||||||
|
--tk-font-size : 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Roboto";
|
||||||
|
src : /**/url("./roboto.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
@font-face {
|
||||||
|
font-family: "Inconsolata SemiExpanded Medium";
|
||||||
|
src : /**/url("./inconsolata.woff2") format("woff2");
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background: var(--tk-control);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk {
|
||||||
|
box-sizing : border-box;
|
||||||
|
font-family: var(--tk-font-dialog);
|
||||||
|
font-size : var(--tk-font-size);
|
||||||
|
line-height: 1em;
|
||||||
|
margin : 0;
|
||||||
|
outline : none; /* User agent focus indicator */
|
||||||
|
padding : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.tk {
|
||||||
|
border : none;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.mono {
|
||||||
|
font-family: var(--tk-font-mono);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk::selection,
|
||||||
|
.tk *::selection {
|
||||||
|
background: var(--tk-selected);
|
||||||
|
color : var(--tk-selected-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk:not(:focus-within)::selection,
|
||||||
|
.tk *:not(:focus-within)::selection {
|
||||||
|
background: var(--tk-selected-blur);
|
||||||
|
color : var(--tk-selected-blur-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.display {
|
||||||
|
background: var(--tk-desktop);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.desktop {
|
||||||
|
background: var(--tk-desktop);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************** Button ***********************************/
|
||||||
|
|
||||||
|
.tk.button {
|
||||||
|
align-items : stretch;
|
||||||
|
display : inline-grid;
|
||||||
|
grid-template-columns: auto;
|
||||||
|
justify-content : stretch;
|
||||||
|
padding : 0 1px 1px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.button .label {
|
||||||
|
align-items : center;
|
||||||
|
background : var(--tk-control);
|
||||||
|
border : 1px solid var(--tk-control-border);
|
||||||
|
box-shadow : 1px 1px 0 var(--tk-control-border);
|
||||||
|
color : var(--tk-control-text);
|
||||||
|
display : grid;
|
||||||
|
grid-template-columns: auto;
|
||||||
|
justify-content : center;
|
||||||
|
padding : 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.button:focus .label {
|
||||||
|
background: var(--tk-control-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.button.pushed {
|
||||||
|
padding: 1px 0 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.button.pushed .label {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.button[aria-disabled="true"] .label {
|
||||||
|
color : var(--tk-control-shadow);
|
||||||
|
border : 1px solid var(--tk-control-shadow);
|
||||||
|
box-shadow: 1px 1px 0 var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************* Checkbox **********************************/
|
||||||
|
|
||||||
|
.tk.checkbox {
|
||||||
|
column-gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.checkbox .box {
|
||||||
|
border: 1px solid var(--tk-control-shadow);
|
||||||
|
color : var(--tk-control-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.checkbox:focus .box {
|
||||||
|
background: var(--tk-control-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.checkbox .box:before {
|
||||||
|
background : transparent;
|
||||||
|
content : "";
|
||||||
|
display : block;
|
||||||
|
height : 10px;
|
||||||
|
mask : /**/url("./check.svg") center no-repeat;
|
||||||
|
-webkit-mask: /**/url("./check.svg") center no-repeat;
|
||||||
|
width : 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.checkbox[aria-checked="true"] .box:before {
|
||||||
|
background: currentcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.checkbox[aria-checked="mixed"] .box:before {
|
||||||
|
background : currentcolor;
|
||||||
|
mask : /**/url("./check2.svg") center no-repeat;
|
||||||
|
-webkit-mask: /**/url("./check2.svg") center no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.checkbox.pushed .box:before {
|
||||||
|
background: var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.checkbox[aria-disabled="true"] .box {
|
||||||
|
background: var(--tk-control);
|
||||||
|
color : var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
.tk.checkbox[aria-disabled="true"] .label {
|
||||||
|
color: var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************* DropDown **********************************/
|
||||||
|
|
||||||
|
.tk.drop-down {
|
||||||
|
background: var(--tk-window);
|
||||||
|
border : 1px solid var(--tk-control-shadow);
|
||||||
|
color : var(--tk-window-text);
|
||||||
|
padding : 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.drop-down:focus {
|
||||||
|
background: var(--tk-control-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.drop-down[aria-disabled="true"] {
|
||||||
|
color: var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** Menus ***********************************/
|
||||||
|
|
||||||
|
.tk.menu-bar {
|
||||||
|
background : var(--tk-control);
|
||||||
|
border-bottom: 1px solid var(--tk-control-border);
|
||||||
|
color : var(--tk-control-text);
|
||||||
|
cursor : default;
|
||||||
|
padding : 2px;
|
||||||
|
position : relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu {
|
||||||
|
background: var(--tk-control);
|
||||||
|
border : 1px solid var(--tk-control-border);
|
||||||
|
box-shadow: 1px 1px 0 var(--tk-control-border);
|
||||||
|
color : var(--tk-control-text);
|
||||||
|
margin : -1px 0 0 1px;
|
||||||
|
padding : 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu-item[aria-disabled="true"] {
|
||||||
|
color: var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu-item > * {
|
||||||
|
align-items: center;
|
||||||
|
border : 1px solid transparent;
|
||||||
|
column-gap : 4px;
|
||||||
|
display : flex;
|
||||||
|
margin : 0 1px 1px 0;
|
||||||
|
padding : 2px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu-item .icon {
|
||||||
|
box-sizing: border-box;
|
||||||
|
height : 1em;
|
||||||
|
width : 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu-item .icon:before {
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height : 100%;
|
||||||
|
width : 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu-bar > .menu-item .icon,
|
||||||
|
.tk.menu:not(.icons) > .menu-item .icon {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu-item.checkbox .icon {
|
||||||
|
border: 1px solid currentcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu-item.checkbox[aria-checked="true"] .icon:before {
|
||||||
|
background : currentcolor;
|
||||||
|
mask : /**/url("./check.svg") center no-repeat;
|
||||||
|
-webkit-mask: /**/url("./check.svg") center no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu-item .label {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu-item:not([aria-expanded="true"],
|
||||||
|
[aria-disabled="true"], .pushed):hover > *,
|
||||||
|
.tk.menu-item:not([aria-expanded="true"], .pushed):focus > * {
|
||||||
|
border : 1px solid var(--tk-control-shadow);
|
||||||
|
box-shadow: 1px 1px 0 var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu-item:focus > * {
|
||||||
|
background: var(--tk-control-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu-item.pushed > *,
|
||||||
|
.tk.menu-item[aria-expanded="true"] > * {
|
||||||
|
background: var(--tk-control-active);
|
||||||
|
border : 1px solid var(--tk-control-shadow);
|
||||||
|
box-shadow: none;
|
||||||
|
margin : 1px 0 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.menu > [role="separator"] {
|
||||||
|
border : solid var(--tk-control-shadow);
|
||||||
|
border-width: 1px 0 0 0;
|
||||||
|
margin : 4px 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*********************************** Radio ***********************************/
|
||||||
|
|
||||||
|
.tk.radio {
|
||||||
|
column-gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.radio .box {
|
||||||
|
border : 1px solid var(--tk-control-shadow);
|
||||||
|
border-radius: 50%;
|
||||||
|
color : var(--tk-control-text);
|
||||||
|
margin : 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.radio:focus .box {
|
||||||
|
background: var(--tk-control-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.radio .box:before {
|
||||||
|
background : transparent;
|
||||||
|
border-radius: 50%;
|
||||||
|
content : "";
|
||||||
|
display : block;
|
||||||
|
height : 4px;
|
||||||
|
margin : 2px;
|
||||||
|
width : 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.radio[aria-checked="true"] .box:before {
|
||||||
|
background: currentcolor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.radio.pushed .box:before {
|
||||||
|
background: var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.radio[aria-disabled="true"] .box {
|
||||||
|
background: var(--tk-control);
|
||||||
|
color : var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
.tk.radio[aria-disabled="true"] .label {
|
||||||
|
color: var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************* ScrollBar *********************************/
|
||||||
|
|
||||||
|
.tk.scroll-bar {
|
||||||
|
border : 1px solid var(--tk-control-shadow);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar .unit-less,
|
||||||
|
.tk.scroll-bar .unit-more {
|
||||||
|
background: var(--tk-control);
|
||||||
|
border : 0 solid var(--tk-control-shadow);
|
||||||
|
color : var(--tk-control-text);
|
||||||
|
height : 11px;
|
||||||
|
width : 11px;
|
||||||
|
}
|
||||||
|
.tk.scroll-bar[aria-orientation="horizontal"] .unit-less {
|
||||||
|
border-right-width: 1px;
|
||||||
|
}
|
||||||
|
.tk.scroll-bar[aria-orientation="horizontal"] .unit-more {
|
||||||
|
border-left-width: 1px;
|
||||||
|
}
|
||||||
|
.tk.scroll-bar[aria-orientation="vertical"] .unit-less {
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
}
|
||||||
|
.tk.scroll-bar[aria-orientation="vertical"] .unit-more {
|
||||||
|
border-top-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar .unit-less:before,
|
||||||
|
.tk.scroll-bar .unit-more:before {
|
||||||
|
background : currentColor;
|
||||||
|
content : "";
|
||||||
|
display : block;
|
||||||
|
height : 100%;
|
||||||
|
mask : /**/url("./scroll.svg") center no-repeat;
|
||||||
|
-webkit-mask: /**/url("./scroll.svg") center no-repeat;
|
||||||
|
width : 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar .unit-less.pushed:before,
|
||||||
|
.tk.scroll-bar .unit-more.pushed:before {
|
||||||
|
mask-size : 9px;
|
||||||
|
-webkit-mask-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar[aria-orientation="horizontal"] .unit-less:before {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
.tk.scroll-bar[aria-orientation="horizontal"] .unit-more:before {
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
.tk.scroll-bar[aria-orientation="vertical"] .unit-more:before {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar .track {
|
||||||
|
background: var(--tk-control-highlight);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar .thumb {
|
||||||
|
background: var(--tk-control);
|
||||||
|
box-shadow: 0 0 0 1px var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar .block-less.pushed,
|
||||||
|
.tk.scroll-bar .block-more.pushed {
|
||||||
|
background: var(--tk-control-shadow);
|
||||||
|
opacity : 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar:focus .unit-less,
|
||||||
|
.tk.scroll-bar:focus .unit-more,
|
||||||
|
.tk.scroll-bar:focus .thumb {
|
||||||
|
background: var(--tk-control-active);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar[aria-disabled="true"] .unit-less,
|
||||||
|
.tk.scroll-bar[aria-disabled="true"] .unit-more,
|
||||||
|
.tk.scroll-bar.unneeded .unit-less,
|
||||||
|
.tk.scroll-bar.unneeded .unit-more,
|
||||||
|
.tk.scroll-bar[aria-disabled="true"] .thumb {
|
||||||
|
color: var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar.unneeded .thumb {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************** ScrollPane *********************************/
|
||||||
|
|
||||||
|
.tk.scroll-pane {
|
||||||
|
border: 1px solid var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-pane > .scroll-bar[aria-orientation="horizontal"] {
|
||||||
|
border-width: 1px 1px 0 0;
|
||||||
|
}
|
||||||
|
.tk.scroll-pane:not(.vertical) > .scroll-bar[aria-orientation="horizontal"] {
|
||||||
|
border-width: 1px 0 0 0;
|
||||||
|
}
|
||||||
|
.tk.scroll-pane > .scroll-bar[aria-orientation="vertical"] {
|
||||||
|
border-width: 0 0 1px 1px;
|
||||||
|
}
|
||||||
|
.tk.scroll-pane:not(.horizontal) > .scroll-bar[aria-orientation="vertical"] {
|
||||||
|
border-width: 0 0 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-pane > .viewport,
|
||||||
|
.tk.scroll-pane > .corner {
|
||||||
|
background: var(--tk-control);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************* SplitPane *********************************/
|
||||||
|
|
||||||
|
.tk.split-pane > [role="separator"]:focus {
|
||||||
|
background: var(--tk-splitter-focus);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.split-pane > .horizontal[role="separator"] {
|
||||||
|
width: 3px;
|
||||||
|
}
|
||||||
|
.tk.split-pane > .vertical[role="separator"] {
|
||||||
|
height: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************** TextBox **********************************/
|
||||||
|
|
||||||
|
.tk.text-box {
|
||||||
|
background : var(--tk-window);
|
||||||
|
border : 1px solid var(--tk-control-border);
|
||||||
|
color : var(--tk-window-text);
|
||||||
|
line-height: 1em;
|
||||||
|
height : calc(1em + 2px);
|
||||||
|
padding : 0;
|
||||||
|
margin : 0;
|
||||||
|
min-width : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.text-box.[aria-disabled="true"] {
|
||||||
|
background: var(--tk-control-shadow);
|
||||||
|
color : var(--tk-window-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/********************************** Windows **********************************/
|
||||||
|
|
||||||
|
.tk.window {
|
||||||
|
background: var(--tk-control);
|
||||||
|
border : 1px solid var(--tk-control-shadow);
|
||||||
|
box-shadow: 1px 1px 0 var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window:focus-within {
|
||||||
|
border : 1px solid var(--tk-control-border);
|
||||||
|
box-shadow: 1px 1px 0 var(--tk-control-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window > .nw1 { left : -2px; top : -2px; width : 8px; height: 3px; }
|
||||||
|
.tk.window > .nw2 { left : -2px; top : 1px; width : 3px; height: 5px; }
|
||||||
|
.tk.window > .n { left : 6px; top : -2px; right : 6px; height: 3px; }
|
||||||
|
.tk.window > .ne1 { right: -2px; top : -2px; width : 8px; height: 3px; }
|
||||||
|
.tk.window > .ne2 { right: -2px; top : 1px; width : 3px; height: 5px; }
|
||||||
|
.tk.window > .w { left : -2px; top : 6px; bottom: 6px; width : 3px; }
|
||||||
|
.tk.window > .e { right: -2px; top : 6px; bottom: 6px; width : 3px; }
|
||||||
|
.tk.window > .sw1 { left : -2px; bottom: -2px; width : 8px; height: 3px; }
|
||||||
|
.tk.window > .sw2 { left : -2px; bottom: 1px; width : 3px; height: 5px; }
|
||||||
|
.tk.window > .s { left : 6px; bottom: -2px; right : 6px; height: 3px; }
|
||||||
|
.tk.window > .se1 { right: -2px; bottom: -2px; width : 8px; height: 3px; }
|
||||||
|
.tk.window > .se2 { right: -2px; bottom: 1px; width : 3px; height: 5px; }
|
||||||
|
|
||||||
|
.tk.window > .title {
|
||||||
|
align-items : center;
|
||||||
|
background : var(--tk-window-blur-title);
|
||||||
|
border-bottom: 1px solid var(--tk-control-shadow);
|
||||||
|
color : var(--tk-window-blur-title-text);
|
||||||
|
padding : 1px;
|
||||||
|
user-select : none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window:focus-within > .title {
|
||||||
|
background: var(--tk-window-title);
|
||||||
|
color : var(--tk-window-title-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.two > .title {
|
||||||
|
background: var(--tk-window-blur-title2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.two:focus-within > .title {
|
||||||
|
background: var(--tk-window-title2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window > .title .text {
|
||||||
|
cursor : default;
|
||||||
|
font-weight : bold;
|
||||||
|
overflow : hidden;
|
||||||
|
text-align : center;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space : nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window > .title .close-button {
|
||||||
|
background: var(--tk-window-blur-close);
|
||||||
|
border : 1px solid var(--tk-control-shadow);
|
||||||
|
box-sizing: border-box;
|
||||||
|
color : var(--tk-window-close-text);
|
||||||
|
height : 13px;
|
||||||
|
width : 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window:focus-within > .title .close-button {
|
||||||
|
background: var(--tk-window-close);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window > .title .close-button:focus {
|
||||||
|
background: var(--tk-window-close-focus);
|
||||||
|
color : var(--tk-window-close-focus-text);
|
||||||
|
outline : 1px solid var(--tk-control);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window > .title .close-button:before {
|
||||||
|
background : currentcolor;
|
||||||
|
content : "";
|
||||||
|
display : block;
|
||||||
|
height : 11px;
|
||||||
|
mask : /**/url("./close.svg") center no-repeat;
|
||||||
|
-webkit-mask: /**/url("./close.svg") center no-repeat;
|
||||||
|
width : 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window > .title .close-button.pushed:before {
|
||||||
|
mask-size : 9px;
|
||||||
|
-webkit-mask-size: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window > .client {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
:root {
|
||||||
|
--tk-control : #eeeeee;
|
||||||
|
--tk-control-active : #cccccc;
|
||||||
|
--tk-control-border : #000000;
|
||||||
|
--tk-control-highlight : #f8f8f8;
|
||||||
|
--tk-control-shadow : #6c6c6c;
|
||||||
|
--tk-control-text : #000000;
|
||||||
|
--tk-desktop : #cccccc;
|
||||||
|
--tk-selected : #008542;
|
||||||
|
--tk-selected-blur : #5e7d70;
|
||||||
|
--tk-selected-blur-text : #ffffff;
|
||||||
|
--tk-selected-text : #ffffff;
|
||||||
|
--tk-splitter-focus : #008542c0;
|
||||||
|
--tk-window : #ffffff;
|
||||||
|
--tk-window-blur-close : #d9aeae;
|
||||||
|
--tk-window-blur-close-text : #eeeeee;
|
||||||
|
--tk-window-blur-title : #aac4d5;
|
||||||
|
--tk-window-blur-title2 : #dbc4b8;
|
||||||
|
--tk-window-blur-title-text : #444444;
|
||||||
|
--tk-window-close : #ee9999;
|
||||||
|
--tk-window-close-focus : #99ee99;
|
||||||
|
--tk-window-close-focus-text: #333333;
|
||||||
|
--tk-window-close-text : #ffffff;
|
||||||
|
--tk-window-text : #000000;
|
||||||
|
--tk-window-title : #80ccff;
|
||||||
|
--tk-window-title2 : #ffb894;
|
||||||
|
--tk-window-title-text : #000000;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 2.6458332 2.6458332" version="1.1">
|
||||||
|
<g>
|
||||||
|
<circle style="opacity:1;fill:#000000;stroke-width:0.264583;stroke-linecap:square" cx="1.3229166" cy="1.3229166" r="0.66145831" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 361 B |
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 2.9104166 2.9104167" version="1.1">
|
||||||
|
<g>
|
||||||
|
<path style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="M 1.4552083,0.66145833 0.52916666,1.5874999 V 2.2489583 L 1.4552083,1.3229166 2.38125,2.2489583 V 1.5874999 Z" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 484 B |
@@ -0,0 +1,194 @@
|
|||||||
|
/******************************** CPU Window *********************************/
|
||||||
|
|
||||||
|
.tk.window.cpu .client {
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .scr-dasm {
|
||||||
|
border-right-width: 0;
|
||||||
|
box-shadow : 1px 0 0 var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .scr-system {
|
||||||
|
border-width: 1px 1px 0 0;
|
||||||
|
box-shadow : -0.5px 0.5px 0 0.5px var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .scr-program {
|
||||||
|
border-width: 0 1px 1px 0;
|
||||||
|
box-shadow : -0.5px -0.5px 0 0.5px var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .disassembler {
|
||||||
|
background : var(--tk-window);
|
||||||
|
color : var(--tk-window-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .disassembler div {
|
||||||
|
cursor : default;
|
||||||
|
line-height: calc(1em + 2px);
|
||||||
|
isolation : isolate;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .disassembler .address {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .disassembler .byte {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
text-align : center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .disassembler .operands {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .disassembler .byte.b0,
|
||||||
|
.tk.window.cpu .disassembler .mnemonic,
|
||||||
|
.tk.window.cpu .disassembler .operands {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .disassembler .pc {
|
||||||
|
background: var(--tk-selected-blur);
|
||||||
|
left : 1px;
|
||||||
|
right : 1px;
|
||||||
|
}
|
||||||
|
.tk.window.cpu .disassembler:focus-within .pc {
|
||||||
|
background: var(--tk-selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .disassembler .is-pc {
|
||||||
|
color: var(--tk-selected-blur-text);
|
||||||
|
}
|
||||||
|
.tk.window.cpu .disassembler:focus-within .is-pc {
|
||||||
|
color: var(--tk-selected-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .registers {
|
||||||
|
background: var(--tk-window);
|
||||||
|
color : var(--tk-window-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .registers > * {
|
||||||
|
padding: 0 1px;
|
||||||
|
}
|
||||||
|
.tk.window.cpu .registers > *:first-child {
|
||||||
|
padding-top: 1px;
|
||||||
|
}
|
||||||
|
.tk.window.cpu .registers > *:last-child {
|
||||||
|
padding-bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .registers .expand .box {
|
||||||
|
border : none;
|
||||||
|
border-radius: 2px;
|
||||||
|
margin : 0 1px 0 0;
|
||||||
|
}
|
||||||
|
.tk.window.cpu .registers .expand .box:before {
|
||||||
|
background: transparent;
|
||||||
|
content : "";
|
||||||
|
display : block;
|
||||||
|
height : 11px;
|
||||||
|
width : 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .registers .expand[role="checkbox"] .box:before {
|
||||||
|
background : currentcolor;
|
||||||
|
mask : /**/url("./expand.svg") center no-repeat;
|
||||||
|
-webkit-mask: /**/url("./expand.svg") center no-repeat;
|
||||||
|
}
|
||||||
|
.tk.window.cpu .registers .expand[aria-checked="true"] .box:before {
|
||||||
|
background : currentcolor;
|
||||||
|
mask : /**/url("./collapse.svg") center no-repeat;
|
||||||
|
-webkit-mask: /**/url("./collapse.svg") center no-repeat;
|
||||||
|
}
|
||||||
|
.tk.window.cpu .registers .expand:focus .box {
|
||||||
|
background: var(--tk-control-active);
|
||||||
|
}
|
||||||
|
.tk.window.cpu .registers .main {
|
||||||
|
column-gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .registers .expansion {
|
||||||
|
gap : 1px 1em;
|
||||||
|
padding: 2px 2px 2px 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .registers .main {
|
||||||
|
column-gap: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .registers .text-box {
|
||||||
|
background: transparent;
|
||||||
|
border : none;
|
||||||
|
padding : 0 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .registers .text-box:focus {
|
||||||
|
outline: 1px solid var(--tk-selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .registers .text-dec {
|
||||||
|
column-gap: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .registers .text-dec .label {
|
||||||
|
text-align: center;
|
||||||
|
min-width : 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.cpu .registers *[aria-disabled="true"]:is(.label, .text-box) {
|
||||||
|
color: var(--tk-control-shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************************* Memory Window *******************************/
|
||||||
|
|
||||||
|
.tk.window.memory .client {
|
||||||
|
gap : 1px;
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.memory .hex-editor {
|
||||||
|
align-items: center;
|
||||||
|
background : var(--tk-window);
|
||||||
|
color : var(--tk-window-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.memory .hex-editor div {
|
||||||
|
cursor : default;
|
||||||
|
line-height: calc(1em + 2px);
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.memory .hex-editor .addr {
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.memory .hex-editor .byte {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
text-align : center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.memory .hex-editor .b0,
|
||||||
|
.tk.window.memory .hex-editor .b8 {
|
||||||
|
margin-left: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.memory .hex-editor .b15 {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window.memory .hex-editor .edit {
|
||||||
|
background: var(--tk-selected-blur);
|
||||||
|
color : var(--tk-selected-blur-text);
|
||||||
|
outline : 1px solid var(--tk-selected-blur);
|
||||||
|
}
|
||||||
|
.tk.window.memory .hex-editor:focus-within .edit {
|
||||||
|
background: var(--tk-selected);
|
||||||
|
color : var(--tk-selected-text);
|
||||||
|
outline : 1px solid var(--tk-selected);
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
:root {
|
||||||
|
--tk-control : #000000;
|
||||||
|
--tk-control-active : #550000;
|
||||||
|
--tk-control-border : #ff0000;
|
||||||
|
--tk-control-highlight : #550000;
|
||||||
|
--tk-control-shadow : #aa0000;
|
||||||
|
--tk-control-text : #ff0000;
|
||||||
|
--tk-desktop : #000000;
|
||||||
|
--tk-selected : #aa0000;
|
||||||
|
--tk-selected-blur : #550000;
|
||||||
|
--tk-selected-blur-text : #ff0000;
|
||||||
|
--tk-selected-text : #000000;
|
||||||
|
--tk-splitter-focus : #ff0000aa;
|
||||||
|
--tk-window : #000000;
|
||||||
|
--tk-window-blur-close : #000000;
|
||||||
|
--tk-window-blur-close-text : #aa0000;
|
||||||
|
--tk-window-blur-title : #000000;
|
||||||
|
--tk-window-blur-title2 : #000000;
|
||||||
|
--tk-window-blur-title-text : #aa0000;
|
||||||
|
--tk-window-close : #550000;
|
||||||
|
--tk-window-close-focus : #ff0000;
|
||||||
|
--tk-window-close-focus-text: #550000;
|
||||||
|
--tk-window-close-text : #ff0000;
|
||||||
|
--tk-window-text : #ff0000;
|
||||||
|
--tk-window-title : #550000;
|
||||||
|
--tk-window-title2 : #550000;
|
||||||
|
--tk-window-title-text : #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
input, select {
|
||||||
|
filter: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxmaWx0ZXIgaWQ9InYiPjxmZUNvbG9yTWF0cml4IGluPSJTb3VyY2VHcmFwaGljIiB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMSAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMSAwIiAvPjwvZmlsdGVyPjwvc3ZnPg==#v");
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar .unit-less,
|
||||||
|
.tk.scroll-bar .unit-more,
|
||||||
|
.tk.scroll-bar .thumb {
|
||||||
|
background: #550000;
|
||||||
|
}
|
||||||
|
.tk.scroll-bar .track {
|
||||||
|
background: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.scroll-bar:focus,
|
||||||
|
.tk.scroll-bar:focus .unit-less,
|
||||||
|
.tk.scroll-bar:focus .unit-more,
|
||||||
|
.tk.scroll-bar:focus .thumb {
|
||||||
|
background : #aa0000;
|
||||||
|
border-color: #ff0000;
|
||||||
|
color : #000000;
|
||||||
|
}
|
||||||
|
.tk.scroll-bar:focus .track {
|
||||||
|
background: #550000;
|
||||||
|
}
|
||||||
|
.tk.scroll-bar:focus .thumb {
|
||||||
|
box-shadow: 0 0 0 1px #ff0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tk.window {
|
||||||
|
box-shadow: 1px 1px 0 #550000;
|
||||||
|
}
|
||||||
|
.tk.window:focus-within {
|
||||||
|
box-shadow: 1px 1px 0 #aa0000;
|
||||||
|
}
|
||||||
@@ -0,0 +1,249 @@
|
|||||||
|
let register = Toolkit => Toolkit.App =
|
||||||
|
|
||||||
|
// Root application container and localization manager
|
||||||
|
class App extends Toolkit.Component {
|
||||||
|
|
||||||
|
///////////////////////// Initialization Methods //////////////////////////
|
||||||
|
|
||||||
|
constructor(options) {
|
||||||
|
super(null, Object.assign({
|
||||||
|
tabIndex: -1
|
||||||
|
}, options));
|
||||||
|
|
||||||
|
// Configure instance fields
|
||||||
|
this.components = new Set();
|
||||||
|
this.dragElement = null;
|
||||||
|
this.lastFocus = null;
|
||||||
|
this.locale = null;
|
||||||
|
this.locales = new Map();
|
||||||
|
|
||||||
|
// Configure event handlers
|
||||||
|
this.addEventListener("focusin", e=>this.onFocus(e));
|
||||||
|
this.addEventListener("keydown", e=>this.onKey (e));
|
||||||
|
this.addEventListener("keyup" , e=>this.onKey (e));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Event Handlers //////////////////////////////
|
||||||
|
|
||||||
|
// Child element focus gained
|
||||||
|
onFocus(e) {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (e.target != document.activeElement)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Target is self
|
||||||
|
if (e.target == this.element)
|
||||||
|
return this.restoreFocus();
|
||||||
|
|
||||||
|
// Ensure the child is not contained in a MenuBar
|
||||||
|
for (let elm = e.target; elm != this.element; elm = elm.parentNode) {
|
||||||
|
if (elm.getAttribute("role") == "menubar")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Track the (non-menu) element as the most recent focused component
|
||||||
|
this.lastFocus = e.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Key press, key release
|
||||||
|
onKey(e) {
|
||||||
|
if (this.dragElement == null || e.rerouted)
|
||||||
|
return;
|
||||||
|
this.dragElement.dispatchEvent(Object.assign(new Event(e.type), {
|
||||||
|
altKey : e.altKey,
|
||||||
|
ctrlKey : e.ctrlKey,
|
||||||
|
key : e.key,
|
||||||
|
rerouted: true,
|
||||||
|
shiftKey: e.shiftKey
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Public Methods //////////////////////////////
|
||||||
|
|
||||||
|
// Install a locale from URL
|
||||||
|
async addLocale(url) {
|
||||||
|
let data;
|
||||||
|
|
||||||
|
// Load the file as JSON, using UTF-8 with or without a BOM
|
||||||
|
try { data = JSON.parse(new TextDecoder().decode(
|
||||||
|
await (await fetch(url)).arrayBuffer() )); }
|
||||||
|
catch { return null; }
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (!data.id || !data.name)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
// Flatten the object to keys
|
||||||
|
let locale = new Map();
|
||||||
|
let entries = Object.entries(data);
|
||||||
|
let stack = [];
|
||||||
|
while (entries.length != 0) {
|
||||||
|
let entry = entries.shift();
|
||||||
|
|
||||||
|
// The value is a non-array object
|
||||||
|
if (entry[1] instanceof Object && !Array.isArray(entry[1])) {
|
||||||
|
entries = entries.concat(Object.entries(entry[1])
|
||||||
|
.map(e=>[ entry[0] + "." + e[0], e[1] ]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The value is a primitive or array
|
||||||
|
else locale.set(entry[0].toLowerCase(), entry[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.locales.set(data.id, locale);
|
||||||
|
return data.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Specify a localization dictionary
|
||||||
|
setLocale(id) {
|
||||||
|
if (!this.locales.has(id))
|
||||||
|
return false;
|
||||||
|
this.locale = this.locales.get(id);
|
||||||
|
for (let comp of this.components)
|
||||||
|
comp.localize();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Package Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Begin dragging on an element
|
||||||
|
get drag() { return this.dragElement; }
|
||||||
|
set drag(event) {
|
||||||
|
|
||||||
|
// Begin dragging
|
||||||
|
if (event) {
|
||||||
|
this.dragElement = event.currentTarget;
|
||||||
|
this.dragPointer = event.pointerId;
|
||||||
|
this.dragElement.setPointerCapture(event.pointerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// End dragging
|
||||||
|
else {
|
||||||
|
if (this.dragElement)
|
||||||
|
this.dragElement.releasePointerCapture(this.dragPointer);
|
||||||
|
this.dragElement = null;
|
||||||
|
this.dragPointer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure components for automatic localization, or localize a message
|
||||||
|
localize(a, b) {
|
||||||
|
return a instanceof Object ? this.localizeComponents(a, b) :
|
||||||
|
this.localizeMessage(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return focus to the most recent focused element
|
||||||
|
restoreFocus() {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (!this.lastFocus)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Unable to restore focus
|
||||||
|
if (!this.isVisible(this.lastFocus))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Transfer focus to the most recent element
|
||||||
|
this.lastFocus.focus({ preventScroll: true });
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////// Private Methods /////////////////////////////
|
||||||
|
|
||||||
|
// Configure components for automatic localization
|
||||||
|
localizeComponents(comps, add) {
|
||||||
|
|
||||||
|
// Process all components
|
||||||
|
for (let comp of (Array.isArray(comps) ? comps : [comps])) {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (
|
||||||
|
!(comp instanceof Toolkit.Component) ||
|
||||||
|
!(comp.localize instanceof Function)
|
||||||
|
) continue;
|
||||||
|
|
||||||
|
// Update the collection and component text
|
||||||
|
this.components[add ? "add" : "delete"](comp);
|
||||||
|
comp.localize();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Localize a message
|
||||||
|
localizeMessage(message, substs, circle = new Set()) {
|
||||||
|
let parts = [];
|
||||||
|
|
||||||
|
// Separate the substitution keys from the literal text
|
||||||
|
for (let x = 0;;) {
|
||||||
|
|
||||||
|
// Locate the start of the next substitution key
|
||||||
|
let y = message.indexOf("{", x);
|
||||||
|
let z = y == -1 ? -1 : message.indexOf("}", y + 1);
|
||||||
|
|
||||||
|
// No substitution key or malformed substitution expression
|
||||||
|
if (z == -1) {
|
||||||
|
parts.push(message.substring(z == -1 ? x : y));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append the literal text and the substitution key
|
||||||
|
parts.push(message.substring(x, y), message.substring(y + 1, z));
|
||||||
|
x = z + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process all substitutions
|
||||||
|
for (let x = 1; x < parts.length; x += 2) {
|
||||||
|
let key = parts[x].toLowerCase();
|
||||||
|
let value;
|
||||||
|
|
||||||
|
// The substitution key is already in the recursion chain
|
||||||
|
if (circle.has(key)) {
|
||||||
|
parts[x] = "{\u21ba" + key.toUpperCase() + "}";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the substitution key from the argument
|
||||||
|
if (substs && substs.has(key)) {
|
||||||
|
value = substs.get(key);
|
||||||
|
|
||||||
|
// Do not recurse for this substitution
|
||||||
|
if (!value[1]) {
|
||||||
|
parts[x] = value[0];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Substitution text
|
||||||
|
value = value[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resolve the substitution from the current locale
|
||||||
|
else if (this.locale && this.locale.has(key))
|
||||||
|
value = this.locale.get(key);
|
||||||
|
|
||||||
|
// A matching substitution key was not found
|
||||||
|
else {
|
||||||
|
parts[x] = "{\u00d7" + key.toUpperCase() + "}";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform recursive substitution
|
||||||
|
circle.add(key);
|
||||||
|
parts[x] = this.localizeMessage(value, substs, circle);
|
||||||
|
circle.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
export { register };
|
||||||