1
0
ain48/docs/isa.typ
2026-05-31 12:33:05 +02:00

678 lines
20 KiB
Typst

#import ".template.typ": *
#show: conf
#title[AIN-48 Instruction Set Architecture]
#align(center, grid(
columns: 4,
column-gutter: .5em,
align: left + horizon,
row-gutter: .5em,
grid.cell(rowspan: 2)[Architectúra],
[Internátionális],
[Nórmae],
grid.cell(rowspan: 2)[--- 48 bit],
[Intstrúctiónum],
[Numerátiónum],
))
#show heading: set text(weight: 900)
#show heading: it => [
#set par(justify: false)
#grid(columns: (auto, 1fr), column-gutter: .25em, if it.numbering != none {
context [#counter(heading).display(it.numbering)]
} else [], it.body)
]
= Conventions
A *byte* (singulus) is the smallest adressable part of memory, that is, a 12-bit
binary number.
A *word* (quaternus) is a binary number composed of 4 bytes, that is, a 48 bit
binary number.
In memory, multibyte numbers are laid out with the least significant byte at the
lowest memory adress
= Registers
The main registers of the architecture are all word-length:
#columns(2)[
/ %0/%nul:Constant zero
/ %01: General purpose
/ %02: General purpose
/ %03: General purpose
/ %04: General purpose
/ %05: General purpose
/ %06: General purpose
/ %07: General purpose
/ %ia: Index ad actiónem\ (Instruction pointer)
#colbreak()
/ %10: General purpose
/ %11: General purpose
/ %12: General purpose
/ %13: General purpose
/ %14: General purpose
/ %15: General purpose
/ %16: General purpose
/ %17/%it: Index ad turrim\ (Stack pointer)
]
There is also a 3-bit status register.
= Instructions as encoded
In instruction diagrams, each large rectangle represents a byte, that are
disposed in memory as implied by the reading order, starting at the lowest
memory adress.
#let instruction_diagram(..bytes, voff: 0) = {
import cetz.draw: *
for (i, b) in bytes.pos().enumerate() {
let j = 0
for (l, d) in b {
content((i * 12.5 + j + l / 2, 1 + voff), d)
j = j + l
line((i * 12.5 + j, voff), (i * 12.5 + j, 2 + voff), stroke: gray)
}
rect((i * 12.5, voff), (i * 12.5 + 12, 2 + voff))
for j in range(0, 13) {
line((i * 12.5 + j, .125 + voff), (i * 12.5 + j, -.125 + voff))
line((i * 12.5 + j, 2.125 + voff), (i * 12.5 + j, 1.875 + voff))
}
content((i * 12.5, voff + 2.25), anchor: "south-west")[11]
content((i * 12.5 + 12, voff + 2.25), anchor: "south-east")[0]
}
}
#let amal = [Actió Machinae Arithméticae Logicaeque]
== #amal
#align(
center,
cetz.canvas(
{
import cetz.draw: *
scale(.65)
instruction_diagram((
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [i#sub[2]\ 0]),
(1, [i#sub[1]\ 0]),
(1, [s]),
(4, [op]),
), ((4, [r#sub[d]]), (4, [r#sub[2]]), (4, [r#sub[1]])))
content((12.25, -1))[or]
instruction_diagram(voff: -5, (
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [i#sub[2]\ 0]),
(1, [i#sub[1]\ 1]),
(1, [s]),
(4, [op]),
), ((4, [r#sub[d]]), (4, [r#sub[2]]), (4, text(size: .9em)[imm[0:3]])))
content((12.25, -6))[or]
instruction_diagram(voff: -10, (
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [i#sub[2]\ 1]),
(1, [i#sub[1]\ 0]),
(1, [s]),
(4, [op]),
), ((4, [r#sub[d]]), (4, text(size: .9em)[imm[0:3]]), (4, [r#sub[1]])))
content((12.25, -11))[or]
instruction_diagram(voff: -15, (
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [i#sub[2]\ 1]),
(1, [i#sub[1]\ 1]),
(1, [s]),
(4, [op]),
), ((4, [r#sub[d]]), (8, [imm[0:7]])))
},
),
)
#let iml = [Immediátum Magnum Lectura]
== #iml
#align(
center,
cetz.canvas(
{
import cetz.draw: *
scale(.65)
instruction_diagram((
(1, [0]),
(1, [0]),
(1, [0]),
(1, [1]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [s]),
(4, [r]),
), ((12, [imm[0:11]]),))
instruction_diagram(((12, [imm[12:23]]),), ((12, [ımm[24:35]]),), voff: -3)
instruction_diagram(((12, [imm[36:47]]),), voff: -6)
},
),
)
#let ipl = [Immediátum Parvum Lectura]
== #ipl
#align(center, cetz.canvas({
import cetz.draw: *
scale(.65)
instruction_diagram((
(1, [0]),
(1, [0]),
(1, [0]),
(1, [1]),
(1, [1]),
(1, [0]),
(1, [0]),
(1, [s]),
(4, [r]),
), ((12, [imm[0:11]]),))
}))
#let amls = [Ad Memóriam Lectura Scripturaque]
== #amls
#align(center, cetz.canvas({
import cetz.draw: *
scale(.65)
instruction_diagram((
(1, [0]),
(1, [0]),
(1, [1]),
(1, [w]),
(4, [r#sub[d]]),
(4, [r#sub[a]]),
), ((12, [imm[0:11]]),))
}))
#let asss = [Actió Sequentiae Singulí Simplicis]
== #asss
#align(center, cetz.canvas({
import cetz.draw: *
scale(.65)
instruction_diagram((
(1, [1]),
(1, [1]),
(1, [1]),
(1, [1]),
(1, [1]),
(1, [1]),
(1, [1]),
(1, [1]),
(1, [1]),
(3, [op]),
))
}))
#let asli = [Actió Sequentiae Locí Immediátí]
== #asli
#align(
center,
cetz.canvas(
{
import cetz.draw: *
scale(.65)
instruction_diagram((
(1, [1]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [I]),
(1, [C]),
(1, [N]),
(1, [Z]),
(1, [R]),
), ((12, [imm[0:11]]),))
instruction_diagram(((12, [imm[12:23]]),), ((12, [ımm[24:35]]),), voff: -3)
instruction_diagram(((12, [imm[36:47]]),), voff: -6)
},
),
)
#let aslr = [Actió Sequentiae Loci Recordandí]
== #aslr
#align(center, cetz.canvas({
cetz.draw.scale(.65)
instruction_diagram((
(1, [1]),
(1, [0]),
(1, [1]),
(4, [r#sub[a]]),
(1, [I]),
(1, [C]),
(1, [N]),
(1, [Z]),
(1, [R]),
))
}))
#let asrd = [Actió Sequentiae Recordandó Dépositus]
== #asrd
#align(center, cetz.canvas({
cetz.draw.scale(.65)
instruction_diagram((
(1, [1]),
(1, [1]),
(1, [0]),
(4, [r#sub[a]]),
(1, [I]),
(1, [C]),
(1, [N]),
(1, [Z]),
(1, [R]),
), ((12, [imm[0:11]]),))
}))
#pagebreak(weak: true)
= Instructions by their mnemonics
Common Notations:
/ ```ain48 %a, %b, %c, ...```: any register a, b, c
/ ```ain48 #imm[n]```: An immediate (max: n bits)
/ ```ain48 %0, %nul```: Writes to ```ain48 %0 / %nul``` are discarded
/ ```ain48 (%a)```: The memory at the adress contained by %a
/ ```ain48 (#imm[n])```: The memory at the adress represented by the immediate (max n bits)
/ ```ain48 a_label```: The memory adress correspoding to the label
#let ristr(mnem) = [*#raw(mnem, lang: "ain48")* (#ref(label("ins:" + mnem), form: "page"))]
#let istr(mnem) = strong(raw(mnem, lang: "ain48"))
#let opt(a, b) = box[
*#a* = #b
]
#let optgrid(..opts) = block({
text(weight: 900)[Instruction parameters]
linebreak()
h(1em)
opts.pos().join(h(2em))
})
#let aludoc(mnemonic, opnum, opsym, commutative, s) = [
#optgrid(opt[op][#opnum], opt[s][#s])
/ Instruction format: #amal
/ #raw(mnemonic + " %a, %b, %c", lang: "ain48"): %a %b #opsym %c
#optgrid(
opt[i#sub[1]][0],
opt[i#sub[2]][0],
opt[r#sub[d]][a],
opt[r#sub[1]][b],
opt[r#sub[2]][c],
)
/ #raw(mnemonic + " %a, %b, #imm[4]", lang: "ain48"): %a %b #opsym imm
#optgrid(
opt[i#sub[1]][0],
opt[i#sub[2]][1],
opt[r#sub[d]][a],
opt[r#sub[1]][b],
opt[imm][imm],
)
#if not commutative [
/ #raw(mnemonic + " %a, #imm[4], %b", lang: "ain48"): %a imm #opsym %b
#optgrid(
opt[i#sub[1]][1],
opt[i#sub[2]][0],
opt[r#sub[d]][a],
opt[r#sub[2]][b],
opt[imm][imm],
)
]
/ #raw(mnemonic + " %a, #imm[8]", lang: "ain48"): %a %a #opsym imm
#optgrid(opt[i#sub[1]][1], opt[i#sub[2]][1], opt[r#sub[d]][a], opt[imm][imm])
]
#let instructions = (
(mnem: "STR", name: "Subtrahere", sign: true, description: [
Subtract one signed number from another
#aludoc("STR", "01", "-", false, 1)
]),
(mnem: "ADD", name: "Addere", sign: true, description: [
Add two signed numbers together.
#aludoc("ADD", "00", "+", true, 1)
]),
(mnem: "MPL", name: "Multiplicáre", sign: true, description: [
Multiply two signed numbers together
#aludoc("MPL", "02", "×", true, 1)
/ Instruction format: #amal
]),
(mnem: "RSD", name: "Residérí", sign: true, description: [
Reminder of the euclidian division of two words
#aludoc("RSD", "05", "mod", false, 1)
]),
(mnem: "DVD", name: "Dívidere", sign: true, description: [
Divide a signed number by another
#aludoc("DVD", "04", "/", false, 1)
]),
(mnem: "DEM", name: "Ad Dextró Movére", sign: true, description: [
Shift a signed number left by a signed number.
#aludoc("DEM", "06", ">>", false, 1)
]),
(mnem: "SIM", name: "Ad Sinistró Movére", description: [
Shift a word to the left
#aludoc("SIM", "07", "<<", false, 0)
]),
(
mnem: "ILG",
name: "Immediátum Legere",
sign: true,
description: [
Load a signed immediate value into a register
/ ```ain48 ILG %a, #imm[12]```: %a imm
/ Instruction format: #ipl
#optgrid(opt[s][1], opt[r][a], opt[imm][imm])
/ ```ain48 ILG %a, #imm[48]```: %a imm
/ Instruction format: #iml
#optgrid(opt[s][1], opt[r][a], opt[imm][imm])
The assembler has freedom to choose the appropriate format given the size of the
immediate. The mnemonics #ristr("IMLG") and #ristr("IPLG") also exist to allow
the programmer to explicitely request one format or the other: #istr("IMLG") uses #iml and
#istr("IPLG") uses #ipl.
],
also: (
("IPLG", "Immediátum Parvum Legere"),
("IMLG", "Immediátum Magnum Legere"),
),
),
(
mnem: "I",
name: "Íre",
description: [
Go to
/ ```ain48 I a_label```: %ia a_label
/ Instruction Format: #asli
#optgrid(
opt[I][1],
opt[C][0],
opt[N][0],
opt[Z][0],
opt[R][0],
opt[imm][a_label],
)
Some assemblers may opt to assemble calls to nearby labels as if they were of
the form `IRE %0, #imm[12]` with no loss of correctness.
/ ```ain48 I #imm[48]```: %ia imm\
/ Instruction Format: #asli
#optgrid(
opt[I][1],
opt[C][0],
opt[N][0],
opt[Z][0],
opt[R][0],
opt[imm][a_label],
)
/ ```ain48 I %a```: %ia %a\
/ Instruction Format: #aslr
#optgrid(
opt[I][1],
opt[C][0],
opt[N][0],
opt[Z][0],
opt[R][0],
opt[r#sub[a]][a],
)
/ ```ain48 I %nul, #imm[12]```: %ia %ia + %a
/ Instruction Format: #asrd
#optgrid(
opt[I][1],
opt[C][0],
opt[N][0],
opt[Z][0],
opt[R][0],
opt[r#sub[a]][0],
opt[imm][imm],
)
This is a special case of the format just below, where becasue offsetting from
%nul is useless, we instead understand it as an offset from %ia
/ ```ain48 I %a, #imm[12]```: %ia %a + imm
/ Instruction Format: #asrd
#optgrid(
opt[I][1],
opt[C][0],
opt[N][0],
opt[Z][0],
opt[R][0],
opt[r#sub[a]][a],
opt[imm][imm],
)
],
also: (
(
"UI",
"Si nullus est íre",
[
Used like #istr("I") but only jump if the zero bit of the staus flag is set.\
#optgrid(opt[I][0], opt[Z][1])
],
),
(
"NUI",
"Si nón nullus est íre",
[
Used like #istr("I") but only jump if the zero bit of the status flag is not set.\
#optgrid(opt[I][1], opt[Z][1])
],
),
(
"NI",
"Si negatívus est íre",
[
Used like #istr("I") but only jump if the negative bit of the status flag is set.\
#optgrid(opt[I][0], opt[N][1])
],
),
(
"NNI",
"Si nón negatívus est íre",
[
Used like #istr("I") but only jump if the negative bit of the status flag is not set.\
#optgrid(opt[I][1], opt[N][1])
],
),
(
"EI",
"Si excedit íre",
[
Used like #istr("I") but only jump if the carry bit of the status flag is set.\
#optgrid(opt[I][0], opt[C][1])
],
),
(
"NEI",
"Si nón excedit íre",
[
Used like #istr("I") but only jump if the carry bit of the status flag is not set.\
#optgrid(opt[I][1], opt[C][1])
],
),
(
"PI",
"Si positívus est íre",
[
Used like #istr("I") but only jump if neither the zero nor negative flags of the status
flag is set.
#optgrid(opt[I][1], opt[N][1], opt[Z][1])
],
),
(
"NPI",
"Si nón positívus est íre",
[
Used like #istr("I") but only jump if either the zero or negative flags of the status
flag are set.
#optgrid(opt[I][0], opt[N][1], opt[Z][1])
],
),
("IVC", "Invocáre", [
Used like #istr("I") but sets up a function environment prior to jumping:\
(%it) %ia; %it %it - 4
#optgrid(opt[R][1])
],),
),
),
(
mnem: "NCIVC",
name: "In Nucleó Invocáre",
description: [
Invoke a kernel space function, see your operating system's ABI for more
details.
/ Instruction format: #asss
#optgrid(opt[op][3])
/ ```ain48 NCIVC```: Perform a kernel call.
],
),
(mnem: "IRRV", name: "De Interruptó Reveníre", description: [
*Priviledged*
Instrucion used to exit from the interrupt handler.
#optgrid(opt[op][4])
]),
(mnem: "NCRV", name: "De Nucleó Reveníre", description: [
*Priviledged*
Return to user space after a system call
/ Instruction Format: #asss
#optgrid(opt[op][2])
]),
(mnem: "VEL", name: "Vel", description: [
Bitwise disjunction between two words
#aludoc("VEL", [11], sym.or, true, 0)
]),
(mnem: "NEC", name: "Nec", description: [
Bitwise negated disjunction between two words
#aludoc("NEC", [15], sym.not + sym.or, true, 0)
]),
(mnem: "AUT", name: "Aut", description: [
Bitwise difference between two numbers.
#aludoc("AUT", [12], sym.xor, true, 0)
]),
(mnem: "AEQ", name: "Aequí", description: [
Bitwise equality between two numbers.
#aludoc("AEQ", [16], [=], true, 0)
]),
(mnem: "ET", name: "Et", description: [
Bitwise logical conjunction between two numbers.
#aludoc("ET", [10], sym.and, true, 0)
]),
(mnem: "NAM", name: "Non ambó", description: [
Bitwise negated logical conjunction between two numbers.
#aludoc("NAM", [14], sym.not + sym.and, true, 0)
]),
(
mnem: "LG",
name: "Legere",
description: [
Read a word from memory
/ Instruction Format: #amls
/ ```ain48 LG %a, (%b), #imm[12]```: %a (%b + imm)
#optgrid(opt[w][0], opt[r#sub[d]][a], opt[r#sub[a]][b], opt[imm][imm])
/ ```ain48 LG %a, (%b)```: This is a special case of ```ain48 LG %a, (%b), #imm[12]``` where imm = 0
],
),
(
mnem: "SC",
name: "Scríbere",
description: [
Write a word to memory
/ Instruction Format: #amls
/ ```ain48 SC %a, (%b), #imm[12]```: (%b + imm) %a
#optgrid(opt[w][1], opt[r#sub[d]][a], opt[r#sub[a]][b], opt[imm][imm])
/ ```ain48 SC %a, (%b)```: This is a special case of ```ain48 SC %a, (%b), #imm[12]``` where imm = 0
],
),
(mnem: "CESS", name: "Cessáre", description: [
Halts Execution of the process
/ Instruction Format: #asss
#optgrid(opt[op][7])
/ ```ain48 CESS```: Pause execution.
]),
(mnem: "RV", name: "Reveníre", description: [
Returns from a subroutine.
/ Instruction Format: #asss
#optgrid(opt[op][0])
/ ```ain48 RV```: %it %it - 4; %ia (%it)
]),
(mnem: "CST", name: "Construere", description: [
Push the contents of a register onto the stack:
/ ```ain48 CST %a```: This instruction is a macro implemented as follows:\
#raw(block: true, "SC %a, (%it)\nPSTR %it, #4", lang: "ain48")
]),
(mnem: "DST", name: "Destruere", description: [
Push the contents of a register onto the stack:
/ ```ain48 DST %a```: This instruction is a macro implemented as follows:\
#raw(block: true, "PADD %it, #4\nLG %a, (%it)", lang: "ain48")
]),
(mnem: "TSC", name: "Transcríbere", description: [
Copy between registers
/ ```ain48 TSC %a, %b```: %a %b. Macro for ```ain48 VEL %a, %b, %nul```
]
),
)
#instructions.map(
it => {
let arr = ((mnem: it.mnem, desc: block(breakable: false, width: 100%)[
== #it.mnem | #it.name
#label("ins:" + it.mnem)
#it.description
#if it.keys().contains("sign") [See also #ristr("P" + it.mnem)]
]),)
if it.keys().contains("sign") {
arr.push((mnem: "P" + it.mnem, desc: [
== P#(it.mnem) | Positívós #it.name
#label("ins:P" + it.mnem)
Same form(s) as #istr(it.mnem) but with s = 0\
Unsigned version of #ristr(it.mnem)
]))
}
if it.keys().contains("also") {
for (m, n, ..r) in it.also {
arr.push((mnem: m, desc: [
== #m | #n
#label("ins:" + m)
#r.join()
See #ristr(it.mnem)
]))
if it.keys().contains("sign") {
arr.push(
(
mnem: "P" + m,
desc: [
== P#m | Positivós #n
Unsigned version of #m, see #ristr("P" + it.mnem) and #ristr(it.mnem)
],
),
)
}
}
}
arr
},
).join().sorted(key: it => it.mnem).map(it => it.desc).join()
#pagebreak(weak: true)
#outline(depth: 2)