1
0

Compare commits

...

11 Commits

Author SHA1 Message Date
2317f1e2b7 programs: start rewrite to haskell 2026-06-08 14:03:05 +02:00
30ff19d7ff docs: isa: add sized instructions 2026-06-08 13:59:47 +02:00
cinthyr
52b743de9a tooling: add syntax highlighting for Kakoune
Signed-off-by: Annwan <annwan@annwan.me>
2026-06-05 15:34:49 +02:00
cinthyr
f1ef3d99f1 typo: [0-5]+ > s[0-5]+
Signed-off-by: Annwan <annwan@annwan.me>
2026-06-03 11:25:38 +02:00
3515dee802 typo: asm_main soruce_file > source_file 2026-06-03 00:32:09 +02:00
91a8633243 new: add dump12 utility 2026-06-03 00:31:32 +02:00
cinthyr
6ccbe2fdbf docs: fix typos and punctuation errors, run formatter
Signed-off-by: Annwan <annwan@annwan.me>
2026-06-02 13:43:40 +02:00
bd58085d9e docs: ucsd abi 2026-06-01 18:33:05 +02:00
3e3a39963e build: Makefile actually clean compile_flags.txt 2026-06-01 13:15:47 +02:00
c8dbbac38f build: compile_commands.json → compile_flags.txt
Also have make update it
2026-06-01 13:13:35 +02:00
cinthyr
57d82b7e29 typo: fix typo in error message 2026-06-01 10:41:02 +02:00
18 changed files with 890 additions and 610 deletions

3
.gitignore vendored
View File

