Compare commits
115 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
72dddfb88b | ||
![]() |
644007b002 | ||
![]() |
3b31c2d738 | ||
![]() |
a7aa9348ef | ||
![]() |
761e5140b3 | ||
![]() |
44328f7cb4 | ||
![]() |
4b88c88a52 | ||
![]() |
96f120a99d | ||
![]() |
a21c5c3320 | ||
![]() |
d9c7ec81c3 | ||
![]() |
4e3c6d725b | ||
![]() |
82d69b721e | ||
![]() |
660f10c867 | ||
![]() |
d0b8f2d978 | ||
![]() |
37f1afe452 | ||
![]() |
a2420be4c8 | ||
![]() |
d71a2bdb97 | ||
![]() |
13b4827969 | ||
![]() |
ae3d25bf65 | ||
![]() |
d067a72c38 | ||
![]() |
676109cbc3 | ||
![]() |
1adf41888a | ||
![]() |
c422e92ec7 | ||
![]() |
c6c866b86e | ||
![]() |
7b060062af | ||
![]() |
800c7c065c | ||
![]() |
9ad268b619 | ||
![]() |
926e572a53 | ||
![]() |
6b33921e8d | ||
![]() |
ac78950784 | ||
![]() |
40d1031392 | ||
![]() |
0f5c426699 | ||
![]() |
fd188204b4 | ||
![]() |
2b9bdf568a | ||
![]() |
76c2fbb344 | ||
![]() |
50de68941d | ||
![]() |
078f4b0736 | ||
![]() |
99a3024b2a | ||
![]() |
4dd6bf6115 | ||
![]() |
e67be67d57 | ||
![]() |
708bd3a7b9 | ||
![]() |
a441e4bad2 | ||
![]() |
8e337fe933 | ||
![]() |
772472d932 | ||
![]() |
aa13da3219 | ||
![]() |
60b1278f6a | ||
![]() |
8b5d6e5ad4 | ||
![]() |
fd6a442962 | ||
![]() |
3ff3e8bd67 | ||
![]() |
36c64a79bf | ||
![]() |
150906141f | ||
![]() |
4db5192579 | ||
![]() |
59534c188f | ||
![]() |
46db67a678 | ||
![]() |
d436b965d8 | ||
![]() |
94b4e7760c | ||
![]() |
75efe84317 | ||
![]() |
e4d45e470c | ||
![]() |
ec2b562610 | ||
![]() |
db786624d7 | ||
![]() |
e48304d978 | ||
![]() |
51ef1d1d15 | ||
![]() |
66e1898a0d | ||
![]() |
09ecc55898 | ||
![]() |
5586eec381 | ||
![]() |
710927523e | ||
![]() |
0277e26422 | ||
![]() |
00379b472b | ||
![]() |
def8aa8e90 | ||
![]() |
a977124736 | ||
![]() |
8012c9409e | ||
![]() |
7edc6f7582 | ||
![]() |
9d0b798c20 | ||
![]() |
12e5aa4913 | ||
![]() |
2a8eff03a0 | ||
![]() |
866d4eeb6b | ||
![]() |
2702d98253 | ||
![]() |
cdc60b86d1 | ||
![]() |
b240f29efa | ||
![]() |
9292c91352 | ||
![]() |
680e6eaf23 | ||
![]() |
d661391132 | ||
![]() |
bb5cb61556 | ||
![]() |
5fcb5f2a7f | ||
![]() |
343d028398 | ||
![]() |
6755bedf9a | ||
![]() |
35d15eb478 | ||
![]() |
ddd50eb165 | ||
![]() |
c2dc8dec0d | ||
![]() |
e900daa435 | ||
![]() |
d4638fc517 | ||
![]() |
59cbe4938a | ||
![]() |
ff924891f2 | ||
![]() |
650cb46417 | ||
![]() |
2c9e19b6fe | ||
![]() |
e2025ec55d | ||
![]() |
fef6fa0ecb | ||
![]() |
6fb40ff4e2 | ||
![]() |
d51763221a | ||
![]() |
e6a0351942 | ||
![]() |
e2f3ab756d | ||
![]() |
655832e672 | ||
![]() |
e6008f5d74 | ||
![]() |
ca8716ac6d | ||
![]() |
9b86c68ebc | ||
![]() |
883295bdfa | ||
![]() |
b94229c504 | ||
![]() |
52ae1cc1fa | ||
![]() |
58f00e79d7 | ||
![]() |
3988f16ad4 | ||
![]() |
e40bc8675f | ||
![]() |
dd7169d6ca | ||
![]() |
5d30f44295 | ||
![]() |
f5fdd52a71 | ||
![]() |
4925cd654d |
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
META-INF/
|
||||
*.class
|
||||
*.jar
|
||||
*.out
|
||||
build.mia
|
13
Makefile
Normal file
13
Makefile
Normal file
@ -0,0 +1,13 @@
|
||||
KCOMPILER=kotlinc
|
||||
KEXEC=kotlin
|
||||
TEMPLATE=weaver.jar compiler.jar microcompiler.jar
|
||||
|
||||
$(TEMPLATE): %: $(%:.jar=.kt)
|
||||
$(KCOMPILER) $(@:.jar=.kt) -d $@
|
||||
|
||||
all: weaver.jar compiler.jar microcompiler.jar
|
||||
|
||||
clean:
|
||||
rm -f weaver.jar
|
||||
rm -f compiler.jar
|
||||
rm -f microcompiler.jar
|
616
README.md
Normal file
616
README.md
Normal file
@ -0,0 +1,616 @@
|
||||
# LMIA advanced development kit
|
||||
|
||||
This project was developed as a simple development kit for programming and
|
||||
microprogramming the LMIA system.
|
||||
|
||||
|
||||
## μASM instruction set
|
||||
|
||||
### NOP
|
||||
No-operation. This wastes one clock cycle.
|
||||
|
||||
|
||||
### MOV [regA] \[regB\]
|
||||
Move value in *regA* to *regB*
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If regB is [**LC**](#lc), no other [**LC**](#lc) operation can be specified in the
|
||||
same cycle*
|
||||
|
||||
|
||||
### MVN {[reg] | [const]}
|
||||
Move the inverse of a value (from register or constant) into register
|
||||
[**AR**](#ar).
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
|
||||
### MVZ
|
||||
Set [**AR**](#ar) to zero.
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n)*
|
||||
|
||||
|
||||
### ADD {[reg] | [const]}
|
||||
Add value (from register or constant) to register [**AR**](#ar).
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n), [**O**](#o), [**C**](#c)*
|
||||
|
||||
|
||||
### SUB {[reg] | [const]}
|
||||
Subtract value (from register or constant) from register [**AR**](#ar).
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n), [**O**](#o), [**C**](#c)*
|
||||
|
||||
|
||||
### AND {[reg] | [const]}
|
||||
Perform bitwise AND with given value (from register or constant) and register
|
||||
**AR**. *reg* is ignored and only prevalent due to a microcompiler quirk;
|
||||
expect it to be removed in future releases.
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n)*
|
||||
|
||||
|
||||
### ORR {[reg] | [const]}
|
||||
Perform bitwise Or with given value (from register or constant) and register
|
||||
**AR**.
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n)*
|
||||
|
||||
|
||||
|
||||
### ADN {[reg] | [const]}
|
||||
Add value (from register or constant) to register [**AR**](#ar) without
|
||||
updating flags.
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
|
||||
### LSL
|
||||
Performs a logical shift left of [**AR**](#ar).
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n), [**C**](#c)*
|
||||
|
||||
|
||||
### ISL
|
||||
Performs a logical shift left of [**AR**](#ar) and [**HR**](#hr) as if they
|
||||
were one 32-bit register where [**AR**](#ar) corresponds to the
|
||||
most-significant bits.
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n), [**C**](#c)*
|
||||
|
||||
|
||||
### ASR
|
||||
Performs an arithmetic shift right on [**AR**](#ar).
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n), [**C**](#c)*
|
||||
|
||||
|
||||
### ISR
|
||||
Performs an arithmetic shift right of [**AR**](#ar) and [**HR**](#hr) as if
|
||||
they were one 32-bit register where [**AR**](#ar) corresponds to the
|
||||
most-significant bits.
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n), [**C**](#c)*
|
||||
|
||||
|
||||
### LSR
|
||||
Performs a logical shift right of [**AR**](#ar).
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n), [**C**](#c)*
|
||||
|
||||
|
||||
### ROL
|
||||
Performs an arithmetic rotate right on [**AR**](#ar).
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n), [**C**](#c)*
|
||||
|
||||
|
||||
### IRL
|
||||
Performs an arithmetic shift left of [**AR**](#ar) and [**HR**](#hr) as if they
|
||||
were one 32-bit register where [**AR**](#ar) corresponds to the
|
||||
most-significant bits.
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
*If a constant is passed, this operation cannot be parallellized*
|
||||
|
||||
*Sets flags: [**Z**](#z), [**N**](#n), [**C**](#c)*
|
||||
|
||||
|
||||
### LCSET [const]
|
||||
Set [**LC**](#lc) to value of constant.
|
||||
|
||||
|
||||
### CONST [const]
|
||||
Set [**AR**](#ar) to value of cosntant.
|
||||
|
||||
*This operation cannot be parallelized*
|
||||
|
||||
|
||||
### INCPC
|
||||
Increment value in [**PC**](#pc) by one.
|
||||
|
||||
|
||||
### DECLC
|
||||
Decrement value in [**LC**](#lc) by one.
|
||||
|
||||
|
||||
### CALL [label]
|
||||
Move value in [**uPC**](#upc) to [**uSP**](#usp) and set value in [**uPC**](#upc) to point
|
||||
to the address of the given label.
|
||||
|
||||
|
||||
### RET
|
||||
Move value in [**uSP**](#usp) into [**uPC**](#upc).
|
||||
|
||||
|
||||
### HALT
|
||||
Stop execution and set value in [**uPC**](#upc) to **0**.
|
||||
|
||||
|
||||
### BRA [label]
|
||||
Perform an unconditional branch to the address of the given label.
|
||||
|
||||
|
||||
### BNZ [label]
|
||||
Branch to address of label if [**Z-flag**](#z) is **0**.
|
||||
|
||||
|
||||
### BRZ [label]
|
||||
Branch to address of label if [**Z-flag**](#z) is **1**.
|
||||
|
||||
|
||||
### BRN [label]
|
||||
Branch to address of label if [**N-flag**](#n) is **1**.
|
||||
|
||||
|
||||
### BRC [label]
|
||||
Branch to address of label if [**C-flag**](#c) is **1**.
|
||||
|
||||
|
||||
### BRO [label]
|
||||
Branch to address of label if [**O-flag**](#o) is **1**.
|
||||
|
||||
|
||||
### BLS [label]
|
||||
Branch to address of label if [**L-flag**](#l) is **1**.
|
||||
|
||||
|
||||
### BNC [label]
|
||||
Branch to address of label if [**C-flag**](#c) is **0**.
|
||||
|
||||
|
||||
### BNO [label]
|
||||
Branch to address of label if [**O-flag**](#o) is **0**.
|
||||
|
||||
|
||||
### BOP
|
||||
Branch to address specified by entry in optable pointed to by highest [**OP**](#op)
|
||||
in [**IR**](#ir).
|
||||
|
||||
|
||||
### BAM
|
||||
Branch to address specified by entry in addressing mode pointed to by [**M**](#m)
|
||||
in [**IR**](#ir).
|
||||
|
||||
|
||||
### BST
|
||||
Branch to start. This sets value in [**uPC**](#upc) to **0**.
|
||||
|
||||
|
||||
### RESET [reg]
|
||||
Sets all bits in the specified register to **1**.
|
||||
|
||||
*This operation uses the bus*
|
||||
|
||||
|
||||
## Compiler/weaver directives
|
||||
|
||||
### \#define \[name] [const]
|
||||
Define a compile-time constant. This will replace all (valid) constant
|
||||
declarations with the given name with the value supplied here.
|
||||
|
||||
**NOTE**: Constant names are case-insensitive; i.e. *FOO* and *foo* are
|
||||
functionally indistinguishable to the compiler.
|
||||
|
||||
|
||||
### \#data \[address] [const]
|
||||
Define an initial value in the program memory at the given address.
|
||||
|
||||
|
||||
### $[label]
|
||||
Define a compile-time label at the given position in the microprogram. Labels
|
||||
can be referenced using an '@' symbol.
|
||||
|
||||
For example: `$BAR` would declare a label *BAR* which can be referenced with
|
||||
`@BAR`.
|
||||
|
||||
**NOTE**: Label names are case-insensitive; i.e. `@FOO` and `@foo` are
|
||||
functionally indistinguishable to the compiler.
|
||||
|
||||
|
||||
### \#optable \[index] {[label] | [const]}
|
||||
Declare an entry in the opcode jump table (**K1**).
|
||||
|
||||
**NOTE**: The given index must be at most 15 and at least 0 and in the case of
|
||||
a constant being supplied as the value, this value my not be negative nor be
|
||||
greater than 127.
|
||||
|
||||
|
||||
### \#amode \[index] {[label] | [const]}
|
||||
Declare an entry in the addressing mode jump table (**K2**).
|
||||
|
||||
**NOTE**: The given index must be at most 3 and at least 0 and in the case of
|
||||
a constant being supplied as the value, this value my not be negative nor be
|
||||
greater than 127.
|
||||
|
||||
|
||||
### \#pmgen [python]
|
||||
Define a script to be run for each address in program memory. This can be used
|
||||
to generate program memory value mappings more efficiently than manually
|
||||
defining values. The script is run in a for-loop with the index variable
|
||||
`address`. I.e. the variable `address` can be used in the specified script to
|
||||
refer to the current program memory address. To emit a value, simply print a
|
||||
string in the format "\[address] [value]" where the value can be in the range
|
||||
-32768 to 65535 (inclusive).
|
||||
|
||||
|
||||
### \#emit
|
||||
Define a script to emit uASM code at compile-time. Each following line preceded
|
||||
by a `>` will be included in the script; the first line not preceded by `>`
|
||||
(or EOF) will define the end of the script. To emit code, simply print the code
|
||||
that should be emitted.
|
||||
|
||||
Example:
|
||||
|
||||
```
|
||||
mov ar asr
|
||||
#emit
|
||||
>for i in range(4):
|
||||
> print("lsl; mov ar ir")
|
||||
> print("add pm")
|
||||
sub gr
|
||||
```
|
||||
|
||||
the above code will be converted into the the following code by the preprocessor:
|
||||
|
||||
```
|
||||
mov ar asr
|
||||
lsl; mov ar ir
|
||||
add pm
|
||||
lsl; mov ar ir
|
||||
add pm
|
||||
lsl; mov ar ir
|
||||
add pm
|
||||
lsl; mov ar ir
|
||||
add pm
|
||||
sub gr
|
||||
```
|
||||
|
||||
|
||||
## Flags
|
||||
Flags - *aside from [**L**](#l)* - are set based on ALU operations, so they depend
|
||||
on [**AR**](#ar) and the **BUS**. Henceforth, unless otherwise implied or
|
||||
stated, [**AR**](#ar) will refer to the state/value of [**AR**](#ar) *after* an
|
||||
ALU operation. Flags retain their state until an instruction which is declared
|
||||
as modifying said flag is executed.
|
||||
|
||||
### Z
|
||||
Set if [**AR**](#ar) == **0**.
|
||||
|
||||
### N
|
||||
Set if sign bit in [**AR**](#ar) is set.
|
||||
|
||||
### O
|
||||
Set if sign of [**AR**](#ar) differs from signs of both [**AR**](#ar) *before*
|
||||
the arithmetic operation and of the **BUS**.
|
||||
|
||||
### C
|
||||
Set if [**AR**](#ar) is less than or equal to [**AR**](#ar) *before* the
|
||||
arithmetic operation.
|
||||
|
||||
### L
|
||||
Set if [**LC**](#lc) == **0**.
|
||||
|
||||
|
||||
## Registers
|
||||
Available registers for read/write operations are documented below. Unless
|
||||
otherwise specified, the registers are directly accessible via the bus for read
|
||||
and write operations.
|
||||
|
||||
### AR
|
||||
The accumulator register. This register can only be written to as the result of
|
||||
an ALU operation. This is to say, that **AR** is indirectly writable via the ALU,
|
||||
but is nonetheless directly readable via the bus.
|
||||
|
||||
### PM
|
||||
The program-memory pseudo-register allows you to read/write values from the
|
||||
currently accessed program memory address (see [**ASR**](#asr-1)).
|
||||
|
||||
### ASR
|
||||
The address register; this register is used to specify which address of
|
||||
program-memory to be accessible via [**PM**](#pm).
|
||||
|
||||
**NOTE**: This register cannot be read.
|
||||
|
||||
### HR
|
||||
The help register. This is a general-purpose register which is useful for
|
||||
storing ephemeral or intermediate values during a computation.
|
||||
|
||||
### IR
|
||||
The instruction register. This register offers extra functionality such as
|
||||
**K1**- and **K2**-table addressing via the [**OP**](#op) and [**M**](#m)
|
||||
bits respectively. The [**GRx**](#grx) and [**M**](#m) bits can also be used
|
||||
to address a specific general register via the [**GR**](#gr) multiplexer.
|
||||
|
||||
Bit-level layout of IR (from MSB to LSB, left-to-right):
|
||||
|
||||
| **OP** | **GRx** | **M** | **ADR** |
|
||||
|:------:|:-------:|:------:|:-------:|
|
||||
| 4 bits | 2 bits | 2 bits | 8 bits |
|
||||
|
||||
|
||||
##### OP
|
||||
Machine instruction. Indexes **K1**-table.
|
||||
|
||||
##### GRx
|
||||
[**GR**](#gr) multiplexer selector.
|
||||
|
||||
##### M
|
||||
Addressing mode. Indexes **K2**-table.
|
||||
|
||||
##### ADR
|
||||
[**ASR**](#asr-1) address argument.
|
||||
|
||||
|
||||
|
||||
### GR
|
||||
This is a shorthand for accessing the general register currently made available
|
||||
by the GR multiplexer when said MUX is controlled by the [**GRx**](#grx) bits in
|
||||
[**IR**](#ir).
|
||||
|
||||
**NOTE**: Only one GR can be accessed per cycle. Which register this is (of
|
||||
the four available registers) is determined by the value in [**IR**](#ir).
|
||||
|
||||
### GRM
|
||||
This is a shorthand for accessing the general register currently made available
|
||||
by the GR multiplexer when said MUX is controlled by the [**M**](#m) bits in
|
||||
[**IR**](#ir).
|
||||
|
||||
**NOTE**: Only one GR can be accessed per cycle. Which register this is (of
|
||||
the four available registers) is determined by the value in [**IR**](#ir).
|
||||
|
||||
### LC
|
||||
The loopcounter register is a special register that can only be modified in
|
||||
three ways: all three ways are dictated by the L-field in the microprogram.
|
||||
Additionally, the counter can remain unchanged by simply not specifying an
|
||||
action to take for the register. The three ways of modifying it are as follows:
|
||||
|
||||
* Decrement counter by one. The corresponding uASM instruction for this is
|
||||
`declc`
|
||||
* Load value from bus. The uASM instruction being `mov [reg] LC`
|
||||
* Load a 7-bit constant from micromemory. The uASM instruction for this is
|
||||
`lcset [const]`
|
||||
|
||||
**NOTE**: LC cannot be read and can only be written to as detailed above. The
|
||||
value in LC can, though, to some degree be inferred from the [**L-flag**](#l)
|
||||
(see [*Flags*](#flags)).
|
||||
|
||||
### PC
|
||||
The program counter. This register is only 8 bits wide. Writing a value larger
|
||||
than 8 bits to this register will simply truncate the binary string to 8 bits.
|
||||
Attemping to read a value from PC to a register of a larger width will result
|
||||
in the value in PC populating the lowesr 8 bits and the rest being filled with
|
||||
0's.
|
||||
|
||||
### uPC
|
||||
The microprogram counter; sometimes also referred to as **MyPC**. This register
|
||||
cannot directly be read. It can, though, be written to in a variety of ways.
|
||||
These have been specified as *branch* instructions (such as [`BRA`](#bra-label)), as
|
||||
well as subroutine instructions (such as [`ret`](#ret)).
|
||||
|
||||
**NOTE**: This register is not connected to the bus.
|
||||
|
||||
### uSP
|
||||
The microstack pointer; sometimes also referred to as **MySPC**. This register
|
||||
cannot be directly read or written to. To write to it, a call to a subroutine
|
||||
must be issued (using [`call [label]`](#call-label)). The only way to "read" this
|
||||
register is to issue a subroutine-return instruction ([`ret`](#ret)) which
|
||||
copies the value in this register to [**uPC**](#upc).
|
||||
|
||||
**NOTE**: This register is not connected to the bus. The value in this register
|
||||
cannot be directly acted upon insofar as it cannot be directly read or copied
|
||||
to any other register than [**uPC**](#upc), meaning that the actual value in
|
||||
this register (and consequently in [**uPC**](#upc) aswell) cannot be directly
|
||||
accessed or transformed. I.e. the value in this register cannot be copied to,
|
||||
for example, a general register and thus, its value can only be inferred, not
|
||||
directly measured.
|
||||
|
||||
|
||||
## Sorting algorithms
|
||||
For the sorting competition, we have chosen to focus on implementing bucketsort
|
||||
with an inline insertionsort when inserting values into corresponding buckets.
|
||||
|
||||
### bucksort.uc
|
||||
|
||||
This was the first attempt at a sorting implementation in uASM. It doesn't do
|
||||
more than a simple hash and possibly updating some bucket-specific values.
|
||||
Nonetheless, it formed a clear basis for future implementations.
|
||||
|
||||
Pros:
|
||||
* N/A
|
||||
|
||||
Cons:
|
||||
* N/A
|
||||
|
||||
**Average cycle count: N/A**
|
||||
|
||||
|
||||
### bsrt.uc
|
||||
|
||||
The first successful uASM bucketsort implementation. It uses the aforementioned
|
||||
inline insertionsort. Additionally, it employs a lookup table for pointing to
|
||||
buckets; this had the benefit of significantly decreasing the cycles required
|
||||
to hash values, as well as allowing for buckets of sizes other than even
|
||||
exponents of 2 (as opposed to other implementations). On top of this, it used a
|
||||
parallel-hashing system which allowed the rotation step of the hash algorithm
|
||||
to be applied to two elements of the list (to be sorted) at once, further
|
||||
decreasing the amount of cycles required to perform a hash (per element).
|
||||
|
||||
Pros:
|
||||
* Variable bucket size
|
||||
* Arbitrary bucket arrangement
|
||||
* Parallel-hash implementation
|
||||
* Uses the most recent merge algorithm (rated around 400 cycles)
|
||||
|
||||
Cons:
|
||||
* Heavy bookkeeping due to lookup table paired with parallel-hash
|
||||
* Inefficiency in merge due to lookup table (average loss of 80 cycles)
|
||||
|
||||
**Average cycle count: 1250**
|
||||
|
||||
|
||||
### sort2.uc
|
||||
|
||||
A second iteration of the *bsrt* implementation, this one refines the dual-hash
|
||||
by omitting the lookup table. This means that bookkeeping can be minimized, as
|
||||
it was only necessary due to a lack of available registers. This, of course,
|
||||
comes at the cost of not being able to have variable-sized buckets; i.e. their
|
||||
sizes must an exponent of 2. A minor optimization that was created for this
|
||||
algorithm was the hash-based bookkeeping, wherein a small optimization to
|
||||
bookkeeping could be done during the hashing of the list elements. Any
|
||||
bookkeeping which does not require the ALU can, in fact, be performed during the
|
||||
hashing, since the rotation steps only require AR and HR to be untouched. All
|
||||
other operations are permitted.
|
||||
|
||||
Pros:
|
||||
* Merge: (320-330 cycles)
|
||||
* Direct addressing fits optimally due to register limitations
|
||||
* Optimized regster allocations
|
||||
* Minimal bookkeeping since it is mostly performed during the hashing operation
|
||||
|
||||
Cons:
|
||||
* Bucket sizes must be exponents of 2
|
||||
* Fixed bucket indices based on hash
|
||||
* Two merge operations required (negative + positive)
|
||||
* 95 unused program-memory addresses
|
||||
|
||||
**Average cycle count: 1100**
|
||||
|
||||
|
||||
### sort3.uc
|
||||
|
||||
A third iteration of the *bsrt* implementation: this time entirely scrapping
|
||||
the bucket header, opting to delegate this behaviour to the lookup-table itself.
|
||||
I.e. the lookup table no long points to the start of a bucket (as this can be
|
||||
inferred later), but rather points to the last element of the bucket.
|
||||
Additionally, this implementation scraps the insertion-sort performed in the
|
||||
bucket sort, delegating this task to the merge stage of the sorting algorithm.
|
||||
|
||||
Pros:
|
||||
* Constant-time bucket sort (406 cycles)
|
||||
* More optimally used LUT
|
||||
* No bookkeeping during bucket-sort
|
||||
|
||||
Cons:
|
||||
* Non-constant-time merge
|
||||
* Optimized LUT requires marginally more arithmetic operations over other variants
|
||||
|
||||
**Average cycle count: N/A (not fully implemented)**
|
||||
|
||||
|
||||
### bsrt2.uc
|
||||
|
||||
A fourth iteration developed in parallel with *sort3* as a proof-of-concept of
|
||||
the recently designed lookup-table replacement for hashing. This version is an
|
||||
almost direct copy of *bsrt*, except without a LUT, a K1 jump-table and absolute
|
||||
sizes in the bucket headers (as opposed to a pointer to the last element of a
|
||||
bucket).
|
||||
|
||||
Pros:
|
||||
* Highly optimized K1 jump-table
|
||||
* Subroutined jump-table for possible reuse elsewhere
|
||||
* 13 elements per bucket (+1 over *bsrt*)
|
||||
* Low instruction count
|
||||
|
||||
Cons:
|
||||
* Only sorts one element per iteration
|
||||
|
||||
**Average cycle count: 1050**
|
||||
|
||||
|
||||
### sort4.uc
|
||||
|
||||
A fifth iteration of the common bucketsort algorithm. This one is, as its name
|
||||
implies, based on the *sort2* algorithm. It improves upon it by making heavy
|
||||
use of the `call` and `ret` instructions. I.e. by moving the entire bucketsort
|
||||
implementation to a subroutine, it effectively allows 16 calls to sort values
|
||||
per iteration of the outermost loop. This conversion to a subroutine is done at
|
||||
zero cost to performance, as it exploits the fact that a call to a jumptable-
|
||||
based hashing algorithm is made and rather than returning to the algorithm
|
||||
after the hashing has taken place, the hash-table simply jumps to the
|
||||
insertionsort immediately, after which a `ret` is used to return back to normal
|
||||
execution. This has the effect of reducing a bucketsort of a single value be
|
||||
two instructions to dereference the value to sort, during which a call to the
|
||||
subroutine is made and after sorting, it continues execution at the instruction
|
||||
immediately after the dereference.
|
||||
|
||||
Pros:
|
||||
* Optimized K1 jump-table with inline bucketsort
|
||||
* Highly un-rollable bucketsort implementation
|
||||
* Highly efficient bus use (almost always saturated)
|
||||
* Dynamic bucket placement, allowing for very fast merge operations
|
||||
* Efficient use of general registers to reduce arithmetic operations requiring
|
||||
constant values
|
||||
|
||||
Cons:
|
||||
* Maximum of 6 elements per bucket
|
||||
* 96 unused program-memory addresses (+1 per bucket)
|
||||
|
||||
**Average cycle count: 758**
|
172
bucksort.cpp
Normal file
172
bucksort.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
|
||||
#define BUCKETS 0b1000
|
||||
#define BUCKET_SIZE 20
|
||||
#define LENGTH 32
|
||||
|
||||
int count = 0;
|
||||
bool LT(int a, int b)
|
||||
{
|
||||
count++;
|
||||
return a < b;
|
||||
}
|
||||
|
||||
// TODO: parallellize rotate-left instruction (BSL)
|
||||
// This can be done by partially unrolling the loop ;)
|
||||
// Bucket/Insertion sort (except final merge). C-style pseudocode
|
||||
void bi_sort(short *data){
|
||||
short *buckets;
|
||||
short pc_, ir_, ar, gr, hr, lc_, ir_;
|
||||
|
||||
for(pc_ = 0xE0; pc_ != 0; ++pc_){
|
||||
// Fetch and hash
|
||||
ar = data[pc_];
|
||||
gr = data[pc_];
|
||||
|
||||
ar = ar >> 9;
|
||||
ar = ar & 0b1111000;
|
||||
hr = ar;
|
||||
|
||||
ar = buckets[ar]; // Index 0 is pointer to end of array
|
||||
|
||||
++ar;
|
||||
buckets[hr] = ar; // Increment pointer (since we're inserting a new value)
|
||||
--ar;
|
||||
|
||||
ir_ = pc_; // Push pc
|
||||
pc_ = ar; // Copy start index to pc (for fast indexing)
|
||||
ar = ar - hr; // Compute length
|
||||
|
||||
lc_ = ar; // Loop AR-times
|
||||
|
||||
--lc_;
|
||||
|
||||
while(lc_ != 0){
|
||||
++pc_;
|
||||
ar = data[pc_];
|
||||
if((ar -= gr) < 0){
|
||||
ar += gr;
|
||||
data[pc_] = gr;
|
||||
// Insert here
|
||||
while(lc_ != 0){
|
||||
++pc_;
|
||||
gr = data[pc_];
|
||||
data[pc_] = ar;
|
||||
ar = gr;
|
||||
--lc_;
|
||||
}
|
||||
|
||||
goto END;
|
||||
}
|
||||
--lc_;
|
||||
}
|
||||
|
||||
++pc_;
|
||||
|
||||
data[pc_] = gr; // GR was the biggest. Insert it at the end
|
||||
|
||||
END:
|
||||
pc_ = ir_; // Pop pc
|
||||
}
|
||||
}
|
||||
|
||||
void sort(short *data, int length)
|
||||
{
|
||||
short buckets[BUCKETS][BUCKET_SIZE] = {};
|
||||
short a_, pc, c, d;
|
||||
|
||||
// Bucketsort
|
||||
for (a_ = 0; a_ < length; a_++)
|
||||
{
|
||||
c = data[a_];
|
||||
d = (c >> 13) & 0b111;
|
||||
pc = buckets[d][0];
|
||||
pc++;
|
||||
buckets[d][0] = pc;
|
||||
buckets[d][pc] = c;
|
||||
}
|
||||
|
||||
for (short q = 0; q < BUCKETS; q++)
|
||||
{
|
||||
short *curr = buckets[q];
|
||||
int length = curr[0];
|
||||
printf("buck: %d\tlength: %hd\n", q, curr[0]);
|
||||
for (short i = 0; i < length; i++)
|
||||
{
|
||||
printf("%hd, ", curr[i + 1]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#if 0
|
||||
i ← 1
|
||||
while i < length(A)
|
||||
x ← A[i]
|
||||
j ← i - 1
|
||||
while j >= 0 and A[j] > x
|
||||
A[j+1] ← A[j]
|
||||
j ← j - 1
|
||||
end while
|
||||
A[j+1] ← x
|
||||
i ← i + 1
|
||||
end while
|
||||
#endif
|
||||
|
||||
// Insertion Sort
|
||||
for (short q = 0; q < BUCKETS; q++)
|
||||
{
|
||||
short length = buckets[q][0];
|
||||
short *curr = buckets[q] + 1;
|
||||
a_ = 1;
|
||||
while (a_ < length)
|
||||
{
|
||||
c = curr[a_];
|
||||
pc = a_ - 1;
|
||||
while (pc >= 0 && LT(curr[pc], c))
|
||||
{
|
||||
curr[pc+1] = curr[pc];
|
||||
pc--;
|
||||
}
|
||||
curr[pc+1] = c;
|
||||
a_++;
|
||||
}
|
||||
}
|
||||
|
||||
// Merge the buckets
|
||||
pc = 0;
|
||||
int h = 0b100;
|
||||
for (short q = 0; q < BUCKETS; q++)
|
||||
{
|
||||
short *curr = buckets[h];
|
||||
h = (h + 1) & 0b111;
|
||||
a_ = curr[0];
|
||||
while (a_ >= 1)
|
||||
{
|
||||
data[pc] = curr[a_];
|
||||
pc++;
|
||||
a_--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int *argc, char **argv)
|
||||
{
|
||||
short data[LENGTH] = {};
|
||||
|
||||
srand(clock());
|
||||
for (int i = 0; i < LENGTH; i++)
|
||||
{
|
||||
data[i] = rand() % 0xFFFF;
|
||||
}
|
||||
|
||||
sort(data, LENGTH);
|
||||
|
||||
printf("Num compares: %d\n", count);
|
||||
for (int i = 0; i < LENGTH; i++)
|
||||
{
|
||||
printf("%hd, ", data[i] & 0xFFFF);
|
||||
}
|
||||
printf("\n");
|
||||
return 0;
|
||||
}
|
126
build.sh
Executable file
126
build.sh
Executable file
@ -0,0 +1,126 @@
|
||||
#!/bin/bash
|
||||
|
||||
MODULE_PATH=$(realpath ${0%/*})
|
||||
|
||||
# JARs
|
||||
UCOMP=microcompiler.jar
|
||||
COMP=compiler.jar
|
||||
WEAVER=weaver.jar
|
||||
|
||||
# Main classes
|
||||
UCOMPMAIN=MicrocompilerKt
|
||||
COMPMAIN=CompilerKt
|
||||
WEAVERMAIN=WeaverKt
|
||||
|
||||
# Random number generator
|
||||
RGEN=rand_gen.py
|
||||
|
||||
# Intermediate file
|
||||
INTER=comp.out
|
||||
|
||||
|
||||
usage(){
|
||||
echo -e "Usage:\n\t$0 [TARGET] {TYPE | OUTFILE}\n\t$0 [TARGET] [MICROTARGET] [OUTFILE]\n\t$0 [TARGET] [TYPE] [OUTFILE] [COMBINE]\n\t$0 [TARGET] asm [OUTFILE] [COMBINE] [MICROTARGET]\n\nWhere:\n\tTARGET: main compilation target file\n\tTYPE: either \"asm\" or \"micro\"\n\tOUTFILE: output .mia file\n\tCOMBINE: existing .mia file to combine compilation output with\n\tMICROTARGET: explicit microinstruction target"
|
||||
}
|
||||
|
||||
path_of(){
|
||||
echo "$MODULE_PATH/$1"
|
||||
}
|
||||
|
||||
if [ $# -eq 0 ]; then
|
||||
>&2 echo "Not enough arguments!"
|
||||
usage
|
||||
exit
|
||||
fi
|
||||
|
||||
|
||||
TARGET=$1
|
||||
|
||||
case $# in
|
||||
1)
|
||||
TYPE="asm"
|
||||
OUTPUT="build.mia"
|
||||
;;
|
||||
|
||||
2)
|
||||
if [ "$2" = "asm" ] || [ "$2" = "micro" ] ; then
|
||||
TYPE=$2
|
||||
OUTPUT="build.mia"
|
||||
else
|
||||
TYPE="asm"
|
||||
OUTPUT=$2
|
||||
fi
|
||||
;;
|
||||
|
||||
3)
|
||||
if [ "$2" = "asm" ] || [ "$2" = "micro" ]; then
|
||||
TYPE=$2
|
||||
if [ "$3" = "-v" ]; then
|
||||
VERILOG=$3
|
||||
OUTPUT="build.mia"
|
||||
else
|
||||
OUTPUT=$3
|
||||
fi
|
||||
else
|
||||
TYPE="asm"
|
||||
OUTPUT=$2
|
||||
MICRO=$3
|
||||
fi
|
||||
;;
|
||||
|
||||
4)
|
||||
TYPE=$2
|
||||
OUTPUT=$3
|
||||
COMBINE=$4
|
||||
;;
|
||||
|
||||
5)
|
||||
if [ "$2" != "asm" ]; then
|
||||
>&2 echo "TYPE must be asm"
|
||||
usage
|
||||
exit
|
||||
fi
|
||||
|
||||
TYPE=$2
|
||||
OUTPUT=$3
|
||||
COMBINE=$4
|
||||
MICRO=$5
|
||||
;;
|
||||
|
||||
*)
|
||||
>&2 echo "Too many arguments!"
|
||||
usage
|
||||
exit
|
||||
;;
|
||||
esac
|
||||
|
||||
(cd $MODULE_PATH && make all)
|
||||
|
||||
if [ "$TYPE" = "asm" ]; then
|
||||
KJAR=$COMP
|
||||
KCLASS=$COMPMAIN
|
||||
else
|
||||
KJAR=$UCOMP
|
||||
KCLASS=$UCOMPMAIN
|
||||
fi
|
||||
|
||||
kotlin -classpath $(path_of $KJAR) $KCLASS $TARGET > $INTER || exit
|
||||
python "$(path_of $RGEN)" >> $INTER
|
||||
|
||||
if [ "$COMBINE" = "" ]; then
|
||||
if [ "$MICRO" != "" ]; then
|
||||
kotlin -classpath $(path_of $UCOMP) $UCOMPMAIN $MICRO >> $INTER || exit
|
||||
fi
|
||||
|
||||
kotlin -classpath $(path_of $WEAVER) $WEAVERMAIN $INTER $OUTPUT $VERILOG || exit
|
||||
else
|
||||
if [ "$MICRO" != "" ]; then
|
||||
kotlin -classpath $(path_of $UCOMP) $UCOMPMAIN $MICRO >> $INTER || exit
|
||||
fi
|
||||
kotlin -classpath $(path_of $WEAVERJAR) $WEAVERMAIN $INTER $COMBINE $OUTPUT $VERILOG || exit
|
||||
fi
|
||||
|
||||
# Remove intermediate compilation file
|
||||
rm -f $INTER
|
||||
|
||||
echo "Compiled successfully to $OUTPUT"
|
60
code.eda
60
code.eda
@ -1,31 +1,39 @@
|
||||
AND GR1, 0
|
||||
#AND GR1, 0
|
||||
#
|
||||
#LOAD GR0, *0xFE
|
||||
#AND GR0, 0xF
|
||||
#STORE GR0, *0xFF
|
||||
#ADD GR1, *0xFF
|
||||
#
|
||||
#LOAD GR0, *0xFE
|
||||
#LSR GR0, *4
|
||||
#AND GR0, 0xF
|
||||
#STORE GR0, *0xFF
|
||||
#ADD GR1, *0xFF
|
||||
#
|
||||
#LOAD GR0, *0xFE
|
||||
#LSR GR0, *8
|
||||
#AND GR0, 0xF
|
||||
#STORE GR0, *0xFF
|
||||
#ADD GR1, *0xFF
|
||||
#
|
||||
#LOAD GR0, *0xFE
|
||||
#LSR GR0, *12
|
||||
#AND GR0, 0xF
|
||||
#STORE GR0, *0xFF
|
||||
#ADD GR1, *0xFF
|
||||
#
|
||||
#STORE GR1, *0xFF
|
||||
#
|
||||
LOAD GR0, *0x00
|
||||
LOAD GR1, *0x00
|
||||
|
||||
LOAD GR0, *0xFE
|
||||
AND GR0, 0xF
|
||||
STORE GR0, *0xFF
|
||||
ADD GR1, *0xFF
|
||||
CMP GR0, GR1
|
||||
BEQ @TEST
|
||||
|
||||
LOAD GR0, *0xFE
|
||||
LSR GR0, *4
|
||||
AND GR0, 0xF
|
||||
STORE GR0, *0xFF
|
||||
ADD GR1, *0xFF
|
||||
LOAD GR0, *0xFF
|
||||
|
||||
LOAD GR0, *0xFE
|
||||
LSR GR0, *8
|
||||
AND GR0, 0xF
|
||||
STORE GR0, *0xFF
|
||||
ADD GR1, *0xFF
|
||||
TEST:
|
||||
HALT
|
||||
|
||||
LOAD GR0, *0xFE
|
||||
LSR GR0, *12
|
||||
AND GR0, 0xF
|
||||
STORE GR0, *0xFF
|
||||
ADD GR1, *0xFF
|
||||
|
||||
STORE GR1, *0xFF
|
||||
|
||||
HALT GR0, *0
|
||||
|
||||
STORE GR1, *0xFF
|
||||
|
||||
|
306
compiler.kt
Normal file
306
compiler.kt
Normal file
@ -0,0 +1,306 @@
|
||||
import java.io.*
|
||||
|
||||
enum class OpCode(val opcode: Int, val useM: Boolean, val useADR: Boolean, val useReg: Boolean = true) {
|
||||
LOAD(0, true, true),
|
||||
STORE(1, true, true),
|
||||
ADD(2, true, true),
|
||||
SUB(3, true, true),
|
||||
AND(4, true, true),
|
||||
LSR(5, true, true),
|
||||
BRA(6, false, true),
|
||||
BNE(7, false, true),
|
||||
CMP(8, true, false),
|
||||
BEQ(9, false, true),
|
||||
HALT(15, false, false, false);
|
||||
|
||||
companion object {
|
||||
fun fromString(name: String) = OpCode.values().firstOrNull{ it.name == name }
|
||||
}
|
||||
}
|
||||
|
||||
enum class Mode(val id: Int) {
|
||||
DIRECT(0), IMMEDIATE(1), INDIRECT(2), INDEXED(3)
|
||||
}
|
||||
|
||||
abstract class Instruction(val words: Int) {
|
||||
abstract fun getData(insns: Iterable<Instruction>): ShortArray
|
||||
}
|
||||
|
||||
class Label(val name: String): Instruction(0) {
|
||||
override fun getData(insns: Iterable<Instruction>) = ShortArray(0)
|
||||
}
|
||||
|
||||
class Operation(val code: OpCode, val reg: Int, val m: Mode, val adr: AddressReference, val immediate: Short? = null): Instruction(if(m == Mode.IMMEDIATE) 2 else 1) {
|
||||
constructor(code: OpCode, reg: Int): this(code, reg, Mode.DIRECT, AddressReference(0)){
|
||||
if(code.useM || code.useADR)
|
||||
throw IllegalArgumentException("Not enough parameters specified for instruction: ${code.name}")
|
||||
}
|
||||
constructor(code: OpCode, m: Mode, adr: AddressReference, immediate: Short? = null): this(code, 0, m, adr, immediate){
|
||||
if(code.useReg)
|
||||
throw IllegalArgumentException("Not enough parameters specified for instruction: ${code.name}")
|
||||
}
|
||||
constructor(code: OpCode): this(code, 0, Mode.DIRECT, AddressReference(0)){
|
||||
if(code.useM || code.useADR || code.useReg)
|
||||
throw IllegalArgumentException("Not enough parameters specified for instruction: ${code.name}")
|
||||
}
|
||||
|
||||
init {
|
||||
if(m == Mode.IMMEDIATE && immediate == null)
|
||||
throw IllegalArgumentException("No immediate argument passed!")
|
||||
}
|
||||
|
||||
override fun getData(insns: Iterable<Instruction>): ShortArray {
|
||||
val array = ShortArray(words)
|
||||
array[0] = code.opcode
|
||||
.and(0b1111)
|
||||
.shl(12)
|
||||
.or(
|
||||
if(code.useReg) reg.and(0b11).shl(10)
|
||||
else 0
|
||||
)
|
||||
.or(
|
||||
if(code.useM) m.id.and(0b11).shl(8)
|
||||
else 0
|
||||
)
|
||||
.or(
|
||||
if(code.useADR) adr.getAddress(insns).and(0b11111111)
|
||||
else 0
|
||||
)
|
||||
.toShort()
|
||||
if(m == Mode.IMMEDIATE) array[1] = immediate!!
|
||||
|
||||
return array
|
||||
}
|
||||
}
|
||||
|
||||
class AddressReference(private val label: Label?, private val absolute: Int?) {
|
||||
constructor(label: Label): this(label, null)
|
||||
constructor(absolute: Int): this(null, absolute)
|
||||
|
||||
fun getAddress(insns: Iterable<Instruction>): Int {
|
||||
if(absolute != null) return absolute
|
||||
var addrOff = 0
|
||||
for(insn in insns)
|
||||
if(insn == label) return addrOff
|
||||
else addrOff += insn.words
|
||||
|
||||
throw RuntimeException("Found reference to undeclared label!")
|
||||
}
|
||||
}
|
||||
|
||||
class CompilationUnit {
|
||||
private val instructions = ArrayList<Instruction>()
|
||||
private val labels = ArrayList<Label>()
|
||||
|
||||
fun declareLabel(name: String) {
|
||||
if(instructions.firstOrNull{ it is Label && it.name == name } != null)
|
||||
throw IllegalStateException("Attempt to declare the same label twice!")
|
||||
registerInstruction(getLabel(name))
|
||||
}
|
||||
|
||||
fun getLabel(name: String): Label {
|
||||
if(labels.firstOrNull{ it.name == name } == null)
|
||||
labels.add(Label(name))
|
||||
return labels.first{ it.name == name }
|
||||
}
|
||||
|
||||
fun registerInstruction(insn: Instruction){
|
||||
instructions.add(insn)
|
||||
}
|
||||
|
||||
fun compile(): ShortArray {
|
||||
var dat = 0
|
||||
for(insn in instructions)
|
||||
dat += insn.words
|
||||
|
||||
if(dat > 256)
|
||||
throw RuntimeException("Instruction overflow")
|
||||
|
||||
val rawData = ShortArray(dat)
|
||||
var index = 0
|
||||
|
||||
for(insn in instructions)
|
||||
for(short in insn.getData(instructions))
|
||||
rawData[index++] = short
|
||||
|
||||
return rawData
|
||||
}
|
||||
}
|
||||
|
||||
enum class ArgType {
|
||||
REG, LABEL, INDEX, NUMBER
|
||||
}
|
||||
|
||||
fun parseRegister(arg: String): Int? {
|
||||
if(!arg.toUpperCase().startsWith("GR")) return null
|
||||
try{
|
||||
val reg = Integer.parseInt(arg.substring(2))
|
||||
if(reg > 3 || reg < 0)
|
||||
throw IllegalArgumentException("Register index out of range!")
|
||||
return reg
|
||||
}catch(e: NumberFormatException){
|
||||
throw IllegalArgumentException("Invalid register value")
|
||||
}
|
||||
}
|
||||
|
||||
fun parseLabelReference(arg: String, unit: CompilationUnit): Pair<Label, Mode>? {
|
||||
if(arg.length < 2 || Character.isDigit(arg[1])) return null
|
||||
if(arg.startsWith("@")) return unit.getLabel(arg.substring(1)) to Mode.DIRECT
|
||||
if(arg.startsWith("*")) return unit.getLabel(arg.substring(1)) to Mode.INDIRECT
|
||||
return null
|
||||
}
|
||||
|
||||
fun parseIndex(arg: String): Pair<Int, Mode>? {
|
||||
if(arg.startsWith("[") && arg.endsWith("]")){
|
||||
val literal = arg.substring(1, arg.length - 1)
|
||||
return parseNumber(literal) to Mode.INDEXED
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun parseNumber(literal: String): Int {
|
||||
return if(literal.startsWith("0") && literal.length > 1){
|
||||
when(literal[1]){
|
||||
'x' -> Integer.parseInt(literal.substring(2), 16)
|
||||
'b' -> Integer.parseInt(literal.substring(2), 2)
|
||||
else -> Integer.parseInt(literal.substring(2), 8)
|
||||
}
|
||||
}else{
|
||||
Integer.parseInt(literal)
|
||||
}
|
||||
}
|
||||
|
||||
fun checkProperSize(number: Int, max: Int): Int {
|
||||
if(number > max || number < 0)
|
||||
throw IllegalArgumentException("Parsed value out of range!")
|
||||
return number
|
||||
}
|
||||
|
||||
fun parseNumberLiteral(arg: String): Pair<Int, Mode>? {
|
||||
if(arg.startsWith("$")) return checkProperSize(parseNumber(arg.substring(1)), 65535) to Mode.IMMEDIATE
|
||||
if(arg.startsWith("*")) return checkProperSize(parseNumber(arg.substring(1)), 255) to Mode.INDIRECT
|
||||
return try{
|
||||
checkProperSize(parseNumber(arg), 255) to Mode.DIRECT
|
||||
}catch(e: NumberFormatException){
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun parseInstruction(line: String, unit: CompilationUnit): Instruction? {
|
||||
if(line.length == 0) return null
|
||||
if(line.replace(" ", "").endsWith(":")){
|
||||
val label = line.substring(0, line.length - 1)
|
||||
if(label.toUpperCase().startsWith("GR") || label.startsWith("$") || label.startsWith("*") || label.startsWith("@"))
|
||||
throw IllegalArgumentException("Label uses reserved prefix")
|
||||
return unit.getLabel(label)
|
||||
}
|
||||
val firstSpace = line.indexOf(" ")
|
||||
if(firstSpace == -1){
|
||||
val opcode = OpCode.fromString(line.toUpperCase())
|
||||
if(opcode == null)
|
||||
throw IllegalArgumentException("Unknown instruction: $line")
|
||||
if(opcode.useReg || opcode.useADR || opcode.useM)
|
||||
throw IllegalArgumentException("Not enough arguments passed to $line")
|
||||
return Operation(opcode)
|
||||
}else{
|
||||
val opcode = OpCode.fromString(line.substring(0, firstSpace).toUpperCase())
|
||||
if(opcode == null)
|
||||
throw IllegalArgumentException("Unknown instruction: $line")
|
||||
val args = line.substring(firstSpace + 1).replace(" ", "").split(",").toTypedArray()
|
||||
when(args.size){
|
||||
1 -> {
|
||||
var arg = parseArguments(args[0], null, unit)
|
||||
return when(arg.first.first){
|
||||
ArgType.REG -> Operation(opcode, arg.first.second as Int)
|
||||
ArgType.LABEL -> Operation(opcode, (arg.first.second as Pair<Label, Mode>).second, AddressReference((arg.first.second as Pair<Label, Mode>).first))
|
||||
ArgType.INDEX -> Operation(opcode, (arg.first.second as Pair<Int, Mode>).second, AddressReference((arg.first.second as Pair<Int, Mode>).first))
|
||||
ArgType.NUMBER -> if((arg.first.second as Pair<Int, Mode>).second == Mode.IMMEDIATE)
|
||||
Operation(opcode, (arg.first.second as Pair<Int, Mode>).second, AddressReference(0), (arg.first.second as Pair<Int, Mode>).first.toShort())
|
||||
else Operation(opcode, (arg.first.second as Pair<Int, Mode>).second, AddressReference((arg.first.second as Pair<Int, Mode>).first))
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
val arg = parseArguments(args[0], args[1], unit)
|
||||
val first = arg.first
|
||||
val second = arg.second!!
|
||||
|
||||
if(first.first != ArgType.REG)
|
||||
throw IllegalArgumentException("First argument must be a register")
|
||||
|
||||
return when(second.first){
|
||||
ArgType.REG -> Operation(opcode, first.second as Int, Mode.values()[second.second as Int], AddressReference(0), 0)
|
||||
ArgType.LABEL -> Operation(opcode, first.second as Int, (second.second as Pair<Label, Mode>).second, AddressReference((second.second as Pair<Label, Mode>).first))
|
||||
ArgType.NUMBER -> if((second.second as Pair<Int, Mode>).second == Mode.IMMEDIATE)
|
||||
Operation(opcode, first.second as Int, (second.second as Pair<Int, Mode>).second, AddressReference(0), (second.second as Pair<Int, Mode>).first.toShort())
|
||||
else Operation(opcode, first.second as Int, (second.second as Pair<Int, Mode>).second, AddressReference((second.second as Pair<Int, Mode>).first))
|
||||
ArgType.INDEX -> Operation(opcode, first.second as Int, (second.second as Pair<Int, Mode>).second, AddressReference((second.second as Pair<Int, Mode>).first))
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("Too many arguments specified")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseArgument(arg: String, unit: CompilationUnit): Pair<ArgType, Any>? {
|
||||
var resolve: Any? = parseRegister(arg)
|
||||
if(resolve != null) return ArgType.REG to resolve
|
||||
resolve = parseIndex(arg)
|
||||
if(resolve != null) return ArgType.INDEX to resolve
|
||||
resolve = parseLabelReference(arg, unit)
|
||||
if(resolve != null) return ArgType.LABEL to resolve
|
||||
resolve = parseNumberLiteral(arg)
|
||||
if(resolve != null) return ArgType.NUMBER to resolve
|
||||
|
||||
return null // Unresolved
|
||||
}
|
||||
|
||||
fun parseArguments(arg0: String, arg1: String?, unit: CompilationUnit): Pair<Pair<ArgType, Any>, Pair<ArgType, Any>?> {
|
||||
val resolve0 = parseArgument(arg0, unit)
|
||||
if(resolve0 == null) throw IllegalArgumentException("Invalid argument: $arg0")
|
||||
|
||||
val resolve1 = if(arg1 != null) parseArgument(arg1, unit) else null
|
||||
if(arg1 != null && resolve1 == null) throw IllegalArgumentException("Invalid argument: $arg0")
|
||||
|
||||
return resolve0 to resolve1
|
||||
}
|
||||
|
||||
fun parseInstructions(fileData: String): ShortArray {
|
||||
val unit = CompilationUnit()
|
||||
val lines = fileData.replace("\r", "").replace("\t", "").split('\n').toTypedArray()
|
||||
for(index in lines.indices){
|
||||
val commentIndex = lines[index].indexOf("#")
|
||||
if(commentIndex > 0)
|
||||
lines[index] = lines[index].substring(0, commentIndex)
|
||||
try{
|
||||
val insn = parseInstruction(lines[index], unit)
|
||||
if(insn != null) unit.registerInstruction(insn)
|
||||
}catch(e: Exception){
|
||||
print("An error occurred when compiling (line $index)")
|
||||
val message = e.message
|
||||
if(message != null) print(":\n\t$message")
|
||||
println()
|
||||
}
|
||||
}
|
||||
return unit.compile()
|
||||
}
|
||||
|
||||
fun main(args: Array<String>){
|
||||
// Ensure correct argument length 'n stuff
|
||||
if(args.size != 1){
|
||||
System.err.println("Invalid argument length!")
|
||||
System.exit(-1)
|
||||
}
|
||||
|
||||
val file = File(args[0])
|
||||
|
||||
// Make sure we have a valid file
|
||||
if(!file.isFile){
|
||||
System.err.println("Given file doesn't exist!")
|
||||
System.exit(-2)
|
||||
}
|
||||
for(insn in parseInstructions(file.readText()))
|
||||
println(insn.toUHex())
|
||||
}
|
||||
|
||||
|
||||
fun Short.toUHex() = toInt().and(0xFFFF.toInt()).or(1.shl(30)).toString(16).substring(4)
|
66
compiler.py
66
compiler.py
@ -3,14 +3,16 @@
|
||||
# Compiles ED-Assembly to Machinecode
|
||||
#
|
||||
|
||||
# LOAD GRx, M, ADR
|
||||
# STORE GRx, M, ADR
|
||||
# ADD GRx, M, ADR
|
||||
# SUB GRx, M, ADR
|
||||
# AND GRx, M, ADR
|
||||
# LSR GRx, M, Y # STEG
|
||||
# LOAD GRx, ADR
|
||||
# STORE GRx, ADR
|
||||
# ADD GRx, ADR
|
||||
# SUB GRx, ADR
|
||||
# AND GRx, ADR
|
||||
# LSR GRx, Y # STEG
|
||||
# BRA ADR
|
||||
# BNE ADR
|
||||
# BEQ ADR
|
||||
# CMP GRx, GRy
|
||||
|
||||
#
|
||||
# One machine instruction
|
||||
@ -125,6 +127,10 @@ OPCODE_TABLE = {
|
||||
"LSR" : 0b0101,
|
||||
"BRA" : 0b0110,
|
||||
"BNE" : 0b0111,
|
||||
"CMP" : 0b1000,
|
||||
"BEQ" : 0b1001,
|
||||
"BLT" : 0b1010,
|
||||
"CMI" : 0b1011,
|
||||
"HALT" : 0b1111,
|
||||
}
|
||||
|
||||
@ -229,6 +235,34 @@ def parse_instruction(args):
|
||||
"""
|
||||
Parse an assembly instruction into an instruction object.
|
||||
"""
|
||||
if args[0] in ["HALT"]:
|
||||
return Instruction(OPCODE_TABLE[args[0]], 0, 0, 0)
|
||||
if args[0] in ["LOAD", "STORE", "ADD", "SUB", "AND", "LSR"]:
|
||||
return parse_tripple_instuction(args)
|
||||
if args[0] in ["BRA", "BNE", "BEQ", "BLT"]:
|
||||
return parse_jump(args)
|
||||
if args[0] in ["CMP"]:
|
||||
return parse_dual_registers(args)
|
||||
if args[0] in ["CMI"]:
|
||||
return parse_compare_imediate(args)
|
||||
else:
|
||||
print("Foregotten instruction: ", args[0])
|
||||
assert False
|
||||
|
||||
def parse_compare_imediate(args):
|
||||
if len(args) < 3:
|
||||
raise SyntaxError("Not enough arguments.")
|
||||
if len(args) > 3:
|
||||
raise SyntaxError("Trash at end of line \"{}\"".format(" ".join(args[3:])))
|
||||
opcode = OPCODE_TABLE[args[0]]
|
||||
if args[1].upper() not in REGISTERS:
|
||||
raise SyntaxError("Invalid register name" + args[1])
|
||||
register = REGISTERS[args[1].upper()]
|
||||
address, mode = parse_operand(args[2])
|
||||
return Instruction(opcode, register, ADDRESS_MODES["DIRECT"], address)
|
||||
|
||||
|
||||
def parse_tripple_instuction(args):
|
||||
if len(args) < 3:
|
||||
raise SyntaxError("Not enough arguments.")
|
||||
if len(args) > 3:
|
||||
@ -240,6 +274,26 @@ def parse_instruction(args):
|
||||
address, mode = parse_operand(args[2])
|
||||
return Instruction(opcode, register, mode, address)
|
||||
|
||||
def parse_jump(args):
|
||||
if len(args) < 2:
|
||||
raise SyntaxError("Not enough arguments.")
|
||||
if len(args) > 2:
|
||||
raise SyntaxError("Trash at end of line \"{}\"".format(" ".join(args[3:])))
|
||||
opcode = OPCODE_TABLE[args[0]]
|
||||
address, mode = parse_operand(args[1])
|
||||
return Instruction(opcode, 0, ADDRESS_MODES["DIRECT"], address)
|
||||
|
||||
def parse_dual_registers(args):
|
||||
if len(args) < 3:
|
||||
raise SyntaxError("Not enough arguments.")
|
||||
if len(args) > 3:
|
||||
raise SyntaxError("Trash at end of line \"{}\"".format(" ".join(args[3:])))
|
||||
opcode = OPCODE_TABLE[args[0]]
|
||||
if args[1].upper() not in REGISTERS:
|
||||
raise SyntaxError("Invalid register name" + args[1])
|
||||
register_a = REGISTERS[args[1].upper()]
|
||||
register_b = REGISTERS[args[2].upper()]
|
||||
return Instruction(opcode, register_a, register_b, 0)
|
||||
|
||||
def main():
|
||||
success = True
|
||||
|
110
labb3.mia
110
labb3.mia
@ -1,31 +1,31 @@
|
||||
PM:
|
||||
00: 4500
|
||||
01: 0000
|
||||
02: 00fe
|
||||
03: 4100
|
||||
04: 000f
|
||||
05: 10ff
|
||||
06: 24ff
|
||||
07: 00fe
|
||||
08: 5004
|
||||
09: 4100
|
||||
0a: 000f
|
||||
0b: 10ff
|
||||
0c: 24ff
|
||||
0d: 00fe
|
||||
0e: 5008
|
||||
0f: 4100
|
||||
10: 000f
|
||||
11: 10ff
|
||||
12: 24ff
|
||||
13: 00fe
|
||||
14: 500c
|
||||
15: 4100
|
||||
16: 000f
|
||||
17: 10ff
|
||||
18: 24ff
|
||||
19: 14ff
|
||||
1a: f000
|
||||
00: 00df
|
||||
01: 2100
|
||||
02: 0001
|
||||
03: b0ff
|
||||
04: 9014
|
||||
05: 1015
|
||||
06: 0a15
|
||||
07: 1415
|
||||
08: 2500
|
||||
09: 0001
|
||||
0a: 1417
|
||||
0b: 0e17
|
||||
0c: 8b00
|
||||
0d: a008
|
||||
0e: 1a17
|
||||
0f: 1e15
|
||||
10: 0a15
|
||||
11: b4ff
|
||||
12: 7008
|
||||
13: 6001
|
||||
14: f000
|
||||
15: 0020
|
||||
16: 0000
|
||||
17: 0000
|
||||
18: 0000
|
||||
19: 00ff
|
||||
1a: 0000
|
||||
1b: 0000
|
||||
1c: 0000
|
||||
1d: 0000
|
||||
@ -272,7 +272,7 @@ MyM:
|
||||
0c: 0380000
|
||||
0d: 0880000
|
||||
0e: 0130180
|
||||
0f: 000020f
|
||||
0f: 0000780
|
||||
10: 0480000
|
||||
11: 11c0001
|
||||
12: 0980000
|
||||
@ -286,17 +286,17 @@ MyM:
|
||||
1a: 0000299
|
||||
1b: 0000400
|
||||
1c: 0058180
|
||||
1d: 0000000
|
||||
1e: 0000000
|
||||
1f: 0000000
|
||||
20: 0000000
|
||||
21: 0000000
|
||||
22: 0000000
|
||||
23: 0000000
|
||||
24: 0000000
|
||||
25: 0000000
|
||||
26: 0000000
|
||||
27: 0000000
|
||||
1d: 0380000
|
||||
1e: 0b84180
|
||||
1f: 000041c
|
||||
20: 0000180
|
||||
21: 000049c
|
||||
22: 0000180
|
||||
23: 03c00ff
|
||||
24: 0c40000
|
||||
25: 0128000
|
||||
26: 0380000
|
||||
27: 0b40180
|
||||
28: 0000000
|
||||
29: 0000000
|
||||
2a: 0000000
|
||||
@ -395,10 +395,10 @@ K1:
|
||||
05: 17
|
||||
06: 1c
|
||||
07: 1b
|
||||
08: 00
|
||||
09: 00
|
||||
0a: 00
|
||||
0b: 00
|
||||
08: 1d
|
||||
09: 1f
|
||||
0a: 21
|
||||
0b: 23
|
||||
0c: 00
|
||||
0d: 00
|
||||
0e: 00
|
||||
@ -411,22 +411,22 @@ K2:
|
||||
03: 07
|
||||
|
||||
PC:
|
||||
1b
|
||||
|
||||
ASR:
|
||||
00
|
||||
|
||||
AR:
|
||||
001a
|
||||
ASR:
|
||||
01
|
||||
|
||||
HR:
|
||||
AR:
|
||||
0000
|
||||
|
||||
HR:
|
||||
0001
|
||||
|
||||
GR0:
|
||||
000a
|
||||
0001
|
||||
|
||||
GR1:
|
||||
002e
|
||||
0000
|
||||
|
||||
GR2:
|
||||
0000
|
||||
@ -435,16 +435,16 @@ GR3:
|
||||
0000
|
||||
|
||||
IR:
|
||||
f000
|
||||
b001
|
||||
|
||||
MyPC:
|
||||
0f
|
||||
00
|
||||
|
||||
SMyPC:
|
||||
00
|
||||
|
||||
LC:
|
||||
ff
|
||||
00
|
||||
|
||||
O_flag:
|
||||
|
||||
@ -455,4 +455,4 @@ N_flag:
|
||||
Z_flag:
|
||||
|
||||
L_flag:
|
||||
End_of_dump_file
|
||||
End_of_dump_file
|
||||
|
620
microcompiler.kt
Normal file
620
microcompiler.kt
Normal file
@ -0,0 +1,620 @@
|
||||
enum class ALU(val value: Int, val parameters: Int = 1) {
|
||||
NOP(0b0000, 0), // No operation
|
||||
MOV(0b0001), // Move from bus to AR
|
||||
MVN(0b0010), // Move inverse of bus to AR
|
||||
MVZ(0b0011, 0), // Set AR to zero
|
||||
ADD(0b0100), // Add bus to AR
|
||||
SUB(0b0101), // Subtract bus from AR
|
||||
AND(0b0110), // Bitwise AND with bus and AR
|
||||
ORR(0b0111), // Bitwise OR with bus and AR
|
||||
ADN(0b1000), // Add bus to AR without setting flags
|
||||
LSL(0b1001, 0), // Logical shift left AR
|
||||
ISL(0b1010, 0), // Shift contents of (AR and HR) left (32-bit shift)
|
||||
ASR(0b1011, 0), // Arithmetic shift right
|
||||
ISR(0b1100, 0), // Signed big shift right
|
||||
LSR(0b1101, 0), // Logical shift right AR
|
||||
ROL(0b1110, 0), // Rotate AR left
|
||||
IRL(0b1111, 0); // Rotate ARHR left (32-bit rotate)
|
||||
|
||||
companion object {
|
||||
fun locate(value: Int) = values().first{ it.value == value }
|
||||
fun matchName(name: String) = values().firstOrNull{ it.name.toLowerCase() == name.toLowerCase() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum class ToBus(val value: Int) {
|
||||
NONE(0b000),
|
||||
IR(0b001),
|
||||
PM(0b010),
|
||||
PC(0b011),
|
||||
AR(0b100),
|
||||
HR(0b101),
|
||||
GR(0b110),
|
||||
CONST(0b111);
|
||||
|
||||
companion object {
|
||||
fun locate(value: Int) = values().first{ it.value == value }
|
||||
fun matchName(name: String) = values().first{ it.name.toLowerCase() == name.toLowerCase() }
|
||||
}
|
||||
}
|
||||
|
||||
enum class FromBus(val value: Int) {
|
||||
NONE(0b000),
|
||||
IR(0b001),
|
||||
PM(0b010),
|
||||
PC(0b011),
|
||||
// No AR
|
||||
HR(0b101),
|
||||
GR(0b110),
|
||||
ASR(0b111);
|
||||
|
||||
companion object {
|
||||
fun locate(value: Int) = values().first{ it.value == value }
|
||||
}
|
||||
}
|
||||
|
||||
enum class LoopCounter(val value: Int) {
|
||||
NONE(0b00),
|
||||
|
||||
DEC(0b01),
|
||||
BUS(0b10),
|
||||
U(0b11);
|
||||
|
||||
companion object {
|
||||
fun locate(value: Int) = values().first{ it.value == value }
|
||||
}
|
||||
}
|
||||
|
||||
enum class SEQ(val value: Int) {
|
||||
INC(0b0000),
|
||||
BOP(0b0001), // Branch on opcode
|
||||
BAM(0b0010), // Branch on addressing mode
|
||||
BST(0b0011), // Branch to start (set uPC = 0)
|
||||
CAL(0b0110),
|
||||
RET(0b0111),
|
||||
BRA(0b0101),
|
||||
BNZ(0b0100),
|
||||
BRZ(0b1000),
|
||||
BRN(0b1001),
|
||||
BRC(0b1010),
|
||||
BRO(0b1011),
|
||||
BLS(0b1100),
|
||||
BNC(0b1101),
|
||||
BNO(0b1110),
|
||||
HALT(0b1111);
|
||||
|
||||
companion object {
|
||||
fun locate(value: Int) = values().first{ it.value == value }
|
||||
fun matchName(name: String) = values().firstOrNull{ it.name.toLowerCase() == name.toLowerCase() }
|
||||
}
|
||||
}
|
||||
|
||||
open class MicroInstruction(private val _compiledValue: Int, val isConstInstr: Boolean = false) {
|
||||
constructor(
|
||||
aluOP: ALU,
|
||||
toBus: ToBus,
|
||||
fromBus: FromBus,
|
||||
muxControl: Boolean,
|
||||
increment: Boolean,
|
||||
lc: LoopCounter,
|
||||
seq: SEQ
|
||||
): this((
|
||||
(aluOP.value shl 21) or
|
||||
(toBus.value shl 18) or
|
||||
(fromBus.value shl 15) or
|
||||
(muxControl.toBit() shl 14) or
|
||||
(increment.toBit() shl 13) or
|
||||
(lc.value shl 11) or
|
||||
(seq.value shl 7)
|
||||
) and (-1 ushr 7))
|
||||
|
||||
constructor(aluOP: ALU, toBus: ToBus, setM: Boolean = false): this(aluOP, toBus, FromBus.NONE, setM, false, LoopCounter.NONE, SEQ.INC)
|
||||
constructor(aluOP: ALU, constant: Int): this((aluOP.value shl 21) or (ToBus.CONST.value shl 18) or (constant onlyBits 16), true)
|
||||
constructor(aluOP: ALU): this(aluOP, 0)
|
||||
constructor(toBus: ToBus, fromBus: FromBus, setM: Boolean = false): this(ALU.NOP, toBus, fromBus, setM, false, LoopCounter.NONE, SEQ.INC)
|
||||
constructor(toBus: ToBus, setM: Boolean = false): this(ALU.NOP, toBus, FromBus.NONE, setM, false, LoopCounter.BUS, SEQ.INC)
|
||||
|
||||
var addressReference: AddressReference? = null
|
||||
|
||||
val aluOP: ALU
|
||||
get() = ALU.locate(_compiledValue ushr 21 onlyBits 4)
|
||||
|
||||
val toBus: ToBus
|
||||
get() = ToBus.locate(_compiledValue ushr 18 onlyBits 2)
|
||||
|
||||
val fromBus: FromBus
|
||||
get() = FromBus.locate(_compiledValue ushr 15 onlyBits 2)
|
||||
|
||||
val muxControl = _compiledValue.getBitAt(14)
|
||||
val increment = _compiledValue.getBitAt(13)
|
||||
|
||||
val lc: LoopCounter
|
||||
get() = LoopCounter.locate(_compiledValue ushr 11 onlyBits 2)
|
||||
|
||||
val seq: SEQ
|
||||
get() = SEQ.locate(_compiledValue ushr 7 onlyBits 4)
|
||||
|
||||
val address = _compiledValue onlyBits 7
|
||||
|
||||
val usesBus = isConstInstr || toBus != ToBus.NONE
|
||||
val readsBus = isConstInstr || fromBus != FromBus.NONE
|
||||
|
||||
val needsUADR = isConstInstr || (lc == LoopCounter.U) || (seq.value in 0b0100..0b1110)
|
||||
private val rawValue: Int = _compiledValue onlyBits 25 or (if(addressReference == null || addressReference!!.actualAddress == -1) 0 else addressReference!!.actualAddress)
|
||||
val compiledValue: Int
|
||||
get(){
|
||||
if(addressReference != null && addressReference!!.actualAddress == -1)
|
||||
throw RuntimeException("Unresolved label reference: ${addressReference!!.labelName}")
|
||||
return _compiledValue onlyBits 25 or (if(addressReference == null) 0 else addressReference!!.actualAddress)
|
||||
}
|
||||
|
||||
|
||||
infix fun withReference(ref: AddressReference): MicroInstruction {
|
||||
addressReference = ref
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun withReference(name: String): MicroInstruction {
|
||||
addressReference = AddressReference.makeReference(name)
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun merge(uInstr: MicroInstruction?): MicroInstruction? {
|
||||
if(uInstr == null) return this
|
||||
if(isConstInstr || uInstr.isConstInstr) throw RuntimeException("CONST-instructions are fundamentally un-parallellizable")
|
||||
if(usesBus && uInstr.usesBus && (toBus != uInstr.toBus)) throw RuntimeException("Instructions cannot share bus (output)") // Instructions can't share bus
|
||||
if(readsBus && uInstr.readsBus && (fromBus != uInstr.fromBus)) throw RuntimeException("Instructions cannot share bus (input)") // Instructions cannot both read from bus to differing outputs
|
||||
if(seq != SEQ.INC && uInstr.seq != SEQ.INC && seq != uInstr.seq) throw RuntimeException("SEQ mismatch") // We can merge an INC with something else, but we cannot merge more than this
|
||||
if(needsUADR && uInstr.needsUADR && address != uInstr.address) throw RuntimeException("uADR mismatch") // Instructions cannot depend on differing uADR values
|
||||
|
||||
val newInstr = MicroInstruction(rawValue or uInstr.rawValue)
|
||||
|
||||
val adrRef = if(needsUADR) addressReference else if(uInstr.needsUADR) uInstr.addressReference else null
|
||||
if(adrRef != null && newInstr.needsUADR) newInstr withReference adrRef
|
||||
|
||||
return newInstr
|
||||
}
|
||||
}
|
||||
|
||||
enum class Register(val busValue: Int, val canRead: Boolean = true) {
|
||||
ASR(0b111, false),
|
||||
IR(0b001),
|
||||
PM(0b010),
|
||||
PC(0b011),
|
||||
AR(0b100),
|
||||
HR(0b101),
|
||||
GR(0b110);
|
||||
|
||||
companion object {
|
||||
fun lookup(name: String) = if(name.toLowerCase() == "grm") (GR to true) else (values().firstOrNull{ it.name.toLowerCase() == name.toLowerCase() } to false)
|
||||
}
|
||||
}
|
||||
|
||||
class AddressReference{
|
||||
private val address: Int
|
||||
val labelName: String?
|
||||
|
||||
private constructor(address: Int?, labelName: String?){
|
||||
this.address = address ?: -1
|
||||
_actualAddress = this.address
|
||||
this.labelName = labelName
|
||||
if(address == null && labelName == null) throw RuntimeException("Reference must be accessible by name or address")
|
||||
}
|
||||
|
||||
val actualAddress: Int
|
||||
get() {
|
||||
if(_actualAddress == -1)
|
||||
throw RuntimeException("Unresolved label reference: $labelName")
|
||||
return _actualAddress
|
||||
}
|
||||
|
||||
private var _actualAddress: Int
|
||||
|
||||
private fun checkResolved(){
|
||||
if(_actualAddress != -1) throw RuntimeException("Address already resolved")
|
||||
}
|
||||
|
||||
fun resolveConstant(value: Int){
|
||||
checkResolved()
|
||||
_actualAddress = value
|
||||
}
|
||||
|
||||
fun resolveAddress(target: Int){
|
||||
if(target > 128 || target < 0) throw RuntimeException("Invalid target address")
|
||||
checkResolved()
|
||||
_actualAddress = target
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val registry = ArrayList<AddressReference>()
|
||||
fun makeReference(address: Int) = AddressReference(address, null)
|
||||
fun makeReference(labelName: String): AddressReference {
|
||||
val ref = registry.firstOrNull{ it.labelName == labelName }
|
||||
if(ref != null) return ref
|
||||
val reference = AddressReference(null, labelName)
|
||||
registry.add(reference)
|
||||
return reference
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Boolean.toBit() = if(this) 1 else 0
|
||||
fun Int.toInstruction() = or(0x10000000).toString(16).substring(1)
|
||||
infix fun Int.onlyBits(bits: Int) = and(-1 ushr (32 - bits))
|
||||
fun Int.getBitAt(index: Int) = ushr(index).and(1) == 1
|
||||
|
||||
fun parseMOV(instr: String): MicroInstruction {
|
||||
val args = instr.split(" ")
|
||||
if(args.size != 3) throw RuntimeException("MOV instruction requires two arguments: $instr")
|
||||
if(args[1] == args[2]) throw RuntimeException("Cannot move from register being moved to: $instr")
|
||||
|
||||
val (a, setMA) = Register.lookup(args[1])
|
||||
if(!a!!.canRead) throw RuntimeException("Cannot read from register: $instr")
|
||||
|
||||
|
||||
if(args[2] == "lc") return MicroInstruction(ToBus.locate(a.busValue), setMA)
|
||||
|
||||
val (b, setMB) = Register.lookup(args[2])
|
||||
if(a == b!! && setMA != setMB)
|
||||
throw RuntimeException("Cannot directly move data between multiplexed registers!")
|
||||
if(b == Register.AR) return MicroInstruction(ALU.MOV, ToBus.locate(a.busValue), setMA || setMB)
|
||||
|
||||
return MicroInstruction(ToBus.locate(a.busValue), FromBus.locate(b.busValue), setMA || setMB)
|
||||
}
|
||||
|
||||
fun readNumber(literal: String, max: Int): Int {
|
||||
val number = if(literal.startsWith("0") && literal.length > 1){ // Parse special number
|
||||
if(literal[1] == 'x') Integer.parseInt(literal.substring(2), 16)
|
||||
else if(literal[1] == 'b') Integer.parseInt(literal.substring(2), 2)
|
||||
else Integer.parseInt(literal.substring(1), 8)
|
||||
}else{
|
||||
Integer.parseInt(literal, 10)
|
||||
}
|
||||
|
||||
if(number > max || number < 0) throw RuntimeException("Value out of range: $literal")
|
||||
|
||||
return number
|
||||
}
|
||||
|
||||
fun parseCONST(instr: String): MicroInstruction {
|
||||
val args = instr.split(" ").toTypedArray()
|
||||
if(args.size != 2) throw RuntimeException("Unexpected arguments: $instr")
|
||||
|
||||
return MicroInstruction(ALU.MOV) withReference try{ AddressReference.makeReference(readNumber(args[1], 65535)) } catch(e: NumberFormatException){ AddressReference.makeReference(args[1]) }
|
||||
}
|
||||
|
||||
fun parseALU(instr: String): MicroInstruction {
|
||||
val args = instr.split(" ")
|
||||
|
||||
val aluOperation = ALU.matchName(args[0])!!
|
||||
|
||||
if(args.size != (1 + aluOperation.parameters)) throw RuntimeException("Unexpected arguments: $instr")
|
||||
|
||||
if(aluOperation.parameters == 0) return MicroInstruction(aluOperation, ToBus.NONE, FromBus.NONE, false, false, LoopCounter.NONE, SEQ.INC)
|
||||
|
||||
try{
|
||||
val num = readNumber(args[1], 65535)
|
||||
return MicroInstruction(aluOperation, num)
|
||||
}catch(e: NumberFormatException){
|
||||
// NaN
|
||||
}
|
||||
|
||||
val (source, setM) = Register.lookup(args[1])
|
||||
if(source == null) return MicroInstruction(aluOperation) withReference AddressReference.makeReference(args[1])
|
||||
if(!source.canRead) throw RuntimeException("Cannot read from source: $instr")
|
||||
|
||||
return MicroInstruction(aluOperation, ToBus.locate(source.busValue), setM)
|
||||
}
|
||||
|
||||
fun parseLabelReference(ref: String): AddressReference? {
|
||||
return if(ref.startsWith("@")) AddressReference.makeReference(ref.substring(1)) else null
|
||||
}
|
||||
|
||||
fun parseCALL(instr: String): MicroInstruction {
|
||||
val args = instr.split(" ")
|
||||
if(args.size != 2) throw RuntimeException("Unexpected arguments: $instr")
|
||||
|
||||
return MicroInstruction(ALU.NOP, ToBus.NONE, FromBus.NONE, false, false, LoopCounter.NONE, SEQ.CAL) withReference parseAddressReference(args[1])
|
||||
}
|
||||
|
||||
fun parseBranch(instr: String): MicroInstruction {
|
||||
val args = instr.split(" ")
|
||||
if(args.size != 2) throw RuntimeException("Unexpected arguments: $instr")
|
||||
|
||||
return MicroInstruction(ALU.NOP, ToBus.NONE, FromBus.NONE, false, false, LoopCounter.NONE, SEQ.matchName(args[0])!!) withReference parseAddressReference(args[1])
|
||||
}
|
||||
|
||||
fun parseLCSet(instr: String): MicroInstruction {
|
||||
val args = instr.split(" ")
|
||||
if(args.size != 2) throw RuntimeException("Unexpected arguments: $instr")
|
||||
|
||||
val ref = try{
|
||||
AddressReference.makeReference(readNumber(args[1], 0xFF))
|
||||
}catch(e: NumberFormatException){
|
||||
AddressReference.makeReference(args[1])
|
||||
}
|
||||
|
||||
return MicroInstruction(ALU.NOP, ToBus.NONE, FromBus.NONE, false, false, LoopCounter.U, SEQ.INC) withReference ref
|
||||
}
|
||||
|
||||
fun parseAddressReference(arg: String): AddressReference {
|
||||
return parseLabelReference(arg) ?: AddressReference.makeReference(readNumber(arg, 0xFF))
|
||||
}
|
||||
|
||||
fun parseInstruction(line: String): MicroInstruction? {
|
||||
val subInstructions = line.split(";")
|
||||
if(subInstructions.size == 1){
|
||||
var shave = subInstructions[0].toLowerCase()
|
||||
while(shave.startsWith(" ") || shave.startsWith("\t")) shave = shave.substring(1)
|
||||
while(shave.endsWith(" ") || shave.endsWith("\t")) shave = shave.substring(0, shave.length - 1)
|
||||
|
||||
if(shave.length == 0) return null
|
||||
|
||||
if(shave.startsWith("mov ")) return parseMOV(shave)
|
||||
else if(shave.startsWith("const ")) return parseCONST(shave)
|
||||
else if(shave == "incpc") return MicroInstruction(ALU.NOP, ToBus.NONE, FromBus.NONE, false, true, LoopCounter.NONE, SEQ.INC)
|
||||
else if(ALU.matchName(shave.substring(0, if(shave.indexOf(" ") == -1) shave.length else shave.indexOf(" "))) != null) return parseALU(shave)
|
||||
else if(shave.startsWith("call ")) return parseCALL(shave)
|
||||
else if(shave == "ret") return MicroInstruction(ALU.NOP, ToBus.NONE, FromBus.NONE, false, false, LoopCounter.NONE, SEQ.RET)
|
||||
else if(shave == "bop") return MicroInstruction(ALU.NOP, ToBus.NONE, FromBus.NONE, false, false, LoopCounter.NONE, SEQ.BOP)
|
||||
else if(shave == "bam") return MicroInstruction(ALU.NOP, ToBus.NONE, FromBus.NONE, false, false, LoopCounter.NONE, SEQ.BAM)
|
||||
else if(shave == "bst") return MicroInstruction(ALU.NOP, ToBus.NONE, FromBus.NONE, false, false, LoopCounter.NONE, SEQ.BST)
|
||||
else if(shave.startsWith("b") && shave.indexOf(" ") != -1 && SEQ.matchName(shave.substring(0, shave.indexOf(" "))) != null) return parseBranch(shave)
|
||||
else if(shave == "halt") return MicroInstruction(ALU.NOP, ToBus.NONE, FromBus.NONE, false, false, LoopCounter.NONE, SEQ.HALT)
|
||||
else if(shave.startsWith("lcset")) return parseLCSet(shave)
|
||||
else if(shave == "declc") return MicroInstruction(ALU.NOP, ToBus.NONE, FromBus.NONE, false, false, LoopCounter.DEC, SEQ.INC)
|
||||
else if(shave.startsWith("reset")){
|
||||
val (resetReg, setM) = Register.lookup(shave.substring(6))
|
||||
return MicroInstruction(ALU.NOP, ToBus.NONE, FromBus.locate(resetReg!!.busValue), setM, false, LoopCounter.NONE, SEQ.INC)
|
||||
}
|
||||
else throw RuntimeException("Unknown instruction: $shave")
|
||||
}else{
|
||||
var result: MicroInstruction? = null
|
||||
for(rawInstruction in subInstructions){
|
||||
val parsed = parseInstruction(rawInstruction)
|
||||
if(parsed == null) continue
|
||||
result = parsed merge result
|
||||
if(result == null) throw RuntimeException("Instructions ($line) could not be merged!")
|
||||
}
|
||||
return result!!
|
||||
}
|
||||
}
|
||||
|
||||
fun error(message: String, line: Int){
|
||||
System.err.println("Error on line $line:\n\t$message")
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>){
|
||||
if(args.size != 1) throw RuntimeException("Bad arguments :(")
|
||||
val file = java.io.File(args[0])
|
||||
|
||||
if(!file.isFile) throw RuntimeException("File not found :(")
|
||||
val builder = StringBuilder("@u\n")
|
||||
var currentLine = 0
|
||||
var lineCount = 0
|
||||
val insns = ArrayList<MicroInstruction>()
|
||||
val k1 = HashMap<Int, AddressReference>()
|
||||
val k2 = HashMap<Int, AddressReference>()
|
||||
|
||||
var emitting = false
|
||||
val emitter = ArrayList<String>()
|
||||
|
||||
val rawData = file.readText().replace("\r", "").split("\n")
|
||||
val pregen = StringBuilder()
|
||||
|
||||
fun emitCode(){
|
||||
val emission = StringBuilder()
|
||||
for(line in emitter)
|
||||
emission.append(line).append("\n")
|
||||
|
||||
val (output, error) = emission.toString().runPython()
|
||||
if(error.length > 0)
|
||||
throw RuntimeException("Error when attemping to emit code:\n ${error.replace("\n", "\n ")}")
|
||||
|
||||
pregen.append(output)
|
||||
|
||||
emitter.clear()
|
||||
|
||||
}
|
||||
|
||||
// Process code emission first
|
||||
for(line in rawData){
|
||||
if(line == "#emit") emitting = true
|
||||
else{
|
||||
if(emitting){
|
||||
if(line.startsWith(">")){
|
||||
emitter.add(line.substring(1))
|
||||
continue
|
||||
}
|
||||
else{
|
||||
emitCode()
|
||||
emitting = false
|
||||
}
|
||||
}
|
||||
|
||||
pregen.append(line).append("\n")
|
||||
}
|
||||
}
|
||||
|
||||
if(emitter.size > 0) emitCode()
|
||||
|
||||
println("@p")
|
||||
for(line in pregen.toString().replace("\r", "").split("\n")){
|
||||
++lineCount
|
||||
try{
|
||||
val actualCode = (if(line.indexOf("//") != -1) line.substring(0, line.indexOf("//")) else line).toLowerCase()
|
||||
if(actualCode.length == 0) continue
|
||||
|
||||
if(actualCode.startsWith("$")){
|
||||
AddressReference.makeReference(actualCode.substring(1)).resolveAddress(currentLine)
|
||||
continue
|
||||
}
|
||||
|
||||
// Define compile-time constant
|
||||
if(actualCode.startsWith("#define ")){
|
||||
val substr = actualCode.substring(8)
|
||||
if(substr.indexOf(" ") == -1) throw RuntimeException("Bad compile-time constant definition: $actualCode")
|
||||
val name = substr.substring(0, substr.indexOf(" "))
|
||||
val value = substr.substring(substr.indexOf(" ") + 1).replace(" ", "").replace("\t", "")
|
||||
try{
|
||||
val constant = readNumber(value, 65535)
|
||||
|
||||
AddressReference.makeReference(name).resolveConstant(constant)
|
||||
continue
|
||||
}catch(e: NumberFormatException){
|
||||
throw RuntimeException("Cannot parse constant: $actualCode")
|
||||
}
|
||||
}
|
||||
|
||||
// Define Program-Memory constants
|
||||
if(actualCode.startsWith("#data ")){
|
||||
fun err(): Nothing = throw RuntimeException("Bad program-memory state definition: $actualCode")
|
||||
val substr = actualCode.substring(6)
|
||||
if(substr.indexOf(" ") == -1) err()
|
||||
|
||||
val address = substr.substring(0, substr.indexOf(" "))
|
||||
val constant = substr.substring(substr.indexOf(" ") + 1).replace(" ", "").replace("\t", "")
|
||||
|
||||
try{
|
||||
println("@0x${readNumber(address, 255).toString(16)}\n${readNumber(constant, 65535).or(1 shl 30).toString(16).substring(4)}")
|
||||
}catch(e: NumberFormatException){
|
||||
err()
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Define optable (k1) entry
|
||||
if(actualCode.startsWith("#optable ")){
|
||||
// Parse K1 declaration
|
||||
val substr = actualCode.substring(9)
|
||||
if(substr.indexOf(" ") == -1) throw RuntimeException("Bad optable declaration: $actualCode")
|
||||
|
||||
val address = substr.substring(0, substr.indexOf(" "))
|
||||
val constant = substr.substring(substr.indexOf(" ") + 1).replace(" ", "").replace("\t", "")
|
||||
|
||||
try{
|
||||
k1[readNumber(address, 0xF)] =
|
||||
if(constant.startsWith("@")) AddressReference.makeReference(constant.substring(1))
|
||||
else AddressReference.makeReference(readNumber(constant, 127))
|
||||
}catch(e: NumberFormatException){
|
||||
throw RuntimeException("Unknown number format for optable declaration: $actualCode")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Define addressing mode (k2) entry
|
||||
if(actualCode.startsWith("#amode ")){
|
||||
// Parse K2 declarations
|
||||
val substr = actualCode.substring(7)
|
||||
if(substr.indexOf(" ") == -1) throw RuntimeException("Bad amode declaration: $actualCode")
|
||||
|
||||
val address = substr.substring(0, substr.indexOf(" "))
|
||||
val constant = substr.substring(substr.indexOf(" ") + 1).replace(" ", "").replace("\t", "")
|
||||
|
||||
try{
|
||||
k2[readNumber(address, 0x3)] =
|
||||
if(constant.startsWith("@")) AddressReference.makeReference(constant.substring(1))
|
||||
else AddressReference.makeReference(readNumber(constant, 127))
|
||||
}catch(e: NumberFormatException){
|
||||
throw RuntimeException("Unknown number format for amode declaration: $actualCode")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if(actualCode.startsWith("#pmgen ")){
|
||||
val code = actualCode.substring("#pmgen ".length)
|
||||
|
||||
val (output, error) = "for address in range(0, 256):\n\t$code".runPython()
|
||||
|
||||
if(error.length > 0)
|
||||
throw RuntimeException("An error ocurred when running PM-generation script:\n ${error.replace("\n", "\n ")}")
|
||||
|
||||
val outStrings = output.split("\n")
|
||||
|
||||
for(directive in outStrings){
|
||||
if(directive.length == 0) continue
|
||||
val split = directive.split(" ")
|
||||
if(split.size != 2)
|
||||
throw RuntimeException("Unknown PM mapping: $directive")
|
||||
|
||||
val address: Int
|
||||
val value: Int
|
||||
try{
|
||||
address = split[0].toInt()
|
||||
if(address < 0 || address > 255)
|
||||
throw RuntimeException("Could not parse address: $directive")
|
||||
}catch(e: NumberFormatException){
|
||||
throw RuntimeException("Could not parse address: $directive")
|
||||
}
|
||||
|
||||
try{
|
||||
value = split[1].toInt()
|
||||
if(value < -32768 || address > 65535)
|
||||
throw RuntimeException("Could not parse value: $directive")
|
||||
}catch(e: NumberFormatException){
|
||||
throw RuntimeException("Could not parse value: $directive")
|
||||
}
|
||||
|
||||
// TODO: Fix this. This works, but it's absolutely terrible
|
||||
println("@0x${address.toString(16)}\n${value.and(0xFFFF).or(1 shl 30).toString(16).substring(4)}")
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
val instr = parseInstruction(actualCode)
|
||||
if(instr == null) continue // Blank line
|
||||
insns.add(instr)
|
||||
|
||||
++currentLine
|
||||
}catch(e: Throwable){
|
||||
error(e.message ?: "Compilation failed with an unknown error", lineCount)
|
||||
}
|
||||
}
|
||||
if(currentLine > 127){
|
||||
System.err.println("Instruction count overflow: $currentLine instructions")
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
// Convert instructions to strings
|
||||
// Also resolve address references here
|
||||
for(instr in insns)
|
||||
builder.append(instr.compiledValue.toInstruction()).append("\n")
|
||||
|
||||
// Write K-table data ahead of instructions
|
||||
for(k1Pair in k1)
|
||||
println("@k1${k1Pair.key.toString(16)}${k1Pair.value.actualAddress.toPaddedHexString(2)}")
|
||||
|
||||
for(k2Pair in k2)
|
||||
println("@k2${k2Pair.key.toString(16)}${k2Pair.value.actualAddress.toPaddedHexString(2)}")
|
||||
|
||||
// Write instructions
|
||||
print(builder.toString())
|
||||
|
||||
// Informational message
|
||||
System.err.println("INFO: Microcompilation succeeded! Instruction count: $currentLine")
|
||||
}
|
||||
|
||||
|
||||
fun Int.toPaddedHexString(len: Int): String {
|
||||
val str = toString(16)
|
||||
return if(str.length >= len) str else ("0".repeat(len - str.length) + str)
|
||||
}
|
||||
|
||||
fun String.runPython(): Pair<String, String> {
|
||||
val proc = ProcessBuilder("python", "-c", "$this")
|
||||
.redirectOutput(ProcessBuilder.Redirect.PIPE)
|
||||
.redirectError(ProcessBuilder.Redirect.PIPE)
|
||||
.start()
|
||||
|
||||
// Await termination of pm generator
|
||||
proc.waitFor()
|
||||
return proc.inputStream.readString() to proc.errorStream.readString()
|
||||
}
|
||||
|
||||
fun java.io.InputStream.readString(): String {
|
||||
val bytes = ByteArray(available())
|
||||
read(bytes, 0, bytes.size)
|
||||
return String(bytes)
|
||||
}
|
34
qs.c
Normal file
34
qs.c
Normal file
@ -0,0 +1,34 @@
|
||||
#include <stdio.h>
|
||||
|
||||
void quicksort(int *data, int low, int high){
|
||||
while(low < high){
|
||||
int pivot = data[high];
|
||||
int i = low;
|
||||
|
||||
for(int j = low; j < high; ++j){
|
||||
if(data[j] < pivot){
|
||||
int tmp = data[j];
|
||||
data[j] = data[i];
|
||||
data[i] = tmp;
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
int tmp = data[high];
|
||||
data[high] = data[i];
|
||||
data[i] = tmp;
|
||||
|
||||
quicksort(data, low, i - 1);
|
||||
low = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv){
|
||||
int data[26] = {1, 2, 0, 3, 5, 9, 8, 7, 6, 11, 10, 4, 5, 6, 7, 9, 4, 6, 8, 7, 2, 4, 1, 12, 40, 25};
|
||||
for(int i = 0; i < 26; ++i)
|
||||
printf("#: %d\n", data[i]);
|
||||
|
||||
quicksort(data, 0, 25);
|
||||
for(int i = 0; i < 26; ++i)
|
||||
printf("%: %d\n", data[i]);
|
||||
}
|
9
rand_gen.py
Executable file
9
rand_gen.py
Executable file
@ -0,0 +1,9 @@
|
||||
import random
|
||||
|
||||
def getRandomHex():
|
||||
return hex(random.randint(0, 0x10000) | (1 << 30))[-4:].upper()
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("@p\n@0xE0")
|
||||
for i in range(0, 32):
|
||||
print(getRandomHex())
|
58
sorter.eda
Normal file
58
sorter.eda
Normal file
@ -0,0 +1,58 @@
|
||||
# Beatuifull
|
||||
LOAD GR0 0x00DF # I
|
||||
|
||||
OUTER:
|
||||
ADD GR0 1
|
||||
CMI GR0 0xFF
|
||||
BEQ @END
|
||||
|
||||
STORE GR0 *@I
|
||||
LOAD GR2 **@I
|
||||
|
||||
LOAD GR1 *@I
|
||||
|
||||
INNER:
|
||||
ADD GR1 1
|
||||
STORE GR1 *@J
|
||||
LOAD GR3 **@J
|
||||
|
||||
CMP GR2 GR3
|
||||
BLT @SKIPP
|
||||
|
||||
STORE GR2 **@J
|
||||
STORE GR3 **@I
|
||||
LOAD GR2 **@I
|
||||
SKIPP:
|
||||
CMI GR1 0xFF
|
||||
BNE @INNER
|
||||
BRA @OUTER
|
||||
|
||||
END:
|
||||
HALT
|
||||
|
||||
# for (int i = 0; i < length; i++)
|
||||
# {
|
||||
# for (int j = i; j < length;)
|
||||
# {
|
||||
# j++
|
||||
# if (data[i] < data[j])
|
||||
# {
|
||||
# int a = data[i];
|
||||
# data[i] = data[j];
|
||||
# data[j] = a;
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
|
||||
|
||||
I:
|
||||
. 0020
|
||||
J:
|
||||
. 0000
|
||||
A: # Pointer to value at I
|
||||
. 0000
|
||||
B: # Pointer to value at J
|
||||
. 0000
|
||||
LAST:
|
||||
. 00FF
|
253
ucode/bsrt.uc
Normal file
253
ucode/bsrt.uc
Normal file
@ -0,0 +1,253 @@
|
||||
#define LIST_START 0xE0
|
||||
#define HASH_MASK 0b1111
|
||||
#define BUCKET_SIZE 13
|
||||
#define FIRST_BUCKET_ADDRESS 0x10
|
||||
#define LAST_BUCKET_ADDRESS 0xE0
|
||||
#define LAST_BUCKET_START_ADDR 0xD3
|
||||
|
||||
// PM initial state
|
||||
#data 0x00 0x78
|
||||
#data 0x01 0x85
|
||||
#data 0x02 0x92
|
||||
#data 0x03 0x9f
|
||||
#data 0x04 0xac
|
||||
#data 0x05 0xb9
|
||||
#data 0x06 0xc6
|
||||
#data 0x07 0xd3
|
||||
#data 0x08 0x10
|
||||
#data 0x09 0x1d
|
||||
#data 0x0a 0x2a
|
||||
#data 0x0b 0x37
|
||||
#data 0x0c 0x44
|
||||
#data 0x0d 0x51
|
||||
#data 0x0e 0x5e
|
||||
#data 0x0f 0x6b
|
||||
|
||||
#data 0x10 0x10
|
||||
#data 0x1d 0x1d
|
||||
#data 0x2a 0x2a
|
||||
#data 0x37 0x37
|
||||
#data 0x44 0x44
|
||||
#data 0x51 0x51
|
||||
#data 0x5e 0x5e
|
||||
#data 0x6b 0x6b
|
||||
#data 0x78 0x78
|
||||
#data 0x85 0x85
|
||||
#data 0x92 0x92
|
||||
#data 0x9f 0x9f
|
||||
#data 0xac 0xac
|
||||
#data 0xb9 0xb9
|
||||
#data 0xc6 0xc6
|
||||
#data 0xd3 0xd3
|
||||
|
||||
|
||||
// TODO: Include register initial-state compiler directive (saves, like, 2 cycles max)
|
||||
// Set PC to LIST_START
|
||||
const LIST_START
|
||||
mov ar pc
|
||||
|
||||
|
||||
//// ---- START OF DANK SORT ---- ////
|
||||
$DANK_SORT
|
||||
|
||||
mov pc asr
|
||||
mov pc ir; incpc
|
||||
mov pm ar
|
||||
mov pc asr
|
||||
mov pm hr
|
||||
|
||||
irl; mov hr gr
|
||||
irl
|
||||
irl
|
||||
irl
|
||||
and HASH_MASK
|
||||
mov ar asr
|
||||
mov pm ar; mov pm asr
|
||||
mov ar pc; mvn ar
|
||||
add 1
|
||||
add pm
|
||||
|
||||
mov ar lc
|
||||
|
||||
mov pc ar
|
||||
|
||||
// Move
|
||||
mov pm pc
|
||||
incpc
|
||||
mov pc pm
|
||||
mov ar pc
|
||||
|
||||
|
||||
// First insertion
|
||||
$DANK_INSERT_FIRST_START
|
||||
|
||||
incpc; bls @DANK_INSERT_FIRST_END_BIGGEST
|
||||
|
||||
mov pc asr
|
||||
mov pm ar
|
||||
sub gr
|
||||
adn gr; brn @DANK_INSERT_FIRST_BOTTOM
|
||||
|
||||
mov gr pm; incpc
|
||||
|
||||
$DANK_INSERT_FIRST_SHIFT
|
||||
mov pc asr; bls @DANK_INSERT_SECOND_START
|
||||
mov pm gr
|
||||
mov ar pm
|
||||
mov gr ar; declc; incpc; bra @DANK_INSERT_FIRST_SHIFT
|
||||
|
||||
$DANK_INSERT_FIRST_BOTTOM
|
||||
declc; bra @DANK_INSERT_FIRST_START
|
||||
|
||||
$DANK_INSERT_FIRST_END_BIGGEST
|
||||
mov pc asr
|
||||
mov gr pm
|
||||
|
||||
$DANK_INSERT_SECOND_START
|
||||
// Finalize hash in HR
|
||||
mov hr ar
|
||||
and HASH_MASK
|
||||
mov ar asr
|
||||
mov pm hr
|
||||
mov pm asr
|
||||
|
||||
mov pm pc; mov pm ar
|
||||
incpc; sub hr
|
||||
mov pc pm
|
||||
|
||||
mov ar lc
|
||||
|
||||
mov hr pc
|
||||
|
||||
// Get the value we hashed
|
||||
mov ir asr
|
||||
mov pm gr
|
||||
|
||||
// Second insertion
|
||||
$DANK_INSERT_SECOND_LOOP
|
||||
incpc; bls @DANK_INSERT_SECOND_END_BIGGEST
|
||||
|
||||
mov pc asr
|
||||
mov pm ar
|
||||
sub gr
|
||||
adn gr; brn @DANK_INSERT_SECOND_BOTTOM
|
||||
|
||||
mov gr pm; incpc
|
||||
|
||||
$DANK_INSERT_SECOND_SHIFT
|
||||
mov pc asr; bls @DANK_INSERT_SECOND_END_NUMLET
|
||||
mov pm gr
|
||||
mov ar pm
|
||||
mov gr ar; declc; incpc; bra @DANK_INSERT_SECOND_SHIFT
|
||||
|
||||
$DANK_INSERT_SECOND_BOTTOM
|
||||
declc; bra @DANK_INSERT_SECOND_LOOP
|
||||
|
||||
$DANK_INSERT_SECOND_END_BIGGEST
|
||||
mov pc asr
|
||||
mov gr pm
|
||||
|
||||
$DANK_INSERT_SECOND_END_NUMLET
|
||||
|
||||
mov ir ar; mov ir pc
|
||||
sub 0xFE
|
||||
brz @BUCKET_SORT_END; incpc
|
||||
bra @DANK_SORT; incpc
|
||||
|
||||
|
||||
$BUCKET_SORT_END
|
||||
|
||||
|
||||
|
||||
// New merge-algo. Saves about 100 cycles over previous version
|
||||
const LAST_BUCKET_START_ADDR
|
||||
mov ar hr
|
||||
const FIRST_BUCKET_ADDRESS
|
||||
mov ar pc
|
||||
const LIST_START
|
||||
|
||||
$MERGE
|
||||
mov pc ir
|
||||
mov ar gr
|
||||
mov pc asr; incpc
|
||||
mov pm ar
|
||||
sub ir
|
||||
mov ar lc
|
||||
mov gr ar
|
||||
$MERGE_MOVE
|
||||
mov pc asr; bls @MERGE_BOTTOM
|
||||
mov pm gr
|
||||
mov ar asr
|
||||
add 1
|
||||
mov gr pm; declc; incpc; bra @MERGE_MOVE
|
||||
|
||||
$MERGE_BOTTOM
|
||||
mov ar gr
|
||||
mov ir ar
|
||||
sub hr
|
||||
adn hr; brz @MERGE_END
|
||||
add BUCKET_SIZE
|
||||
mov ar pc
|
||||
mov gr ar; bra @MERGE
|
||||
|
||||
$MERGE_END
|
||||
$BREAK
|
||||
halt
|
||||
|
||||
|
||||
|
||||
// Old merge-algo
|
||||
|
||||
// Set PC to write index
|
||||
//const 0xE0
|
||||
//mov ar pc
|
||||
//
|
||||
//
|
||||
//// ######## INSERTION ##########
|
||||
//
|
||||
//// COPY NEGATIVE
|
||||
//
|
||||
//// Initialize GR as bucket pointer and IR as element pointer
|
||||
//const FIRST_BUCKET_ADDRESS
|
||||
//mov ar gr
|
||||
//
|
||||
//
|
||||
//$LOOP
|
||||
//// Dereference GR into IR and compute length
|
||||
//mov gr asr
|
||||
//mov pm ar
|
||||
//
|
||||
//// Subtract start from end and save (the resultant length) into LC
|
||||
//sub gr
|
||||
//mov ar lc
|
||||
//
|
||||
//// Calcuate the first element of the array and store in IR
|
||||
//mov gr ar
|
||||
//add 1
|
||||
//mov ar ir
|
||||
//
|
||||
//$COPY_BUCKET
|
||||
//bls @NEXT_BUCKET
|
||||
//
|
||||
//mov ir asr; mov ir ar
|
||||
//mov pm hr
|
||||
//
|
||||
//add 1
|
||||
//mov ar ir
|
||||
//
|
||||
//// Write value to be copied into write index (data[pc] = data[ir])
|
||||
//mov pc asr
|
||||
//mov hr pm; incpc; declc
|
||||
//bra @COPY_BUCKET
|
||||
//$NEXT_BUCKET
|
||||
//
|
||||
//
|
||||
//mov gr ar
|
||||
//add BUCKET_SIZE
|
||||
//mov ar gr
|
||||
//sub LAST_BUCKET_ADDRESS
|
||||
//bnz @LOOP
|
||||
//
|
||||
//
|
||||
//$BREAK
|
||||
//halt
|
267
ucode/bsrt2.uc
Normal file
267
ucode/bsrt2.uc
Normal file
@ -0,0 +1,267 @@
|
||||
#define LIST_START 0xE0
|
||||
#define HASH_MASK 0b1111
|
||||
#define BUCKET_SIZE 14
|
||||
#define BUCKET_ADDRESS 0x00
|
||||
#define LAST_BUCKET_ADDRESS 0xE0
|
||||
#define LAST_BUCKET_START_ADDR 0xD2
|
||||
|
||||
|
||||
#optable 0x0 @OT_0
|
||||
#optable 0x1 @OT_1
|
||||
#optable 0x2 @OT_2
|
||||
#optable 0x3 @OT_3
|
||||
#optable 0x4 @OT_4
|
||||
#optable 0x5 @OT_5
|
||||
#optable 0x6 @OT_6
|
||||
#optable 0x7 @OT_7
|
||||
#optable 0x8 @OT_8
|
||||
#optable 0x9 @OT_9
|
||||
#optable 0xa @OT_A
|
||||
#optable 0xb @OT_B
|
||||
#optable 0xc @OT_C
|
||||
#optable 0xd @OT_D
|
||||
#optable 0xe @OT_E
|
||||
#optable 0xf @OT_F
|
||||
|
||||
// PM initial state
|
||||
#data 0x00 0x00
|
||||
#data 0x0E 0x00
|
||||
#data 0x1C 0x00
|
||||
#data 0x2A 0x00
|
||||
#data 0x38 0x00
|
||||
#data 0x46 0x00
|
||||
#data 0x54 0x00
|
||||
#data 0x62 0x00
|
||||
#data 0x70 0x00
|
||||
#data 0x7e 0x00
|
||||
#data 0x8c 0x00
|
||||
#data 0x9a 0x00
|
||||
#data 0xa8 0x00
|
||||
#data 0xb6 0x00
|
||||
#data 0xc4 0x00
|
||||
#data 0xd2 0x00
|
||||
|
||||
|
||||
// TODO: Include register initial-state compiler directive
|
||||
// (saves, like, 2 cycles max)
|
||||
// Set PC to LIST_START
|
||||
const LIST_START
|
||||
mov ar pc
|
||||
|
||||
|
||||
//// ---- START OF DANK SORT ---- ////
|
||||
$SORT
|
||||
|
||||
mov pc asr; incpc
|
||||
mov pm ir; call @JTABLE // Call jumptable. This also copies PC to HR
|
||||
|
||||
// We return here after visiting jump table
|
||||
// AR contains a magic value and ASR contains the same value
|
||||
mov pm lc; mov pm pc
|
||||
|
||||
// Move
|
||||
incpc
|
||||
mov pc pm
|
||||
mov ar pc
|
||||
|
||||
|
||||
// First insertion
|
||||
$INSERT_START
|
||||
|
||||
// Load a value and compare with value to be inserted
|
||||
// If L is set, value is greater than all other values in bucket
|
||||
incpc
|
||||
mov pc asr; bls @INSERT_END_BIGGEST
|
||||
mov pm ar
|
||||
sub ir
|
||||
adn ir; brn @INSERT_BOTTOM
|
||||
|
||||
// We found where to insert value. Insert it
|
||||
mov ir pm; incpc
|
||||
|
||||
// Shift values after the value we inserted
|
||||
$INSERT_SHIFT
|
||||
mov pc asr; bls @INSERT_END_NUMLET
|
||||
mov pm gr
|
||||
mov ar pm
|
||||
mov gr ar; declc; incpc; bra @INSERT_SHIFT
|
||||
|
||||
// Continue to next value in bucket for comparison
|
||||
$INSERT_BOTTOM
|
||||
declc; bra @INSERT_START
|
||||
|
||||
// Value we are inserting was the biggest value in the bucket
|
||||
$INSERT_END_BIGGEST
|
||||
mov ir pm
|
||||
|
||||
$INSERT_END_NUMLET
|
||||
|
||||
// Check if we've sorted all values. If we have, merge then,
|
||||
// otherwise return to top of bucketsort
|
||||
mov hr ar; mov hr pc
|
||||
sub 0
|
||||
bnz @SORT
|
||||
|
||||
|
||||
$BUCKET_SORT_END
|
||||
|
||||
|
||||
const LAST_BUCKET_START_ADDR
|
||||
mov ar hr
|
||||
const BUCKET_ADDRESS
|
||||
mov ar pc
|
||||
const LIST_START
|
||||
|
||||
$MERGE
|
||||
mov pc ir
|
||||
mov pc asr; incpc
|
||||
mov pm lc
|
||||
|
||||
$MERGE_MOVE
|
||||
mov pc asr; bls @MERGE_BOTTOM
|
||||
mov pm gr
|
||||
mov ar asr
|
||||
add 1
|
||||
mov gr pm; declc; incpc; bra @MERGE_MOVE
|
||||
|
||||
$MERGE_BOTTOM
|
||||
mov ar gr
|
||||
mov ir ar
|
||||
sub hr
|
||||
adn hr; brz @PROGRAM_END
|
||||
adn BUCKET_SIZE
|
||||
mov ar pc
|
||||
mov gr ar; bra @MERGE
|
||||
|
||||
|
||||
$PROGRAM_END
|
||||
$BREAK
|
||||
halt
|
||||
|
||||
|
||||
// Jump-table subroutine
|
||||
$JTABLE
|
||||
mov pc hr; bop
|
||||
|
||||
// IR OP-field jump table
|
||||
$OT_0
|
||||
const 0x70
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_1
|
||||
const 0x7e
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_2
|
||||
const 0x8c
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_3
|
||||
const 0x9a
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_4
|
||||
const 0xa8
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_5
|
||||
const 0xb6
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_6
|
||||
const 0xc4
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_7
|
||||
const 0xd2
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_8
|
||||
const 0x00
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_9
|
||||
const 0x0e
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_A
|
||||
const 0x1c
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_B
|
||||
const 0x2a
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_C
|
||||
const 0x38
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_D
|
||||
const 0x46
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_E
|
||||
const 0x54
|
||||
mov ar asr; ret
|
||||
|
||||
$OT_F
|
||||
const 0x62
|
||||
mov ar asr; ret
|
||||
|
||||
|
||||
|
||||
// Old merge-algo
|
||||
|
||||
// Set PC to write index
|
||||
//const 0xE0
|
||||
//mov ar pc
|
||||
//
|
||||
//
|
||||
//// ######## INSERTION ##########
|
||||
//
|
||||
//// COPY NEGATIVE
|
||||
//
|
||||
//// Initialize GR as bucket pointer and IR as element pointer
|
||||
//const BUCKET_ADDRESS
|
||||
//mov ar gr
|
||||
//
|
||||
//
|
||||
//$LOOP
|
||||
//// Dereference GR into IR and compute length
|
||||
//mov gr asr
|
||||
//mov pm ar
|
||||
//
|
||||
//// Subtract start from end and save (the resultant length) into LC
|
||||
//sub gr
|
||||
//mov ar lc
|
||||
//
|
||||
//// Calcuate the first element of the array and store in IR
|
||||
//mov gr ar
|
||||
//add 1
|
||||
//mov ar ir
|
||||
//
|
||||
//$COPY_BUCKET
|
||||
//bls @NEXT_BUCKET
|
||||
//
|
||||
//mov ir asr; mov ir ar
|
||||
//mov pm hr
|
||||
//
|
||||
//add 1
|
||||
//mov ar ir
|
||||
//
|
||||
//// Write value to be copied into write index (data[pc] = data[ir])
|
||||
//mov pc asr
|
||||
//mov hr pm; incpc; declc
|
||||
//bra @COPY_BUCKET
|
||||
//$NEXT_BUCKET
|
||||
//
|
||||
//
|
||||
//mov gr ar
|
||||
//add BUCKET_SIZE
|
||||
//mov ar gr
|
||||
//sub LAST_BUCKET_ADDRESS
|
||||
//bnz @LOOP
|
||||
//
|
||||
//
|
||||
//$BREAK
|
||||
//halt
|
154
ucode/bucksort.uc
Normal file
154
ucode/bucksort.uc
Normal file
@ -0,0 +1,154 @@
|
||||
#define BUCKET_SIZE 0b1000
|
||||
#define BUCKET_COUNT 15
|
||||
#define LIST_START 0xE0
|
||||
#define HASH_MASK 0b1111000
|
||||
|
||||
#data 0x00 0x00
|
||||
#data 0x08 0x08
|
||||
#data 0x10 0x10
|
||||
#data 0x18 0x18
|
||||
#data 0x20 0x20
|
||||
#data 0x28 0x28
|
||||
#data 0x30 0x30
|
||||
#data 0x38 0x38
|
||||
#data 0x40 0x40
|
||||
#data 0x48 0x48
|
||||
#data 0x50 0x50
|
||||
#data 0x58 0x58
|
||||
#data 0x60 0x60
|
||||
#data 0x68 0x68
|
||||
#data 0x70 0x70
|
||||
|
||||
|
||||
//// FOR TESTING PURPOSES ONLY ////
|
||||
|
||||
// TODO: Buckets store last index instead of length.
|
||||
|
||||
//// ---- Bucket sort ----
|
||||
|
||||
// Set PC to E0
|
||||
const LIST_START
|
||||
mov ar pc
|
||||
|
||||
$MOVE_TO_BUCKETS
|
||||
mov pc asr; mov pc ar;
|
||||
add pc; incpc;
|
||||
brz @MOVE_TO_BUCKETS_END
|
||||
mov pm ar; mov pm gr
|
||||
|
||||
// The hash
|
||||
rol
|
||||
rol
|
||||
rol
|
||||
rol
|
||||
rol
|
||||
rol
|
||||
rol
|
||||
and HASH_MASK
|
||||
|
||||
// Push PC
|
||||
mov pc ir
|
||||
|
||||
// Set current address to the hash.
|
||||
mov ar asr
|
||||
mov ar pc
|
||||
|
||||
// Increase length
|
||||
// Compare loop
|
||||
// if a < b : keep looping
|
||||
// else: swap a and b and shift the rest one step
|
||||
// if at end, insert value and return.
|
||||
|
||||
|
||||
// Length of loop, and set up PC for first list index
|
||||
const 1
|
||||
add pm; incpc
|
||||
mov ar pm
|
||||
sub pc
|
||||
// Set loop counter
|
||||
mov ar lc
|
||||
|
||||
$COMPARE_LOOP
|
||||
bls @INSERT_LAST_ELEMENT
|
||||
mov pc asr; incpc; declc
|
||||
|
||||
// TODO: Make sure this is optimal
|
||||
mov pm ar
|
||||
sub gr
|
||||
brn @COMPARE_LOOP
|
||||
add gr; mov gr pm
|
||||
|
||||
mov pc asr; incpc; declc
|
||||
// call @BREAK
|
||||
|
||||
$INSERT_LAST_ELEMENT
|
||||
mov gr pm
|
||||
|
||||
// Pop PC
|
||||
mov ir pc
|
||||
bra @MOVE_TO_BUCKETS
|
||||
|
||||
// mov ar asr
|
||||
// mov pc ir // Push PC
|
||||
// mov pm pc; add pm
|
||||
// incpc
|
||||
// mov pc pm
|
||||
// mov ar asr
|
||||
// mov gr pm
|
||||
// mov ir pc // Pop PC
|
||||
// bra @MOVE_TO_BUCKETS
|
||||
|
||||
$MOVE_TO_BUCKETS_END
|
||||
halt
|
||||
|
||||
// //// ---- Bubble sort ----
|
||||
//
|
||||
//
|
||||
// $BUBBLE_YEET
|
||||
//
|
||||
// mov pm ir; incpc
|
||||
//
|
||||
// mov pc asr
|
||||
//
|
||||
//
|
||||
// $SORT_LOOP
|
||||
// mov pc hr // Current lowest index
|
||||
// mov pm gr // Current lowest value
|
||||
//
|
||||
// reset asr
|
||||
// mov pc pm
|
||||
//
|
||||
// $LOOP
|
||||
// mov pc asr
|
||||
// mov gr ar
|
||||
// sub pm
|
||||
//
|
||||
// // If PM < GR
|
||||
// brn @CHECK
|
||||
// mov pc hr // Current lowest index
|
||||
// mov pm gr // Current lowest value
|
||||
//
|
||||
// $CHECK
|
||||
// incpc
|
||||
// mov pc ar
|
||||
// sub ir
|
||||
// bnz @LOOP
|
||||
//
|
||||
// reset asr
|
||||
// mov pm pc
|
||||
// mov pm asr; incpc
|
||||
// mov pm ar
|
||||
// mov gr pm
|
||||
// mov hr asr
|
||||
// mov ar pm
|
||||
// reset asr
|
||||
// mov pc pm
|
||||
// declc
|
||||
// bls @END_SORT
|
||||
// bra @SORT_LOOP
|
||||
//
|
||||
// $END_SORT
|
||||
// halt
|
||||
|
||||
$BREAK
|
||||
halt
|
219
ucode/sort2.uc
Normal file
219
ucode/sort2.uc
Normal file
@ -0,0 +1,219 @@
|
||||
// Hi! If you're reading this, you probably want to know what this program does.
|
||||
// To that, I say: Good luck and I hope you have patience and a strong will to
|
||||
// live, 'cause both negatively impacted by trying to read the code below.
|
||||
// This isn't a joke; I feel like someone telling a person to get off the edge
|
||||
// of a tall building here: reading the code below WILL negatively impact your
|
||||
// life. You have been warned.
|
||||
|
||||
#define LIST_START 0xE0
|
||||
#define LIST_END 0xFE
|
||||
#define LIST_START_MODIFIED 0xDF
|
||||
#define LIST_END_MODIFIED 0xFD
|
||||
#define HASH_MASK 0b1111000
|
||||
#define NEGATIVE_START 0x40
|
||||
#define NEGATIVE_END 0x78
|
||||
#define POSITIVE_START 0x00
|
||||
#define POSITIVE_END 0x38
|
||||
#define BUCKET_SIZE 8
|
||||
#define BUCKET_INDEX_TRACKER 0b11111000
|
||||
|
||||
|
||||
// Set up buckets (with absolute sizes)
|
||||
// Bucket count: 16
|
||||
// Bucket size: 8
|
||||
// Motivation: direct mapping from hash to bucket at the cost of bucket size (-6 per bucket)
|
||||
#data 0x00 0x00 // 0 (POSITIVE)
|
||||
#data 0x08 0x00 // 1 (POSITIVE)
|
||||
#data 0x10 0x00 // 2 (POSITIVE)
|
||||
#data 0x18 0x00 // 3 (POSITIVE)
|
||||
#data 0x20 0x00 // 4 (POSITIVE)
|
||||
#data 0x28 0x00 // 5 (POSITIVE)
|
||||
#data 0x30 0x00 // 6 (POSITIVE)
|
||||
#data 0x38 0x00 // 7 (POSITIVE)
|
||||
#data 0x40 0x00 // 8 (NEGATIVE)
|
||||
#data 0x48 0x00 // 9 (NEGATIVE)
|
||||
#data 0x50 0x00 // A (NEGATIVE)
|
||||
#data 0x58 0x00 // B (NEGATIVE)
|
||||
#data 0x60 0x00 // C (NEGATIVE)
|
||||
#data 0x68 0x00 // D (NEGATIVE)
|
||||
#data 0x70 0x00 // E (NEGATIVE)
|
||||
#data 0x78 0x00 // F (NEGATIVE)
|
||||
|
||||
|
||||
// Shift list down by one address, freeing up 0xFF for random-access use
|
||||
reset asr
|
||||
mov pm hr
|
||||
|
||||
const LIST_START_MODIFIED
|
||||
mov ar asr
|
||||
mov hr pm
|
||||
|
||||
// Initialize PC to point at list
|
||||
//const LIST_START
|
||||
mov ar pc
|
||||
|
||||
|
||||
$BUCKET_SORT_START
|
||||
|
||||
// AR = PM[PC]
|
||||
// HR = PM[PC + 1]
|
||||
mov pc asr
|
||||
mov pc ir; incpc
|
||||
mov pm ar
|
||||
mov pc asr
|
||||
mov pm hr
|
||||
|
||||
// Hash HR, partially hash AR.
|
||||
// Result of HR-hash is in AR
|
||||
// Result of AR-hash is in HR
|
||||
irl; mov ar gr // PM[0xFF] = AR
|
||||
irl; reset asr
|
||||
irl; mov gr pm
|
||||
irl; mov pc asr // Set GR to pre-hash value of HR
|
||||
irl; mov pm gr
|
||||
irl
|
||||
irl
|
||||
and HASH_MASK
|
||||
|
||||
mov ar asr
|
||||
mov pm lc; mov pm pc
|
||||
incpc
|
||||
mov pc pm
|
||||
mov ar pc
|
||||
|
||||
$FIRST_INSERTION
|
||||
|
||||
incpc; bls @FIRST_INSERTION_END_BIGGEST
|
||||
|
||||
mov pc asr
|
||||
mov pm ar
|
||||
sub gr
|
||||
adn gr; brn @FIRST_INSERTION_BOTTOM
|
||||
|
||||
mov gr pm; incpc
|
||||
|
||||
$FIRST_INSERTION_SHIFT
|
||||
mov pc asr; bls @FIRST_INSERTION_END_NOTBIGGEST
|
||||
mov pm gr
|
||||
mov ar pm
|
||||
mov gr ar; declc; incpc; bra @FIRST_INSERTION_SHIFT
|
||||
|
||||
$FIRST_INSERTION_BOTTOM
|
||||
declc; bra @FIRST_INSERTION
|
||||
|
||||
$FIRST_INSERTION_END_BIGGEST
|
||||
mov pc asr
|
||||
mov gr pm
|
||||
|
||||
$FIRST_INSERTION_END_NOTBIGGEST
|
||||
|
||||
// Prepare second insertion (minimal bookkeeping)
|
||||
mov hr ar
|
||||
and HASH_MASK
|
||||
mov ar asr
|
||||
mov pm pc; mov pm lc
|
||||
incpc; reset asr
|
||||
mov pm gr
|
||||
mov ar asr
|
||||
mov pc pm
|
||||
mov ar pc
|
||||
|
||||
|
||||
$SECOND_INSERTION
|
||||
|
||||
incpc; bls @SECOND_INSERTION_END_BIGGEST
|
||||
|
||||
mov pc asr
|
||||
mov pm ar
|
||||
sub gr
|
||||
adn gr; brn @SECOND_INSERTION_BOTTOM
|
||||
|
||||
mov gr pm; incpc
|
||||
|
||||
$SECOND_INSERTION_SHIFT
|
||||
mov pc asr; bls @SECOND_INSERTION_END_NOTBIGGEST
|
||||
mov pm gr
|
||||
mov ar pm
|
||||
mov gr ar; declc; incpc; bra @SECOND_INSERTION_SHIFT
|
||||
|
||||
$SECOND_INSERTION_BOTTOM
|
||||
declc; bra @SECOND_INSERTION
|
||||
|
||||
$SECOND_INSERTION_END_BIGGEST
|
||||
mov pc asr
|
||||
mov gr pm
|
||||
|
||||
$SECOND_INSERTION_END_NOTBIGGEST
|
||||
mov ir ar; mov ir pc
|
||||
sub LIST_END_MODIFIED
|
||||
brz @BUCKET_SORT_END; incpc
|
||||
bra @BUCKET_SORT_START; incpc
|
||||
|
||||
$BUCKET_SORT_END
|
||||
|
||||
|
||||
//call @BREAK
|
||||
|
||||
|
||||
// Merge negative values
|
||||
const NEGATIVE_END
|
||||
mov ar hr
|
||||
const NEGATIVE_START
|
||||
mov ar pc
|
||||
const LIST_START
|
||||
|
||||
$NEGATIVE_MERGE
|
||||
mov pc ir
|
||||
mov pc asr; incpc
|
||||
mov pm lc
|
||||
$NEGATIVE_MERGE_MOVE
|
||||
mov pc asr; bls @NEGATIVE_MERGE_BOTTOM
|
||||
mov pm gr
|
||||
mov ar asr
|
||||
add 1
|
||||
mov gr pm; declc; incpc; bra @NEGATIVE_MERGE_MOVE
|
||||
|
||||
$NEGATIVE_MERGE_BOTTOM
|
||||
mov ar gr
|
||||
mov ir ar
|
||||
sub hr
|
||||
adn hr; brz @POSITIVE_MERGE_INIT
|
||||
add BUCKET_SIZE
|
||||
mov ar pc
|
||||
mov gr ar; bra @NEGATIVE_MERGE
|
||||
|
||||
|
||||
|
||||
|
||||
// Merge positive values
|
||||
$POSITIVE_MERGE_INIT
|
||||
const POSITIVE_END
|
||||
mov ar hr
|
||||
const POSITIVE_START
|
||||
mov ar pc
|
||||
mov gr ar // Pop AR
|
||||
|
||||
$POSITIVE_MERGE
|
||||
mov pc asr; incpc
|
||||
mov pm lc
|
||||
$POSITIVE_MERGE_MOVE
|
||||
mov pc asr; bls @POSITIVE_MERGE_BOTTOM
|
||||
mov pm gr
|
||||
mov ar asr
|
||||
add 1
|
||||
mov gr pm; declc; incpc; bra @POSITIVE_MERGE_MOVE
|
||||
|
||||
$POSITIVE_MERGE_BOTTOM
|
||||
mov ar gr
|
||||
mov pc ar
|
||||
and BUCKET_INDEX_TRACKER
|
||||
sub hr
|
||||
adn hr; brz @PROGRAM_END
|
||||
adn BUCKET_SIZE
|
||||
mov ar pc
|
||||
mov gr ar; bra @POSITIVE_MERGE
|
||||
|
||||
|
||||
$PROGRAM_END
|
||||
$BREAK
|
||||
halt
|
119
ucode/sort3.uc
Normal file
119
ucode/sort3.uc
Normal file
@ -0,0 +1,119 @@
|
||||
#optable 0x0 @BREAK
|
||||
#optable 0x1 @BREAK
|
||||
#optable 0x2 @BREAK
|
||||
#optable 0x3 @BREAK
|
||||
#optable 0x4 @BREAK
|
||||
#optable 0x5 @BREAK
|
||||
#optable 0x6 @BREAK
|
||||
#optable 0x7 @BREAK
|
||||
#optable 0x9 @BREAK
|
||||
#optable 0xa @BREAK
|
||||
#optable 0xb @BREAK
|
||||
#optable 0xc @BREAK
|
||||
#optable 0xd @BREAK
|
||||
#optable 0xe @BREAK
|
||||
#optable 0xf @BREAK
|
||||
|
||||
|
||||
#define LIST_START 0xE0
|
||||
#define HASH_MASK 0b1111
|
||||
#define FIRST_BUCKET 0x10 // Index of start of first bucket
|
||||
#define LAST_BUCKET 0xD3 // Index of start of last bucket
|
||||
#define BUCKET_SIZE 13 // Each bucket an hold 13 elements before spilling
|
||||
|
||||
// LUT entries contain pointers to last element in bucket
|
||||
// This allows the LUT to serve as both a jump-table and as bucket-headers
|
||||
// Look at me being all resourceful and stuff, huh? Inb4 slowdowns ;))
|
||||
#data 0x00 0x77
|
||||
#data 0x01 0x84
|
||||
#data 0x02 0x91
|
||||
#data 0x03 0x9e
|
||||
#data 0x04 0xab
|
||||
#data 0x05 0xb8
|
||||
#data 0x06 0xc5
|
||||
#data 0x07 0xd2
|
||||
#data 0x08 0x0f
|
||||
#data 0x09 0x1c
|
||||
#data 0x0a 0x29
|
||||
#data 0x0b 0x36
|
||||
#data 0x0c 0x43
|
||||
#data 0x0d 0x50
|
||||
#data 0x0e 0x5d
|
||||
#data 0x0f 0x6a
|
||||
|
||||
const LIST_START
|
||||
mov ar pc
|
||||
|
||||
// Two values sorted per iteration, so half the iterations obv ;)
|
||||
// For consistency, we just decrement LC twice per iteration, though
|
||||
// Had this course also stressed power use or other efficiency-related questions,
|
||||
// we probably wouldn't decrement twice per iteration for the sole purpose of "clarity"
|
||||
// But alas, Kent doesn't care, so why should we?
|
||||
lcset 32
|
||||
|
||||
$BUCKET_SORT
|
||||
bls @MERGE_INIT
|
||||
|
||||
mov pc asr; incpc
|
||||
mov pm ar; mov pm ir // Use whatever value we're sorting to index GR at complete random
|
||||
mov pc asr; incpc
|
||||
mov pm hr
|
||||
|
||||
// Shift AR and HR
|
||||
irl; mov pm gr
|
||||
irl
|
||||
irl
|
||||
irl
|
||||
and HASH_MASK // Completely hash value from HR
|
||||
mov ar asr
|
||||
mov pm ar
|
||||
add 1
|
||||
mov ar pm
|
||||
mov ar asr
|
||||
mov gr pm; declc // Mark element as sorted by decrementing LC
|
||||
|
||||
mov hr ar
|
||||
and HASH_MASK // Completely hash value from AR
|
||||
mov ar asr
|
||||
mov pm ar
|
||||
add 1
|
||||
mov ar pm
|
||||
mov ar asr
|
||||
mov ir pm; declc // Mark element as sorted by decrementing LC
|
||||
|
||||
bra @BUCKET_SORT
|
||||
|
||||
|
||||
//// ---- MERGE ---- ////
|
||||
$MERGE_INIT
|
||||
|
||||
// HR points to LUT
|
||||
// PC points to start of first bucket
|
||||
// AR points to list
|
||||
mov pc hr // PC is always 0 after bucketsort because FF+1 mod 100 = 0. Imagine that!
|
||||
const FIRST_BUCKET
|
||||
mov ar pc
|
||||
const LIST_START
|
||||
|
||||
$MERGE
|
||||
mov ar ir
|
||||
mov hr asr
|
||||
sub pm
|
||||
mov pc gr
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// If this was not the last bucket, branch back to start of merge
|
||||
sub LAST_BUCKET
|
||||
adn LAST_BUCKET
|
||||
bnz @MERGE
|
||||
|
||||
|
||||
$END
|
||||
|
||||
$BREAK
|
||||
halt
|
148
ucode/sort4.uc
Normal file
148
ucode/sort4.uc
Normal file
@ -0,0 +1,148 @@
|
||||
// Hi! If you're reading this, you probably want to know what this program does
|
||||
// To that, I say: Good luck and I hope you have patience and a strong will to
|
||||
// live, 'cause both will be negatively impacted by trying to read the code
|
||||
// below.
|
||||
// This isn't a joke; I feel like someone telling a person to get off the edge
|
||||
// of a tall building here: reading the code below WILL negatively impact your
|
||||
// life. You have been warned.
|
||||
|
||||
// For clarity's sake, I thought I'd add a small preface here:
|
||||
// We use the optable as a replacement for a hashing algorithm. Check the
|
||||
// #optable directives if you're wondering what bucket the 4 highest bits of a
|
||||
// value end up pointing to. Each entry in the optable simply loads the
|
||||
// address of the corresponding bucket and initiates an insertion sort for said
|
||||
// bucket.
|
||||
// Labels ending in "_SPEC" are edge-case optimization labels. They are
|
||||
// branched to in the case of special edge cases with the idea being that they
|
||||
// are much faster than following the reguar code-path (even if said path would
|
||||
// ultimately perform the same action).
|
||||
// For example, "call @JTABLE_SPEC" invokes a special jumptable subroutine
|
||||
// which parallelizes some of the boilerplate needed for the upcoming merge.
|
||||
|
||||
|
||||
#define LIST_START 0xE0
|
||||
#define HIGHEST_BUCKET 0xD2
|
||||
|
||||
// Generate jump-table ;)
|
||||
#emit
|
||||
>for i in range(16): print("#optable "+hex(i)+" @OT_"+hex(i)[2::])
|
||||
|
||||
// Generate program memory:
|
||||
// Addresses 0xE0-0xFF ignored
|
||||
// Addresses that are a multiple of 0xE are set to 0
|
||||
// All other addresses contain a relative pointer to the next bucket start
|
||||
#pmgen if address < 0xE0: print(str(address)+" "+str(0 if (address % 14) == 0 else ((address - (address % 0xE)) - 0xE) if address > 0xD else -1))
|
||||
|
||||
|
||||
// Set all GR-registers to -1, so that we always can call "sub gr" to increment
|
||||
// AR, no matter what value we have in IR ;)
|
||||
const 0xB00 // GRx=10, M=11
|
||||
lsr; mov ar ir
|
||||
lsr; reset gr
|
||||
lsr; reset grm
|
||||
mov ar ir // GRx=00, M=01
|
||||
reset gr
|
||||
reset grm
|
||||
|
||||
|
||||
// Initialize PC to point at list
|
||||
const LIST_START
|
||||
mov ar pc
|
||||
mov pc asr
|
||||
|
||||
// Perform bucketsort for 32 elements
|
||||
#emit
|
||||
>for i in range(31):
|
||||
> print("mov pm ir; incpc; call @JTABLE")
|
||||
mov pm ir; call @JTABLE_SPEC
|
||||
|
||||
|
||||
// Initialize state for merge
|
||||
const HIGHEST_BUCKET
|
||||
mov ar asr
|
||||
mov pm lc
|
||||
sub gr
|
||||
|
||||
// Copy elements to list
|
||||
$MERGE
|
||||
mov ar asr; declc; bls @MB_SPEC
|
||||
mov pm ir
|
||||
mov pc asr // This branch improves stability
|
||||
mov ir pm; incpc; bls @MERGE_BOTTOM // Transfer value and copy more elements
|
||||
sub gr; bra @MERGE
|
||||
|
||||
$MERGE_BOTTOM
|
||||
sub gr
|
||||
mov ar asr
|
||||
|
||||
$MB_SPEC
|
||||
mov pm ar; mov pm asr
|
||||
sub gr
|
||||
mov pm lc; bnz @MERGE
|
||||
|
||||
// Breakpoint hook and program termination point
|
||||
$BREAK
|
||||
$END
|
||||
halt
|
||||
|
||||
|
||||
|
||||
|
||||
// Jump-table subroutine
|
||||
$JTABLE
|
||||
mov pc hr; bop
|
||||
|
||||
$JTABLE_SPEC
|
||||
const LIST_START
|
||||
mov ar hr; bop
|
||||
|
||||
|
||||
// Generate jump table
|
||||
#emit
|
||||
>bucket_size=14
|
||||
>for i in range(16):
|
||||
> print("$OT_"+hex(i)[2::])
|
||||
> if i < 8:
|
||||
> print("const "+str(bucket_size*(7-i)))
|
||||
> else:
|
||||
> print("const "+str(bucket_size*(23-i)))
|
||||
> print("mov ar asr; bra @PREPARE_SORT")
|
||||
|
||||
|
||||
// Actual bucketsort
|
||||
$PREPARE_SORT
|
||||
mov pm pc; mov pm lc // Load bucket length
|
||||
sub gr; incpc // Increment bucket length
|
||||
mov pc pm; bls @IE_SPEC // Store new length
|
||||
|
||||
mov ar pc; sub ar
|
||||
sub ir // Save -IR into AR (you'll see why)
|
||||
|
||||
|
||||
$INSERTION
|
||||
mov pc asr; incpc; declc; bls @INSERTION_END_BIGGEST
|
||||
add pm // Effectively, AR=PM-IR, except more like AR=-IR+PM
|
||||
sub pm; brn @INSERTION
|
||||
|
||||
mov pm ar
|
||||
mov ir pm; bls @IEN_SPEC
|
||||
|
||||
$INSERTION_SHIFT
|
||||
mov pc asr
|
||||
mov pm ir
|
||||
mov ar pm; bls @INSERTION_END_NOTBIGGEST
|
||||
mov ir ar; declc; incpc; bra @INSERTION_SHIFT
|
||||
|
||||
$IEN_SPEC
|
||||
mov pc asr
|
||||
mov ar pm; bra @INSERTION_END_NOTBIGGEST
|
||||
|
||||
$IE_SPEC
|
||||
mov ar asr
|
||||
|
||||
$INSERTION_END_BIGGEST
|
||||
mov ir pm
|
||||
|
||||
$INSERTION_END_NOTBIGGEST
|
||||
mov hr pc
|
||||
mov pc asr; ret
|
31
ucode/test.uc
Normal file
31
ucode/test.uc
Normal file
@ -0,0 +1,31 @@
|
||||
#optable 0x0 @TABLE_0
|
||||
#optable 0x1 @TABLE_1
|
||||
#optable 0x2 @TABLE_2
|
||||
#optable 0x3 @TABLE_3
|
||||
|
||||
mov pm ir
|
||||
reset asr
|
||||
mov pm grm
|
||||
const 0
|
||||
mov ar asr
|
||||
mov pm gr
|
||||
bop
|
||||
|
||||
$TABLE_0
|
||||
const 1
|
||||
bra @END
|
||||
|
||||
$TABLE_1
|
||||
const 2
|
||||
bra @END
|
||||
|
||||
$TABLE_2
|
||||
const 3
|
||||
bra @END
|
||||
|
||||
$TABLE_3
|
||||
const 4
|
||||
|
||||
|
||||
$END
|
||||
halt
|
255
weaver.kt
Normal file
255
weaver.kt
Normal file
@ -0,0 +1,255 @@
|
||||
import java.io.*
|
||||
|
||||
class MachineState {
|
||||
val programMemory = ShortArray(256)// PM
|
||||
val microMemory = IntArray(128) // MyM
|
||||
val k1 = ByteArray(16) // K1
|
||||
val k2 = ByteArray(4) // K2
|
||||
var pc: Byte = 0.toByte() // PC
|
||||
var asr: Byte = 0.toByte() // ASR
|
||||
var ar: Short = 0.toShort() // AR
|
||||
var hr: Short = 0.toShort() // HR
|
||||
var gr0: Short = 0.toShort() // GR0
|
||||
var gr1: Short = 0.toShort() // GR0
|
||||
var gr2: Short = 0.toShort() // GR0
|
||||
var gr3: Short = 0.toShort() // GR0
|
||||
var ir: Byte = 0.toByte() // IR
|
||||
var uPC: Byte = 0.toByte() // MyPC
|
||||
var uSP: Byte = 0.toByte() // SMyPC
|
||||
var lc: Byte = 0.toByte() // LC
|
||||
|
||||
override fun toString() = toString(false)
|
||||
fun toString(verilog: Boolean): String {
|
||||
val builder = StringBuilder()
|
||||
if(!verilog) builder.append("PM:\n")
|
||||
for(index in 0 until programMemory.size){
|
||||
if(verilog) builder.append("initial PM[").append(index).append("] = 16'h")
|
||||
else builder.append(index.toUHex()).append(": ")
|
||||
builder.append(programMemory[index].toUHex())
|
||||
if(verilog) builder.append(';')
|
||||
builder.append("\n")
|
||||
}
|
||||
|
||||
if(!verilog) builder.append("\nMyM:\n")
|
||||
for(index in 0 until microMemory.size){
|
||||
if(verilog) builder.append("assign uPM[").append(index).append("] = 25'h")
|
||||
else builder.append(index.toUHex()).append(": ")
|
||||
builder.append(microMemory[index].toShortUHex())
|
||||
if(verilog) builder.append(';')
|
||||
builder.append("\n")
|
||||
}
|
||||
|
||||
if(!verilog) builder.append("\nK1:\n")
|
||||
for(index in 0 until k1.size){
|
||||
if(verilog) builder.append("assign K1[").append(index).append("] = 7'h")
|
||||
else builder.append(index.toUHex()).append(": ")
|
||||
builder.append(k1[index].toUHex())
|
||||
if(verilog) builder.append(';')
|
||||
builder.append("\n")
|
||||
}
|
||||
|
||||
if(!verilog) builder.append("\nK2:\n")
|
||||
for(index in 0 until k2.size){
|
||||
if(verilog) builder.append("assign K2[").append(index).append("] = 7'h")
|
||||
else builder.append(index.toUHex()).append(": ")
|
||||
builder.append(k2[index].toUHex())
|
||||
if(verilog) builder.append(';')
|
||||
builder.append("\n")
|
||||
}
|
||||
|
||||
fun StringBuilder.regSet(name: String, value: Short) =
|
||||
if(verilog) append("initial ").append(name).append(" = ").append(value).append(";\n")
|
||||
else append('\n').append(name).append(":\n").append(value.toUHex())
|
||||
|
||||
fun StringBuilder.regSet(name: String, value: Byte) =
|
||||
if(verilog) append("initial ").append(name).append(" = ").append(value).append(";\n")
|
||||
else append('\n').append(name).append(":\n").append(value.toUHex())
|
||||
|
||||
fun StringBuilder.grSet(index: Int, value: Short) =
|
||||
if(verilog) append("initial GR[").append(index).append("] = ").append(value).append(";\n")
|
||||
else regSet("GR"+index, value)
|
||||
|
||||
fun StringBuilder.irSet(value: Byte) =
|
||||
if(verilog) append("initial IR = ").append(value).append(";\n")
|
||||
else append("\nIR:\nb").append(value.toInt().or(1 shl 30).toString(2).substring(28))
|
||||
|
||||
fun StringBuilder.uPCSet(value: Byte) =
|
||||
if(verilog) append("initial uPC = ").append(value).append(";\n")
|
||||
else append("\n\nMyPC:\n").append(value.toUHex())
|
||||
|
||||
fun StringBuilder.uSPSet(value: Byte) =
|
||||
if(verilog) append("initial uSP = ").append(value).append(";\n")
|
||||
else append("\n\nSMyPC:\n").append(value.toUHex())
|
||||
|
||||
fun StringBuilder.flagInit() =
|
||||
if(verilog) this
|
||||
else append("\n\nO_flag:\n\nC_flag:\n\nN_flag:\n\nZ_flag:\n\nL_flag:\nEnd_of_dump_file")
|
||||
|
||||
return builder
|
||||
.regSet("PC", pc)
|
||||
.regSet("ASR", asr)
|
||||
.regSet("AR", ar)
|
||||
.regSet("HR", hr)
|
||||
.grSet(0, gr0)
|
||||
.grSet(1, gr1)
|
||||
.grSet(2, gr2)
|
||||
.grSet(3, gr3)
|
||||
.regSet("AR", ar)
|
||||
.irSet(ir)
|
||||
.uPCSet(uPC)
|
||||
.uSPSet(uSP)
|
||||
.append('\n').regSet("LC", lc)
|
||||
.flagInit()
|
||||
.toString()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun parseState(rawState: String): MachineState {
|
||||
val state = MachineState()
|
||||
val lines = rawState.replace("\r", "").split("\n").toTypedArray()
|
||||
|
||||
// Read PM
|
||||
for(index in 0 until 256)
|
||||
state.programMemory[index] = Integer.parseInt(lines[index + 1].substring(4), 16).toShort()
|
||||
|
||||
// Read MyM
|
||||
for(index in 0 until 128)
|
||||
state.microMemory[index] = Integer.parseInt(lines[index + 3 + 256].substring(4), 16)
|
||||
|
||||
// Read K1
|
||||
for(index in 0 until 16)
|
||||
state.k1[index] = Integer.parseInt(lines[index + 5 + 256 + 128].substring(4), 16).toByte()
|
||||
|
||||
// Read K2
|
||||
for(index in 0 until 4)
|
||||
state.k2[index] = Integer.parseInt(lines[index + 7 + 256 + 128 + 16].substring(4), 16).toByte()
|
||||
|
||||
state.pc = Integer.parseInt(lines[413], 16).toByte()
|
||||
state.asr = Integer.parseInt(lines[416], 16).toByte()
|
||||
state.ar = Integer.parseInt(lines[419], 16).toShort()
|
||||
state.hr = Integer.parseInt(lines[422], 16).toShort()
|
||||
state.gr0 = Integer.parseInt(lines[425], 16).toShort()
|
||||
state.gr1 = Integer.parseInt(lines[428], 16).toShort()
|
||||
state.gr2 = Integer.parseInt(lines[431], 16).toShort()
|
||||
state.gr3 = Integer.parseInt(lines[434], 16).toShort()
|
||||
state.ir = Integer.parseInt(lines[437].substring(1), 2).toByte()
|
||||
state.uPC = Integer.parseInt(lines[440], 16).toByte()
|
||||
state.uSP = Integer.parseInt(lines[443], 16).toByte()
|
||||
state.lc = Integer.parseInt(lines[446], 16).toByte()
|
||||
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun error(message: String){
|
||||
System.err.println(message)
|
||||
println("Usage:\n\tkotlin WeaverKt [machineCodeFile] [outputFile]\nor\n\tkotlin WeaverKt [machineCodeFile] [inputState] [outputFile]")
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
fun main(args: Array<String>){
|
||||
if(args.size > 4) error("Too many arguments!")
|
||||
if(args.size < 2) error("Too few arguments!")
|
||||
|
||||
val weaveFile = File(args[0])
|
||||
if(!weaveFile.isFile) error("Given machine code file doesn't exist!")
|
||||
|
||||
val state: MachineState
|
||||
|
||||
val verilogOutput = args[args.size - 1] == "-v"
|
||||
|
||||
if(args.size == 2 || (verilogOutput && args.size == 3)) state = MachineState()
|
||||
else{
|
||||
val file = File(args[1])
|
||||
if(file.isFile) state = MachineState.parseState(file.readText())
|
||||
else{
|
||||
System.err.println("Machine state file (${args[1]}) doesn't exist! Starting from scratch...")
|
||||
state = MachineState()
|
||||
}
|
||||
}
|
||||
|
||||
val weaveData = weaveFile.readText().replace("\r", "").split("\n").toTypedArray()
|
||||
var weaveUCode = false
|
||||
var pIndex = 0
|
||||
var uIndex = 0
|
||||
fun index()= if(weaveUCode) uIndex else pIndex
|
||||
fun iIdx() {
|
||||
if(weaveUCode) ++uIndex
|
||||
else ++pIndex
|
||||
}
|
||||
fun sIdx(value: Int){
|
||||
if(weaveUCode) uIndex = value
|
||||
else pIndex = value
|
||||
}
|
||||
for(rawValue in weaveData){
|
||||
val value = {
|
||||
var v = rawValue.replace(" ", "").replace("\t", "")
|
||||
|
||||
// Return
|
||||
if(v.indexOf("#") > 0) v.substring(0, v.indexOf("#")) else v
|
||||
}()
|
||||
|
||||
if(value.length == 0) continue
|
||||
else if(value.startsWith("@")){
|
||||
if(value == "@u") weaveUCode = true
|
||||
else if(value == "@p") weaveUCode = false
|
||||
else if(value.startsWith("@k1")){
|
||||
// Parse K1 table entry
|
||||
if(value.length != 6)
|
||||
error("Badly formatted K1 declaration: $rawValue")
|
||||
val index = value.substring(3, 4).toInt(16)
|
||||
val k1Value = value.substring(4, 6).toInt(16).toByte()
|
||||
if(k1Value < 0)
|
||||
error("Invalid K1 address pointer (must be in range 00-7F): $rawValue")
|
||||
|
||||
state.k1[index] = k1Value
|
||||
}
|
||||
else if(value.startsWith("@k2")){
|
||||
// Parse K2 table entry
|
||||
if(value.length != 6)
|
||||
error("Badly formatted K2 declaration: $rawValue")
|
||||
|
||||
val index = value.substring(3, 4).toInt(16)
|
||||
if(index > 4)
|
||||
error("Invalid K2 index value (must be in range 0-3): $rawValue")
|
||||
|
||||
|
||||
val k2Value = value.substring(4, 6).toInt(16).toByte()
|
||||
if(k2Value < 0)
|
||||
error("Invalid K2 address pointer (must be in range 00-7F): $rawValue")
|
||||
|
||||
state.k2[index] = k2Value
|
||||
}
|
||||
else if(value.startsWith("@0x")) sIdx(Integer.parseInt(value.substring(3), 16))
|
||||
else sIdx(Integer.parseInt(value.substring(1), 10))
|
||||
|
||||
continue
|
||||
}
|
||||
else if((weaveUCode && index() >= state.microMemory.size) || (!weaveUCode && index() >= state.programMemory.size)) error("Memory out of bounds: ${index()}! Did you pass too much data?")
|
||||
else if((weaveUCode && value.length != 7) || (!weaveUCode && value.length != 4)) error("Cannot weave data of bad length: $value")
|
||||
else try{
|
||||
if(weaveUCode) state.microMemory[index()] = Integer.parseInt(value, 16)
|
||||
else state.programMemory[index()] = Integer.parseInt(value, 16).toShort()
|
||||
}catch(e: NumberFormatException){
|
||||
error("Cannot weave non-hex data: $value")
|
||||
}
|
||||
iIdx()
|
||||
}
|
||||
|
||||
val outputFile = if(!verilogOutput && args.size == 3) File(args[2]) else File(args[1])
|
||||
if(outputFile.isFile) outputFile.delete()
|
||||
outputFile.createNewFile()
|
||||
|
||||
|
||||
val machineData = state.toString(verilogOutput)
|
||||
outputFile.bufferedWriter().use{
|
||||
it.write(machineData, 0, machineData.length)
|
||||
it.flush()
|
||||
}
|
||||
}
|
||||
|
||||
fun Short.toUHex() = toInt().and(0xFFFF.toInt()).or(1.shl(30)).toString(16).substring(4)
|
||||
fun Int.toUHex() = and(0xFF).or(1.shl(30)).toString(16).substring(6)
|
||||
fun Int.toShortUHex() = and((-1 ushr 7)).or(1.shl(30)).toString(16).substring(1)
|
||||
fun Byte.toUHex() = toInt().and(0xFF.toInt()).or(1.shl(30)).toString(16).substring(6)
|
Loading…
x
Reference in New Issue
Block a user