Commit 103b5351 by Jessica Hawkwell

Merge branch 'doc' into 'master'

Doc

See merge request !1
2 parents 502e0839 626d591d
Pipeline #224 passed
in 1 minute 13 seconds
# K Architecture
_Short for "Kitty"_ :smiley_cat:
> _Short for "Kitty"_ :smiley_cat:
The K architecture features a 16-bit CPU with 16-bit memory addressing, and a 32-bit math co-processor.
The instruction set is relatively simple, for details, see [Writing Kasm](doc/kasm/writing.md).
The instruction set is relatively simple, for details, see [Writing Kasm](doc/kasm/writing.md). For information on how
to use the Kasm assembler, see [Kasm Documentation](doc/kasm)
# Building the Emulator
......
......@@ -6,6 +6,11 @@ The assembler can be invoked with a simple command line argument:
java -jar target/kcpu-1.0-SNAPSHOT.jar -kasm
```
Alternatively, there is a script provided:
```
./kasm.sh
```
To view the available command line arguments:
```
./kasm.sh -h
......@@ -67,4 +72,4 @@ This command will compile the source files in the specified order and write out
# Writing Kasm Programs
Writing Kasm programs is a fairly involved process, as assembly language(s) generally are. For information on writing
Kasm programs, see [Writing Kasm](writing.md)
Kasm programs, see [Writing Kasm](doc/kasm/writing.md)
# Writing Kasm
The Kcpu instruction set is fairly simple, but that's not what this document covers. For the full instruction set,
see [Kcpu Instruction Set](doc/kcpu/opcodes.md). This document covers the syntax, aliases, and special keywords
recognized by the [Kasm assembler](doc/kasm/README.md).
## Basic Structure
The basic structure for every operation consists of the instruction, followed by up to four optional arguments.
Arguments are completely optional for the entire instruction set. For instructions which write data, the first argument
is **always** the destination register, memory address, or memory address stored in the register.
Generally, instructions look like this:
```
instruction arg0 arg1 arg2 arg3
```
Whenever an argument is omitted, the processor's default will be to use the register associated with that argument's
position. For example:
```kasm
add
add r0 r1
```
While Kasm will emit different binary code, both of these are functionally equivalent. They tell the processor to add
the value of register one (`r1`) to the value stored in register zero (`r0`).
## Data Location
There are four locations for which the processor can load data, and three where the processor can store data. Markers
are prefixed to each argument to tell the processor where to fetch the data required.
* For using the data given directly by the argument, either use no prefix _or_ prefix `d.`; Example: `16` or `d.16`
* To use data already stored in a register, either prefix `r` _or_ `r.`; Example: `r1` _or_ `r.1`
* To load data from a memory address defined by the argument, prefix `%` _or_ `ad.`; Example: `%128` or `ad.128`
* To load data from a memory address defined by the value of a register, prefix `$` _or_ `ar.`; Example: `$64` or `ar.64`
```kasm
add r2 ad.4096 // add the value stored at memory address 4096 to the value stored in register 2
```
When data must be stored, direct values (`d.`) are the same as registers (`r.`).
## Numbers
All numbers are decimal, by default. Optionally, you can define a radix using a comma (`,`). Currently, radix values
10 and 16 have been tested. Others should be available.[^1] Instead of defining a number using normal notation, such
as `0x0fac`, you should instead use `0fac,16`. You may use underscores to separate long numbers into a more readable
form, such as `0f_ac,16`.
## Special Keywords
Kasm recognizes three keywords which are not valid instructions. The keywords `base`, `fill`, and `mark` all have
different meanings and uses. Kasm keywords are always dereferenced before binary code is emitted. The table below
shows where each keyword may be used.
| | Used as instruction? | Used as value? |
|:------:|:--------------------:|:------------------:|
| `base` | :x: | :white_check_mark: |
| `fill` | :white_check_mark: | :x: |
| `mark` | :white_check_mark: | :white_check_mark: |
### `base`
The `base` keyword may be used as a value in any instruction. It stores the "base address value" specified on the
command line using the `-b` or `--base` command line argument. If none is specified, the default value will be zero.
### `fill`
`fill` requires at least one argument, and it can take an optional second argument. It is used to fill a region with
zeroes, or the specified value.
Usage: `fill length [value]`
The value will always be truncated to a single byte.
### `mark`
Perhaps the most useful of all, the `mark` keyword has a dual-purpose. When used as an instruction, `mark` will store
its current instruction location. Later, it may be used to load that location. A complete example is below:
```kasm
mark
add r0 1
add r3 16
mov r0 ar.3
jdle r0 f,16 mark
```
In this example, if the value in `r0` is less than or equal to `0x0f`, it will jump down to the marked location. `mark`
does not emit any code, and it is always used as a reference only. Additionally, `mark` never references a direct
memory address.
## Kasm Math
Kasm supports certain basic math operations on all arguments. Since arguments are separated by spaces, take care to
remove any spaces from the equation for a particular argument. The supported operations are:
* `+` add
* `-` subtract
* `*` multiply
* `/` divide
The Kcpu is very simple and cannot perform any math operations on any argument. Kasm will perform the math operations
itself and emit the results in the binary. All numbers are supported, with radix notations. Keyword-based values will
also be dereferenced. Kasm cannot (and probably will not) dereference a number intended to be loaded from a memory
address or register. Attempting to do may result in undefined behavior.
[^1]: Kasm's numberizer uses [Integer.valueOf(String s, int radix)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf-java.lang.String-int-)
# The K Architecture CPU
The K Architecture CPU (Kcpu) is a simplistic 16-bit processor with 16-bit memory addressing. The instruction length is
also 16-bits, and includes a 16-bit reference value and 4 general-purpose 16-bit registers. In addition, instructions
may have up to 4 argument positions, although most only have 1 or 2. Kcpu supports up to 32 total devices and up to 256
interrupt codes. Kcpu also contains 2 stacks - the data stack may contain up to 128 entries and
the call stack may contain up to 64 entries.
## Device Bus
The general device bus has 16 slots, with one port (port 0) typically occupied by the processor. The device bus allows
the system to access a device's built-in memory using `mov` and `movs` instructions. The processor can also send
interrupt codes to devices, and receive interrupt codes from devices. All devices on the device bus are also
memory devices, described below. Devices on the device bus can send and receive memory interrupts and be relocated just
like memory bus devices.
## Memory Bus
Memory Bus devices cannot send or receive interrupt codes, however they may receive memory interrupts from the CPU and
send memory interrupts to the CPU. Generally, the BootRom is already attached to the memory bus at port 0. Unlike
device interrupts, memory interrupts do not carry any data, however the device receiving a memory interrupt will still
be informed of which port on the bus sent the interrupt. Any device can be relocated by setting a new base address.
Port numbers for memory devices are offset by 16 (`0x10`). Additionally, devices can also be temporarily detached
from memory. Since memory devices effectively hide system memory, detaching them may be useful for using the system
memory previously hidden by a device. When a device is re-attached to the memory bus, the system memory at the address
range used by the device becomes non-accessible.
## Stacks
When adding to a stack, the instruction will silently fail if the stack is full. The complete contents of the stack
may be examined by reading the processor's memory range. The processor's internal memory is read-only.
# Opcodes
The Kcpu currently has a very limited instruction set. For the numerical values of the opcodes, see
(OpDirector.java)[src/main/java/me/felinewith/kcpu/opcodes/OpDirector.java].
# Listing by Number of Arguments
## Zero-Argument Instructions
* [`gpc`](#gpc)
* [`zpwr`](#zpwr)
## Single-Argument Instructions
* [`push`](#push)
* [`pop`](#pop)
* [`call`](#call)
* [`ret`](#ret)
* [`deva`](#deva)
* [`devb`](#devb)
* [`devp`](#devp)
* [`irqd`](#irqd)
* [`irqm`](#irqm)
* [`irqdh`](#irqdh)
* [`irqmh`](#irqmh)
* [`iret`](#iret)
* [`jmpu`](#jmpu)
* [`jmpa`](#jmpa)
* [`jmpd`](#jmpd)
## Two-Argument Instructions
* [`add`](#add)
* [`sub`](#sub)
* [`mul`](#mul)
* [`div`](#div)
* [`ivec`](#ivec)
* [`devm`](#devm)
* [`vcmp`](#vcmp)
## Three-Argument Instructions
* [`jue`](#jue)
* [`jae`](#jae)
* [`jde`](#jde)
* [`jug`](#jug)
* [`jag`](#jag)
* [`jdg`](#jdg)
* [`jul`](#jul)
* [`jal`](#jal)
* [`jdl`](#jdl)
* [`juge`](#juge)
* [`jage`](#jage)
* [`jdge`](#jdge)
* [`jule`](#jule)
* [`jale`](#jale)
* [`jdle`](#jdle)
# Listing by Instruction Category
## Basic Math
* [`add`](#add)
* [`sub`](#sub)
* [`mul`](#mul)
* [`div`](#div)
## Memory
* [`mov`](#mov)
* [`movs`](#movs)
## Stack Manipulation
* [`push`](#push)
* [`pop`](#pop)
* [`call`](#call)
* [`ret`](#ret)
* [`iret`](#iret)
## Interrupt Vector Management
* [`ivec`](#ivec)
## Device Management
* [`deva`](#deva)
* [`devb`](#devb)
* [`devp`](#devp)
* [`devm`](#devm)
## Interrupts
* [`irqd`](#irqd)
* [`irqm`](#irqm)
* [`irqdh`](#irqdh)
* [`irqmh`](#irqmh)
## Jumps
* [`jmpu`](#jmpu)
* [`jmpa`](#jmpa)
* [`jmpd`](#jmpd)
## Conditional Jumps
* [`jue`](#jue)
* [`jae`](#jae)
* [`jde`](#jde)
* [`jug`](#jug)
* [`jag`](#jag)
* [`jdg`](#jdg)
* [`jul`](#jul)
* [`jal`](#jal)
* [`jdl`](#jdl)
* [`juge`](#juge)
* [`jage`](#jage)
* [`jdge`](#jdge)
* [`jule`](#jule)
* [`jale`](#jale)
* [`jdle`](#jdle)
## Value Compare
* [`vcmp`](#vcmp)
## Power Management
* [`zpwr`](#zpwr)
# Instruction Information
## `add`
```
add output input
```
Adds the values of both `input` and `output`, then places the result in `output`.
## `call`
```
call address
```
Places the current program counter onto the call stack, then jumps to the specified address.
## `deva`
```
deva address
```
If there is a device at the specified memory address, its port number will be placed in `r0`. If there is no device,
the value `0x7f` will be placed in `r0`.
## `devd`
```
devd port
```
Detaches the device on the specified port from the memory bus. Does nothing if the device is already detached.
## `devm`
```
devm port newaddress
```
Relocates device on the specified port to the specified base address. Use with caution, as there is nothing to prevent
multiple devices from sharing the same memory address range.
## `devp`
```
devp port
```
Obtains information on the device on the specified port:
* `r0` will contain the device's base address
* `r1` will contain the length of the devices's address range.
## `div`
```
div numerator denominator
```
Divides the numerator against the denominator:
* `r0` will contain the quotient
* `r1` will contain the remainder
## `gpc`
```
gpc the current program counter in `r0`
## `iret`
```
iret from an interrupt state. Does nothing if the processor is not currently in an interrupt state.
## `irqd`
```
irqd port
```
Sends an interrupt to the device on the specified port. This is a non-blocking instruction.
For the blocking equivalent, see [`irqdh`](#irqdh)
## `irqdh`
```
irqdh port
```
Sends an interrupt to the device on the specified port. This is a blocking instruction - the processor will wait until
the hardware finishes its task.
## `irqm`
```
irqm address
```
Sends an interrupt to the device using the specified memory address. This is a non-blocking instruction.
For the blocking equivalent, see [`irqmh`](#irqmh)
## `irqmh`
```
irqmh address
```
Sends an interrupt to the device using the specified memory address. This is a blocking instruction - the processor
will wait until the hardware finishes its task.
## `ivec`
```
ivec interrupt address
```
Sets the specified address as the interrupt handler (vector) for the specified interrupt value.
## `jae`
```
jae number0 number1 address
```
Jumps to address if number0 is equal to number1.
## `jag`
```
jag number0 number1 address
```
Jumps to address if number0 is greater than number1.
## `jage`
```
jage number0 number1 address
```
Jumps to address if number0 is greater than or equal to number1.
## `jal`
```
jal number0 number1 address
```
Jumps to address if number0 is less than number1.
## `jale`
```
jale number0 number1 address
```
Jumps to address if number0 is less than or equal to number1.
## `jde`
```
jde number0 number1 address
```
Jumps down if number0 is equal to number1.
## `jdg`
```
jdg number0 number1 address
```
Jumps down if number0 is greater than number1.
## `jdge`
```
jdge number0 number1 address
```
Jumps down if number0 is greater than or equal to number1.
## `jdl`
```
jdl number0 number1 address
```
Jumps down if number0 is less than number1.
## `jdle`
```
jdle number0 number1 address
```
Jumps down if number0 is less than or equal to number1.
## `jmpa`
```
jmpa address
```
Jumps to address.
## `jmpd`
```
jmpd address
```
Jumps down.
## `jmpu`
```
jmpu address
```
Jumps up.
## `jue`
```
jue number0 number1 address
```
Jumps up if number0 is equal to number1.
## `jug`
```
jug number0 number1 address
```
Jumps up if number0 is greater than number1.
## `juge`
```
juge number0 number1 address
```
Jumps up if number0 is greater than or equal to number1.
## `jul`
```
jul number0 number1 address
```
Jumps up if number0 is less than number1.
## `jule`
```
jule number0 number1 address
```
Jumps up if number0 is less than or equal to number1.
## `mul`
```
mul output input
```
Multiplies the values of both `input` and `output`, then places the result in `output`.
## `pop`
```
pop
```
Takes the topmost value from the stack and moves it to `r0`.
## `push`
```
push value
```
Places the specified value at the top of the stack.
## `ret`
```
ret
```
Returns from a function. Does nothing if the call stack is empty.
## `sub`
```
sub output input
```
Subtracts the value `output` from the value of `input`, then places the result in `output`.
## `vcmp`
```
vcmp number0 number1
```
Performs 4 comparisons between number0 and number1, then puts the result in `r0`. The resulting value is a bitmask.
* `number0 != number1` : `0x01`
* `number0 >= number1` : `0x02`
* `number0 == number1` : `0x04`
* `number0 <= number1` : `0x08`
## `zpwr`
```
zpwr
```
Turns the system off.
......@@ -255,9 +255,6 @@ public class Kcpu extends BaseDevice {
// device management
case DEVA: registers[0] = kboard.checkDevice(data); break;
case DEVD: kboard.detachDevice(data); break;
case DEVM:
kboard.setDeviceRange(data, ValueHelper.readShort(1, ref, registers, values, kboard));
break;
case DEVP:
KMemoryRange kmr = kboard.getDeviceRange(data);
registers[0] = kmr.getOffset();
......@@ -302,6 +299,9 @@ public class Kcpu extends BaseDevice {
}
switch (od) {
case DEVM:
kboard.setDeviceRange(data, data2);
break;
case VCMP:
registers[0] = 0;
if (data != data2) { registers[0] += 0x0001; }
......@@ -313,19 +313,19 @@ public class Kcpu extends BaseDevice {
short data3 = ValueHelper.readShort(2, ref, registers, values, kboard);
switch (od) {
// conditionals
case JE: if (data == data2) { pc += data3; } break;
case JUE: case JE: if (data == data2) { pc += data3; } break;
case JAE: if (data == data2) { pc = data3; } break;
case JDE: if (data == data2) { pc -= data3; } break;
case JG: if (data > data2) { pc += data3; } break;
case JUG: case JG: if (data > data2) { pc += data3; } break;
case JAG: if (data > data2) { pc = data3; } break;
case JDG: if (data > data2) { pc -= data3; } break;
case JL: if (data < data2) { pc += data3; } break;
case JUL: case JL: if (data < data2) { pc += data3; } break;
case JAL: if (data < data2) { pc = data3; } break;
case JDL: if (data < data2) { pc -= data3; } break;
case JGE: if (data >= data2) { pc += data3; } break;
case JUGE: case JGE: if (data >= data2) { pc += data3; } break;
case JAGE: if (data >= data2) { pc = data3; } break;
case JDGE: if (data >= data2) { pc -= data3; } break;
case JLE: if (data <= data2) { pc += data3; } break;
case JULE: case JLE: if (data <= data2) { pc += data3; } break;
case JALE: if (data <= data2) { pc = data3; } break;
case JDLE: if (data <= data2) { pc -= data3; } break;
}
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!