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: +

+ + + + + + + + + +
• LiteralProvided directly by the expression.
• SymbolRefers 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: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
addressCurrent memory access's address.
break + Identifies what type of break scenario is being considered. See below for + a list of identifiers. +
codeCurrent exception's exception code.
cond + Condition code for Bcond and SETF + instructions. See below for a list of conditions. +
dispDisplacement offset for jumps and memory accesses.
fetch + Data unit index during a fetch operation, or -1 if the read + operation is not a fetch. +
formatCurrent 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. +
immImmediate operand.
opcodeCurrent instruction's top-level opcode.
reg1Source register index.
reg2Destination register index.
regid + System register index in LDSR and STSR + instructions. +
sizeNumber of bytes occupied by the current instruction.
subopcodeCurrent instruction's secondary opcode.
typeData type of the current memory access.
valueValue read by the current memory access.
vectorVector 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 BitwiseCannot be used with a float value.
!Not LogicalAlways produces a boolean.
-NegateCannot be used with an unsigned word value.
boolCast to Boolean
ceilRound Up
floatCast to Float
floorRound Down
roundRound to Nearest
s8, byteCast to Signed ByteResult is of type signed word.
s16, halfwordCast to Signed HalfwordResult is of type signed word.
s32, wordCast to Signed Word
truncTruncateRemoves any fraction.
u8, ubyteCast to Unsigned ByteResult is of type unsigned word.
u16, uhalfwordCast to Unsigned HalfwordResult is of type unsigned word.
u32, uwordCast to Unsigned Word
xfloatReinterpret as FloatThe binary value is not modified.
xs32, xwordReinterpret as Signed WordThe binary value is not modified.
xu32, xuwordReinterpret as Unsgned WordThe binary value is not modified.
/DivideZero divisor yields zero as result.
*Multiply
%RemainderZero divisor yields zero as result.
+Add
-Subtract
<<Shift LeftCannot be used with a float value.
>>Shift Right ArithmeticCannot be used with a float value.
>>>Shift Right LogicalCannot be used with a float value.
>GreaterAlways produces a boolean.
>=Greater or EqualAlways produces a boolean.
<LessAlways produces a boolean.
<=Less or EqualAlways produces a boolean.
==EqualAlways produces a boolean.
!=Not EqualAlways produces a boolean.
&And BitwiseCannot be used with a float value.
^Exclusive Or BitwiseCannot be used with a float value.
|Or BitwiseCannot be used with a float value.
&&And LogicalIf left is true, returns right; else returns left.
^^Exclusive Or LogicalIf only one operand is true, returns the truthy value.
||Or LogicalIf 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: +

+ + + + + + + + + + + + + +
floatBehaves like xfloat instead.
s8, u8Performs a byte read of the specified signedness.
s16, u16Performs 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) { }