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>
|
192
src/core/cpu.c
192
src/core/cpu.c
|
@ -116,7 +116,7 @@ static vbool cpuFloatReserved(Vue *vue, vbool left) {
|
||||||
)) continue;
|
)) continue;
|
||||||
|
|
||||||
/* The value is a reserved operand */
|
/* The value is a reserved operand */
|
||||||
vue->cpu.exception.code = 0xFF60;
|
vue->cpu.exception = 0xFF60;
|
||||||
vue->cpu.psw_fro = 1;
|
vue->cpu.psw_fro = 1;
|
||||||
return VUE_TRUE;
|
return VUE_TRUE;
|
||||||
}
|
}
|
||||||
|
@ -150,7 +150,7 @@ static void cpuFloatConvert(Vue *vue, vbool round) {
|
||||||
|
|
||||||
/* Invalid operation (word overflow) */
|
/* Invalid operation (word overflow) */
|
||||||
else if (bits >= 8) {
|
else if (bits >= 8) {
|
||||||
vue->cpu.exception.code = 0xFF70;
|
vue->cpu.exception = 0xFF70;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +193,7 @@ static void cpuFloatResult(Vue *vue, vbool compare, double full) {
|
||||||
|
|
||||||
/* Overflow */
|
/* Overflow */
|
||||||
if (full > result || full < -result) {
|
if (full > result || full < -result) {
|
||||||
vue->cpu.exception.code = 0xFF64;
|
vue->cpu.exception = 0xFF64;
|
||||||
vue->cpu.psw_fov = 1;
|
vue->cpu.psw_fov = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -578,7 +578,7 @@ static void cpuDIV(Vue *vue) {
|
||||||
|
|
||||||
/* Zero division */
|
/* Zero division */
|
||||||
if (right == 0) {
|
if (right == 0) {
|
||||||
vue->cpu.exception.code = 0xFF80;
|
vue->cpu.exception = 0xFF80;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -612,7 +612,7 @@ static void cpuDIVF_S(Vue *vue) {
|
||||||
|
|
||||||
/* An exception has occurred */
|
/* An exception has occurred */
|
||||||
if (!right) {
|
if (!right) {
|
||||||
vue->cpu.exception.code = left ? 0xFF68 : 0xFF70;
|
vue->cpu.exception = left ? 0xFF68 : 0xFF70;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -628,7 +628,7 @@ static void cpuDIVU(Vue *vue) {
|
||||||
|
|
||||||
/* Zero division */
|
/* Zero division */
|
||||||
if (right == 0) {
|
if (right == 0) {
|
||||||
vue->cpu.exception.code = 0xFF80;
|
vue->cpu.exception = 0xFF80;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -848,7 +848,7 @@ static void cpuREV(Vue *vue) {
|
||||||
|
|
||||||
/* Trap */
|
/* Trap */
|
||||||
#define cpuTRAP(vue) \
|
#define cpuTRAP(vue) \
|
||||||
vue->cpu.exception.code = 0xFFA0 | (vue->cpu.inst.imm & 15), \
|
vue->cpu.exception = 0xFFA0 | (vue->cpu.inst.imm & 15), \
|
||||||
vue->cpu.pc += 2
|
vue->cpu.pc += 2
|
||||||
|
|
||||||
/* Truncate Short Floating to Word */
|
/* Truncate Short Floating to Word */
|
||||||
|
@ -908,8 +908,12 @@ static void cpuDecode(VueInstruction *inst) {
|
||||||
x = inst->bits >> 16 & 31;
|
x = inst->bits >> 16 & 31;
|
||||||
inst->reg2 = inst->bits >> 21 & 31;
|
inst->reg2 = inst->bits >> 21 & 31;
|
||||||
inst->imm = extend < 0 ? SIGN_EXTEND(5, x) : x;
|
inst->imm = extend < 0 ? SIGN_EXTEND(5, x) : x;
|
||||||
if (inst->id == BITSTRING)
|
if (inst->id == BITSTRING) {
|
||||||
inst->id = x >= 16 ? VUE_ILLEGAL : LOOKUP_BITSTRING[x];
|
inst->id = x >= 16 ? VUE_ILLEGAL : LOOKUP_BITSTRING[x];
|
||||||
|
inst->subopcode = x;
|
||||||
|
}
|
||||||
|
if (inst->id == VUE_SETF)
|
||||||
|
inst->cond = x & 15;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
x = inst->bits >> 16 & 0x1FF;
|
x = inst->bits >> 16 & 0x1FF;
|
||||||
|
@ -944,21 +948,84 @@ static void cpuDecode(VueInstruction *inst) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Operations for exception stage */
|
||||||
|
static vbool cpuException(Vue *vue) {
|
||||||
|
vbool isIRQ; /* The exception is an interrupt */
|
||||||
|
int32_t psw; /* Current value of PSW */
|
||||||
|
|
||||||
|
/* Application callback */
|
||||||
|
if (vue->onException != NULL) {
|
||||||
|
vue->breakCode = vue->onException(vue, vue->cpu.exception);
|
||||||
|
if (vue->breakCode != 0)
|
||||||
|
return VUE_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure working variables */
|
||||||
|
vue->cpu.exception &= 0xFFFF;
|
||||||
|
isIRQ = (vue->cpu.exception & 0xFF00) == 0xFE00;
|
||||||
|
psw = cpuGetSystemRegister(vue, VUE_PSW);
|
||||||
|
|
||||||
|
/* Fatal exception */
|
||||||
|
if (vue->cpu.psw_np != 0) {
|
||||||
|
vueWrite(vue, 0x00000000, VUE_S32, 0xFFFF0000 | vue->cpu.exception);
|
||||||
|
vueWrite(vue, 0x00000004, VUE_S32, psw);
|
||||||
|
vueWrite(vue, 0x00000008, VUE_S32, vue->cpu.pc);
|
||||||
|
vue->cpu.stage = CPU_FATAL;
|
||||||
|
return VUE_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Duplexed exception */
|
||||||
|
if (vue->cpu.psw_ep != 0) {
|
||||||
|
vue->cpu.ecr_fecc = vue->cpu.exception;
|
||||||
|
vue->cpu.fepc = vue->cpu.pc;
|
||||||
|
vue->cpu.fepsw = psw;
|
||||||
|
vue->cpu.psw_np = 1;
|
||||||
|
vue->cpu.pc = 0xFFFFFFD0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Regular exception */
|
||||||
|
else {
|
||||||
|
vue->cpu.ecr_eicc = vue->cpu.exception;
|
||||||
|
vue->cpu.eipc = vue->cpu.pc;
|
||||||
|
vue->cpu.eipsw = psw;
|
||||||
|
vue->cpu.psw_ep = 1;
|
||||||
|
vue->cpu.pc = 0xFFFF0000 | (vue->cpu.exception & 0xFFF0);
|
||||||
|
if (vue->cpu.pc == (int32_t) 0xFFFFFF70) /* FIV */
|
||||||
|
vue->cpu.pc = 0xFFFFFF60;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Interrupt */
|
||||||
|
if (isIRQ) {
|
||||||
|
psw = vue->cpu.exception >> 4 & 15;
|
||||||
|
vue->cpu.psw_i = psw < 15 ? psw : 15;
|
||||||
|
if (vue->cpu.stage == CPU_HALT)
|
||||||
|
vue->cpu.pc += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Common processing */
|
||||||
|
vue->cpu.cycles = 0; /* TODO: Determine the actual number */
|
||||||
|
vue->cpu.exception = 0;
|
||||||
|
vue->cpu.psw_ae = 0;
|
||||||
|
vue->cpu.psw_id = 1;
|
||||||
|
vue->cpu.stage = CPU_FETCH;
|
||||||
|
return VUE_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/* Check for an exception or interrupt */
|
/* Check for an exception or interrupt */
|
||||||
static vbool cpuTestException(Vue *vue) {
|
static vbool cpuTestException(Vue *vue) {
|
||||||
int32_t level; /* Interrupt level */
|
int32_t level; /* Interrupt level */
|
||||||
|
|
||||||
/* Check for an interrupt */
|
/* Check for an interrupt */
|
||||||
if (vue->cpu.irq != 0 && (vue->cpu.exception.code |
|
if (vue->cpu.irq != 0 && (vue->cpu.exception |
|
||||||
vue->cpu.psw_id | vue->cpu.psw_ep | vue->cpu.psw_np) == 0) {
|
vue->cpu.psw_id | vue->cpu.psw_ep | vue->cpu.psw_np) == 0) {
|
||||||
for (level = 4; level >= 0; level--)
|
for (level = 4; level >= 0; level--)
|
||||||
if ((vue->cpu.irq >> level & 1) != 0)
|
if ((vue->cpu.irq >> level & 1) != 0)
|
||||||
break;
|
break;
|
||||||
vue->cpu.exception.code = 0xFE00 | level << 4;
|
vue->cpu.exception = 0xFE00 | level << 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* There is no exception */
|
/* There is no exception */
|
||||||
if (vue->cpu.exception.code == 0) /* No further processing */
|
if (vue->cpu.exception == 0) /* No further processing */
|
||||||
return vue->cpu.stage == CPU_HALT && vue->cpu.cycles == 0;
|
return vue->cpu.stage == CPU_HALT && vue->cpu.cycles == 0;
|
||||||
|
|
||||||
/* An exception has occurred */
|
/* An exception has occurred */
|
||||||
|
@ -975,8 +1042,6 @@ static vbool cpuExecute(Vue *vue) {
|
||||||
vue->breakCode = vue->onExecute(vue, &vue->cpu.inst);
|
vue->breakCode = vue->onExecute(vue, &vue->cpu.inst);
|
||||||
if (vue->breakCode != 0)
|
if (vue->breakCode != 0)
|
||||||
return VUE_TRUE;
|
return VUE_TRUE;
|
||||||
vue->cpu.inst.reg1 &= 31;
|
|
||||||
vue->cpu.inst.reg2 &= 31;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Processing by instruction ID */
|
/* Processing by instruction ID */
|
||||||
|
@ -1058,11 +1123,11 @@ static vbool cpuExecute(Vue *vue) {
|
||||||
case VUE_XORI : cpuXORI (vue); break;
|
case VUE_XORI : cpuXORI (vue); break;
|
||||||
/*case VUE_XORNBSU: cpuXORNBSU(vue); break;*/
|
/*case VUE_XORNBSU: cpuXORNBSU(vue); break;*/
|
||||||
default: /* Invalid instruction */
|
default: /* Invalid instruction */
|
||||||
vue->cpu.exception.code = 0xFF90;
|
vue->cpu.exception = 0xFF90;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Common processing */
|
/* Common processing */
|
||||||
if (vue->cpu.exception.code == 0) {
|
if (vue->cpu.exception == 0) {
|
||||||
vue->cpu.cycles += CYCLES[vue->cpu.inst.id];
|
vue->cpu.cycles += CYCLES[vue->cpu.inst.id];
|
||||||
vue->cpu.pc += vue->cpu.inst.size;
|
vue->cpu.pc += vue->cpu.inst.size;
|
||||||
}
|
}
|
||||||
|
@ -1082,10 +1147,15 @@ static int32_t cpuSize(int32_t opcode) {
|
||||||
/* Operations for fetch stage */
|
/* Operations for fetch stage */
|
||||||
static vbool cpuFetch(Vue *vue) {
|
static vbool cpuFetch(Vue *vue) {
|
||||||
|
|
||||||
|
/* Entering the fetch stage */
|
||||||
|
if (vue->cpu.fetch == -1)
|
||||||
|
vue->cpu.fetch = 0;
|
||||||
|
|
||||||
/* Read the bits from the bus */
|
/* Read the bits from the bus */
|
||||||
if (cpuRead(vue, vue->cpu.pc + (vue->cpu.fetch << 1),
|
if (cpuRead(vue, vue->cpu.pc + (vue->cpu.fetch << 1),
|
||||||
VUE_U16, vue->cpu.fetch))
|
VUE_U16, vue->cpu.fetch))
|
||||||
return VUE_TRUE;
|
return VUE_TRUE;
|
||||||
|
/* TODO: Determine how many cycles this takes */
|
||||||
|
|
||||||
/* First unit */
|
/* First unit */
|
||||||
if (vue->cpu.fetch == 0) {
|
if (vue->cpu.fetch == 0) {
|
||||||
|
@ -1099,7 +1169,7 @@ static vbool cpuFetch(Vue *vue) {
|
||||||
/* Second unit */
|
/* Second unit */
|
||||||
else {
|
else {
|
||||||
vue->cpu.inst.bits |= vue->cpu.access.value & 0xFFFF;
|
vue->cpu.inst.bits |= vue->cpu.access.value & 0xFFFF;
|
||||||
vue->cpu.fetch = 0;
|
vue->cpu.fetch = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Decode the instruction and advance to execute stage */
|
/* Decode the instruction and advance to execute stage */
|
||||||
|
@ -1108,94 +1178,30 @@ static vbool cpuFetch(Vue *vue) {
|
||||||
return VUE_FALSE;
|
return VUE_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Operations for exception stage */
|
|
||||||
static vbool cpuException(Vue *vue) {
|
|
||||||
vbool isIRQ; /* The exception is an interrupt */
|
|
||||||
int32_t psw; /* Current value of PSW */
|
|
||||||
|
|
||||||
/* Application callback */
|
|
||||||
if (vue->onException != NULL) {
|
|
||||||
vue->breakCode = vue->onException(vue, &vue->cpu.exception);
|
|
||||||
if (vue->breakCode != 0)
|
|
||||||
return VUE_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Configure working variables */
|
|
||||||
vue->cpu.exception.code &= 0xFFFF;
|
|
||||||
isIRQ = (vue->cpu.exception.code & 0xFF00) == 0xFE00;
|
|
||||||
psw = cpuGetSystemRegister(vue, VUE_PSW);
|
|
||||||
|
|
||||||
/* Fatal exception */
|
|
||||||
if (vue->cpu.psw_np != 0) {
|
|
||||||
vueWrite(vue, 0x00000000, VUE_S32, 0xFFFF0000|vue->cpu.exception.code);
|
|
||||||
vueWrite(vue, 0x00000004, VUE_S32, psw);
|
|
||||||
vueWrite(vue, 0x00000008, VUE_S32, vue->cpu.pc);
|
|
||||||
vue->cpu.stage = CPU_FATAL;
|
|
||||||
return VUE_TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Duplexed exception */
|
|
||||||
if (vue->cpu.psw_ep != 0) {
|
|
||||||
vue->cpu.ecr_fecc = vue->cpu.exception.code;
|
|
||||||
vue->cpu.fepc = vue->cpu.pc;
|
|
||||||
vue->cpu.fepsw = psw;
|
|
||||||
vue->cpu.psw_np = 1;
|
|
||||||
vue->cpu.pc = 0xFFFFFFD0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Regular exception */
|
|
||||||
else {
|
|
||||||
vue->cpu.ecr_eicc = vue->cpu.exception.code;
|
|
||||||
vue->cpu.eipc = vue->cpu.pc;
|
|
||||||
vue->cpu.eipsw = psw;
|
|
||||||
vue->cpu.psw_ep = 1;
|
|
||||||
vue->cpu.pc = 0xFFFF0000 | (vue->cpu.exception.code & 0xFFF0);
|
|
||||||
if (vue->cpu.pc == (int32_t) 0xFFFFFF70) /* FIV */
|
|
||||||
vue->cpu.pc = 0xFFFFFF60;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Interrupt */
|
|
||||||
if (isIRQ) {
|
|
||||||
psw = vue->cpu.exception.code >> 4 & 15;
|
|
||||||
vue->cpu.psw_i = psw < 15 ? psw : 15;
|
|
||||||
if (vue->cpu.stage == CPU_HALT)
|
|
||||||
vue->cpu.pc += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Common processing */
|
|
||||||
vue->cpu.cycles = 0; /* TODO: Determine the actual number */
|
|
||||||
vue->cpu.exception.code = 0;
|
|
||||||
vue->cpu.psw_ae = 0;
|
|
||||||
vue->cpu.psw_id = 1;
|
|
||||||
vue->cpu.stage = CPU_FETCH;
|
|
||||||
return VUE_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Process the simulation */
|
/* Process the simulation */
|
||||||
static void cpuEmulate(Vue *vue, int32_t cycles) {
|
static void cpuEmulate(Vue *vue, uint32_t cycles) {
|
||||||
|
|
||||||
/* The CPU is halting */
|
/* The CPU is in fatal halt status */
|
||||||
if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT)
|
if (vue->cpu.stage == CPU_FATAL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
vue->cpu.cycles = 0; /* DEBUG: Stop processing after execute */
|
|
||||||
|
|
||||||
/* Process for the given number of cycles */
|
/* Process for the given number of cycles */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
/* The next event occurs after the given number of cycles */
|
/* The next stage occurs after the given number of cycles */
|
||||||
if (vue->cpu.cycles > cycles) {
|
if (vue->cpu.cycles > cycles) {
|
||||||
vue->cpu.cycles -= cycles;
|
vue->cpu.cycles -= cycles;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Processing by stage */
|
/* Processing by stage */
|
||||||
|
cycles -= vue->cpu.cycles;
|
||||||
vue->cpu.cycles = 0;
|
vue->cpu.cycles = 0;
|
||||||
switch (vue->cpu.stage) {
|
switch (vue->cpu.stage) {
|
||||||
case CPU_EXCEPTION: if (cpuException(vue)) return; break;
|
case CPU_EXCEPTION: if (cpuException(vue)) return; break;
|
||||||
case CPU_EXECUTE : if (cpuExecute (vue)) return; break;
|
case CPU_EXECUTE : if (cpuExecute (vue)) return; break;
|
||||||
case CPU_FETCH : if (cpuFetch (vue)) return; break;
|
case CPU_FETCH : if (cpuFetch (vue)) return; break;
|
||||||
case CPU_HALT : if (cpuTestException(vue)) return; break;
|
case CPU_HALT : cpuTestException(vue); return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1208,8 +1214,8 @@ static void cpuReset(Vue *vue) {
|
||||||
|
|
||||||
/* Configure instance fields */
|
/* Configure instance fields */
|
||||||
vue->cpu.cycles = 0;
|
vue->cpu.cycles = 0;
|
||||||
vue->cpu.exception.code = 0;
|
vue->cpu.exception = 0;
|
||||||
vue->cpu.fetch = 0;
|
vue->cpu.fetch = -1;
|
||||||
vue->cpu.irq = 0;
|
vue->cpu.irq = 0;
|
||||||
vue->cpu.stage = CPU_FETCH;
|
vue->cpu.stage = CPU_FETCH;
|
||||||
|
|
||||||
|
@ -1229,12 +1235,10 @@ static void cpuReset(Vue *vue) {
|
||||||
vue->cpu.psw_np = 1;
|
vue->cpu.psw_np = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Determine the number of CPU cycles until something can happen */
|
/* Determine the number of CPU cycles until a breakpoint could trigger */
|
||||||
static int32_t cpuUntil(Vue *vue, int32_t cycles) {
|
static uint32_t cpuUntil(Vue *vue, uint32_t cycles) {
|
||||||
if (vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT)
|
return vue->cpu.stage == CPU_FATAL || vue->cpu.stage == CPU_HALT ?
|
||||||
return cycles;
|
cycles : cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles;
|
||||||
return cycles < 0 ? vue->cpu.cycles :
|
|
||||||
cycles < vue->cpu.cycles ? cycles : vue->cpu.cycles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -154,11 +154,6 @@ typedef struct {
|
||||||
int8_t type; /* Data type */
|
int8_t type; /* Data type */
|
||||||
} VueAccess;
|
} VueAccess;
|
||||||
|
|
||||||
/* Exception state */
|
|
||||||
typedef struct {
|
|
||||||
uint16_t code; /* Exception code */
|
|
||||||
} VueException;
|
|
||||||
|
|
||||||
/* Instruction state */
|
/* Instruction state */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int32_t bits; /* Binary encoding */
|
int32_t bits; /* Binary encoding */
|
||||||
|
@ -174,31 +169,32 @@ typedef struct {
|
||||||
uint8_t subopcode; /* Instruction subopcode */
|
uint8_t subopcode; /* Instruction subopcode */
|
||||||
} VueInstruction;
|
} VueInstruction;
|
||||||
|
|
||||||
/* Callbacks */
|
/* Breakpoint handler callbacks */
|
||||||
typedef int32_t (*VueOnException)(Vue *, VueException *);
|
typedef int32_t (*VueOnAccess )(Vue *, VueAccess *);
|
||||||
|
typedef int32_t (*VueOnException)(Vue *, uint16_t );
|
||||||
typedef int32_t (*VueOnExecute )(Vue *, VueInstruction *);
|
typedef int32_t (*VueOnExecute )(Vue *, VueInstruction *);
|
||||||
typedef int32_t (*VueOnRead )(Vue *, VueAccess *);
|
typedef int32_t (*VueOnFrame )(Vue * );
|
||||||
typedef int32_t (*VueOnWrite )(Vue *, VueAccess *);
|
|
||||||
|
|
||||||
/* Emulation state */
|
/* Emulation state */
|
||||||
struct Vue {
|
struct Vue {
|
||||||
int32_t breakCode; /* Application break code */
|
int32_t breakCode; /* Application break code */
|
||||||
uint8_t wram[0x10000]; /* System memory */
|
uint8_t wram[0x10000]; /* System memory */
|
||||||
|
|
||||||
/* Callback handlers */
|
/* Breakpoint handlers */
|
||||||
VueOnException onException;
|
VueOnException onException;
|
||||||
VueOnExecute onExecute;
|
VueOnExecute onExecute;
|
||||||
VueOnRead onRead;
|
VueOnFrame onFrame;
|
||||||
VueOnWrite onWrite;
|
VueOnAccess onRead;
|
||||||
|
VueOnAccess onWrite;
|
||||||
|
|
||||||
/* CPU state */
|
/* CPU state */
|
||||||
struct {
|
struct {
|
||||||
VueAccess access; /* Access state */
|
VueAccess access; /* Access state */
|
||||||
VueException exception; /* Exception state */
|
|
||||||
VueInstruction inst; /* Instruction state */
|
VueInstruction inst; /* Instruction state */
|
||||||
int32_t cycles; /* Cycles until next stage */
|
uint32_t cycles; /* Cycles until next stage */
|
||||||
int32_t jumpFrom[3]; /* Source PCs of most recent jumps */
|
int32_t jumpFrom[3]; /* Source PCs of most recent jumps */
|
||||||
int32_t jumpTo [3]; /* Destination PCs of most recent jumps */
|
int32_t jumpTo [3]; /* Destination PCs of most recent jumps */
|
||||||
|
uint16_t exception; /* Exception code */
|
||||||
uint16_t irq; /* Interrupt lines */
|
uint16_t irq; /* Interrupt lines */
|
||||||
int fetch; /* Fetch unit index */
|
int fetch; /* Fetch unit index */
|
||||||
int stage; /* Current processing stage */
|
int stage; /* Current processing stage */
|
||||||
|
@ -265,19 +261,21 @@ struct Vue {
|
||||||
* Function Prototypes *
|
* Function Prototypes *
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
VUEAPI int32_t vueEmulate (Vue *vue, int32_t maxCycles);
|
VUEAPI uint32_t vueEmulate (Vue *vue, uint32_t maxCycles);
|
||||||
VUEAPI int32_t vueGetBreakCode (Vue *vue);
|
VUEAPI int32_t vueGetBreakCode (Vue *vue);
|
||||||
|
VUEAPI uint16_t vueGetExceptionCode (Vue *vue);
|
||||||
VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system);
|
VUEAPI int32_t vueGetRegister (Vue *vue, int32_t index, vbool system);
|
||||||
VUEAPI void vueInitialize (Vue *vue);
|
VUEAPI void vueInitialize (Vue *vue);
|
||||||
|
VUEAPI VueOnException vueOnException (Vue *vue, VueOnException callback);
|
||||||
|
VUEAPI VueOnExecute vueOnExecute (Vue *vue, VueOnExecute callback);
|
||||||
|
VUEAPI VueOnFrame vueOnFrame (Vue *vue, VueOnFrame callback);
|
||||||
|
VUEAPI VueOnAccess vueOnRead (Vue *vue, VueOnAccess callback);
|
||||||
|
VUEAPI VueOnAccess vueOnWrite (Vue *vue, VueOnAccess callback);
|
||||||
VUEAPI int32_t vueRead (Vue *vue, uint32_t address, int32_t type);
|
VUEAPI int32_t vueRead (Vue *vue, uint32_t address, int32_t type);
|
||||||
VUEAPI vbool vueReadBytes (Vue *vue, uint32_t address, uint8_t *dest, uint32_t length);
|
VUEAPI vbool vueReadBytes (Vue *vue, uint32_t address, uint8_t *dest, uint32_t length);
|
||||||
VUEAPI void vueReset (Vue *vue);
|
VUEAPI void vueReset (Vue *vue);
|
||||||
VUEAPI void vueSetException(Vue *vue, VueOnException callback);
|
|
||||||
VUEAPI void vueSetExecute (Vue *vue, VueOnExecute callback);
|
|
||||||
VUEAPI void vueSetRead (Vue *vue, VueOnRead callback);
|
|
||||||
VUEAPI int32_t vueSetRegister (Vue *vue, int32_t index, vbool system, int32_t value);
|
VUEAPI int32_t vueSetRegister (Vue *vue, int32_t index, vbool system, int32_t value);
|
||||||
VUEAPI vbool vueSetROM (Vue *vue, uint8_t *rom, uint32_t size);
|
VUEAPI vbool vueSetROM (Vue *vue, uint8_t *rom, uint32_t size);
|
||||||
VUEAPI void vueSetWrite (Vue *vue, VueOnWrite callback);
|
|
||||||
VUEAPI void vueWrite (Vue *vue, uint32_t address, int32_t type, int32_t value);
|
VUEAPI void vueWrite (Vue *vue, uint32_t address, int32_t type, int32_t value);
|
||||||
VUEAPI vbool vueWriteBytes (Vue *vue, uint32_t address, uint8_t *src, uint32_t length);
|
VUEAPI vbool vueWriteBytes (Vue *vue, uint32_t address, uint8_t *src, uint32_t length);
|
||||||
|
|
||||||
|
|
115
src/core/vue.c
115
src/core/vue.c
|
@ -155,55 +155,46 @@ static void writeBytes(uint8_t *data, uint32_t datlen, uint32_t address,
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
/* Process the simulation */
|
/* Process the simulation */
|
||||||
int32_t vueEmulate(Vue *vue, int32_t maxCycles) {
|
uint32_t vueEmulate(Vue *vue, uint32_t maxCycles) {
|
||||||
int32_t cycles; /* Number of cycles to process */
|
uint32_t cycles; /* Number of cycles to process */
|
||||||
|
|
||||||
/* Process up to the given number of cycles */
|
/* Process up to the given number of cycles */
|
||||||
do {
|
do {
|
||||||
|
|
||||||
/* Determine the number of cycles during which nothing will happen */
|
/* Determine the number of cycles where no breakpoint will occur */
|
||||||
cycles = -1;
|
cycles = maxCycles; /* min(maxCycles, nextFrameCycles) */
|
||||||
|
cycles = cpuUntil (vue, cycles);
|
||||||
/*cycles = padUntil (vue, cycles);*/
|
/*cycles = padUntil (vue, cycles);*/
|
||||||
/*cycles = linkUntil (vue, cycles);*/
|
/*cycles = linkUntil (vue, cycles);*/
|
||||||
/*cycles = timerUntil(vue, cycles);*/
|
/*cycles = timerUntil(vue, cycles);*/
|
||||||
/*cycles = vipUntil (vue, cycles);*/
|
/*cycles = vipUntil (vue, cycles);*/
|
||||||
/*cycles = vsuUntil (vue, cycles);*/
|
|
||||||
cycles = cpuUntil (vue, cycles);
|
|
||||||
|
|
||||||
/* Range checking */
|
|
||||||
if (cycles == -1) /* No activity on any component */
|
|
||||||
break;
|
|
||||||
if (maxCycles >= 0) /* Restrict to given number of cycles */
|
|
||||||
cycles = cycles < maxCycles ? cycles : maxCycles;
|
|
||||||
|
|
||||||
/* Process all system components */
|
/* Process all system components */
|
||||||
vue->breakCode = 0;
|
vue->breakCode = 0;
|
||||||
|
cpuEmulate (vue, cycles);
|
||||||
/*padEmulate (vue, cycles);*/
|
/*padEmulate (vue, cycles);*/
|
||||||
/*pakEmulate (vue, cycles);*/
|
/*pakEmulate (vue, cycles);*/
|
||||||
/*linkEmulate (vue, cycles);*/
|
/*linkEmulate (vue, cycles);*/
|
||||||
/*timerEmulate(vue, cycles);*/
|
/*timerEmulate(vue, cycles);*/
|
||||||
/*vipEmulate (vue, cycles);*/
|
/*vipEmulate (vue, cycles);*/
|
||||||
/*vsuEmulate (vue, cycles);*/
|
/*vsuEmulate (vue, cycles);*/
|
||||||
cpuEmulate (vue, cycles);
|
|
||||||
|
|
||||||
/* An application break was requested */
|
|
||||||
if (vue->breakCode != 0)
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* Update the number of cycles remaining */
|
|
||||||
if (maxCycles >= 0)
|
|
||||||
maxCycles -= cycles;
|
maxCycles -= cycles;
|
||||||
} while (maxCycles != 0);
|
} while (vue->breakCode == 0 && maxCycles != 0);
|
||||||
|
|
||||||
/* A break condition has occurred */
|
/* A break condition has occurred */
|
||||||
return maxCycles;
|
return maxCycles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Retrieve the application break code */
|
/* Retrieve the most recent applicaiton break code */
|
||||||
int32_t vueGetBreakCode(Vue *vue) {
|
int32_t vueGetBreakCode(Vue *vue) {
|
||||||
return vue == NULL ? 0 : vue->breakCode;
|
return vue == NULL ? 0 : vue->breakCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Retrieve the most recent exception code */
|
||||||
|
uint16_t vueGetExceptionCode(Vue *vue) {
|
||||||
|
return vue == NULL ? 0 : vue->cpu.exception;
|
||||||
|
}
|
||||||
|
|
||||||
/* Retrieve the value of a register */
|
/* Retrieve the value of a register */
|
||||||
int32_t vueGetRegister(Vue *vue, int32_t index, vbool system) {
|
int32_t vueGetRegister(Vue *vue, int32_t index, vbool system) {
|
||||||
|
|
||||||
|
@ -234,17 +225,69 @@ void vueInitialize(Vue *vue) {
|
||||||
return;
|
return;
|
||||||
vue->onException = NULL;
|
vue->onException = NULL;
|
||||||
vue->onExecute = NULL;
|
vue->onExecute = NULL;
|
||||||
|
vue->onFrame = NULL;
|
||||||
vue->onRead = NULL;
|
vue->onRead = NULL;
|
||||||
vue->onWrite = NULL;
|
vue->onWrite = NULL;
|
||||||
vue->pak.ram = NULL;
|
vue->pak.ram = NULL;
|
||||||
vue->pak.rom = NULL;
|
vue->pak.rom = NULL;
|
||||||
|
vueReset(vue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify an exception breakpoint callback */
|
||||||
|
VueOnException vueOnException(Vue *vue, VueOnException callback) {
|
||||||
|
VueOnException ret;
|
||||||
|
if (vue == NULL)
|
||||||
|
return NULL;
|
||||||
|
ret = vue->onException;
|
||||||
|
vue->onException = callback;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify an execute breakpoint callback */
|
||||||
|
VueOnExecute vueOnExecute(Vue *vue, VueOnExecute callback) {
|
||||||
|
VueOnExecute ret;
|
||||||
|
if (vue == NULL)
|
||||||
|
return NULL;
|
||||||
|
ret = vue->onExecute;
|
||||||
|
vue->onExecute = callback;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify a frame breakpoint callback */
|
||||||
|
VueOnFrame vueOnFrame(Vue *vue, VueOnFrame callback) {
|
||||||
|
VueOnFrame ret;
|
||||||
|
if (vue == NULL)
|
||||||
|
return NULL;
|
||||||
|
ret = vue->onFrame;
|
||||||
|
vue->onFrame = callback;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify a read breakpoint callback */
|
||||||
|
VueOnAccess vueSetRead(Vue *vue, VueOnAccess callback) {
|
||||||
|
VueOnAccess ret;
|
||||||
|
if (vue == NULL)
|
||||||
|
return NULL;
|
||||||
|
ret = vue->onRead;
|
||||||
|
vue->onRead = callback;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Specify a write breakpoint callback */
|
||||||
|
VueOnAccess vueOnWrite(Vue *vue, VueOnAccess callback) {
|
||||||
|
VueOnAccess ret;
|
||||||
|
if (vue == NULL)
|
||||||
|
return NULL;
|
||||||
|
ret = vue->onWrite;
|
||||||
|
vue->onWrite = callback;
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read a value from the CPU bus */
|
/* Read a value from the CPU bus */
|
||||||
int32_t vueRead(Vue *vue, uint32_t address, int32_t type) {
|
int32_t vueRead(Vue *vue, uint32_t address, int32_t type) {
|
||||||
|
|
||||||
/* Error checking */
|
/* Error checking */
|
||||||
if (vue == NULL)
|
if (vue == NULL || type < 0 || type > 4)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Perform the operation */
|
/* Perform the operation */
|
||||||
|
@ -299,28 +342,10 @@ void vueReset(Vue *vue) {
|
||||||
/* Reset state */
|
/* Reset state */
|
||||||
cpuReset(vue);
|
cpuReset(vue);
|
||||||
pakReset(vue);
|
pakReset(vue);
|
||||||
for (x = 0; x < 0x1000; x++)
|
for (x = 0; x < 0x10000; x++)
|
||||||
vue->wram[x] = 0;
|
vue->wram[x] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Specify an exception breakpoint callback */
|
|
||||||
void vueSetException(Vue *vue, VueOnException callback) {
|
|
||||||
if (vue != NULL)
|
|
||||||
vue->onException = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Specify an execute breakpoint callback */
|
|
||||||
void vueSetExecute(Vue *vue, VueOnExecute callback) {
|
|
||||||
if (vue != NULL)
|
|
||||||
vue->onExecute = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Specify a read breakpoint callback */
|
|
||||||
void vueSetRead(Vue *vue, VueOnRead callback) {
|
|
||||||
if (vue != NULL)
|
|
||||||
vue->onRead = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Specify a value for a register */
|
/* Specify a value for a register */
|
||||||
int32_t vueSetRegister(Vue *vue, int32_t index, vbool system, int32_t value) {
|
int32_t vueSetRegister(Vue *vue, int32_t index, vbool system, int32_t value) {
|
||||||
return vue == NULL ? 0 :
|
return vue == NULL ? 0 :
|
||||||
|
@ -348,12 +373,6 @@ vbool vueSetROM(Vue *vue, uint8_t *rom, uint32_t size) {
|
||||||
return VUE_TRUE;
|
return VUE_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Specify a write breakpoint callback */
|
|
||||||
void vueSetWrite(Vue *vue, VueOnRead callback) {
|
|
||||||
if (vue != NULL)
|
|
||||||
vue->onWrite = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Write a value to the CPU bus */
|
/* Write a value to the CPU bus */
|
||||||
void vueWrite(Vue *vue, uint32_t address, int32_t type, int32_t value) {
|
void vueWrite(Vue *vue, uint32_t address, int32_t type, int32_t value) {
|
||||||
|
|
||||||
|
|
|
@ -49,43 +49,7 @@ public class Main {
|
||||||
useNative = true;
|
useNative = true;
|
||||||
|
|
||||||
// Begin application operations
|
// Begin application operations
|
||||||
//new App(useNative);
|
new App(useNative);
|
||||||
|
|
||||||
var brk = new Breakpoint(null);
|
|
||||||
|
|
||||||
/*
|
|
||||||
//String exp =
|
|
||||||
// "([sp - 8] & 3) << 6 != 12.0 + r6 * ecr && [0x0500008C + r8] == 0";
|
|
||||||
//String exp = "float true + -3 * 4";
|
|
||||||
String exp = "1100 ^ uword 1010";
|
|
||||||
System.out.println("\n" + exp);
|
|
||||||
if (brk.setCondition(exp))
|
|
||||||
System.out.println(brk.debugTokens());
|
|
||||||
else {
|
|
||||||
var err = brk.getConditionError();
|
|
||||||
System.out.println("Error " +
|
|
||||||
err.code + "\t" +
|
|
||||||
err.position + ":" +
|
|
||||||
err.text
|
|
||||||
);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
exp = "123, 456 - 987 , f0-fffffff2";
|
|
||||||
System.out.println("\n" + exp);
|
|
||||||
if (brk.setAddresses(exp))
|
|
||||||
System.out.println(brk.debugRanges());
|
|
||||||
else {
|
|
||||||
var err = brk.getAddressError();
|
|
||||||
System.out.println("Error " +
|
|
||||||
err.code + "\t" +
|
|
||||||
err.position + ":" +
|
|
||||||
err.text
|
|
||||||
);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class App {
|
||||||
|
|
||||||
// Specify whether using the native module
|
// Specify whether using the native module
|
||||||
boolean setUseNative(boolean useNative) {
|
boolean setUseNative(boolean useNative) {
|
||||||
return this.useNative = useNative && Vue.isNativeLoaded();
|
return this.useNative = useNative && Vue.getNativeID() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ class ChildWindow extends JInternalFrame {
|
||||||
addInternalFrameListener(Util.onClose2(e->setVisible(false)));
|
addInternalFrameListener(Util.onClose2(e->setVisible(false)));
|
||||||
setClosable(true);
|
setClosable(true);
|
||||||
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
|
setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
|
||||||
|
setMaximizable(true);
|
||||||
setResizable(true);
|
setResizable(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -63,6 +63,7 @@ class DisassemblerPane extends JScrollPane {
|
||||||
Util.onFocus(e->client.repaint(), e->client.repaint()));
|
Util.onFocus(e->client.repaint(), e->client.repaint()));
|
||||||
client.addKeyListener(Util.onKey(e->onKeyDown(e), null));
|
client.addKeyListener(Util.onKey(e->onKeyDown(e), null));
|
||||||
client.addMouseListener(Util.onMouse(e->client.requestFocus(), null));
|
client.addMouseListener(Util.onMouse(e->client.requestFocus(), null));
|
||||||
|
client.addMouseWheelListener(e->onMouseWheel(e));
|
||||||
client.setBackground(SystemColor.window);
|
client.setBackground(SystemColor.window);
|
||||||
client.setFocusable(true);
|
client.setFocusable(true);
|
||||||
|
|
||||||
|
@ -98,6 +99,7 @@ class DisassemblerPane extends JScrollPane {
|
||||||
int code = e.getKeyCode();
|
int code = e.getKeyCode();
|
||||||
int count = tall(false);
|
int count = tall(false);
|
||||||
int mods = e.getModifiersEx();
|
int mods = e.getModifiersEx();
|
||||||
|
var vue = parent.parent.vue;
|
||||||
boolean alt = (mods & InputEvent.ALT_DOWN_MASK ) != 0;
|
boolean alt = (mods & InputEvent.ALT_DOWN_MASK ) != 0;
|
||||||
boolean ctrl = (mods & InputEvent.CTRL_DOWN_MASK) != 0;
|
boolean ctrl = (mods & InputEvent.CTRL_DOWN_MASK) != 0;
|
||||||
|
|
||||||
|
@ -116,16 +118,37 @@ class DisassemblerPane extends JScrollPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing by key code
|
// Processing by key code
|
||||||
|
int pc = vue.getRegister(Vue.PC, true);
|
||||||
|
var step = parent.parent.brkStep;
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case KeyEvent.VK_UP : seek(address, 1); break;
|
case KeyEvent.VK_UP : seek(address, 1); break;
|
||||||
case KeyEvent.VK_DOWN : seek(address, -1); break;
|
case KeyEvent.VK_DOWN : seek(address, -1); break;
|
||||||
case KeyEvent.VK_PAGE_UP : seek(address, count); break;
|
case KeyEvent.VK_PAGE_UP : seek(address, count); break;
|
||||||
case KeyEvent.VK_PAGE_DOWN: seek(address, -count); break;
|
case KeyEvent.VK_PAGE_DOWN: seek(address, -count); break;
|
||||||
|
|
||||||
|
// Single Step
|
||||||
case KeyEvent.VK_F11:
|
case KeyEvent.VK_F11:
|
||||||
parent.parent.vue.emulate(0);
|
step.setCondition("pc!=" + pc);
|
||||||
|
step.setEnabled(true);
|
||||||
|
vue.emulate(20000000);
|
||||||
|
if (step.evaluate())
|
||||||
|
step.setEnabled(false);
|
||||||
parent.parent.refreshDebug();
|
parent.parent.refreshDebug();
|
||||||
int pc = parent.parent.vue.getRegister(Vue.PC, true);
|
pc = vue.getRegister(Vue.PC, true);
|
||||||
|
if (!isVisible(pc))
|
||||||
|
seek(pc, count / 3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Run to Next
|
||||||
|
case KeyEvent.VK_F12:
|
||||||
|
step.setCondition("pc==" +
|
||||||
|
(pc + Instruction.size(vue.read(pc, Vue.U16) >> 10)));
|
||||||
|
step.setEnabled(true);
|
||||||
|
vue.emulate(20000000);
|
||||||
|
if (step.evaluate())
|
||||||
|
step.setEnabled(false);
|
||||||
|
parent.parent.refreshDebug();
|
||||||
|
pc = vue.getRegister(Vue.PC, true);
|
||||||
if (!isVisible(pc))
|
if (!isVisible(pc))
|
||||||
seek(pc, count / 3);
|
seek(pc, count / 3);
|
||||||
break;
|
break;
|
||||||
|
@ -133,6 +156,11 @@ class DisassemblerPane extends JScrollPane {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mouse wheel
|
||||||
|
private void onMouseWheel(MouseWheelEvent e) {
|
||||||
|
seek(address, -e.getUnitsToScroll());
|
||||||
|
}
|
||||||
|
|
||||||
// Client paint
|
// Client paint
|
||||||
private void onPaint(Graphics2D g, int width, int height) {
|
private void onPaint(Graphics2D g, int width, int height) {
|
||||||
var vue = parent.parent.vue;
|
var vue = parent.parent.vue;
|
||||||
|
|
|
@ -16,6 +16,7 @@ class MainWindow extends JFrame {
|
||||||
|
|
||||||
// Instance fields
|
// Instance fields
|
||||||
App app; // Containing application
|
App app; // Containing application
|
||||||
|
Breakpoint brkStep; // Single step internal breakpoint
|
||||||
Vue vue; // Emulation core context
|
Vue vue; // Emulation core context
|
||||||
|
|
||||||
// Private fields
|
// Private fields
|
||||||
|
@ -97,6 +98,10 @@ class MainWindow extends JFrame {
|
||||||
desktop.add(cpu = new CPUWindow (this));
|
desktop.add(cpu = new CPUWindow (this));
|
||||||
desktop.add(memory = new MemoryWindow (this));
|
desktop.add(memory = new MemoryWindow (this));
|
||||||
|
|
||||||
|
// Configure internal breakpoints
|
||||||
|
brkStep = vue.breakpoint();
|
||||||
|
brkStep.setExecute(true);
|
||||||
|
|
||||||
// Display window
|
// Display window
|
||||||
refreshDebug();
|
refreshDebug();
|
||||||
pack();
|
pack();
|
||||||
|
|
|
@ -63,20 +63,23 @@ public class Breakpoint {
|
||||||
|
|
||||||
// Functional symbol IDs
|
// Functional symbol IDs
|
||||||
private static final int ADDRESS = 0;
|
private static final int ADDRESS = 0;
|
||||||
private static final int CODE = 1;
|
private static final int BREAK = 1;
|
||||||
private static final int COND = 2;
|
private static final int CODE = 2;
|
||||||
private static final int DISP = 3;
|
private static final int COND = 3;
|
||||||
private static final int FORMAT = 4;
|
private static final int DISP = 4;
|
||||||
private static final int ID = 5;
|
private static final int FETCH = 5;
|
||||||
private static final int IMM = 6;
|
private static final int FORMAT = 6;
|
||||||
private static final int OPCODE = 7;
|
private static final int ID = 7;
|
||||||
private static final int REG1 = 8;
|
private static final int IMM = 8;
|
||||||
private static final int REG2 = 9;
|
private static final int OPCODE = 9;
|
||||||
private static final int REGID = 10;
|
private static final int REG1 = 10;
|
||||||
private static final int SIZE = 11;
|
private static final int REG2 = 11;
|
||||||
private static final int SUBOPCODE = 12;
|
private static final int REGID = 12;
|
||||||
private static final int VALUE = 13;
|
private static final int SIZE = 13;
|
||||||
private static final int VECTOR = 14;
|
private static final int SUBOPCODE = 14;
|
||||||
|
private static final int TYPE = 15;
|
||||||
|
private static final int VALUE = 16;
|
||||||
|
private static final int VECTOR = 17;
|
||||||
|
|
||||||
// Evaluation operator IDs
|
// Evaluation operator IDs
|
||||||
private static final int GROUP = 0; // ()
|
private static final int GROUP = 0; // ()
|
||||||
|
@ -190,6 +193,12 @@ public class Breakpoint {
|
||||||
SYMDEFS.put("false", new Def(BOOL, 0));
|
SYMDEFS.put("false", new Def(BOOL, 0));
|
||||||
SYMDEFS.put("true" , new Def(BOOL, 1));
|
SYMDEFS.put("true" , new Def(BOOL, 1));
|
||||||
|
|
||||||
|
// Break type symbl definitions
|
||||||
|
SYMDEFS.put("exception", new Def(SIGNED, EXCEPTION));
|
||||||
|
SYMDEFS.put("execute" , new Def(SIGNED, EXECUTE ));
|
||||||
|
SYMDEFS.put("read" , new Def(SIGNED, READ ));
|
||||||
|
SYMDEFS.put("write" , new Def(SIGNED, WRITE ));
|
||||||
|
|
||||||
// Condition code symbol definitions
|
// Condition code symbol definitions
|
||||||
SYMDEFS.put("c" , new Def(SIGNED, 1));
|
SYMDEFS.put("c" , new Def(SIGNED, 1));
|
||||||
SYMDEFS.put("e" , new Def(SIGNED, 2));
|
SYMDEFS.put("e" , new Def(SIGNED, 2));
|
||||||
|
@ -293,9 +302,11 @@ public class Breakpoint {
|
||||||
|
|
||||||
// Functional symbol definitions
|
// Functional symbol definitions
|
||||||
SYMDEFS.put("address" , new Def(SYMBOL, ADDRESS ));
|
SYMDEFS.put("address" , new Def(SYMBOL, ADDRESS ));
|
||||||
|
SYMDEFS.put("break" , new Def(SYMBOL, BREAK ));
|
||||||
SYMDEFS.put("code" , new Def(SYMBOL, CODE ));
|
SYMDEFS.put("code" , new Def(SYMBOL, CODE ));
|
||||||
SYMDEFS.put("cond" , new Def(SYMBOL, COND ));
|
SYMDEFS.put("cond" , new Def(SYMBOL, COND ));
|
||||||
SYMDEFS.put("disp" , new Def(SYMBOL, DISP ));
|
SYMDEFS.put("disp" , new Def(SYMBOL, DISP ));
|
||||||
|
SYMDEFS.put("fetch" , new Def(SYMBOL, FETCH ));
|
||||||
SYMDEFS.put("format" , new Def(SYMBOL, FORMAT ));
|
SYMDEFS.put("format" , new Def(SYMBOL, FORMAT ));
|
||||||
SYMDEFS.put("id" , new Def(SYMBOL, ID ));
|
SYMDEFS.put("id" , new Def(SYMBOL, ID ));
|
||||||
SYMDEFS.put("imm" , new Def(SYMBOL, IMM ));
|
SYMDEFS.put("imm" , new Def(SYMBOL, IMM ));
|
||||||
|
@ -305,6 +316,7 @@ public class Breakpoint {
|
||||||
SYMDEFS.put("regid" , new Def(SYMBOL, REGID ));
|
SYMDEFS.put("regid" , new Def(SYMBOL, REGID ));
|
||||||
SYMDEFS.put("size" , new Def(SYMBOL, SIZE ));
|
SYMDEFS.put("size" , new Def(SYMBOL, SIZE ));
|
||||||
SYMDEFS.put("subopcode", new Def(SYMBOL, SUBOPCODE));
|
SYMDEFS.put("subopcode", new Def(SYMBOL, SUBOPCODE));
|
||||||
|
SYMDEFS.put("type" , new Def(SYMBOL, TYPE ));
|
||||||
SYMDEFS.put("value" , new Def(SYMBOL, VALUE ));
|
SYMDEFS.put("value" , new Def(SYMBOL, VALUE ));
|
||||||
SYMDEFS.put("vector" , new Def(SYMBOL, VECTOR ));
|
SYMDEFS.put("vector" , new Def(SYMBOL, VECTOR ));
|
||||||
|
|
||||||
|
@ -454,9 +466,34 @@ public class Breakpoint {
|
||||||
|
|
||||||
// Evaluate the condition against the emulation context
|
// Evaluate the condition against the emulation context
|
||||||
public boolean evaluate() {
|
public boolean evaluate() {
|
||||||
return vue == null || conditionError.code != NONE ?
|
|
||||||
false : tokens.length == 0 ? true :
|
// Error checking
|
||||||
evaluate(null, null, null) != 0;
|
if (vue == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Retrieve state objects
|
||||||
|
Access acc = vue.getAccess ();
|
||||||
|
int breakType = vue.getBreakType ();
|
||||||
|
Instruction inst = vue.getInstruction();
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (!appliesTo(breakType))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check address ranges for execute
|
||||||
|
if (breakType == EXECUTE) {
|
||||||
|
int pc = vue.getRegister(Vue.PC, true);
|
||||||
|
if (!inRange(pc, pc + inst.size - 1))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check address ranges for read and write
|
||||||
|
else if (breakType == READ || breakType == WRITE &&
|
||||||
|
!inRange(acc.address, acc.address+JavaVue.TYPE_SIZES[acc.type]-1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Evaluate the condition
|
||||||
|
return isTrue(new int[depth], breakType, inst, acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the most recent address text
|
// Retrieve the most recent address text
|
||||||
|
@ -504,6 +541,15 @@ public class Breakpoint {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Determine whether the breakpoint can be used
|
||||||
|
boolean isActive() {
|
||||||
|
return
|
||||||
|
isEnabled &&
|
||||||
|
addressError .code == NONE &&
|
||||||
|
conditionError.code == NONE
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine whether the breakpoint is enabled
|
// Determine whether the breakpoint is enabled
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return isEnabled;
|
return isEnabled;
|
||||||
|
@ -738,12 +784,16 @@ public class Breakpoint {
|
||||||
// Package Methods //
|
// Package Methods //
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Cast a value to the condition evaluation's data type
|
// Determine whether the breakpoint applies to a break scenario
|
||||||
Object cast(int value) {
|
boolean appliesTo(int breakType) {
|
||||||
if (conditionError.code != NONE)
|
return isActive() && (breakType == 0 || (breakType & hooks) != 0);
|
||||||
return conditionError;
|
}
|
||||||
|
|
||||||
|
// Perform a typed evaluation of the condition expression
|
||||||
|
Object evaluateTyped(int[]stack,int breakType,Instruction inst,Access acc){
|
||||||
if (tokens.length == 0)
|
if (tokens.length == 0)
|
||||||
return null;
|
return null;
|
||||||
|
int value = evaluate(stack, breakType, inst, acc);
|
||||||
switch (dataType) {
|
switch (dataType) {
|
||||||
case BOOL : return (Boolean) (value != 0);
|
case BOOL : return (Boolean) (value != 0);
|
||||||
case FLOAT : return (Float ) Float.intBitsToFloat(value);
|
case FLOAT : return (Float ) Float.intBitsToFloat(value);
|
||||||
|
@ -752,54 +802,28 @@ public class Breakpoint {
|
||||||
return (Integer) value;
|
return (Integer) value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the condition, returning only the computed value
|
// Determine whether the breakpoint applies to a given address range
|
||||||
int evaluate(int[] stack, Instruction inst, Access acc) {
|
boolean inRange(int start, int end) {
|
||||||
|
|
||||||
// Error checking
|
// Implicit inclusion
|
||||||
if (conditionError.code != NONE)
|
if (ranges.length == 0)
|
||||||
return 0;
|
return true;
|
||||||
if (tokens.length == 0)
|
|
||||||
return 1;
|
|
||||||
if (stack == null)
|
|
||||||
stack = new int[depth];
|
|
||||||
|
|
||||||
// Process all tokens
|
// Check all ranges
|
||||||
int size = 0;
|
for (var range : ranges)
|
||||||
for (var tok : tokens) switch (tok.type) {
|
if (
|
||||||
|
Integer.compareUnsigned(
|
||||||
// Binary operator
|
start - range[0], range[1] - range[0]) <= 0 ||
|
||||||
case BINARY:
|
Integer.compareUnsigned(
|
||||||
stack[size - 2] =
|
range[0] - start , end - start ) <= 0
|
||||||
evalBinary(tok.id, stack[size - 2], stack[size - 1]);
|
) return true;
|
||||||
break;
|
return false;
|
||||||
|
|
||||||
// Literal
|
|
||||||
case BOOL:
|
|
||||||
case FLOAT:
|
|
||||||
case SIGNED:
|
|
||||||
case UNSIGNED:
|
|
||||||
stack[size++] = tok.id;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// CPU register
|
|
||||||
case PROREG:
|
|
||||||
case SYSREG:
|
|
||||||
stack[size++] = vue.getRegister(tok.id, tok.type == SYSREG);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Symbol
|
|
||||||
case SYMBOL:
|
|
||||||
stack[size++] = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Unary operator
|
|
||||||
case UNARY:
|
|
||||||
stack[size - 1] = evalUnary(tok.id, stack[size - 1]);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the remaining stack value
|
// Determine whether the breakpoint's condition is truthy
|
||||||
return stack[0];
|
boolean isTrue(int[] stack, int breakType, Instruction inst, Access acc) {
|
||||||
|
return tokens.length == 0 ? true :
|
||||||
|
evaluate(stack, breakType, inst, acc) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Produce a one-dimensional array from the address ranges
|
// Produce a one-dimensional array from the address ranges
|
||||||
|
@ -829,21 +853,6 @@ public class Breakpoint {
|
||||||
return depth;
|
return depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the bit mask for enabled hooks
|
|
||||||
int getHooks() {
|
|
||||||
return hooks;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the list of address ranges
|
|
||||||
int[][] getRanges() {
|
|
||||||
return ranges;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve the list of condition tokens
|
|
||||||
Token[] getTokens() {
|
|
||||||
return tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The breakpoint is being removed from its emulation context
|
// The breakpoint is being removed from its emulation context
|
||||||
void remove() {
|
void remove() {
|
||||||
vue = null;
|
vue = null;
|
||||||
|
@ -1216,9 +1225,10 @@ public class Breakpoint {
|
||||||
|
|
||||||
// Binary operators
|
// Binary operators
|
||||||
if (tok.type == BINARY) {
|
if (tok.type == BINARY) {
|
||||||
|
int dataType = Math.max(tok.left.dataType, tok.right.dataType);
|
||||||
int id = tok.id;
|
int id = tok.id;
|
||||||
boolean literal = isLiteral(tok.left) && isLiteral(tok.right);
|
boolean literal = isLiteral(tok.left) && isLiteral(tok.right);
|
||||||
tok.dataType = Math.max(tok.left.dataType, tok.right.dataType);
|
tok.dataType = dataType;
|
||||||
tok.id = -id * 4 + tok.dataType;
|
tok.id = -id * 4 + tok.dataType;
|
||||||
|
|
||||||
// Process by ID
|
// Process by ID
|
||||||
|
@ -1269,7 +1279,7 @@ public class Breakpoint {
|
||||||
var top = op == 0 ? tok.left : tok.right;
|
var top = op == 0 ? tok.left : tok.right;
|
||||||
|
|
||||||
// The operand is already the result type
|
// The operand is already the result type
|
||||||
if (top.dataType == tok.dataType)
|
if (top.dataType == dataType)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Select the ID of the conversion operation
|
// Select the ID of the conversion operation
|
||||||
|
@ -1284,7 +1294,7 @@ public class Breakpoint {
|
||||||
// Convert the literal operand directly
|
// Convert the literal operand directly
|
||||||
if (isLiteral(top)) {
|
if (isLiteral(top)) {
|
||||||
top.id = evalUnary(cvt, top);
|
top.id = evalUnary(cvt, top);
|
||||||
top.type = top.dataType = tok.dataType;
|
top.type = top.dataType = dataType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert a conversion token
|
// Insert a conversion token
|
||||||
|
@ -1296,7 +1306,7 @@ public class Breakpoint {
|
||||||
case SIGNED : imp.text = "s32" ; break;
|
case SIGNED : imp.text = "s32" ; break;
|
||||||
case UNSIGNED: imp.text = "u32" ; break;
|
case UNSIGNED: imp.text = "u32" ; break;
|
||||||
}
|
}
|
||||||
imp.dataType = tok.dataType;
|
imp.dataType = dataType;
|
||||||
imp.id = -cvt * 4 + top.dataType;
|
imp.id = -cvt * 4 + top.dataType;
|
||||||
imp.parent = tok;
|
imp.parent = tok;
|
||||||
imp.right = top;
|
imp.right = top;
|
||||||
|
@ -1444,202 +1454,71 @@ public class Breakpoint {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private Methods //
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Adjust a float value as needed
|
|
||||||
private static float adjust(float value) {
|
|
||||||
int bits = Float.floatToRawIntBits(value);
|
|
||||||
int exp = bits & 0x7F800000;
|
|
||||||
return
|
|
||||||
(bits & 0x7FFFFFFF) == 0 || // Zero
|
|
||||||
exp == 0x7F800000 || // Indefinite
|
|
||||||
exp == 0 // Denormal
|
|
||||||
? 0 : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the required stack size to evaluate the expression
|
|
||||||
private int depth() {
|
|
||||||
|
|
||||||
// Error checking
|
|
||||||
if (conditionError.code != NONE)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// Count the maximum size of the stack
|
|
||||||
int max = 0;
|
|
||||||
int size = 0;
|
|
||||||
for (var tok : tokens) switch (tok.type) {
|
|
||||||
case BINARY: size--; break;
|
|
||||||
case UNARY : break;
|
|
||||||
default : max = Math.max(max, ++size);
|
|
||||||
}
|
|
||||||
return max;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the fraction part of a float
|
|
||||||
private static float trunc(float x) {
|
|
||||||
return (float) (x < 0 ? Math.ceil(x) : Math.floor(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Debugging Methods //
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Produce a string representation of the condition token list
|
|
||||||
public String debugTokens() {
|
|
||||||
var ret = new StringBuilder();
|
|
||||||
|
|
||||||
// The condition was not successfully parsed
|
|
||||||
if (conditionError.code != NONE)
|
|
||||||
return "Error";
|
|
||||||
|
|
||||||
// Determine the maximum width of the text fields
|
|
||||||
int max = 0;
|
|
||||||
for (var tok : tokens)
|
|
||||||
max = Math.max(max, tok.text.length());
|
|
||||||
|
|
||||||
// Output all tokens
|
|
||||||
var last = tokens[tokens.length - 1];
|
|
||||||
for (var tok : tokens) {
|
|
||||||
|
|
||||||
// Text
|
|
||||||
ret.append(String.format("%-" + max + "s ", tok.text));
|
|
||||||
|
|
||||||
// Type
|
|
||||||
String type = null;
|
|
||||||
switch (tok.type) {
|
|
||||||
case BINARY : type = "Binary" ; break;
|
|
||||||
case BOOL : type = "Bool" ; break;
|
|
||||||
case FLOAT : type = "Float" ; break;
|
|
||||||
case PROREG : type = "ProReg" ; break;
|
|
||||||
case SYSREG : type = "SysReg" ; break;
|
|
||||||
case SIGNED : type = "Signed" ; break;
|
|
||||||
case SYMBOL : type = "Symbol" ; break;
|
|
||||||
case UNARY : type = "Unary" ; break;
|
|
||||||
case UNSIGNED: type = "Unsigned"; break;
|
|
||||||
default: type = Integer.toString(tok.type);
|
|
||||||
}
|
|
||||||
ret.append(String.format("%-8s ", type));
|
|
||||||
|
|
||||||
// Data type
|
|
||||||
switch (tok.dataType) {
|
|
||||||
case BOOL : type = "Bool" ; break;
|
|
||||||
case FLOAT : type = "Float" ; break;
|
|
||||||
case SIGNED : type = "Signed" ; break;
|
|
||||||
case UNSIGNED: type = "Unsigned"; break;
|
|
||||||
default: type = Integer.toString(tok.dataType);
|
|
||||||
}
|
|
||||||
ret.append(String.format("%-8s ", type));
|
|
||||||
|
|
||||||
// Operator
|
|
||||||
if (tok.type == BINARY || tok.type == UNARY) {
|
|
||||||
String id = Integer.toString(tok.id);
|
|
||||||
switch (-tok.id / 4) {
|
|
||||||
case 0 : id = "REINTERPRET"; break;
|
|
||||||
case READ8 : id = "READ8" ; break;
|
|
||||||
case READ16 : id = "READ16" ; break;
|
|
||||||
case READ32 : id = "READ32" ; break;
|
|
||||||
case ADD : id = "ADD" ; break;
|
|
||||||
case AND_B : id = "AND_B" ; break;
|
|
||||||
case AND_L : id = "AND_L" ; break;
|
|
||||||
case BOOL_ : id = "BOOL_" ; break;
|
|
||||||
case CEIL : id = "CEIL" ; break;
|
|
||||||
case DIVIDE : id = "DIVIDE" ; break;
|
|
||||||
case EQUAL : id = "EQUAL" ; break;
|
|
||||||
case FLOAT_ : id = "FLOAT_" ; break;
|
|
||||||
case FLOOR : id = "FLOOR" ; break;
|
|
||||||
case GREATER : id = "GREATER" ; break;
|
|
||||||
case GREQUAL : id = "GREQUAL" ; break;
|
|
||||||
case LEFT_L : id = "LEFT_L" ; break;
|
|
||||||
case LEQUAL : id = "LEQUAL" ; break;
|
|
||||||
case LESS : id = "LESS" ; break;
|
|
||||||
case MULTIPLY : id = "MULTIPLY" ; break;
|
|
||||||
case NEQUAL : id = "NEQUAL" ; break;
|
|
||||||
case NOT_B : id = "NOT_B" ; break;
|
|
||||||
case NOT_L : id = "NOT_L" ; break;
|
|
||||||
case NEGATE : id = "NEGATE" ; break;
|
|
||||||
case OR_B : id = "OR_B" ; break;
|
|
||||||
case OR_L : id = "OR_L" ; break;
|
|
||||||
case REMAINDER: id = "REMAINDER" ; break;
|
|
||||||
case RIGHT_A : id = "RIGHT_A" ; break;
|
|
||||||
case RIGHT_L : id = "RIGHT_L" ; break;
|
|
||||||
case ROUND : id = "ROUND" ; break;
|
|
||||||
case S8 : id = "S8" ; break;
|
|
||||||
case S16 : id = "S16" ; break;
|
|
||||||
case S32 : id = "S32" ; break;
|
|
||||||
case SUBTRACT : id = "SUBTRACT" ; break;
|
|
||||||
case TRUNC : id = "TRUNC" ; break;
|
|
||||||
case U8 : id = "U8" ; break;
|
|
||||||
case U16 : id = "U16" ; break;
|
|
||||||
case U32 : id = "U32" ; break;
|
|
||||||
case XFLOAT : id = "XFLOAT" ; break;
|
|
||||||
case XOR_B : id = "XOR_B" ; break;
|
|
||||||
case XOR_L : id = "XOR_L" ; break;
|
|
||||||
case XS32 : id = "XS32" ; break;
|
|
||||||
case XU32 : id = "XU32" ; break;
|
|
||||||
default: ret.append(Integer.toString(tok.id));
|
|
||||||
}
|
|
||||||
ret.append(String.format("%-11s", id));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Symbol or literal
|
|
||||||
else switch (tok.type) {
|
|
||||||
case PROREG:
|
|
||||||
case SYSREG:
|
|
||||||
case SYMBOL:
|
|
||||||
ret.append(Integer.toString(tok.id));
|
|
||||||
break;
|
|
||||||
case FLOAT:
|
|
||||||
ret.append(String.format("%.6f",
|
|
||||||
Float.intBitsToFloat(tok.id)));
|
|
||||||
break;
|
|
||||||
case BOOL:
|
|
||||||
case SIGNED:
|
|
||||||
ret.append(Integer.toString(tok.id));
|
|
||||||
break;
|
|
||||||
case UNSIGNED:
|
|
||||||
ret.append(Long.toString(tok.id & 0xFFFFFFFFL));
|
|
||||||
break;
|
|
||||||
default: ret.append("Error");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance to the next line
|
|
||||||
if (tok != last)
|
|
||||||
ret.append("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Produce a string representation of the internal address ranges
|
|
||||||
public String debugRanges() {
|
|
||||||
var ret = new StringBuilder();
|
|
||||||
|
|
||||||
// The address ranges were not successfully parsed
|
|
||||||
if (addressError.code != NONE)
|
|
||||||
return "Error";
|
|
||||||
|
|
||||||
// Process ranges
|
|
||||||
for (int x = 0; x < ranges.length; x++) {
|
|
||||||
var range = ranges[x];
|
|
||||||
if (x > 0)
|
|
||||||
ret.append("\n");
|
|
||||||
ret.append(String.format("%08X", range[0]));
|
|
||||||
if (range[0] != range[1])
|
|
||||||
ret.append(String.format("-%08X", range[1]));
|
|
||||||
}
|
|
||||||
return ret.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Evaluation Methods //
|
// Evaluation Methods //
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Evaluate the condition, returning only the computed value
|
||||||
|
private int evaluate(int[]stack,int breakType,Instruction inst,Access acc){
|
||||||
|
|
||||||
|
// Process all tokens
|
||||||
|
int size = 0;
|
||||||
|
for (var tok : tokens) switch (tok.type) {
|
||||||
|
|
||||||
|
// Binary operator
|
||||||
|
case BINARY:
|
||||||
|
stack[size - 2] =
|
||||||
|
evalBinary(tok.id, stack[size - 2], stack[size - 1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Literal
|
||||||
|
case BOOL:
|
||||||
|
case FLOAT:
|
||||||
|
case SIGNED:
|
||||||
|
case UNSIGNED:
|
||||||
|
stack[size++] = tok.id;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// CPU register
|
||||||
|
case PROREG:
|
||||||
|
case SYSREG:
|
||||||
|
stack[size++] = vue.getRegister(tok.id, tok.type == SYSREG);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Unary operator
|
||||||
|
case UNARY:
|
||||||
|
stack[size - 1] = evalUnary(tok.id, stack[size - 1]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Symbol
|
||||||
|
case SYMBOL:
|
||||||
|
int sym = inst.imm; // IMM, REGID, VECTOR
|
||||||
|
switch (tok.id) {
|
||||||
|
case ADDRESS : sym = acc.address ; break;
|
||||||
|
case BREAK : sym = breakType ; break;
|
||||||
|
case CODE : sym = vue.getExceptionCode(); break;
|
||||||
|
case COND : sym = inst.cond ; break;
|
||||||
|
case DISP : sym = inst.disp ; break;
|
||||||
|
case FETCH : sym = vue.getFetch (); break;
|
||||||
|
case FORMAT : sym = inst.format ; break;
|
||||||
|
case ID : sym = inst.id ; break;
|
||||||
|
case OPCODE : sym = inst.opcode ; break;
|
||||||
|
case REG1 : sym = inst.reg1 ; break;
|
||||||
|
case REG2 : sym = inst.reg2 ; break;
|
||||||
|
case SIZE : sym = inst.size ; break;
|
||||||
|
case SUBOPCODE: sym = inst.subopcode ; break;
|
||||||
|
case TYPE : sym = acc.type ; break;
|
||||||
|
case VALUE : sym = acc.value ; break;
|
||||||
|
}
|
||||||
|
stack[size++] = sym;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the remaining stack value
|
||||||
|
return stack[0];
|
||||||
|
}
|
||||||
|
|
||||||
// Evaluate a unary operator given a token
|
// Evaluate a unary operator given a token
|
||||||
private int evalUnary(int id, Token tok) {
|
private int evalUnary(int id, Token tok) {
|
||||||
return evalUnary(-id * 4 + tok.dataType, tok.id);
|
return evalUnary(-id * 4 + tok.dataType, tok.id);
|
||||||
|
@ -1988,4 +1867,196 @@ public class Breakpoint {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Private Methods //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Adjust a float value as needed
|
||||||
|
private static float adjust(float value) {
|
||||||
|
int bits = Float.floatToRawIntBits(value);
|
||||||
|
int exp = bits & 0x7F800000;
|
||||||
|
return
|
||||||
|
(bits & 0x7FFFFFFF) == 0 || // Zero
|
||||||
|
exp == 0x7F800000 || // Indefinite
|
||||||
|
exp == 0 // Denormal
|
||||||
|
? 0 : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine the required stack size to evaluate the expression
|
||||||
|
private int depth() {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (conditionError.code != NONE)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Count the maximum size of the stack
|
||||||
|
int max = 0;
|
||||||
|
int size = 0;
|
||||||
|
for (var tok : tokens) switch (tok.type) {
|
||||||
|
case BINARY: size--; break;
|
||||||
|
case UNARY : break;
|
||||||
|
default : max = Math.max(max, ++size);
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the fraction part of a float
|
||||||
|
private static float trunc(float x) {
|
||||||
|
return (float) (x < 0 ? Math.ceil(x) : Math.floor(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Debugging Methods //
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Produce a string representation of the condition token list
|
||||||
|
public String debugTokens() {
|
||||||
|
var ret = new StringBuilder();
|
||||||
|
|
||||||
|
// The condition was not successfully parsed
|
||||||
|
if (conditionError.code != NONE)
|
||||||
|
return "Error";
|
||||||
|
|
||||||
|
// Determine the maximum width of the text fields
|
||||||
|
int max = 0;
|
||||||
|
for (var tok : tokens)
|
||||||
|
max = Math.max(max, tok.text.length());
|
||||||
|
|
||||||
|
// Output all tokens
|
||||||
|
var last = tokens[tokens.length - 1];
|
||||||
|
for (var tok : tokens) {
|
||||||
|
|
||||||
|
// Text
|
||||||
|
ret.append(String.format("%-" + max + "s ", tok.text));
|
||||||
|
|
||||||
|
// Type
|
||||||
|
String type = null;
|
||||||
|
switch (tok.type) {
|
||||||
|
case BINARY : type = "Binary" ; break;
|
||||||
|
case BOOL : type = "Bool" ; break;
|
||||||
|
case FLOAT : type = "Float" ; break;
|
||||||
|
case PROREG : type = "ProReg" ; break;
|
||||||
|
case SYSREG : type = "SysReg" ; break;
|
||||||
|
case SIGNED : type = "Signed" ; break;
|
||||||
|
case SYMBOL : type = "Symbol" ; break;
|
||||||
|
case UNARY : type = "Unary" ; break;
|
||||||
|
case UNSIGNED: type = "Unsigned"; break;
|
||||||
|
default: type = Integer.toString(tok.type);
|
||||||
|
}
|
||||||
|
ret.append(String.format("%-8s ", type));
|
||||||
|
|
||||||
|
// Data type
|
||||||
|
switch (tok.dataType) {
|
||||||
|
case BOOL : type = "Bool" ; break;
|
||||||
|
case FLOAT : type = "Float" ; break;
|
||||||
|
case SIGNED : type = "Signed" ; break;
|
||||||
|
case UNSIGNED: type = "Unsigned"; break;
|
||||||
|
default: type = Integer.toString(tok.dataType);
|
||||||
|
}
|
||||||
|
ret.append(String.format("%-8s ", type));
|
||||||
|
|
||||||
|
// Operator
|
||||||
|
if (tok.type == BINARY || tok.type == UNARY) {
|
||||||
|
String id = Integer.toString(tok.id);
|
||||||
|
switch (-tok.id / 4) {
|
||||||
|
case 0 : id = "REINTERPRET"; break;
|
||||||
|
case READ8 : id = "READ8" ; break;
|
||||||
|
case READ16 : id = "READ16" ; break;
|
||||||
|
case READ32 : id = "READ32" ; break;
|
||||||
|
case ADD : id = "ADD" ; break;
|
||||||
|
case AND_B : id = "AND_B" ; break;
|
||||||
|
case AND_L : id = "AND_L" ; break;
|
||||||
|
case BOOL_ : id = "BOOL_" ; break;
|
||||||
|
case CEIL : id = "CEIL" ; break;
|
||||||
|
case DIVIDE : id = "DIVIDE" ; break;
|
||||||
|
case EQUAL : id = "EQUAL" ; break;
|
||||||
|
case FLOAT_ : id = "FLOAT_" ; break;
|
||||||
|
case FLOOR : id = "FLOOR" ; break;
|
||||||
|
case GREATER : id = "GREATER" ; break;
|
||||||
|
case GREQUAL : id = "GREQUAL" ; break;
|
||||||
|
case LEFT_L : id = "LEFT_L" ; break;
|
||||||
|
case LEQUAL : id = "LEQUAL" ; break;
|
||||||
|
case LESS : id = "LESS" ; break;
|
||||||
|
case MULTIPLY : id = "MULTIPLY" ; break;
|
||||||
|
case NEQUAL : id = "NEQUAL" ; break;
|
||||||
|
case NOT_B : id = "NOT_B" ; break;
|
||||||
|
case NOT_L : id = "NOT_L" ; break;
|
||||||
|
case NEGATE : id = "NEGATE" ; break;
|
||||||
|
case OR_B : id = "OR_B" ; break;
|
||||||
|
case OR_L : id = "OR_L" ; break;
|
||||||
|
case REMAINDER: id = "REMAINDER" ; break;
|
||||||
|
case RIGHT_A : id = "RIGHT_A" ; break;
|
||||||
|
case RIGHT_L : id = "RIGHT_L" ; break;
|
||||||
|
case ROUND : id = "ROUND" ; break;
|
||||||
|
case S8 : id = "S8" ; break;
|
||||||
|
case S16 : id = "S16" ; break;
|
||||||
|
case S32 : id = "S32" ; break;
|
||||||
|
case SUBTRACT : id = "SUBTRACT" ; break;
|
||||||
|
case TRUNC : id = "TRUNC" ; break;
|
||||||
|
case U8 : id = "U8" ; break;
|
||||||
|
case U16 : id = "U16" ; break;
|
||||||
|
case U32 : id = "U32" ; break;
|
||||||
|
case XFLOAT : id = "XFLOAT" ; break;
|
||||||
|
case XOR_B : id = "XOR_B" ; break;
|
||||||
|
case XOR_L : id = "XOR_L" ; break;
|
||||||
|
case XS32 : id = "XS32" ; break;
|
||||||
|
case XU32 : id = "XU32" ; break;
|
||||||
|
default: ret.append(Integer.toString(tok.id));
|
||||||
|
}
|
||||||
|
ret.append(String.format("%-11s", id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Symbol or literal
|
||||||
|
else switch (tok.type) {
|
||||||
|
case PROREG:
|
||||||
|
case SYSREG:
|
||||||
|
case SYMBOL:
|
||||||
|
ret.append(Integer.toString(tok.id));
|
||||||
|
break;
|
||||||
|
case FLOAT:
|
||||||
|
ret.append(String.format("%.6f",
|
||||||
|
Float.intBitsToFloat(tok.id)));
|
||||||
|
break;
|
||||||
|
case BOOL:
|
||||||
|
case SIGNED:
|
||||||
|
ret.append(Integer.toString(tok.id));
|
||||||
|
break;
|
||||||
|
case UNSIGNED:
|
||||||
|
ret.append(Long.toString(tok.id & 0xFFFFFFFFL));
|
||||||
|
break;
|
||||||
|
default: ret.append("Error");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Advance to the next line
|
||||||
|
if (tok != last)
|
||||||
|
ret.append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Produce a string representation of the internal address ranges
|
||||||
|
public String debugRanges() {
|
||||||
|
var ret = new StringBuilder();
|
||||||
|
|
||||||
|
// The address ranges were not successfully parsed
|
||||||
|
if (addressError.code != NONE)
|
||||||
|
return "Error";
|
||||||
|
|
||||||
|
// Process ranges
|
||||||
|
for (int x = 0; x < ranges.length; x++) {
|
||||||
|
var range = ranges[x];
|
||||||
|
if (x > 0)
|
||||||
|
ret.append("\n");
|
||||||
|
ret.append(String.format("%08X", range[0]));
|
||||||
|
if (range[0] != range[1])
|
||||||
|
ret.append(String.format("-%08X", range[1]));
|
||||||
|
}
|
||||||
|
return ret.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -12,7 +12,7 @@ class CPU {
|
||||||
// Package fields
|
// Package fields
|
||||||
Access access; // Access state
|
Access access; // Access state
|
||||||
int cycles; // Cycles until next stage
|
int cycles; // Cycles until next stage
|
||||||
Ecxeption exception; // Exception state
|
int exception; // Exception code
|
||||||
int fetch; // Fetch unit index
|
int fetch; // Fetch unit index
|
||||||
Instruction inst; // Instruction state
|
Instruction inst; // Instruction state
|
||||||
int irq; // Interrupt lines
|
int irq; // Interrupt lines
|
||||||
|
@ -97,7 +97,6 @@ class CPU {
|
||||||
// Default constructor
|
// Default constructor
|
||||||
CPU(JavaVue vue) {
|
CPU(JavaVue vue) {
|
||||||
access = new Access();
|
access = new Access();
|
||||||
exception = new Ecxeption();
|
|
||||||
inst = new Instruction();
|
inst = new Instruction();
|
||||||
jumpFrom = new int[3];
|
jumpFrom = new int[3];
|
||||||
jumpTo = new int[3];
|
jumpTo = new int[3];
|
||||||
|
@ -114,28 +113,27 @@ class CPU {
|
||||||
// Process the simulation
|
// Process the simulation
|
||||||
void emulate(int cycles) {
|
void emulate(int cycles) {
|
||||||
|
|
||||||
// The CPU is halting
|
// The CPU is in fatal halt status
|
||||||
if (stage == HALT || stage == FATAL)
|
if (stage == FATAL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.cycles = 0; // DEBUG: Stop processing after execute
|
|
||||||
|
|
||||||
// Process for the given number of cycles
|
// Process for the given number of cycles
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
||||||
// The next event occurs after the given number of cycles
|
// The next stage occurs after the given number of cycles
|
||||||
if (this.cycles > cycles) {
|
if (this.cycles > cycles) {
|
||||||
this.cycles -= cycles;
|
this.cycles -= cycles;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing by stage
|
// Processing by stage
|
||||||
|
cycles -= this.cycles;
|
||||||
this.cycles = 0;
|
this.cycles = 0;
|
||||||
switch (stage) {
|
switch (stage) {
|
||||||
case EXCEPTION: if (exception()) return; break;
|
case EXCEPTION: if (exception()) return; break;
|
||||||
case EXECUTE : if (execute ()) return; break;
|
case EXECUTE : if (execute ()) return; break;
|
||||||
case FETCH : if (fetch ()) return; break;
|
case FETCH : if (fetch ()) return; break;
|
||||||
case HALT : if (testException()) return; break;
|
case HALT : testException(); return;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -178,8 +176,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
|
|
||||||
// Configure instance fields
|
// Configure instance fields
|
||||||
cycles = 0;
|
cycles = 0;
|
||||||
exception.code = 0;
|
exception = 0;
|
||||||
fetch = 0;
|
fetch = -1;
|
||||||
irq = 0;
|
irq = 0;
|
||||||
stage = FETCH;
|
stage = FETCH;
|
||||||
|
|
||||||
|
@ -257,11 +255,10 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
return 1; // Unreachable
|
return 1; // Unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the number of CPU cycles until something can happen
|
// Determine the number of CPU cycles until a breakpoint could trigger
|
||||||
int until(int cycles) {
|
int until(int cycles) {
|
||||||
if (stage == FATAL || stage == HALT)
|
return stage == FATAL || stage == HALT ?
|
||||||
return cycles;
|
cycles : Math.min(cycles, this.cycles);
|
||||||
return cycles < 0 ? this.cycles : Math.min(cycles, this.cycles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -273,19 +270,18 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
// Operations for exception stage
|
// Operations for exception stage
|
||||||
private boolean exception() {
|
private boolean exception() {
|
||||||
|
|
||||||
// Application callback
|
// Check for breakpoints
|
||||||
vue.breakCode = vue.onException() ? 1 : 0;
|
if (vue.onBreakpoint(Breakpoint.EXCEPTION))
|
||||||
if (vue.breakCode != 0)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Configure working variables
|
// Configure working variables
|
||||||
exception.code &= 0xFFFF;
|
exception &= 0xFFFF;
|
||||||
boolean isIRQ = (exception.code & 0xFF00) == 0xFE00;
|
boolean isIRQ = (exception & 0xFF00) == 0xFE00;
|
||||||
int psw = getSystemRegister(Vue.PSW);
|
int psw = getSystemRegister(Vue.PSW);
|
||||||
|
|
||||||
// Fatal exception
|
// Fatal exception
|
||||||
if (psw_np != 0) {
|
if (psw_np != 0) {
|
||||||
vue.write(0x00000000, Vue.S32, 0xFFFF0000 | exception.code);
|
vue.write(0x00000000, Vue.S32, 0xFFFF0000 | exception);
|
||||||
vue.write(0x00000004, Vue.S32, psw);
|
vue.write(0x00000004, Vue.S32, psw);
|
||||||
vue.write(0x00000008, Vue.S32, pc);
|
vue.write(0x00000008, Vue.S32, pc);
|
||||||
stage = FATAL;
|
stage = FATAL;
|
||||||
|
@ -294,7 +290,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
|
|
||||||
// Duplexed exception
|
// Duplexed exception
|
||||||
if (psw_ep != 0) {
|
if (psw_ep != 0) {
|
||||||
ecr_fecc = exception.code;
|
ecr_fecc = exception;
|
||||||
fepc = pc;
|
fepc = pc;
|
||||||
fepsw = psw;
|
fepsw = psw;
|
||||||
psw_np = 1;
|
psw_np = 1;
|
||||||
|
@ -303,25 +299,25 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
|
|
||||||
// Regular exception
|
// Regular exception
|
||||||
else {
|
else {
|
||||||
ecr_eicc = exception.code;
|
ecr_eicc = exception;
|
||||||
eipc = pc;
|
eipc = pc;
|
||||||
eipsw = psw;
|
eipsw = psw;
|
||||||
psw_ep = 1;
|
psw_ep = 1;
|
||||||
pc = 0xFFFF0000 | exception.code & 0xFFF0;
|
pc = 0xFFFF0000 | exception & 0xFFF0;
|
||||||
if (pc == 0xFFFFFF70) // FIV
|
if (pc == 0xFFFFFF70) // FIV
|
||||||
pc = 0xFFFFFF60;
|
pc = 0xFFFFFF60;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interrupt
|
// Interrupt
|
||||||
if (isIRQ) {
|
if (isIRQ) {
|
||||||
psw_i = Math.min(15, exception.code >> 4 & 15);
|
psw_i = Math.min(15, exception >> 4 & 15);
|
||||||
if (stage == HALT)
|
if (stage == HALT)
|
||||||
pc += 2;
|
pc += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common processing
|
// Common processing
|
||||||
cycles = 0; // TODO: Determine the actual number
|
cycles = 0; // TODO: Determine the actual number
|
||||||
exception.code = 0;
|
exception = 0;
|
||||||
psw_ae = 0;
|
psw_ae = 0;
|
||||||
psw_id = 1;
|
psw_id = 1;
|
||||||
stage = FETCH;
|
stage = FETCH;
|
||||||
|
@ -331,9 +327,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
// Operations for execute stage
|
// Operations for execute stage
|
||||||
private boolean execute() {
|
private boolean execute() {
|
||||||
|
|
||||||
// Application callback
|
// Check for breakpoints
|
||||||
vue.breakCode = vue.onExecute() ? 1 : 0;
|
if (vue.onBreakpoint(Breakpoint.EXECUTE))
|
||||||
if (vue.breakCode != 0)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Processing by instruction ID
|
// Processing by instruction ID
|
||||||
|
@ -415,7 +410,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
case Vue.XORI : XORI (); break;
|
case Vue.XORI : XORI (); break;
|
||||||
//case Vue.XORNBSU: XORNBSU(); break;
|
//case Vue.XORNBSU: XORNBSU(); break;
|
||||||
default: // Invalid instruction
|
default: // Invalid instruction
|
||||||
exception.code = 0xFF90;
|
exception = 0xFF90;
|
||||||
}
|
}
|
||||||
|
|
||||||
// An application break was requested
|
// An application break was requested
|
||||||
|
@ -423,7 +418,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Common processing
|
// Common processing
|
||||||
if (exception.code == 0) {
|
if (exception == 0) {
|
||||||
cycles += CYCLES[inst.id];
|
cycles += CYCLES[inst.id];
|
||||||
pc += inst.size;
|
pc += inst.size;
|
||||||
}
|
}
|
||||||
|
@ -437,9 +432,14 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
// Operations for fetch stage
|
// Operations for fetch stage
|
||||||
private boolean fetch() {
|
private boolean fetch() {
|
||||||
|
|
||||||
|
// Entering the fetch stage
|
||||||
|
if (fetch == -1)
|
||||||
|
fetch = 0;
|
||||||
|
|
||||||
// Read the bits from the bus
|
// Read the bits from the bus
|
||||||
if (read(pc + (fetch << 1), Vue.U16, fetch))
|
if (read(pc + (fetch << 1), Vue.U16, fetch))
|
||||||
return true;
|
return true;
|
||||||
|
// TODO: Determine how many cycles this takes
|
||||||
|
|
||||||
// First unit
|
// First unit
|
||||||
if (fetch == 0) {
|
if (fetch == 0) {
|
||||||
|
@ -453,7 +453,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
// Second unit
|
// Second unit
|
||||||
else {
|
else {
|
||||||
inst.bits |= access.value & 0xFFFF;
|
inst.bits |= access.value & 0xFFFF;
|
||||||
fetch = 0;
|
fetch = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode the instruction and advance to execute stage
|
// Decode the instruction and advance to execute stage
|
||||||
|
@ -471,9 +471,8 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
access.type = type;
|
access.type = type;
|
||||||
access.value = vue.read(address, type);
|
access.value = vue.read(address, type);
|
||||||
|
|
||||||
// Application callback
|
// Check for breakpoints
|
||||||
vue.breakCode = vue.onRead() ? 1 : 0;
|
return vue.onBreakpoint(Breakpoint.READ);
|
||||||
return vue.breakCode != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test a condition
|
// Test a condition
|
||||||
|
@ -495,16 +494,16 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
private boolean testException() {
|
private boolean testException() {
|
||||||
|
|
||||||
// Check for an interrupt
|
// Check for an interrupt
|
||||||
if (irq != 0 && (exception.code | psw_id | psw_ep | psw_np) == 0) {
|
if (irq != 0 && (exception | psw_id | psw_ep | psw_np) == 0) {
|
||||||
int level;
|
int level;
|
||||||
for (level = 4; level >= 0; level--)
|
for (level = 4; level >= 0; level--)
|
||||||
if ((irq >> level & 1) != 0)
|
if ((irq >> level & 1) != 0)
|
||||||
break;
|
break;
|
||||||
exception.code = 0xFE00 | level << 4;
|
exception = 0xFE00 | level << 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There is no exception
|
// There is no exception
|
||||||
if (exception.code == 0)
|
if (exception == 0)
|
||||||
return stage == HALT && cycles == 0; // No further processing
|
return stage == HALT && cycles == 0; // No further processing
|
||||||
|
|
||||||
// An exception has occurred
|
// An exception has occurred
|
||||||
|
@ -522,14 +521,12 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
access.type = type;
|
access.type = type;
|
||||||
access.value = value;
|
access.value = value;
|
||||||
|
|
||||||
// Application callback
|
// Check for breakpoints
|
||||||
vue.breakCode = vue.onWrite() ? 1 : 0;
|
if (vue.onBreakpoint(Breakpoint.WRITE))
|
||||||
if (vue.breakCode != 0)
|
|
||||||
return true;
|
return true;
|
||||||
if (access.type == Vue.CANCEL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Perform the operation
|
// Perform the operation
|
||||||
|
if (access.type != Vue.CANCEL)
|
||||||
vue.write(access.address, access.type, access.value);
|
vue.write(access.address, access.type, access.value);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -572,7 +569,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
|
|
||||||
// Invalid operation
|
// Invalid operation
|
||||||
if (value > 0x7FFFFFFF || value < 0x80000000) {
|
if (value > 0x7FFFFFFF || value < 0x80000000) {
|
||||||
exception.code = 0xFF70;
|
exception = 0xFF70;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,7 +596,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
)) continue;
|
)) continue;
|
||||||
|
|
||||||
// The value is a reserved operand
|
// The value is a reserved operand
|
||||||
exception.code = 0xFF60;
|
exception = 0xFF60;
|
||||||
psw_fro = 1;
|
psw_fro = 1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -611,7 +608,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
|
|
||||||
// Overflow
|
// Overflow
|
||||||
if (full > Float.MAX_VALUE || full < -Float.MAX_VALUE) {
|
if (full > Float.MAX_VALUE || full < -Float.MAX_VALUE) {
|
||||||
exception.code = 0xFF64;
|
exception = 0xFF64;
|
||||||
psw_fov = 1;
|
psw_fov = 1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -803,7 +800,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
|
|
||||||
// Zero division
|
// Zero division
|
||||||
if (right == 0) {
|
if (right == 0) {
|
||||||
exception.code = 0xFF80;
|
exception = 0xFF80;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -837,7 +834,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
|
|
||||||
// An exception has occurred
|
// An exception has occurred
|
||||||
if (right == 0) {
|
if (right == 0) {
|
||||||
exception.code = left == 0 ? 0xFF70 : 0xFF68;
|
exception = left == 0 ? 0xFF70 : 0xFF68;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -854,7 +851,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
|
|
||||||
// Zero division
|
// Zero division
|
||||||
if (right == 0) {
|
if (right == 0) {
|
||||||
exception.code = 0xFF80;
|
exception = 0xFF80;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1105,7 +1102,7 @@ this.cycles = 0; // DEBUG: Stop processing after execute
|
||||||
|
|
||||||
// Trap
|
// Trap
|
||||||
private void TRAP() {
|
private void TRAP() {
|
||||||
exception.code = 0xFFA0 | inst.imm & 31;
|
exception = 0xFFA0 | inst.imm & 31;
|
||||||
pc += 2;
|
pc += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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:
|
case 2:
|
||||||
reg2 = bits >> 21 & 31;
|
reg2 = bits >> 21 & 31;
|
||||||
imm = extend < 0 ? bits << 11 >> 27 : bits >> 16 & 31;
|
imm = extend < 0 ? bits << 11 >> 27 : bits >> 16 & 31;
|
||||||
if (id == BITSTRING)
|
if (id == BITSTRING) {
|
||||||
id = imm >= 16 ? Vue.ILLEGAL : LOOKUP_BITSTRING[imm];
|
id = imm >= 16 ? Vue.ILLEGAL : LOOKUP_BITSTRING[imm];
|
||||||
|
subopcode = imm;
|
||||||
|
}
|
||||||
|
if (id == Vue.SETF)
|
||||||
|
cond = imm & 15;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
opcode = 0x20;
|
opcode = 0x20;
|
||||||
|
|
|
@ -7,11 +7,11 @@ import java.util.*;
|
||||||
class JavaVue extends Vue {
|
class JavaVue extends Vue {
|
||||||
|
|
||||||
// Package fields
|
// Package fields
|
||||||
int breakCode; // Application break code
|
int breakCode; // Application breakpoint code
|
||||||
|
int breakType; // Most recent breakpoint scenario
|
||||||
CPU cpu; // Processor
|
CPU cpu; // Processor
|
||||||
int hook; // Most recent breakpoint hook
|
|
||||||
GamePak pak; // Game pak
|
GamePak pak; // Game pak
|
||||||
int[] stack; // Breakpoint evaluation stack
|
int[] stack; // Breakpoint condition evaluation stack
|
||||||
byte[] wram; // System WRAM
|
byte[] wram; // System WRAM
|
||||||
|
|
||||||
|
|
||||||
|
@ -53,50 +53,47 @@ class JavaVue extends Vue {
|
||||||
// Process up to the given number of cycles
|
// Process up to the given number of cycles
|
||||||
do {
|
do {
|
||||||
|
|
||||||
// Determine the number of cycles during which nothing will happen
|
// Determine the number of cycles where no breakpoint will occur
|
||||||
int cycles = -1;
|
int cycles = maxCycles; // min(maxCycles, nextFrameCycles)
|
||||||
cycles = cpu .until(cycles);
|
cycles = cpu .until(cycles);
|
||||||
//cycles = pad .until(cycles);
|
//cycles = pad .until(cycles);
|
||||||
//cycles = link .until(cycles);
|
//cycles = link .until(cycles);
|
||||||
//cycles = timer.until(cycles);
|
//cycles = timer.until(cycles);
|
||||||
//cycles = vip .until(cycles);
|
//cycles = vip .until(cycles);
|
||||||
//cycles = vsu .until(cycles);
|
|
||||||
|
|
||||||
// Range checking
|
|
||||||
if (cycles == -1) // No activity on any component
|
|
||||||
break;
|
|
||||||
if (maxCycles >= 0) // Restrict to given number of cycles
|
|
||||||
cycles = Math.min(cycles, maxCycles);
|
|
||||||
|
|
||||||
// Process all system components
|
// Process all system components
|
||||||
|
breakCode = 0;
|
||||||
cpu .emulate(cycles);
|
cpu .emulate(cycles);
|
||||||
//pad .emulate(cycles);
|
//pad .emulate(cycles);
|
||||||
//link .emulate(cycles);
|
//link .emulate(cycles);
|
||||||
//timer.emulate(cycles);
|
//timer.emulate(cycles);
|
||||||
//vip .emulate(cycles);
|
//vip .emulate(cycles);
|
||||||
//vsu .emulate(cycles);
|
//vsu .emulate(cycles);
|
||||||
|
|
||||||
// An application break was requested
|
|
||||||
//if (...)
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// Update the number of cycles remaining
|
|
||||||
if (maxCycles >= 0)
|
|
||||||
maxCycles -= cycles;
|
maxCycles -= cycles;
|
||||||
} while (maxCycles != 0);
|
} while (breakCode == 0 && maxCycles != 0);
|
||||||
|
|
||||||
// A break condition has occurred
|
// A break condition has occurred
|
||||||
return Math.max(0, maxCycles);
|
return maxCycles;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate an expression
|
// Retrieve a snapshot of the current state's memory access
|
||||||
public Object evaluate(String expression) {
|
public Access getAccess() {
|
||||||
return null;
|
return cpu.access;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the most recent applicaiton break code
|
||||||
|
public int getBreakCode() {
|
||||||
|
return breakCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the most recent exception code
|
// Retrieve the most recent exception code
|
||||||
public int getException() {
|
public int getExceptionCode() {
|
||||||
return cpu.exception.code;
|
return cpu.exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve a snapshot of the current state's instruction
|
||||||
|
public Instruction getInstruction() {
|
||||||
|
return cpu.inst;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve a register value
|
// Retrieve a register value
|
||||||
|
@ -139,6 +136,12 @@ class JavaVue extends Vue {
|
||||||
|
|
||||||
// Read a value from the CPU bus
|
// Read a value from the CPU bus
|
||||||
public int read(int address, int type) {
|
public int read(int address, int type) {
|
||||||
|
|
||||||
|
// Error checking
|
||||||
|
if (type < 0 || type > 4)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Perform the operation
|
||||||
switch (address >> 24 & 7) {
|
switch (address >> 24 & 7) {
|
||||||
case 5: return readBuffer(wram , address, type);
|
case 5: return readBuffer(wram , address, type);
|
||||||
case 6: return readBuffer(pak.ram, address, type);
|
case 6: return readBuffer(pak.ram, address, type);
|
||||||
|
@ -250,29 +253,48 @@ class JavaVue extends Vue {
|
||||||
// Package Methods //
|
// Package Methods //
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Evaluate the condition in a breakpoint
|
// Retrieve the current state's breakpoint scenario
|
||||||
boolean evaluate(Breakpoint brk) {
|
int getBreakType() {
|
||||||
return false;
|
return breakType;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exception break handler
|
// Retrieve the current instruction fetch index
|
||||||
boolean onException() {
|
int getFetch() {
|
||||||
return onHook(Breakpoint.EXCEPTION);
|
return cpu.fetch;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute break handler
|
// Check for breakpoints
|
||||||
boolean onExecute() {
|
boolean onBreakpoint(int breakType) {
|
||||||
return onHook(Breakpoint.EXECUTE);
|
int end = 0;
|
||||||
|
boolean ranged = false;
|
||||||
|
int start = 0;
|
||||||
|
this.breakType = breakType;
|
||||||
|
|
||||||
|
// Processing for Execute
|
||||||
|
if (breakType == Breakpoint.EXECUTE) {
|
||||||
|
ranged = true;
|
||||||
|
start = cpu.pc;
|
||||||
|
end = start + cpu.inst.size - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read break handler
|
// Processing for Read and Write
|
||||||
boolean onRead() {
|
else if (breakType==Breakpoint.READ || breakType==Breakpoint.WRITE) {
|
||||||
return onHook(Breakpoint.READ);
|
ranged = true;
|
||||||
|
start = cpu.access.address;
|
||||||
|
end = start + TYPE_SIZES[cpu.access.type] - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write break handler
|
// Check all breakpoints
|
||||||
boolean onWrite() {
|
int count = breakpoints.size();
|
||||||
return onHook(Breakpoint.WRITE);
|
for (int x = 0; breakCode == 0 && x < count; x++) {
|
||||||
|
var brk = breakpoints.get(x);
|
||||||
|
if (
|
||||||
|
brk.appliesTo(breakType) &&
|
||||||
|
(!ranged || brk.inRange(start, end)) &&
|
||||||
|
brk.isTrue(stack, breakType, cpu.inst, cpu.access)
|
||||||
|
) breakCode = x + 1;
|
||||||
|
}
|
||||||
|
return breakCode != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read a value from a byte buffer
|
// Read a value from a byte buffer
|
||||||
|
@ -331,6 +353,16 @@ class JavaVue extends Vue {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A breakpoint's condition tokens have changed
|
||||||
|
void updateTokens(Breakpoint brk) {
|
||||||
|
super.updateTokens(brk);
|
||||||
|
int depth = 0;
|
||||||
|
for (var bre : breakpoints)
|
||||||
|
depth = Math.max(depth, bre.getDepth());
|
||||||
|
if (depth != stack.length)
|
||||||
|
stack = new int[depth];
|
||||||
|
}
|
||||||
|
|
||||||
// Write a value to a byte buffer
|
// Write a value to a byte buffer
|
||||||
static void writeBuffer(byte[] data, int address, int type, int value) {
|
static void writeBuffer(byte[] data, int address, int type, int value) {
|
||||||
|
|
||||||
|
@ -377,19 +409,4 @@ class JavaVue extends Vue {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private Methods //
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Check breakpoints during a hooked event
|
|
||||||
private boolean onHook(int hook) {
|
|
||||||
this.hook = hook;
|
|
||||||
for (var brk : breakpoints)
|
|
||||||
if (evaluate(brk))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,13 +117,20 @@ JNIEXPORT jint JNICALL Java_vue_NativeVue_emulate
|
||||||
return vueEmulate(&core->vue, maxCycles);
|
return vueEmulate(&core->vue, maxCycles);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the application break code
|
// Retrieve the most recent exception code
|
||||||
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode
|
JNIEXPORT jint JNICALL Java_vue_NativeVue_getBreakCode
|
||||||
(JNIEnv *env, jobject vue, jlong handle) {
|
(JNIEnv *env, jobject vue, jlong handle) {
|
||||||
Core *core = *(Core **)&handle;
|
Core *core = *(Core **)&handle;
|
||||||
return vueGetBreakCode(&core->vue);
|
return vueGetBreakCode(&core->vue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve the most recent exception code
|
||||||
|
JNIEXPORT jint JNICALL Java_vue_NativeVue_getExceptionCode
|
||||||
|
(JNIEnv *env, jobject vue, jlong handle) {
|
||||||
|
Core *core = *(Core **)&handle;
|
||||||
|
return vueGetExceptionCode(&core->vue);
|
||||||
|
}
|
||||||
|
|
||||||
// Retrieve a register value
|
// Retrieve a register value
|
||||||
JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister
|
JNIEXPORT jint JNICALL Java_vue_NativeVue_getRegister
|
||||||
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) {
|
(JNIEnv *env, jobject vue, jlong handle, jint index, jboolean system) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ class NativeVue extends Vue {
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Produce a new breakpoint and add it to the collection
|
// Produce a new breakpoint and add it to the collection
|
||||||
|
private native void breakpoint(long handle);
|
||||||
public Breakpoint breakpoint() {
|
public Breakpoint breakpoint() {
|
||||||
var brk = super.breakpoint();
|
var brk = super.breakpoint();
|
||||||
breakpoint(handle);
|
breakpoint(handle);
|
||||||
|
@ -41,14 +42,26 @@ class NativeVue extends Vue {
|
||||||
public int emulate(int maxCycles)
|
public int emulate(int maxCycles)
|
||||||
{ return emulate(handle, maxCycles); }
|
{ return emulate(handle, maxCycles); }
|
||||||
|
|
||||||
// Retrieve the application break code
|
// Retrieve a snapshot of the current state's memory access
|
||||||
|
private native Access getAccess(long handle, Access access);
|
||||||
|
public Access getAccess() {
|
||||||
|
return getAccess(handle, new Access());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the most recent application break code
|
||||||
private native int getBreakCode(long handle);
|
private native int getBreakCode(long handle);
|
||||||
public int getBreakCode() { return getBreakCode(handle); }
|
public int getBreakCode() { return getBreakCode(handle); }
|
||||||
|
|
||||||
// Retrieve the most recent exception code
|
// Retrieve the most recent exception code
|
||||||
private native int getException(long handle);
|
private native int getExceptionCode(long handle);
|
||||||
public int getException() {
|
public int getExceptionCode() {
|
||||||
return getException(handle);
|
return getExceptionCode(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve a snapshot of the current state's instruction
|
||||||
|
private native Instruction getInstruction(long handle, Instruction inst);
|
||||||
|
public Instruction getInstruction() {
|
||||||
|
return getInstruction(handle, new Instruction());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve a register value
|
// Retrieve a register value
|
||||||
|
@ -75,6 +88,7 @@ class NativeVue extends Vue {
|
||||||
{ return readBytes(handle, address, dest, offset, length); }
|
{ return readBytes(handle, address, dest, offset, length); }
|
||||||
|
|
||||||
// Remove a breakpoint from the collection
|
// Remove a breakpoint from the collection
|
||||||
|
private native void remove(long handle, int index);
|
||||||
public boolean remove(Breakpoint brk) {
|
public boolean remove(Breakpoint brk) {
|
||||||
int index = breakpoints.indexOf(brk);
|
int index = breakpoints.indexOf(brk);
|
||||||
if (!super.remove(brk))
|
if (!super.remove(brk))
|
||||||
|
@ -117,9 +131,16 @@ class NativeVue extends Vue {
|
||||||
// Package Methods //
|
// Package Methods //
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Evaluate the condition in a breakpoint
|
// Retrieve the current state's breakpoint scenario
|
||||||
boolean evaluate(Breakpoint brk) {
|
private native int getBreakType(long handle);
|
||||||
return false;
|
int getBreakType() {
|
||||||
|
return getBreakType(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve the current instruction fetch index
|
||||||
|
private native int getFetch(long handle);
|
||||||
|
int getFetch() {
|
||||||
|
return getFetch(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
// A breakpoint's address ranges have changed
|
// A breakpoint's address ranges have changed
|
||||||
|
@ -137,16 +158,4 @@ class NativeVue extends Vue {
|
||||||
super.updateTokens(brk);
|
super.updateTokens(brk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private Methods //
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Produce a new breakpoint and add it to the collection
|
|
||||||
private native void breakpoint(long handle);
|
|
||||||
|
|
||||||
// Remove a breakpoint from the collection
|
|
||||||
private native void remove(long handle, int index);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -138,7 +138,7 @@ public abstract class Vue {
|
||||||
// Produce an emulation core context
|
// Produce an emulation core context
|
||||||
public static Vue create(boolean useNative) {
|
public static Vue create(boolean useNative) {
|
||||||
return !useNative ? new JavaVue() :
|
return !useNative ? new JavaVue() :
|
||||||
isNativeLoaded() ? new NativeVue() : null;
|
nativeID != null ? new NativeVue() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retrieve the ID of the loaded native library, if any
|
// Retrieve the ID of the loaded native library, if any
|
||||||
|
@ -146,11 +146,6 @@ public abstract class Vue {
|
||||||
return nativeID;
|
return nativeID;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine whether the native module is loaded
|
|
||||||
public static boolean isNativeLoaded() {
|
|
||||||
return nativeID != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Specify the ID of the loaded native library
|
// Specify the ID of the loaded native library
|
||||||
public static void setNativeID(String nativeID) {
|
public static void setNativeID(String nativeID) {
|
||||||
Vue.nativeID = nativeID;
|
Vue.nativeID = nativeID;
|
||||||
|
@ -180,40 +175,32 @@ public abstract class Vue {
|
||||||
return brk;
|
return brk;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate an expression
|
|
||||||
public Object evaluate(String expression) {
|
|
||||||
var brk = new Breakpoint(this);
|
|
||||||
brk.setCondition(expression);
|
|
||||||
return brk.evaluate(null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Produce an array of the current breakpoint collection
|
|
||||||
public Breakpoint[] listBreakpoints() {
|
|
||||||
return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a breakpoint from the collection
|
|
||||||
public boolean remove(Breakpoint brk) {
|
|
||||||
if (!breakpoints.remove(brk))
|
|
||||||
return false;
|
|
||||||
brk.remove();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Abstract Methods //
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Release any used resources
|
// Release any used resources
|
||||||
public abstract void dispose();
|
public abstract void dispose();
|
||||||
|
|
||||||
// Process the simulation
|
// Process the simulation
|
||||||
public abstract int emulate(int maxCycles);
|
public abstract int emulate(int maxCycles);
|
||||||
|
|
||||||
|
// Evaluate an expression
|
||||||
|
public Object evaluate(String expression) {
|
||||||
|
var brk = new Breakpoint(this);
|
||||||
|
brk.setCondition(expression);
|
||||||
|
brk.setEnabled (true);
|
||||||
|
return brk.evaluateTyped(new int[brk.getDepth()], 0,
|
||||||
|
getInstruction(), getAccess());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve a snapshot of the current state's memory access
|
||||||
|
public abstract Access getAccess();
|
||||||
|
|
||||||
|
// Retrieve the most recent applicaiton break code
|
||||||
|
public abstract int getBreakCode();
|
||||||
|
|
||||||
// Retrieve the most recent exception code
|
// Retrieve the most recent exception code
|
||||||
public abstract int getException();
|
public abstract int getExceptionCode();
|
||||||
|
|
||||||
|
// Retrieve a snapshot of the current state's instruction
|
||||||
|
public abstract Instruction getInstruction();
|
||||||
|
|
||||||
// Retrieve a register value
|
// Retrieve a register value
|
||||||
public abstract int getRegister(int index, boolean system);
|
public abstract int getRegister(int index, boolean system);
|
||||||
|
@ -224,6 +211,11 @@ public abstract class Vue {
|
||||||
// Determine whether the context is native-backed
|
// Determine whether the context is native-backed
|
||||||
public abstract boolean isNative();
|
public abstract boolean isNative();
|
||||||
|
|
||||||
|
// Produce an array of the current breakpoint collection
|
||||||
|
public Breakpoint[] listBreakpoints() {
|
||||||
|
return breakpoints.toArray(new Breakpoint[breakpoints.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
// Read a value from the CPU bus
|
// Read a value from the CPU bus
|
||||||
public abstract int read(int address, int type);
|
public abstract int read(int address, int type);
|
||||||
|
|
||||||
|
@ -231,6 +223,14 @@ public abstract class Vue {
|
||||||
public abstract boolean readBytes(int address, byte[] dest, int offset,
|
public abstract boolean readBytes(int address, byte[] dest, int offset,
|
||||||
int length);
|
int length);
|
||||||
|
|
||||||
|
// Remove a breakpoint from the collection
|
||||||
|
public boolean remove(Breakpoint brk) {
|
||||||
|
if (!breakpoints.remove(brk))
|
||||||
|
return false;
|
||||||
|
brk.remove();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize all system components
|
// Initialize all system components
|
||||||
public abstract void reset();
|
public abstract void reset();
|
||||||
|
|
||||||
|
@ -253,6 +253,12 @@ public abstract class Vue {
|
||||||
// Package Methods //
|
// Package Methods //
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// Retrieve the current state's breakpoint scenario
|
||||||
|
abstract int getBreakType();
|
||||||
|
|
||||||
|
// Retrieve the current instruction fetch index
|
||||||
|
abstract int getFetch();
|
||||||
|
|
||||||
// A breakpoint's address ranges have changed
|
// A breakpoint's address ranges have changed
|
||||||
void updateRanges(Breakpoint brk) { }
|
void updateRanges(Breakpoint brk) { }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue