diff --git a/expressions.html b/expressions.html
new file mode 100644
index 0000000..a9f4b1f
--- /dev/null
+++ b/expressions.html
@@ -0,0 +1,696 @@
+
+
+
+
+ Expression Evaluator
+
+
+
+
+Expression Evaluator
+
+ 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.
+
+
+ Expressions are organized into series of tokens with three main modes of
+ significance:
+
+
+
+ • Value |
+
+ 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.
+ |
+
+
+ • Operator |
+
+ 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.
+ |
+
+
+ • Group |
+
+ Operand groups, mainly through the use of parentheses () ,
+ override the default order of operations. Anything enclosed in a group
+ is processed before any operations adjacent to that group.
+ |
+
+
+
+ Expressions are case-insensitive.
+
+
+
+
+Values
+
+ Values come in two forms:
+
+
+
+ • Literal |
+ Provided directly by the expression. |
+
+
+ • Symbol |
+ Refers to something in the current emulation state. |
+
+
+
+ Values may be one of four data types:
+
+
+
+ • Signed Word |
+
+ 32-bit, two's complement integer.
+ |
+
+
+ • Unsigned Word |
+
+ 32-bit, unsigned integer.
+ |
+
+
+ • Float |
+
+ 32-bit floating short in IEEE-754 format.
+ |
+
+
+ • Boolean |
+
+ 1-bit value representing whether a condition is true or false.
+ |
+
+
+
+ Signed word and unsigned word are represented as sequences of character
+ digits. A numeric token that begins with the characters 0x
+ will be interpreted as hexadecimal, allowing letters A
through
+ F
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.
+
+
+ Floats, like words, are represented as sequences of character digits. They
+ are differentiated from words by the presence of a dot .
+ 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 same
+ restrictions as on the Virtual Boy's CPU: if the result of any float
+ operation is NaN, an infinity, a denormal number or negative zero, it will be
+ changed to positive zero.
+
+
+ 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 0
if false or the value 1
if true. Boolean
+ literals are specified with the named values true
and
+ false
within the expression. In a boolean operation, any result
+ that is zero is considered false, and any non-zero value is considered true.
+
+
+ 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.
+
+
+ The following symbols may be used for accessing information about the current
+ instruction being executed:
+
+
+
+ address |
+ Current memory access's address. |
+
+
+ break |
+
+ Identifies what type of break scenario is being considered. See below for
+ a list of identifiers.
+ |
+
+
+ code |
+ Current exception's exception code. |
+
+
+ cond |
+
+ Condition code for Bcond and SETF
+ instructions. See below for a list of conditions.
+ |
+
+
+ disp |
+ Displacement offset for jumps and memory accesses. |
+
+
+ fetch |
+
+ Data unit index during a fetch operation, or -1 if the read
+ operation is not a fetch.
+ |
+
+
+ format |
+ Current instruction's encoding format. |
+
+
+ id |
+
+ Identifies which specific instruction is being executed, considering
+ opcode and, where applicable, subopcode . See
+ below for a list of identifiers.
+ |
+
+
+ imm |
+ Immediate operand. |
+
+
+ opcode |
+ Current instruction's top-level opcode. |
+
+
+ reg1 |
+ Source register index. |
+
+
+ reg2 |
+ Destination register index. |
+
+
+ regid |
+
+ System register index in LDSR and STSR
+ instructions.
+ |
+
+
+ size |
+ Number of bytes occupied by the current instruction. |
+
+
+ subopcode |
+ Current instruction's secondary opcode. |
+
+
+ type |
+ Data type of the current memory access. |
+
+
+ value |
+ Value read by the current memory access. |
+
+
+ vector |
+ Vector for TRAP instructions. |
+
+
+
+ The following symbols may be used in conjunction with the break
+ symbol:
+
+
+
exception
execute
read
write
+
+
+ The following symbols may be used in conjunction with the cond
+ symbol:
+
+
+
c
e
f
ge
gt
+
h
l
le
lt
n
+
nc
ne
nh
nl
nv
+
nz
p
t
v
z
+
+
+ The following symbols may be used in conjunction with the id
+ symbol:
+
+
+
illegal
add_imm
add_reg
addf.s
+
addi
and
andbsu
andi
+
andnbsu
bcond
caxi
cli
+
cmp_imm
cmp_reg
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_imm
+
mov_reg
movbsu
movea
movhi
+
mpyhw
mul
mulf.s
mulu
+
not
notbsu
or
orbsu
+
ori
ornbsu
out.b
out.h
+
out.w
reti
rev
sar_imm
+
sar_reg
sch0bsd
sch0bsu
sch1bsd
+
sch1bsu
sei
setf
shl_imm
+
shl_reg
shr_imm
shr_reg
st.b
+
st.h
st.w
stsr
sub
+
subf.s
trap
trnc.sw
xb
+
xh
xor
xorbsu
xori
+
xornbsu
+
+
+ The following symbols may be used to retrieve the contents of a CPU program
+ register:
+
+
+
r0
r1
r2
r3
r4
+
r5
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
r31
gp
hp
lp
+
sp
tp
+
+
+ The following symbols may be used to retrieve the contents of a CPU system
+ register:
+
+
+
adtre
chcw
ecr
eipc
+
eipsw
fepc
fepsw
pc
+
pir
psw
tkcw
sr29
+
sr30
sr31
+
+
+
+Operators
+
+ 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. 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 depends on the "greater" of the two types, in the following order
+ (higher is "greater"):
+
+
+ ↑ | Float |
+ | Unsigned word |
+ | Signed word |
+ ↓ | Boolean |
+
+
+ For example, if an operation contains both a signed word and a float, the
+ signed word value is first converted to float.
+
+
+ Operators have assigned precedence that specifies the order of operations.
+ For example, in the expression 1 + 2 * 3
, the multiplication
+ happens before the addition because it has higher precedence. Parentheses
+ ()
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 (1 + 2) * 3
, the
+ addition happens before the multiplication because it is enclosed in
+ parentheses.
+
+
+ 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 in the order they appear in
+ the expression from left to right.
+
+
+
+ ~ |
+ Not Bitwise |
+ Cannot be used with a float value. |
+
+
+ ! |
+ Not Logical |
+ Always produces a boolean. |
+
+
+ - |
+ Negate |
+ Cannot be used with an unsigned word value. |
+
+
+ bool |
+ Cast to Boolean |
+ |
+
+
+ ceil |
+ Round Up |
+ |
+
+
+ float |
+ Cast to Float |
+ |
+
+
+ floor |
+ Round Down |
+ |
+
+
+ round |
+ Round to Nearest |
+ |
+
+
+ s8 , byte |
+ Cast to Signed Byte |
+ Result is of type signed word. |
+
+
+ s16 , halfword |
+ Cast to Signed Halfword |
+ Result is of type signed word. |
+
+
+ s32 , word |
+ Cast to Signed Word |
+ |
+
+
+ trunc |
+ Truncate |
+ Removes any fraction. |
+
+
+ u8 , ubyte
|
+ Cast to Unsigned Byte |
+ Result is of type unsigned word. |
+
+
+ u16 , uhalfword |
+ Cast to Unsigned Halfword |
+ Result is of type unsigned word. |
+
+
+ u32 , uword |
+ Cast to Unsigned Word |
+ |
+
+
+ xfloat |
+ Reinterpret as Float |
+ The binary value is not modified. |
+
+
+ xs32 , xword |
+ Reinterpret as Signed Word |
+ The binary value is not modified. |
+
+
+ xu32 , xuword |
+ Reinterpret as Unsgned Word |
+ The binary value is not modified. |
+
+
+ / |
+ Divide |
+ Zero divisor yields zero as result. |
+
+
+ * |
+ Multiply |
+ |
+
+
+ % |
+ Remainder |
+ Zero divisor yields zero as result. |
+
+
+ + |
+ Add |
+ |
+
+
+ - |
+ Subtract |
+ |
+
+
+ << |
+ Shift Left |
+ Cannot be used with a float value. |
+
+
+ >> |
+ Shift Right Arithmetic |
+ Cannot be used with a float value. |
+
+
+ >>> |
+ Shift Right Logical |
+ Cannot be used with a float value. |
+
+
+ > |
+ Greater |
+ Always produces a boolean. |
+
+
+ >= |
+ Greater or Equal |
+ Always produces a boolean. |
+
+
+ < |
+ Less |
+ Always produces a boolean. |
+
+
+ <= |
+ Less or Equal |
+ Always produces a boolean. |
+
+
+ == |
+ Equal |
+ Always produces a boolean. |
+
+
+ != |
+ Not Equal |
+ Always produces a boolean. |
+
+
+ & |
+ And Bitwise |
+ Cannot be used with a float value. |
+
+
+ ^ |
+ Exclusive Or Bitwise |
+ Cannot be used with a float value. |
+
+
+ | |
+ Or Bitwise |
+ Cannot be used with a float value. |
+
+
+ && |
+ And Logical |
+ If left is true, returns right; else returns left. |
+
+
+ ^^ |
+ Exclusive Or Logical |
+ If only one operand is true, returns the truthy value. |
+
+
+ || |
+ Or Logical |
+ If left is true, returns left; else returns right. |
+
+
+
+
+
+Memory Read
+
+ 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 []
. This functions in an identical manner to
+ parentheses ()
, but will additionally perform the read
+ operation.
+
+
+ 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:
+
+
+
+ • float |
+ Behaves like xfloat instead. |
+
+
+ • s8 , u8 |
+ Performs a byte read of the specified signedness. |
+
+
+ • s16 , u16 |
+ Performs a halfword read of the specified signedness. |
+
+
+
+
+
diff --git a/src/core/cpu.c b/src/core/cpu.c
index b99829a..f862569 100644
--- a/src/core/cpu.c
+++ b/src/core/cpu.c
@@ -116,8 +116,8 @@ static vbool cpuFloatReserved(Vue *vue, vbool left) {
)) continue;
/* The value is a reserved operand */
- vue->cpu.exception.code = 0xFF60;
- vue->cpu.psw_fro = 1;
+ vue->cpu.exception = 0xFF60;
+ vue->cpu.psw_fro = 1;
return VUE_TRUE;
}
@@ -150,7 +150,7 @@ static void cpuFloatConvert(Vue *vue, vbool round) {
/* Invalid operation (word overflow) */
else if (bits >= 8) {
- vue->cpu.exception.code = 0xFF70;
+ vue->cpu.exception = 0xFF70;
return;
}
@@ -193,8 +193,8 @@ static void cpuFloatResult(Vue *vue, vbool compare, double full) {
/* Overflow */
if (full > result || full < -result) {
- vue->cpu.exception.code = 0xFF64;
- vue->cpu.psw_fov = 1;
+ vue->cpu.exception = 0xFF64;
+ vue->cpu.psw_fov = 1;
return;
}
@@ -578,7 +578,7 @@ static void cpuDIV(Vue *vue) {
/* Zero division */
if (right == 0) {
- vue->cpu.exception.code = 0xFF80;
+ vue->cpu.exception = 0xFF80;
return;
}
@@ -612,7 +612,7 @@ static void cpuDIVF_S(Vue *vue) {
/* An exception has occurred */
if (!right) {
- vue->cpu.exception.code = left ? 0xFF68 : 0xFF70;
+ vue->cpu.exception = left ? 0xFF68 : 0xFF70;
return;
}
@@ -628,7 +628,7 @@ static void cpuDIVU(Vue *vue) {
/* Zero division */
if (right == 0) {
- vue->cpu.exception.code = 0xFF80;
+ vue->cpu.exception = 0xFF80;
return;
}
@@ -848,7 +848,7 @@ static void cpuREV(Vue *vue) {
/* Trap */
#define cpuTRAP(vue) \
- vue->cpu.exception.code = 0xFFA0 | (vue->cpu.inst.imm & 15), \
+ vue->cpu.exception = 0xFFA0 | (vue->cpu.inst.imm & 15), \
vue->cpu.pc += 2
/* Truncate Short Floating to Word */
@@ -908,8 +908,12 @@ static void cpuDecode(VueInstruction *inst) {
x = inst->bits >> 16 & 31;
inst->reg2 = inst->bits >> 21 & 31;
inst->imm = extend < 0 ? SIGN_EXTEND(5, x) : x;
- if (inst->id == BITSTRING)
+ if (inst->id == BITSTRING) {
inst->id = x >= 16 ? VUE_ILLEGAL : LOOKUP_BITSTRING[x];
+ inst->subopcode = x;
+ }
+ if (inst->id == VUE_SETF)
+ inst->cond = x & 15;
break;
case 3:
x = inst->bits >> 16 & 0x1FF;
@@ -944,21 +948,84 @@ static void cpuDecode(VueInstruction *inst) {
}
+/* Operations for exception stage */
+static vbool cpuException(Vue *vue) {
+ vbool isIRQ; /* The exception is an interrupt */
+ int32_t psw; /* Current value of PSW */
+
+ /* Application callback */
+ if (vue->onException != NULL) {
+ vue->breakCode = vue->onException(vue, vue->cpu.exception);
+ if (vue->breakCode != 0)
+ return VUE_TRUE;
+ }
+
+ /* Configure working variables */
+ vue->cpu.exception &= 0xFFFF;
+ isIRQ = (vue->cpu.exception & 0xFF00) == 0xFE00;
+ psw = cpuGetSystemRegister(vue, VUE_PSW);
+
+ /* Fatal exception */
+ if (vue->cpu.psw_np != 0) {
+ vueWrite(vue, 0x00000000, VUE_S32, 0xFFFF0000 | vue->cpu.exception);
+ vueWrite(vue, 0x00000004, VUE_S32, psw);
+ vueWrite(vue, 0x00000008, VUE_S32, vue->cpu.pc);
+ vue->cpu.stage = CPU_FATAL;
+ return VUE_TRUE;
+ }
+
+ /* Duplexed exception */
+ if (vue->cpu.psw_ep != 0) {
+ vue->cpu.ecr_fecc = vue->cpu.exception;
+ vue->cpu.fepc = vue->cpu.pc;
+ vue->cpu.fepsw = psw;
+ vue->cpu.psw_np = 1;
+ vue->cpu.pc = 0xFFFFFFD0;
+ }
+
+ /* Regular exception */
+ else {
+ vue->cpu.ecr_eicc = vue->cpu.exception;
+ vue->cpu.eipc = vue->cpu.pc;
+ vue->cpu.eipsw = psw;
+ vue->cpu.psw_ep = 1;
+ vue->cpu.pc = 0xFFFF0000 | (vue->cpu.exception & 0xFFF0);
+ if (vue->cpu.pc == (int32_t) 0xFFFFFF70) /* FIV */
+ vue->cpu.pc = 0xFFFFFF60;
+ }
+
+ /* Interrupt */
+ if (isIRQ) {
+ psw = vue->cpu.exception >> 4 & 15;
+ vue->cpu.psw_i = psw < 15 ? psw : 15;
+ if (vue->cpu.stage == CPU_HALT)
+ vue->cpu.pc += 2;
+ }
+
+ /* Common processing */
+ vue->cpu.cycles = 0; /* TODO: Determine the actual number */
+ vue->cpu.exception = 0;
+ vue->cpu.psw_ae = 0;
+ vue->cpu.psw_id = 1;
+ vue->cpu.stage = CPU_FETCH;
+ return VUE_FALSE;
+}
+
/* Check for an exception or interrupt */
static vbool cpuTestException(Vue *vue) {
int32_t level; /* Interrupt level */
/* Check for an interrupt */
- if (vue->cpu.irq != 0 && (vue->cpu.exception.code |
+ if (vue->cpu.irq != 0 && (vue->cpu.exception |
vue->cpu.psw_id | vue->cpu.psw_ep | vue->cpu.psw_np) == 0) {
for (level = 4; level >= 0; level--)
if ((vue->cpu.irq >> level & 1) != 0)
break;
- vue->cpu.exception.code = 0xFE00 | level << 4;
+ vue->cpu.exception = 0xFE00 | level << 4;
}
/* There is no exception */
- if (vue->cpu.exception.code == 0) /* No further processing */
+ if (vue->cpu.exception == 0) /* No further processing */
return vue->cpu.stage == CPU_HALT && vue->cpu.cycles == 0;
/* An exception has occurred */
@@ -975,8 +1042,6 @@ static vbool cpuExecute(Vue *vue) {
vue->breakCode = vue->onExecute(vue, &vue->cpu.inst);
if (vue->breakCode != 0)
return VUE_TRUE;
- vue->cpu.inst.reg1 &= 31;
- vue->cpu.inst.reg2 &= 31;
}
/* Processing by instruction ID */
@@ -1058,11 +1123,11 @@ static vbool cpuExecute(Vue *vue) {
case VUE_XORI : cpuXORI (vue); break;
/*case VUE_XORNBSU: cpuXORNBSU(vue); break;*/
default: /* Invalid instruction */
- vue->cpu.exception.code = 0xFF90;
+ vue->cpu.exception = 0xFF90;
}
/* Common processing */
- if (vue->cpu.exception.code == 0) {
+ if (vue->cpu.exception == 0) {
vue->cpu.cycles += CYCLES[vue->cpu.inst.id];
vue->cpu.pc += vue->cpu.inst.size;
}
@@ -1082,10 +1147,15 @@ static int32_t cpuSize(int32_t opcode) {
/* Operations for fetch stage */
static vbool cpuFetch(Vue *vue) {
+ /* Entering the fetch stage */
+ if (vue->cpu.fetch == -1)
+ vue->cpu.fetch = 0;
+
/* Read the bits from the bus */
if (cpuRead(vue, vue->cpu.pc + (vue->cpu.fetch << 1),
VUE_U16, vue->cpu.fetch))
return VUE_TRUE;
+ /* TODO: Determine how many cycles this takes */
/* First unit */
if (vue->cpu.fetch == 0) {
@@ -1099,7 +1169,7 @@ static vbool cpuFetch(Vue *vue) {
/* Second unit */
else {
vue->cpu.inst.bits |= vue->cpu.access.value & 0xFFFF;
- vue->cpu.fetch = 0;
+ vue->cpu.fetch = -1;
}
/* Decode the instruction and advance to execute stage */
@@ -1108,94 +1178,30 @@ static vbool cpuFetch(Vue *vue) {
return VUE_FALSE;
}
-/* Operations for exception stage */
-static vbool cpuException(Vue *vue) {
- vbool isIRQ; /* The exception is an interrupt */
- int32_t psw; /* Current value of PSW */
-
- /* Application callback */
- if (vue->onException != NULL) {
- vue->breakCode = vue->onException(vue, &vue->cpu.exception);
- if (vue->breakCode != 0)
- return VUE_TRUE;
- }
-
- /* Configure working variables */
- vue->cpu.exception.code &= 0xFFFF;
- isIRQ = (vue->cpu.exception.code & 0xFF00) == 0xFE00;
- psw = cpuGetSystemRegister(vue, VUE_PSW);
-
- /* Fatal exception */
- if (vue->cpu.psw_np != 0) {
- vueWrite(vue, 0x00000000, VUE_S32, 0xFFFF0000|vue->cpu.exception.code);
- vueWrite(vue, 0x00000004, VUE_S32, psw);
- vueWrite(vue, 0x00000008, VUE_S32, vue->cpu.pc);
- vue->cpu.stage = CPU_FATAL;
- return VUE_TRUE;
- }
-
- /* Duplexed exception */
- if (vue->cpu.psw_ep != 0) {
- vue->cpu.ecr_fecc = vue->cpu.exception.code;
- vue->cpu.fepc = vue->cpu.pc;
- vue->cpu.fepsw = psw;
- vue->cpu.psw_np = 1;
- vue->cpu.pc = 0xFFFFFFD0;
- }
-
- /* Regular exception */
- else {
- vue->cpu.ecr_eicc = vue->cpu.exception.code;
- vue->cpu.eipc = vue->cpu.pc;
- vue->cpu.eipsw = psw;
- vue->cpu.psw_ep = 1;
- vue->cpu.pc = 0xFFFF0000 | (vue->cpu.exception.code & 0xFFF0);
- if (vue->cpu.pc == (int32_t) 0xFFFFFF70) /* FIV */
- vue->cpu.pc = 0xFFFFFF60;
- }
-
- /* Interrupt */
- if (isIRQ) {
- psw = vue->cpu.exception.code >> 4 & 15;
- vue->cpu.psw_i = psw < 15 ? psw : 15;
- if (vue->cpu.stage == CPU_HALT)
- vue->cpu.pc += 2;
- }
-
- /* Common processing */
- vue->cpu.cycles = 0; /* TODO: Determine the actual number */
- vue->cpu.exception.code = 0;
- vue->cpu.psw_ae = 0;
- vue->cpu.psw_id = 1;
- vue->cpu.stage = CPU_FETCH;
- return VUE_FALSE;
-}
-
/* Process the simulation */
-static void cpuEmulate(Vue *vue, int32_t cycles) {
+static void cpuEmulate(Vue *vue, uint32_t cycles) {
- /* The CPU is halting */
- if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT)
+ /* The CPU is in fatal halt status */
+ if (vue->cpu.stage == CPU_FATAL)
return;
-vue->cpu.cycles = 0; /* DEBUG: Stop processing after execute */
-
/* Process for the given number of cycles */
for (;;) {
- /* The next event occurs after the given number of cycles */
+ /* The next stage occurs after the given number of cycles */
if (vue->cpu.cycles > cycles) {
vue->cpu.cycles -= cycles;
return;
}
/* Processing by stage */
+ cycles -= vue->cpu.cycles;
vue->cpu.cycles = 0;
switch (vue->cpu.stage) {
- case CPU_EXCEPTION: if (cpuException (vue)) return; break;
- case CPU_EXECUTE : if (cpuExecute (vue)) return; break;
- case CPU_FETCH : if (cpuFetch (vue)) return; break;
- case CPU_HALT : if (cpuTestException(vue)) return; break;
+ case CPU_EXCEPTION: if (cpuException(vue)) return; break;
+ case CPU_EXECUTE : if (cpuExecute (vue)) return; break;
+ case CPU_FETCH : if (cpuFetch (vue)) return; break;
+ case CPU_HALT : cpuTestException(vue); return;
}
}
@@ -1207,11 +1213,11 @@ static void cpuReset(Vue *vue) {
int32_t x;
/* Configure instance fields */
- vue->cpu.cycles = 0;
- vue->cpu.exception.code = 0;
- vue->cpu.fetch = 0;
- vue->cpu.irq = 0;
- vue->cpu.stage = CPU_FETCH;
+ vue->cpu.cycles = 0;
+ vue->cpu.exception = 0;
+ vue->cpu.fetch = -1;
+ vue->cpu.irq = 0;
+ vue->cpu.stage = CPU_FETCH;
/* Clear all registers (hardware only sets ECR, PC and PSW) */
for (x = 0; x < 32; x++) {
@@ -1229,12 +1235,10 @@ static void cpuReset(Vue *vue) {
vue->cpu.psw_np = 1;
}
-/* Determine the number of CPU cycles until something can happen */
-static int32_t cpuUntil(Vue *vue, int32_t cycles) {
- if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT)
- return cycles;
- return cycles < 0 ? vue->cpu.cycles :
- cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles;
+/* Determine the number of CPU cycles until a breakpoint could trigger */
+static uint32_t cpuUntil(Vue *vue, uint32_t cycles) {
+ return vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT ?
+ cycles : cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles;
}
diff --git a/src/core/include/vue.h b/src/core/include/vue.h
index 197b88c..2a87b74 100644
--- a/src/core/include/vue.h
+++ b/src/core/include/vue.h
@@ -154,11 +154,6 @@ typedef struct {
int8_t type; /* Data type */
} VueAccess;
-/* Exception state */
-typedef struct {
- uint16_t code; /* Exception code */
-} VueException;
-
/* Instruction state */
typedef struct {
int32_t bits; /* Binary encoding */
@@ -174,31 +169,32 @@ typedef struct {
uint8_t subopcode; /* Instruction subopcode */
} VueInstruction;
-/* Callbacks */
-typedef int32_t (*VueOnException)(Vue *, VueException *);
+/* 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 (*VueOnRead )(Vue *, VueAccess *);
-typedef int32_t (*VueOnWrite )(Vue *, VueAccess *);
+typedef int32_t (*VueOnFrame )(Vue * );
/* Emulation state */
struct Vue {
int32_t breakCode; /* Application break code */
uint8_t wram[0x10000]; /* System memory */
- /* Callback handlers */
+ /* Breakpoint handlers */
VueOnException onException;
VueOnExecute onExecute;
- VueOnRead onRead;
- VueOnWrite onWrite;
+ VueOnFrame onFrame;
+ VueOnAccess onRead;
+ VueOnAccess onWrite;
/* CPU state */
struct {
VueAccess access; /* Access state */
- VueException exception; /* Exception state */
VueInstruction inst; /* Instruction state */
- int32_t cycles; /* Cycles until next stage */
+ uint32_t cycles; /* Cycles until next stage */
int32_t jumpFrom[3]; /* Source PCs of most recent jumps */
int32_t 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 */
@@ -265,21 +261,23 @@ struct Vue {
* Function Prototypes *
*****************************************************************************/
-VUEAPI int32_t vueEmulate (Vue *vue, int32_t maxCycles);
-VUEAPI int32_t vueGetBreakCode(Vue *vue);
-VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system);
-VUEAPI void vueInitialize (Vue *vue);
-VUEAPI int32_t vueRead (Vue *vue, uint32_t address, int32_t type);
-VUEAPI vbool vueReadBytes (Vue *vue, uint32_t address, uint8_t *dest, uint32_t length);
-VUEAPI void vueReset (Vue *vue);
-VUEAPI void vueSetException(Vue *vue, VueOnException callback);
-VUEAPI void vueSetExecute (Vue *vue, VueOnExecute callback);
-VUEAPI void vueSetRead (Vue *vue, VueOnRead callback);
-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 vueSetWrite (Vue *vue, VueOnWrite callback);
-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);
+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 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 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);
diff --git a/src/core/vue.c b/src/core/vue.c
index f24cbb1..225829b 100644
--- a/src/core/vue.c
+++ b/src/core/vue.c
@@ -155,55 +155,46 @@ static void writeBytes(uint8_t *data, uint32_t datlen, uint32_t address,
*****************************************************************************/
/* Process the simulation */
-int32_t vueEmulate(Vue *vue, int32_t maxCycles) {
- int32_t cycles; /* Number of cycles to process */
+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 during which nothing will happen */
- cycles = -1;
+ /* 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);*/
- /*cycles = vsuUntil (vue, cycles);*/
- cycles = cpuUntil (vue, cycles);
-
- /* Range checking */
- if (cycles == -1) /* No activity on any component */
- break;
- if (maxCycles >= 0) /* Restrict to given number of cycles */
- cycles = cycles < maxCycles ? cycles : maxCycles;
/* 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);*/
- cpuEmulate (vue, cycles);
-
- /* An application break was requested */
- if (vue->breakCode != 0)
- break;
-
- /* Update the number of cycles remaining */
- if (maxCycles >= 0)
- maxCycles -= cycles;
- } while (maxCycles != 0);
+ maxCycles -= cycles;
+ } while (vue->breakCode == 0 && maxCycles != 0);
/* A break condition has occurred */
return maxCycles;
}
-/* Retrieve the application break code */
+/* 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) {
@@ -234,17 +225,69 @@ void vueInitialize(Vue *vue) {
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 vueSetRead(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)
+ if (vue == NULL || type < 0 || type > 4)
return 0;
/* Perform the operation */
@@ -299,28 +342,10 @@ void vueReset(Vue *vue) {
/* Reset state */
cpuReset(vue);
pakReset(vue);
- for (x = 0; x < 0x1000; x++)
+ for (x = 0; x < 0x10000; x++)
vue->wram[x] = 0;
}
-/* Specify an exception breakpoint callback */
-void vueSetException(Vue *vue, VueOnException callback) {
- if (vue != NULL)
- vue->onException = callback;
-}
-
-/* Specify an execute breakpoint callback */
-void vueSetExecute(Vue *vue, VueOnExecute callback) {
- if (vue != NULL)
- vue->onExecute = callback;
-}
-
-/* Specify a read breakpoint callback */
-void vueSetRead(Vue *vue, VueOnRead callback) {
- if (vue != NULL)
- vue->onRead = callback;
-}
-
/* Specify a value for a register */
int32_t vueSetRegister(Vue *vue, int32_t index, vbool system, int32_t value) {
return vue == NULL ? 0 :
@@ -348,12 +373,6 @@ vbool vueSetROM(Vue *vue, uint8_t *rom, uint32_t size) {
return VUE_TRUE;
}
-/* Specify a write breakpoint callback */
-void vueSetWrite(Vue *vue, VueOnRead callback) {
- if (vue != NULL)
- vue->onWrite = callback;
-}
-
/* Write a value to the CPU bus */
void vueWrite(Vue *vue, uint32_t address, int32_t type, int32_t value) {
diff --git a/src/desktop/Main.java b/src/desktop/Main.java
index 960fe6e..2aa4ec5 100644
--- a/src/desktop/Main.java
+++ b/src/desktop/Main.java
@@ -49,43 +49,7 @@ public class Main {
useNative = true;
// Begin application operations
- //new App(useNative);
-
- var brk = new Breakpoint(null);
-
- /*
- //String exp =
- // "([sp - 8] & 3) << 6 != 12.0 + r6 * ecr && [0x0500008C + r8] == 0";
- //String exp = "float true + -3 * 4";
- String exp = "1100 ^ uword 1010";
- System.out.println("\n" + exp);
- if (brk.setCondition(exp))
- System.out.println(brk.debugTokens());
- else {
- var err = brk.getConditionError();
- System.out.println("Error " +
- err.code + "\t" +
- err.position + ":" +
- err.text
- );
- }
- */
-
- /*
- exp = "123, 456 - 987 , f0-fffffff2";
- System.out.println("\n" + exp);
- if (brk.setAddresses(exp))
- System.out.println(brk.debugRanges());
- else {
- var err = brk.getAddressError();
- System.out.println("Error " +
- err.code + "\t" +
- err.position + ":" +
- err.text
- );
- }
- */
-
+ new App(useNative);
}
}
diff --git a/src/desktop/app/App.java b/src/desktop/app/App.java
index fb760c6..61f5c4f 100644
--- a/src/desktop/app/App.java
+++ b/src/desktop/app/App.java
@@ -74,7 +74,7 @@ public class App {
// Specify whether using the native module
boolean setUseNative(boolean useNative) {
- return this.useNative = useNative && Vue.isNativeLoaded();
+ return this.useNative = useNative && Vue.getNativeID() != null;
}
diff --git a/src/desktop/app/ChildWindow.java b/src/desktop/app/ChildWindow.java
index c7ffe50..f60423b 100644
--- a/src/desktop/app/ChildWindow.java
+++ b/src/desktop/app/ChildWindow.java
@@ -31,6 +31,7 @@ class ChildWindow extends JInternalFrame {
addInternalFrameListener(Util.onClose2(e->setVisible(false)));
setClosable(true);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
+ setMaximizable(true);
setResizable(true);
}
diff --git a/src/desktop/app/DisassemblerPane.java b/src/desktop/app/DisassemblerPane.java
index 75653f9..dd6e832 100644
--- a/src/desktop/app/DisassemblerPane.java
+++ b/src/desktop/app/DisassemblerPane.java
@@ -63,6 +63,7 @@ class DisassemblerPane extends JScrollPane {
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.setBackground(SystemColor.window);
client.setFocusable(true);
@@ -98,6 +99,7 @@ class DisassemblerPane extends JScrollPane {
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;
@@ -116,16 +118,37 @@ class DisassemblerPane extends JScrollPane {
}
// 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:
- parent.parent.vue.emulate(0);
+ step.setCondition("pc!=" + pc);
+ step.setEnabled(true);
+ vue.emulate(20000000);
+ if (step.evaluate())
+ step.setEnabled(false);
parent.parent.refreshDebug();
- int pc = parent.parent.vue.getRegister(Vue.PC, true);
+ pc = vue.getRegister(Vue.PC, true);
+ if (!isVisible(pc))
+ seek(pc, count / 3);
+ break;
+
+ // Run to Next
+ case KeyEvent.VK_F12:
+ step.setCondition("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();
+ pc = vue.getRegister(Vue.PC, true);
if (!isVisible(pc))
seek(pc, count / 3);
break;
@@ -133,6 +156,11 @@ class DisassemblerPane extends JScrollPane {
}
+ // 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;
diff --git a/src/desktop/app/MainWindow.java b/src/desktop/app/MainWindow.java
index 543ce0a..265823e 100644
--- a/src/desktop/app/MainWindow.java
+++ b/src/desktop/app/MainWindow.java
@@ -15,8 +15,9 @@ import vue.*;
class MainWindow extends JFrame {
// Instance fields
- App app; // Containing application
- Vue vue; // Emulation core context
+ App app; // Containing application
+ Breakpoint brkStep; // Single step internal breakpoint
+ Vue vue; // Emulation core context
// Private fields
private boolean debugMode; // Window is in debug mode
@@ -97,6 +98,10 @@ class MainWindow extends JFrame {
desktop.add(cpu = new CPUWindow (this));
desktop.add(memory = new MemoryWindow (this));
+ // Configure internal breakpoints
+ brkStep = vue.breakpoint();
+ brkStep.setExecute(true);
+
// Display window
refreshDebug();
pack();
diff --git a/src/desktop/vue/Breakpoint.java b/src/desktop/vue/Breakpoint.java
index 1515485..16e3df9 100644
--- a/src/desktop/vue/Breakpoint.java
+++ b/src/desktop/vue/Breakpoint.java
@@ -63,20 +63,23 @@ public class Breakpoint {
// Functional symbol IDs
private static final int ADDRESS = 0;
- private static final int CODE = 1;
- private static final int COND = 2;
- private static final int DISP = 3;
- private static final int FORMAT = 4;
- private static final int ID = 5;
- private static final int IMM = 6;
- private static final int OPCODE = 7;
- private static final int REG1 = 8;
- private static final int REG2 = 9;
- private static final int REGID = 10;
- private static final int SIZE = 11;
- private static final int SUBOPCODE = 12;
- private static final int VALUE = 13;
- private static final int VECTOR = 14;
+ private static final int BREAK = 1;
+ private static final int CODE = 2;
+ private static final int COND = 3;
+ private static final int DISP = 4;
+ private static final int FETCH = 5;
+ private static final int FORMAT = 6;
+ private static final int ID = 7;
+ private static final int IMM = 8;
+ private static final int OPCODE = 9;
+ private static final int REG1 = 10;
+ private static final int REG2 = 11;
+ private static final int REGID = 12;
+ private static final int SIZE = 13;
+ private static final int SUBOPCODE = 14;
+ private static final int TYPE = 15;
+ private static final int VALUE = 16;
+ private static final int VECTOR = 17;
// Evaluation operator IDs
private static final int GROUP = 0; // ()
@@ -190,6 +193,12 @@ public class Breakpoint {
SYMDEFS.put("false", new Def(BOOL, 0));
SYMDEFS.put("true" , new Def(BOOL, 1));
+ // Break type symbl definitions
+ SYMDEFS.put("exception", new Def(SIGNED, EXCEPTION));
+ SYMDEFS.put("execute" , new Def(SIGNED, EXECUTE ));
+ SYMDEFS.put("read" , new Def(SIGNED, READ ));
+ SYMDEFS.put("write" , new Def(SIGNED, WRITE ));
+
// Condition code symbol definitions
SYMDEFS.put("c" , new Def(SIGNED, 1));
SYMDEFS.put("e" , new Def(SIGNED, 2));
@@ -293,9 +302,11 @@ public class Breakpoint {
// Functional symbol definitions
SYMDEFS.put("address" , new Def(SYMBOL, ADDRESS ));
+ SYMDEFS.put("break" , new Def(SYMBOL, BREAK ));
SYMDEFS.put("code" , new Def(SYMBOL, CODE ));
SYMDEFS.put("cond" , new Def(SYMBOL, COND ));
SYMDEFS.put("disp" , new Def(SYMBOL, DISP ));
+ SYMDEFS.put("fetch" , new Def(SYMBOL, FETCH ));
SYMDEFS.put("format" , new Def(SYMBOL, FORMAT ));
SYMDEFS.put("id" , new Def(SYMBOL, ID ));
SYMDEFS.put("imm" , new Def(SYMBOL, IMM ));
@@ -305,6 +316,7 @@ public class Breakpoint {
SYMDEFS.put("regid" , new Def(SYMBOL, REGID ));
SYMDEFS.put("size" , new Def(SYMBOL, SIZE ));
SYMDEFS.put("subopcode", new Def(SYMBOL, SUBOPCODE));
+ SYMDEFS.put("type" , new Def(SYMBOL, TYPE ));
SYMDEFS.put("value" , new Def(SYMBOL, VALUE ));
SYMDEFS.put("vector" , new Def(SYMBOL, VECTOR ));
@@ -454,9 +466,34 @@ public class Breakpoint {
// Evaluate the condition against the emulation context
public boolean evaluate() {
- return vue == null || conditionError.code != NONE ?
- false : tokens.length == 0 ? true :
- evaluate(null, null, null) != 0;
+
+ // Error checking
+ if (vue == null)
+ return false;
+
+ // Retrieve state objects
+ Access acc = vue.getAccess ();
+ int breakType = vue.getBreakType ();
+ Instruction inst = vue.getInstruction();
+
+ // Error checking
+ if (!appliesTo(breakType))
+ return false;
+
+ // Check address ranges for execute
+ if (breakType == EXECUTE) {
+ int pc = vue.getRegister(Vue.PC, true);
+ if (!inRange(pc, pc + inst.size - 1))
+ return false;
+ }
+
+ // Check address ranges for read and write
+ else if (breakType == READ || breakType == WRITE &&
+ !inRange(acc.address, acc.address+JavaVue.TYPE_SIZES[acc.type]-1))
+ return false;
+
+ // Evaluate the condition
+ return isTrue(new int[depth], breakType, inst, acc);
}
// Retrieve the most recent address text
@@ -504,6 +541,15 @@ public class Breakpoint {
return name;
}
+ // Determine whether the breakpoint can be used
+ boolean isActive() {
+ return
+ isEnabled &&
+ addressError .code == NONE &&
+ conditionError.code == NONE
+ ;
+ }
+
// Determine whether the breakpoint is enabled
public boolean isEnabled() {
return isEnabled;
@@ -738,12 +784,16 @@ public class Breakpoint {
// Package Methods //
///////////////////////////////////////////////////////////////////////////
- // Cast a value to the condition evaluation's data type
- Object cast(int value) {
- if (conditionError.code != NONE)
- return conditionError;
+ // Determine whether the breakpoint applies to a break scenario
+ boolean appliesTo(int breakType) {
+ return isActive() && (breakType == 0 || (breakType & hooks) != 0);
+ }
+
+ // Perform a typed evaluation of the condition expression
+ Object evaluateTyped(int[]stack,int breakType,Instruction inst,Access acc){
if (tokens.length == 0)
return null;
+ int value = evaluate(stack, breakType, inst, acc);
switch (dataType) {
case BOOL : return (Boolean) (value != 0);
case FLOAT : return (Float ) Float.intBitsToFloat(value);
@@ -752,54 +802,28 @@ public class Breakpoint {
return (Integer) value;
}
- // Evaluate the condition, returning only the computed value
- int evaluate(int[] stack, Instruction inst, Access acc) {
+ // Determine whether the breakpoint applies to a given address range
+ boolean inRange(int start, int end) {
- // Error checking
- if (conditionError.code != NONE)
- return 0;
- if (tokens.length == 0)
- return 1;
- if (stack == null)
- stack = new int[depth];
+ // Implicit inclusion
+ if (ranges.length == 0)
+ return true;
- // Process all tokens
- int size = 0;
- for (var tok : tokens) switch (tok.type) {
+ // Check all ranges
+ for (var range : ranges)
+ if (
+ Integer.compareUnsigned(
+ start - range[0], range[1] - range[0]) <= 0 ||
+ Integer.compareUnsigned(
+ range[0] - start , end - start ) <= 0
+ ) return true;
+ return false;
+ }
- // Binary operator
- case BINARY:
- stack[size - 2] =
- evalBinary(tok.id, stack[size - 2], stack[size - 1]);
- break;
-
- // Literal
- case BOOL:
- case FLOAT:
- case SIGNED:
- case UNSIGNED:
- stack[size++] = tok.id;
- break;
-
- // CPU register
- case PROREG:
- case SYSREG:
- stack[size++] = vue.getRegister(tok.id, tok.type == SYSREG);
- break;
-
- // Symbol
- case SYMBOL:
- stack[size++] = 0;
- break;
-
- // Unary operator
- case UNARY:
- stack[size - 1] = evalUnary(tok.id, stack[size - 1]);
- break;
- }
-
- // Return the remaining stack value
- return stack[0];
+ // Determine whether the breakpoint's condition is truthy
+ boolean isTrue(int[] stack, int breakType, Instruction inst, Access acc) {
+ return tokens.length == 0 ? true :
+ evaluate(stack, breakType, inst, acc) != 0;
}
// Produce a one-dimensional array from the address ranges
@@ -829,21 +853,6 @@ public class Breakpoint {
return depth;
}
- // Retrieve the bit mask for enabled hooks
- int getHooks() {
- return hooks;
- }
-
- // Retrieve the list of address ranges
- int[][] getRanges() {
- return ranges;
- }
-
- // Retrieve the list of condition tokens
- Token[] getTokens() {
- return tokens;
- }
-
// The breakpoint is being removed from its emulation context
void remove() {
vue = null;
@@ -1216,10 +1225,11 @@ public class Breakpoint {
// Binary operators
if (tok.type == BINARY) {
- int id = tok.id;
- boolean literal = isLiteral(tok.left) && isLiteral(tok.right);
- tok.dataType = Math.max(tok.left.dataType, tok.right.dataType);
- tok.id = -id * 4 + tok.dataType;
+ int dataType = Math.max(tok.left.dataType, tok.right.dataType);
+ int id = tok.id;
+ boolean literal = isLiteral(tok.left) && isLiteral(tok.right);
+ tok.dataType = dataType;
+ tok.id = -id * 4 + tok.dataType;
// Process by ID
switch (id) {
@@ -1269,7 +1279,7 @@ public class Breakpoint {
var top = op == 0 ? tok.left : tok.right;
// The operand is already the result type
- if (top.dataType == tok.dataType)
+ if (top.dataType == dataType)
continue;
// Select the ID of the conversion operation
@@ -1284,7 +1294,7 @@ public class Breakpoint {
// Convert the literal operand directly
if (isLiteral(top)) {
top.id = evalUnary(cvt, top);
- top.type = top.dataType = tok.dataType;
+ top.type = top.dataType = dataType;
}
// Insert a conversion token
@@ -1296,7 +1306,7 @@ public class Breakpoint {
case SIGNED : imp.text = "s32" ; break;
case UNSIGNED: imp.text = "u32" ; break;
}
- imp.dataType = tok.dataType;
+ imp.dataType = dataType;
imp.id = -cvt * 4 + top.dataType;
imp.parent = tok;
imp.right = top;
@@ -1444,202 +1454,71 @@ public class Breakpoint {
- ///////////////////////////////////////////////////////////////////////////
- // Private Methods //
- ///////////////////////////////////////////////////////////////////////////
-
- // Adjust a float value as needed
- private static float adjust(float value) {
- int bits = Float.floatToRawIntBits(value);
- int exp = bits & 0x7F800000;
- return
- (bits & 0x7FFFFFFF) == 0 || // Zero
- exp == 0x7F800000 || // Indefinite
- exp == 0 // Denormal
- ? 0 : value;
- }
-
- // Determine the required stack size to evaluate the expression
- private int depth() {
-
- // Error checking
- if (conditionError.code != NONE)
- return 0;
-
- // Count the maximum size of the stack
- int max = 0;
- int size = 0;
- for (var tok : tokens) switch (tok.type) {
- case BINARY: size--; break;
- case UNARY : break;
- default : max = Math.max(max, ++size);
- }
- return max;
- }
-
- // Remove the fraction part of a float
- private static float trunc(float x) {
- return (float) (x < 0 ? Math.ceil(x) : Math.floor(x));
- }
-
-
-
- ///////////////////////////////////////////////////////////////////////////
- // Debugging Methods //
- ///////////////////////////////////////////////////////////////////////////
-
- // Produce a string representation of the condition token list
- public String debugTokens() {
- var ret = new StringBuilder();
-
- // The condition was not successfully parsed
- if (conditionError.code != NONE)
- return "Error";
-
- // Determine the maximum width of the text fields
- int max = 0;
- for (var tok : tokens)
- max = Math.max(max, tok.text.length());
-
- // Output all tokens
- var last = tokens[tokens.length - 1];
- for (var tok : tokens) {
-
- // Text
- ret.append(String.format("%-" + max + "s ", tok.text));
-
- // Type
- String type = null;
- switch (tok.type) {
- case BINARY : type = "Binary" ; break;
- case BOOL : type = "Bool" ; break;
- case FLOAT : type = "Float" ; break;
- case PROREG : type = "ProReg" ; break;
- case SYSREG : type = "SysReg" ; break;
- case SIGNED : type = "Signed" ; break;
- case SYMBOL : type = "Symbol" ; break;
- case UNARY : type = "Unary" ; break;
- case UNSIGNED: type = "Unsigned"; break;
- default: type = Integer.toString(tok.type);
- }
- ret.append(String.format("%-8s ", type));
-
- // Data type
- switch (tok.dataType) {
- case BOOL : type = "Bool" ; break;
- case FLOAT : type = "Float" ; break;
- case SIGNED : type = "Signed" ; break;
- case UNSIGNED: type = "Unsigned"; break;
- default: type = Integer.toString(tok.dataType);
- }
- ret.append(String.format("%-8s ", type));
-
- // Operator
- if (tok.type == BINARY || tok.type == UNARY) {
- String id = Integer.toString(tok.id);
- switch (-tok.id / 4) {
- case 0 : id = "REINTERPRET"; break;
- case READ8 : id = "READ8" ; break;
- case READ16 : id = "READ16" ; break;
- case READ32 : id = "READ32" ; break;
- case ADD : id = "ADD" ; break;
- case AND_B : id = "AND_B" ; break;
- case AND_L : id = "AND_L" ; break;
- case BOOL_ : id = "BOOL_" ; break;
- case CEIL : id = "CEIL" ; break;
- case DIVIDE : id = "DIVIDE" ; break;
- case EQUAL : id = "EQUAL" ; break;
- case FLOAT_ : id = "FLOAT_" ; break;
- case FLOOR : id = "FLOOR" ; break;
- case GREATER : id = "GREATER" ; break;
- case GREQUAL : id = "GREQUAL" ; break;
- case LEFT_L : id = "LEFT_L" ; break;
- case LEQUAL : id = "LEQUAL" ; break;
- case LESS : id = "LESS" ; break;
- case MULTIPLY : id = "MULTIPLY" ; break;
- case NEQUAL : id = "NEQUAL" ; break;
- case NOT_B : id = "NOT_B" ; break;
- case NOT_L : id = "NOT_L" ; break;
- case NEGATE : id = "NEGATE" ; break;
- case OR_B : id = "OR_B" ; break;
- case OR_L : id = "OR_L" ; break;
- case REMAINDER: id = "REMAINDER" ; break;
- case RIGHT_A : id = "RIGHT_A" ; break;
- case RIGHT_L : id = "RIGHT_L" ; break;
- case ROUND : id = "ROUND" ; break;
- case S8 : id = "S8" ; break;
- case S16 : id = "S16" ; break;
- case S32 : id = "S32" ; break;
- case SUBTRACT : id = "SUBTRACT" ; break;
- case TRUNC : id = "TRUNC" ; break;
- case U8 : id = "U8" ; break;
- case U16 : id = "U16" ; break;
- case U32 : id = "U32" ; break;
- case XFLOAT : id = "XFLOAT" ; break;
- case XOR_B : id = "XOR_B" ; break;
- case XOR_L : id = "XOR_L" ; break;
- case XS32 : id = "XS32" ; break;
- case XU32 : id = "XU32" ; break;
- default: ret.append(Integer.toString(tok.id));
- }
- ret.append(String.format("%-11s", id));
- }
-
- // Symbol or literal
- else switch (tok.type) {
- case PROREG:
- case SYSREG:
- case SYMBOL:
- ret.append(Integer.toString(tok.id));
- break;
- case FLOAT:
- ret.append(String.format("%.6f",
- Float.intBitsToFloat(tok.id)));
- break;
- case BOOL:
- case SIGNED:
- ret.append(Integer.toString(tok.id));
- break;
- case UNSIGNED:
- ret.append(Long.toString(tok.id & 0xFFFFFFFFL));
- break;
- default: ret.append("Error");
- }
-
- // Advance to the next line
- if (tok != last)
- ret.append("\n");
- }
-
- return ret.toString();
- }
-
- // Produce a string representation of the internal address ranges
- public String debugRanges() {
- var ret = new StringBuilder();
-
- // The address ranges were not successfully parsed
- if (addressError.code != NONE)
- return "Error";
-
- // Process ranges
- for (int x = 0; x < ranges.length; x++) {
- var range = ranges[x];
- if (x > 0)
- ret.append("\n");
- ret.append(String.format("%08X", range[0]));
- if (range[0] != range[1])
- ret.append(String.format("-%08X", range[1]));
- }
- return ret.toString();
- }
-
-
-
///////////////////////////////////////////////////////////////////////////
// Evaluation Methods //
///////////////////////////////////////////////////////////////////////////
+ // Evaluate the condition, returning only the computed value
+ private int evaluate(int[]stack,int breakType,Instruction inst,Access acc){
+
+ // Process all tokens
+ int size = 0;
+ for (var tok : tokens) switch (tok.type) {
+
+ // Binary operator
+ case BINARY:
+ stack[size - 2] =
+ evalBinary(tok.id, stack[size - 2], stack[size - 1]);
+ break;
+
+ // Literal
+ case BOOL:
+ case FLOAT:
+ case SIGNED:
+ case UNSIGNED:
+ stack[size++] = tok.id;
+ break;
+
+ // CPU register
+ case PROREG:
+ case SYSREG:
+ stack[size++] = vue.getRegister(tok.id, tok.type == SYSREG);
+ break;
+
+ // Unary operator
+ case UNARY:
+ stack[size - 1] = evalUnary(tok.id, stack[size - 1]);
+ break;
+
+ // Symbol
+ case SYMBOL:
+ int sym = inst.imm; // IMM, REGID, VECTOR
+ switch (tok.id) {
+ case ADDRESS : sym = acc.address ; break;
+ case BREAK : sym = breakType ; break;
+ case CODE : sym = vue.getExceptionCode(); break;
+ case COND : sym = inst.cond ; break;
+ case DISP : sym = inst.disp ; break;
+ case FETCH : sym = vue.getFetch (); break;
+ case FORMAT : sym = inst.format ; break;
+ case ID : sym = inst.id ; break;
+ case OPCODE : sym = inst.opcode ; break;
+ case REG1 : sym = inst.reg1 ; break;
+ case REG2 : sym = inst.reg2 ; break;
+ case SIZE : sym = inst.size ; break;
+ case SUBOPCODE: sym = inst.subopcode ; break;
+ case TYPE : sym = acc.type ; break;
+ case VALUE : sym = acc.value ; break;
+ }
+ stack[size++] = sym;
+ break;
+
+ }
+
+ // Return the remaining stack value
+ return stack[0];
+ }
+
// Evaluate a unary operator given a token
private int evalUnary(int id, Token tok) {
return evalUnary(-id * 4 + tok.dataType, tok.id);
@@ -1988,4 +1867,196 @@ public class Breakpoint {
return 0;
}
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Private Methods //
+ ///////////////////////////////////////////////////////////////////////////
+
+ // Adjust a float value as needed
+ private static float adjust(float value) {
+ int bits = Float.floatToRawIntBits(value);
+ int exp = bits & 0x7F800000;
+ return
+ (bits & 0x7FFFFFFF) == 0 || // Zero
+ exp == 0x7F800000 || // Indefinite
+ exp == 0 // Denormal
+ ? 0 : value;
+ }
+
+ // Determine the required stack size to evaluate the expression
+ private int depth() {
+
+ // Error checking
+ if (conditionError.code != NONE)
+ return 0;
+
+ // Count the maximum size of the stack
+ int max = 0;
+ int size = 0;
+ for (var tok : tokens) switch (tok.type) {
+ case BINARY: size--; break;
+ case UNARY : break;
+ default : max = Math.max(max, ++size);
+ }
+ return max;
+ }
+
+ // Remove the fraction part of a float
+ private static float trunc(float x) {
+ return (float) (x < 0 ? Math.ceil(x) : Math.floor(x));
+ }
+
+
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Debugging Methods //
+ ///////////////////////////////////////////////////////////////////////////
+
+ // Produce a string representation of the condition token list
+ public String debugTokens() {
+ var ret = new StringBuilder();
+
+ // The condition was not successfully parsed
+ if (conditionError.code != NONE)
+ return "Error";
+
+ // Determine the maximum width of the text fields
+ int max = 0;
+ for (var tok : tokens)
+ max = Math.max(max, tok.text.length());
+
+ // Output all tokens
+ var last = tokens[tokens.length - 1];
+ for (var tok : tokens) {
+
+ // Text
+ ret.append(String.format("%-" + max + "s ", tok.text));
+
+ // Type
+ String type = null;
+ switch (tok.type) {
+ case BINARY : type = "Binary" ; break;
+ case BOOL : type = "Bool" ; break;
+ case FLOAT : type = "Float" ; break;
+ case PROREG : type = "ProReg" ; break;
+ case SYSREG : type = "SysReg" ; break;
+ case SIGNED : type = "Signed" ; break;
+ case SYMBOL : type = "Symbol" ; break;
+ case UNARY : type = "Unary" ; break;
+ case UNSIGNED: type = "Unsigned"; break;
+ default: type = Integer.toString(tok.type);
+ }
+ ret.append(String.format("%-8s ", type));
+
+ // Data type
+ switch (tok.dataType) {
+ case BOOL : type = "Bool" ; break;
+ case FLOAT : type = "Float" ; break;
+ case SIGNED : type = "Signed" ; break;
+ case UNSIGNED: type = "Unsigned"; break;
+ default: type = Integer.toString(tok.dataType);
+ }
+ ret.append(String.format("%-8s ", type));
+
+ // Operator
+ if (tok.type == BINARY || tok.type == UNARY) {
+ String id = Integer.toString(tok.id);
+ switch (-tok.id / 4) {
+ case 0 : id = "REINTERPRET"; break;
+ case READ8 : id = "READ8" ; break;
+ case READ16 : id = "READ16" ; break;
+ case READ32 : id = "READ32" ; break;
+ case ADD : id = "ADD" ; break;
+ case AND_B : id = "AND_B" ; break;
+ case AND_L : id = "AND_L" ; break;
+ case BOOL_ : id = "BOOL_" ; break;
+ case CEIL : id = "CEIL" ; break;
+ case DIVIDE : id = "DIVIDE" ; break;
+ case EQUAL : id = "EQUAL" ; break;
+ case FLOAT_ : id = "FLOAT_" ; break;
+ case FLOOR : id = "FLOOR" ; break;
+ case GREATER : id = "GREATER" ; break;
+ case GREQUAL : id = "GREQUAL" ; break;
+ case LEFT_L : id = "LEFT_L" ; break;
+ case LEQUAL : id = "LEQUAL" ; break;
+ case LESS : id = "LESS" ; break;
+ case MULTIPLY : id = "MULTIPLY" ; break;
+ case NEQUAL : id = "NEQUAL" ; break;
+ case NOT_B : id = "NOT_B" ; break;
+ case NOT_L : id = "NOT_L" ; break;
+ case NEGATE : id = "NEGATE" ; break;
+ case OR_B : id = "OR_B" ; break;
+ case OR_L : id = "OR_L" ; break;
+ case REMAINDER: id = "REMAINDER" ; break;
+ case RIGHT_A : id = "RIGHT_A" ; break;
+ case RIGHT_L : id = "RIGHT_L" ; break;
+ case ROUND : id = "ROUND" ; break;
+ case S8 : id = "S8" ; break;
+ case S16 : id = "S16" ; break;
+ case S32 : id = "S32" ; break;
+ case SUBTRACT : id = "SUBTRACT" ; break;
+ case TRUNC : id = "TRUNC" ; break;
+ case U8 : id = "U8" ; break;
+ case U16 : id = "U16" ; break;
+ case U32 : id = "U32" ; break;
+ case XFLOAT : id = "XFLOAT" ; break;
+ case XOR_B : id = "XOR_B" ; break;
+ case XOR_L : id = "XOR_L" ; break;
+ case XS32 : id = "XS32" ; break;
+ case XU32 : id = "XU32" ; break;
+ default: ret.append(Integer.toString(tok.id));
+ }
+ ret.append(String.format("%-11s", id));
+ }
+
+ // Symbol or literal
+ else switch (tok.type) {
+ case PROREG:
+ case SYSREG:
+ case SYMBOL:
+ ret.append(Integer.toString(tok.id));
+ break;
+ case FLOAT:
+ ret.append(String.format("%.6f",
+ Float.intBitsToFloat(tok.id)));
+ break;
+ case BOOL:
+ case SIGNED:
+ ret.append(Integer.toString(tok.id));
+ break;
+ case UNSIGNED:
+ ret.append(Long.toString(tok.id & 0xFFFFFFFFL));
+ break;
+ default: ret.append("Error");
+ }
+
+ // Advance to the next line
+ if (tok != last)
+ ret.append("\n");
+ }
+
+ return ret.toString();
+ }
+
+ // Produce a string representation of the internal address ranges
+ public String debugRanges() {
+ var ret = new StringBuilder();
+
+ // The address ranges were not successfully parsed
+ if (addressError.code != NONE)
+ return "Error";
+
+ // Process ranges
+ for (int x = 0; x < ranges.length; x++) {
+ var range = ranges[x];
+ if (x > 0)
+ ret.append("\n");
+ ret.append(String.format("%08X", range[0]));
+ if (range[0] != range[1])
+ ret.append(String.format("-%08X", range[1]));
+ }
+ return ret.toString();
+ }
+
}
\ No newline at end of file
diff --git a/src/desktop/vue/CPU.java b/src/desktop/vue/CPU.java
index 20d689b..fdd62f7 100644
--- a/src/desktop/vue/CPU.java
+++ b/src/desktop/vue/CPU.java
@@ -12,7 +12,7 @@ class CPU {
// Package fields
Access access; // Access state
int cycles; // Cycles until next stage
- Ecxeption exception; // Exception state
+ int exception; // Exception code
int fetch; // Fetch unit index
Instruction inst; // Instruction state
int irq; // Interrupt lines
@@ -97,7 +97,6 @@ class CPU {
// Default constructor
CPU(JavaVue vue) {
access = new Access();
- exception = new Ecxeption();
inst = new Instruction();
jumpFrom = new int[3];
jumpTo = new int[3];
@@ -114,28 +113,27 @@ class CPU {
// Process the simulation
void emulate(int cycles) {
- // The CPU is halting
- if (stage == HALT || stage == FATAL)
+ // The CPU is in fatal halt status
+ if (stage == FATAL)
return;
-this.cycles = 0; // DEBUG: Stop processing after execute
-
// Process for the given number of cycles
for (;;) {
- // The next event occurs after the given number of cycles
+ // The next stage occurs after the given number of cycles
if (this.cycles > cycles) {
this.cycles -= cycles;
return;
}
// Processing by stage
+ cycles -= this.cycles;
this.cycles = 0;
switch (stage) {
- case EXCEPTION: if (exception ()) return; break;
- case EXECUTE : if (execute ()) return; break;
- case FETCH : if (fetch ()) return; break;
- case HALT : if (testException()) return; break;
+ case EXCEPTION: if (exception()) return; break;
+ case EXECUTE : if (execute ()) return; break;
+ case FETCH : if (fetch ()) return; break;
+ case HALT : testException(); return;
}
}
@@ -177,11 +175,11 @@ this.cycles = 0; // DEBUG: Stop processing after execute
void reset() {
// Configure instance fields
- cycles = 0;
- exception.code = 0;
- fetch = 0;
- irq = 0;
- stage = FETCH;
+ cycles = 0;
+ exception = 0;
+ fetch = -1;
+ irq = 0;
+ stage = FETCH;
// Clear all registers (hardware only sets ECR, PC and PSW)
for (int x = 0; x < 32; x++) {
@@ -257,11 +255,10 @@ this.cycles = 0; // DEBUG: Stop processing after execute
return 1; // Unreachable
}
- // Determine the number of CPU cycles until something can happen
+ // Determine the number of CPU cycles until a breakpoint could trigger
int until(int cycles) {
- if (stage == FATAL || stage == HALT)
- return cycles;
- return cycles < 0 ? this.cycles : Math.min(cycles, this.cycles);
+ return stage == FATAL || stage == HALT ?
+ cycles : Math.min(cycles, this.cycles);
}
@@ -273,19 +270,18 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Operations for exception stage
private boolean exception() {
- // Application callback
- vue.breakCode = vue.onException() ? 1 : 0;
- if (vue.breakCode != 0)
+ // Check for breakpoints
+ if (vue.onBreakpoint(Breakpoint.EXCEPTION))
return true;
// Configure working variables
- exception.code &= 0xFFFF;
- boolean isIRQ = (exception.code & 0xFF00) == 0xFE00;
- int psw = getSystemRegister(Vue.PSW);
+ exception &= 0xFFFF;
+ boolean isIRQ = (exception & 0xFF00) == 0xFE00;
+ int psw = getSystemRegister(Vue.PSW);
// Fatal exception
if (psw_np != 0) {
- vue.write(0x00000000, Vue.S32, 0xFFFF0000 | exception.code);
+ vue.write(0x00000000, Vue.S32, 0xFFFF0000 | exception);
vue.write(0x00000004, Vue.S32, psw);
vue.write(0x00000008, Vue.S32, pc);
stage = FATAL;
@@ -294,7 +290,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Duplexed exception
if (psw_ep != 0) {
- ecr_fecc = exception.code;
+ ecr_fecc = exception;
fepc = pc;
fepsw = psw;
psw_np = 1;
@@ -303,37 +299,36 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Regular exception
else {
- ecr_eicc = exception.code;
+ ecr_eicc = exception;
eipc = pc;
eipsw = psw;
psw_ep = 1;
- pc = 0xFFFF0000 | exception.code & 0xFFF0;
+ pc = 0xFFFF0000 | exception & 0xFFF0;
if (pc == 0xFFFFFF70) // FIV
pc = 0xFFFFFF60;
}
// Interrupt
if (isIRQ) {
- psw_i = Math.min(15, exception.code >> 4 & 15);
+ psw_i = Math.min(15, exception >> 4 & 15);
if (stage == HALT)
pc += 2;
}
// Common processing
- cycles = 0; // TODO: Determine the actual number
- exception.code = 0;
- psw_ae = 0;
- psw_id = 1;
- stage = FETCH;
+ cycles = 0; // TODO: Determine the actual number
+ exception = 0;
+ psw_ae = 0;
+ psw_id = 1;
+ stage = FETCH;
return false;
}
// Operations for execute stage
private boolean execute() {
- // Application callback
- vue.breakCode = vue.onExecute() ? 1 : 0;
- if (vue.breakCode != 0)
+ // Check for breakpoints
+ if (vue.onBreakpoint(Breakpoint.EXECUTE))
return true;
// Processing by instruction ID
@@ -415,7 +410,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
case Vue.XORI : XORI (); break;
//case Vue.XORNBSU: XORNBSU(); break;
default: // Invalid instruction
- exception.code = 0xFF90;
+ exception = 0xFF90;
}
// An application break was requested
@@ -423,7 +418,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
return true;
// Common processing
- if (exception.code == 0) {
+ if (exception == 0) {
cycles += CYCLES[inst.id];
pc += inst.size;
}
@@ -437,9 +432,14 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Operations for fetch stage
private boolean fetch() {
+ // Entering the fetch stage
+ if (fetch == -1)
+ fetch = 0;
+
// Read the bits from the bus
if (read(pc + (fetch << 1), Vue.U16, fetch))
return true;
+ // TODO: Determine how many cycles this takes
// First unit
if (fetch == 0) {
@@ -453,7 +453,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Second unit
else {
inst.bits |= access.value & 0xFFFF;
- fetch = 0;
+ fetch = -1;
}
// Decode the instruction and advance to execute stage
@@ -471,9 +471,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
access.type = type;
access.value = vue.read(address, type);
- // Application callback
- vue.breakCode = vue.onRead() ? 1 : 0;
- return vue.breakCode != 0;
+ // Check for breakpoints
+ return vue.onBreakpoint(Breakpoint.READ);
}
// Test a condition
@@ -495,16 +494,16 @@ this.cycles = 0; // DEBUG: Stop processing after execute
private boolean testException() {
// Check for an interrupt
- if (irq != 0 && (exception.code | psw_id | psw_ep | psw_np) == 0) {
+ if (irq != 0 && (exception | psw_id | psw_ep | psw_np) == 0) {
int level;
for (level = 4; level >= 0; level--)
if ((irq >> level & 1) != 0)
break;
- exception.code = 0xFE00 | level << 4;
+ exception = 0xFE00 | level << 4;
}
// There is no exception
- if (exception.code == 0)
+ if (exception == 0)
return stage == HALT && cycles == 0; // No further processing
// An exception has occurred
@@ -522,15 +521,13 @@ this.cycles = 0; // DEBUG: Stop processing after execute
access.type = type;
access.value = value;
- // Application callback
- vue.breakCode = vue.onWrite() ? 1 : 0;
- if (vue.breakCode != 0)
+ // Check for breakpoints
+ if (vue.onBreakpoint(Breakpoint.WRITE))
return true;
- if (access.type == Vue.CANCEL)
- return false;
// Perform the operation
- vue.write(access.address, access.type, access.value);
+ if (access.type != Vue.CANCEL)
+ vue.write(access.address, access.type, access.value);
return false;
}
@@ -572,7 +569,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Invalid operation
if (value > 0x7FFFFFFF || value < 0x80000000) {
- exception.code = 0xFF70;
+ exception = 0xFF70;
return;
}
@@ -599,8 +596,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
)) continue;
// The value is a reserved operand
- exception.code = 0xFF60;
- psw_fro = 1;
+ exception = 0xFF60;
+ psw_fro = 1;
return true;
}
return false;
@@ -611,8 +608,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Overflow
if (full > Float.MAX_VALUE || full < -Float.MAX_VALUE) {
- exception.code = 0xFF64;
- psw_fov = 1;
+ exception = 0xFF64;
+ psw_fov = 1;
return;
}
@@ -803,7 +800,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Zero division
if (right == 0) {
- exception.code = 0xFF80;
+ exception = 0xFF80;
return;
}
@@ -837,7 +834,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// An exception has occurred
if (right == 0) {
- exception.code = left == 0 ? 0xFF70 : 0xFF68;
+ exception = left == 0 ? 0xFF70 : 0xFF68;
return;
}
@@ -854,7 +851,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Zero division
if (right == 0) {
- exception.code = 0xFF80;
+ exception = 0xFF80;
return;
}
@@ -1105,7 +1102,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Trap
private void TRAP() {
- exception.code = 0xFFA0 | inst.imm & 31;
+ exception = 0xFFA0 | inst.imm & 31;
pc += 2;
}
diff --git a/src/desktop/vue/Ecxeption.java b/src/desktop/vue/Ecxeption.java
deleted file mode 100644
index 42905d7..0000000
--- a/src/desktop/vue/Ecxeption.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package vue;
-
-// Intentionally misspelled to avoid ambiguity with java.lang.Exception
-
-// Exception state
-public class Ecxeption {
-
- // Instance fields
- public int code; // Exception code
-
-
-
- ///////////////////////////////////////////////////////////////////////////
- // Constructors //
- ///////////////////////////////////////////////////////////////////////////
-
- // Default constructor
- Ecxeption() { }
-
- // Cloning constructor
- Ecxeption(Ecxeption o) {
- this();
- code = o.code;
- }
-
-}
diff --git a/src/desktop/vue/Instruction.java b/src/desktop/vue/Instruction.java
index 94f137b..a93028f 100644
--- a/src/desktop/vue/Instruction.java
+++ b/src/desktop/vue/Instruction.java
@@ -135,8 +135,12 @@ public class Instruction {
case 2:
reg2 = bits >> 21 & 31;
imm = extend < 0 ? bits << 11 >> 27 : bits >> 16 & 31;
- if (id == BITSTRING)
+ 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;
diff --git a/src/desktop/vue/JavaVue.java b/src/desktop/vue/JavaVue.java
index 5b9d322..773d196 100644
--- a/src/desktop/vue/JavaVue.java
+++ b/src/desktop/vue/JavaVue.java
@@ -7,11 +7,11 @@ import java.util.*;
class JavaVue extends Vue {
// Package fields
- int breakCode; // Application break code
+ int breakCode; // Application breakpoint code
+ int breakType; // Most recent breakpoint scenario
CPU cpu; // Processor
- int hook; // Most recent breakpoint hook
GamePak pak; // Game pak
- int[] stack; // Breakpoint evaluation stack
+ int[] stack; // Breakpoint condition evaluation stack
byte[] wram; // System WRAM
@@ -53,50 +53,47 @@ class JavaVue extends Vue {
// Process up to the given number of cycles
do {
- // Determine the number of cycles during which nothing will happen
- int cycles = -1;
+ // 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);
- //cycles = vsu .until(cycles);
-
- // Range checking
- if (cycles == -1) // No activity on any component
- break;
- if (maxCycles >= 0) // Restrict to given number of cycles
- cycles = Math.min(cycles, maxCycles);
// Process all system components
+ breakCode = 0;
cpu .emulate(cycles);
//pad .emulate(cycles);
//link .emulate(cycles);
//timer.emulate(cycles);
//vip .emulate(cycles);
//vsu .emulate(cycles);
-
- // An application break was requested
- //if (...)
- // break;
-
- // Update the number of cycles remaining
- if (maxCycles >= 0)
- maxCycles -= cycles;
- } while (maxCycles != 0);
+ maxCycles -= cycles;
+ } while (breakCode == 0 && maxCycles != 0);
// A break condition has occurred
- return Math.max(0, maxCycles);
+ return maxCycles;
}
- // Evaluate an expression
- public Object evaluate(String expression) {
- return null;
+ // Retrieve a snapshot of the current state's memory access
+ public Access getAccess() {
+ return cpu.access;
+ }
+
+ // Retrieve the most recent applicaiton break code
+ public int getBreakCode() {
+ return breakCode;
}
// Retrieve the most recent exception code
- public int getException() {
- return cpu.exception.code;
+ public int getExceptionCode() {
+ return cpu.exception;
+ }
+
+ // Retrieve a snapshot of the current state's instruction
+ public Instruction getInstruction() {
+ return cpu.inst;
}
// Retrieve a register value
@@ -139,6 +136,12 @@ class JavaVue extends Vue {
// 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 5: return readBuffer(wram , address, type);
case 6: return readBuffer(pak.ram, address, type);
@@ -250,29 +253,48 @@ class JavaVue extends Vue {
// Package Methods //
///////////////////////////////////////////////////////////////////////////
- // Evaluate the condition in a breakpoint
- boolean evaluate(Breakpoint brk) {
- return false;
+ // Retrieve the current state's breakpoint scenario
+ int getBreakType() {
+ return breakType;
}
- // Exception break handler
- boolean onException() {
- return onHook(Breakpoint.EXCEPTION);
+ // Retrieve the current instruction fetch index
+ int getFetch() {
+ return cpu.fetch;
}
- // Execute break handler
- boolean onExecute() {
- return onHook(Breakpoint.EXECUTE);
- }
+ // Check for breakpoints
+ boolean onBreakpoint(int breakType) {
+ int end = 0;
+ boolean ranged = false;
+ int start = 0;
+ this.breakType = breakType;
- // Read break handler
- boolean onRead() {
- return onHook(Breakpoint.READ);
- }
+ // Processing for Execute
+ if (breakType == Breakpoint.EXECUTE) {
+ ranged = true;
+ start = cpu.pc;
+ end = start + cpu.inst.size - 1;
+ }
- // Write break handler
- boolean onWrite() {
- return onHook(Breakpoint.WRITE);
+ // 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();
+ for (int x = 0; breakCode == 0 && x < count; x++) {
+ var brk = breakpoints.get(x);
+ if (
+ brk.appliesTo(breakType) &&
+ (!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
@@ -331,6 +353,16 @@ class JavaVue extends Vue {
}
+ // 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) {
@@ -377,19 +409,4 @@ class JavaVue extends Vue {
}
-
-
- ///////////////////////////////////////////////////////////////////////////
- // Private Methods //
- ///////////////////////////////////////////////////////////////////////////
-
- // Check breakpoints during a hooked event
- private boolean onHook(int hook) {
- this.hook = hook;
- for (var brk : breakpoints)
- if (evaluate(brk))
- return true;
- return false;
- }
-
}
diff --git a/src/desktop/vue/NativeVue.c b/src/desktop/vue/NativeVue.c
index b2a01bb..f9a01a2 100644
--- a/src/desktop/vue/NativeVue.c
+++ b/src/desktop/vue/NativeVue.c
@@ -117,13 +117,20 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_emulate
return vueEmulate(&core->vue, maxCycles);
}
-// Retrieve the application break code
+// 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) {
diff --git a/src/desktop/vue/NativeVue.java b/src/desktop/vue/NativeVue.java
index e6cb79f..16e87e1 100644
--- a/src/desktop/vue/NativeVue.java
+++ b/src/desktop/vue/NativeVue.java
@@ -25,6 +25,7 @@ class NativeVue extends Vue {
///////////////////////////////////////////////////////////////////////////
// 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);
@@ -41,14 +42,26 @@ class NativeVue extends Vue {
public int emulate(int maxCycles)
{ return emulate(handle, maxCycles); }
- // Retrieve the application break code
+ // Retrieve a snapshot of the current state's memory access
+ private native Access getAccess(long handle, Access access);
+ public Access getAccess() {
+ return getAccess(handle, new Access());
+ }
+
+ // 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 getException(long handle);
- public int getException() {
- return getException(handle);
+ private native int getExceptionCode(long handle);
+ public int getExceptionCode() {
+ return getExceptionCode(handle);
+ }
+
+ // Retrieve a snapshot of the current state's instruction
+ private native Instruction getInstruction(long handle, Instruction inst);
+ public Instruction getInstruction() {
+ return getInstruction(handle, new Instruction());
}
// Retrieve a register value
@@ -75,6 +88,7 @@ class NativeVue extends Vue {
{ 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))
@@ -117,9 +131,16 @@ class NativeVue extends Vue {
// Package Methods //
///////////////////////////////////////////////////////////////////////////
- // Evaluate the condition in a breakpoint
- boolean evaluate(Breakpoint brk) {
- return false;
+ // 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
@@ -137,16 +158,4 @@ class NativeVue extends Vue {
super.updateTokens(brk);
}
-
-
- ///////////////////////////////////////////////////////////////////////////
- // Private Methods //
- ///////////////////////////////////////////////////////////////////////////
-
- // Produce a new breakpoint and add it to the collection
- private native void breakpoint(long handle);
-
- // Remove a breakpoint from the collection
- private native void remove(long handle, int index);
-
}
diff --git a/src/desktop/vue/Vue.java b/src/desktop/vue/Vue.java
index d599212..22ff06e 100644
--- a/src/desktop/vue/Vue.java
+++ b/src/desktop/vue/Vue.java
@@ -138,7 +138,7 @@ public abstract class Vue {
// Produce an emulation core context
public static Vue create(boolean useNative) {
return !useNative ? new JavaVue() :
- isNativeLoaded() ? new NativeVue() : null;
+ nativeID != null ? new NativeVue() : null;
}
// Retrieve the ID of the loaded native library, if any
@@ -146,11 +146,6 @@ public abstract class Vue {
return nativeID;
}
- // Determine whether the native module is loaded
- public static boolean isNativeLoaded() {
- return nativeID != null;
- }
-
// Specify the ID of the loaded native library
public static void setNativeID(String nativeID) {
Vue.nativeID = nativeID;
@@ -180,40 +175,32 @@ public abstract class Vue {
return brk;
}
- // Evaluate an expression
- public Object evaluate(String expression) {
- var brk = new Breakpoint(this);
- brk.setCondition(expression);
- return brk.evaluate(null, null, null);
- }
-
- // Produce an array of the current breakpoint collection
- public Breakpoint[] listBreakpoints() {
- return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
- }
-
- // Remove a breakpoint from the collection
- public boolean remove(Breakpoint brk) {
- if (!breakpoints.remove(brk))
- return false;
- brk.remove();
- return true;
- }
-
-
-
- ///////////////////////////////////////////////////////////////////////////
- // Abstract Methods //
- ///////////////////////////////////////////////////////////////////////////
-
// 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 brk = new Breakpoint(this);
+ brk.setCondition(expression);
+ brk.setEnabled (true);
+ return brk.evaluateTyped(new int[brk.getDepth()], 0,
+ getInstruction(), getAccess());
+ }
+
+ // Retrieve a snapshot of the current state's memory access
+ public abstract Access getAccess();
+
+ // Retrieve the most recent applicaiton break code
+ public abstract int getBreakCode();
+
// Retrieve the most recent exception code
- public abstract int getException();
+ public abstract int getExceptionCode();
+
+ // Retrieve a snapshot of the current state's instruction
+ public abstract Instruction getInstruction();
// Retrieve a register value
public abstract int getRegister(int index, boolean system);
@@ -224,6 +211,11 @@ public abstract class Vue {
// 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);
@@ -231,6 +223,14 @@ public abstract class Vue {
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();
@@ -253,6 +253,12 @@ public abstract class Vue {
// 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) { }