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

View File

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

View File

@ -155,55 +155,46 @@ static void writeBytes(uint8_t *data, uint32_t datlen, uint32_t address,
*****************************************************************************/
/* Process the simulation */
int32_t vueEmulate(Vue *vue, int32_t maxCycles) {
int32_t cycles; /* Number of cycles to process */
uint32_t vueEmulate(Vue *vue, uint32_t maxCycles) {
uint32_t cycles; /* Number of cycles to process */
/* Process up to the given number of cycles */
do {
/* Determine the number of cycles during which nothing will happen */
cycles = -1;
/* Determine the number of cycles where no breakpoint will occur */
cycles = maxCycles; /* min(maxCycles, nextFrameCycles) */
cycles = cpuUntil (vue, cycles);
/*cycles = padUntil (vue, cycles);*/
/*cycles = linkUntil (vue, cycles);*/
/*cycles = timerUntil(vue, cycles);*/
/*cycles = vipUntil (vue, cycles);*/
/*cycles = vsuUntil (vue, cycles);*/
cycles = cpuUntil (vue, cycles);
/* Range checking */
if (cycles == -1) /* No activity on any component */
break;
if (maxCycles >= 0) /* Restrict to given number of cycles */
cycles = cycles < maxCycles ? cycles : maxCycles;
/* Process all system components */
vue->breakCode = 0;
cpuEmulate (vue, cycles);
/*padEmulate (vue, cycles);*/
/*pakEmulate (vue, cycles);*/
/*linkEmulate (vue, cycles);*/
/*timerEmulate(vue, cycles);*/
/*vipEmulate (vue, cycles);*/
/*vsuEmulate (vue, cycles);*/
cpuEmulate (vue, cycles);
/* An application break was requested */
if (vue->breakCode != 0)
break;
/* Update the number of cycles remaining */
if (maxCycles >= 0)
maxCycles -= cycles;
} while (maxCycles != 0);
} while (vue->breakCode == 0 && maxCycles != 0);
/* A break condition has occurred */
return maxCycles;
}
/* Retrieve the application break code */
/* Retrieve the most recent applicaiton break code */
int32_t vueGetBreakCode(Vue *vue) {
return vue == NULL ? 0 : vue->breakCode;
}
/* Retrieve the most recent exception code */
uint16_t vueGetExceptionCode(Vue *vue) {
return vue == NULL ? 0 : vue->cpu.exception;
}
/* Retrieve the value of a register */
int32_t vueGetRegister(Vue *vue, int32_t index, vbool system) {
@ -234,17 +225,69 @@ void vueInitialize(Vue *vue) {
return;
vue->onException = NULL;
vue->onExecute = NULL;
vue->onFrame = NULL;
vue->onRead = NULL;
vue->onWrite = NULL;
vue->pak.ram = NULL;
vue->pak.rom = NULL;
vueReset(vue);
}
/* Specify an exception breakpoint callback */
VueOnException vueOnException(Vue *vue, VueOnException callback) {
VueOnException ret;
if (vue == NULL)
return NULL;
ret = vue->onException;
vue->onException = callback;
return ret;
}
/* Specify an execute breakpoint callback */
VueOnExecute vueOnExecute(Vue *vue, VueOnExecute callback) {
VueOnExecute ret;
if (vue == NULL)
return NULL;
ret = vue->onExecute;
vue->onExecute = callback;
return ret;
}
/* Specify a frame breakpoint callback */
VueOnFrame vueOnFrame(Vue *vue, VueOnFrame callback) {
VueOnFrame ret;
if (vue == NULL)
return NULL;
ret = vue->onFrame;
vue->onFrame = callback;
return ret;
}
/* Specify a read breakpoint callback */
VueOnAccess vueSetRead(Vue *vue, VueOnAccess callback) {
VueOnAccess ret;
if (vue == NULL)
return NULL;
ret = vue->onRead;
vue->onRead = callback;
return ret;
}
/* Specify a write breakpoint callback */
VueOnAccess vueOnWrite(Vue *vue, VueOnAccess callback) {
VueOnAccess ret;
if (vue == NULL)
return NULL;
ret = vue->onWrite;
vue->onWrite = callback;
return ret;
}
/* Read a value from the CPU bus */
int32_t vueRead(Vue *vue, uint32_t address, int32_t type) {
/* Error checking */
if (vue == NULL)
if (vue == NULL || type < 0 || type > 4)
return 0;
/* Perform the operation */
@ -299,28 +342,10 @@ void vueReset(Vue *vue) {
/* Reset state */
cpuReset(vue);
pakReset(vue);
for (x = 0; x < 0x1000; x++)
for (x = 0; x < 0x10000; x++)
vue->wram[x] = 0;
}
/* Specify an exception breakpoint callback */
void vueSetException(Vue *vue, VueOnException callback) {
if (vue != NULL)
vue->onException = callback;
}
/* Specify an execute breakpoint callback */
void vueSetExecute(Vue *vue, VueOnExecute callback) {
if (vue != NULL)
vue->onExecute = callback;
}
/* Specify a read breakpoint callback */
void vueSetRead(Vue *vue, VueOnRead callback) {
if (vue != NULL)
vue->onRead = callback;
}
/* Specify a value for a register */
int32_t vueSetRegister(Vue *vue, int32_t index, vbool system, int32_t value) {
return vue == NULL ? 0 :
@ -348,12 +373,6 @@ vbool vueSetROM(Vue *vue, uint8_t *rom, uint32_t size) {
return VUE_TRUE;
}
/* Specify a write breakpoint callback */
void vueSetWrite(Vue *vue, VueOnRead callback) {
if (vue != NULL)
vue->onWrite = callback;
}
/* Write a value to the CPU bus */
void vueWrite(Vue *vue, uint32_t address, int32_t type, int32_t value) {

View File

@ -49,43 +49,7 @@ public class Main {
useNative = true;
// Begin application operations
//new App(useNative);
var brk = new Breakpoint(null);
/*
//String exp =
// "([sp - 8] & 3) << 6 != 12.0 + r6 * ecr && [0x0500008C + r8] == 0";
//String exp = "float true + -3 * 4";
String exp = "1100 ^ uword 1010";
System.out.println("\n" + exp);
if (brk.setCondition(exp))
System.out.println(brk.debugTokens());
else {
var err = brk.getConditionError();
System.out.println("Error " +
err.code + "\t" +
err.position + ":" +
err.text
);
}
*/
/*
exp = "123, 456 - 987 , f0-fffffff2";
System.out.println("\n" + exp);
if (brk.setAddresses(exp))
System.out.println(brk.debugRanges());
else {
var err = brk.getAddressError();
System.out.println("Error " +
err.code + "\t" +
err.position + ":" +
err.text
);
}
*/
new App(useNative);
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -63,20 +63,23 @@ public class Breakpoint {
// Functional symbol IDs
private static final int ADDRESS = 0;
private static final int CODE = 1;
private static final int COND = 2;
private static final int DISP = 3;
private static final int FORMAT = 4;
private static final int ID = 5;
private static final int IMM = 6;
private static final int OPCODE = 7;
private static final int REG1 = 8;
private static final int REG2 = 9;
private static final int REGID = 10;
private static final int SIZE = 11;
private static final int SUBOPCODE = 12;
private static final int VALUE = 13;
private static final int VECTOR = 14;
private static final int BREAK = 1;
private static final int CODE = 2;
private static final int COND = 3;
private static final int DISP = 4;
private static final int FETCH = 5;
private static final int FORMAT = 6;
private static final int ID = 7;
private static final int IMM = 8;
private static final int OPCODE = 9;
private static final int REG1 = 10;
private static final int REG2 = 11;
private static final int REGID = 12;
private static final int SIZE = 13;
private static final int SUBOPCODE = 14;
private static final int TYPE = 15;
private static final int VALUE = 16;
private static final int VECTOR = 17;
// Evaluation operator IDs
private static final int GROUP = 0; // ()
@ -190,6 +193,12 @@ public class Breakpoint {
SYMDEFS.put("false", new Def(BOOL, 0));
SYMDEFS.put("true" , new Def(BOOL, 1));
// Break type symbl definitions
SYMDEFS.put("exception", new Def(SIGNED, EXCEPTION));
SYMDEFS.put("execute" , new Def(SIGNED, EXECUTE ));
SYMDEFS.put("read" , new Def(SIGNED, READ ));
SYMDEFS.put("write" , new Def(SIGNED, WRITE ));
// Condition code symbol definitions
SYMDEFS.put("c" , new Def(SIGNED, 1));
SYMDEFS.put("e" , new Def(SIGNED, 2));
@ -293,9 +302,11 @@ public class Breakpoint {
// Functional symbol definitions
SYMDEFS.put("address" , new Def(SYMBOL, ADDRESS ));
SYMDEFS.put("break" , new Def(SYMBOL, BREAK ));
SYMDEFS.put("code" , new Def(SYMBOL, CODE ));
SYMDEFS.put("cond" , new Def(SYMBOL, COND ));
SYMDEFS.put("disp" , new Def(SYMBOL, DISP ));
SYMDEFS.put("fetch" , new Def(SYMBOL, FETCH ));
SYMDEFS.put("format" , new Def(SYMBOL, FORMAT ));
SYMDEFS.put("id" , new Def(SYMBOL, ID ));
SYMDEFS.put("imm" , new Def(SYMBOL, IMM ));
@ -305,6 +316,7 @@ public class Breakpoint {
SYMDEFS.put("regid" , new Def(SYMBOL, REGID ));
SYMDEFS.put("size" , new Def(SYMBOL, SIZE ));
SYMDEFS.put("subopcode", new Def(SYMBOL, SUBOPCODE));
SYMDEFS.put("type" , new Def(SYMBOL, TYPE ));
SYMDEFS.put("value" , new Def(SYMBOL, VALUE ));
SYMDEFS.put("vector" , new Def(SYMBOL, VECTOR ));
@ -454,9 +466,34 @@ public class Breakpoint {
// Evaluate the condition against the emulation context
public boolean evaluate() {
return vue == null || conditionError.code != NONE ?
false : tokens.length == 0 ? true :
evaluate(null, null, null) != 0;
// Error checking
if (vue == null)
return false;
// Retrieve state objects
Access acc = vue.getAccess ();
int breakType = vue.getBreakType ();
Instruction inst = vue.getInstruction();
// Error checking
if (!appliesTo(breakType))
return false;
// Check address ranges for execute
if (breakType == EXECUTE) {
int pc = vue.getRegister(Vue.PC, true);
if (!inRange(pc, pc + inst.size - 1))
return false;
}
// Check address ranges for read and write
else if (breakType == READ || breakType == WRITE &&
!inRange(acc.address, acc.address+JavaVue.TYPE_SIZES[acc.type]-1))
return false;
// Evaluate the condition
return isTrue(new int[depth], breakType, inst, acc);
}
// Retrieve the most recent address text
@ -504,6 +541,15 @@ public class Breakpoint {
return name;
}
// Determine whether the breakpoint can be used
boolean isActive() {
return
isEnabled &&
addressError .code == NONE &&
conditionError.code == NONE
;
}
// Determine whether the breakpoint is enabled
public boolean isEnabled() {
return isEnabled;
@ -738,12 +784,16 @@ public class Breakpoint {
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Cast a value to the condition evaluation's data type
Object cast(int value) {
if (conditionError.code != NONE)
return conditionError;
// Determine whether the breakpoint applies to a break scenario
boolean appliesTo(int breakType) {
return isActive() && (breakType == 0 || (breakType & hooks) != 0);
}
// Perform a typed evaluation of the condition expression
Object evaluateTyped(int[]stack,int breakType,Instruction inst,Access acc){
if (tokens.length == 0)
return null;
int value = evaluate(stack, breakType, inst, acc);
switch (dataType) {
case BOOL : return (Boolean) (value != 0);
case FLOAT : return (Float ) Float.intBitsToFloat(value);
@ -752,54 +802,28 @@ public class Breakpoint {
return (Integer) value;
}
// Evaluate the condition, returning only the computed value
int evaluate(int[] stack, Instruction inst, Access acc) {
// Determine whether the breakpoint applies to a given address range
boolean inRange(int start, int end) {
// Error checking
if (conditionError.code != NONE)
return 0;
if (tokens.length == 0)
return 1;
if (stack == null)
stack = new int[depth];
// Implicit inclusion
if (ranges.length == 0)
return true;
// Process all tokens
int size = 0;
for (var tok : tokens) switch (tok.type) {
// Binary operator
case BINARY:
stack[size - 2] =
evalBinary(tok.id, stack[size - 2], stack[size - 1]);
break;
// Literal
case BOOL:
case FLOAT:
case SIGNED:
case UNSIGNED:
stack[size++] = tok.id;
break;
// CPU register
case PROREG:
case SYSREG:
stack[size++] = vue.getRegister(tok.id, tok.type == SYSREG);
break;
// Symbol
case SYMBOL:
stack[size++] = 0;
break;
// Unary operator
case UNARY:
stack[size - 1] = evalUnary(tok.id, stack[size - 1]);
break;
// Check all ranges
for (var range : ranges)
if (
Integer.compareUnsigned(
start - range[0], range[1] - range[0]) <= 0 ||
Integer.compareUnsigned(
range[0] - start , end - start ) <= 0
) return true;
return false;
}
// Return the remaining stack value
return stack[0];
// Determine whether the breakpoint's condition is truthy
boolean isTrue(int[] stack, int breakType, Instruction inst, Access acc) {
return tokens.length == 0 ? true :
evaluate(stack, breakType, inst, acc) != 0;
}
// Produce a one-dimensional array from the address ranges
@ -829,21 +853,6 @@ public class Breakpoint {
return depth;
}
// Retrieve the bit mask for enabled hooks
int getHooks() {
return hooks;
}
// Retrieve the list of address ranges
int[][] getRanges() {
return ranges;
}
// Retrieve the list of condition tokens
Token[] getTokens() {
return tokens;
}
// The breakpoint is being removed from its emulation context
void remove() {
vue = null;
@ -1216,9 +1225,10 @@ public class Breakpoint {
// Binary operators
if (tok.type == BINARY) {
int dataType = Math.max(tok.left.dataType, tok.right.dataType);
int id = tok.id;
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;
// Process by ID
@ -1269,7 +1279,7 @@ public class Breakpoint {
var top = op == 0 ? tok.left : tok.right;
// The operand is already the result type
if (top.dataType == tok.dataType)
if (top.dataType == dataType)
continue;
// Select the ID of the conversion operation
@ -1284,7 +1294,7 @@ public class Breakpoint {
// Convert the literal operand directly
if (isLiteral(top)) {
top.id = evalUnary(cvt, top);
top.type = top.dataType = tok.dataType;
top.type = top.dataType = dataType;
}
// Insert a conversion token
@ -1296,7 +1306,7 @@ public class Breakpoint {
case SIGNED : imp.text = "s32" ; break;
case UNSIGNED: imp.text = "u32" ; break;
}
imp.dataType = tok.dataType;
imp.dataType = dataType;
imp.id = -cvt * 4 + top.dataType;
imp.parent = tok;
imp.right = top;
@ -1444,202 +1454,71 @@ public class Breakpoint {
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Adjust a float value as needed
private static float adjust(float value) {
int bits = Float.floatToRawIntBits(value);
int exp = bits & 0x7F800000;
return
(bits & 0x7FFFFFFF) == 0 || // Zero
exp == 0x7F800000 || // Indefinite
exp == 0 // Denormal
? 0 : value;
}
// Determine the required stack size to evaluate the expression
private int depth() {
// Error checking
if (conditionError.code != NONE)
return 0;
// Count the maximum size of the stack
int max = 0;
int size = 0;
for (var tok : tokens) switch (tok.type) {
case BINARY: size--; break;
case UNARY : break;
default : max = Math.max(max, ++size);
}
return max;
}
// Remove the fraction part of a float
private static float trunc(float x) {
return (float) (x < 0 ? Math.ceil(x) : Math.floor(x));
}
///////////////////////////////////////////////////////////////////////////
// Debugging Methods //
///////////////////////////////////////////////////////////////////////////
// Produce a string representation of the condition token list
public String debugTokens() {
var ret = new StringBuilder();
// The condition was not successfully parsed
if (conditionError.code != NONE)
return "Error";
// Determine the maximum width of the text fields
int max = 0;
for (var tok : tokens)
max = Math.max(max, tok.text.length());
// Output all tokens
var last = tokens[tokens.length - 1];
for (var tok : tokens) {
// Text
ret.append(String.format("%-" + max + "s ", tok.text));
// Type
String type = null;
switch (tok.type) {
case BINARY : type = "Binary" ; break;
case BOOL : type = "Bool" ; break;
case FLOAT : type = "Float" ; break;
case PROREG : type = "ProReg" ; break;
case SYSREG : type = "SysReg" ; break;
case SIGNED : type = "Signed" ; break;
case SYMBOL : type = "Symbol" ; break;
case UNARY : type = "Unary" ; break;
case UNSIGNED: type = "Unsigned"; break;
default: type = Integer.toString(tok.type);
}
ret.append(String.format("%-8s ", type));
// Data type
switch (tok.dataType) {
case BOOL : type = "Bool" ; break;
case FLOAT : type = "Float" ; break;
case SIGNED : type = "Signed" ; break;
case UNSIGNED: type = "Unsigned"; break;
default: type = Integer.toString(tok.dataType);
}
ret.append(String.format("%-8s ", type));
// Operator
if (tok.type == BINARY || tok.type == UNARY) {
String id = Integer.toString(tok.id);
switch (-tok.id / 4) {
case 0 : id = "REINTERPRET"; break;
case READ8 : id = "READ8" ; break;
case READ16 : id = "READ16" ; break;
case READ32 : id = "READ32" ; break;
case ADD : id = "ADD" ; break;
case AND_B : id = "AND_B" ; break;
case AND_L : id = "AND_L" ; break;
case BOOL_ : id = "BOOL_" ; break;
case CEIL : id = "CEIL" ; break;
case DIVIDE : id = "DIVIDE" ; break;
case EQUAL : id = "EQUAL" ; break;
case FLOAT_ : id = "FLOAT_" ; break;
case FLOOR : id = "FLOOR" ; break;
case GREATER : id = "GREATER" ; break;
case GREQUAL : id = "GREQUAL" ; break;
case LEFT_L : id = "LEFT_L" ; break;
case LEQUAL : id = "LEQUAL" ; break;
case LESS : id = "LESS" ; break;
case MULTIPLY : id = "MULTIPLY" ; break;
case NEQUAL : id = "NEQUAL" ; break;
case NOT_B : id = "NOT_B" ; break;
case NOT_L : id = "NOT_L" ; break;
case NEGATE : id = "NEGATE" ; break;
case OR_B : id = "OR_B" ; break;
case OR_L : id = "OR_L" ; break;
case REMAINDER: id = "REMAINDER" ; break;
case RIGHT_A : id = "RIGHT_A" ; break;
case RIGHT_L : id = "RIGHT_L" ; break;
case ROUND : id = "ROUND" ; break;
case S8 : id = "S8" ; break;
case S16 : id = "S16" ; break;
case S32 : id = "S32" ; break;
case SUBTRACT : id = "SUBTRACT" ; break;
case TRUNC : id = "TRUNC" ; break;
case U8 : id = "U8" ; break;
case U16 : id = "U16" ; break;
case U32 : id = "U32" ; break;
case XFLOAT : id = "XFLOAT" ; break;
case XOR_B : id = "XOR_B" ; break;
case XOR_L : id = "XOR_L" ; break;
case XS32 : id = "XS32" ; break;
case XU32 : id = "XU32" ; break;
default: ret.append(Integer.toString(tok.id));
}
ret.append(String.format("%-11s", id));
}
// Symbol or literal
else switch (tok.type) {
case PROREG:
case SYSREG:
case SYMBOL:
ret.append(Integer.toString(tok.id));
break;
case FLOAT:
ret.append(String.format("%.6f",
Float.intBitsToFloat(tok.id)));
break;
case BOOL:
case SIGNED:
ret.append(Integer.toString(tok.id));
break;
case UNSIGNED:
ret.append(Long.toString(tok.id & 0xFFFFFFFFL));
break;
default: ret.append("Error");
}
// Advance to the next line
if (tok != last)
ret.append("\n");
}
return ret.toString();
}
// Produce a string representation of the internal address ranges
public String debugRanges() {
var ret = new StringBuilder();
// The address ranges were not successfully parsed
if (addressError.code != NONE)
return "Error";
// Process ranges
for (int x = 0; x < ranges.length; x++) {
var range = ranges[x];
if (x > 0)
ret.append("\n");
ret.append(String.format("%08X", range[0]));
if (range[0] != range[1])
ret.append(String.format("-%08X", range[1]));
}
return ret.toString();
}
///////////////////////////////////////////////////////////////////////////
// Evaluation Methods //
///////////////////////////////////////////////////////////////////////////
// Evaluate the condition, returning only the computed value
private int evaluate(int[]stack,int breakType,Instruction inst,Access acc){
// Process all tokens
int size = 0;
for (var tok : tokens) switch (tok.type) {
// Binary operator
case BINARY:
stack[size - 2] =
evalBinary(tok.id, stack[size - 2], stack[size - 1]);
break;
// Literal
case BOOL:
case FLOAT:
case SIGNED:
case UNSIGNED:
stack[size++] = tok.id;
break;
// CPU register
case PROREG:
case SYSREG:
stack[size++] = vue.getRegister(tok.id, tok.type == SYSREG);
break;
// Unary operator
case UNARY:
stack[size - 1] = evalUnary(tok.id, stack[size - 1]);
break;
// Symbol
case SYMBOL:
int sym = inst.imm; // IMM, REGID, VECTOR
switch (tok.id) {
case ADDRESS : sym = acc.address ; break;
case BREAK : sym = breakType ; break;
case CODE : sym = vue.getExceptionCode(); break;
case COND : sym = inst.cond ; break;
case DISP : sym = inst.disp ; break;
case FETCH : sym = vue.getFetch (); break;
case FORMAT : sym = inst.format ; break;
case ID : sym = inst.id ; break;
case OPCODE : sym = inst.opcode ; break;
case REG1 : sym = inst.reg1 ; break;
case REG2 : sym = inst.reg2 ; break;
case SIZE : sym = inst.size ; break;
case SUBOPCODE: sym = inst.subopcode ; break;
case TYPE : sym = acc.type ; break;
case VALUE : sym = acc.value ; break;
}
stack[size++] = sym;
break;
}
// Return the remaining stack value
return stack[0];
}
// Evaluate a unary operator given a token
private int evalUnary(int id, Token tok) {
return evalUnary(-id * 4 + tok.dataType, tok.id);
@ -1988,4 +1867,196 @@ public class Breakpoint {
return 0;
}
///////////////////////////////////////////////////////////////////////////
// Private Methods //
///////////////////////////////////////////////////////////////////////////
// Adjust a float value as needed
private static float adjust(float value) {
int bits = Float.floatToRawIntBits(value);
int exp = bits & 0x7F800000;
return
(bits & 0x7FFFFFFF) == 0 || // Zero
exp == 0x7F800000 || // Indefinite
exp == 0 // Denormal
? 0 : value;
}
// Determine the required stack size to evaluate the expression
private int depth() {
// Error checking
if (conditionError.code != NONE)
return 0;
// Count the maximum size of the stack
int max = 0;
int size = 0;
for (var tok : tokens) switch (tok.type) {
case BINARY: size--; break;
case UNARY : break;
default : max = Math.max(max, ++size);
}
return max;
}
// Remove the fraction part of a float
private static float trunc(float x) {
return (float) (x < 0 ? Math.ceil(x) : Math.floor(x));
}
///////////////////////////////////////////////////////////////////////////
// Debugging Methods //
///////////////////////////////////////////////////////////////////////////
// Produce a string representation of the condition token list
public String debugTokens() {
var ret = new StringBuilder();
// The condition was not successfully parsed
if (conditionError.code != NONE)
return "Error";
// Determine the maximum width of the text fields
int max = 0;
for (var tok : tokens)
max = Math.max(max, tok.text.length());
// Output all tokens
var last = tokens[tokens.length - 1];
for (var tok : tokens) {
// Text
ret.append(String.format("%-" + max + "s ", tok.text));
// Type
String type = null;
switch (tok.type) {
case BINARY : type = "Binary" ; break;
case BOOL : type = "Bool" ; break;
case FLOAT : type = "Float" ; break;
case PROREG : type = "ProReg" ; break;
case SYSREG : type = "SysReg" ; break;
case SIGNED : type = "Signed" ; break;
case SYMBOL : type = "Symbol" ; break;
case UNARY : type = "Unary" ; break;
case UNSIGNED: type = "Unsigned"; break;
default: type = Integer.toString(tok.type);
}
ret.append(String.format("%-8s ", type));
// Data type
switch (tok.dataType) {
case BOOL : type = "Bool" ; break;
case FLOAT : type = "Float" ; break;
case SIGNED : type = "Signed" ; break;
case UNSIGNED: type = "Unsigned"; break;
default: type = Integer.toString(tok.dataType);
}
ret.append(String.format("%-8s ", type));
// Operator
if (tok.type == BINARY || tok.type == UNARY) {
String id = Integer.toString(tok.id);
switch (-tok.id / 4) {
case 0 : id = "REINTERPRET"; break;
case READ8 : id = "READ8" ; break;
case READ16 : id = "READ16" ; break;
case READ32 : id = "READ32" ; break;
case ADD : id = "ADD" ; break;
case AND_B : id = "AND_B" ; break;
case AND_L : id = "AND_L" ; break;
case BOOL_ : id = "BOOL_" ; break;
case CEIL : id = "CEIL" ; break;
case DIVIDE : id = "DIVIDE" ; break;
case EQUAL : id = "EQUAL" ; break;
case FLOAT_ : id = "FLOAT_" ; break;
case FLOOR : id = "FLOOR" ; break;
case GREATER : id = "GREATER" ; break;
case GREQUAL : id = "GREQUAL" ; break;
case LEFT_L : id = "LEFT_L" ; break;
case LEQUAL : id = "LEQUAL" ; break;
case LESS : id = "LESS" ; break;
case MULTIPLY : id = "MULTIPLY" ; break;
case NEQUAL : id = "NEQUAL" ; break;
case NOT_B : id = "NOT_B" ; break;
case NOT_L : id = "NOT_L" ; break;
case NEGATE : id = "NEGATE" ; break;
case OR_B : id = "OR_B" ; break;
case OR_L : id = "OR_L" ; break;
case REMAINDER: id = "REMAINDER" ; break;
case RIGHT_A : id = "RIGHT_A" ; break;
case RIGHT_L : id = "RIGHT_L" ; break;
case ROUND : id = "ROUND" ; break;
case S8 : id = "S8" ; break;
case S16 : id = "S16" ; break;
case S32 : id = "S32" ; break;
case SUBTRACT : id = "SUBTRACT" ; break;
case TRUNC : id = "TRUNC" ; break;
case U8 : id = "U8" ; break;
case U16 : id = "U16" ; break;
case U32 : id = "U32" ; break;
case XFLOAT : id = "XFLOAT" ; break;
case XOR_B : id = "XOR_B" ; break;
case XOR_L : id = "XOR_L" ; break;
case XS32 : id = "XS32" ; break;
case XU32 : id = "XU32" ; break;
default: ret.append(Integer.toString(tok.id));
}
ret.append(String.format("%-11s", id));
}
// Symbol or literal
else switch (tok.type) {
case PROREG:
case SYSREG:
case SYMBOL:
ret.append(Integer.toString(tok.id));
break;
case FLOAT:
ret.append(String.format("%.6f",
Float.intBitsToFloat(tok.id)));
break;
case BOOL:
case SIGNED:
ret.append(Integer.toString(tok.id));
break;
case UNSIGNED:
ret.append(Long.toString(tok.id & 0xFFFFFFFFL));
break;
default: ret.append("Error");
}
// Advance to the next line
if (tok != last)
ret.append("\n");
}
return ret.toString();
}
// Produce a string representation of the internal address ranges
public String debugRanges() {
var ret = new StringBuilder();
// The address ranges were not successfully parsed
if (addressError.code != NONE)
return "Error";
// Process ranges
for (int x = 0; x < ranges.length; x++) {
var range = ranges[x];
if (x > 0)
ret.append("\n");
ret.append(String.format("%08X", range[0]));
if (range[0] != range[1])
ret.append(String.format("-%08X", range[1]));
}
return ret.toString();
}
}

View File

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

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:
reg2 = bits >> 21 & 31;
imm = extend < 0 ? bits << 11 >> 27 : bits >> 16 & 31;
if (id == BITSTRING)
if (id == BITSTRING) {
id = imm >= 16 ? Vue.ILLEGAL : LOOKUP_BITSTRING[imm];
subopcode = imm;
}
if (id == Vue.SETF)
cond = imm & 15;
break;
case 3:
opcode = 0x20;

View File

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

View File

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

View File

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

View File

@ -138,7 +138,7 @@ public abstract class Vue {
// Produce an emulation core context
public static Vue create(boolean useNative) {
return !useNative ? new JavaVue() :
isNativeLoaded() ? new NativeVue() : null;
nativeID != null ? new NativeVue() : null;
}
// Retrieve the ID of the loaded native library, if any
@ -146,11 +146,6 @@ public abstract class Vue {
return nativeID;
}
// Determine whether the native module is loaded
public static boolean isNativeLoaded() {
return nativeID != null;
}
// Specify the ID of the loaded native library
public static void setNativeID(String nativeID) {
Vue.nativeID = nativeID;
@ -180,40 +175,32 @@ public abstract class Vue {
return brk;
}
// Evaluate an expression
public Object evaluate(String expression) {
var brk = new Breakpoint(this);
brk.setCondition(expression);
return brk.evaluate(null, null, null);
}
// Produce an array of the current breakpoint collection
public Breakpoint[] listBreakpoints() {
return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
}
// Remove a breakpoint from the collection
public boolean remove(Breakpoint brk) {
if (!breakpoints.remove(brk))
return false;
brk.remove();
return true;
}
///////////////////////////////////////////////////////////////////////////
// Abstract Methods //
///////////////////////////////////////////////////////////////////////////
// Release any used resources
public abstract void dispose();
// Process the simulation
public abstract int emulate(int maxCycles);
// Evaluate an expression
public Object evaluate(String expression) {
var brk = new Breakpoint(this);
brk.setCondition(expression);
brk.setEnabled (true);
return brk.evaluateTyped(new int[brk.getDepth()], 0,
getInstruction(), getAccess());
}
// Retrieve a snapshot of the current state's memory access
public abstract Access getAccess();
// Retrieve the most recent applicaiton break code
public abstract int getBreakCode();
// Retrieve the most recent exception code
public abstract int getException();
public abstract int getExceptionCode();
// Retrieve a snapshot of the current state's instruction
public abstract Instruction getInstruction();
// Retrieve a register value
public abstract int getRegister(int index, boolean system);
@ -224,6 +211,11 @@ public abstract class Vue {
// Determine whether the context is native-backed
public abstract boolean isNative();
// Produce an array of the current breakpoint collection
public Breakpoint[] listBreakpoints() {
return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
}
// Read a value from the CPU bus
public abstract int read(int address, int type);
@ -231,6 +223,14 @@ public abstract class Vue {
public abstract boolean readBytes(int address, byte[] dest, int offset,
int length);
// Remove a breakpoint from the collection
public boolean remove(Breakpoint brk) {
if (!breakpoints.remove(brk))
return false;
brk.remove();
return true;
}
// Initialize all system components
public abstract void reset();
@ -253,6 +253,12 @@ public abstract class Vue {
// Package Methods //
///////////////////////////////////////////////////////////////////////////
// Retrieve the current state's breakpoint scenario
abstract int getBreakType();
// Retrieve the current instruction fetch index
abstract int getFetch();
// A breakpoint's address ranges have changed
void updateRanges(Breakpoint brk) { }