Adjusting top-level emulation logic, integrating breakpoints
This commit is contained in:
parent
e13c56e1e7
commit
167bc4f8bf
|
@ -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">• Value</td>
|
||||
<td>
|
||||
Numeric value that can be used in computation. This may take the form of
|
||||
a literal number provided directly in the expression, or one of the
|
||||
named symbols that represent things in the CPU state, current
|
||||
instruction and/or current memory access.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="narrow nowrap">• Operator</td>
|
||||
<td>
|
||||
Modifies one or two values through a given operation. Unary operators
|
||||
modify only the value on their right, while binary operators consider the
|
||||
values on their left and right and produce a new value as the result.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• Group</td>
|
||||
<td>
|
||||
Operand groups, mainly through the use of parentheses <code>()</code>,
|
||||
override the default order of operations. Anything enclosed in a group
|
||||
is processed before any operations adjacent to that group.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
Expressions are case-insensitive.
|
||||
</p>
|
||||
|
||||
|
||||
|
||||
<h2>Values</h2>
|
||||
<p>
|
||||
Values come in two forms:
|
||||
</p>
|
||||
<table class="indent">
|
||||
<tr>
|
||||
<td class="narrow nowrap">• Literal</td>
|
||||
<td>Provided directly by the expression.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• Symbol</td>
|
||||
<td>Refers to something in the current emulation state.</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
Values may be one of four data types:
|
||||
</p>
|
||||
<table class="indent">
|
||||
<tr>
|
||||
<td class="narrow nowrap">• Signed Word</td>
|
||||
<td>
|
||||
32-bit, two's complement integer.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• Unsigned Word</td>
|
||||
<td>
|
||||
32-bit, unsigned integer.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• Float</td>
|
||||
<td>
|
||||
32-bit floating short in IEEE-754 format.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• Boolean</td>
|
||||
<td>
|
||||
1-bit value representing whether a condition is true or false.
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
Signed word and unsigned word are represented as sequences of character
|
||||
digits. A numeric token that begins with the characters <code>0x</code>
|
||||
will be interpreted as hexadecimal, allowing letters <code>A</code> through
|
||||
<code>F</code> to be used as digits as well. If the given value can be
|
||||
represented in the signed word data type, the token is a signed word.
|
||||
Otherwise, if it can be represented in the unsigned word data type, the token
|
||||
is an unsigned word. If the value cannot be represented in either data type,
|
||||
a parsing error occurs.
|
||||
</p>
|
||||
<p>
|
||||
Floats, like words, are represented as sequences of character digits. They
|
||||
are 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">↑</td><td>Float</td></tr>
|
||||
<tr><td class="center"></td><td>Unsigned word</td></tr>
|
||||
<tr><td class="center"></td><td>Signed word</td></tr>
|
||||
<tr><td class="center">↓</td><td>Boolean</td></tr>
|
||||
</table>
|
||||
<p>
|
||||
For example, if an operation contains both a signed word and a float, the
|
||||
signed word value is 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"><<</td>
|
||||
<td class="open">Shift Left</td>
|
||||
<td class="open">Cannot be used with a float value.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">>></td>
|
||||
<td class="open">Shift Right Arithmetic</td>
|
||||
<td class="open">Cannot be used with a float value.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">>>></td>
|
||||
<td>Shift Right Logical</td>
|
||||
<td>Cannot be used with a float value.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">></td>
|
||||
<td class="open">Greater</td>
|
||||
<td class="open">Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">>=</td>
|
||||
<td class="open">Greater or Equal</td>
|
||||
<td class="open">Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open"><</td>
|
||||
<td class="open">Less</td>
|
||||
<td class="open">Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono"><=</td>
|
||||
<td>Less or Equal</td>
|
||||
<td>Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono open">==</td>
|
||||
<td class="open">Equal</td>
|
||||
<td class="open">Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">!=</td>
|
||||
<td>Not Equal</td>
|
||||
<td>Always produces a boolean.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">&</td>
|
||||
<td>And Bitwise</td>
|
||||
<td>Cannot be used with 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">&&</td>
|
||||
<td>And Logical</td>
|
||||
<td>If left is true, returns right; else returns left.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">^^</td>
|
||||
<td>Exclusive Or Logical</td>
|
||||
<td>If only one operand is true, returns the truthy value.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="mono">||</td>
|
||||
<td>Or Logical</td>
|
||||
<td>If left is true, returns left; else returns right.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<h2>Memory Read</h2>
|
||||
<p>
|
||||
A value can be read from the memory bus of the emulation state. This is done
|
||||
by enclosing the part of the expression that represents the address in square
|
||||
brackets <code>[]</code>. This functions in an identical manner to
|
||||
parentheses <code>()</code>, but will additionally perform the read
|
||||
operation.
|
||||
</p>
|
||||
<p>
|
||||
Under most circumstances, a signed word read is performed. Certain operators
|
||||
can be placed in front of the group to alter how the read is performed:
|
||||
</p>
|
||||
<table class="indent">
|
||||
<tr>
|
||||
<td class="narrow nowrap">• <code>float</code></td>
|
||||
<td>Behaves like <code>xfloat</code> instead.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• <code>s8</code>, <code>u8</code></td>
|
||||
<td>Performs a byte read of the specified signedness.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nowrap">• <code>s16</code>, <code>u16</code></td>
|
||||
<td>Performs a halfword read of the specified signedness.</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</body>
|
||||
</html>
|
208
src/core/cpu.c
208
src/core/cpu.c
|
@ -116,8 +116,8 @@ static vbool cpuFloatReserved(Vue *vue, vbool left) {
|
|||
)) continue;
|
||||
|
||||
/* The value is a reserved operand */
|
||||
vue->cpu.exception.code = 0xFF60;
|
||||
vue->cpu.psw_fro = 1;
|
||||
vue->cpu.exception = 0xFF60;
|
||||
vue->cpu.psw_fro = 1;
|
||||
return VUE_TRUE;
|
||||
}
|
||||
|
||||
|
@ -150,7 +150,7 @@ static void cpuFloatConvert(Vue *vue, vbool round) {
|
|||
|
||||
/* Invalid operation (word overflow) */
|
||||
else if (bits >= 8) {
|
||||
vue->cpu.exception.code = 0xFF70;
|
||||
vue->cpu.exception = 0xFF70;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -193,8 +193,8 @@ static void cpuFloatResult(Vue *vue, vbool compare, double full) {
|
|||
|
||||
/* Overflow */
|
||||
if (full > result || full < -result) {
|
||||
vue->cpu.exception.code = 0xFF64;
|
||||
vue->cpu.psw_fov = 1;
|
||||
vue->cpu.exception = 0xFF64;
|
||||
vue->cpu.psw_fov = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -578,7 +578,7 @@ static void cpuDIV(Vue *vue) {
|
|||
|
||||
/* Zero division */
|
||||
if (right == 0) {
|
||||
vue->cpu.exception.code = 0xFF80;
|
||||
vue->cpu.exception = 0xFF80;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -612,7 +612,7 @@ static void cpuDIVF_S(Vue *vue) {
|
|||
|
||||
/* An exception has occurred */
|
||||
if (!right) {
|
||||
vue->cpu.exception.code = left ? 0xFF68 : 0xFF70;
|
||||
vue->cpu.exception = left ? 0xFF68 : 0xFF70;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -628,7 +628,7 @@ static void cpuDIVU(Vue *vue) {
|
|||
|
||||
/* Zero division */
|
||||
if (right == 0) {
|
||||
vue->cpu.exception.code = 0xFF80;
|
||||
vue->cpu.exception = 0xFF80;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -848,7 +848,7 @@ static void cpuREV(Vue *vue) {
|
|||
|
||||
/* Trap */
|
||||
#define cpuTRAP(vue) \
|
||||
vue->cpu.exception.code = 0xFFA0 | (vue->cpu.inst.imm & 15), \
|
||||
vue->cpu.exception = 0xFFA0 | (vue->cpu.inst.imm & 15), \
|
||||
vue->cpu.pc += 2
|
||||
|
||||
/* Truncate Short Floating to Word */
|
||||
|
@ -908,8 +908,12 @@ static void cpuDecode(VueInstruction *inst) {
|
|||
x = inst->bits >> 16 & 31;
|
||||
inst->reg2 = inst->bits >> 21 & 31;
|
||||
inst->imm = extend < 0 ? SIGN_EXTEND(5, x) : x;
|
||||
if (inst->id == BITSTRING)
|
||||
if (inst->id == BITSTRING) {
|
||||
inst->id = x >= 16 ? VUE_ILLEGAL : LOOKUP_BITSTRING[x];
|
||||
inst->subopcode = x;
|
||||
}
|
||||
if (inst->id == VUE_SETF)
|
||||
inst->cond = x & 15;
|
||||
break;
|
||||
case 3:
|
||||
x = inst->bits >> 16 & 0x1FF;
|
||||
|
@ -944,21 +948,84 @@ static void cpuDecode(VueInstruction *inst) {
|
|||
|
||||
}
|
||||
|
||||
/* Operations for exception stage */
|
||||
static vbool cpuException(Vue *vue) {
|
||||
vbool isIRQ; /* The exception is an interrupt */
|
||||
int32_t psw; /* Current value of PSW */
|
||||
|
||||
/* Application callback */
|
||||
if (vue->onException != NULL) {
|
||||
vue->breakCode = vue->onException(vue, vue->cpu.exception);
|
||||
if (vue->breakCode != 0)
|
||||
return VUE_TRUE;
|
||||
}
|
||||
|
||||
/* Configure working variables */
|
||||
vue->cpu.exception &= 0xFFFF;
|
||||
isIRQ = (vue->cpu.exception & 0xFF00) == 0xFE00;
|
||||
psw = cpuGetSystemRegister(vue, VUE_PSW);
|
||||
|
||||
/* Fatal exception */
|
||||
if (vue->cpu.psw_np != 0) {
|
||||
vueWrite(vue, 0x00000000, VUE_S32, 0xFFFF0000 | vue->cpu.exception);
|
||||
vueWrite(vue, 0x00000004, VUE_S32, psw);
|
||||
vueWrite(vue, 0x00000008, VUE_S32, vue->cpu.pc);
|
||||
vue->cpu.stage = CPU_FATAL;
|
||||
return VUE_TRUE;
|
||||
}
|
||||
|
||||
/* Duplexed exception */
|
||||
if (vue->cpu.psw_ep != 0) {
|
||||
vue->cpu.ecr_fecc = vue->cpu.exception;
|
||||
vue->cpu.fepc = vue->cpu.pc;
|
||||
vue->cpu.fepsw = psw;
|
||||
vue->cpu.psw_np = 1;
|
||||
vue->cpu.pc = 0xFFFFFFD0;
|
||||
}
|
||||
|
||||
/* Regular exception */
|
||||
else {
|
||||
vue->cpu.ecr_eicc = vue->cpu.exception;
|
||||
vue->cpu.eipc = vue->cpu.pc;
|
||||
vue->cpu.eipsw = psw;
|
||||
vue->cpu.psw_ep = 1;
|
||||
vue->cpu.pc = 0xFFFF0000 | (vue->cpu.exception & 0xFFF0);
|
||||
if (vue->cpu.pc == (int32_t) 0xFFFFFF70) /* FIV */
|
||||
vue->cpu.pc = 0xFFFFFF60;
|
||||
}
|
||||
|
||||
/* Interrupt */
|
||||
if (isIRQ) {
|
||||
psw = vue->cpu.exception >> 4 & 15;
|
||||
vue->cpu.psw_i = psw < 15 ? psw : 15;
|
||||
if (vue->cpu.stage == CPU_HALT)
|
||||
vue->cpu.pc += 2;
|
||||
}
|
||||
|
||||
/* Common processing */
|
||||
vue->cpu.cycles = 0; /* TODO: Determine the actual number */
|
||||
vue->cpu.exception = 0;
|
||||
vue->cpu.psw_ae = 0;
|
||||
vue->cpu.psw_id = 1;
|
||||
vue->cpu.stage = CPU_FETCH;
|
||||
return VUE_FALSE;
|
||||
}
|
||||
|
||||
/* Check for an exception or interrupt */
|
||||
static vbool cpuTestException(Vue *vue) {
|
||||
int32_t level; /* Interrupt level */
|
||||
|
||||
/* Check for an interrupt */
|
||||
if (vue->cpu.irq != 0 && (vue->cpu.exception.code |
|
||||
if (vue->cpu.irq != 0 && (vue->cpu.exception |
|
||||
vue->cpu.psw_id | vue->cpu.psw_ep | vue->cpu.psw_np) == 0) {
|
||||
for (level = 4; level >= 0; level--)
|
||||
if ((vue->cpu.irq >> level & 1) != 0)
|
||||
break;
|
||||
vue->cpu.exception.code = 0xFE00 | level << 4;
|
||||
vue->cpu.exception = 0xFE00 | level << 4;
|
||||
}
|
||||
|
||||
/* There is no exception */
|
||||
if (vue->cpu.exception.code == 0) /* No further processing */
|
||||
if (vue->cpu.exception == 0) /* No further processing */
|
||||
return vue->cpu.stage == CPU_HALT && vue->cpu.cycles == 0;
|
||||
|
||||
/* An exception has occurred */
|
||||
|
@ -975,8 +1042,6 @@ static vbool cpuExecute(Vue *vue) {
|
|||
vue->breakCode = vue->onExecute(vue, &vue->cpu.inst);
|
||||
if (vue->breakCode != 0)
|
||||
return VUE_TRUE;
|
||||
vue->cpu.inst.reg1 &= 31;
|
||||
vue->cpu.inst.reg2 &= 31;
|
||||
}
|
||||
|
||||
/* Processing by instruction ID */
|
||||
|
@ -1058,11 +1123,11 @@ static vbool cpuExecute(Vue *vue) {
|
|||
case VUE_XORI : cpuXORI (vue); break;
|
||||
/*case VUE_XORNBSU: cpuXORNBSU(vue); break;*/
|
||||
default: /* Invalid instruction */
|
||||
vue->cpu.exception.code = 0xFF90;
|
||||
vue->cpu.exception = 0xFF90;
|
||||
}
|
||||
|
||||
/* Common processing */
|
||||
if (vue->cpu.exception.code == 0) {
|
||||
if (vue->cpu.exception == 0) {
|
||||
vue->cpu.cycles += CYCLES[vue->cpu.inst.id];
|
||||
vue->cpu.pc += vue->cpu.inst.size;
|
||||
}
|
||||
|
@ -1082,10 +1147,15 @@ static int32_t cpuSize(int32_t opcode) {
|
|||
/* Operations for fetch stage */
|
||||
static vbool cpuFetch(Vue *vue) {
|
||||
|
||||
/* Entering the fetch stage */
|
||||
if (vue->cpu.fetch == -1)
|
||||
vue->cpu.fetch = 0;
|
||||
|
||||
/* Read the bits from the bus */
|
||||
if (cpuRead(vue, vue->cpu.pc + (vue->cpu.fetch << 1),
|
||||
VUE_U16, vue->cpu.fetch))
|
||||
return VUE_TRUE;
|
||||
/* TODO: Determine how many cycles this takes */
|
||||
|
||||
/* First unit */
|
||||
if (vue->cpu.fetch == 0) {
|
||||
|
@ -1099,7 +1169,7 @@ static vbool cpuFetch(Vue *vue) {
|
|||
/* Second unit */
|
||||
else {
|
||||
vue->cpu.inst.bits |= vue->cpu.access.value & 0xFFFF;
|
||||
vue->cpu.fetch = 0;
|
||||
vue->cpu.fetch = -1;
|
||||
}
|
||||
|
||||
/* Decode the instruction and advance to execute stage */
|
||||
|
@ -1108,94 +1178,30 @@ static vbool cpuFetch(Vue *vue) {
|
|||
return VUE_FALSE;
|
||||
}
|
||||
|
||||
/* Operations for exception stage */
|
||||
static vbool cpuException(Vue *vue) {
|
||||
vbool isIRQ; /* The exception is an interrupt */
|
||||
int32_t psw; /* Current value of PSW */
|
||||
|
||||
/* Application callback */
|
||||
if (vue->onException != NULL) {
|
||||
vue->breakCode = vue->onException(vue, &vue->cpu.exception);
|
||||
if (vue->breakCode != 0)
|
||||
return VUE_TRUE;
|
||||
}
|
||||
|
||||
/* Configure working variables */
|
||||
vue->cpu.exception.code &= 0xFFFF;
|
||||
isIRQ = (vue->cpu.exception.code & 0xFF00) == 0xFE00;
|
||||
psw = cpuGetSystemRegister(vue, VUE_PSW);
|
||||
|
||||
/* Fatal exception */
|
||||
if (vue->cpu.psw_np != 0) {
|
||||
vueWrite(vue, 0x00000000, VUE_S32, 0xFFFF0000|vue->cpu.exception.code);
|
||||
vueWrite(vue, 0x00000004, VUE_S32, psw);
|
||||
vueWrite(vue, 0x00000008, VUE_S32, vue->cpu.pc);
|
||||
vue->cpu.stage = CPU_FATAL;
|
||||
return VUE_TRUE;
|
||||
}
|
||||
|
||||
/* Duplexed exception */
|
||||
if (vue->cpu.psw_ep != 0) {
|
||||
vue->cpu.ecr_fecc = vue->cpu.exception.code;
|
||||
vue->cpu.fepc = vue->cpu.pc;
|
||||
vue->cpu.fepsw = psw;
|
||||
vue->cpu.psw_np = 1;
|
||||
vue->cpu.pc = 0xFFFFFFD0;
|
||||
}
|
||||
|
||||
/* Regular exception */
|
||||
else {
|
||||
vue->cpu.ecr_eicc = vue->cpu.exception.code;
|
||||
vue->cpu.eipc = vue->cpu.pc;
|
||||
vue->cpu.eipsw = psw;
|
||||
vue->cpu.psw_ep = 1;
|
||||
vue->cpu.pc = 0xFFFF0000 | (vue->cpu.exception.code & 0xFFF0);
|
||||
if (vue->cpu.pc == (int32_t) 0xFFFFFF70) /* FIV */
|
||||
vue->cpu.pc = 0xFFFFFF60;
|
||||
}
|
||||
|
||||
/* Interrupt */
|
||||
if (isIRQ) {
|
||||
psw = vue->cpu.exception.code >> 4 & 15;
|
||||
vue->cpu.psw_i = psw < 15 ? psw : 15;
|
||||
if (vue->cpu.stage == CPU_HALT)
|
||||
vue->cpu.pc += 2;
|
||||
}
|
||||
|
||||
/* Common processing */
|
||||
vue->cpu.cycles = 0; /* TODO: Determine the actual number */
|
||||
vue->cpu.exception.code = 0;
|
||||
vue->cpu.psw_ae = 0;
|
||||
vue->cpu.psw_id = 1;
|
||||
vue->cpu.stage = CPU_FETCH;
|
||||
return VUE_FALSE;
|
||||
}
|
||||
|
||||
/* Process the simulation */
|
||||
static void cpuEmulate(Vue *vue, int32_t cycles) {
|
||||
static void cpuEmulate(Vue *vue, uint32_t cycles) {
|
||||
|
||||
/* The CPU is halting */
|
||||
if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT)
|
||||
/* The CPU is in fatal halt status */
|
||||
if (vue->cpu.stage == CPU_FATAL)
|
||||
return;
|
||||
|
||||
vue->cpu.cycles = 0; /* DEBUG: Stop processing after execute */
|
||||
|
||||
/* Process for the given number of cycles */
|
||||
for (;;) {
|
||||
|
||||
/* The next event occurs after the given number of cycles */
|
||||
/* The next stage occurs after the given number of cycles */
|
||||
if (vue->cpu.cycles > cycles) {
|
||||
vue->cpu.cycles -= cycles;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Processing by stage */
|
||||
cycles -= vue->cpu.cycles;
|
||||
vue->cpu.cycles = 0;
|
||||
switch (vue->cpu.stage) {
|
||||
case CPU_EXCEPTION: if (cpuException (vue)) return; break;
|
||||
case CPU_EXECUTE : if (cpuExecute (vue)) return; break;
|
||||
case CPU_FETCH : if (cpuFetch (vue)) return; break;
|
||||
case CPU_HALT : if (cpuTestException(vue)) return; break;
|
||||
case CPU_EXCEPTION: if (cpuException(vue)) return; break;
|
||||
case CPU_EXECUTE : if (cpuExecute (vue)) return; break;
|
||||
case CPU_FETCH : if (cpuFetch (vue)) return; break;
|
||||
case CPU_HALT : cpuTestException(vue); return;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1207,11 +1213,11 @@ static void cpuReset(Vue *vue) {
|
|||
int32_t x;
|
||||
|
||||
/* Configure instance fields */
|
||||
vue->cpu.cycles = 0;
|
||||
vue->cpu.exception.code = 0;
|
||||
vue->cpu.fetch = 0;
|
||||
vue->cpu.irq = 0;
|
||||
vue->cpu.stage = CPU_FETCH;
|
||||
vue->cpu.cycles = 0;
|
||||
vue->cpu.exception = 0;
|
||||
vue->cpu.fetch = -1;
|
||||
vue->cpu.irq = 0;
|
||||
vue->cpu.stage = CPU_FETCH;
|
||||
|
||||
/* Clear all registers (hardware only sets ECR, PC and PSW) */
|
||||
for (x = 0; x < 32; x++) {
|
||||
|
@ -1229,12 +1235,10 @@ static void cpuReset(Vue *vue) {
|
|||
vue->cpu.psw_np = 1;
|
||||
}
|
||||
|
||||
/* Determine the number of CPU cycles until something can happen */
|
||||
static int32_t cpuUntil(Vue *vue, int32_t cycles) {
|
||||
if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT)
|
||||
return cycles;
|
||||
return cycles < 0 ? vue->cpu.cycles :
|
||||
cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles;
|
||||
/* Determine the number of CPU cycles until a breakpoint could trigger */
|
||||
static uint32_t cpuUntil(Vue *vue, uint32_t cycles) {
|
||||
return vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT ?
|
||||
cycles : cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -154,11 +154,6 @@ typedef struct {
|
|||
int8_t type; /* Data type */
|
||||
} VueAccess;
|
||||
|
||||
/* Exception state */
|
||||
typedef struct {
|
||||
uint16_t code; /* Exception code */
|
||||
} VueException;
|
||||
|
||||
/* Instruction state */
|
||||
typedef struct {
|
||||
int32_t bits; /* Binary encoding */
|
||||
|
@ -174,31 +169,32 @@ typedef struct {
|
|||
uint8_t subopcode; /* Instruction subopcode */
|
||||
} VueInstruction;
|
||||
|
||||
/* Callbacks */
|
||||
typedef int32_t (*VueOnException)(Vue *, VueException *);
|
||||
/* Breakpoint handler callbacks */
|
||||
typedef int32_t (*VueOnAccess )(Vue *, VueAccess *);
|
||||
typedef int32_t (*VueOnException)(Vue *, uint16_t );
|
||||
typedef int32_t (*VueOnExecute )(Vue *, VueInstruction *);
|
||||
typedef int32_t (*VueOnRead )(Vue *, VueAccess *);
|
||||
typedef int32_t (*VueOnWrite )(Vue *, VueAccess *);
|
||||
typedef int32_t (*VueOnFrame )(Vue * );
|
||||
|
||||
/* Emulation state */
|
||||
struct Vue {
|
||||
int32_t breakCode; /* Application break code */
|
||||
uint8_t wram[0x10000]; /* System memory */
|
||||
|
||||
/* Callback handlers */
|
||||
/* Breakpoint handlers */
|
||||
VueOnException onException;
|
||||
VueOnExecute onExecute;
|
||||
VueOnRead onRead;
|
||||
VueOnWrite onWrite;
|
||||
VueOnFrame onFrame;
|
||||
VueOnAccess onRead;
|
||||
VueOnAccess onWrite;
|
||||
|
||||
/* CPU state */
|
||||
struct {
|
||||
VueAccess access; /* Access state */
|
||||
VueException exception; /* Exception state */
|
||||
VueInstruction inst; /* Instruction state */
|
||||
int32_t cycles; /* Cycles until next stage */
|
||||
uint32_t cycles; /* Cycles until next stage */
|
||||
int32_t jumpFrom[3]; /* Source PCs of most recent jumps */
|
||||
int32_t jumpTo [3]; /* Destination PCs of most recent jumps */
|
||||
uint16_t exception; /* Exception code */
|
||||
uint16_t irq; /* Interrupt lines */
|
||||
int fetch; /* Fetch unit index */
|
||||
int stage; /* Current processing stage */
|
||||
|
@ -265,21 +261,23 @@ struct Vue {
|
|||
* Function Prototypes *
|
||||
*****************************************************************************/
|
||||
|
||||
VUEAPI int32_t vueEmulate (Vue *vue, int32_t maxCycles);
|
||||
VUEAPI int32_t vueGetBreakCode(Vue *vue);
|
||||
VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system);
|
||||
VUEAPI void vueInitialize (Vue *vue);
|
||||
VUEAPI int32_t vueRead (Vue *vue, uint32_t address, int32_t type);
|
||||
VUEAPI vbool vueReadBytes (Vue *vue, uint32_t address, uint8_t *dest, uint32_t length);
|
||||
VUEAPI void vueReset (Vue *vue);
|
||||
VUEAPI void vueSetException(Vue *vue, VueOnException callback);
|
||||
VUEAPI void vueSetExecute (Vue *vue, VueOnExecute callback);
|
||||
VUEAPI void vueSetRead (Vue *vue, VueOnRead callback);
|
||||
VUEAPI int32_t vueSetRegister (Vue *vue, int32_t index, vbool system, int32_t value);
|
||||
VUEAPI vbool vueSetROM (Vue *vue, uint8_t *rom, uint32_t size);
|
||||
VUEAPI void vueSetWrite (Vue *vue, VueOnWrite callback);
|
||||
VUEAPI void vueWrite (Vue *vue, uint32_t address, int32_t type, int32_t value);
|
||||
VUEAPI vbool vueWriteBytes (Vue *vue, uint32_t address, uint8_t *src, uint32_t length);
|
||||
VUEAPI uint32_t vueEmulate (Vue *vue, uint32_t maxCycles);
|
||||
VUEAPI int32_t vueGetBreakCode (Vue *vue);
|
||||
VUEAPI uint16_t vueGetExceptionCode (Vue *vue);
|
||||
VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system);
|
||||
VUEAPI void vueInitialize (Vue *vue);
|
||||
VUEAPI VueOnException vueOnException (Vue *vue, VueOnException callback);
|
||||
VUEAPI VueOnExecute vueOnExecute (Vue *vue, VueOnExecute callback);
|
||||
VUEAPI VueOnFrame vueOnFrame (Vue *vue, VueOnFrame callback);
|
||||
VUEAPI VueOnAccess vueOnRead (Vue *vue, VueOnAccess callback);
|
||||
VUEAPI VueOnAccess vueOnWrite (Vue *vue, VueOnAccess callback);
|
||||
VUEAPI int32_t vueRead (Vue *vue, uint32_t address, int32_t type);
|
||||
VUEAPI vbool vueReadBytes (Vue *vue, uint32_t address, uint8_t *dest, uint32_t length);
|
||||
VUEAPI void vueReset (Vue *vue);
|
||||
VUEAPI int32_t vueSetRegister (Vue *vue, int32_t index, vbool system, int32_t value);
|
||||
VUEAPI vbool vueSetROM (Vue *vue, uint8_t *rom, uint32_t size);
|
||||
VUEAPI void vueWrite (Vue *vue, uint32_t address, int32_t type, int32_t value);
|
||||
VUEAPI vbool vueWriteBytes (Vue *vue, uint32_t address, uint8_t *src, uint32_t length);
|
||||
|
||||
|
||||
|
||||
|
|
117
src/core/vue.c
117
src/core/vue.c
|
@ -155,55 +155,46 @@ static void writeBytes(uint8_t *data, uint32_t datlen, uint32_t address,
|
|||
*****************************************************************************/
|
||||
|
||||
/* Process the simulation */
|
||||
int32_t vueEmulate(Vue *vue, int32_t maxCycles) {
|
||||
int32_t cycles; /* Number of cycles to process */
|
||||
uint32_t vueEmulate(Vue *vue, uint32_t maxCycles) {
|
||||
uint32_t cycles; /* Number of cycles to process */
|
||||
|
||||
/* Process up to the given number of cycles */
|
||||
do {
|
||||
|
||||
/* Determine the number of cycles during which nothing will happen */
|
||||
cycles = -1;
|
||||
/* Determine the number of cycles where no breakpoint will occur */
|
||||
cycles = maxCycles; /* min(maxCycles, nextFrameCycles) */
|
||||
cycles = cpuUntil (vue, cycles);
|
||||
/*cycles = padUntil (vue, cycles);*/
|
||||
/*cycles = linkUntil (vue, cycles);*/
|
||||
/*cycles = timerUntil(vue, cycles);*/
|
||||
/*cycles = vipUntil (vue, cycles);*/
|
||||
/*cycles = vsuUntil (vue, cycles);*/
|
||||
cycles = cpuUntil (vue, cycles);
|
||||
|
||||
/* Range checking */
|
||||
if (cycles == -1) /* No activity on any component */
|
||||
break;
|
||||
if (maxCycles >= 0) /* Restrict to given number of cycles */
|
||||
cycles = cycles < maxCycles ? cycles : maxCycles;
|
||||
|
||||
/* Process all system components */
|
||||
vue->breakCode = 0;
|
||||
cpuEmulate (vue, cycles);
|
||||
/*padEmulate (vue, cycles);*/
|
||||
/*pakEmulate (vue, cycles);*/
|
||||
/*linkEmulate (vue, cycles);*/
|
||||
/*timerEmulate(vue, cycles);*/
|
||||
/*vipEmulate (vue, cycles);*/
|
||||
/*vsuEmulate (vue, cycles);*/
|
||||
cpuEmulate (vue, cycles);
|
||||
|
||||
/* An application break was requested */
|
||||
if (vue->breakCode != 0)
|
||||
break;
|
||||
|
||||
/* Update the number of cycles remaining */
|
||||
if (maxCycles >= 0)
|
||||
maxCycles -= cycles;
|
||||
} while (maxCycles != 0);
|
||||
maxCycles -= cycles;
|
||||
} while (vue->breakCode == 0 && maxCycles != 0);
|
||||
|
||||
/* A break condition has occurred */
|
||||
return maxCycles;
|
||||
}
|
||||
|
||||
/* Retrieve the application break code */
|
||||
/* Retrieve the most recent applicaiton break code */
|
||||
int32_t vueGetBreakCode(Vue *vue) {
|
||||
return vue == NULL ? 0 : vue->breakCode;
|
||||
}
|
||||
|
||||
/* Retrieve the most recent exception code */
|
||||
uint16_t vueGetExceptionCode(Vue *vue) {
|
||||
return vue == NULL ? 0 : vue->cpu.exception;
|
||||
}
|
||||
|
||||
/* Retrieve the value of a register */
|
||||
int32_t vueGetRegister(Vue *vue, int32_t index, vbool system) {
|
||||
|
||||
|
@ -234,17 +225,69 @@ void vueInitialize(Vue *vue) {
|
|||
return;
|
||||
vue->onException = NULL;
|
||||
vue->onExecute = NULL;
|
||||
vue->onFrame = NULL;
|
||||
vue->onRead = NULL;
|
||||
vue->onWrite = NULL;
|
||||
vue->pak.ram = NULL;
|
||||
vue->pak.rom = NULL;
|
||||
vueReset(vue);
|
||||
}
|
||||
|
||||
/* Specify an exception breakpoint callback */
|
||||
VueOnException vueOnException(Vue *vue, VueOnException callback) {
|
||||
VueOnException ret;
|
||||
if (vue == NULL)
|
||||
return NULL;
|
||||
ret = vue->onException;
|
||||
vue->onException = callback;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Specify an execute breakpoint callback */
|
||||
VueOnExecute vueOnExecute(Vue *vue, VueOnExecute callback) {
|
||||
VueOnExecute ret;
|
||||
if (vue == NULL)
|
||||
return NULL;
|
||||
ret = vue->onExecute;
|
||||
vue->onExecute = callback;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Specify a frame breakpoint callback */
|
||||
VueOnFrame vueOnFrame(Vue *vue, VueOnFrame callback) {
|
||||
VueOnFrame ret;
|
||||
if (vue == NULL)
|
||||
return NULL;
|
||||
ret = vue->onFrame;
|
||||
vue->onFrame = callback;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Specify a read breakpoint callback */
|
||||
VueOnAccess vueSetRead(Vue *vue, VueOnAccess callback) {
|
||||
VueOnAccess ret;
|
||||
if (vue == NULL)
|
||||
return NULL;
|
||||
ret = vue->onRead;
|
||||
vue->onRead = callback;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Specify a write breakpoint callback */
|
||||
VueOnAccess vueOnWrite(Vue *vue, VueOnAccess callback) {
|
||||
VueOnAccess ret;
|
||||
if (vue == NULL)
|
||||
return NULL;
|
||||
ret = vue->onWrite;
|
||||
vue->onWrite = callback;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read a value from the CPU bus */
|
||||
int32_t vueRead(Vue *vue, uint32_t address, int32_t type) {
|
||||
|
||||
/* Error checking */
|
||||
if (vue == NULL)
|
||||
if (vue == NULL || type < 0 || type > 4)
|
||||
return 0;
|
||||
|
||||
/* Perform the operation */
|
||||
|
@ -299,28 +342,10 @@ void vueReset(Vue *vue) {
|
|||
/* Reset state */
|
||||
cpuReset(vue);
|
||||
pakReset(vue);
|
||||
for (x = 0; x < 0x1000; x++)
|
||||
for (x = 0; x < 0x10000; x++)
|
||||
vue->wram[x] = 0;
|
||||
}
|
||||
|
||||
/* Specify an exception breakpoint callback */
|
||||
void vueSetException(Vue *vue, VueOnException callback) {
|
||||
if (vue != NULL)
|
||||
vue->onException = callback;
|
||||
}
|
||||
|
||||
/* Specify an execute breakpoint callback */
|
||||
void vueSetExecute(Vue *vue, VueOnExecute callback) {
|
||||
if (vue != NULL)
|
||||
vue->onExecute = callback;
|
||||
}
|
||||
|
||||
/* Specify a read breakpoint callback */
|
||||
void vueSetRead(Vue *vue, VueOnRead callback) {
|
||||
if (vue != NULL)
|
||||
vue->onRead = callback;
|
||||
}
|
||||
|
||||
/* Specify a value for a register */
|
||||
int32_t vueSetRegister(Vue *vue, int32_t index, vbool system, int32_t value) {
|
||||
return vue == NULL ? 0 :
|
||||
|
@ -348,12 +373,6 @@ vbool vueSetROM(Vue *vue, uint8_t *rom, uint32_t size) {
|
|||
return VUE_TRUE;
|
||||
}
|
||||
|
||||
/* Specify a write breakpoint callback */
|
||||
void vueSetWrite(Vue *vue, VueOnRead callback) {
|
||||
if (vue != NULL)
|
||||
vue->onWrite = callback;
|
||||
}
|
||||
|
||||
/* Write a value to the CPU bus */
|
||||
void vueWrite(Vue *vue, uint32_t address, int32_t type, int32_t value) {
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -15,8 +15,9 @@ import vue.*;
|
|||
class MainWindow extends JFrame {
|
||||
|
||||
// Instance fields
|
||||
App app; // Containing application
|
||||
Vue vue; // Emulation core context
|
||||
App app; // Containing application
|
||||
Breakpoint brkStep; // Single step internal breakpoint
|
||||
Vue vue; // Emulation core context
|
||||
|
||||
// Private fields
|
||||
private boolean debugMode; // Window is in debug mode
|
||||
|
@ -97,6 +98,10 @@ class MainWindow extends JFrame {
|
|||
desktop.add(cpu = new CPUWindow (this));
|
||||
desktop.add(memory = new MemoryWindow (this));
|
||||
|
||||
// Configure internal breakpoints
|
||||
brkStep = vue.breakpoint();
|
||||
brkStep.setExecute(true);
|
||||
|
||||
// Display window
|
||||
refreshDebug();
|
||||
pack();
|
||||
|
|
|
@ -63,20 +63,23 @@ public class Breakpoint {
|
|||
|
||||
// Functional symbol IDs
|
||||
private static final int ADDRESS = 0;
|
||||
private static final int CODE = 1;
|
||||
private static final int COND = 2;
|
||||
private static final int DISP = 3;
|
||||
private static final int FORMAT = 4;
|
||||
private static final int ID = 5;
|
||||
private static final int IMM = 6;
|
||||
private static final int OPCODE = 7;
|
||||
private static final int REG1 = 8;
|
||||
private static final int REG2 = 9;
|
||||
private static final int REGID = 10;
|
||||
private static final int SIZE = 11;
|
||||
private static final int SUBOPCODE = 12;
|
||||
private static final int VALUE = 13;
|
||||
private static final int VECTOR = 14;
|
||||
private static final int BREAK = 1;
|
||||
private static final int CODE = 2;
|
||||
private static final int COND = 3;
|
||||
private static final int DISP = 4;
|
||||
private static final int FETCH = 5;
|
||||
private static final int FORMAT = 6;
|
||||
private static final int ID = 7;
|
||||
private static final int IMM = 8;
|
||||
private static final int OPCODE = 9;
|
||||
private static final int REG1 = 10;
|
||||
private static final int REG2 = 11;
|
||||
private static final int REGID = 12;
|
||||
private static final int SIZE = 13;
|
||||
private static final int SUBOPCODE = 14;
|
||||
private static final int TYPE = 15;
|
||||
private static final int VALUE = 16;
|
||||
private static final int VECTOR = 17;
|
||||
|
||||
// Evaluation operator IDs
|
||||
private static final int GROUP = 0; // ()
|
||||
|
@ -190,6 +193,12 @@ public class Breakpoint {
|
|||
SYMDEFS.put("false", new Def(BOOL, 0));
|
||||
SYMDEFS.put("true" , new Def(BOOL, 1));
|
||||
|
||||
// Break type symbl definitions
|
||||
SYMDEFS.put("exception", new Def(SIGNED, EXCEPTION));
|
||||
SYMDEFS.put("execute" , new Def(SIGNED, EXECUTE ));
|
||||
SYMDEFS.put("read" , new Def(SIGNED, READ ));
|
||||
SYMDEFS.put("write" , new Def(SIGNED, WRITE ));
|
||||
|
||||
// Condition code symbol definitions
|
||||
SYMDEFS.put("c" , new Def(SIGNED, 1));
|
||||
SYMDEFS.put("e" , new Def(SIGNED, 2));
|
||||
|
@ -293,9 +302,11 @@ public class Breakpoint {
|
|||
|
||||
// Functional symbol definitions
|
||||
SYMDEFS.put("address" , new Def(SYMBOL, ADDRESS ));
|
||||
SYMDEFS.put("break" , new Def(SYMBOL, BREAK ));
|
||||
SYMDEFS.put("code" , new Def(SYMBOL, CODE ));
|
||||
SYMDEFS.put("cond" , new Def(SYMBOL, COND ));
|
||||
SYMDEFS.put("disp" , new Def(SYMBOL, DISP ));
|
||||
SYMDEFS.put("fetch" , new Def(SYMBOL, FETCH ));
|
||||
SYMDEFS.put("format" , new Def(SYMBOL, FORMAT ));
|
||||
SYMDEFS.put("id" , new Def(SYMBOL, ID ));
|
||||
SYMDEFS.put("imm" , new Def(SYMBOL, IMM ));
|
||||
|
@ -305,6 +316,7 @@ public class Breakpoint {
|
|||
SYMDEFS.put("regid" , new Def(SYMBOL, REGID ));
|
||||
SYMDEFS.put("size" , new Def(SYMBOL, SIZE ));
|
||||
SYMDEFS.put("subopcode", new Def(SYMBOL, SUBOPCODE));
|
||||
SYMDEFS.put("type" , new Def(SYMBOL, TYPE ));
|
||||
SYMDEFS.put("value" , new Def(SYMBOL, VALUE ));
|
||||
SYMDEFS.put("vector" , new Def(SYMBOL, VECTOR ));
|
||||
|
||||
|
@ -454,9 +466,34 @@ public class Breakpoint {
|
|||
|
||||
// Evaluate the condition against the emulation context
|
||||
public boolean evaluate() {
|
||||
return vue == null || conditionError.code != NONE ?
|
||||
false : tokens.length == 0 ? true :
|
||||
evaluate(null, null, null) != 0;
|
||||
|
||||
// Error checking
|
||||
if (vue == null)
|
||||
return false;
|
||||
|
||||
// Retrieve state objects
|
||||
Access acc = vue.getAccess ();
|
||||
int breakType = vue.getBreakType ();
|
||||
Instruction inst = vue.getInstruction();
|
||||
|
||||
// Error checking
|
||||
if (!appliesTo(breakType))
|
||||
return false;
|
||||
|
||||
// Check address ranges for execute
|
||||
if (breakType == EXECUTE) {
|
||||
int pc = vue.getRegister(Vue.PC, true);
|
||||
if (!inRange(pc, pc + inst.size - 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check address ranges for read and write
|
||||
else if (breakType == READ || breakType == WRITE &&
|
||||
!inRange(acc.address, acc.address+JavaVue.TYPE_SIZES[acc.type]-1))
|
||||
return false;
|
||||
|
||||
// Evaluate the condition
|
||||
return isTrue(new int[depth], breakType, inst, acc);
|
||||
}
|
||||
|
||||
// Retrieve the most recent address text
|
||||
|
@ -504,6 +541,15 @@ public class Breakpoint {
|
|||
return name;
|
||||
}
|
||||
|
||||
// Determine whether the breakpoint can be used
|
||||
boolean isActive() {
|
||||
return
|
||||
isEnabled &&
|
||||
addressError .code == NONE &&
|
||||
conditionError.code == NONE
|
||||
;
|
||||
}
|
||||
|
||||
// Determine whether the breakpoint is enabled
|
||||
public boolean isEnabled() {
|
||||
return isEnabled;
|
||||
|
@ -738,12 +784,16 @@ public class Breakpoint {
|
|||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Cast a value to the condition evaluation's data type
|
||||
Object cast(int value) {
|
||||
if (conditionError.code != NONE)
|
||||
return conditionError;
|
||||
// Determine whether the breakpoint applies to a break scenario
|
||||
boolean appliesTo(int breakType) {
|
||||
return isActive() && (breakType == 0 || (breakType & hooks) != 0);
|
||||
}
|
||||
|
||||
// Perform a typed evaluation of the condition expression
|
||||
Object evaluateTyped(int[]stack,int breakType,Instruction inst,Access acc){
|
||||
if (tokens.length == 0)
|
||||
return null;
|
||||
int value = evaluate(stack, breakType, inst, acc);
|
||||
switch (dataType) {
|
||||
case BOOL : return (Boolean) (value != 0);
|
||||
case FLOAT : return (Float ) Float.intBitsToFloat(value);
|
||||
|
@ -752,54 +802,28 @@ public class Breakpoint {
|
|||
return (Integer) value;
|
||||
}
|
||||
|
||||
// Evaluate the condition, returning only the computed value
|
||||
int evaluate(int[] stack, Instruction inst, Access acc) {
|
||||
// Determine whether the breakpoint applies to a given address range
|
||||
boolean inRange(int start, int end) {
|
||||
|
||||
// Error checking
|
||||
if (conditionError.code != NONE)
|
||||
return 0;
|
||||
if (tokens.length == 0)
|
||||
return 1;
|
||||
if (stack == null)
|
||||
stack = new int[depth];
|
||||
// Implicit inclusion
|
||||
if (ranges.length == 0)
|
||||
return true;
|
||||
|
||||
// Process all tokens
|
||||
int size = 0;
|
||||
for (var tok : tokens) switch (tok.type) {
|
||||
// Check all ranges
|
||||
for (var range : ranges)
|
||||
if (
|
||||
Integer.compareUnsigned(
|
||||
start - range[0], range[1] - range[0]) <= 0 ||
|
||||
Integer.compareUnsigned(
|
||||
range[0] - start , end - start ) <= 0
|
||||
) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Binary operator
|
||||
case BINARY:
|
||||
stack[size - 2] =
|
||||
evalBinary(tok.id, stack[size - 2], stack[size - 1]);
|
||||
break;
|
||||
|
||||
// Literal
|
||||
case BOOL:
|
||||
case FLOAT:
|
||||
case SIGNED:
|
||||
case UNSIGNED:
|
||||
stack[size++] = tok.id;
|
||||
break;
|
||||
|
||||
// CPU register
|
||||
case PROREG:
|
||||
case SYSREG:
|
||||
stack[size++] = vue.getRegister(tok.id, tok.type == SYSREG);
|
||||
break;
|
||||
|
||||
// Symbol
|
||||
case SYMBOL:
|
||||
stack[size++] = 0;
|
||||
break;
|
||||
|
||||
// Unary operator
|
||||
case UNARY:
|
||||
stack[size - 1] = evalUnary(tok.id, stack[size - 1]);
|
||||
break;
|
||||
}
|
||||
|
||||
// Return the remaining stack value
|
||||
return stack[0];
|
||||
// Determine whether the breakpoint's condition is truthy
|
||||
boolean isTrue(int[] stack, int breakType, Instruction inst, Access acc) {
|
||||
return tokens.length == 0 ? true :
|
||||
evaluate(stack, breakType, inst, acc) != 0;
|
||||
}
|
||||
|
||||
// Produce a one-dimensional array from the address ranges
|
||||
|
@ -829,21 +853,6 @@ public class Breakpoint {
|
|||
return depth;
|
||||
}
|
||||
|
||||
// Retrieve the bit mask for enabled hooks
|
||||
int getHooks() {
|
||||
return hooks;
|
||||
}
|
||||
|
||||
// Retrieve the list of address ranges
|
||||
int[][] getRanges() {
|
||||
return ranges;
|
||||
}
|
||||
|
||||
// Retrieve the list of condition tokens
|
||||
Token[] getTokens() {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
// The breakpoint is being removed from its emulation context
|
||||
void remove() {
|
||||
vue = null;
|
||||
|
@ -1216,10 +1225,11 @@ public class Breakpoint {
|
|||
|
||||
// Binary operators
|
||||
if (tok.type == BINARY) {
|
||||
int id = tok.id;
|
||||
boolean literal = isLiteral(tok.left) && isLiteral(tok.right);
|
||||
tok.dataType = Math.max(tok.left.dataType, tok.right.dataType);
|
||||
tok.id = -id * 4 + tok.dataType;
|
||||
int dataType = Math.max(tok.left.dataType, tok.right.dataType);
|
||||
int id = tok.id;
|
||||
boolean literal = isLiteral(tok.left) && isLiteral(tok.right);
|
||||
tok.dataType = dataType;
|
||||
tok.id = -id * 4 + tok.dataType;
|
||||
|
||||
// Process by ID
|
||||
switch (id) {
|
||||
|
@ -1269,7 +1279,7 @@ public class Breakpoint {
|
|||
var top = op == 0 ? tok.left : tok.right;
|
||||
|
||||
// The operand is already the result type
|
||||
if (top.dataType == tok.dataType)
|
||||
if (top.dataType == dataType)
|
||||
continue;
|
||||
|
||||
// Select the ID of the conversion operation
|
||||
|
@ -1284,7 +1294,7 @@ public class Breakpoint {
|
|||
// Convert the literal operand directly
|
||||
if (isLiteral(top)) {
|
||||
top.id = evalUnary(cvt, top);
|
||||
top.type = top.dataType = tok.dataType;
|
||||
top.type = top.dataType = dataType;
|
||||
}
|
||||
|
||||
// Insert a conversion token
|
||||
|
@ -1296,7 +1306,7 @@ public class Breakpoint {
|
|||
case SIGNED : imp.text = "s32" ; break;
|
||||
case UNSIGNED: imp.text = "u32" ; break;
|
||||
}
|
||||
imp.dataType = tok.dataType;
|
||||
imp.dataType = dataType;
|
||||
imp.id = -cvt * 4 + top.dataType;
|
||||
imp.parent = tok;
|
||||
imp.right = top;
|
||||
|
@ -1444,202 +1454,71 @@ public class Breakpoint {
|
|||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Adjust a float value as needed
|
||||
private static float adjust(float value) {
|
||||
int bits = Float.floatToRawIntBits(value);
|
||||
int exp = bits & 0x7F800000;
|
||||
return
|
||||
(bits & 0x7FFFFFFF) == 0 || // Zero
|
||||
exp == 0x7F800000 || // Indefinite
|
||||
exp == 0 // Denormal
|
||||
? 0 : value;
|
||||
}
|
||||
|
||||
// Determine the required stack size to evaluate the expression
|
||||
private int depth() {
|
||||
|
||||
// Error checking
|
||||
if (conditionError.code != NONE)
|
||||
return 0;
|
||||
|
||||
// Count the maximum size of the stack
|
||||
int max = 0;
|
||||
int size = 0;
|
||||
for (var tok : tokens) switch (tok.type) {
|
||||
case BINARY: size--; break;
|
||||
case UNARY : break;
|
||||
default : max = Math.max(max, ++size);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
// Remove the fraction part of a float
|
||||
private static float trunc(float x) {
|
||||
return (float) (x < 0 ? Math.ceil(x) : Math.floor(x));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Debugging Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Produce a string representation of the condition token list
|
||||
public String debugTokens() {
|
||||
var ret = new StringBuilder();
|
||||
|
||||
// The condition was not successfully parsed
|
||||
if (conditionError.code != NONE)
|
||||
return "Error";
|
||||
|
||||
// Determine the maximum width of the text fields
|
||||
int max = 0;
|
||||
for (var tok : tokens)
|
||||
max = Math.max(max, tok.text.length());
|
||||
|
||||
// Output all tokens
|
||||
var last = tokens[tokens.length - 1];
|
||||
for (var tok : tokens) {
|
||||
|
||||
// Text
|
||||
ret.append(String.format("%-" + max + "s ", tok.text));
|
||||
|
||||
// Type
|
||||
String type = null;
|
||||
switch (tok.type) {
|
||||
case BINARY : type = "Binary" ; break;
|
||||
case BOOL : type = "Bool" ; break;
|
||||
case FLOAT : type = "Float" ; break;
|
||||
case PROREG : type = "ProReg" ; break;
|
||||
case SYSREG : type = "SysReg" ; break;
|
||||
case SIGNED : type = "Signed" ; break;
|
||||
case SYMBOL : type = "Symbol" ; break;
|
||||
case UNARY : type = "Unary" ; break;
|
||||
case UNSIGNED: type = "Unsigned"; break;
|
||||
default: type = Integer.toString(tok.type);
|
||||
}
|
||||
ret.append(String.format("%-8s ", type));
|
||||
|
||||
// Data type
|
||||
switch (tok.dataType) {
|
||||
case BOOL : type = "Bool" ; break;
|
||||
case FLOAT : type = "Float" ; break;
|
||||
case SIGNED : type = "Signed" ; break;
|
||||
case UNSIGNED: type = "Unsigned"; break;
|
||||
default: type = Integer.toString(tok.dataType);
|
||||
}
|
||||
ret.append(String.format("%-8s ", type));
|
||||
|
||||
// Operator
|
||||
if (tok.type == BINARY || tok.type == UNARY) {
|
||||
String id = Integer.toString(tok.id);
|
||||
switch (-tok.id / 4) {
|
||||
case 0 : id = "REINTERPRET"; break;
|
||||
case READ8 : id = "READ8" ; break;
|
||||
case READ16 : id = "READ16" ; break;
|
||||
case READ32 : id = "READ32" ; break;
|
||||
case ADD : id = "ADD" ; break;
|
||||
case AND_B : id = "AND_B" ; break;
|
||||
case AND_L : id = "AND_L" ; break;
|
||||
case BOOL_ : id = "BOOL_" ; break;
|
||||
case CEIL : id = "CEIL" ; break;
|
||||
case DIVIDE : id = "DIVIDE" ; break;
|
||||
case EQUAL : id = "EQUAL" ; break;
|
||||
case FLOAT_ : id = "FLOAT_" ; break;
|
||||
case FLOOR : id = "FLOOR" ; break;
|
||||
case GREATER : id = "GREATER" ; break;
|
||||
case GREQUAL : id = "GREQUAL" ; break;
|
||||
case LEFT_L : id = "LEFT_L" ; break;
|
||||
case LEQUAL : id = "LEQUAL" ; break;
|
||||
case LESS : id = "LESS" ; break;
|
||||
case MULTIPLY : id = "MULTIPLY" ; break;
|
||||
case NEQUAL : id = "NEQUAL" ; break;
|
||||
case NOT_B : id = "NOT_B" ; break;
|
||||
case NOT_L : id = "NOT_L" ; break;
|
||||
case NEGATE : id = "NEGATE" ; break;
|
||||
case OR_B : id = "OR_B" ; break;
|
||||
case OR_L : id = "OR_L" ; break;
|
||||
case REMAINDER: id = "REMAINDER" ; break;
|
||||
case RIGHT_A : id = "RIGHT_A" ; break;
|
||||
case RIGHT_L : id = "RIGHT_L" ; break;
|
||||
case ROUND : id = "ROUND" ; break;
|
||||
case S8 : id = "S8" ; break;
|
||||
case S16 : id = "S16" ; break;
|
||||
case S32 : id = "S32" ; break;
|
||||
case SUBTRACT : id = "SUBTRACT" ; break;
|
||||
case TRUNC : id = "TRUNC" ; break;
|
||||
case U8 : id = "U8" ; break;
|
||||
case U16 : id = "U16" ; break;
|
||||
case U32 : id = "U32" ; break;
|
||||
case XFLOAT : id = "XFLOAT" ; break;
|
||||
case XOR_B : id = "XOR_B" ; break;
|
||||
case XOR_L : id = "XOR_L" ; break;
|
||||
case XS32 : id = "XS32" ; break;
|
||||
case XU32 : id = "XU32" ; break;
|
||||
default: ret.append(Integer.toString(tok.id));
|
||||
}
|
||||
ret.append(String.format("%-11s", id));
|
||||
}
|
||||
|
||||
// Symbol or literal
|
||||
else switch (tok.type) {
|
||||
case PROREG:
|
||||
case SYSREG:
|
||||
case SYMBOL:
|
||||
ret.append(Integer.toString(tok.id));
|
||||
break;
|
||||
case FLOAT:
|
||||
ret.append(String.format("%.6f",
|
||||
Float.intBitsToFloat(tok.id)));
|
||||
break;
|
||||
case BOOL:
|
||||
case SIGNED:
|
||||
ret.append(Integer.toString(tok.id));
|
||||
break;
|
||||
case UNSIGNED:
|
||||
ret.append(Long.toString(tok.id & 0xFFFFFFFFL));
|
||||
break;
|
||||
default: ret.append("Error");
|
||||
}
|
||||
|
||||
// Advance to the next line
|
||||
if (tok != last)
|
||||
ret.append("\n");
|
||||
}
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
// Produce a string representation of the internal address ranges
|
||||
public String debugRanges() {
|
||||
var ret = new StringBuilder();
|
||||
|
||||
// The address ranges were not successfully parsed
|
||||
if (addressError.code != NONE)
|
||||
return "Error";
|
||||
|
||||
// Process ranges
|
||||
for (int x = 0; x < ranges.length; x++) {
|
||||
var range = ranges[x];
|
||||
if (x > 0)
|
||||
ret.append("\n");
|
||||
ret.append(String.format("%08X", range[0]));
|
||||
if (range[0] != range[1])
|
||||
ret.append(String.format("-%08X", range[1]));
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Evaluation Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Evaluate the condition, returning only the computed value
|
||||
private int evaluate(int[]stack,int breakType,Instruction inst,Access acc){
|
||||
|
||||
// Process all tokens
|
||||
int size = 0;
|
||||
for (var tok : tokens) switch (tok.type) {
|
||||
|
||||
// Binary operator
|
||||
case BINARY:
|
||||
stack[size - 2] =
|
||||
evalBinary(tok.id, stack[size - 2], stack[size - 1]);
|
||||
break;
|
||||
|
||||
// Literal
|
||||
case BOOL:
|
||||
case FLOAT:
|
||||
case SIGNED:
|
||||
case UNSIGNED:
|
||||
stack[size++] = tok.id;
|
||||
break;
|
||||
|
||||
// CPU register
|
||||
case PROREG:
|
||||
case SYSREG:
|
||||
stack[size++] = vue.getRegister(tok.id, tok.type == SYSREG);
|
||||
break;
|
||||
|
||||
// Unary operator
|
||||
case UNARY:
|
||||
stack[size - 1] = evalUnary(tok.id, stack[size - 1]);
|
||||
break;
|
||||
|
||||
// Symbol
|
||||
case SYMBOL:
|
||||
int sym = inst.imm; // IMM, REGID, VECTOR
|
||||
switch (tok.id) {
|
||||
case ADDRESS : sym = acc.address ; break;
|
||||
case BREAK : sym = breakType ; break;
|
||||
case CODE : sym = vue.getExceptionCode(); break;
|
||||
case COND : sym = inst.cond ; break;
|
||||
case DISP : sym = inst.disp ; break;
|
||||
case FETCH : sym = vue.getFetch (); break;
|
||||
case FORMAT : sym = inst.format ; break;
|
||||
case ID : sym = inst.id ; break;
|
||||
case OPCODE : sym = inst.opcode ; break;
|
||||
case REG1 : sym = inst.reg1 ; break;
|
||||
case REG2 : sym = inst.reg2 ; break;
|
||||
case SIZE : sym = inst.size ; break;
|
||||
case SUBOPCODE: sym = inst.subopcode ; break;
|
||||
case TYPE : sym = acc.type ; break;
|
||||
case VALUE : sym = acc.value ; break;
|
||||
}
|
||||
stack[size++] = sym;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// Return the remaining stack value
|
||||
return stack[0];
|
||||
}
|
||||
|
||||
// Evaluate a unary operator given a token
|
||||
private int evalUnary(int id, Token tok) {
|
||||
return evalUnary(-id * 4 + tok.dataType, tok.id);
|
||||
|
@ -1988,4 +1867,196 @@ public class Breakpoint {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Adjust a float value as needed
|
||||
private static float adjust(float value) {
|
||||
int bits = Float.floatToRawIntBits(value);
|
||||
int exp = bits & 0x7F800000;
|
||||
return
|
||||
(bits & 0x7FFFFFFF) == 0 || // Zero
|
||||
exp == 0x7F800000 || // Indefinite
|
||||
exp == 0 // Denormal
|
||||
? 0 : value;
|
||||
}
|
||||
|
||||
// Determine the required stack size to evaluate the expression
|
||||
private int depth() {
|
||||
|
||||
// Error checking
|
||||
if (conditionError.code != NONE)
|
||||
return 0;
|
||||
|
||||
// Count the maximum size of the stack
|
||||
int max = 0;
|
||||
int size = 0;
|
||||
for (var tok : tokens) switch (tok.type) {
|
||||
case BINARY: size--; break;
|
||||
case UNARY : break;
|
||||
default : max = Math.max(max, ++size);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
// Remove the fraction part of a float
|
||||
private static float trunc(float x) {
|
||||
return (float) (x < 0 ? Math.ceil(x) : Math.floor(x));
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Debugging Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Produce a string representation of the condition token list
|
||||
public String debugTokens() {
|
||||
var ret = new StringBuilder();
|
||||
|
||||
// The condition was not successfully parsed
|
||||
if (conditionError.code != NONE)
|
||||
return "Error";
|
||||
|
||||
// Determine the maximum width of the text fields
|
||||
int max = 0;
|
||||
for (var tok : tokens)
|
||||
max = Math.max(max, tok.text.length());
|
||||
|
||||
// Output all tokens
|
||||
var last = tokens[tokens.length - 1];
|
||||
for (var tok : tokens) {
|
||||
|
||||
// Text
|
||||
ret.append(String.format("%-" + max + "s ", tok.text));
|
||||
|
||||
// Type
|
||||
String type = null;
|
||||
switch (tok.type) {
|
||||
case BINARY : type = "Binary" ; break;
|
||||
case BOOL : type = "Bool" ; break;
|
||||
case FLOAT : type = "Float" ; break;
|
||||
case PROREG : type = "ProReg" ; break;
|
||||
case SYSREG : type = "SysReg" ; break;
|
||||
case SIGNED : type = "Signed" ; break;
|
||||
case SYMBOL : type = "Symbol" ; break;
|
||||
case UNARY : type = "Unary" ; break;
|
||||
case UNSIGNED: type = "Unsigned"; break;
|
||||
default: type = Integer.toString(tok.type);
|
||||
}
|
||||
ret.append(String.format("%-8s ", type));
|
||||
|
||||
// Data type
|
||||
switch (tok.dataType) {
|
||||
case BOOL : type = "Bool" ; break;
|
||||
case FLOAT : type = "Float" ; break;
|
||||
case SIGNED : type = "Signed" ; break;
|
||||
case UNSIGNED: type = "Unsigned"; break;
|
||||
default: type = Integer.toString(tok.dataType);
|
||||
}
|
||||
ret.append(String.format("%-8s ", type));
|
||||
|
||||
// Operator
|
||||
if (tok.type == BINARY || tok.type == UNARY) {
|
||||
String id = Integer.toString(tok.id);
|
||||
switch (-tok.id / 4) {
|
||||
case 0 : id = "REINTERPRET"; break;
|
||||
case READ8 : id = "READ8" ; break;
|
||||
case READ16 : id = "READ16" ; break;
|
||||
case READ32 : id = "READ32" ; break;
|
||||
case ADD : id = "ADD" ; break;
|
||||
case AND_B : id = "AND_B" ; break;
|
||||
case AND_L : id = "AND_L" ; break;
|
||||
case BOOL_ : id = "BOOL_" ; break;
|
||||
case CEIL : id = "CEIL" ; break;
|
||||
case DIVIDE : id = "DIVIDE" ; break;
|
||||
case EQUAL : id = "EQUAL" ; break;
|
||||
case FLOAT_ : id = "FLOAT_" ; break;
|
||||
case FLOOR : id = "FLOOR" ; break;
|
||||
case GREATER : id = "GREATER" ; break;
|
||||
case GREQUAL : id = "GREQUAL" ; break;
|
||||
case LEFT_L : id = "LEFT_L" ; break;
|
||||
case LEQUAL : id = "LEQUAL" ; break;
|
||||
case LESS : id = "LESS" ; break;
|
||||
case MULTIPLY : id = "MULTIPLY" ; break;
|
||||
case NEQUAL : id = "NEQUAL" ; break;
|
||||
case NOT_B : id = "NOT_B" ; break;
|
||||
case NOT_L : id = "NOT_L" ; break;
|
||||
case NEGATE : id = "NEGATE" ; break;
|
||||
case OR_B : id = "OR_B" ; break;
|
||||
case OR_L : id = "OR_L" ; break;
|
||||
case REMAINDER: id = "REMAINDER" ; break;
|
||||
case RIGHT_A : id = "RIGHT_A" ; break;
|
||||
case RIGHT_L : id = "RIGHT_L" ; break;
|
||||
case ROUND : id = "ROUND" ; break;
|
||||
case S8 : id = "S8" ; break;
|
||||
case S16 : id = "S16" ; break;
|
||||
case S32 : id = "S32" ; break;
|
||||
case SUBTRACT : id = "SUBTRACT" ; break;
|
||||
case TRUNC : id = "TRUNC" ; break;
|
||||
case U8 : id = "U8" ; break;
|
||||
case U16 : id = "U16" ; break;
|
||||
case U32 : id = "U32" ; break;
|
||||
case XFLOAT : id = "XFLOAT" ; break;
|
||||
case XOR_B : id = "XOR_B" ; break;
|
||||
case XOR_L : id = "XOR_L" ; break;
|
||||
case XS32 : id = "XS32" ; break;
|
||||
case XU32 : id = "XU32" ; break;
|
||||
default: ret.append(Integer.toString(tok.id));
|
||||
}
|
||||
ret.append(String.format("%-11s", id));
|
||||
}
|
||||
|
||||
// Symbol or literal
|
||||
else switch (tok.type) {
|
||||
case PROREG:
|
||||
case SYSREG:
|
||||
case SYMBOL:
|
||||
ret.append(Integer.toString(tok.id));
|
||||
break;
|
||||
case FLOAT:
|
||||
ret.append(String.format("%.6f",
|
||||
Float.intBitsToFloat(tok.id)));
|
||||
break;
|
||||
case BOOL:
|
||||
case SIGNED:
|
||||
ret.append(Integer.toString(tok.id));
|
||||
break;
|
||||
case UNSIGNED:
|
||||
ret.append(Long.toString(tok.id & 0xFFFFFFFFL));
|
||||
break;
|
||||
default: ret.append("Error");
|
||||
}
|
||||
|
||||
// Advance to the next line
|
||||
if (tok != last)
|
||||
ret.append("\n");
|
||||
}
|
||||
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
// Produce a string representation of the internal address ranges
|
||||
public String debugRanges() {
|
||||
var ret = new StringBuilder();
|
||||
|
||||
// The address ranges were not successfully parsed
|
||||
if (addressError.code != NONE)
|
||||
return "Error";
|
||||
|
||||
// Process ranges
|
||||
for (int x = 0; x < ranges.length; x++) {
|
||||
var range = ranges[x];
|
||||
if (x > 0)
|
||||
ret.append("\n");
|
||||
ret.append(String.format("%08X", range[0]));
|
||||
if (range[0] != range[1])
|
||||
ret.append(String.format("-%08X", range[1]));
|
||||
}
|
||||
return ret.toString();
|
||||
}
|
||||
|
||||
}
|
|
@ -12,7 +12,7 @@ class CPU {
|
|||
// Package fields
|
||||
Access access; // Access state
|
||||
int cycles; // Cycles until next stage
|
||||
Ecxeption exception; // Exception state
|
||||
int exception; // Exception code
|
||||
int fetch; // Fetch unit index
|
||||
Instruction inst; // Instruction state
|
||||
int irq; // Interrupt lines
|
||||
|
@ -97,7 +97,6 @@ class CPU {
|
|||
// Default constructor
|
||||
CPU(JavaVue vue) {
|
||||
access = new Access();
|
||||
exception = new Ecxeption();
|
||||
inst = new Instruction();
|
||||
jumpFrom = new int[3];
|
||||
jumpTo = new int[3];
|
||||
|
@ -114,28 +113,27 @@ class CPU {
|
|||
// Process the simulation
|
||||
void emulate(int cycles) {
|
||||
|
||||
// The CPU is halting
|
||||
if (stage == HALT || stage == FATAL)
|
||||
// The CPU is in fatal halt status
|
||||
if (stage == FATAL)
|
||||
return;
|
||||
|
||||
this.cycles = 0; // DEBUG: Stop processing after execute
|
||||
|
||||
// Process for the given number of cycles
|
||||
for (;;) {
|
||||
|
||||
// The next event occurs after the given number of cycles
|
||||
// The next stage occurs after the given number of cycles
|
||||
if (this.cycles > cycles) {
|
||||
this.cycles -= cycles;
|
||||
return;
|
||||
}
|
||||
|
||||
// Processing by stage
|
||||
cycles -= this.cycles;
|
||||
this.cycles = 0;
|
||||
switch (stage) {
|
||||
case EXCEPTION: if (exception ()) return; break;
|
||||
case EXECUTE : if (execute ()) return; break;
|
||||
case FETCH : if (fetch ()) return; break;
|
||||
case HALT : if (testException()) return; break;
|
||||
case EXCEPTION: if (exception()) return; break;
|
||||
case EXECUTE : if (execute ()) return; break;
|
||||
case FETCH : if (fetch ()) return; break;
|
||||
case HALT : testException(); return;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -177,11 +175,11 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
void reset() {
|
||||
|
||||
// Configure instance fields
|
||||
cycles = 0;
|
||||
exception.code = 0;
|
||||
fetch = 0;
|
||||
irq = 0;
|
||||
stage = FETCH;
|
||||
cycles = 0;
|
||||
exception = 0;
|
||||
fetch = -1;
|
||||
irq = 0;
|
||||
stage = FETCH;
|
||||
|
||||
// Clear all registers (hardware only sets ECR, PC and PSW)
|
||||
for (int x = 0; x < 32; x++) {
|
||||
|
@ -257,11 +255,10 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
return 1; // Unreachable
|
||||
}
|
||||
|
||||
// Determine the number of CPU cycles until something can happen
|
||||
// Determine the number of CPU cycles until a breakpoint could trigger
|
||||
int until(int cycles) {
|
||||
if (stage == FATAL || stage == HALT)
|
||||
return cycles;
|
||||
return cycles < 0 ? this.cycles : Math.min(cycles, this.cycles);
|
||||
return stage == FATAL || stage == HALT ?
|
||||
cycles : Math.min(cycles, this.cycles);
|
||||
}
|
||||
|
||||
|
||||
|
@ -273,19 +270,18 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
// Operations for exception stage
|
||||
private boolean exception() {
|
||||
|
||||
// Application callback
|
||||
vue.breakCode = vue.onException() ? 1 : 0;
|
||||
if (vue.breakCode != 0)
|
||||
// Check for breakpoints
|
||||
if (vue.onBreakpoint(Breakpoint.EXCEPTION))
|
||||
return true;
|
||||
|
||||
// Configure working variables
|
||||
exception.code &= 0xFFFF;
|
||||
boolean isIRQ = (exception.code & 0xFF00) == 0xFE00;
|
||||
int psw = getSystemRegister(Vue.PSW);
|
||||
exception &= 0xFFFF;
|
||||
boolean isIRQ = (exception & 0xFF00) == 0xFE00;
|
||||
int psw = getSystemRegister(Vue.PSW);
|
||||
|
||||
// Fatal exception
|
||||
if (psw_np != 0) {
|
||||
vue.write(0x00000000, Vue.S32, 0xFFFF0000 | exception.code);
|
||||
vue.write(0x00000000, Vue.S32, 0xFFFF0000 | exception);
|
||||
vue.write(0x00000004, Vue.S32, psw);
|
||||
vue.write(0x00000008, Vue.S32, pc);
|
||||
stage = FATAL;
|
||||
|
@ -294,7 +290,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
|
||||
// Duplexed exception
|
||||
if (psw_ep != 0) {
|
||||
ecr_fecc = exception.code;
|
||||
ecr_fecc = exception;
|
||||
fepc = pc;
|
||||
fepsw = psw;
|
||||
psw_np = 1;
|
||||
|
@ -303,37 +299,36 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
|
||||
// Regular exception
|
||||
else {
|
||||
ecr_eicc = exception.code;
|
||||
ecr_eicc = exception;
|
||||
eipc = pc;
|
||||
eipsw = psw;
|
||||
psw_ep = 1;
|
||||
pc = 0xFFFF0000 | exception.code & 0xFFF0;
|
||||
pc = 0xFFFF0000 | exception & 0xFFF0;
|
||||
if (pc == 0xFFFFFF70) // FIV
|
||||
pc = 0xFFFFFF60;
|
||||
}
|
||||
|
||||
// Interrupt
|
||||
if (isIRQ) {
|
||||
psw_i = Math.min(15, exception.code >> 4 & 15);
|
||||
psw_i = Math.min(15, exception >> 4 & 15);
|
||||
if (stage == HALT)
|
||||
pc += 2;
|
||||
}
|
||||
|
||||
// Common processing
|
||||
cycles = 0; // TODO: Determine the actual number
|
||||
exception.code = 0;
|
||||
psw_ae = 0;
|
||||
psw_id = 1;
|
||||
stage = FETCH;
|
||||
cycles = 0; // TODO: Determine the actual number
|
||||
exception = 0;
|
||||
psw_ae = 0;
|
||||
psw_id = 1;
|
||||
stage = FETCH;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Operations for execute stage
|
||||
private boolean execute() {
|
||||
|
||||
// Application callback
|
||||
vue.breakCode = vue.onExecute() ? 1 : 0;
|
||||
if (vue.breakCode != 0)
|
||||
// Check for breakpoints
|
||||
if (vue.onBreakpoint(Breakpoint.EXECUTE))
|
||||
return true;
|
||||
|
||||
// Processing by instruction ID
|
||||
|
@ -415,7 +410,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
case Vue.XORI : XORI (); break;
|
||||
//case Vue.XORNBSU: XORNBSU(); break;
|
||||
default: // Invalid instruction
|
||||
exception.code = 0xFF90;
|
||||
exception = 0xFF90;
|
||||
}
|
||||
|
||||
// An application break was requested
|
||||
|
@ -423,7 +418,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
return true;
|
||||
|
||||
// Common processing
|
||||
if (exception.code == 0) {
|
||||
if (exception == 0) {
|
||||
cycles += CYCLES[inst.id];
|
||||
pc += inst.size;
|
||||
}
|
||||
|
@ -437,9 +432,14 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
// Operations for fetch stage
|
||||
private boolean fetch() {
|
||||
|
||||
// Entering the fetch stage
|
||||
if (fetch == -1)
|
||||
fetch = 0;
|
||||
|
||||
// Read the bits from the bus
|
||||
if (read(pc + (fetch << 1), Vue.U16, fetch))
|
||||
return true;
|
||||
// TODO: Determine how many cycles this takes
|
||||
|
||||
// First unit
|
||||
if (fetch == 0) {
|
||||
|
@ -453,7 +453,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
// Second unit
|
||||
else {
|
||||
inst.bits |= access.value & 0xFFFF;
|
||||
fetch = 0;
|
||||
fetch = -1;
|
||||
}
|
||||
|
||||
// Decode the instruction and advance to execute stage
|
||||
|
@ -471,9 +471,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
access.type = type;
|
||||
access.value = vue.read(address, type);
|
||||
|
||||
// Application callback
|
||||
vue.breakCode = vue.onRead() ? 1 : 0;
|
||||
return vue.breakCode != 0;
|
||||
// Check for breakpoints
|
||||
return vue.onBreakpoint(Breakpoint.READ);
|
||||
}
|
||||
|
||||
// Test a condition
|
||||
|
@ -495,16 +494,16 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
private boolean testException() {
|
||||
|
||||
// Check for an interrupt
|
||||
if (irq != 0 && (exception.code | psw_id | psw_ep | psw_np) == 0) {
|
||||
if (irq != 0 && (exception | psw_id | psw_ep | psw_np) == 0) {
|
||||
int level;
|
||||
for (level = 4; level >= 0; level--)
|
||||
if ((irq >> level & 1) != 0)
|
||||
break;
|
||||
exception.code = 0xFE00 | level << 4;
|
||||
exception = 0xFE00 | level << 4;
|
||||
}
|
||||
|
||||
// There is no exception
|
||||
if (exception.code == 0)
|
||||
if (exception == 0)
|
||||
return stage == HALT && cycles == 0; // No further processing
|
||||
|
||||
// An exception has occurred
|
||||
|
@ -522,15 +521,13 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
access.type = type;
|
||||
access.value = value;
|
||||
|
||||
// Application callback
|
||||
vue.breakCode = vue.onWrite() ? 1 : 0;
|
||||
if (vue.breakCode != 0)
|
||||
// Check for breakpoints
|
||||
if (vue.onBreakpoint(Breakpoint.WRITE))
|
||||
return true;
|
||||
if (access.type == Vue.CANCEL)
|
||||
return false;
|
||||
|
||||
// Perform the operation
|
||||
vue.write(access.address, access.type, access.value);
|
||||
if (access.type != Vue.CANCEL)
|
||||
vue.write(access.address, access.type, access.value);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -572,7 +569,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
|
||||
// Invalid operation
|
||||
if (value > 0x7FFFFFFF || value < 0x80000000) {
|
||||
exception.code = 0xFF70;
|
||||
exception = 0xFF70;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -599,8 +596,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
)) continue;
|
||||
|
||||
// The value is a reserved operand
|
||||
exception.code = 0xFF60;
|
||||
psw_fro = 1;
|
||||
exception = 0xFF60;
|
||||
psw_fro = 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -611,8 +608,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
|
||||
// Overflow
|
||||
if (full > Float.MAX_VALUE || full < -Float.MAX_VALUE) {
|
||||
exception.code = 0xFF64;
|
||||
psw_fov = 1;
|
||||
exception = 0xFF64;
|
||||
psw_fov = 1;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -803,7 +800,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
|
||||
// Zero division
|
||||
if (right == 0) {
|
||||
exception.code = 0xFF80;
|
||||
exception = 0xFF80;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -837,7 +834,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
|
||||
// An exception has occurred
|
||||
if (right == 0) {
|
||||
exception.code = left == 0 ? 0xFF70 : 0xFF68;
|
||||
exception = left == 0 ? 0xFF70 : 0xFF68;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -854,7 +851,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
|
||||
// Zero division
|
||||
if (right == 0) {
|
||||
exception.code = 0xFF80;
|
||||
exception = 0xFF80;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1105,7 +1102,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
|||
|
||||
// Trap
|
||||
private void TRAP() {
|
||||
exception.code = 0xFFA0 | inst.imm & 31;
|
||||
exception = 0xFFA0 | inst.imm & 31;
|
||||
pc += 2;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -7,11 +7,11 @@ import java.util.*;
|
|||
class JavaVue extends Vue {
|
||||
|
||||
// Package fields
|
||||
int breakCode; // Application break code
|
||||
int breakCode; // Application breakpoint code
|
||||
int breakType; // Most recent breakpoint scenario
|
||||
CPU cpu; // Processor
|
||||
int hook; // Most recent breakpoint hook
|
||||
GamePak pak; // Game pak
|
||||
int[] stack; // Breakpoint evaluation stack
|
||||
int[] stack; // Breakpoint condition evaluation stack
|
||||
byte[] wram; // System WRAM
|
||||
|
||||
|
||||
|
@ -53,50 +53,47 @@ class JavaVue extends Vue {
|
|||
// Process up to the given number of cycles
|
||||
do {
|
||||
|
||||
// Determine the number of cycles during which nothing will happen
|
||||
int cycles = -1;
|
||||
// Determine the number of cycles where no breakpoint will occur
|
||||
int cycles = maxCycles; // min(maxCycles, nextFrameCycles)
|
||||
cycles = cpu .until(cycles);
|
||||
//cycles = pad .until(cycles);
|
||||
//cycles = link .until(cycles);
|
||||
//cycles = timer.until(cycles);
|
||||
//cycles = vip .until(cycles);
|
||||
//cycles = vsu .until(cycles);
|
||||
|
||||
// Range checking
|
||||
if (cycles == -1) // No activity on any component
|
||||
break;
|
||||
if (maxCycles >= 0) // Restrict to given number of cycles
|
||||
cycles = Math.min(cycles, maxCycles);
|
||||
|
||||
// Process all system components
|
||||
breakCode = 0;
|
||||
cpu .emulate(cycles);
|
||||
//pad .emulate(cycles);
|
||||
//link .emulate(cycles);
|
||||
//timer.emulate(cycles);
|
||||
//vip .emulate(cycles);
|
||||
//vsu .emulate(cycles);
|
||||
|
||||
// An application break was requested
|
||||
//if (...)
|
||||
// break;
|
||||
|
||||
// Update the number of cycles remaining
|
||||
if (maxCycles >= 0)
|
||||
maxCycles -= cycles;
|
||||
} while (maxCycles != 0);
|
||||
maxCycles -= cycles;
|
||||
} while (breakCode == 0 && maxCycles != 0);
|
||||
|
||||
// A break condition has occurred
|
||||
return Math.max(0, maxCycles);
|
||||
return maxCycles;
|
||||
}
|
||||
|
||||
// Evaluate an expression
|
||||
public Object evaluate(String expression) {
|
||||
return null;
|
||||
// Retrieve a snapshot of the current state's memory access
|
||||
public Access getAccess() {
|
||||
return cpu.access;
|
||||
}
|
||||
|
||||
// Retrieve the most recent applicaiton break code
|
||||
public int getBreakCode() {
|
||||
return breakCode;
|
||||
}
|
||||
|
||||
// Retrieve the most recent exception code
|
||||
public int getException() {
|
||||
return cpu.exception.code;
|
||||
public int getExceptionCode() {
|
||||
return cpu.exception;
|
||||
}
|
||||
|
||||
// Retrieve a snapshot of the current state's instruction
|
||||
public Instruction getInstruction() {
|
||||
return cpu.inst;
|
||||
}
|
||||
|
||||
// Retrieve a register value
|
||||
|
@ -139,6 +136,12 @@ class JavaVue extends Vue {
|
|||
|
||||
// Read a value from the CPU bus
|
||||
public int read(int address, int type) {
|
||||
|
||||
// Error checking
|
||||
if (type < 0 || type > 4)
|
||||
return 0;
|
||||
|
||||
// Perform the operation
|
||||
switch (address >> 24 & 7) {
|
||||
case 5: return readBuffer(wram , address, type);
|
||||
case 6: return readBuffer(pak.ram, address, type);
|
||||
|
@ -250,29 +253,48 @@ class JavaVue extends Vue {
|
|||
// Package Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Evaluate the condition in a breakpoint
|
||||
boolean evaluate(Breakpoint brk) {
|
||||
return false;
|
||||
// Retrieve the current state's breakpoint scenario
|
||||
int getBreakType() {
|
||||
return breakType;
|
||||
}
|
||||
|
||||
// Exception break handler
|
||||
boolean onException() {
|
||||
return onHook(Breakpoint.EXCEPTION);
|
||||
// Retrieve the current instruction fetch index
|
||||
int getFetch() {
|
||||
return cpu.fetch;
|
||||
}
|
||||
|
||||
// Execute break handler
|
||||
boolean onExecute() {
|
||||
return onHook(Breakpoint.EXECUTE);
|
||||
}
|
||||
// Check for breakpoints
|
||||
boolean onBreakpoint(int breakType) {
|
||||
int end = 0;
|
||||
boolean ranged = false;
|
||||
int start = 0;
|
||||
this.breakType = breakType;
|
||||
|
||||
// Read break handler
|
||||
boolean onRead() {
|
||||
return onHook(Breakpoint.READ);
|
||||
}
|
||||
// Processing for Execute
|
||||
if (breakType == Breakpoint.EXECUTE) {
|
||||
ranged = true;
|
||||
start = cpu.pc;
|
||||
end = start + cpu.inst.size - 1;
|
||||
}
|
||||
|
||||
// Write break handler
|
||||
boolean onWrite() {
|
||||
return onHook(Breakpoint.WRITE);
|
||||
// Processing for Read and Write
|
||||
else if (breakType==Breakpoint.READ || breakType==Breakpoint.WRITE) {
|
||||
ranged = true;
|
||||
start = cpu.access.address;
|
||||
end = start + TYPE_SIZES[cpu.access.type] - 1;
|
||||
}
|
||||
|
||||
// Check all breakpoints
|
||||
int count = breakpoints.size();
|
||||
for (int x = 0; breakCode == 0 && x < count; x++) {
|
||||
var brk = breakpoints.get(x);
|
||||
if (
|
||||
brk.appliesTo(breakType) &&
|
||||
(!ranged || brk.inRange(start, end)) &&
|
||||
brk.isTrue(stack, breakType, cpu.inst, cpu.access)
|
||||
) breakCode = x + 1;
|
||||
}
|
||||
return breakCode != 0;
|
||||
}
|
||||
|
||||
// Read a value from a byte buffer
|
||||
|
@ -331,6 +353,16 @@ class JavaVue extends Vue {
|
|||
|
||||
}
|
||||
|
||||
// A breakpoint's condition tokens have changed
|
||||
void updateTokens(Breakpoint brk) {
|
||||
super.updateTokens(brk);
|
||||
int depth = 0;
|
||||
for (var bre : breakpoints)
|
||||
depth = Math.max(depth, bre.getDepth());
|
||||
if (depth != stack.length)
|
||||
stack = new int[depth];
|
||||
}
|
||||
|
||||
// Write a value to a byte buffer
|
||||
static void writeBuffer(byte[] data, int address, int type, int value) {
|
||||
|
||||
|
@ -377,19 +409,4 @@ class JavaVue extends Vue {
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Private Methods //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Check breakpoints during a hooked event
|
||||
private boolean onHook(int hook) {
|
||||
this.hook = hook;
|
||||
for (var brk : breakpoints)
|
||||
if (evaluate(brk))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
|
|
@ -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) { }
|
||||
|
||||
|
|
Loading…
Reference in New Issue