@ -1,6 +1,9 @@
/compile_commands.json
/compile_flags.txt
/build
/docs/*.pdf
/ain48
/scratch
/programs/dist-newstyle/

View File

@ -1,62 +0,0 @@
# PROGRAMS
CC = clang
CXX = clang++
LD = clang
AR = ar
RM = rm -f
TYPST = typst
# COMPILATION FLAGS
LIBS = raylib
ifdef DEBUG
CFLAGS += -Og -g -DDEBUG
else
CFLAGS += -O2
endif
CFLAGS += -Wall -Wextra -pedantic -std=gnu23 -Iinclude -Werror=return-type $(shell pkg-config --cflags $(LIBS))
LDFLAGS += $(shell pkg-config --libs $(LIBS))
# FILES
SRCS=$(wildcard src/*.c)
DEPS=$(patsubst src/%.c, build/%.d, $(SRCS))
OBJS=$(patsubst src/%.c, build/%.o, $(SRCS))
DOCSRC=$(wildcard docs/*.typ)
DOCS=$(patsubst docs/%.typ, docs/%.pdf, $(DOCSRC))
PROGRAM=ain48
.PHONY: all build docs clean deepclean
all: build docs
build: $(PROGRAM)
docs: $(DOCS)
docs/%.pdf: docs/%.typ
$(TYPST) c $< $@
clean:
$(RM) $(OBJS)
$(RM) $(DEPS)
$(RM) compile_commands.json
deepclean: clean
$(RM) $(PROGRAM)
$(RM) $(DOCS)
$(PROGRAM): $(OBJS)
$(LD) $(LDFLAGS) $(OBJS) -o $@
build/%.d: src/%.c
@mkdir -p $(@D)
@set -e ; $(RM) $@; \
$(CC) -M $(CFLAGS) $< > $@.$$$$; \
sed 's,\($*\)\.o[ :]*,build/\1.o $@ : ,g' < $@.$$$$ > $@; \
$(RM) $@.$$$$
build/%.o: src/%.c
@mkdir -p $(@D)
$(CC) $(CFLAGS) -c $< -o $@
include $(DEPS)

View File

@ -6,7 +6,6 @@
set text(
font: "Iosevka Etoile",
features: (cv47: 10),
number-type: "old-style",
weight: 400,
size: 12pt,
)
@ -26,5 +25,25 @@
}))
set raw(theme: "./gruvbox-white.tmTheme")
show raw.where(lang: "ain48"): set raw(syntaxes: "./ain48.sublime-syntax")
show raw.where(lang: "struct"): set raw(syntaxes: "./struct.sublime-syntax")
doc
}
#let byte_diag(..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]
}
}

17
docs/Makefile Normal file
View File

@ -0,0 +1,17 @@
# PROGRAMS
RM = rm -f
TYPST = typst
# FILES
DOCSRC=$(wildcard *.typ)
DOCS=$(DOCSRC:.typ=.pdf)
.PHONY: all docs clean
all: docs
docs: $(DOCS)
%.pdf: %.typ
$(TYPST) c $< $@
clean:
$(RM) $(DOCS)

View File

@ -6,20 +6,20 @@ file_extensions:
scope: source.ain48asm
contexts:
main:
- match: '(?i)\b(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)\b'
- match: '(?i)\b(P?[SBQ]?(ADD|DEM|DVD|MPL|RSD|SIM|STR)|AEQ|AN|AUT|[SBQ]?(LG|SC)|CESS|CST|DST|(N?[ENUP])?I|ET|P?I[MP]?LG|IRRV|(NC)?(IVC|RV)|NAM|NEC|TSC|VEL)\b'
scope: keyword.operator.word.ain48
- match: '(?i)%([a-z0-7]+)\b'
scope: variable.register.ain48
- match: '#-?([0-5]+|o[0-7]+|b[01]+|x[0-9]+)\b'
- match: '#-?(s[0-5]+|o[0-7]+|b[01]+|x[0-9]+)\b'
scope: constant.numeric.ain48
- match: '#imm\[[0-9n]+\]'
scope: constant.numeric.ain48
- match: ';'
scope: comment
push: comments
- match: '^[a-zA-Z0-9_-]+:$'
- match: '^[a-zA-Z0-9_-]+:'
scope: keyword.control.label.ain48
- match: '^.[a-zA-Z]+'
- match: '\.[a-zA-Z]+'
scope: keyword.directive.ain48
comments:
- meta_scope: comment

View File

@ -27,6 +27,9 @@
A *byte* (singulus) is the smallest addressable part of memory, that is, a 12-bit
binary number.
A *half-word* (binus) is a binary number composed of 2 bytes, that is, a 24 bit
binary number.
A *word* (quaternus) is a binary number composed of 4 bytes, that is, a 48 bit
binary number.
@ -64,24 +67,6 @@ 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
@ -92,50 +77,24 @@ memory address.
{
import cetz.draw: *
scale(.65)
instruction_diagram((
byte_diag((
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [i#sub[2]\ 0]),
(1, [i#sub[1]\ 0]),
(2, [m]),
(1, [i\ 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, (
byte_diag(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]),
(2, [m]),
(1, [i\ 1]),
(1, [s]),
(4, [ac]),
), ((4, [r#sub[f]]), (8, [imm[0:7]])))
@ -152,19 +111,19 @@ memory address.
{
import cetz.draw: *
scale(.65)
instruction_diagram((
(1, [0]),
byte_diag((
(1, [0]),
(1, [0]),
(1, [1]),
(1, [0]),
(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)
byte_diag(((12, [imm[12:23]]),), ((12, [imm[24:35]]),), voff: -3)
byte_diag(((12, [imm[36:47]]),), voff: -6)
},
),
)
@ -175,14 +134,14 @@ memory address.
#align(center, cetz.canvas({
import cetz.draw: *
scale(.65)
instruction_diagram((
(1, [0]),
byte_diag((
(1, [0]),
(1, [0]),
(1, [1]),
(1, [1]),
(1, [0]),
(1, [0]),
(1, [0]),
(1, [s]),
(4, [r]),
), ((12, [imm[0:11]]),))
@ -193,10 +152,10 @@ memory address.
#align(center, cetz.canvas({
import cetz.draw: *
scale(.65)
instruction_diagram((
(1, [0]),
byte_diag((
(1, [0]),
(1, [1]),
(1, [0]),
(1, [s]),
(4, [r#sub[d]]),
(4, [r#sub[l]]),
@ -208,7 +167,7 @@ memory address.
#align(center, cetz.canvas({
import cetz.draw: *
scale(.65)
instruction_diagram((
byte_diag((
(1, [1]),
(1, [1]),
(1, [1]),
@ -231,7 +190,7 @@ memory address.
{
import cetz.draw: *
scale(.65)
instruction_diagram((
byte_diag((
(1, [1]),
(1, [0]),
(1, [0]),
@ -245,8 +204,8 @@ memory address.
(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)
byte_diag(((12, [imm[12:23]]),), ((12, [imm[24:35]]),), voff: -3)
byte_diag(((12, [imm[36:47]]),), voff: -6)
},
),
)
@ -256,7 +215,7 @@ memory address.
#align(center, cetz.canvas({
cetz.draw.scale(.65)
instruction_diagram((
byte_diag((
(1, [1]),
(1, [0]),
(1, [1]),
@ -274,7 +233,7 @@ memory address.
#align(center, cetz.canvas({
cetz.draw.scale(.65)
instruction_diagram((
byte_diag((
(1, [1]),
(1, [1]),
(1, [0]),
@ -309,79 +268,65 @@ Common Notations:
h(1em)
opts.pos().join(h(2em))
})
#let aludoc(mnemonic, opnum, opsym, commutative, s) = [
#optgrid(opt[op][#opnum], opt[s][#s])
#let aludoc(mnemonic, opnum, opsym, s) = [
#optgrid(opt[op][#opnum], opt[s][#s], opt[w][2])
/ 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[i][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])
#optgrid(opt[i][1], opt[r#sub[f]][a], opt[imm][imm])
]
#let sized_also(mnem, name) = (also: (
( "S" + mnem, "Singulum " + name, [Same as #istr(mnem), but operates on bytes #optgrid(opt[w][0])]),
( "B" + mnem, "Binum " + name, [Same as #istr(mnem), but operates on half-words #optgrid(opt[w][1])]),
( "Q" + mnem, "Quaternum " + name, [Alias for the unsized version, operates on words]),
))
#let instructions = (
(mnem: "STR", name: "Subtrahere", sign: true, description: [
Subtract one signed number from another.
#aludoc("STR", "01", "-", false, 1)
#aludoc("STR", "01", "-", 1)
/ Instruction format: #amal
]),
], ..sized_also("STR", "Subtrahere")),
(mnem: "ADD", name: "Addere", sign: true, description: [
Add two signed numbers together.
#aludoc("ADD", "00", "+", true, 1)
#aludoc("ADD", "00", "+", 1)
/ Instruction format: #amal
]),
], ..sized_also("ADD", "Addere")),
(mnem: "MPL", name: "Multiplicáre", sign: true, description: [
Multiply two signed numbers together.
#aludoc("MPL", "02", "×", true, 1)
#aludoc("MPL", "02", "×", 1)
/ Instruction format: #amal
]),
], ..sized_also("MPL", "Multiplicáre")),
(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)
#aludoc("RSD", "05", "mod", 1)
/ Instruction format: #amal
]),
], ..sized_also("RSD", "Residérí")),
(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)
#aludoc("DVD", "04", "/", 1)
/ Instruction format: #amal
]),
], ..sized_also("DVD", "Dívidere")),
(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)
#aludoc("DEM", "06", ">>", 1)
/ Instruction format: #amal
]),
], ..sized_also("DEM", "Ad Dextró Movére")),
(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)
#aludoc("SIM", "07", "<<", 0)
/ Instruction format: #amal
]),
], ..sized_also("SIM", "Ad Sinistró Movére")),
(
mnem: "ILG",
name: "Immediátum Legere",
@ -544,27 +489,27 @@ Common Notations:
]),
(mnem: "VEL", name: "Vel", description: [
Bitwise disjunction between two words.
#aludoc("VEL", [11], sym.or, true, 0)
#aludoc("VEL", [11], sym.or, 0)
]),
(mnem: "NEC", name: "Nec", description: [
Bitwise negated disjunction between two words
#aludoc("NEC", [15], sym.not + sym.or, true, 0)
#aludoc("NEC", [15], sym.not + sym.or, 0)
]),
(mnem: "AUT", name: "Aut", description: [
Bitwise difference between two numbers.
#aludoc("AUT", [12], sym.xor, true, 0)
#aludoc("AUT", [12], sym.xor, 0)
]),
(mnem: "AEQ", name: "Aequí", description: [
Bitwise equality between two numbers.
#aludoc("AEQ", [16], [=], true, 0)
#aludoc("AEQ", [16], [=], 0)
]),
(mnem: "ET", name: "Et", description: [
Bitwise logical conjunction between two numbers.
#aludoc("ET", [10], sym.and, true, 0)
#aludoc("ET", [10], sym.and, 0)
]),
(mnem: "NAM", name: "Non ambó", description: [
Bitwise negated logical conjunction between two numbers.
#aludoc("NAM", [14], sym.not + sym.and, true, 0)
#aludoc("NAM", [14], sym.not + sym.and, 0)
]),
(
mnem: "LG",
@ -576,6 +521,7 @@ Common Notations:
#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.
],
..sized_also("LG", "Legere"),
),
(
mnem: "SC",
@ -587,6 +533,7 @@ Common Notations:
#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
],
..sized_also("SC", "Scríbere")
),
(mnem: "CESS", name: "Cessáre", description: [
*Privileged* Halts the CPU.
@ -655,7 +602,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 #istr(m); see #ristr("P" + it.mnem) and #ristr(it.mnem).
],
),
)

View File

@ -0,0 +1,17 @@
%YAML 1.2
---
name: struct
file_extensions:
- struct
scope: source.struct
contexts:
main:
- match: '(?i)\b(byte|hword|word|bit)\b'
scope: storage.type.struct
- match: '\b[0-9_]+\b'
scope: constant.numeric.struct
- match: '\b(struct|enum|union|flags)\s+[a-zA-Z0-9_]+\b'
scope: storage.type.struct
- match: '%.*$'
scope: comment

View File

@ -1,34 +1,298 @@
#import ".template.typ": *
#show: conf
#show regex("[\u{e3e0}-\u{e3ff}]+(\s+[\u{e3e0-\u{e3ff}]+)*"): text.with(
font: "AndikaAmbNaran",
)
#show raw.where(block: true): block.with(breakable: false)
#title[Unichal Software Distribution System ABI\ Version 3.4 for AIN-48]
© University of Chalmosique, Cross. 301 -- Cross. 319
= Object Format
b
= The RIM file format
The RIM#footnote[
   --- Rainsihen Iehanac Móscirts --- Computer
Instruction Format
] format is the format used by UCSD to encode machine code object files and executables.
It is defined as follows.
All numbers bigger than a byte are represented in little-endian.
The sizes of the types are as follows:
/ ```struct byte```: 12 bits
/ ```struct hword```: 24 bits, or 2 bytes
/ ```struct word```: 48 bits, or 4 bytes
All numbers are unsigned.
= Integer representation
= Calling convention
== Parameter passing
== Return value passing
== Call Sequence
== RIM Header
Placed at the start of the file, it identifies the file as a RIM object and contains some additional flags.
```ain48
.at #o00000000
fib_n:
ilg %2, #0
ilg %3, #1
__loop:
padd %4, %3, %2
tsc %2, %3
tsc %3, %3
pstr %1, #1
nui __loop
tsc %1, %2
rv
```struct
enum PLATFORM as hword:
UCSD-at12 = 00000000
UCSD-ain24 = 00000001
UCSD-ain48 = 00000002
% other formats may exist, as defined in their own implementation
% of the RIM specification
```
```struct
flags FILETYPE as byte:
EXECUTABLE = 0001
SHARED = 0002
REPOSITIONABLE = 0004
```
```struct
struct RIM_HEADER:
byte[4] magic_number = [0206, 0203, 0213, 7777]
% \7777 in the old AT12-IM codepage
enum PLATFORM platform
hword version % For UCSD 3.4: version = 0001
flags FILETYPE type
word index_address % offset in the file where the section table is
```
adjective
== Section Table
Placed at the file offset indicated by `index_address` in the header, the section table contains a list of all the sections, their lengths and their offsets into the file.
```struct
enum SEC_TYPE as byte:
RO_DATA = 0 % Data to be copied into non-executable read-only memory
CODE = 1 % Data to be copied into executable read-only memory
RW_DATA = 2 % Data to be copied into non-executable read-write memory
RW_CODE = 3 % Data to be copied into executable read-write memory
SYMBOLS_INTERNAL = 4 % Table listing all the linkable symbols of
% the object
SYMBOLS_EXTERNAL = 5 % Table listing all the symbols the objects needs
% to link to to be usable.
STRINGS = 6 % Container for all the strings for symbols and debug info
DEBUG = 7 % Debug information
META = 7777 % Extra Information about the object
```
```struct
struct SEC_TABLE_ENTRY:
enum SEC_TYPE type
adjective
word offset
```
```struct
struct SECTION_TABLE:
hword count
struct SEC_TABLE_ENTRY[count] entries
```
== Code and Data Sections
Sections tagged `RO_DATA`, `CODE`, `RW_DATA` and `RW_CODE` are all encoded the same way.
Ideally the start of such a section is to be at an offset that is a multiple of 4 bytes.
```struct
struct DATA_SECTION:
word length % How many bytes of data
word req_addr % Where should the data be located in memory. Note
% that this field is ignored if the RELOCATABLE flag
% is set in the header.
byte[length] data
```
== Symbol tables
The symbol tables contain data about the linkable symbols of the object.
```struct
struct SYMBOL_TABLE:
hword count
struct SYMBOL_TABLE_ENTRY[count] entries
```
```struct
struct SYMBOL_TABLE_ENTRY:
% Number of the string section and index into that section
% referencing the name of the symbol
hword string_section
hword string_number
% For internal symbol tables: the section and offset referring to the
% symbol. For external symbol tables: the section and offset where
% the address for that symbol needs to be replaced. If a symbol needs
% to be linked between two data sections of the object, have it be
% present in both the internal and external tables.
hword data_section
word data_offset
```
== Strings section
```struct
struct STRINGS_SECTION:
hword count % number of string entries
word len % length of the data part
word[count] entry_offsets % offsets into `data` of each string
byte[len] data % binary data containing the strings
```
== Debug
The contents of debug tables is platform-specific.
/ TODO: Write the debug info format for UCSD eventually™
== Meta
The meta section contains additional metadata about the file.
```struct
struct META_TABLE:
hword count
struct META_ENTRY[count] entries
```
```struct
struct META_ENTRY:
enum META_ENTRY_TYPE type
union META_VALUE val
```
```struct
union META_VALUE: % variant depends on meta entry type
struct META_VALUE_STRING string_value
word int_value
```
```struct
struct META_VALUE_STRING:
hword section % strings section number
hword id % entry in string section
```
```struct
enum META_ENTRY_TYPE as hword:
ENTRY_POINT = 0 % Kind: int_value, only useful when the file is said to
% be executable, gives the entry point for the program.
DYNAMIC_LINKER = 1 % Kind: string_value, the name of the dynamic linker
% in charge of resolving the symbols upon load of
% theobject, only useful if the SHARED flag is set.
HASH = 2 % Kind: string_value, a hash against which to verify the
% integrity of the object file. Supported algorithms depend on
% the linker used. The string is of the format
% `algorithm:octal-dump-of-hash`
% META_ENTRY_TYPEs between 1000 0000 and 3777 7777 (inclusive) are
% reserved for platform-specific information. Here are the ones for
% UCSD 3.4
UCSD_APPLICATION_NAME = 1000 0000 % Kind: string_value, user-facing name
% of the application
UCSD_ICON_DATA_SYM = 1000 0001 % Kind: string_value, name of a symbol
% referencing bitmap image data for an
% application icon
UCSD_ICON_DATA_NAME = 1000 0002 % Kind: string_value, name of a system-
% wide icon that may be installed on
% the system
% Multiple instances of UCSD_ICON_DATA_NAME and UCSD_ICON_DATA_SYM can
% be specified, the system will make use of the first one that resolves
% to a usable image.
% META_ENTRY_TYPEs greater or equal to 4000 0000 are reserved for
% private use by external tooling and are not specified in this
% document
```
= Representation of types
== Integers
#block(
breakable: false,
)[This ABI defines 6 integer types: `u12`, `u24` and `u48` which are unsigned 12, 24 and 48 bit integers, as well as `i12`, `i24` and `i48` which are signed (by the two's complement method) 12, 24 and 48 bit integers.
#grid(columns: (1fr, 1fr), row-gutter: .65em)[
/ `u12`: Unsigned 12-bit integer
/ Storage size: 1 byte
/ Alignment: 1 byte
][
/ `i12`: Signed 12-bit integer
/ Storage size: 1 byte
/ Alignment: 1 byte
][
/ `u24`: Unsigned 24-bit integer
/ Storage size: 2 bytes
/ Alignment: 2 bytes
][
/ `i24`: Signed 24-bit integer
/ Storage size: 2 bytes
/ Alignment: 2 bytes
][
/ `u48`: Unsigned 48-bit integer
/ Storage size: 4 bytes
/ Alignment: 4 bytes
][
/ `i48`: Signed 48-bit integer
/ Storage size: 4 bytes
/ Alignment: 4 bytes
]]
== Booleans
Booleans are represented as `u12`s, where 0 is false and any non-zero value (but canonically 7777) is true.
== Strings
Strings are represented by pointers to a `u12`, followed by that number of bytes of data representing the contents of the string.
= Userspace invocation convention
== Parameter passing
Parameters are expanded to take a full word and are placed in order:
#columns(2)[
+ in ```ain48 %01```
+ in ```ain48 %02```
+ in ```ain48 %03```
+ in ```ain48 %04```
#colbreak()
5. in ```ain48 %05```
+ in ```ain48 %06```
+ in ```ain48 %07```
+ pushed on the stack, with the 8#super[th] argument at the lowest address and the last argument at the highest address.
]
Arguments placed on the stack are placed starting at the word with the address immediately higher to that of the return adress.
== Return value passing
The return value is to be placed in ```ain48 %01```.
== Clobbering
Registers ```ain48 %01```, ```ain48 %02```, ```ain48 %03```, ```ain48 %04```, ```ain48 %05```,
```ain48 %06``` and ```ain48 %07``` may freely be clobbered by the invokee. It is the responsability of the invoker to save them if it is desired to keep their value. Registers ```ain48 %10```, ```ain48 %11```, ```ain48 %12```, ```ain48 %13```, ```ain48 %14```, ```ain48 %15```, ```ain48 %16``` and ```ain48 %it``` must be returned to the invoker in the same state they were upon call. It is the responsibility of the invokee to save them if they are needed for other uses.
== Invocation Sequence
+ The invoker pushes all the registers it needs saved to the stack
+ The invoker pushes the stack arguments to the stack
+ The invoker loads the registers with the register arguments
+ The register executes the ```ain48 IVC``` instruction. This pushes the return address to the stack and transfers control flow to the invokee.
+ The invokee allocates its local variables on top of the stack.
+ The invokee saves any register it needs to prevent the clobbering of.
+ The invokee runs its course
+ The invokee restores the registers
+ The invokee frees its local variables
+ The invokee loads its return value into ```ain48 %01```
+ The invokee runs the ```ain48 RV``` instruction. This pops the return address off the stack and transfers control flow back to the invoker.
+ The invoker frees the stack arguments.
+ The invoker restores the registers it pushed
```ain48
; An example
.init _init
.pars data_sl
.univ arg8, arg9
arg8: .word #o7612
arg9: .hword #o5 .byte #o0, #o0, #o0, #o0, #o0
.pars actiones
.univ _init
_init: CST %02 ; 1. Push registers to be saved
ILG %02, arg9 ; 2. Push 9th argument
CST %02
ILG %02, arg8 ; 2. Push 8th argument
LG %02, (%02)
CST %02
ILG %01, #o1 ; 3. Load register arguments
...
ILG %07, #o7
IVC res ; 4. Transfer to the invokee
PADD %it, #o10 ; 12. Free the 2 words of stack arguments
DST %02 ; 13. Restore the saved register
res: PSTR %it, #o14 ; 5. Allocate local variables (here 3 words)
CST %10 ; 6. Save invoker-safe registers
... ; 7. Res does whatever it does
DST %10 ; 8. Restore the invoker-safe registers
PADD %it, #o14 ; 9. Free the local variables
; 10. Assuming that res has already put its
; return value in %01
RV ; 11. Transfer back to the invoker
```
= Kernel invocation convention
Same as userspace invocation, except for the fact that all arguments are shifted by one, and that ```ain48 %01``` is used to hold the kernel invocation number.
#pagebreak(weak: true)
#outline(depth: 2)

55
editors/kak/a48s.kak Normal file
View File

@ -0,0 +1,55 @@
# ain48 assembly syntax highlighting
# put this in your autoload folder in ~/.config/kak/autoload and it should work
# note: if you are *creating* an autoload folder in that location,
# the *default* autoload scripts in /usr/share/kak/autoload will not trigger,
# so it might be a good idea to copy the contents of that directory.
hook global BufCreate .*\.(a|ain)48(s|asm) %{
set-option buffer filetype a48s
}
hook global WinSetOption filetype=a48s %{
require-module a48s
hook -once -always window WinSetOption filetype=.* %{
remove-hooks window a48s-.+
}
}
hook -group a48s-highlight global WinSetOption filetype=a48s %{
add-highlighter window/a48s ref a48s
hook -once -always window WinSetOption filetype=.* %{
remove-highlighter window/a48s
}
}
provide-module a48s %{
add-highlighter shared/a48s regions
add-highlighter shared/a48s/ region ';' '\n' fill comment
add-highlighter shared/a48s/dqstring region '"' '"' group
add-highlighter shared/a48s/dqstring/ fill string
add-highlighter shared/a48s/dqstring/ \
regex (\\[\\abefhnrtv\n])|(\\.) 1:keyword 2:Error
add-highlighter shared/a48s/code default-region group
# immediates
add-highlighter shared/a48s/code/ regex \
(?i)#-?(s[0-5]+|o[0-7]+|b[01]+|x[0-9]+)\b \
0:value
# registers
add-highlighter shared/a48s/code/ regex (?i)%([a-z0-7]+)\b 0:builtin
# directives
add-highlighter shared/a48s/code/ regex \.[a-zA-Z]+ 0:type
# labels
add-highlighter shared/a48s/code/ regex ^\h*[A-Za-z0-9_.-]+: 0:operator
# instructions
add-highlighter shared/a48s/code/ regex \
(?i)\b((p?(add|dem|dvd|i[mp]?lg|mpl|rsd|str))|(n?[enpu]?i)|(ir|nc)?rv|(nc)?ivc|aeq|an|aut|cess|cst|dem|dst|et|(p?[sbq])?(lg|sc)|nam|nec|sim|tsc|vel)\b \
0:keyword
# the regex is painfully long but i can't do anything about that so...
}

View File

@ -1,67 +0,0 @@
#ifndef AIN48__H
#define AIN48__H
#include <assert.h>
#include <common.h>
enum instruction_format {
AMAL,
IML,
IPL,
AMLS,
ASSS,
ASLI,
ASLR,
ASRD,
};
enum amal_ac : u8 {
ADD = 000,
STR = 001,
MPL = 002,
DVD = 004,
RSD = 005,
DEM = 006,
ET = 010,
VEL = 011,
AUT = 012,
NAM = 014,
NEC = 015,
};
enum asss_ac : u8 {
RV = 00,
IRRV = 04,
NCIVC = 03,
NCRV = 02,
};
// clang-format off
struct instruction {
enum instruction_format format;
union {
struct {
bool s, i1, i2; enum amal_ac ac; u4 rf;
union {
u8 imm; i8 simm;
struct {
union { u4 r2; u4 imm2; i4 simm2; };
union { u4 r1; u4 imm1; u4 simm1; };
};
} data;
} AMAL;
struct { u4 r; u48 imm; } IML;
struct { bool s; u4 r; union { u12 imm; i12 simm; }; } IPL;
struct { bool s; u4 rd; u4 rl; i12 imm; } AMLS;
enum asss_ac ASSS_ac;
struct { bool I, E, N, U, R; u48 imm; } ASLI;
struct { bool I, E, N, U, R; u4 rl; } ASLR;
struct { bool I, E, N, U, R; u4 rl; i12 imm; } ASRD;
};
};
// clang-format on
u12 *ain48_read_instr(u12 *code, struct instruction *i);
u12 *ain48_write_instr(u12 *code, struct instruction *i);
#endif // AIN48__H

View File

@ -1,48 +0,0 @@
#ifndef __UCSD_COMMON_H__
#define __UCSD_COMMON_H__
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
// # NUMBER TYPES
typedef unsigned _BitInt(4) u4;
typedef uint8_t u8;
typedef unsigned _BitInt(12) u12;
typedef uint16_t u16;
typedef uint32_t u32;
typedef unsigned _BitInt(48) u48;
typedef uint64_t u64;
typedef unsigned __int128 u128;
typedef size_t usz;
typedef uintptr_t uptr;
typedef _BitInt(4) i4;
typedef int8_t i8;
typedef _BitInt(12) i12;
typedef int16_t i16;
typedef int32_t i32;
typedef _BitInt(48) i48;
typedef int64_t i64;
typedef __int128 i128;
typedef ssize_t isz;
typedef intptr_t iptr;
typedef float f32;
typedef double f64;
// # LOGGING FACILITES
[[gnu::format(printf, 1, 2)]] void log_debug(char const *format, ...);
[[gnu::format(printf, 1, 2)]] void log_warn(char const *format, ...);
[[gnu::format(printf, 1, 2)]] void log_err(char const *format, ...);
[[gnu::format(printf, 1, 2)]] void log_err_errno(char const *format, ...);
void set_log_opts(FILE *f, bool colours);
extern int foo;
#endif // ndef __UCSD_COMMON_H__

34
programs/ain48.cabal Normal file
View File

@ -0,0 +1,34 @@
cabal-version: 3.0
-- The cabal-version field refers to the version of the .cabal specification,
-- and can be different from the cabal-install (the tool) version and the
-- Cabal (the library) version you are using. As such, the Cabal (the library)
-- version used must be equal or greater than the version stated in this field.
-- Starting from the specification version 2.2, the cabal-version field must be
-- the first thing in the cabal file.
-- Initial package description 'ain48' generated by
name: ain48
version: 0.1.0.0
homepage: https://git.annwan.me/worldbuilding/ain48
license: MIT
author: Annwan
maintainer: annwan@annwan.me
build-type: Simple
common warnings
ghc-options: -Wall
library
default-language: GHC2024
hs-source-dirs: common
build-depends: base ^>= 4.21.2.0
exposed-modules: Ain48.Types
executable dump12
import: warnings
main-is: Main.hs
-- other-modules:
build-depends: base ^>=4.21.2.0
, ain48
, optparse-applicative
hs-source-dirs: dump12
default-language: GHC2024

View File

@ -0,0 +1,383 @@
module Ain48.Types
( module Data.Word
, module Data.Int
, Word4(..), Word6(..), Word12(..), Word24(..), Word48(..)
, Int4(..), Int6(..), Int12(..), Int24(..), Int48(..),
) where
import Data.Word (Word8, Word16, Word32, Word64)
import Data.Int (Int8, Int16, Int32, Int64)
import Data.Bits ((.&.))
import Data.Bifunctor (bimap)
newtype Word4 = Word4 { unWord4 :: Word8 } deriving (Eq)
instance Show Word4 where
show :: Word4 -> String
show = show . unWord4
instance Num Word4 where
(+) :: Word4 -> Word4 -> Word4
(+) a b = Word4 $ unWord4 a + unWord4 b
(*) :: Word4 -> Word4 -> Word4
(*) a b = Word4 $ unWord4 a * unWord4 b
(-) :: Word4 -> Word4 -> Word4
(-) a b = Word4 $ unWord4 a - unWord4 b
abs :: Word4 -> Word4
abs = Word4 . abs . unWord4
signum :: Word4 -> Word4
signum = Word4 . signum . unWord4
fromInteger :: Integer -> Word4
fromInteger = Word4 . (.&. 0xF) . fromInteger
instance Ord Word4 where
compare :: Word4 -> Word4 -> Ordering
compare a b = compare (unWord4 a) (unWord4 b)
instance Real Word4 where
toRational :: Word4 -> Rational
toRational = toRational . unWord4
instance Bounded Word4 where
minBound :: Word4
minBound = Word4 0
maxBound :: Word4
maxBound = Word4 0xF
instance Enum Word4 where
fromEnum :: Word4 -> Int
fromEnum = fromEnum . unWord4
toEnum :: Int -> Word4
toEnum = Word4 . (.&. 0xF) . toEnum
instance Integral Word4 where
quotRem :: Word4 -> Word4 -> (Word4, Word4)
quotRem a b = bimap Word4 Word4 $ quotRem (unWord4 a) (unWord4 b)
toInteger :: Word4 -> Integer
toInteger = toInteger . unWord4
newtype Word6 = Word6 { unWord6 :: Word8 } deriving (Eq)
instance Show Word6 where
show :: Word6 -> String
show = show . unWord6
instance Num Word6 where
(+) :: Word6 -> Word6 -> Word6
(+) a b = Word6 $ unWord6 a + unWord6 b
(*) :: Word6 -> Word6 -> Word6
(*) a b = Word6 $ unWord6 a * unWord6 b
(-) :: Word6 -> Word6 -> Word6
(-) a b = Word6 $ unWord6 a - unWord6 b
abs :: Word6 -> Word6
abs = Word6 . abs . unWord6
signum :: Word6 -> Word6
signum = Word6 . signum . unWord6
fromInteger :: Integer -> Word6
fromInteger = Word6 . (.&. 0x3F) . fromInteger
instance Ord Word6 where
compare :: Word6 -> Word6 -> Ordering
compare a b = compare (unWord6 a) (unWord6 b)
instance Real Word6 where
toRational :: Word6 -> Rational
toRational = toRational . unWord6
instance Bounded Word6 where
minBound :: Word6
minBound = Word6 0
maxBound :: Word6
maxBound = Word6 0x3F
instance Enum Word6 where
fromEnum :: Word6 -> Int
fromEnum = fromEnum . unWord6
toEnum :: Int -> Word6
toEnum = Word6 . (.&. 0x3F) . toEnum
instance Integral Word6 where
quotRem :: Word6 -> Word6 -> (Word6, Word6)
quotRem a b = bimap Word6 Word6 $ quotRem (unWord6 a) (unWord6 b)
toInteger :: Word6 -> Integer
toInteger = toInteger . unWord6
newtype Word12 = Word12 { unWord12 :: Word16 } deriving (Eq)
instance Show Word12 where
show :: Word12 -> String
show = show . unWord12
instance Num Word12 where
(+) :: Word12 -> Word12 -> Word12
(+) a b = Word12 $ unWord12 a + unWord12 b
(*) :: Word12 -> Word12 -> Word12
(*) a b = Word12 $ unWord12 a * unWord12 b
(-) :: Word12 -> Word12 -> Word12
(-) a b = Word12 $ unWord12 a - unWord12 b
abs :: Word12 -> Word12
abs = Word12 . abs . unWord12
signum :: Word12 -> Word12
signum = Word12 . signum . unWord12
fromInteger :: Integer -> Word12
fromInteger = Word12 . (.&. 0xFFF) . fromInteger
instance Ord Word12 where
compare :: Word12 -> Word12 -> Ordering
compare a b = compare (unWord12 a) (unWord12 b)
instance Real Word12 where
toRational :: Word12 -> Rational
toRational = toRational . unWord12
instance Bounded Word12 where
minBound :: Word12
minBound = Word12 0
maxBound :: Word12
maxBound = Word12 0xFFF
instance Enum Word12 where
fromEnum :: Word12 -> Int
fromEnum = fromEnum . unWord12
toEnum :: Int -> Word12
toEnum = Word12 . (.&. 0xFFF) . toEnum
instance Integral Word12 where
quotRem :: Word12 -> Word12 -> (Word12, Word12)
quotRem a b = bimap Word12 Word12 $ quotRem (unWord12 a) (unWord12 b)
toInteger :: Word12 -> Integer
toInteger = toInteger . unWord12
newtype Word24 = Word24 { unWord24 :: Word32 } deriving (Eq)
instance Show Word24 where
show :: Word24 -> String
show = show . unWord24
instance Num Word24 where
(+) :: Word24 -> Word24 -> Word24
(+) a b = Word24 $ unWord24 a + unWord24 b
(*) :: Word24 -> Word24 -> Word24
(*) a b = Word24 $ unWord24 a * unWord24 b
(-) :: Word24 -> Word24 -> Word24
(-) a b = Word24 $ unWord24 a - unWord24 b
abs :: Word24 -> Word24
abs = Word24 . abs . unWord24
signum :: Word24 -> Word24
signum = Word24 . signum . unWord24
fromInteger :: Integer -> Word24
fromInteger = Word24 . (.&. 0xFFFFFF) . fromInteger
instance Ord Word24 where
compare :: Word24 -> Word24 -> Ordering
compare a b = compare (unWord24 a) (unWord24 b)
instance Real Word24 where
toRational :: Word24 -> Rational
toRational = toRational . unWord24
instance Bounded Word24 where
minBound :: Word24
minBound = Word24 0
maxBound :: Word24
maxBound = Word24 0xFFFFFF
instance Enum Word24 where
fromEnum :: Word24 -> Int
fromEnum = fromEnum . unWord24
toEnum :: Int -> Word24
toEnum = Word24 . (.&. 0xFFFFFF) . toEnum
instance Integral Word24 where
quotRem :: Word24 -> Word24 -> (Word24, Word24)
quotRem a b = bimap Word24 Word24 $ quotRem (unWord24 a) (unWord24 b)
toInteger :: Word24 -> Integer
toInteger = toInteger . unWord24
newtype Word48 = Word48 { unWord48 :: Word64 } deriving (Eq)
instance Show Word48 where
show :: Word48 -> String
show = show . unWord48
instance Num Word48 where
(+) :: Word48 -> Word48 -> Word48
(+) a b = Word48 $ unWord48 a + unWord48 b
(*) :: Word48 -> Word48 -> Word48
(*) a b = Word48 $ unWord48 a * unWord48 b
(-) :: Word48 -> Word48 -> Word48
(-) a b = Word48 $ unWord48 a - unWord48 b
abs :: Word48 -> Word48
abs = Word48 . abs . unWord48
signum :: Word48 -> Word48
signum = Word48 . signum . unWord48
fromInteger :: Integer -> Word48
fromInteger = Word48 . (.&. 0xFFFFFFFFFFFF) . fromInteger
instance Ord Word48 where
compare :: Word48 -> Word48 -> Ordering
compare a b = compare (unWord48 a) (unWord48 b)
instance Real Word48 where
toRational :: Word48 -> Rational
toRational = toRational . unWord48
instance Bounded Word48 where
minBound :: Word48
minBound = Word48 0
maxBound :: Word48
maxBound = Word48 0xFFFFFFFFFFFF
instance Enum Word48 where
fromEnum :: Word48 -> Int
fromEnum = fromEnum . unWord48
toEnum :: Int -> Word48
toEnum = Word48 . (.&. 0xFFFFFFFFFFFF) . toEnum
instance Integral Word48 where
quotRem :: Word48 -> Word48 -> (Word48, Word48)
quotRem a b = bimap Word48 Word48 $ quotRem (unWord48 a) (unWord48 b)
toInteger :: Word48 -> Integer
toInteger = toInteger . unWord48
newtype Int4 = Int4 { unInt4 :: Int8 } deriving (Eq)
instance Show Int4 where
show :: Int4 -> String
show = show . unInt4
instance Num Int4 where
(+) :: Int4 -> Int4 -> Int4
(+) a b = Int4 $ unInt4 a + unInt4 b
(*) :: Int4 -> Int4 -> Int4
(*) a b = Int4 $ unInt4 a * unInt4 b
(-) :: Int4 -> Int4 -> Int4
(-) a b = Int4 $ unInt4 a - unInt4 b
abs :: Int4 -> Int4
abs = Int4 . abs . unInt4
signum :: Int4 -> Int4
signum = Int4 . signum . unInt4
fromInteger :: Integer -> Int4
fromInteger = Int4 . (.&. 0xF) . fromInteger
instance Ord Int4 where
compare :: Int4 -> Int4 -> Ordering
compare a b = compare (unInt4 a) (unInt4 b)
instance Real Int4 where
toRational :: Int4 -> Rational
toRational = toRational . unInt4
instance Bounded Int4 where
minBound :: Int4
minBound = Int4 (-8)
maxBound :: Int4
maxBound = Int4 7
instance Enum Int4 where
fromEnum :: Int4 -> Int
fromEnum = fromEnum . unInt4
toEnum :: Int -> Int4
toEnum = Int4 . (.&. 0xF) . toEnum
instance Integral Int4 where
quotRem :: Int4 -> Int4 -> (Int4, Int4)
quotRem a b = bimap Int4 Int4 $ quotRem (unInt4 a) (unInt4 b)
toInteger :: Int4 -> Integer
toInteger = toInteger . unInt4
newtype Int6 = Int6 { unInt6 :: Int8 } deriving (Eq)
instance Show Int6 where
show :: Int6 -> String
show = show . unInt6
instance Num Int6 where
(+) :: Int6 -> Int6 -> Int6
(+) a b = Int6 $ unInt6 a + unInt6 b
(*) :: Int6 -> Int6 -> Int6
(*) a b = Int6 $ unInt6 a * unInt6 b
(-) :: Int6 -> Int6 -> Int6
(-) a b = Int6 $ unInt6 a - unInt6 b
abs :: Int6 -> Int6
abs = Int6 . abs . unInt6
signum :: Int6 -> Int6
signum = Int6 . signum . unInt6
fromInteger :: Integer -> Int6
fromInteger = Int6 . (.&. 0x3F) . fromInteger
instance Ord Int6 where
compare :: Int6 -> Int6 -> Ordering
compare a b = compare (unInt6 a) (unInt6 b)
instance Real Int6 where
toRational :: Int6 -> Rational
toRational = toRational . unInt6
instance Bounded Int6 where
minBound :: Int6
minBound = Int6 (-32)
maxBound :: Int6
maxBound = Int6 31
instance Enum Int6 where
fromEnum :: Int6 -> Int
fromEnum = fromEnum . unInt6
toEnum :: Int -> Int6
toEnum = Int6 . (.&. 0x3F) . toEnum
instance Integral Int6 where
quotRem :: Int6 -> Int6 -> (Int6, Int6)
quotRem a b = (bimap Int6 Int6) $ quotRem (unInt6 a) (unInt6 b)
toInteger :: Int6 -> Integer
toInteger = toInteger . unInt6
newtype Int12 = Int12 { unInt12 :: Int16 } deriving (Eq)
instance Show Int12 where
show :: Int12 -> String
show = show . unInt12
instance Num Int12 where
(+) :: Int12 -> Int12 -> Int12
(+) a b = Int12 $ unInt12 a + unInt12 b
(*) :: Int12 -> Int12 -> Int12
(*) a b = Int12 $ unInt12 a * unInt12 b
(-) :: Int12 -> Int12 -> Int12
(-) a b = Int12 $ unInt12 a - unInt12 b
abs :: Int12 -> Int12
abs = Int12 . abs . unInt12
signum :: Int12 -> Int12
signum = Int12 . signum . unInt12
fromInteger :: Integer -> Int12
fromInteger = Int12 . (.&. 0xFFF) . fromInteger
instance Ord Int12 where
compare :: Int12 -> Int12 -> Ordering
compare a b = compare (unInt12 a) (unInt12 b)
instance Real Int12 where
toRational :: Int12 -> Rational
toRational = toRational . unInt12
instance Bounded Int12 where
minBound :: Int12
minBound = Int12 (-2048)
maxBound :: Int12
maxBound = Int12 2047
instance Enum Int12 where
fromEnum :: Int12 -> Int
fromEnum = fromEnum . unInt12
toEnum :: Int -> Int12
toEnum = Int12 . (.&. 0xFFF) . toEnum
instance Integral Int12 where
quotRem :: Int12 -> Int12 -> (Int12, Int12)
quotRem a b = bimap Int12 Int12 $ quotRem (unInt12 a) (unInt12 b)
toInteger :: Int12 -> Integer
toInteger = toInteger . unInt12
newtype Int24 = Int24 { unInt24 :: Int32 } deriving (Eq)
instance Show Int24 where
show :: Int24 -> String
show = show . unInt24
instance Num Int24 where
(+) :: Int24 -> Int24 -> Int24
(+) a b = Int24 $ unInt24 a + unInt24 b
(*) :: Int24 -> Int24 -> Int24
(*) a b = Int24 $ unInt24 a * unInt24 b
(-) :: Int24 -> Int24 -> Int24
(-) a b = Int24 $ unInt24 a - unInt24 b
abs :: Int24 -> Int24
abs = Int24 . abs . unInt24
signum :: Int24 -> Int24
signum = Int24 . signum . unInt24
fromInteger :: Integer -> Int24
fromInteger = Int24 . (.&. 0xFFFFFF) . fromInteger
instance Ord Int24 where
compare :: Int24 -> Int24 -> Ordering
compare a b = compare (unInt24 a) (unInt24 b)
instance Real Int24 where
toRational :: Int24 -> Rational
toRational = toRational . unInt24
instance Bounded Int24 where
minBound :: Int24
minBound = Int24 (-8388608)
maxBound :: Int24
maxBound = Int24 8388607
instance Enum Int24 where
fromEnum :: Int24 -> Int
fromEnum = fromEnum . unInt24
toEnum :: Int -> Int24
toEnum = Int24 . (.&. 0xFFFFFF) . toEnum
instance Integral Int24 where
quotRem :: Int24 -> Int24 -> (Int24, Int24)
quotRem a b = bimap Int24 Int24 $ quotRem (unInt24 a) (unInt24 b)
toInteger :: Int24 -> Integer
toInteger = toInteger . unInt24
newtype Int48 = Int48 { unInt48 :: Int64 } deriving (Eq)
instance Show Int48 where
show :: Int48 -> String
show = show . unInt48
instance Num Int48 where
(+) :: Int48 -> Int48 -> Int48
(+) a b = Int48 $ unInt48 a + unInt48 b
(*) :: Int48 -> Int48 -> Int48
(*) a b = Int48 $ unInt48 a * unInt48 b
(-) :: Int48 -> Int48 -> Int48
(-) a b = Int48 $ unInt48 a - unInt48 b
abs :: Int48 -> Int48
abs = Int48 . abs . unInt48
signum :: Int48 -> Int48
signum = Int48 . signum . unInt48
fromInteger = Int48 . (.&. 0xFFFFFFFFFFFF) . fromInteger
instance Ord Int48 where
compare a b = compare (unInt48 a) (unInt48 b)
instance Real Int48 where
toRational = toRational . unInt48
instance Bounded Int48 where
minBound = Int48 (-140737488355328)
maxBound = Int48 140737488355327
instance Enum Int48 where
fromEnum = fromEnum . unInt48
toEnum = Int48 . (.&. 0xFFFFFFFFFFFF) . toEnum
instance Integral Int48 where
quotRem a b = (\(a, b) -> (Int48 a, Int48 b)) $ quotRem (unInt48 a) (unInt48 b)
toInteger = toInteger . unInt48

16
programs/dump12/Main.hs Normal file
View File

@ -0,0 +1,16 @@
module Main (main) where
import Ain48.Types
import Options.Applicative
parser_opts :: Parser Bool
parser_opts = switch ( long "decode"
<> short 'd'
<> help "Decode a binary file instead of encoding a dump file")
main :: IO ()
main = do
opts <- execParser $ info (parser_opts <**> helper)
( fullDesc <> progDesc "Convert to and from 12 bit byte binaries"
<> header "dump12 --- conversion utility" )
print opts

View File

@ -1,191 +0,0 @@
#include <ain48.h>
#include <string.h>
u12 *ain48_write_instruction(u12 *code, struct instruction *i) {
u4 u4tmp;
u8 u8tmp;
switch (i->format) {
case AMAL:
*code++ = i->AMAL.ac | (i->AMAL.s ? 1 << 4 : 0) |
(i->AMAL.i1 ? 1 << 5 : 0) | (i->AMAL.i2 ? 1 << 6 : 0);
*code = ((u12)i->AMAL.rf) << 8;
if (!i->AMAL.i1)
*code |= ((u12)i->AMAL.data.r1) << 4;
if (!i->AMAL.i2)
*code |= ((u12)i->AMAL.data.r2);
if (i->AMAL.i1 && i->AMAL.i2) {
if (i->AMAL.s)
memcpy(&u8tmp, &i->AMAL.data.simm, sizeof(u8));
else
u8tmp = i->AMAL.data.imm;
*code |= u8tmp;
} else if (i->AMAL.i1) {
if (i->AMAL.s)
memcpy(&u4tmp, &i->AMAL.data.simm1, sizeof(u4));
else
u4tmp = i->AMAL.data.imm1;
*code |= u4tmp;
} else if (i->AMAL.i2) {
if (i->AMAL.s)
memcpy(&u4tmp, &i->AMAL.data.simm2, sizeof(u4));
else
u4tmp = i->AMAL.data.imm2;
*code |= ((u12)u4tmp) << 4;
}
return ++code;
case IML:
*code++ = 00400 | i->IML.r;
*code++ = i->IML.imm & 07777;
*code++ = (i->IML.imm >> 12) & 07777;
*code++ = (i->IML.imm >> 24) & 07777;
*code++ = (i->IML.imm >> 36) & 07777;
return code;
case IPL:
*code++ = 00500 | (i->IPL.s ? (1 << 4) : 0) | i->IPL.r;
if (i->IPL.s) {
memcpy(code++, &i->IPL.simm, sizeof(u12));
} else {
*code++ = i->IPL.imm;
}
return code;
case AMLS:
*code++ = 01000 | (i->AMLS.s ? 1 << 8 : 0) | (((u12)i->AMLS.rd) << 4) |
i->AMLS.rl;
memcpy(code++, &i->AMLS.imm, sizeof(u12));
return code;
case ASSS:
*code++ = 07770 | i->ASSS_ac;
return code;
case ASLI:
*code++ = 04000 | (i->ASLI.R ? 1 << 0 : 0) | (i->ASLI.U ? 1 << 1 : 0) |
(i->ASLI.N ? 1 << 2 : 0) | (i->ASLI.E ? 1 << 3 : 0) |
(i->ASLI.I ? 1 << 4 : 0);
*code++ = i->ASLI.imm & 07777;
*code++ = (i->ASLI.imm >> 12) & 07777;
*code++ = (i->ASLI.imm >> 24) & 07777;
*code++ = (i->ASLI.imm >> 36) & 07777;
return code;
case ASLR:
*code++ = 05000 | (i->ASLR.R ? 1 << 0 : 0) | (i->ASLR.U ? 1 << 1 : 0) |
(i->ASLR.N ? 1 << 2 : 0) | (i->ASLR.E ? 1 << 3 : 0) |
(i->ASLR.I ? 1 << 4 : 0) | (((u12)i->ASLR.rl) << 5);
return code;
case ASRD:
*code++ = 05000 | (i->ASRD.R ? 1 << 0 : 0) | (i->ASRD.U ? 1 << 1 : 0) |
(i->ASRD.N ? 1 << 2 : 0) | (i->ASRD.E ? 1 << 3 : 0) |
(i->ASLR.I ? 1 << 4 : 0) | (((u12)i->ASRD.rl) << 5);
memcpy(code++, &i->ASRD.imm, sizeof(u12));
return code;
default:
return nullptr;
}
}
u12 *ain48_read_instr(u12 *code, struct instruction *i) {
u12 byte = *code++;
if ((byte & 07600) == 0) { // AMAL
i->format = AMAL;
i->AMAL.i1 = (byte & 00040) > 0;
i->AMAL.i2 = (byte & 00100) > 0;
i->AMAL.s = (byte & 00020) > 0;
i->AMAL.ac = (enum amal_ac) (byte & 00017);
byte = *code++;
i->AMAL.rf = (byte >> 8) & 017;
if (!i->AMAL.i1)
i->AMAL.data.r1 = (byte >> 4) & 017;
if (!i->AMAL.i2)
i->AMAL.data.r2 = byte & 017;
if (i->AMAL.i1 && i->AMAL.i2) {
if (i->AMAL.s) {
u8 tmp = byte & 0377;
memcpy(&i->AMAL.data.simm, &tmp, sizeof(i8));
} else
i->AMAL.data.imm = byte & 0337;
} else if (i->AMAL.i1) {
if (i->AMAL.s) {
u4 tmp = byte & 017;
memcpy(&i->AMAL.data.simm1, &tmp, sizeof(i4));
}
else
i->AMAL.data.imm1 = byte & 017;
} else if (i->AMAL.i2) {
if (i->AMAL.s) {
u4 tmp = (byte >> 4) & 017;
memcpy(&i->AMAL.data.simm2, &tmp, sizeof(i4));
}
else
i->AMAL.data.imm2 = (byte >> 4) & 017;
}
return code;
} else if ((byte & 07600) == 00400) { // IML
i->format = IML;
i->IML.r = byte & 017;
byte = *code++;
i->IML.imm = (u48)byte;
byte = *code++;
i->IML.imm |= ((u48)byte << 12);
byte = *code++;
i->IML.imm |= ((u48)byte << 24);
byte = *code++;
i->IML.imm |= ((u48)byte << 36);
return code;
} else if ((byte & 07600) == 00600) { // IPL
i->format = IPL;
i->IPL.r = byte & 017;
i->IPL.s = (byte & 00020) > 0;
byte = *code++;
if (i->IPL.s) {
memcpy(&i->IPL.simm, &byte, sizeof(i12));
} else {
i->IPL.imm = byte;
}
return code;
} else if ((byte & 07000) == 01000) { // AMLS
i->format = AMLS;
i->AMLS.s = (byte & 00400) > 0;
i->AMLS.rd = (byte >> 4) & 017;
i->AMLS.rl = byte & 017;
memcpy(&i->AMLS.imm, code++, sizeof(i12));
return code;
} else if ((byte & 07770) == 07770) { // ASSS
i->format = ASSS;
i->ASSS_ac = byte & 00007;
return code;
} else if ((byte & 07000) == 04000) { // ASLI
i->format = ASLI;
i->ASLI.R = (byte & 0b1) > 0;
i->ASLI.U = (byte & 0b10) > 0;
i->ASLI.N = (byte & 0b100) > 0;
i->ASLI.E = (byte & 0b1000) > 0;
i->ASLI.I = (byte & 0b10000) > 0;
byte = *code++;
i->ASLI.imm = (u48)byte;
byte = *code++;
i->ASLI.imm |= ((u48)byte << 12);
byte = *code++;
i->ASLI.imm |= ((u48)byte << 24);
byte = *code++;
i->ASLI.imm |= ((u48)byte << 36);
return code;
} else if ((byte & 07000) == 05000) { // ASLR
i->format = ASLR;
i->ASLR.R = (byte & 0b1) > 0;
i->ASLR.U = (byte & 0b10) > 0;
i->ASLR.N = (byte & 0b100) > 0;
i->ASLR.E = (byte & 0b1000) > 0;
i->ASLR.I = (byte & 0b10000) > 0;
i->ASLR.rl = (byte >> 5) & 017;
return code;
} else if ((byte & 07000) == 06000) { // ASRD
i->format = ASRD;
i->ASRD.R = (byte & 0b1) > 0;
i->ASRD.U = (byte & 0b10) > 0;
i->ASRD.N = (byte & 0b100) > 0;
i->ASRD.E = (byte & 0b1000) > 0;
i->ASRD.I = (byte & 0b10000) > 0;
i->ASRD.rl = (byte >> 5) & 017;
memcpy(&i->ASRD.imm, code++, sizeof(i12));
return code;
} else return nullptr; // ILLEGAL INSTRUCTION
}

View File

@ -1,29 +0,0 @@
#include <common.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <ain48.h>
i32 asm_main(i32 argc, char *argv[]) {
if (argc != 3) {
log_err("Usage: asm source.a48s dest.bin");
return 1;
}
int soruce_file = open(argv[1], O_RDONLY);
if (soruce_file < 0) {
log_err_errno("Failed to open file `%s`", argv[1]);
return 1;
}
struct stat statres;
if (fstat(soruce_file, &statres) < 0) {
log_err_errno("Failed to stat file `%s`", argv[1]);
return 1;
}
if (statres.st_size == 0) {
log_err("Cannot assemble an empty file");
return 1;
}
return 0;
}

View File

@ -1,52 +0,0 @@
#include <common.h>
#include <stdio.h>
#include <errno.h>
#include <stdarg.h>
#include <string.h>
FILE *logfile;
bool logcolours = true;
[[gnu::constructor(101)]] void init_logging() {
logfile = stderr;
}
#ifndef DEBUG
void log_debug([[maybe_unused]] const char* format, ...) {}
#else
void log_debug(const char* format, ...) {
fprintf(logfile, "%s[DEBUG]%s ", logcolours ? "\033[1;44;97m" : "", logcolours ? "\033[0" : "");
va_list va;
va_start(va);
vfprintf(logfile, format, va);
va_end(va);
fprintf(logfile, "\n");
}
#endif
void log_warn(const char* format, ...) {
fprintf(logfile, "%s[WARN]%s ", logcolours ? "\033[1;43;97m": "", logcolours ? "\033[0m" : "");
va_list va;
va_start(va);
vfprintf(logfile, format, va);
va_end(va);
fprintf(logfile, "\n");
}
void log_err(const char* format, ...) {
fprintf(logfile, "%s[ERROR]%s ", logcolours ? "\033[1;41;97m": "", logcolours ? "\033[0m" : "");
va_list va;
va_start(va);
vfprintf(logfile, format, va);
va_end(va);
fprintf(logfile, "\n");
}
void log_err_errno(const char* format, ...) {
fprintf(logfile, "%s[ERROR]%s ", logcolours ? "\033[1;41;97m": "", logcolours ? "\033[0m" : "");
va_list va;
va_start(va);
vfprintf(logfile, format, va);
va_end(va);
fprintf(logfile, ": %s\n", strerror(errno));
}
void set_log_opts(FILE *f, bool colours) {
logfile = f;
logcolours = colours;
}

View File

@ -1,26 +0,0 @@
#include <common.h>
#include <stdio.h>
#include <string.h>
enum subprorgram {
ASM,
SIM,
};
const char *available = (R"m(
asm)m");
i32 asm_main(i32 argc, char *argv[]);
i32 main(i32 argc, char *argv[]) {
if (argc < 2) {
log_err("No subprogram called\nAvalible subprograms:%s", available);
return 1;
}
if (!strcmp(argv[1], "asm")) {
return asm_main(argc - 1, argv + 1);
} else {
log_err("Unknown subprogram `%s`\nAvailable subpograms:%s", argv[1], available);
return 1;
}
}