From 71e4f3812982dba2efb471283d310224e8db363e Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Wed, 3 Mar 2021 22:09:50 -0800 Subject: 7842 - new directory organization Baremetal is now the default build target and therefore has its sources at the top-level. Baremetal programs build using the phase-2 Mu toolchain that requires a Linux kernel. This phase-2 codebase which used to be at the top-level is now under the linux/ directory. Finally, the phase-2 toolchain, while self-hosting, has a way to bootstrap from a C implementation, which is now stored in linux/bootstrap. The bootstrap C implementation uses some literate programming tools that are now in linux/bootstrap/tools. So the whole thing has gotten inverted. Each directory should build one artifact and include the main sources (along with standard library). Tools used for building it are relegated to sub-directories, even though those tools are often useful in their own right, and have had lots of interesting programs written using them. A couple of things have gotten dropped in this process: - I had old ways to run on just a Linux kernel, or with a Soso kernel. No more. - I had some old tooling for running a single test at the cursor. I haven't used that lately. Maybe I'll bring it back one day. The reorg isn't done yet. Still to do: - redo documentation everywhere. All the README files, all other markdown, particularly vocabulary.md. - clean up how-to-run comments at the start of programs everywhere - rethink what to do with the html/ directory. Do we even want to keep supporting it? In spite of these shortcomings, all the scripts at the top-level, linux/ and linux/bootstrap are working. The names of the scripts also feel reasonable. This is a good milestone to take stock at. --- linux/advent2020/1a.mu | 97 ++++++++++++++ linux/advent2020/1b.mu | 113 ++++++++++++++++ linux/advent2020/2a.mu | 92 +++++++++++++ linux/advent2020/2b.mu | 122 ++++++++++++++++++ linux/advent2020/3a.mu | 107 +++++++++++++++ linux/advent2020/3b.mu | 137 ++++++++++++++++++++ linux/advent2020/4a.mu | 75 +++++++++++ linux/advent2020/4b.mu | 314 +++++++++++++++++++++++++++++++++++++++++++++ linux/advent2020/5a.mu | 79 ++++++++++++ linux/advent2020/5b.mu | 81 ++++++++++++ linux/advent2020/vimrc.vim | 2 + 11 files changed, 1219 insertions(+) create mode 100644 linux/advent2020/1a.mu create mode 100644 linux/advent2020/1b.mu create mode 100644 linux/advent2020/2a.mu create mode 100644 linux/advent2020/2b.mu create mode 100644 linux/advent2020/3a.mu create mode 100644 linux/advent2020/3b.mu create mode 100644 linux/advent2020/4a.mu create mode 100644 linux/advent2020/4b.mu create mode 100644 linux/advent2020/5a.mu create mode 100644 linux/advent2020/5b.mu create mode 100644 linux/advent2020/vimrc.vim (limited to 'linux/advent2020') diff --git a/linux/advent2020/1a.mu b/linux/advent2020/1a.mu new file mode 100644 index 00000000..67fdc358 --- /dev/null +++ b/linux/advent2020/1a.mu @@ -0,0 +1,97 @@ +# https://adventofcode.com/2020/day/1 +# +# To run (on Linux): +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# $ ./translate_mu apps/advent2020/1a.mu +# $ ./a.elf < input +# found +# 1353 667 +# 902451 +# +# You'll need to register to download the 'input' file for yourself. + +fn main -> _/ebx: int { + # data structure + var numbers-storage: (array int 0x100) # 256 ints + var numbers/esi: (addr array int) <- address numbers-storage + var numbers-index/ecx: int <- copy 0 + # phase 1: parse each line from stdin and add it to numbers + { + var line-storage: (stream byte 0x100) # 256 bytes + var line/edx: (addr stream byte) <- address line-storage + { +#? print-string 0, "== iter\n" + # read line from stdin + clear-stream line + read-line-from-real-keyboard line + # if line is empty (not even a newline), quit + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= +#? print-stream-to-real-screen line + # convert line to int and append it to numbers + var n/eax: int <- parse-decimal-int-from-stream line +#? print-int32-decimal 0, n +#? print-string 0, "\n" + var dest/ebx: (addr int) <- index numbers, numbers-index + copy-to *dest, n + numbers-index <- increment +#? print-string 0, "== " +#? print-int32-decimal 0, numbers-index +#? print-string 0, "\n" + loop + } + } + # phase 2: for each number in the array, check if 2020-it is in the rest of + # the array + var i/eax: int <- copy 0 + { + compare i, numbers-index + break-if->= + var src/ebx: (addr int) <- index numbers, i +#? print-int32-decimal 0, *src +#? print-string 0, "\n" + var target/ecx: int <- copy 0x7e4 # 2020 + target <- subtract *src + { + var found?/eax: boolean <- find-after numbers, i, target + compare found?, 0/false + break-if-= + print-string 0, "found\n" + print-int32-decimal 0, *src + print-string 0, " " + print-int32-decimal 0, target + print-string 0, "\n" + target <- multiply *src + print-int32-decimal 0, target + print-string 0, "\n" + return 0/success + } + i <- increment + loop + } + return 1/not-found +} + +fn find-after _numbers: (addr array int), start: int, _target: int -> _/eax: boolean { + var numbers/esi: (addr array int) <- copy _numbers + var target/edi: int <- copy _target + var len/ecx: int <- length numbers + var i/eax: int <- copy start + i <- increment + { + compare i, len + break-if->= + var src/edx: (addr int) <- index numbers, i + # if *src == target, return true + compare *src, target + { + break-if-!= + return 1/true + } + i <- increment + loop + } + return 0/false +} diff --git a/linux/advent2020/1b.mu b/linux/advent2020/1b.mu new file mode 100644 index 00000000..a638d228 --- /dev/null +++ b/linux/advent2020/1b.mu @@ -0,0 +1,113 @@ +# https://adventofcode.com/2020/day/1 +# +# To run (on Linux): +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# $ ./translate_mu apps/advent2020/1b.mu +# $ ./a.elf < input +# found +# 143 407 1470 +# 85555470 +# +# You'll need to register to download the 'input' file for yourself. + +fn main -> _/ebx: int { + # data structure + var numbers-storage: (array int 0x100) # 256 ints + var numbers/esi: (addr array int) <- address numbers-storage + var numbers-index/ecx: int <- copy 0 + # phase 1: parse each line from stdin and add it to numbers + { + var line-storage: (stream byte 0x100) # 256 bytes + var line/edx: (addr stream byte) <- address line-storage + { +#? print-string 0, "== iter\n" + # read line from stdin + clear-stream line + read-line-from-real-keyboard line + # if line is empty (not even a newline), quit + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= +#? print-stream-to-real-screen line + # convert line to int and append it to numbers + var n/eax: int <- parse-decimal-int-from-stream line +#? print-int32-decimal 0, n +#? print-string 0, "\n" + var dest/ebx: (addr int) <- index numbers, numbers-index + copy-to *dest, n + numbers-index <- increment +#? print-string 0, "== " +#? print-int32-decimal 0, numbers-index +#? print-string 0, "\n" + loop + } + } + # phase 2: for every pair of distinct numbers, check if the rest of the + # array has 2020-it + var i/edi: int <- copy 0 + { + compare i, numbers-index + break-if->= + # for j from i+1 to end + var j/edx: int <- copy i + j <- increment + { + compare j, numbers-index + break-if->= + { + compare i, j + break-if-= + var target/ebx: int <- copy 0x7e4 # 2020 + var src/edi: (addr int) <- index numbers, i + target <- subtract *src + var src2/ecx: (addr int) <- index numbers, j + target <- subtract *src2 + { + var found?/eax: boolean <- find-after numbers, j, target + compare found?, 0/false + break-if-= + print-string 0, "found\n" + print-int32-decimal 0, *src + print-string 0, " " + print-int32-decimal 0, *src2 + print-string 0, " " + print-int32-decimal 0, target + print-string 0, "\n" + target <- multiply *src + target <- multiply *src2 + print-int32-decimal 0, target + print-string 0, "\n" + return 0/success + } + } + j <- increment + loop + } + i <- increment + loop + } + return 1/not-found +} + +fn find-after _numbers: (addr array int), start: int, _target: int -> _/eax: boolean { + var numbers/esi: (addr array int) <- copy _numbers + var target/edi: int <- copy _target + var len/ecx: int <- length numbers + var i/eax: int <- copy start + i <- increment + { + compare i, len + break-if->= + var src/edx: (addr int) <- index numbers, i + # if *src == target, return true + compare *src, target + { + break-if-!= + return 1/true + } + i <- increment + loop + } + return 0/false +} diff --git a/linux/advent2020/2a.mu b/linux/advent2020/2a.mu new file mode 100644 index 00000000..dc67b94a --- /dev/null +++ b/linux/advent2020/2a.mu @@ -0,0 +1,92 @@ +# https://adventofcode.com/2020/day/2 +# +# To run (on Linux): +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# $ ./translate_mu apps/advent2020/2a.mu +# $ ./a.elf < input +# +# You'll need to register to download the 'input' file for yourself. + +fn main -> _/ebx: int { + var valid-password-count/edi: int <- copy 0 + var line-storage: (stream byte 0x100) # 256 bytes + var line/edx: (addr stream byte) <- address line-storage + var slice-storage: slice + var slice/ecx: (addr slice) <- address slice-storage + { + # read line from stdin + clear-stream line + read-line-from-real-keyboard line + # if line is empty (not even a newline), quit + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= + print-stream-to-real-screen line + # slice = next-token(line, '-') + next-token line, 0x2d, slice + # start = parse-int(slice) + var _start/eax: int <- parse-decimal-int-from-slice slice + var start/ebx: int <- copy _start + var dash/eax: byte <- read-byte line # skip '-' + # slice = next-token(line, ' ') + next-token line, 0x20, slice + var _end/eax: int <- parse-decimal-int-from-slice slice + var end/esi: int <- copy _end + print-int32-decimal 0, start + print-string 0, " " + print-int32-decimal 0, end + print-string 0, "\n" + # letter = next non-space + skip-chars-matching-whitespace line + var letter/eax: byte <- read-byte line + # skip some stuff + { + var colon/eax: byte <- read-byte line # skip ':' + } + skip-chars-matching-whitespace line + # now check the rest of the line + var is-valid?/eax: boolean <- is-valid? start, end, letter, line + compare is-valid?, 0/false + { + break-if-= + print-string 0, "valid!\n" + valid-password-count <- increment + } + loop + } + print-int32-decimal 0, valid-password-count + print-string 0, "\n" + return 0 +} + +fn is-valid? start: int, end: int, letter: byte, password: (addr stream byte) -> _/eax: boolean { + var letter-count/edi: int <- copy 0 + # for every c in password + # if (c == letter) + # ++letter-count + { + var done?/eax: boolean <- stream-empty? password + compare done?, 0/false + break-if-!= + var c/eax: byte <- read-byte password + compare c, letter + { + break-if-!= + letter-count <- increment + } + loop + } + # return (start <= letter-count <= end) + compare letter-count, start + { + break-if->= + return 0/false + } + compare letter-count, end + { + break-if-<= + return 0/false + } + return 1/true +} diff --git a/linux/advent2020/2b.mu b/linux/advent2020/2b.mu new file mode 100644 index 00000000..13f60f0f --- /dev/null +++ b/linux/advent2020/2b.mu @@ -0,0 +1,122 @@ +# https://adventofcode.com/2020/day/2 +# +# To run (on Linux): +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# $ ./translate_mu apps/advent2020/2b.mu +# $ ./a.elf < input +# +# You'll need to register to download the 'input' file for yourself. + +fn main -> _/ebx: int { + var valid-password-count/edi: int <- copy 0 + var line-storage: (stream byte 0x100) # 256 bytes + var line/edx: (addr stream byte) <- address line-storage + var slice-storage: slice + var slice/ecx: (addr slice) <- address slice-storage + { + # read line from stdin + clear-stream line + read-line-from-real-keyboard line + # if line is empty (not even a newline), quit + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= + print-stream-to-real-screen line + # slice = next-token(line, '-') + next-token line, 0x2d, slice + # pos1 = parse-int(slice) + var _pos1/eax: int <- parse-decimal-int-from-slice slice + var pos1/ebx: int <- copy _pos1 + var dash/eax: byte <- read-byte line # skip '-' + # slice = next-token(line, ' ') + next-token line, 0x20, slice + var _pos2/eax: int <- parse-decimal-int-from-slice slice + var pos2/esi: int <- copy _pos2 + print-int32-decimal 0, pos1 + print-string 0, " " + print-int32-decimal 0, pos2 + print-string 0, "\n" + compare pos1, pos2 + { + break-if-<= + print-string 0, "out of order!\n" + return 1 + } + # letter = next non-space + skip-chars-matching-whitespace line + var letter/eax: byte <- read-byte line + # skip some stuff + { + var colon/eax: byte <- read-byte line # skip ':' + } + skip-chars-matching-whitespace line + # now check the rest of the line + var is-valid?/eax: boolean <- is-valid? pos1, pos2, letter, line + compare is-valid?, 0/false + { + break-if-= + print-string 0, "valid!\n" + valid-password-count <- increment + } + loop + } + print-int32-decimal 0, valid-password-count + print-string 0, "\n" + return 0 +} + +# ideally password would be a random-access array +# we'll just track an index +# one benefit: we can easily start at 1 +fn is-valid? pos1: int, pos2: int, letter: byte, password: (addr stream byte) -> _/eax: boolean { + var i/esi: int <- copy 1 + var letter-count/edi: int <- copy 0 + # while password stream isn't empty + # c = read byte from password + # if (c == letter) + # if (i == pos1) + # ++letter-count + # if (i == pos2) + # ++letter-count + # ++i + { +#? print-string 0, " " +#? print-int32-decimal 0, i +#? print-string 0, "\n" + var done?/eax: boolean <- stream-empty? password + compare done?, 0/false + break-if-!= + var c/eax: byte <- read-byte password +#? { +#? var c2/eax: int <- copy c +#? print-int32-decimal 0, c2 +#? print-string 0, "\n" +#? } + compare c, letter + { + break-if-!= + compare i, pos1 + { + break-if-!= + letter-count <- increment +#? print-string 0, " hit\n" + } + compare i, pos2 + { + break-if-!= + letter-count <- increment +#? print-string 0, " hit\n" + } + } + i <- increment + loop + } + # return (letter-count == 1) + compare letter-count, 1 + { + break-if-!= + return 1/true + } + return 0/false +} diff --git a/linux/advent2020/3a.mu b/linux/advent2020/3a.mu new file mode 100644 index 00000000..76bda961 --- /dev/null +++ b/linux/advent2020/3a.mu @@ -0,0 +1,107 @@ +# https://adventofcode.com/2020/day/3 +# +# To run (on Linux): +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# $ ./translate_mu apps/advent2020/3a.mu +# $ ./a.elf < input +# +# You'll need to register to download the 'input' file for yourself. + +fn main -> _/ebx: int { + # represent trees in a 2D array of ints + # wasteful since each tree is just one bit + var trees-storage: (array int 0x2800) # 10k ints + var trees/esi: (addr array int) <- address trees-storage + var trees-length/ecx: int <- copy 0 + var num-rows: int + var width: int + # phase 1: parse each row of trees from stdin + { + var line-storage: (stream byte 0x40) # 64 bytes + var line/edx: (addr stream byte) <- address line-storage + { + # read line from stdin + clear-stream line + read-line-from-real-keyboard line + # if line is empty (not even a newline), quit + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= + # wastefully recompute width on every line + # zero error-checking; we assume input lines are all equally long + copy-to width, 0 + # turn each byte into a tree and append it + $main:line-loop: { + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= +#? print-int32-decimal 0, num-rows +#? print-string 0, " " +#? print-int32-decimal 0, width +#? print-string 0, "\n" + var dest/ebx: (addr int) <- index trees, trees-length + var c/eax: byte <- read-byte line + # newline comes only at end of line + compare c, 0xa/newline + break-if-= + # '#' = tree + compare c, 0x23/hash + { + break-if-!= + copy-to *dest, 1 + } + # anything else = no tree + { + break-if-= + copy-to *dest, 0 + } + increment width + trees-length <- increment + loop + } + increment num-rows + loop + } + } + # phase 2: compute + print-int32-decimal 0, num-rows + print-string 0, "x" + print-int32-decimal 0, width + print-string 0, "\n" + var row/ecx: int <- copy 0 + var col/edx: int <- copy 0 + var num-trees-hit/edi: int <- copy 0 + { + compare row, num-rows + break-if->= + var curr/eax: int <- index2d trees, row, col, width + compare curr, 0 + { + break-if-= + num-trees-hit <- increment + } + # right 3, down 1 + col <- add 3 + row <- add 1 + loop + } + print-int32-decimal 0, num-trees-hit + print-string 0, "\n" + return 0 +} + +fn index2d _arr: (addr array int), _row: int, _col: int, width: int -> _/eax: int { + # handle repeating columns of trees + var dummy/eax: int <- copy 0 + var col/edx: int <- copy 0 + dummy, col <- integer-divide _col, width + # compute index + var index/eax: int <- copy _row + index <- multiply width + index <- add col + # look up array + var arr/esi: (addr array int) <- copy _arr + var src/eax: (addr int) <- index arr, index + return *src +} diff --git a/linux/advent2020/3b.mu b/linux/advent2020/3b.mu new file mode 100644 index 00000000..fcc9261a --- /dev/null +++ b/linux/advent2020/3b.mu @@ -0,0 +1,137 @@ +# https://adventofcode.com/2020/day/3 +# +# To run (on Linux): +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# $ ./translate_mu apps/advent2020/3a.mu +# $ ./a.elf < input +# +# You'll need to register to download the 'input' file for yourself. + +fn main -> _/ebx: int { + # represent trees in a 2D array of ints + # wasteful since each tree is just one bit + var trees-storage: (array int 0x2800) # 10k ints + var trees/esi: (addr array int) <- address trees-storage + var trees-length/ecx: int <- copy 0 + var num-rows: int + var width: int + # phase 1: parse each row of trees from stdin + { + var line-storage: (stream byte 0x40) # 64 bytes + var line/edx: (addr stream byte) <- address line-storage + { + # read line from stdin + clear-stream line + read-line-from-real-keyboard line + # if line is empty (not even a newline), quit + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= + # wastefully recompute width on every line + # zero error-checking; we assume input lines are all equally long + copy-to width, 0 + # turn each byte into a tree and append it + $main:line-loop: { + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= +#? print-int32-decimal 0, num-rows +#? print-string 0, " " +#? print-int32-decimal 0, width +#? print-string 0, "\n" + var dest/ebx: (addr int) <- index trees, trees-length + var c/eax: byte <- read-byte line + # newline comes only at end of line + compare c, 0xa/newline + break-if-= + # '#' = tree + compare c, 0x23/hash + { + break-if-!= + copy-to *dest, 1 + } + # anything else = no tree + { + break-if-= + copy-to *dest, 0 + } + increment width + trees-length <- increment + loop + } + increment num-rows + loop + } + } + # phase 2: compute + var product/edi: int <- copy 1 + var result/eax: int <- num-trees-hit trees, width, num-rows, 1, 1 + print-int32-decimal 0, result + print-string 0, " x " + product <- multiply result + var result/eax: int <- num-trees-hit trees, width, num-rows, 3, 1 + print-int32-decimal 0, result + print-string 0, " x " + product <- multiply result + var result/eax: int <- num-trees-hit trees, width, num-rows, 5, 1 + print-int32-decimal 0, result + print-string 0, " x " + product <- multiply result + var result/eax: int <- num-trees-hit trees, width, num-rows, 7, 1 + print-int32-decimal 0, result + print-string 0, " x " + product <- multiply result + var result/eax: int <- num-trees-hit trees, width, num-rows, 1, 2 + print-int32-decimal 0, result + print-string 0, " = " + product <- multiply result + print-int32-hex 0, product + print-string 0, "\n" + return 0 +} + +fn num-trees-hit trees: (addr array int), width: int, num-rows: int, right: int, down: int -> _/eax: int { +#? print-string 0, "== " +#? print-int32-decimal 0, right +#? print-string 0, " " +#? print-int32-decimal 0, down +#? print-string 0, "\n" + var row/ecx: int <- copy 0 + var col/edx: int <- copy 0 + var num-trees-hit/edi: int <- copy 0 + { + compare row, num-rows + break-if->= +#? print-int32-decimal 0, col +#? print-string 0, "\n" + var curr/eax: int <- index2d trees, row, col, width + compare curr, 0 + { + break-if-= + num-trees-hit <- increment + } + col <- add right + row <- add down + loop + } + return num-trees-hit +} + +fn index2d _arr: (addr array int), _row: int, _col: int, width: int -> _/eax: int { + # handle repeating columns of trees + var dummy/eax: int <- copy 0 + var col/edx: int <- copy 0 + dummy, col <- integer-divide _col, width +#? print-string 0, " " +#? print-int32-decimal 0, col +#? print-string 0, "\n" + # compute index + var index/eax: int <- copy _row + index <- multiply width + index <- add col + # look up array + var arr/esi: (addr array int) <- copy _arr + var src/eax: (addr int) <- index arr, index + return *src +} diff --git a/linux/advent2020/4a.mu b/linux/advent2020/4a.mu new file mode 100644 index 00000000..e645da24 --- /dev/null +++ b/linux/advent2020/4a.mu @@ -0,0 +1,75 @@ +# https://adventofcode.com/2020/day/4 +# +# To run (on Linux): +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# $ ./translate_mu apps/advent2020/4a.mu +# $ ./a.elf < input +# +# You'll need to register to download the 'input' file for yourself. + +fn main -> _/ebx: int { + var curr-passport-field-count/esi: int <- copy 0 + var valid-passport-count/edi: int <- copy 0 + var line-storage: (stream byte 0x100) # 256 bytes + var line/ecx: (addr stream byte) <- address line-storage + var slice-storage: slice + var slice/edx: (addr slice) <- address slice-storage + $main:line-loop: { + # read line from stdin + clear-stream line + read-line-from-real-keyboard line + # if line is empty (not even a newline), quit + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= + print-stream-to-real-screen line + # if line has just a newline, process passport + skip-chars-matching-whitespace line + var new-passport?/eax: boolean <- stream-empty? line + { + compare new-passport?, 0/false + break-if-= + compare curr-passport-field-count, 7 + { + break-if-!= + valid-passport-count <- increment + print-string 0, "=> " + print-int32-decimal 0, valid-passport-count + print-string 0, "\n" + } + curr-passport-field-count <- copy 0 + loop $main:line-loop + } + $main:word-loop: { + next-word line, slice + var done?/eax: boolean <- slice-empty? slice + compare done?, 0/false + break-if-!= + print-string 0, " " + print-slice-to-real-screen slice + # treat cid as optional + var optional?/eax: boolean <- slice-starts-with? slice, "cid:" + compare optional?, 0/false + { + break-if-!= + # otherwise assume there are no invalid fields and no duplicate fields + curr-passport-field-count <- increment + print-string 0, " => " + print-int32-decimal 0, curr-passport-field-count + } + print-string 0, "\n" + loop + } + loop + } + # process final passport + compare curr-passport-field-count, 7 + { + break-if-!= + valid-passport-count <- increment + } + print-int32-decimal 0, valid-passport-count + print-string 0, "\n" + return 0 +} diff --git a/linux/advent2020/4b.mu b/linux/advent2020/4b.mu new file mode 100644 index 00000000..1ab29456 --- /dev/null +++ b/linux/advent2020/4b.mu @@ -0,0 +1,314 @@ +# https://adventofcode.com/2020/day/4 +# +# To run (on Linux): +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# $ ./translate_mu apps/advent2020/4b.mu +# $ ./a.elf < input +# +# You'll need to register to download the 'input' file for yourself. + +fn main -> _/ebx: int { + var curr-passport-field-count/esi: int <- copy 0 + var valid-passport-count/edi: int <- copy 0 + var line-storage: (stream byte 0x100) # 256 bytes + var line/ecx: (addr stream byte) <- address line-storage + var key-slice-storage: slice + var key-slice/edx: (addr slice) <- address key-slice-storage + var val-slice-storage: slice + var val-slice/ebx: (addr slice) <- address val-slice-storage + $main:line-loop: { + # read line from stdin + clear-stream line + read-line-from-real-keyboard line + # if line is empty (not even a newline), quit + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= + print-stream-to-real-screen line + # if line has just a newline, process passport + skip-chars-matching-whitespace line + var new-passport?/eax: boolean <- stream-empty? line + { + compare new-passport?, 0/false + break-if-= + compare curr-passport-field-count, 7 + { + break-if-!= + valid-passport-count <- increment + print-string 0, "=> " + print-int32-decimal 0, valid-passport-count + print-string 0, "\n" + } + curr-passport-field-count <- copy 0 + loop $main:line-loop + } + $main:word-loop: { + skip-chars-matching-whitespace line + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= + next-token line, 0x3a, key-slice # ':' + var dummy/eax: byte <- read-byte line # skip ':' + next-raw-word line, val-slice + print-slice-to-real-screen key-slice + print-string 0, " : " + print-slice-to-real-screen val-slice + print-string 0, "\n" + # treat cid as optional + var cid?/eax: boolean <- slice-equal? key-slice, "cid" + compare cid?, 0/false + loop-if-!= + # increment field count + curr-passport-field-count <- increment + # - validate fields one by one, setting curr-passport-field-count to impossibly high value to signal invalid + # byr + { + var byr?/eax: boolean <- slice-equal? key-slice, "byr" + compare byr?, 0/false + break-if-= + # 1920 <= byr <= 2002 + var byr/eax: int <- parse-decimal-int-from-slice val-slice + compare byr, 0x780 # 1920 + { + break-if->= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + compare byr, 0x7d2 # 2002 + { + break-if-<= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + } + # iyr + { + var iyr?/eax: boolean <- slice-equal? key-slice, "iyr" + compare iyr?, 0/false + break-if-= + # 2010 <= iyr <= 2020 + var iyr/eax: int <- parse-decimal-int-from-slice val-slice + compare iyr, 0x7da # 2010 + { + break-if->= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + compare iyr, 0x7e4 # 2020 + { + break-if-<= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + } + # eyr + { + var eyr?/eax: boolean <- slice-equal? key-slice, "eyr" + compare eyr?, 0/false + break-if-= + # 2020 <= eyr <= 2030 + var eyr/eax: int <- parse-decimal-int-from-slice val-slice + compare eyr, 0x7e4 # 2020 + { + break-if->= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + compare eyr, 0x7ee # 2030 + { + break-if-<= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + } + # hgt + { + var hgt?/eax: boolean <- slice-equal? key-slice, "hgt" + compare hgt?, 0/false + break-if-= + # convert val + var s: (handle array byte) + var s2/eax: (addr handle array byte) <- address s + _slice-to-string val-slice, s2 + var s3/eax: (addr array byte) <- lookup *s2 + var s4/ebx: (addr array byte) <- copy s3 + # check suffix + var start/edx: int <- length s4 + start <- subtract 2 # luckily both 'in' and 'cm' have the same length + { + var suffix-h: (handle array byte) + var suffix-ah/ecx: (addr handle array byte) <- address suffix-h + substring s4, start, 2, suffix-ah + var suffix/eax: (addr array byte) <- lookup *suffix-ah + { + var match?/eax: boolean <- string-equal? suffix, "in" + compare match?, 0/false + break-if-= + # if suffix is "in", 59 <= val <= 96 + var num-h: (handle array byte) + var num-ah/ecx: (addr handle array byte) <- address num-h + substring s4, 0, start, num-ah + var num/eax: (addr array byte) <- lookup *num-ah + var val/eax: int <- parse-decimal-int num + compare val, 0x3b # 59 + { + break-if->= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + compare val, 0x60 # 96 + { + break-if-<= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + loop $main:word-loop + } + { + var match?/eax: boolean <- string-equal? suffix, "cm" + compare match?, 0/false + break-if-= + # if suffix is "cm", 150 <= val <= 193 + var num-h: (handle array byte) + var num-ah/ecx: (addr handle array byte) <- address num-h + substring s4, 0, start, num-ah + var num/eax: (addr array byte) <- lookup *num-ah + var val/eax: int <- parse-decimal-int num + compare val, 0x96 # 150 + { + break-if->= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + compare val, 0xc1 # 193 + { + break-if-<= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + loop $main:word-loop + } + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + loop $main:word-loop + } + } + # hcl + { + var hcl?/eax: boolean <- slice-equal? key-slice, "hcl" + compare hcl?, 0/false + break-if-= + # convert val + var s: (handle array byte) + var s2/eax: (addr handle array byte) <- address s + _slice-to-string val-slice, s2 + var s3/eax: (addr array byte) <- lookup *s2 + # check length + var len/ebx: int <- length s3 + compare len, 7 + { + break-if-= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + loop $main:word-loop + } + # check first byte + { + var c/eax: (addr byte) <- index s3, 0 + var c2/eax: byte <- copy-byte *c + compare c2, 0x23/hash + break-if-= + print-string 0, "invalid2\n" + curr-passport-field-count <- copy 8 + loop $main:word-loop + } + # check remaining bytes + var i/ebx: int <- copy 1 # skip 0 + { + compare i, 7 + break-if->= + var c/eax: (addr byte) <- index s3, i + { + var c2/eax: byte <- copy-byte *c + var valid?/eax: boolean <- is-hex-digit? c2 + compare valid?, 0 + loop-if-= $main:word-loop + } + i <- increment + loop + } + } + # ecl + { + var ecl?/eax: boolean <- slice-equal? key-slice, "ecl" + compare ecl?, 0/false + break-if-= + var amb?/eax: boolean <- slice-equal? val-slice, "amb" + compare amb?, 0/false + loop-if-!= $main:word-loop + var blu?/eax: boolean <- slice-equal? val-slice, "blu" + compare blu?, 0/false + loop-if-!= $main:word-loop + var brn?/eax: boolean <- slice-equal? val-slice, "brn" + compare brn?, 0/false + loop-if-!= $main:word-loop + var gry?/eax: boolean <- slice-equal? val-slice, "gry" + compare gry?, 0/false + loop-if-!= $main:word-loop + var grn?/eax: boolean <- slice-equal? val-slice, "grn" + compare grn?, 0/false + loop-if-!= $main:word-loop + var hzl?/eax: boolean <- slice-equal? val-slice, "hzl" + compare hzl?, 0/false + loop-if-!= $main:word-loop + var oth?/eax: boolean <- slice-equal? val-slice, "oth" + compare oth?, 0/false + loop-if-!= $main:word-loop + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + # pid + { + var pid?/eax: boolean <- slice-equal? key-slice, "pid" + compare pid?, 0/false + break-if-= + # convert val + var s: (handle array byte) + var s2/eax: (addr handle array byte) <- address s + _slice-to-string val-slice, s2 + var s3/eax: (addr array byte) <- lookup *s2 + # check length + var len/eax: int <- length s3 + compare len, 9 + { + break-if-= + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + loop $main:word-loop + } + # check valid decimal int + # parse-decimal-int-from-slice currently returns 0 on invalid parse, + # which isn't ideal but suffices for our purposes + var val/eax: int <- parse-decimal-int-from-slice val-slice + compare val, 0 + { + break-if-> + print-string 0, "invalid\n" + curr-passport-field-count <- copy 8 + } + } + loop + } + loop + } + # process final passport + compare curr-passport-field-count, 7 + { + break-if-!= + valid-passport-count <- increment + } + print-int32-decimal 0, valid-passport-count + print-string 0, "\n" + return 0 +} diff --git a/linux/advent2020/5a.mu b/linux/advent2020/5a.mu new file mode 100644 index 00000000..5a456608 --- /dev/null +++ b/linux/advent2020/5a.mu @@ -0,0 +1,79 @@ +# https://adventofcode.com/2020/day/5 +# +# To run (on Linux): +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# $ ./translate_mu apps/advent2020/5a.mu +# $ ./a.elf < input +# +# You'll need to register to download the 'input' file for yourself. + +fn main -> _/ebx: int { + var line-storage: (stream byte 0x10) # 16 bytes is enough + var line/edx: (addr stream byte) <- address line-storage + var max-seat-id/edi: int <- copy 0 + { + # read line from stdin + clear-stream line + read-line-from-real-keyboard line + print-stream-to-real-screen line + # if line is empty (not even a newline), quit + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= + # process line + var seat-id/eax: int <- convert-from-binary line + compare seat-id, max-seat-id + { + break-if-<= + max-seat-id <- copy seat-id + } + loop + } + print-int32-decimal 0, max-seat-id + print-string 0, "\n" + return 0 +} + +fn convert-from-binary in: (addr stream byte) -> _/eax: int { + var result/edi: int <- copy 0 + var i/ecx: int <- copy 9 # loop counter and also exponent + { + compare i, 0 + break-if-< + var c/eax: byte <- read-byte in +#? print-string 0, "char: " +#? { +#? var c2/eax: int <- copy c +#? print-int32-hex 0, c2 +#? } +#? print-string 0, "\n" + var bit/edx: int <- copy 0 + { + compare c, 0x42/B + break-if-!= + bit <- copy 1 + } + { + compare c, 0x52/R + break-if-!= + bit <- copy 1 + } +#? print-string 0, "bit: " +#? print-int32-decimal 0, bit +#? print-string 0, "\n" + var bit-value/eax: int <- repeated-shift-left bit, i +#? print-string 0, "bit value: " +#? print-int32-decimal 0, bit-value +#? print-string 0, "\n" + result <- add bit-value +#? print-string 0, "result: " +#? print-int32-decimal 0, result +#? print-string 0, "\n" + i <- decrement + loop + } + print-int32-decimal 0, result + print-string 0, "\n" + return result +} diff --git a/linux/advent2020/5b.mu b/linux/advent2020/5b.mu new file mode 100644 index 00000000..fbc0d618 --- /dev/null +++ b/linux/advent2020/5b.mu @@ -0,0 +1,81 @@ +# https://adventofcode.com/2020/day/5 +# +# To run (on Linux): +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# $ ./translate_mu apps/advent2020/5b.mu +# $ ./a.elf < input +# +# You'll need to register to download the 'input' file for yourself. + +fn main -> _/ebx: int { + var pass-storage: (array int 0x400) # 1k ints + var pass/esi: (addr array int) <- address pass-storage + # phase 1: populate pass array + var line-storage: (stream byte 0x10) # 16 bytes is enough + var line/edx: (addr stream byte) <- address line-storage + { + # read line from stdin + clear-stream line + read-line-from-real-keyboard line + # if line is empty (not even a newline), quit + var done?/eax: boolean <- stream-empty? line + compare done?, 0/false + break-if-!= + # process line + var seat-id/eax: int <- convert-from-binary line + var dest/eax: (addr int) <- index pass, seat-id + copy-to *dest, 1 + loop + } + # phase 2: skip empty seats + var i/eax: int <- copy 0 + { + compare i, 0x400 + break-if->= + var src/ecx: (addr int) <- index pass, i + compare *src, 0 + break-if-!= + i <- increment + loop + } + # phase 3: skip non-empty seats + { + compare i, 0x400 + break-if->= + var src/ecx: (addr int) <- index pass, i + compare *src, 0 + break-if-= + i <- increment + loop + } + print-int32-decimal 0, i + print-string 0, "\n" + return 0 +} + +fn convert-from-binary in: (addr stream byte) -> _/eax: int { + var result/edi: int <- copy 0 + var i/ecx: int <- copy 9 # loop counter and also exponent + { + compare i, 0 + break-if-< + var c/eax: byte <- read-byte in + var bit/edx: int <- copy 0 + { + compare c, 0x42/B + break-if-!= + bit <- copy 1 + } + { + compare c, 0x52/R + break-if-!= + bit <- copy 1 + } + var bit-value/eax: int <- repeated-shift-left bit, i + result <- add bit-value + i <- decrement + loop + } + return result +} diff --git a/linux/advent2020/vimrc.vim b/linux/advent2020/vimrc.vim new file mode 100644 index 00000000..348fe364 --- /dev/null +++ b/linux/advent2020/vimrc.vim @@ -0,0 +1,2 @@ +" when opening files in this directory, load vimrc from cwd (top-level) +source vimrc.vim -- cgit 1.4.1-2-gfad0