1
0

Compare commits

..

2 Commits

Author SHA1 Message Date
cinthyr
905d05624f docs: Fix a whole lot of typos in the ISA docs 2026-05-31 20:53:09 +02:00
dd699cc933 tooling: add vim syntax for ain-48 assembly 2026-05-31 20:46:55 +02:00
5 changed files with 151 additions and 113 deletions

1
.gitignore vendored
View File

@ -2,4 +2,5 @@
/build
/docs/*.pdf
/ain48
/scratch

View File

@ -13,7 +13,6 @@
set par(justify: true)
set heading(numbering: "I.1")
show raw: set text(font: "Iosevka Slab", size: 1em / .8)
show math.equation: set text(font: "Fira Math", weight: 400)
show title: align.with(center)
show strong: text.with(weight: 900)
set figure(numbering: "1", placement: auto)

View File

@ -11,7 +11,7 @@
[Internátionális],
[Nórmae],
grid.cell(rowspan: 2)[--- 48 bit],
[Intstrúctiónum],
[Instrúctiónum],
[Numerátiónum],
))
@ -24,14 +24,14 @@
]
= Conventions
A *byte* (singulus) is the smallest adressable part of memory, that is, a 12-bit
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 adress
lowest memory address.
= Registers
The main registers of the architecture are all word-length:
@ -62,7 +62,7 @@ There is also a 3-bit status register.
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.
memory address.
#let instruction_diagram(..bytes, voff: 0) = {
import cetz.draw: *
@ -101,8 +101,8 @@ memory adress.
(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]])))
(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]),
@ -113,8 +113,8 @@ memory adress.
(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]])))
(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]),
@ -126,7 +126,7 @@ memory adress.
(1, [i#sub[1]\ 0]),
(1, [s]),
(4, [op]),
), ((4, [r#sub[d]]), (4, text(size: .9em)[imm[0:3]]), (4, [r#sub[1]])))
), ((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]),
@ -137,8 +137,8 @@ memory adress.
(1, [i#sub[2]\ 1]),
(1, [i#sub[1]\ 1]),
(1, [s]),
(4, [op]),
), ((4, [r#sub[d]]), (8, [imm[0:7]])))
(4, [ac]),
), ((4, [r#sub[f]]), (8, [imm[0:7]])))
},
),
)
@ -197,9 +197,9 @@ memory adress.
(1, [0]),
(1, [0]),
(1, [1]),
(1, [w]),
(1, [s]),
(4, [r#sub[d]]),
(4, [r#sub[a]]),
(4, [r#sub[l]]),
), ((12, [imm[0:11]]),))
}))
@ -218,7 +218,7 @@ memory adress.
(1, [1]),
(1, [1]),
(1, [1]),
(3, [op]),
(3, [ac]),
))
}))
@ -239,10 +239,10 @@ memory adress.
(1, [0]),
(1, [0]),
(1, [0]),
(1, [I]),
(1, [C]),
(1, [E]),
(1, [N]),
(1, [Z]),
(1, [U]),
(1, [R]),
), ((12, [imm[0:11]]),))
instruction_diagram(((12, [imm[12:23]]),), ((12, [imm[24:35]]),), voff: -3)
@ -260,11 +260,11 @@ memory adress.
(1, [1]),
(1, [0]),
(1, [1]),
(4, [r#sub[a]]),
(4, [r#sub[l]]),
(1, [I]),
(1, [C]),
(1, [E]),
(1, [N]),
(1, [Z]),
(1, [U]),
(1, [R]),
))
}))
@ -278,11 +278,11 @@ memory adress.
(1, [1]),
(1, [1]),
(1, [0]),
(4, [r#sub[a]]),
(4, [r#sub[l]]),
(1, [I]),
(1, [C]),
(1, [E]),
(1, [N]),
(1, [Z]),
(1, [U]),
(1, [R]),
), ((12, [imm[0:11]]),))
}))
@ -294,9 +294,9 @@ 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
/ ```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"))
@ -316,7 +316,7 @@ Common Notations:
#optgrid(
opt[i#sub[1]][0],
opt[i#sub[2]][0],
opt[r#sub[d]][a],
opt[r#sub[f]][a],
opt[r#sub[1]][b],
opt[r#sub[2]][c],
)
@ -324,7 +324,7 @@ Common Notations:
#optgrid(
opt[i#sub[1]][0],
opt[i#sub[2]][1],
opt[r#sub[d]][a],
opt[r#sub[f]][a],
opt[r#sub[1]][b],
opt[imm][imm],
)
@ -333,51 +333,61 @@ Common Notations:
#optgrid(
opt[i#sub[1]][1],
opt[i#sub[2]][0],
opt[r#sub[d]][a],
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[d]][a], opt[imm][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
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
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
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: [
Divide a signed number by another
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 left by a signed number.
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
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
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])
@ -386,7 +396,7 @@ Common Notations:
#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
the programmer to explicitly request one format or the other: #istr("IMLG") uses #iml and
#istr("IPLG") uses #ipl.
],
also: (
@ -398,15 +408,10 @@ Common Notations:
mnem: "I",
name: "Íre",
description: [
Go to
Go to (jump to) an instruction other than the one immediately following this instruction.
/ ```ain48 I a_label```: %ia a_label
/ Instruction Format: #asli
/ 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
@ -414,44 +419,24 @@ Common Notations:
/ ```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
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[I][1],
opt[C][0],
opt[N][0],
opt[Z][0],
opt[R][0],
opt[r#sub[a]][a],
opt[imm][imm],
)
@ -462,23 +447,23 @@ Common Notations:
"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])
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[Z][1])
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.\
Used like #istr("I"), but only jump if the negative bit of the status flag is set.\
#optgrid(opt[I][0], opt[N][1])
],
),
@ -486,7 +471,7 @@ Common Notations:
"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.\
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])
],
),
@ -494,15 +479,15 @@ Common Notations:
"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])
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.\
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])
],
),
@ -510,22 +495,22 @@ Common Notations:
"PI",
"Si positívus est íre",
[
Used like #istr("I") but only jump if neither the zero nor negative flags of the status
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[Z][1])
#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 flags of the status
flag are set.
#optgrid(opt[I][0], opt[N][1], opt[Z][1])
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:\
Used like #istr("I"), but sets up a function environment prior to jumping:\
(%it) %ia; %it %it - 4
#optgrid(opt[R][1])
@ -539,23 +524,26 @@ Common Notations:
Invoke a kernel space function, see your operating system's ABI for more
details.
/ Instruction format: #asss
#optgrid(opt[op][3])
#optgrid(opt[ac][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])
*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: [
*Priviledged*
Return to user space after a system call
*Privileged*
Return to user space after a system call.
/ Instruction Format: #asss
#optgrid(opt[op][2])
#optgrid(opt[ac][2])
/ ```ain48 IRRV```: Return from kernelspace.
]),
(mnem: "VEL", name: "Vel", description: [
Bitwise disjunction between two words
Bitwise disjunction between two words.
#aludoc("VEL", [11], sym.or, true, 0)
]),
(mnem: "NEC", name: "Nec", description: [
@ -582,51 +570,56 @@ Common Notations:
mnem: "LG",
name: "Legere",
description: [
Read a word from memory
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
#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
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])
#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: [
Halts Execution of the process
*Privileged* Halts the CPU.
/ Instruction Format: #asss
#optgrid(opt[op][7])
#optgrid(opt[ac][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)
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")
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")
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. Macro for ```ain48 VEL %a, %b, %nul```
]
),
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(
@ -643,7 +636,7 @@ Common Notations:
== 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)
Unsigned version of #ristr(it.mnem).
]))
}
if it.keys().contains("also") {
@ -662,7 +655,7 @@ Common Notations:
mnem: "P" + m,
desc: [
== P#m | Positivós #n
Unsigned version of #m, see #ristr("P" + it.mnem) and #ristr(it.mnem)
Unsigned version of #m; see #ristr("P" + it.mnem) and #ristr(it.mnem).
],
),
)

View File

@ -0,0 +1,6 @@
" ftdetect file for AIN-48 assembly
au BufRead,BufNewFile *.ain48s set filetype=ain48asm
au BufRead,BufNewFile *.ain48asm set filetype=ain48asm
au BufRead,BufNewFile *.a48s set filetype=ain48asm
au BufRead,BufNewFile *.a48asm set filetype=ain48asm

View File

@ -0,0 +1,39 @@
" Vim syntax file
" Language: AIN-48 Assembly
" Maintainer: Annwan
" Latest Revision: 31 May 2026
if exists("b:current_syntax")
finish
endif
syn case ignore
syn keyword ain48Mnemonic add aeq aut cess cst dem dst dvd ei et i ilg imlg iplg irrv ivc lg mpl nam ncivc ncrv nec nei ni nni npi nui padd pdem pdvd pi pilg pimlg piplg pmpl prsd pstr rsd rv sc sim str tsc ui vel
" Senary constants
syn match ain48Immediate "#[Ss][0-5]\+"
syn match ain48Immediate "#-[Ss][0-5]\+"
" Octal constants
syn match ain48Immediate "#[Oo][0-7]\+"
syn match ain48Immediate "#-[Oo][0-7]\+"
" Decimal constants
syn match ain48Immediate "#[Xx][0-9]\+"
syn match ain48Immediate "#-[Xx][0-9]\+"
" Binary constants
syn match ain48Immediate "#[Bb][01]\+"
syn match ain48Immediate "#-[Bb][01]\+"
syn match ain48Register "%[0-7a-z]\+"
syn match ain48Directive "\.[a-z0-9_-]\+"
syn match ain48Comment ";.*$" contains=ain48Todo
syn keyword ain48Todo contained TODO FIXME NOTE
syn match ain48Label "^[a-zA-Z0-9_-]\+:"
hi link ain48Mnemonic Statement
hi link ain48Comment Comment
hi link ain48Immediate Constant
hi link ain48Directive PreProc
hi link ain48Label Label
hi link ain48Register Identifier