Adjusting top-level emulation logic, integrating breakpoints

This commit is contained in:
Guy Perfect 2020-12-19 19:51:42 -06:00
parent e13c56e1e7
commit 167bc4f8bf
17 changed files with 1504 additions and 704 deletions

696
expressions.html Normal file
View File

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

View File

@ -116,7 +116,7 @@ static vbool cpuFloatReserved(Vue *vue, vbool left) {
)) continue; )) continue;
/* The value is a reserved operand */ /* The value is a reserved operand */
vue->cpu.exception.code = 0xFF60; vue->cpu.exception = 0xFF60;
vue->cpu.psw_fro = 1; vue->cpu.psw_fro = 1;
return VUE_TRUE; return VUE_TRUE;
} }
@ -150,7 +150,7 @@ static void cpuFloatConvert(Vue *vue, vbool round) {
/* Invalid operation (word overflow) */ /* Invalid operation (word overflow) */
else if (bits >= 8) { else if (bits >= 8) {
vue->cpu.exception.code = 0xFF70; vue->cpu.exception = 0xFF70;
return; return;
} }
@ -193,7 +193,7 @@ static void cpuFloatResult(Vue *vue, vbool compare, double full) {
/* Overflow */ /* Overflow */
if (full > result || full < -result) { if (full > result || full < -result) {
vue->cpu.exception.code = 0xFF64; vue->cpu.exception = 0xFF64;
vue->cpu.psw_fov = 1; vue->cpu.psw_fov = 1;
return; return;
} }
@ -578,7 +578,7 @@ static void cpuDIV(Vue *vue) {
/* Zero division */ /* Zero division */
if (right == 0) { if (right == 0) {
vue->cpu.exception.code = 0xFF80; vue->cpu.exception = 0xFF80;
return; return;
} }
@ -612,7 +612,7 @@ static void cpuDIVF_S(Vue *vue) {
/* An exception has occurred */ /* An exception has occurred */
if (!right) { if (!right) {
vue->cpu.exception.code = left ? 0xFF68 : 0xFF70; vue->cpu.exception = left ? 0xFF68 : 0xFF70;
return; return;
} }
@ -628,7 +628,7 @@ static void cpuDIVU(Vue *vue) {
/* Zero division */ /* Zero division */
if (right == 0) { if (right == 0) {
vue->cpu.exception.code = 0xFF80; vue->cpu.exception = 0xFF80;
return; return;
} }
@ -848,7 +848,7 @@ static void cpuREV(Vue *vue) {
/* Trap */ /* Trap */
#define cpuTRAP(vue) \ #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 vue->cpu.pc += 2
/* Truncate Short Floating to Word */ /* Truncate Short Floating to Word */
@ -908,8 +908,12 @@ static void cpuDecode(VueInstruction *inst) {
x = inst->bits >> 16 & 31; x = inst->bits >> 16 & 31;
inst->reg2 = inst->bits >> 21 & 31; inst->reg2 = inst->bits >> 21 & 31;
inst->imm = extend < 0 ? SIGN_EXTEND(5, x) : x; 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->id = x >= 16 ? VUE_ILLEGAL : LOOKUP_BITSTRING[x];
inst->subopcode = x;
}
if (inst->id == VUE_SETF)
inst->cond = x & 15;
break; break;
case 3: case 3:
x = inst->bits >> 16 & 0x1FF; 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 */ /* Check for an exception or interrupt */
static vbool cpuTestException(Vue *vue) { static vbool cpuTestException(Vue *vue) {
int32_t level; /* Interrupt level */ int32_t level; /* Interrupt level */
/* Check for an interrupt */ /* 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) { vue->cpu.psw_id | vue->cpu.psw_ep | vue->cpu.psw_np) == 0) {
for (level = 4; level >= 0; level--) for (level = 4; level >= 0; level--)
if ((vue->cpu.irq >> level & 1) != 0) if ((vue->cpu.irq >> level & 1) != 0)
break; break;
vue->cpu.exception.code = 0xFE00 | level << 4; vue->cpu.exception = 0xFE00 | level << 4;
} }
/* There is no exception */ /* 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; return vue->cpu.stage == CPU_HALT && vue->cpu.cycles == 0;
/* An exception has occurred */ /* An exception has occurred */
@ -975,8 +1042,6 @@ static vbool cpuExecute(Vue *vue) {
vue->breakCode = vue->onExecute(vue, &vue->cpu.inst); vue->breakCode = vue->onExecute(vue, &vue->cpu.inst);
if (vue->breakCode != 0) if (vue->breakCode != 0)
return VUE_TRUE; return VUE_TRUE;
vue->cpu.inst.reg1 &= 31;
vue->cpu.inst.reg2 &= 31;
} }
/* Processing by instruction ID */ /* Processing by instruction ID */
@ -1058,11 +1123,11 @@ static vbool cpuExecute(Vue *vue) {
case VUE_XORI : cpuXORI (vue); break; case VUE_XORI : cpuXORI (vue); break;
/*case VUE_XORNBSU: cpuXORNBSU(vue); break;*/ /*case VUE_XORNBSU: cpuXORNBSU(vue); break;*/
default: /* Invalid instruction */ default: /* Invalid instruction */
vue->cpu.exception.code = 0xFF90; vue->cpu.exception = 0xFF90;
} }
/* Common processing */ /* Common processing */
if (vue->cpu.exception.code == 0) { if (vue->cpu.exception == 0) {
vue->cpu.cycles += CYCLES[vue->cpu.inst.id]; vue->cpu.cycles += CYCLES[vue->cpu.inst.id];
vue->cpu.pc += vue->cpu.inst.size; vue->cpu.pc += vue->cpu.inst.size;
} }
@ -1082,10 +1147,15 @@ static int32_t cpuSize(int32_t opcode) {
/* Operations for fetch stage */ /* Operations for fetch stage */
static vbool cpuFetch(Vue *vue) { static vbool cpuFetch(Vue *vue) {
/* Entering the fetch stage */
if (vue->cpu.fetch == -1)
vue->cpu.fetch = 0;
/* Read the bits from the bus */ /* Read the bits from the bus */
if (cpuRead(vue, vue->cpu.pc + (vue->cpu.fetch << 1), if (cpuRead(vue, vue->cpu.pc + (vue->cpu.fetch << 1),
VUE_U16, vue->cpu.fetch)) VUE_U16, vue->cpu.fetch))
return VUE_TRUE; return VUE_TRUE;
/* TODO: Determine how many cycles this takes */
/* First unit */ /* First unit */
if (vue->cpu.fetch == 0) { if (vue->cpu.fetch == 0) {
@ -1099,7 +1169,7 @@ static vbool cpuFetch(Vue *vue) {
/* Second unit */ /* Second unit */
else { else {
vue->cpu.inst.bits |= vue->cpu.access.value & 0xFFFF; 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 */ /* Decode the instruction and advance to execute stage */
@ -1108,94 +1178,30 @@ static vbool cpuFetch(Vue *vue) {
return VUE_FALSE; 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 */ /* Process the simulation */
static void cpuEmulate(Vue *vue, int32_t cycles) { static void cpuEmulate(Vue *vue, uint32_t cycles) {
/* The CPU is halting */ /* The CPU is in fatal halt status */
if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT) if (vue->cpu.stage == CPU_FATAL)
return; return;
vue->cpu.cycles = 0; /* DEBUG: Stop processing after execute */
/* Process for the given number of cycles */ /* Process for the given number of cycles */
for (;;) { 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) { if (vue->cpu.cycles > cycles) {
vue->cpu.cycles -= cycles; vue->cpu.cycles -= cycles;
return; return;
} }
/* Processing by stage */ /* Processing by stage */
cycles -= vue->cpu.cycles;
vue->cpu.cycles = 0; vue->cpu.cycles = 0;
switch (vue->cpu.stage) { switch (vue->cpu.stage) {
case CPU_EXCEPTION: if (cpuException(vue)) return; break; case CPU_EXCEPTION: if (cpuException(vue)) return; break;
case CPU_EXECUTE : if (cpuExecute (vue)) return; break; case CPU_EXECUTE : if (cpuExecute (vue)) return; break;
case CPU_FETCH : if (cpuFetch (vue)) return; break; case CPU_FETCH : if (cpuFetch (vue)) return; break;
case CPU_HALT : if (cpuTestException(vue)) return; break; case CPU_HALT : cpuTestException(vue); return;
} }
} }
@ -1208,8 +1214,8 @@ static void cpuReset(Vue *vue) {
/* Configure instance fields */ /* Configure instance fields */
vue->cpu.cycles = 0; vue->cpu.cycles = 0;
vue->cpu.exception.code = 0; vue->cpu.exception = 0;
vue->cpu.fetch = 0; vue->cpu.fetch = -1;
vue->cpu.irq = 0; vue->cpu.irq = 0;
vue->cpu.stage = CPU_FETCH; vue->cpu.stage = CPU_FETCH;
@ -1229,12 +1235,10 @@ static void cpuReset(Vue *vue) {
vue->cpu.psw_np = 1; vue->cpu.psw_np = 1;
} }
/* Determine the number of CPU cycles until something can happen */ /* Determine the number of CPU cycles until a breakpoint could trigger */
static int32_t cpuUntil(Vue *vue, int32_t cycles) { static uint32_t cpuUntil(Vue *vue, uint32_t cycles) {
if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT) return vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT ?
return cycles; cycles : cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles;
return cycles < 0 ? vue->cpu.cycles :
cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles;
} }

View File

@ -154,11 +154,6 @@ typedef struct {
int8_t type; /* Data type */ int8_t type; /* Data type */
} VueAccess; } VueAccess;
/* Exception state */
typedef struct {
uint16_t code; /* Exception code */
} VueException;
/* Instruction state */ /* Instruction state */
typedef struct { typedef struct {
int32_t bits; /* Binary encoding */ int32_t bits; /* Binary encoding */
@ -174,31 +169,32 @@ typedef struct {
uint8_t subopcode; /* Instruction subopcode */ uint8_t subopcode; /* Instruction subopcode */
} VueInstruction; } VueInstruction;
/* Callbacks */ /* Breakpoint handler callbacks */
typedef int32_t (*VueOnException)(Vue *, VueException *); typedef int32_t (*VueOnAccess )(Vue *, VueAccess *);
typedef int32_t (*VueOnException)(Vue *, uint16_t );
typedef int32_t (*VueOnExecute )(Vue *, VueInstruction *); typedef int32_t (*VueOnExecute )(Vue *, VueInstruction *);
typedef int32_t (*VueOnRead )(Vue *, VueAccess *); typedef int32_t (*VueOnFrame )(Vue * );
typedef int32_t (*VueOnWrite )(Vue *, VueAccess *);
/* Emulation state */ /* Emulation state */
struct Vue { struct Vue {
int32_t breakCode; /* Application break code */ int32_t breakCode; /* Application break code */
uint8_t wram[0x10000]; /* System memory */ uint8_t wram[0x10000]; /* System memory */
/* Callback handlers */ /* Breakpoint handlers */
VueOnException onException; VueOnException onException;
VueOnExecute onExecute; VueOnExecute onExecute;
VueOnRead onRead; VueOnFrame onFrame;
VueOnWrite onWrite; VueOnAccess onRead;
VueOnAccess onWrite;
/* CPU state */ /* CPU state */
struct { struct {
VueAccess access; /* Access state */ VueAccess access; /* Access state */
VueException exception; /* Exception state */
VueInstruction inst; /* Instruction 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 jumpFrom[3]; /* Source PCs of most recent jumps */
int32_t jumpTo [3]; /* Destination PCs of most recent jumps */ int32_t jumpTo [3]; /* Destination PCs of most recent jumps */
uint16_t exception; /* Exception code */
uint16_t irq; /* Interrupt lines */ uint16_t irq; /* Interrupt lines */
int fetch; /* Fetch unit index */ int fetch; /* Fetch unit index */
int stage; /* Current processing stage */ int stage; /* Current processing stage */
@ -265,19 +261,21 @@ struct Vue {
* Function Prototypes * * Function Prototypes *
*****************************************************************************/ *****************************************************************************/
VUEAPI int32_t vueEmulate (Vue *vue, int32_t maxCycles); VUEAPI uint32_t vueEmulate (Vue *vue, uint32_t maxCycles);
VUEAPI int32_t vueGetBreakCode (Vue *vue); VUEAPI int32_t vueGetBreakCode (Vue *vue);
VUEAPI uint16_t vueGetExceptionCode (Vue *vue);
VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system); VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system);
VUEAPI void vueInitialize (Vue *vue); VUEAPI void vueInitialize (Vue *vue);
VUEAPI VueOnException vueOnException (Vue *vue, VueOnException callback);
VUEAPI VueOnExecute vueOnExecute (Vue *vue, VueOnExecute callback);
VUEAPI VueOnFrame vueOnFrame (Vue *vue, VueOnFrame callback);
VUEAPI VueOnAccess vueOnRead (Vue *vue, VueOnAccess callback);
VUEAPI VueOnAccess vueOnWrite (Vue *vue, VueOnAccess callback);
VUEAPI int32_t vueRead (Vue *vue, uint32_t address, int32_t type); VUEAPI 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 vbool vueReadBytes (Vue *vue, uint32_t address, uint8_t *dest, uint32_t length);
VUEAPI void vueReset (Vue *vue); 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 int32_t vueSetRegister (Vue *vue, int32_t index, vbool system, int32_t value);
VUEAPI vbool vueSetROM (Vue *vue, uint8_t *rom, uint32_t size); VUEAPI vbool vueSetROM (Vue *vue, uint8_t *rom, uint32_t size);
VUEAPI void vueSetWrite (Vue *vue, VueOnWrite callback);
VUEAPI void vueWrite (Vue *vue, uint32_t address, int32_t type, int32_t value); 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 vbool vueWriteBytes (Vue *vue, uint32_t address, uint8_t *src, uint32_t length);

View File

@ -155,55 +155,46 @@ static void writeBytes(uint8_t *data, uint32_t datlen, uint32_t address,
*****************************************************************************/ *****************************************************************************/
/* Process the simulation */ /* Process the simulation */
int32_t vueEmulate(Vue *vue, int32_t maxCycles) { uint32_t vueEmulate(Vue *vue, uint32_t maxCycles) {
int32_t cycles; /* Number of cycles to process */ uint32_t cycles; /* Number of cycles to process */
/* Process up to the given number of cycles */ /* Process up to the given number of cycles */
do { do {
/* Determine the number of cycles during which nothing will happen */ /* Determine the number of cycles where no breakpoint will occur */
cycles = -1; cycles = maxCycles; /* min(maxCycles, nextFrameCycles) */
cycles = cpuUntil (vue, cycles);
/*cycles = padUntil (vue, cycles);*/ /*cycles = padUntil (vue, cycles);*/
/*cycles = linkUntil (vue, cycles);*/ /*cycles = linkUntil (vue, cycles);*/
/*cycles = timerUntil(vue, cycles);*/ /*cycles = timerUntil(vue, cycles);*/
/*cycles = vipUntil (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 */ /* Process all system components */
vue->breakCode = 0; vue->breakCode = 0;
cpuEmulate (vue, cycles);
/*padEmulate (vue, cycles);*/ /*padEmulate (vue, cycles);*/
/*pakEmulate (vue, cycles);*/ /*pakEmulate (vue, cycles);*/
/*linkEmulate (vue, cycles);*/ /*linkEmulate (vue, cycles);*/
/*timerEmulate(vue, cycles);*/ /*timerEmulate(vue, cycles);*/
/*vipEmulate (vue, cycles);*/ /*vipEmulate (vue, cycles);*/
/*vsuEmulate (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; maxCycles -= cycles;
} while (maxCycles != 0); } while (vue->breakCode == 0 && maxCycles != 0);
/* A break condition has occurred */ /* A break condition has occurred */
return maxCycles; return maxCycles;
} }
/* Retrieve the application break code */ /* Retrieve the most recent applicaiton break code */
int32_t vueGetBreakCode(Vue *vue) { int32_t vueGetBreakCode(Vue *vue) {
return vue == NULL ? 0 : vue->breakCode; 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 */ /* Retrieve the value of a register */
int32_t vueGetRegister(Vue *vue, int32_t index, vbool system) { int32_t vueGetRegister(Vue *vue, int32_t index, vbool system) {
@ -234,17 +225,69 @@ void vueInitialize(Vue *vue) {
return; return;
vue->onException = NULL; vue->onException = NULL;
vue->onExecute = NULL; vue->onExecute = NULL;
vue->onFrame = NULL;
vue->onRead = NULL; vue->onRead = NULL;
vue->onWrite = NULL; vue->onWrite = NULL;
vue->pak.ram = NULL; vue->pak.ram = NULL;
vue->pak.rom = 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 */ /* Read a value from the CPU bus */
int32_t vueRead(Vue *vue, uint32_t address, int32_t type) { int32_t vueRead(Vue *vue, uint32_t address, int32_t type) {
/* Error checking */ /* Error checking */
if (vue == NULL) if (vue == NULL || type < 0 || type > 4)
return 0; return 0;
/* Perform the operation */ /* Perform the operation */
@ -299,28 +342,10 @@ void vueReset(Vue *vue) {
/* Reset state */ /* Reset state */
cpuReset(vue); cpuReset(vue);
pakReset(vue); pakReset(vue);
for (x = 0; x < 0x1000; x++) for (x = 0; x < 0x10000; x++)
vue->wram[x] = 0; 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 */ /* Specify a value for a register */
int32_t vueSetRegister(Vue *vue, int32_t index, vbool system, int32_t value) { int32_t vueSetRegister(Vue *vue, int32_t index, vbool system, int32_t value) {
return vue == NULL ? 0 : return vue == NULL ? 0 :
@ -348,12 +373,6 @@ vbool vueSetROM(Vue *vue, uint8_t *rom, uint32_t size) {
return VUE_TRUE; 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 */ /* Write a value to the CPU bus */
void vueWrite(Vue *vue, uint32_t address, int32_t type, int32_t value) { void vueWrite(Vue *vue, uint32_t address, int32_t type, int32_t value) {

View File

@ -49,43 +49,7 @@ public class Main {
useNative = true; useNative = true;
// Begin application operations // Begin application operations
//new App(useNative); 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
);
}
*/
} }
} }

View File

@ -74,7 +74,7 @@ public class App {
// Specify whether using the native module // Specify whether using the native module
boolean setUseNative(boolean useNative) { boolean setUseNative(boolean useNative) {
return this.useNative = useNative && Vue.isNativeLoaded(); return this.useNative = useNative && Vue.getNativeID() != null;
} }

View File

@ -31,6 +31,7 @@ class ChildWindow extends JInternalFrame {
addInternalFrameListener(Util.onClose2(e->setVisible(false))); addInternalFrameListener(Util.onClose2(e->setVisible(false)));
setClosable(true); setClosable(true);
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE); setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
setMaximizable(true);
setResizable(true); setResizable(true);
} }

View File

@ -63,6 +63,7 @@ class DisassemblerPane extends JScrollPane {
Util.onFocus(e->client.repaint(), e->client.repaint())); Util.onFocus(e->client.repaint(), e->client.repaint()));
client.addKeyListener(Util.onKey(e->onKeyDown(e), null)); client.addKeyListener(Util.onKey(e->onKeyDown(e), null));
client.addMouseListener(Util.onMouse(e->client.requestFocus(), null)); client.addMouseListener(Util.onMouse(e->client.requestFocus(), null));
client.addMouseWheelListener(e->onMouseWheel(e));
client.setBackground(SystemColor.window); client.setBackground(SystemColor.window);
client.setFocusable(true); client.setFocusable(true);
@ -98,6 +99,7 @@ class DisassemblerPane extends JScrollPane {
int code = e.getKeyCode(); int code = e.getKeyCode();
int count = tall(false); int count = tall(false);
int mods = e.getModifiersEx(); int mods = e.getModifiersEx();
var vue = parent.parent.vue;
boolean alt = (mods & InputEvent.ALT_DOWN_MASK ) != 0; boolean alt = (mods & InputEvent.ALT_DOWN_MASK ) != 0;
boolean ctrl = (mods & InputEvent.CTRL_DOWN_MASK) != 0; boolean ctrl = (mods & InputEvent.CTRL_DOWN_MASK) != 0;
@ -116,16 +118,37 @@ class DisassemblerPane extends JScrollPane {
} }
// Processing by key code // Processing by key code
int pc = vue.getRegister(Vue.PC, true);
var step = parent.parent.brkStep;
switch (code) { switch (code) {
case KeyEvent.VK_UP : seek(address, 1); break; case KeyEvent.VK_UP : seek(address, 1); break;
case KeyEvent.VK_DOWN : 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_UP : seek(address, count); break;
case KeyEvent.VK_PAGE_DOWN: seek(address, -count); break; case KeyEvent.VK_PAGE_DOWN: seek(address, -count); break;
// Single Step
case KeyEvent.VK_F11: 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(); 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)) if (!isVisible(pc))
seek(pc, count / 3); seek(pc, count / 3);
break; break;
@ -133,6 +156,11 @@ class DisassemblerPane extends JScrollPane {
} }
// Mouse wheel
private void onMouseWheel(MouseWheelEvent e) {
seek(address, -e.getUnitsToScroll());
}
// Client paint // Client paint
private void onPaint(Graphics2D g, int width, int height) { private void onPaint(Graphics2D g, int width, int height) {
var vue = parent.parent.vue; var vue = parent.parent.vue;

View File

@ -16,6 +16,7 @@ class MainWindow extends JFrame {
// Instance fields // Instance fields
App app; // Containing application App app; // Containing application
Breakpoint brkStep; // Single step internal breakpoint
Vue vue; // Emulation core context Vue vue; // Emulation core context
// Private fields // Private fields
@ -97,6 +98,10 @@ class MainWindow extends JFrame {
desktop.add(cpu = new CPUWindow (this)); desktop.add(cpu = new CPUWindow (this));
desktop.add(memory = new MemoryWindow (this)); desktop.add(memory = new MemoryWindow (this));
// Configure internal breakpoints
brkStep = vue.breakpoint();
brkStep.setExecute(true);
// Display window // Display window
refreshDebug(); refreshDebug();
pack(); pack();

View File

@ -63,20 +63,23 @@ public class Breakpoint {
// Functional symbol IDs // Functional symbol IDs
private static final int ADDRESS = 0; private static final int ADDRESS = 0;
private static final int CODE = 1; private static final int BREAK = 1;
private static final int COND = 2; private static final int CODE = 2;
private static final int DISP = 3; private static final int COND = 3;
private static final int FORMAT = 4; private static final int DISP = 4;
private static final int ID = 5; private static final int FETCH = 5;
private static final int IMM = 6; private static final int FORMAT = 6;
private static final int OPCODE = 7; private static final int ID = 7;
private static final int REG1 = 8; private static final int IMM = 8;
private static final int REG2 = 9; private static final int OPCODE = 9;
private static final int REGID = 10; private static final int REG1 = 10;
private static final int SIZE = 11; private static final int REG2 = 11;
private static final int SUBOPCODE = 12; private static final int REGID = 12;
private static final int VALUE = 13; private static final int SIZE = 13;
private static final int VECTOR = 14; 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 // Evaluation operator IDs
private static final int GROUP = 0; // () private static final int GROUP = 0; // ()
@ -190,6 +193,12 @@ public class Breakpoint {
SYMDEFS.put("false", new Def(BOOL, 0)); SYMDEFS.put("false", new Def(BOOL, 0));
SYMDEFS.put("true" , new Def(BOOL, 1)); 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 // Condition code symbol definitions
SYMDEFS.put("c" , new Def(SIGNED, 1)); SYMDEFS.put("c" , new Def(SIGNED, 1));
SYMDEFS.put("e" , new Def(SIGNED, 2)); SYMDEFS.put("e" , new Def(SIGNED, 2));
@ -293,9 +302,11 @@ public class Breakpoint {
// Functional symbol definitions // Functional symbol definitions
SYMDEFS.put("address" , new Def(SYMBOL, ADDRESS )); SYMDEFS.put("address" , new Def(SYMBOL, ADDRESS ));
SYMDEFS.put("break" , new Def(SYMBOL, BREAK ));
SYMDEFS.put("code" , new Def(SYMBOL, CODE )); SYMDEFS.put("code" , new Def(SYMBOL, CODE ));
SYMDEFS.put("cond" , new Def(SYMBOL, COND )); SYMDEFS.put("cond" , new Def(SYMBOL, COND ));
SYMDEFS.put("disp" , new Def(SYMBOL, DISP )); SYMDEFS.put("disp" , new Def(SYMBOL, DISP ));
SYMDEFS.put("fetch" , new Def(SYMBOL, FETCH ));
SYMDEFS.put("format" , new Def(SYMBOL, FORMAT )); SYMDEFS.put("format" , new Def(SYMBOL, FORMAT ));
SYMDEFS.put("id" , new Def(SYMBOL, ID )); SYMDEFS.put("id" , new Def(SYMBOL, ID ));
SYMDEFS.put("imm" , new Def(SYMBOL, IMM )); SYMDEFS.put("imm" , new Def(SYMBOL, IMM ));
@ -305,6 +316,7 @@ public class Breakpoint {
SYMDEFS.put("regid" , new Def(SYMBOL, REGID )); SYMDEFS.put("regid" , new Def(SYMBOL, REGID ));
SYMDEFS.put("size" , new Def(SYMBOL, SIZE )); SYMDEFS.put("size" , new Def(SYMBOL, SIZE ));
SYMDEFS.put("subopcode", new Def(SYMBOL, SUBOPCODE)); SYMDEFS.put("subopcode", new Def(SYMBOL, SUBOPCODE));
SYMDEFS.put("type" , new Def(SYMBOL, TYPE ));
SYMDEFS.put("value" , new Def(SYMBOL, VALUE )); SYMDEFS.put("value" , new Def(SYMBOL, VALUE ));
SYMDEFS.put("vector" , new Def(SYMBOL, VECTOR )); SYMDEFS.put("vector" , new Def(SYMBOL, VECTOR ));
@ -454,9 +466,34 @@ public class Breakpoint {
// Evaluate the condition against the emulation context // Evaluate the condition against the emulation context
public boolean evaluate() { public boolean evaluate() {
return vue == null || conditionError.code != NONE ?
false : tokens.length == 0 ? true : // Error checking
evaluate(null, null, null) != 0; 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 // Retrieve the most recent address text
@ -504,6 +541,15 @@ public class Breakpoint {
return name; 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 // Determine whether the breakpoint is enabled
public boolean isEnabled() { public boolean isEnabled() {
return isEnabled; return isEnabled;
@ -738,12 +784,16 @@ public class Breakpoint {
// Package Methods // // Package Methods //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Cast a value to the condition evaluation's data type // Determine whether the breakpoint applies to a break scenario
Object cast(int value) { boolean appliesTo(int breakType) {
if (conditionError.code != NONE) return isActive() && (breakType == 0 || (breakType & hooks) != 0);
return conditionError; }
// Perform a typed evaluation of the condition expression
Object evaluateTyped(int[]stack,int breakType,Instruction inst,Access acc){
if (tokens.length == 0) if (tokens.length == 0)
return null; return null;
int value = evaluate(stack, breakType, inst, acc);
switch (dataType) { switch (dataType) {
case BOOL : return (Boolean) (value != 0); case BOOL : return (Boolean) (value != 0);
case FLOAT : return (Float ) Float.intBitsToFloat(value); case FLOAT : return (Float ) Float.intBitsToFloat(value);
@ -752,54 +802,28 @@ public class Breakpoint {
return (Integer) value; return (Integer) value;
} }
// Evaluate the condition, returning only the computed value // Determine whether the breakpoint applies to a given address range
int evaluate(int[] stack, Instruction inst, Access acc) { boolean inRange(int start, int end) {
// Error checking // Implicit inclusion
if (conditionError.code != NONE) if (ranges.length == 0)
return 0; return true;
if (tokens.length == 0)
return 1;
if (stack == null)
stack = new int[depth];
// Process all tokens // Check all ranges
int size = 0; for (var range : ranges)
for (var tok : tokens) switch (tok.type) { if (
Integer.compareUnsigned(
// Binary operator start - range[0], range[1] - range[0]) <= 0 ||
case BINARY: Integer.compareUnsigned(
stack[size - 2] = range[0] - start , end - start ) <= 0
evalBinary(tok.id, stack[size - 2], stack[size - 1]); ) return true;
break; return false;
// 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 // Determine whether the breakpoint's condition is truthy
return stack[0]; 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 // Produce a one-dimensional array from the address ranges
@ -829,21 +853,6 @@ public class Breakpoint {
return depth; 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 // The breakpoint is being removed from its emulation context
void remove() { void remove() {
vue = null; vue = null;
@ -1216,9 +1225,10 @@ public class Breakpoint {
// Binary operators // Binary operators
if (tok.type == BINARY) { if (tok.type == BINARY) {
int dataType = Math.max(tok.left.dataType, tok.right.dataType);
int id = tok.id; int id = tok.id;
boolean literal = isLiteral(tok.left) && isLiteral(tok.right); boolean literal = isLiteral(tok.left) && isLiteral(tok.right);
tok.dataType = Math.max(tok.left.dataType, tok.right.dataType); tok.dataType = dataType;
tok.id = -id * 4 + tok.dataType; tok.id = -id * 4 + tok.dataType;
// Process by ID // Process by ID
@ -1269,7 +1279,7 @@ public class Breakpoint {
var top = op == 0 ? tok.left : tok.right; var top = op == 0 ? tok.left : tok.right;
// The operand is already the result type // The operand is already the result type
if (top.dataType == tok.dataType) if (top.dataType == dataType)
continue; continue;
// Select the ID of the conversion operation // Select the ID of the conversion operation
@ -1284,7 +1294,7 @@ public class Breakpoint {
// Convert the literal operand directly // Convert the literal operand directly
if (isLiteral(top)) { if (isLiteral(top)) {
top.id = evalUnary(cvt, top); top.id = evalUnary(cvt, top);
top.type = top.dataType = tok.dataType; top.type = top.dataType = dataType;
} }
// Insert a conversion token // Insert a conversion token
@ -1296,7 +1306,7 @@ public class Breakpoint {
case SIGNED : imp.text = "s32" ; break; case SIGNED : imp.text = "s32" ; break;
case UNSIGNED: imp.text = "u32" ; break; case UNSIGNED: imp.text = "u32" ; break;
} }
imp.dataType = tok.dataType; imp.dataType = dataType;
imp.id = -cvt * 4 + top.dataType; imp.id = -cvt * 4 + top.dataType;
imp.parent = tok; imp.parent = tok;
imp.right = top; 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 // // 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 // Evaluate a unary operator given a token
private int evalUnary(int id, Token tok) { private int evalUnary(int id, Token tok) {
return evalUnary(-id * 4 + tok.dataType, tok.id); return evalUnary(-id * 4 + tok.dataType, tok.id);
@ -1988,4 +1867,196 @@ public class Breakpoint {
return 0; 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();
}
} }

View File

@ -12,7 +12,7 @@ class CPU {
// Package fields // Package fields
Access access; // Access state Access access; // Access state
int cycles; // Cycles until next stage int cycles; // Cycles until next stage
Ecxeption exception; // Exception state int exception; // Exception code
int fetch; // Fetch unit index int fetch; // Fetch unit index
Instruction inst; // Instruction state Instruction inst; // Instruction state
int irq; // Interrupt lines int irq; // Interrupt lines
@ -97,7 +97,6 @@ class CPU {
// Default constructor // Default constructor
CPU(JavaVue vue) { CPU(JavaVue vue) {
access = new Access(); access = new Access();
exception = new Ecxeption();
inst = new Instruction(); inst = new Instruction();
jumpFrom = new int[3]; jumpFrom = new int[3];
jumpTo = new int[3]; jumpTo = new int[3];
@ -114,28 +113,27 @@ class CPU {
// Process the simulation // Process the simulation
void emulate(int cycles) { void emulate(int cycles) {
// The CPU is halting // The CPU is in fatal halt status
if (stage == HALT || stage == FATAL) if (stage == FATAL)
return; return;
this.cycles = 0; // DEBUG: Stop processing after execute
// Process for the given number of cycles // Process for the given number of cycles
for (;;) { 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) { if (this.cycles > cycles) {
this.cycles -= cycles; this.cycles -= cycles;
return; return;
} }
// Processing by stage // Processing by stage
cycles -= this.cycles;
this.cycles = 0; this.cycles = 0;
switch (stage) { switch (stage) {
case EXCEPTION: if (exception()) return; break; case EXCEPTION: if (exception()) return; break;
case EXECUTE : if (execute ()) return; break; case EXECUTE : if (execute ()) return; break;
case FETCH : if (fetch ()) return; break; case FETCH : if (fetch ()) return; break;
case HALT : if (testException()) return; break; case HALT : testException(); return;
} }
} }
@ -178,8 +176,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Configure instance fields // Configure instance fields
cycles = 0; cycles = 0;
exception.code = 0; exception = 0;
fetch = 0; fetch = -1;
irq = 0; irq = 0;
stage = FETCH; stage = FETCH;
@ -257,11 +255,10 @@ this.cycles = 0; // DEBUG: Stop processing after execute
return 1; // Unreachable 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) { int until(int cycles) {
if (stage == FATAL || stage == HALT) return stage == FATAL || stage == HALT ?
return cycles; cycles : Math.min(cycles, this.cycles);
return cycles < 0 ? this.cycles : Math.min(cycles, this.cycles);
} }
@ -273,19 +270,18 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Operations for exception stage // Operations for exception stage
private boolean exception() { private boolean exception() {
// Application callback // Check for breakpoints
vue.breakCode = vue.onException() ? 1 : 0; if (vue.onBreakpoint(Breakpoint.EXCEPTION))
if (vue.breakCode != 0)
return true; return true;
// Configure working variables // Configure working variables
exception.code &= 0xFFFF; exception &= 0xFFFF;
boolean isIRQ = (exception.code & 0xFF00) == 0xFE00; boolean isIRQ = (exception & 0xFF00) == 0xFE00;
int psw = getSystemRegister(Vue.PSW); int psw = getSystemRegister(Vue.PSW);
// Fatal exception // Fatal exception
if (psw_np != 0) { 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(0x00000004, Vue.S32, psw);
vue.write(0x00000008, Vue.S32, pc); vue.write(0x00000008, Vue.S32, pc);
stage = FATAL; stage = FATAL;
@ -294,7 +290,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Duplexed exception // Duplexed exception
if (psw_ep != 0) { if (psw_ep != 0) {
ecr_fecc = exception.code; ecr_fecc = exception;
fepc = pc; fepc = pc;
fepsw = psw; fepsw = psw;
psw_np = 1; psw_np = 1;
@ -303,25 +299,25 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Regular exception // Regular exception
else { else {
ecr_eicc = exception.code; ecr_eicc = exception;
eipc = pc; eipc = pc;
eipsw = psw; eipsw = psw;
psw_ep = 1; psw_ep = 1;
pc = 0xFFFF0000 | exception.code & 0xFFF0; pc = 0xFFFF0000 | exception & 0xFFF0;
if (pc == 0xFFFFFF70) // FIV if (pc == 0xFFFFFF70) // FIV
pc = 0xFFFFFF60; pc = 0xFFFFFF60;
} }
// Interrupt // Interrupt
if (isIRQ) { if (isIRQ) {
psw_i = Math.min(15, exception.code >> 4 & 15); psw_i = Math.min(15, exception >> 4 & 15);
if (stage == HALT) if (stage == HALT)
pc += 2; pc += 2;
} }
// Common processing // Common processing
cycles = 0; // TODO: Determine the actual number cycles = 0; // TODO: Determine the actual number
exception.code = 0; exception = 0;
psw_ae = 0; psw_ae = 0;
psw_id = 1; psw_id = 1;
stage = FETCH; stage = FETCH;
@ -331,9 +327,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Operations for execute stage // Operations for execute stage
private boolean execute() { private boolean execute() {
// Application callback // Check for breakpoints
vue.breakCode = vue.onExecute() ? 1 : 0; if (vue.onBreakpoint(Breakpoint.EXECUTE))
if (vue.breakCode != 0)
return true; return true;
// Processing by instruction ID // Processing by instruction ID
@ -415,7 +410,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
case Vue.XORI : XORI (); break; case Vue.XORI : XORI (); break;
//case Vue.XORNBSU: XORNBSU(); break; //case Vue.XORNBSU: XORNBSU(); break;
default: // Invalid instruction default: // Invalid instruction
exception.code = 0xFF90; exception = 0xFF90;
} }
// An application break was requested // An application break was requested
@ -423,7 +418,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
return true; return true;
// Common processing // Common processing
if (exception.code == 0) { if (exception == 0) {
cycles += CYCLES[inst.id]; cycles += CYCLES[inst.id];
pc += inst.size; pc += inst.size;
} }
@ -437,9 +432,14 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Operations for fetch stage // Operations for fetch stage
private boolean fetch() { private boolean fetch() {
// Entering the fetch stage
if (fetch == -1)
fetch = 0;
// Read the bits from the bus // Read the bits from the bus
if (read(pc + (fetch << 1), Vue.U16, fetch)) if (read(pc + (fetch << 1), Vue.U16, fetch))
return true; return true;
// TODO: Determine how many cycles this takes
// First unit // First unit
if (fetch == 0) { if (fetch == 0) {
@ -453,7 +453,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Second unit // Second unit
else { else {
inst.bits |= access.value & 0xFFFF; inst.bits |= access.value & 0xFFFF;
fetch = 0; fetch = -1;
} }
// Decode the instruction and advance to execute stage // Decode the instruction and advance to execute stage
@ -471,9 +471,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
access.type = type; access.type = type;
access.value = vue.read(address, type); access.value = vue.read(address, type);
// Application callback // Check for breakpoints
vue.breakCode = vue.onRead() ? 1 : 0; return vue.onBreakpoint(Breakpoint.READ);
return vue.breakCode != 0;
} }
// Test a condition // Test a condition
@ -495,16 +494,16 @@ this.cycles = 0; // DEBUG: Stop processing after execute
private boolean testException() { private boolean testException() {
// Check for an interrupt // 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; int level;
for (level = 4; level >= 0; level--) for (level = 4; level >= 0; level--)
if ((irq >> level & 1) != 0) if ((irq >> level & 1) != 0)
break; break;
exception.code = 0xFE00 | level << 4; exception = 0xFE00 | level << 4;
} }
// There is no exception // There is no exception
if (exception.code == 0) if (exception == 0)
return stage == HALT && cycles == 0; // No further processing return stage == HALT && cycles == 0; // No further processing
// An exception has occurred // An exception has occurred
@ -522,14 +521,12 @@ this.cycles = 0; // DEBUG: Stop processing after execute
access.type = type; access.type = type;
access.value = value; access.value = value;
// Application callback // Check for breakpoints
vue.breakCode = vue.onWrite() ? 1 : 0; if (vue.onBreakpoint(Breakpoint.WRITE))
if (vue.breakCode != 0)
return true; return true;
if (access.type == Vue.CANCEL)
return false;
// Perform the operation // Perform the operation
if (access.type != Vue.CANCEL)
vue.write(access.address, access.type, access.value); vue.write(access.address, access.type, access.value);
return false; return false;
} }
@ -572,7 +569,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Invalid operation // Invalid operation
if (value > 0x7FFFFFFF || value < 0x80000000) { if (value > 0x7FFFFFFF || value < 0x80000000) {
exception.code = 0xFF70; exception = 0xFF70;
return; return;
} }
@ -599,7 +596,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
)) continue; )) continue;
// The value is a reserved operand // The value is a reserved operand
exception.code = 0xFF60; exception = 0xFF60;
psw_fro = 1; psw_fro = 1;
return true; return true;
} }
@ -611,7 +608,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Overflow // Overflow
if (full > Float.MAX_VALUE || full < -Float.MAX_VALUE) { if (full > Float.MAX_VALUE || full < -Float.MAX_VALUE) {
exception.code = 0xFF64; exception = 0xFF64;
psw_fov = 1; psw_fov = 1;
return; return;
} }
@ -803,7 +800,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Zero division // Zero division
if (right == 0) { if (right == 0) {
exception.code = 0xFF80; exception = 0xFF80;
return; return;
} }
@ -837,7 +834,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// An exception has occurred // An exception has occurred
if (right == 0) { if (right == 0) {
exception.code = left == 0 ? 0xFF70 : 0xFF68; exception = left == 0 ? 0xFF70 : 0xFF68;
return; return;
} }
@ -854,7 +851,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Zero division // Zero division
if (right == 0) { if (right == 0) {
exception.code = 0xFF80; exception = 0xFF80;
return; return;
} }
@ -1105,7 +1102,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
// Trap // Trap
private void TRAP() { private void TRAP() {
exception.code = 0xFFA0 | inst.imm & 31; exception = 0xFFA0 | inst.imm & 31;
pc += 2; pc += 2;
} }

View File

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

View File

@ -135,8 +135,12 @@ public class Instruction {
case 2: case 2:
reg2 = bits >> 21 & 31; reg2 = bits >> 21 & 31;
imm = extend < 0 ? bits << 11 >> 27 : bits >> 16 & 31; imm = extend < 0 ? bits << 11 >> 27 : bits >> 16 & 31;
if (id == BITSTRING) if (id == BITSTRING) {
id = imm >= 16 ? Vue.ILLEGAL : LOOKUP_BITSTRING[imm]; id = imm >= 16 ? Vue.ILLEGAL : LOOKUP_BITSTRING[imm];
subopcode = imm;
}
if (id == Vue.SETF)
cond = imm & 15;
break; break;
case 3: case 3:
opcode = 0x20; opcode = 0x20;

View File

@ -7,11 +7,11 @@ import java.util.*;
class JavaVue extends Vue { class JavaVue extends Vue {
// Package fields // Package fields
int breakCode; // Application break code int breakCode; // Application breakpoint code
int breakType; // Most recent breakpoint scenario
CPU cpu; // Processor CPU cpu; // Processor
int hook; // Most recent breakpoint hook
GamePak pak; // Game pak GamePak pak; // Game pak
int[] stack; // Breakpoint evaluation stack int[] stack; // Breakpoint condition evaluation stack
byte[] wram; // System WRAM byte[] wram; // System WRAM
@ -53,50 +53,47 @@ class JavaVue extends Vue {
// Process up to the given number of cycles // Process up to the given number of cycles
do { do {
// Determine the number of cycles during which nothing will happen // Determine the number of cycles where no breakpoint will occur
int cycles = -1; int cycles = maxCycles; // min(maxCycles, nextFrameCycles)
cycles = cpu .until(cycles); cycles = cpu .until(cycles);
//cycles = pad .until(cycles); //cycles = pad .until(cycles);
//cycles = link .until(cycles); //cycles = link .until(cycles);
//cycles = timer.until(cycles); //cycles = timer.until(cycles);
//cycles = vip .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 // Process all system components
breakCode = 0;
cpu .emulate(cycles); cpu .emulate(cycles);
//pad .emulate(cycles); //pad .emulate(cycles);
//link .emulate(cycles); //link .emulate(cycles);
//timer.emulate(cycles); //timer.emulate(cycles);
//vip .emulate(cycles); //vip .emulate(cycles);
//vsu .emulate(cycles); //vsu .emulate(cycles);
// An application break was requested
//if (...)
// break;
// Update the number of cycles remaining
if (maxCycles >= 0)
maxCycles -= cycles; maxCycles -= cycles;
} while (maxCycles != 0); } while (breakCode == 0 && maxCycles != 0);
// A break condition has occurred // A break condition has occurred
return Math.max(0, maxCycles); return maxCycles;
} }
// Evaluate an expression // Retrieve a snapshot of the current state's memory access
public Object evaluate(String expression) { public Access getAccess() {
return null; return cpu.access;
}
// Retrieve the most recent applicaiton break code
public int getBreakCode() {
return breakCode;
} }
// Retrieve the most recent exception code // Retrieve the most recent exception code
public int getException() { public int getExceptionCode() {
return cpu.exception.code; return cpu.exception;
}
// Retrieve a snapshot of the current state's instruction
public Instruction getInstruction() {
return cpu.inst;
} }
// Retrieve a register value // Retrieve a register value
@ -139,6 +136,12 @@ class JavaVue extends Vue {
// Read a value from the CPU bus // Read a value from the CPU bus
public int read(int address, int type) { public int read(int address, int type) {
// Error checking
if (type < 0 || type > 4)
return 0;
// Perform the operation
switch (address >> 24 & 7) { switch (address >> 24 & 7) {
case 5: return readBuffer(wram , address, type); case 5: return readBuffer(wram , address, type);
case 6: return readBuffer(pak.ram, address, type); case 6: return readBuffer(pak.ram, address, type);
@ -250,29 +253,48 @@ class JavaVue extends Vue {
// Package Methods // // Package Methods //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Evaluate the condition in a breakpoint // Retrieve the current state's breakpoint scenario
boolean evaluate(Breakpoint brk) { int getBreakType() {
return false; return breakType;
} }
// Exception break handler // Retrieve the current instruction fetch index
boolean onException() { int getFetch() {
return onHook(Breakpoint.EXCEPTION); return cpu.fetch;
} }
// Execute break handler // Check for breakpoints
boolean onExecute() { boolean onBreakpoint(int breakType) {
return onHook(Breakpoint.EXECUTE); int end = 0;
boolean ranged = false;
int start = 0;
this.breakType = breakType;
// Processing for Execute
if (breakType == Breakpoint.EXECUTE) {
ranged = true;
start = cpu.pc;
end = start + cpu.inst.size - 1;
} }
// Read break handler // Processing for Read and Write
boolean onRead() { else if (breakType==Breakpoint.READ || breakType==Breakpoint.WRITE) {
return onHook(Breakpoint.READ); ranged = true;
start = cpu.access.address;
end = start + TYPE_SIZES[cpu.access.type] - 1;
} }
// Write break handler // Check all breakpoints
boolean onWrite() { int count = breakpoints.size();
return onHook(Breakpoint.WRITE); 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 // 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 // Write a value to a byte buffer
static void writeBuffer(byte[] data, int address, int type, int value) { 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;
}
} }

View File

@ -117,13 +117,20 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_emulate
return vueEmulate(&core->vue, maxCycles); return vueEmulate(&core->vue, maxCycles);
} }
// Retrieve the application break code // Retrieve the most recent exception code
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode
(JNIEnv *env, jobject vue, jlong handle) { (JNIEnv *env, jobject vue, jlong handle) {
Core *core = *(Core **)&handle; Core *core = *(Core **)&handle;
return vueGetBreakCode(&core->vue); 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 // Retrieve a register value
JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) { (JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) {

View File

@ -25,6 +25,7 @@ class NativeVue extends Vue {
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Produce a new breakpoint and add it to the collection // Produce a new breakpoint and add it to the collection
private native void breakpoint(long handle);
public Breakpoint breakpoint() { public Breakpoint breakpoint() {
var brk = super.breakpoint(); var brk = super.breakpoint();
breakpoint(handle); breakpoint(handle);
@ -41,14 +42,26 @@ class NativeVue extends Vue {
public int emulate(int maxCycles) public int emulate(int maxCycles)
{ return emulate(handle, 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); private native int getBreakCode(long handle);
public int getBreakCode() { return getBreakCode(handle); } public int getBreakCode() { return getBreakCode(handle); }
// Retrieve the most recent exception code // Retrieve the most recent exception code
private native int getException(long handle); private native int getExceptionCode(long handle);
public int getException() { public int getExceptionCode() {
return getException(handle); 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 // Retrieve a register value
@ -75,6 +88,7 @@ class NativeVue extends Vue {
{ return readBytes(handle, address, dest, offset, length); } { return readBytes(handle, address, dest, offset, length); }
// Remove a breakpoint from the collection // Remove a breakpoint from the collection
private native void remove(long handle, int index);
public boolean remove(Breakpoint brk) { public boolean remove(Breakpoint brk) {
int index = breakpoints.indexOf(brk); int index = breakpoints.indexOf(brk);
if (!super.remove(brk)) if (!super.remove(brk))
@ -117,9 +131,16 @@ class NativeVue extends Vue {
// Package Methods // // Package Methods //
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// Evaluate the condition in a breakpoint // Retrieve the current state's breakpoint scenario
boolean evaluate(Breakpoint brk) { private native int getBreakType(long handle);
return false; 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 // A breakpoint's address ranges have changed
@ -137,16 +158,4 @@ class NativeVue extends Vue {
super.updateTokens(brk); 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);
} }

View File

@ -138,7 +138,7 @@ public abstract class Vue {
// Produce an emulation core context // Produce an emulation core context
public static Vue create(boolean useNative) { public static Vue create(boolean useNative) {
return !useNative ? new JavaVue() : return !useNative ? new JavaVue() :
isNativeLoaded() ? new NativeVue() : null; nativeID != null ? new NativeVue() : null;
} }
// Retrieve the ID of the loaded native library, if any // Retrieve the ID of the loaded native library, if any
@ -146,11 +146,6 @@ public abstract class Vue {
return nativeID; return nativeID;
} }
// Determine whether the native module is loaded
public static boolean isNativeLoaded() {
return nativeID != null;
}
// Specify the ID of the loaded native library // Specify the ID of the loaded native library
public static void setNativeID(String nativeID) { public static void setNativeID(String nativeID) {
Vue.nativeID = nativeID; Vue.nativeID = nativeID;
@ -180,40 +175,32 @@ public abstract class Vue {
return brk; 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 // Release any used resources
public abstract void dispose(); public abstract void dispose();
// Process the simulation // Process the simulation
public abstract int emulate(int maxCycles); 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 // 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 // Retrieve a register value
public abstract int getRegister(int index, boolean system); public abstract int getRegister(int index, boolean system);
@ -224,6 +211,11 @@ public abstract class Vue {
// Determine whether the context is native-backed // Determine whether the context is native-backed
public abstract boolean isNative(); public abstract boolean isNative();
// 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 // Read a value from the CPU bus
public abstract int read(int address, int type); 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, public abstract boolean readBytes(int address, byte[] dest, int offset,
int length); 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 // Initialize all system components
public abstract void reset(); public abstract void reset();
@ -253,6 +253,12 @@ public abstract class Vue {
// Package Methods // // 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 // A breakpoint's address ranges have changed
void updateRanges(Breakpoint brk) { } void updateRanges(Breakpoint brk) { }