From 5788c5d8076eef7616b73a849bd34b6da44df4f2 Mon Sep 17 00:00:00 2001 From: Annwan Date: Sun, 31 May 2026 12:33:05 +0200 Subject: [PATCH] initial commit --- .clang-format | 6 + .gitignore | 5 + Makefile | 61 +++ docs/.template.typ | 31 ++ docs/ain48.sublime-syntax | 28 ++ docs/gruvbox-white.tmTheme | 828 +++++++++++++++++++++++++++++++++++++ docs/isa.typ | 677 ++++++++++++++++++++++++++++++ docs/ucsd_abi.typ | 35 ++ include/ain48.h | 11 + include/common.h | 47 +++ src/asm.c | 28 ++ src/logging.c | 52 +++ src/main.c | 27 ++ 13 files changed, 1836 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 docs/.template.typ create mode 100644 docs/ain48.sublime-syntax create mode 100644 docs/gruvbox-white.tmTheme create mode 100644 docs/isa.typ create mode 100644 docs/ucsd_abi.typ create mode 100644 include/ain48.h create mode 100644 include/common.h create mode 100644 src/asm.c create mode 100644 src/logging.c create mode 100644 src/main.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..258338e --- /dev/null +++ b/.clang-format @@ -0,0 +1,6 @@ +--- +BasedOnStyle: LLVM +AccessModifierOffset: -4 +IndentWidth: 4 +QualifierAlignment: Right + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1236c25 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/compile_commands.json +/out +/docs/*.pdf +/ain48 + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..94df9ce --- /dev/null +++ b/Makefile @@ -0,0 +1,61 @@ +# 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, out/%.d, $(SRCS)) +OBJS=$(patsubst src/%.c, out/%.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) $(PROGRAM) + $(RM) $(DOCS) +deepclean: clean + $(RM) $(OBJS) + $(RM) $(DEPS) + $(RM) compile_commands.json + +$(PROGRAM): $(OBJS) + $(LD) $(LDFLAGS) $(OBJS) -o $@ + + +out/%.d: src/%.c + @set -e ; $(RM) $@; \ + $(CC) -M $(CFLAGS) $< > $@.$$$$; \ + sed 's,\($*\)\.o[ :]*,out/\1.o $@ : ,g' < $@.$$$$ > $@; \ + $(RM) $@.$$$$ + +out/%.o: src/%.c + $(CC) $(CFLAGS) -c $< -o $@ + +include $(DEPS) + diff --git a/docs/.template.typ b/docs/.template.typ new file mode 100644 index 0000000..820fe76 --- /dev/null +++ b/docs/.template.typ @@ -0,0 +1,31 @@ +#import "@preview/cetz:0.5.2" +#import "@preview/smartaref:0.1.0": cref +#import "@preview/libra:0.1.0": balance +#let conf(doc) = { + set page(paper: "a4", numbering: "1") + set text( + font: "Iosevka Etoile", + features: (cv47: 10), + number-type: "old-style", + weight: 400, + size: 12pt, + ) + 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) + show heading.where(level: 3): set heading(numbering: none) + show figure.caption: it => balance(box(width: 100%, { + it.supplement + [ ] + it.counter.display(it.numbering) + [ --- ] + it.body + })) + set raw(theme: "./gruvbox-white.tmTheme") + show raw.where(lang: "ain48"): set raw(syntaxes: "./ain48.sublime-syntax") + doc +} diff --git a/docs/ain48.sublime-syntax b/docs/ain48.sublime-syntax new file mode 100644 index 0000000..d510cc5 --- /dev/null +++ b/docs/ain48.sublime-syntax @@ -0,0 +1,28 @@ +%YAML 1.2 +--- +name: Ain48 +file_extensions: + - a48s +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' + 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' + scope: constant.numeric.ain48 + - match: '#imm\[[0-9n]+\]' + scope: constant.numeric.ain48 + - match: ';' + scope: comment + push: comments + - match: '^[a-zA-Z0-9_-]+:$' + scope: keyword.control.label.ain48 + - match: '^.[a-zA-Z]+' + scope: keyword.directive.ain48 + comments: + - meta_scope: comment + - match: '\n' + scope: comment + pop: true diff --git a/docs/gruvbox-white.tmTheme b/docs/gruvbox-white.tmTheme new file mode 100644 index 0000000..4b062b7 --- /dev/null +++ b/docs/gruvbox-white.tmTheme @@ -0,0 +1,828 @@ + + + + + comment + Based on original gruvbox color scheme. + author + Martin Radimec + name + gruvbox + settings + + + settings + + background + #ffffff + + caret + #3C3836 + + foreground + #3C3836aa + + invisibles + #DA9734 + + lineHighlight + #FAF4D9 + + selection + #EDDAB5 + + bracketContentsForeground + #928374 + + bracketsForeground + #d5c4a1 + + guide + #EDDAB5 + + activeGuide + #7c6f64 + + stackGuide + #BEAD95 + + + + name + Punctuation + scope + punctuation.definition.tag + settings + + fontStyle + + foreground + #418587 + + + + name + Punctuation + scope + punctuation.definition.entity + settings + + fontStyle + + foreground + #B36586 + + + + name + Constant + scope + constant + settings + + fontStyle + + foreground + #B36586 + + + + name + Constant escape + scope + constant.character.escape + settings + + fontStyle + + foreground + #9A942C + + + + name + Constant other + scope + constant.other + settings + + fontStyle + + foreground + #282828 + + + + name + Entity + scope + entity + settings + + fontStyle + + foreground + #679B6C + + + + name + Keyword + scope + keyword.operator.comparison, keyword.operator, keyword.operator.symbolic, keyword.operator.string, keyword.operator.assignment, keyword.operator.arithmetic, keyword.operator.class, keyword.operator.key, keyword.operator.logical + settings + + fontStyle + + foreground + #B23C15 + + + + name + Keyword + scope + keyword, keyword.operator.new, keyword.other, keyword.control + settings + + fontStyle + + foreground + #D02B26 + + + + name + Storage + scope + storage + settings + + fontStyle + + foreground + #D02B26 + + + + name + String + scope + string -string.unquoted.old-plist -string.unquoted.heredoc, string.unquoted.heredoc string + settings + + fontStyle + + foreground + #9A942C + + + + name + Comment + scope + comment + settings + + fontStyle + italic + foreground + #938375 + + + + name + Regexp + scope + string.regexp constant.character.escape + settings + + foreground + #9A942C + + + + name + Support + scope + support + settings + + fontStyle + + foreground + #DA9734 + + + + name + Variable + scope + variable + settings + + fontStyle + + foreground + #282828 + + + + name + Lang Variable + scope + variable.language + settings + + fontStyle + + foreground + #282828 + + + + name + Function Call + scope + meta.function-call + settings + + foreground + #282828 + + + + name + Invalid + scope + invalid + settings + + background + #932b1e + foreground + #282828 + + + + name + Embedded Source + scope + text source, string.unquoted.heredoc, source source + settings + + fontStyle + + foreground + #282828 + + + + name + String embedded-source + scope + string.quoted source + settings + + fontStyle + + foreground + #9A942C + + + + name + String constant + scope + string + settings + + foreground + #9A942C + + + + + name + Support.constant + scope + support.constant + settings + + fontStyle + + foreground + #DA9734 + + + + name + Support.class + scope + support.class + settings + + fontStyle + + foreground + #679B6C + + + + name + Meta.tag.A + scope + entity.name.tag + settings + + fontStyle + bold + foreground + #679B6C + + + + name + Inner tag + scope + meta.tag, meta.tag entity + settings + + foreground + #679B6C + + + + name + css colors + scope + constant.other.color.rgb-value + settings + + foreground + #418587 + + + + name + css tag-name + scope + meta.selector.css entity.name.tag + settings + + foreground + #D02B26 + + + + name + css#id + scope + meta.selector.css, entity.other.attribute-name.id + settings + + foreground + #9A942C + + + + name + css.class + scope + meta.selector.css entity.other.attribute-name.class + settings + + foreground + #9A942C + + + + name + css property-name: + scope + support.type.property-name.css + settings + + foreground + #679B6C + + + + name + css @at-rule + scope + meta.preprocessor.at-rule keyword.control.at-rule + settings + + foreground + #DA9734 + + + + name + css additional-constants + scope + meta.property-value constant + settings + + foreground + #DA9734 + + + + name + css additional-constants + scope + meta.property-value support.constant.named-color.css + settings + + foreground + #B23C15 + + + + name + css constructor.argument + scope + meta.constructor.argument.css + settings + + foreground + #DA9734 + + + + + + + name + diff.header + scope + meta.diff, meta.diff.header + settings + + background + #418587 + foreground + #282828 + + + + name + diff.deleted + scope + markup.deleted + settings + + background + #D02B26 + foreground + #282828 + + + + name + diff.changed + scope + markup.changed + settings + + background + #DA9734 + foreground + #282828 + + + + name + diff.inserted + scope + markup.inserted + settings + + background + #679B6C + foreground + #282828 + + + + + + + name + Bold Markup + scope + markup.bold + settings + + fontStyle + bold + + + + name + Italic Markup + scope + markup.italic + settings + + fontStyle + italic + + + + name + Heading Markup + scope + markup.heading + settings + + fontStyle + bold + foreground + #679B6C + + + + + + + + + name + PHP: class name + scope + entity.name.type.class.php + settings + + foreground + #679B6C + + + + name + PHP: Comment + scope + keyword.other.phpdoc + settings + + fontStyle + + foreground + #938375 + + + + + + name + CSS: numbers + scope + constant.numeric.css, keyword.other.unit.css + settings + + foreground + #B36586 + + + + name + CSS: entity dot, hash, comma, etc. + scope + punctuation.definition.entity.css + settings + + foreground + #9A942C + + + + + name + JS: variable + scope + variable.language.js + settings + + foreground + #DA9734 + + + + name + JS: unquoted labe + scope + string.unquoted.label.js + settings + + foreground + #282828 + + + + + name + Constant other sql + scope + constant.other.table-name.sql + settings + + fontStyle + + foreground + #9A942C + + + + name + Constant other sql + scope + constant.other.database-name.sql + settings + + fontStyle + + foreground + #9A942C + + + + + + + + name + dired directory + scope + storage.type.dired.item.directory, dired.item.directory + settings + + foreground + #679B6C + + + + + + name + orgmode link + scope + orgmode.link + settings + + foreground + #DA9734 + fontStyle + underline + + + + name + orgmode page + scope + orgmode.page + settings + + foreground + #9A942C + + + + name + orgmode break + scope + orgmode.break + settings + + foreground + #B36586 + + + + name + orgmode headline + scope + orgmode.headline + settings + + foreground + #679B6C + + + + name + orgmode tack + scope + orgmode.tack + settings + + foreground + #DA9734 + + + + name + orgmode follow up + scope + orgmode.follow_up + settings + + foreground + #DA9734 + + + + name + orgmode checkbox + scope + orgmode.checkbox + settings + + foreground + #DA9734 + + + + name + orgmode checkbox summary + scope + orgmode.checkbox.summary + settings + + foreground + #DA9734 + + + + name + orgmode tags + scope + orgmode.tags + settings + + foreground + #D02B26 + + + + + name + ain48 assembly label + scope + keyword.control.label.ain48 + settings + + foreground + #076678 + + + + name + ain48 assembly instruction + scope + keyword.operator.word.ain48 + settings + + foreground + #af3a03 + + + + name + ain48 assembly register + scope + variable.register.ain48 + settings + + foreground + #79740e + + + + name + ain48 assembly constants + scope + constant.numeric.ain48 + settings + + foreground + #8f3f71 + + + + name + ain48 assembly directive + scope + keyword.directive.ain48 + settings + + foreground + #b57614 + + + + + + + diff --git a/docs/isa.typ b/docs/isa.typ new file mode 100644 index 0000000..34bb3e3 --- /dev/null +++ b/docs/isa.typ @@ -0,0 +1,677 @@ +#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) diff --git a/docs/ucsd_abi.typ b/docs/ucsd_abi.typ new file mode 100644 index 0000000..dd53929 --- /dev/null +++ b/docs/ucsd_abi.typ @@ -0,0 +1,35 @@ +#import ".template.typ": * +#show: conf + +#title[Unichal Software Distribution System ABI\ Version 3.4 for AIN-48] +© University of Chalmosique, Cross. 301 -- Cross. 319 + += Object Format +b + += Integer representation += Calling convention +== Parameter passing +== Return value passing +== Call Sequence + +```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 +``` + + +#pagebreak(weak: true) +#outline(depth: 2) + diff --git a/include/ain48.h b/include/ain48.h new file mode 100644 index 0000000..aacd45c --- /dev/null +++ b/include/ain48.h @@ -0,0 +1,11 @@ +#ifndef AIN48__H +#define AIN48__H + +#include +#include + + +// #ifdef AIN49__IMPL + +// #endif // def AIN48__IMPL +#endif // AIN48__H diff --git a/include/common.h b/include/common.h new file mode 100644 index 0000000..18d4987 --- /dev/null +++ b/include/common.h @@ -0,0 +1,47 @@ +#ifndef __UCSD_COMMON_H__ +#define __UCSD_COMMON_H__ + +#include +#include +#include +#include +#include +#include + +// # NUMBER TYPES +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__ diff --git a/src/asm.c b/src/asm.c new file mode 100644 index 0000000..00a5cb0 --- /dev/null +++ b/src/asm.c @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include + +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; +} diff --git a/src/logging.c b/src/logging.c new file mode 100644 index 0000000..e0db26b --- /dev/null +++ b/src/logging.c @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include + +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; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..d12cd56 --- /dev/null +++ b/src/main.c @@ -0,0 +1,27 @@ +#include +#include +#include + +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) { + fprintf(stderr, "No subprogram called\nAvalible subprograms:%s\n", available); + return 1; + } + if (!strcmp(argv[1], "asm")) { + return asm_main(argc - 1, argv + 1); + } else { + fprintf(stderr, "Unknown subprogram `%s`\nAvailable subpograms:%s", argv[1], available); + return 1; + } +}