#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], [Instrú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 addressable 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 address. = 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 address. #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, [ac]), ), ((4, [r#sub[f]]), (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, [ac]), ), ((4, [r#sub[f]]), (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[f]]), (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, [ac]), ), ((4, [r#sub[f]]), (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, [imm[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, [s]), (4, [r#sub[d]]), (4, [r#sub[l]]), ), ((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, [ac]), )) })) #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, [C]), (1, [E]), (1, [N]), (1, [U]), (1, [R]), ), ((12, [imm[0:11]]),)) instruction_diagram(((12, [imm[12:23]]),), ((12, [imm[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[l]]), (1, [I]), (1, [E]), (1, [N]), (1, [U]), (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[l]]), (1, [I]), (1, [E]), (1, [N]), (1, [U]), (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 address contained by %a / ```ain48 (#imm[n])```: The memory at the address represented by the immediate (max n bits) / ```ain48 a_label```: The memory address corresponding 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[f]][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[f]][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[f]][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[f]][a], opt[imm][imm]) ] #let instructions = ( (mnem: "STR", name: "Subtrahere", sign: true, description: [ Subtract one signed number from another. #aludoc("STR", "01", "-", false, 1) / Instruction format: #amal ]), (mnem: "ADD", name: "Addere", sign: true, description: [ Add two signed numbers together. #aludoc("ADD", "00", "+", true, 1) / Instruction format: #amal ]), (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: [ Remainder of the Euclidean division of two signed integers. Will raise an arithmetic exception if the right operand is zero. #aludoc("RSD", "05", "mod", false, 1) / Instruction format: #amal ]), (mnem: "DVD", name: "Dívidere", sign: true, description: [ Integer part of the Euclidean division of two signed integers. Will raise an arithmetic exception if the right operand is zero. #aludoc("DVD", "04", "/", false, 1) / Instruction format: #amal ]), (mnem: "DEM", name: "Ad Dextró Movére", sign: true, description: [ Shift a signed number right by a signed number. Will raise an arithmetic exception if the right operand is negative. #aludoc("DEM", "06", ">>", false, 1) / Instruction format: #amal ]), (mnem: "SIM", name: "Ad Sinistró Movére", description: [ Shift a word to the left. Will raise an arithmetic exception if the right operand is negative. #aludoc("SIM", "07", "<<", false, 0) / Instruction format: #amal ]), ( 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 explicitly 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 (jump to) an instruction other than the one immediately following this instruction. / ```ain48 I a_label```: %ia ← a_label / Instruction format: #asli #optgrid( 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[imm][a_label], ) / ```ain48 I %a```: %ia ← %a\ / Instruction Format: #aslr #optgrid( opt[r#sub[a]][a], ) / ```ain48 I %nul, #imm[12]```: %ia ← %ia + %a / Instruction Format: #asrd #optgrid( opt[r#sub[a]][0], opt[imm][imm], ) This is a special case of the format just below, where because offsetting from %nul is useless, we instead interpret it as an offset from %ia. / ```ain48 I %a, #imm[12]```: %ia ← %a + imm / Instruction Format: #asrd #optgrid( 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 status flag is set.\ #optgrid(opt[I][0], opt[U][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[U][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[E][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 flag of the status flag is set. #optgrid(opt[I][1], opt[N][1], opt[U][1]) ], ), ( "NPI", "Si nón positívus est íre", [ Used like #istr("I"), but only jump if either the zero or negative flag of the status flag is set. #optgrid(opt[I][0], opt[N][1], opt[U][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[ac][3]) / ```ain48 NCIVC```: Perform a kernel call. ], ), (mnem: "IRRV", name: "De Interruptó Reveníre", description: [ *Privileged* Instruction used to exit from the interrupt handler. / Instruction format: #asss #optgrid(opt[ac][4]) / ```ain48 IRRV```: Return from an interrupt. ]), (mnem: "NCRV", name: "De Nucleó Reveníre", description: [ *Privileged* Return to user space after a system call. / Instruction Format: #asss #optgrid(opt[ac][2]) / ```ain48 IRRV```: Return from kernelspace. ]), (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[s][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[s][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: [ *Privileged* Halts the CPU. / Instruction Format: #asss #optgrid(opt[ac][7]) / ```ain48 CESS```: Pause execution. ]), (mnem: "RV", name: "Reveníre", description: [ Returns from a subroutine. / Instruction Format: #asss #optgrid(opt[ac][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: [ Pop the contents of the top of the stack into a register. / ```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. This instruction is a macro implemented as ```ain48 VEL %a, %b, %nul```. ]), (mnem: "AN", name: "Actió Nulla", description: [ Do nothing except increment the instruction counter. / ```ain48 AN```: Do nothing. This instruction is a macro implemented as ```ain48 IPLG %0, #0```. ]), ) #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)