diff options
Diffstat (limited to 'apps')
-rw-r--r-- | apps/boot0.hex | 344 | ||||
-rw-r--r-- | apps/colors.mu | 242 | ||||
-rw-r--r-- | apps/ex1.mu | 14 | ||||
-rw-r--r-- | apps/ex10.mu | 42 | ||||
-rw-r--r-- | apps/ex10.mu. | 176 | ||||
-rw-r--r-- | apps/ex11.mu | 261 | ||||
-rw-r--r-- | apps/ex12.mu | 28 | ||||
-rw-r--r-- | apps/ex2.mu | 28 | ||||
-rw-r--r-- | apps/ex3.mu | 31 | ||||
-rw-r--r-- | apps/ex4.mu | 14 | ||||
-rw-r--r-- | apps/ex5.mu | 16 | ||||
-rw-r--r-- | apps/ex6.mu | 32 | ||||
-rw-r--r-- | apps/ex7.mu | 46 | ||||
-rw-r--r-- | apps/ex8.mu | 12 | ||||
-rw-r--r-- | apps/ex9.mu | 51 | ||||
-rw-r--r-- | apps/hest-life.mu | 1029 | ||||
-rw-r--r-- | apps/img.mu | 1148 | ||||
-rw-r--r-- | apps/life.mu | 252 | ||||
-rw-r--r-- | apps/mandelbrot-fixed.mu | 262 | ||||
-rw-r--r-- | apps/mandelbrot-silhouette.mu | 150 | ||||
-rw-r--r-- | apps/mandelbrot.mu | 179 | ||||
-rw-r--r-- | apps/rpn.mu | 151 |
22 files changed, 4508 insertions, 0 deletions
diff --git a/apps/boot0.hex b/apps/boot0.hex new file mode 100644 index 00000000..929d2fd9 --- /dev/null +++ b/apps/boot0.hex @@ -0,0 +1,344 @@ +# A minimal bootable image that: +# - loads more sectors past the first boot sector (using BIOS primitives) +# - switches to 32-bit mode (giving up access to BIOS primitives) +# - sets up a keyboard handler to print '1' at the top-left of screen when '1' is typed +# +# When it's ready to accept keys, it prints 'H' to the top-left of the screen. +# +# If the initial load fails, it prints 'D' to the top-left of the screen and +# halts. +# +# To convert to a disk image, first prepare a realistically sized disk image: +# dd if=/dev/zero of=code.img count=20160 # 512-byte sectors, so 10MB +# Now fill in sectors: +# linux/bootstrap/bootstrap run linux/hex < apps/boot0.hex > boot.bin +# dd if=boot.bin of=code.img conv=notrunc +# To run: +# qemu-system-i386 code.img +# Or: +# bochs -f bochsrc # bochsrc loads code.img +# +# Since we start out in 16-bit mode, we need instructions SubX doesn't +# support. +# This file contains just lowercase hex bytes and comments. Zero +# error-checking. Make liberal use of: +# - comments documenting expected offsets +# - size checks on the emitted file (currently: 512 bytes) +# - xxd to eyeball that offsets contain expected bytes + +## 16-bit entry point + +# Upon reset, the IBM PC +# loads the first sector (512 bytes) +# from some bootable image (see the boot sector marker at the end of this file) +# to the address range [0x7c00, 0x7e00) + +# offset 00 (address 0x7c00): + # disable interrupts for this initialization + fa # cli + + # initialize segment registers + # this isn't always needed, but the recommendation is to not make assumptions + b8 00 00 # ax <- 0 + 8e d8 # ds <- ax + 8e d0 # ss <- ax + 8e c0 # es <- ax + 8e e0 # fs <- ax + 8e e8 # gs <- ax + + # We don't read or write the stack before we get to 32-bit mode. No function + # calls, so we don't need to initialize the stack. + +# 0e: + # load more sectors from disk + b4 02 # ah <- 2 # read sectors from disk + # dl comes conveniently initialized at boot time with the index of the device being booted + b5 00 # ch <- 0 # cylinder 0 + b6 00 # dh <- 0 # track 0 + b1 02 # cl <- 2 # second sector, 1-based + b0 01 # al <- 1 # number of sectors to read + # address to write sectors to = es:bx = 0x7e00, contiguous with boot segment + bb 00 00 # bx <- 0 + 8e c3 # es <- bx + bb 00 7e # bx <- 0x7e00 + cd 13 # int 13h, BIOS disk service + 0f 82 76 00 # jump-if-carry disk-error + +# 26: + # undo the A20 hack: https://en.wikipedia.org/wiki/A20_line + # this is from https://github.com/mit-pdos/xv6-public/blob/master/bootasm.S + # seta20.1: + e4 64 # al <- port 0x64 + a8 02 # set zf if bit 1 (second-least significant) is not set + 75 fa # if zf not set, goto seta20.1 (-6) + + b0 d1 # al <- 0xd1 + e6 64 # port 0x64 <- al + +# 30: + # seta20.2: + e4 64 # al <- port 0x64 + a8 02 # set zf if bit 1 (second-least significant) is not set + 75 fa # if zf not set, goto seta20.2 (-6) + + b0 df # al <- 0xdf + e6 64 # port 0x64 <- al + +# 3a: + # switch to 32-bit mode + 0f 01 16 # lgdt 00/mod/indirect 010/subop 110/rm/use-disp16 + 80 7c # *gdt_descriptor +# 3f: + 0f 20 c0 # eax <- cr0 + 66 83 c8 01 # eax <- or 0x1 + 0f 22 c0 # cr0 <- eax + ea c0 7c 08 00 # far jump to initialize_32bit_mode after setting cs to the record at offset 8 in the gdt (gdt_code) + +# padding +# 4e: + 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## GDT: 3 records of 8 bytes each + +# 60: +# gdt_start: +# gdt_null: mandatory null descriptor + 00 00 00 00 00 00 00 00 +# gdt_code: (offset 8 from gdt_start) + ff ff # limit[0:16] + 00 00 00 # base[0:24] + 9a # 1/present 00/privilege 1/descriptor type = 1001b + # 1/code 0/conforming 1/readable 0/accessed = 1010b + cf # 1/granularity 1/32-bit 0/64-bit-segment 0/AVL = 1100b + # limit[16:20] = 1111b + 00 # base[24:32] +# gdt_data: (offset 16 from gdt_start) + ff ff # limit[0:16] + 00 00 00 # base[0:24] + 92 # 1/present 00/privilege 1/descriptor type = 1001b + # 0/data 0/conforming 1/readable 0/accessed = 0010b + cf # same as gdt_code + 00 # base[24:32] +# gdt_end: + +# padding +# 78: + 00 00 00 00 00 00 00 00 + +# 80: +# gdt_descriptor: + 17 00 # final index of gdt = gdt_end - gdt_start - 1 + 60 7c 00 00 # start = gdt_start + +# padding +# 85: + 00 00 00 00 00 00 00 00 00 00 + +# 90: +# disk_error: + # print 'D' to top-left of screen to indicate disk error + # *0xb8000 <- 0x0f44 + # bx <- 0xb800 + bb 00 b8 + # ds <- bx + 8e db # 11b/mod 011b/reg/ds 011b/rm/bx + # al <- 'D' + b0 44 + # ah <- 0x0f # white on black + b4 0f + # bx <- 0 + bb 00 00 + # *ds:bx <- ax + 89 07 # 00b/mod/indirect 000b/reg/ax 111b/rm/bx + +e9 fb ff # loop forever + +# padding +# a1: + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## 32-bit code from this point (still some instructions not in SubX) + +# c0: +# initialize_32bit_mode: + 66 b8 10 00 # ax <- offset 16 from gdt_start + 8e d8 # ds <- ax + 8e d0 # ss <- ax + 8e c0 # es <- ax + 8e e0 # fs <- ax + 8e e8 # gs <- ax + + # load interrupt handlers + 0f 01 1d # lidt 00/mod/indirect 011/subop 101/rm32/use-disp32 + 00 7f 00 00 # *idt_descriptor + + # enable keyboard IRQ + b0 fd # al <- 0xfd # enable just IRQ1 + e6 21 # port 0x21 <- al + + # initialization is done; enable interrupts + fb + e9 21 00 00 00 # jump to 0x7d00 + +# padding +# df: + 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +## 'application' SubX code: print one character to top-left of screen + +# offset 100 (address 0x7d00): +# Entry: + # eax <- *0x7ff4 # random address in second segment containing 'H' + 8b # copy rm32 to r32 + 05 # 00/mod/indirect 000/r32/eax 101/rm32/use-disp32 + # disp32 + f4 7f 00 00 + # *0xb8000 <- eax + 89 # copy r32 to rm32 + 05 # 00/mod/indirect 000/r32/eax 101/rm32/use-disp32 + # disp32 + 00 80 0b 00 + +e9 fb ff ff ff # loop forever + +# padding +# 111: + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +# 120: +# null interrupt handler: + cf # iret + +# padding +# 121: + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +# 130: +# keyboard interrupt handler: + # prologue + fa # disable interrupts + 60 # push all registers to stack + # acknowledge interrupt + b0 20 # al <- 0x20 + e6 20 # port 0x20 <- al + # check output buffer of 8042 keyboard controller (https://web.archive.org/web/20040604041507/http://panda.cs.ndsu.nodak.edu/~achapwes/PICmicro/keyboard/atkeyboard.html) + e4 64 # al <- port 0x64 + a8 01 # set zf if bit 0 (least significant) is not set + 74 11 # if bit 0 is not set, skip to epilogue + # read keycode into eax + 31 c0 # eax <- xor eax; 11/direct 000/r32/eax 000/rm32/eax + e4 60 # al <- port 0x60 + # map key '1' to ascii; if eax == 2, eax = 0x31 + 3d 02 00 00 00 # compare eax with 0x02 + 75 0b # if not equal, goto epilogue + b8 31 0f 00 00 # eax <- 0x0f31 + # print eax to top-left of screen (*0xb8000) + 89 # copy r32 to rm32 + 05 # 00/mod/indirect 000/r32/eax 101/rm32/use-disp32 + # disp32 + 00 80 0b 00 + # epilogue + 61 # pop all registers + fb # enable interrupts + cf # iret + +# padding +# 155 + 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 + +# final 2 bytes of boot sector +55 aa + +## sector 2 +# loaded by load_disk, not automatically on boot + +# offset 200 (address 0x7e00): interrupt descriptor table +# 32 entries * 8 bytes each = 256 bytes (0x100) +# idt_start: + +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 + +# entry 8: clock + 20 7d # target[0:16] = null interrupt handler + 08 00 # segment selector (gdt_code) + 00 # unused + 8e # 1/p 00/dpl 0 1110/type/32-bit-interrupt-gate + 00 00 # target[16:32] + +# entry 9: keyboard + 30 7d # target[0:16] = keyboard interrupt handler + 08 00 # segment selector (gdt_code) + 00 # unused + 8e # 1/p 00/dpl 0 1110/type/32-bit-interrupt-gate + 00 00 # target[16:32] + +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 +# idt_end: + +# offset 300 (address 0x7f00): +# idt_descriptor: + ff 00 # idt_end - idt_start - 1 + 00 7e 00 00 # start = idt_start + +# padding + 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 +00 00 00 00 48 0f 00 00 00 00 00 00 00 00 00 00 # spot the 'H' with attributes +# offset 400 (address 0x8000) + +# vim:ft=conf diff --git a/apps/colors.mu b/apps/colors.mu new file mode 100644 index 00000000..78e58838 --- /dev/null +++ b/apps/colors.mu @@ -0,0 +1,242 @@ +# Return colors 'near' a given r/g/b value (expressed in hex) +# If we did this rigorously we'd need to implement cosines. So we won't. +# +# To build: +# $ ./translate apps/colors.mu +# +# Example session: +# $ qemu-system-i386 code.img +# Enter 3 hex bytes for r, g, b (lowercase; no 0x prefix) separated by a single space> aa 0 aa +# 5 +# This means only color 5 in the default palette is similar to #aa00aa. + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var in-storage: (stream byte 0x10) + var in/esi: (addr stream byte) <- address in-storage + { + # print prompt + var x/eax: int <- draw-text-rightward screen, "Enter 3 hex bytes for r, g, b (lowercase; no 0x prefix) separated by a single space> ", 0x10/x, 0x80/xmax, 0x28/y, 3/fg/cyan, 0/bg + # read line from keyboard + clear-stream in + { + draw-cursor screen, 0x20/space + var key/eax: byte <- read-key keyboard + compare key, 0xa/newline + break-if-= + compare key, 0 + loop-if-= + var key2/eax: int <- copy key + append-byte in, key2 + var g/eax: grapheme <- copy key2 + draw-grapheme-at-cursor screen, g, 0xf/fg, 0/bg + move-cursor-right 0 + loop + } + clear-screen screen + # parse + var a/ecx: int <- copy 0 + var b/edx: int <- copy 0 + var c/ebx: int <- copy 0 + # a, b, c = r, g, b + a, b, c <- parse in +#? set-cursor-position screen, 0x10/x, 0x1a/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, a, 7/fg, 0/bg +#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, b, 7/fg, 0/bg +#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, c, 7/fg, 0/bg + a, b, c <- hsl a, b, c + # return all colors in the same quadrant in h, s and l + print-nearby-colors screen, a, b, c + # another metric + var color/eax: int <- nearest-color-euclidean-hsl a, b, c + set-cursor-position screen, 0x10/x, 0x26/y + draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "nearest (euclidean, h/s/l): ", 0xf/fg, 0/bg + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, color, 7/fg, 0/bg + draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 0xf/fg, 0/bg + draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 0/fg, color + # + loop + } +} + +# read exactly 3 words in a single line +# Each word consists of exactly 1 or 2 hex bytes. No hex prefix. +fn parse in: (addr stream byte) -> _/ecx: int, _/edx: int, _/ebx: int { + # read first byte of r + var tmp/eax: byte <- read-byte in + { + var valid?/eax: boolean <- hex-digit? tmp + compare valid?, 0/false + break-if-!= + abort "invalid byte 0 of r" + } + tmp <- fast-hex-digit-value tmp + var r/ecx: int <- copy tmp +#? set-cursor-position 0/screen, 0x10/x, 0x10/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, r, 7/fg, 0/bg + # read second byte of r + tmp <- read-byte in + { + { + var valid?/eax: boolean <- hex-digit? tmp + compare valid?, 0/false + } + break-if-= + r <- shift-left 4 + tmp <- fast-hex-digit-value tmp +#? { +#? var foo/eax: int <- copy tmp +#? set-cursor-position 0/screen, 0x10/x, 0x11/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg +#? } + r <- add tmp +#? { +#? set-cursor-position 0/screen, 0x10/x, 0x12/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, r, 7/fg, 0/bg +#? } + tmp <- read-byte in # skip space + } + # read first byte of g + var tmp/eax: byte <- read-byte in + { + var valid?/eax: boolean <- hex-digit? tmp + compare valid?, 0/false + break-if-!= + abort "invalid byte 0 of g" + } + tmp <- fast-hex-digit-value tmp + var g/edx: int <- copy tmp +#? set-cursor-position 0/screen, 0x10/x, 0x13/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, g, 7/fg, 0/bg + # read second byte of g + tmp <- read-byte in + { + { + var valid?/eax: boolean <- hex-digit? tmp + compare valid?, 0/false + } + break-if-= + g <- shift-left 4 + tmp <- fast-hex-digit-value tmp +#? { +#? var foo/eax: int <- copy tmp +#? set-cursor-position 0/screen, 0x10/x, 0x14/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg +#? } + g <- add tmp +#? { +#? set-cursor-position 0/screen, 0x10/x, 0x15/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, g, 7/fg, 0/bg +#? } + tmp <- read-byte in # skip space + } + # read first byte of b + var tmp/eax: byte <- read-byte in + { + var valid?/eax: boolean <- hex-digit? tmp + compare valid?, 0/false + break-if-!= + abort "invalid byte 0 of b" + } + tmp <- fast-hex-digit-value tmp + var b/ebx: int <- copy tmp +#? set-cursor-position 0/screen, 0x10/x, 0x16/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, b, 7/fg, 0/bg + # read second byte of b + { + { + var done?/eax: boolean <- stream-empty? in + compare done?, 0/false + } + break-if-!= + tmp <- read-byte in + { + var valid?/eax: boolean <- hex-digit? tmp + compare valid?, 0/false + } + break-if-= + b <- shift-left 4 + tmp <- fast-hex-digit-value tmp +#? { +#? var foo/eax: int <- copy tmp +#? set-cursor-position 0/screen, 0x10/x, 0x17/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg +#? } + b <- add tmp +#? { +#? set-cursor-position 0/screen, 0x10/x, 0x18/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, b, 7/fg, 0/bg +#? } + } + return r, g, b +} + +# no error checking +fn fast-hex-digit-value in: byte -> _/eax: byte { + var result/eax: byte <- copy in + compare result, 0x39 + { + break-if-> + result <- subtract 0x30/0 + return result + } + result <- subtract 0x61/a + result <- add 0xa/10 + return result +} + +fn print-nearby-colors screen: (addr screen), h: int, s: int, l: int { +#? set-cursor-position screen, 0x10/x, 0x1c/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, h, 7/fg, 0/bg +#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, s, 7/fg, 0/bg +#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, l, 7/fg, 0/bg + # save just top 2 bits of each, so that we narrow down to 1/64th of the volume + shift-right h, 6 + shift-right s, 6 + shift-right l, 6 +#? set-cursor-position screen, 0x10/x, 0x1/y +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, h, 7/fg, 0/bg +#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, s, 7/fg, 0/bg +#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, l, 7/fg, 0/bg + var a/ecx: int <- copy 0 + var b/edx: int <- copy 0 + var c/ebx: int <- copy 0 + var color/eax: int <- copy 0 + var y/esi: int <- copy 2 + { + compare color, 0x100 + break-if->= + a, b, c <- color-rgb color + a, b, c <- hsl a, b, c + a <- shift-right 6 + b <- shift-right 6 + c <- shift-right 6 + { + compare a, h + break-if-!= + compare b, s + break-if-!= + compare c, l + break-if-!= + set-cursor-position screen, 0x10/x, y + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, color, 7/fg, 0/bg + set-cursor-position screen, 0x14/x, y + draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg + draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 0/fg, color +#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, a, 7/fg, 0/bg +#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, b, 7/fg, 0/bg +#? draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg +#? draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, c, 7/fg, 0/bg + y <- increment + } + color <- increment + loop + } +} diff --git a/apps/ex1.mu b/apps/ex1.mu new file mode 100644 index 00000000..a96bed07 --- /dev/null +++ b/apps/ex1.mu @@ -0,0 +1,14 @@ +# The simplest possible bare-metal program. +# +# To build a disk image: +# ./translate apps/ex1.mu # emits code.img +# To run: +# qemu-system-i386 code.img +# Or: +# bochs -f bochsrc # bochsrc loads code.img +# +# Expected output: blank screen with no errors + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + loop +} diff --git a/apps/ex10.mu b/apps/ex10.mu new file mode 100644 index 00000000..05198738 --- /dev/null +++ b/apps/ex10.mu @@ -0,0 +1,42 @@ +# Demo of mouse support. +# +# To build a disk image: +# ./translate apps/ex10.mu # emits code.img +# To run: +# qemu-system-i386 code.img +# Or: +# bochs -f bochsrc # bochsrc loads code.img +# +# Expected output: +# Values between -256 and +255 as you move the mouse over the window. +# You might need to click on the window once. + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + # repeatedly print out mouse driver results if non-zero + $main:event-loop: { + var dx/eax: int <- copy 0 + var dy/ecx: int <- copy 0 + dx, dy <- read-mouse-event + { + compare dx, 0 + break-if-!= + compare dy, 0 + break-if-!= + loop $main:event-loop + } + { + var dummy1/eax: int <- copy 0 + var dummy2/ecx: int <- copy 0 + dummy1, dummy2 <- draw-text-wrapping-right-then-down-over-full-screen screen, " ", 0/x, 0x10/y, 0x31/fg, 0/bg + } + { + var dummy/ecx: int <- copy 0 + dx, dummy <- draw-int32-decimal-wrapping-right-then-down-over-full-screen screen, dx, 0/x, 0x10/y, 0x31/fg, 0/bg + } + { + var dummy/eax: int <- copy 0 + dummy, dy <- draw-int32-decimal-wrapping-right-then-down-over-full-screen screen, dy, 5/x, 0x10/y, 0x31/fg, 0/bg + } + loop + } +} diff --git a/apps/ex10.mu. b/apps/ex10.mu. new file mode 100644 index 00000000..35fea653 --- /dev/null +++ b/apps/ex10.mu. @@ -0,0 +1,176 @@ +# Demo of mouse support. +# +# To build a disk image: +# ./translate ex10.mu # emits disk.img +# To run: +# qemu-system-i386 disk.img +# Or: +# bochs -f bochsrc # bochsrc loads disk.img + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { +#? var x/esi: int <- copy 0x200 +#? var y/edi: int <- copy 0x180 +#? render-grid x, y + $main:event-loop: { + # read deltas from mouse + var dx/eax: int <- copy 0 + var dy/ecx: int <- copy 0 + dx, dy <- read-mouse-event + # loop if deltas are both 0 + { + compare dx, 0 + break-if-!= + compare dy, 0 + break-if-!= + loop $main:event-loop + } + # render unclamped deltas +#? render-grid x, y + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, dx, 7/fg, 0/bg + draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, dy, 7/fg, 0/bg + move-cursor-to-left-margin-of-next-line screen +#? { +#? var dummy1/eax: int <- copy 0 +#? var dummy2/ecx: int <- copy 0 +#? dummy1, dummy2 <- draw-text-wrapping-right-then-down-over-full-screen screen, " ", 0/x, 0x10/y, 0x31/fg, 0/bg +#? } +#? { +#? var ephemeral-dx/eax: int <- copy dx +#? var dummy/ecx: int <- copy 0 +#? ephemeral-dx, dummy <- draw-int32-decimal-wrapping-right-then-down-over-full-screen screen, ephemeral-dx, 0/x, 0x10/y, 0x31/fg, 0/bg +#? } +#? { +#? var dummy/eax: int <- copy 0 +#? var ephemeral-dy/ecx: int <- copy dy +#? dummy, ephemeral-dy <- draw-int32-decimal-wrapping-right-then-down-over-full-screen screen, ephemeral-dy, 5/x, 0x10/y, 0x31/fg, 0/bg +#? } +#? # clamp deltas +#? $clamp-dx: { +#? compare dx, -0xa +#? { +#? break-if-> +#? dx <- copy -0xa +#? break $clamp-dx +#? } +#? compare dx, 0xa +#? { +#? break-if-< +#? dx <- copy 0xa +#? break $clamp-dx +#? } +#? dx <- copy 0 +#? } +#? $clamp-dy: { +#? compare dy, -0xa +#? { +#? break-if-> +#? dy <- copy -0xa +#? break $clamp-dy +#? } +#? compare dy, 0xa +#? { +#? break-if-< +#? dy <- copy 0xa +#? break $clamp-dy +#? } +#? dy <- copy 0 +#? } +#? # render clamped deltas +#? { +#? var dummy1/eax: int <- copy 0 +#? var dummy2/ecx: int <- copy 0 +#? dummy1, dummy2 <- draw-text-wrapping-right-then-down-over-full-screen screen, " ", 0/x, 0x20/y, 0x31/fg, 0/bg +#? } +#? { +#? var save-dx/eax: int <- copy dx +#? var dummy/ecx: int <- copy 0 +#? save-dx, dummy <- draw-int32-decimal-wrapping-right-then-down-over-full-screen screen, save-dx, 0/x, 0x20/y, 0x31/fg, 0/bg +#? } +#? { +#? var dummy/eax: int <- copy 0 +#? var save-dy/ecx: int <- copy dy +#? dummy, save-dy <- draw-int32-decimal-wrapping-right-then-down-over-full-screen screen, save-dy, 5/x, 0x20/y, 0x31/fg, 0/bg +#? } +#? # loop if deltas are both 0 +#? { +#? compare dx, 0 +#? break-if-!= +#? compare dy, 0 +#? break-if-!= +#? loop $main:event-loop +#? } +#? # accumulate deltas and clamp result within screen bounds +#? x <- add dx +#? compare x, 0 +#? { +#? break-if->= +#? x <- copy 0 +#? } +#? compare x, 0x400 +#? { +#? break-if-< +#? x <- copy 0x3ff +#? } +#? y <- subtract dy # mouse y coordinates are reverse compared to screen +#? compare y, 0 +#? { +#? break-if->= +#? y <- copy 0 +#? } +#? compare y, 0x300 +#? { +#? break-if-< +#? y <- copy 0x2ff +#? } + loop + } +} + +#? fn render-grid curr-x: int, curr-y: int { +#? and-with curr-x, 0xfffffffc +#? and-with curr-y, 0xfffffffc +#? var y/eax: int <- copy 0 +#? { +#? compare y, 0x300/screen-height=768 +#? break-if->= +#? var x/edx: int <- copy 0 +#? { +#? compare x, 0x400/screen-width=1024 +#? break-if->= +#? var color/ecx: int <- copy 0 +#? # set color if either x or y is divisible by 4 +#? var tmp/ebx: int <- copy y +#? tmp <- and 3 +#? compare tmp, 0 +#? { +#? break-if-!= +#? color <- copy 3 +#? } +#? tmp <- copy x +#? tmp <- and 3 +#? compare tmp, 0 +#? { +#? break-if-!= +#? color <- copy 3 +#? } +#? # highlight color if x and y match curr-x and curr-y (quantized) +#? { +#? var xq/edx: int <- copy x +#? xq <- and 0xfffffffc +#? var yq/eax: int <- copy y +#? yq <- and 0xfffffffc +#? compare xq, curr-x +#? break-if-!= +#? compare yq, curr-y +#? break-if-!= +#? color <- copy 0xc +#? } +#? pixel-on-real-screen x, y, color +#? x <- increment +#? loop +#? } +#? y <- increment +#? loop +#? } +#? } diff --git a/apps/ex11.mu b/apps/ex11.mu new file mode 100644 index 00000000..6b967724 --- /dev/null +++ b/apps/ex11.mu @@ -0,0 +1,261 @@ +# Demo of an interactive app: controlling a Bezier curve on screen +# +# To build a disk image: +# ./translate apps/ex11.mu # emits code.img +# To run: +# qemu-system-i386 code.img +# Or: +# bochs -f bochsrc # bochsrc loads code.img +# +# Expected output: a spline with 3 control points. Use `Tab` to switch cursor +# between control points, and arrow keys to move the control point at the +# cursor. + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var env-storage: environment + var env/esi: (addr environment) <- address env-storage + initialize-environment env, 0x200 0x20, 0x180 0x90, 0x180 0x160 + { + render screen, env + edit keyboard, env + loop + } +} + +type environment { + p0: (handle point) + p1: (handle point) + p2: (handle point) + cursor: (handle point) # one of p0, p1 or p2 +} + +type point { + x: int + y: int +} + +fn render screen: (addr screen), _self: (addr environment) { + clear-screen screen + var self/esi: (addr environment) <- copy _self + var tmp-ah/ecx: (addr handle point) <- get self, p0 + var tmp/eax: (addr point) <- lookup *tmp-ah + var p0/ebx: (addr point) <- copy tmp + tmp-ah <- get self, p1 + tmp <- lookup *tmp-ah + var p1/edx: (addr point) <- copy tmp + tmp-ah <- get self, p2 + tmp <- lookup *tmp-ah + var p2/ecx: (addr point) <- copy tmp + # control lines + line screen, p0, p1, 7/color + line screen, p1, p2, 7/color + # curve above control lines + bezier screen, p0, p1, p2, 0xc/color + # points above curve + disc screen, p0, 3/radius, 7/color 0xf/border + disc screen, p1, 3/radius, 7/color 0xf/border + disc screen, p2, 3/radius, 7/color 0xf/border + # cursor last of all + var cursor-ah/eax: (addr handle point) <- get self, cursor + var cursor/eax: (addr point) <- lookup *cursor-ah + cursor screen, cursor, 0xa/side, 3/color +} + +fn bezier screen: (addr screen), _p0: (addr point), _p1: (addr point), _p2: (addr point), color: int { + var p0/esi: (addr point) <- copy _p0 + var x0/ecx: (addr int) <- get p0, x + var y0/edx: (addr int) <- get p0, y + var p1/esi: (addr point) <- copy _p1 + var x1/ebx: (addr int) <- get p1, x + var y1/eax: (addr int) <- get p1, y + var p2/esi: (addr point) <- copy _p2 + var x2/edi: (addr int) <- get p2, x + var y2/esi: (addr int) <- get p2, y + draw-monotonic-bezier screen, *x0 *y0, *x1 *y1, *x2 *y2, color +} + +fn cursor screen: (addr screen), _p: (addr point), side: int, color: int { + var half-side/eax: int <- copy side + half-side <- shift-right 1 + var p/esi: (addr point) <- copy _p + var x-a/ecx: (addr int) <- get p, x + var left-x/ecx: int <- copy *x-a + left-x <- subtract half-side + var y-a/edx: (addr int) <- get p, y + var top-y/edx: int <- copy *y-a + top-y <- subtract half-side + var max/eax: int <- copy left-x + max <- add side + draw-horizontal-line screen, top-y, left-x, max, color + max <- copy top-y + max <- add side + draw-vertical-line screen, left-x, top-y, max, color + var right-x/ebx: int <- copy left-x + right-x <- add side + draw-vertical-line screen, right-x, top-y, max, color + var bottom-y/edx: int <- copy top-y + bottom-y <- add side + draw-horizontal-line screen, bottom-y, left-x, right-x, color +} + +fn edit keyboard: (addr keyboard), _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var key/eax: byte <- read-key keyboard + compare key, 0 + loop-if-= + { + compare key, 9/tab + break-if-!= + toggle-cursor self + return + } + { + compare key, 0x80/left-arrow + break-if-!= + cursor-left self + return + } + { + compare key, 0x83/right-arrow + break-if-!= + cursor-right self + return + } + { + compare key, 0x81/down-arrow + break-if-!= + cursor-down self + return + } + { + compare key, 0x82/up-arrow + break-if-!= + cursor-up self + return + } +} + +fn toggle-cursor _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var cursor-ah/edi: (addr handle point) <- get self, cursor + var p0-ah/ecx: (addr handle point) <- get self, p0 + var p1-ah/edx: (addr handle point) <- get self, p1 + var p2-ah/ebx: (addr handle point) <- get self, p2 + { + var p0?/eax: boolean <- handle-equal? *p0-ah, *cursor-ah + compare p0?, 0/false + break-if-= + copy-object p1-ah, cursor-ah + return + } + { + var p1?/eax: boolean <- handle-equal? *p1-ah, *cursor-ah + compare p1?, 0/false + break-if-= + copy-object p2-ah, cursor-ah + return + } + { + var p2?/eax: boolean <- handle-equal? *p2-ah, *cursor-ah + compare p2?, 0/false + break-if-= + copy-object p0-ah, cursor-ah + return + } + abort "lost cursor" +} + +fn cursor-left _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var cursor-ah/esi: (addr handle point) <- get self, cursor + var cursor/eax: (addr point) <- lookup *cursor-ah + var cursor-x/eax: (addr int) <- get cursor, x + compare *cursor-x, 0x20 + { + break-if-< + subtract-from *cursor-x, 0x20 + } +} + +fn cursor-right _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var cursor-ah/esi: (addr handle point) <- get self, cursor + var cursor/eax: (addr point) <- lookup *cursor-ah + var cursor-x/eax: (addr int) <- get cursor, x + compare *cursor-x, 0x3f0 + { + break-if-> + add-to *cursor-x, 0x20 + } +} + +fn cursor-up _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var cursor-ah/esi: (addr handle point) <- get self, cursor + var cursor/eax: (addr point) <- lookup *cursor-ah + var cursor-y/eax: (addr int) <- get cursor, y + compare *cursor-y, 0x20 + { + break-if-< + subtract-from *cursor-y, 0x20 + } +} + +fn cursor-down _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var cursor-ah/esi: (addr handle point) <- get self, cursor + var cursor/eax: (addr point) <- lookup *cursor-ah + var cursor-y/eax: (addr int) <- get cursor, y + compare *cursor-y, 0x2f0 + { + break-if-> + add-to *cursor-y, 0x20 + } +} + +fn line screen: (addr screen), _p0: (addr point), _p1: (addr point), color: int { + var p0/esi: (addr point) <- copy _p0 + var x0/ecx: (addr int) <- get p0, x + var y0/edx: (addr int) <- get p0, y + var p1/esi: (addr point) <- copy _p1 + var x1/ebx: (addr int) <- get p1, x + var y1/eax: (addr int) <- get p1, y + draw-line screen, *x0 *y0, *x1 *y1, color +} + +fn disc screen: (addr screen), _p: (addr point), radius: int, color: int, border-color: int { + var p/esi: (addr point) <- copy _p + var x/ecx: (addr int) <- get p, x + var y/edx: (addr int) <- get p, y + draw-disc screen, *x *y, radius, color, border-color +} + +fn initialize-environment _self: (addr environment), x0: int, y0: int, x1: int, y1: int, x2: int, y2: int { + var self/esi: (addr environment) <- copy _self + var p0-ah/eax: (addr handle point) <- get self, p0 + allocate p0-ah + var p0/eax: (addr point) <- lookup *p0-ah + initialize-point p0, x0 y0 + var p1-ah/eax: (addr handle point) <- get self, p1 + allocate p1-ah + var p1/eax: (addr point) <- lookup *p1-ah + initialize-point p1, x1 y1 + var p2-ah/eax: (addr handle point) <- get self, p2 + allocate p2-ah + var p2/eax: (addr point) <- lookup *p2-ah + initialize-point p2, x2 y2 + # cursor initially at p0 + var cursor-ah/edi: (addr handle point) <- get self, cursor + var src-ah/esi: (addr handle point) <- get self, p0 + copy-object src-ah, cursor-ah +} + +fn initialize-point _p: (addr point), x: int, y: int { + var p/esi: (addr point) <- copy _p + var dest/eax: (addr int) <- get p, x + var src/ecx: int <- copy x + copy-to *dest, src + dest <- get p, y + src <- copy y + copy-to *dest, src +} diff --git a/apps/ex12.mu b/apps/ex12.mu new file mode 100644 index 00000000..79c259b8 --- /dev/null +++ b/apps/ex12.mu @@ -0,0 +1,28 @@ +# Checking the timer. +# +# To build a disk image: +# ./translate apps/ex12.mu # emits code.img +# To run: +# qemu-system-i386 code.img +# Or: +# bochs -f bochsrc # bochsrc loads code.img +# +# Expected output: text with slowly updating colors + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var fg/ecx: int <- copy 0 + var prev-timer-counter/edx: int <- copy 0 + { + var dummy/eax: int <- draw-text-rightward screen, "hello from baremetal Mu!", 0x10/x, 0x400/xmax, 0x10/y, fg, 0/bg + # wait for timer to bump + { + var curr-timer-counter/eax: int <- timer-counter + compare curr-timer-counter, prev-timer-counter + loop-if-= + prev-timer-counter <- copy curr-timer-counter + } + # switch color + fg <- increment + loop + } +} diff --git a/apps/ex2.mu b/apps/ex2.mu new file mode 100644 index 00000000..be66d883 --- /dev/null +++ b/apps/ex2.mu @@ -0,0 +1,28 @@ +# Test out the video mode by filling in the screen with pixels. +# +# To build a disk image: +# ./translate apps/ex2.mu # emits code.img +# To run: +# qemu-system-i386 code.img +# Or: +# bochs -f bochsrc # bochsrc loads code.img + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var y/eax: int <- copy 0 + { + compare y, 0x300/screen-height=768 + break-if->= + var x/edx: int <- copy 0 + { + compare x, 0x400/screen-width=1024 + break-if->= + var color/ecx: int <- copy x + color <- and 0xff + pixel screen x, y, color + x <- increment + loop + } + y <- increment + loop + } +} diff --git a/apps/ex3.mu b/apps/ex3.mu new file mode 100644 index 00000000..155457c6 --- /dev/null +++ b/apps/ex3.mu @@ -0,0 +1,31 @@ +# Draw pixels in response to keyboard events, starting from the top-left +# and in raster order. +# +# To build a disk image: +# ./translate apps/ex3.mu # emits code.img +# To run: +# qemu-system-i386 code.img +# Or: +# bochs -f bochsrc # bochsrc loads code.img +# +# Expected output: a new green pixel starting from the top left corner of the +# screen every time you press a key (letter or digit) + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var x/ecx: int <- copy 0 + var y/edx: int <- copy 0 + { + var key/eax: byte <- read-key keyboard + compare key, 0 + loop-if-= # busy wait + pixel-on-real-screen x, y, 0x31/green + x <- increment + compare x, 0x400/screen-width=1024 + { + break-if-< + y <- increment + x <- copy 0 + } + loop + } +} diff --git a/apps/ex4.mu b/apps/ex4.mu new file mode 100644 index 00000000..9c3e28ee --- /dev/null +++ b/apps/ex4.mu @@ -0,0 +1,14 @@ +# Draw a character using the built-in font (GNU unifont) +# +# To build a disk image: +# ./translate apps/ex4.mu # emits code.img +# To run: +# qemu-system-i386 code.img +# Or: +# bochs -f bochsrc # bochsrc loads code.img +# +# Expected output: letter 'A' in green near the top-left corner of screen + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + draw-code-point screen, 0x41/A, 2/row, 1/col, 0xa/fg, 0/bg +} diff --git a/apps/ex5.mu b/apps/ex5.mu new file mode 100644 index 00000000..07e8bc1a --- /dev/null +++ b/apps/ex5.mu @@ -0,0 +1,16 @@ +# Draw a single line of ASCII text using the built-in font (GNU unifont) +# Also demonstrates bounds-checking _before_ drawing. +# +# To build a disk image: +# ./translate apps/ex5.mu # emits code.img +# To run: +# qemu-system-i386 code.img +# Or: +# bochs -f bochsrc # bochsrc loads code.img +# +# Expected output: text in green near the top-left corner of screen + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var dummy/eax: int <- draw-text-rightward screen, "hello from baremetal Mu!", 0x10/x, 0x400/xmax, 0x10/y, 0xa/fg, 0/bg + dummy <- draw-text-rightward screen, "you shouldn't see this", 0x10/x, 0xa0/xmax, 0x30/y, 3/fg, 0/bg # xmax is too narrow +} diff --git a/apps/ex6.mu b/apps/ex6.mu new file mode 100644 index 00000000..fbad3c13 --- /dev/null +++ b/apps/ex6.mu @@ -0,0 +1,32 @@ +# Drawing ASCII text incrementally. +# +# To build a disk image: +# ./translate apps/ex6.mu # emits code.img +# To run: +# qemu-system-i386 code.img +# Or: +# bochs -f bochsrc # bochsrc loads code.img +# +# Expected output: a box and text that doesn't overflow it + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + # drawing text within a bounding box + draw-box-on-real-screen 0xf, 0x1f, 0x79, 0x51, 0x4 + var x/eax: int <- copy 0x20 + var y/ecx: int <- copy 0x20 + x, y <- draw-text-wrapping-right-then-down screen, "hello ", 0x10/xmin, 0x20/ymin, 0x78/xmax, 0x50/ymax, x, y, 0xa/fg, 0/bg + x, y <- draw-text-wrapping-right-then-down screen, "from ", 0x10/xmin, 0x20/ymin, 0x78/xmax, 0x50/ymax, x, y, 0xa/fg, 0/bg + x, y <- draw-text-wrapping-right-then-down screen, "baremetal ", 0x10/xmin, 0x20/ymin, 0x78/xmax, 0x50/ymax, x, y, 0xa/fg, 0/bg + x, y <- draw-text-wrapping-right-then-down screen, "Mu!", 0x10/xmin, 0x20/ymin, 0x78/xmax, 0x50/ymax, x, y, 0xa/fg, 0/bg + + # drawing at the cursor in multiple directions + draw-text-wrapping-down-then-right-from-cursor-over-full-screen screen, "abc", 0xa/fg, 0/bg + draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "def", 0xa/fg, 0/bg + + # test drawing near the edge + x <- draw-text-rightward screen, "R", 0x7f/x, 0x80/xmax=screen-width, 0x18/y, 0xa/fg, 0/bg + draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "wrapped from R", 0xa/fg, 0/bg + + x <- draw-text-downward screen, "D", 0x20/x, 0x2f/y, 0x30/ymax=screen-height, 0xa/fg, 0/bg + draw-text-wrapping-down-then-right-from-cursor-over-full-screen screen, "wrapped from D", 0xa/fg, 0/bg +} diff --git a/apps/ex7.mu b/apps/ex7.mu new file mode 100644 index 00000000..bd0afd20 --- /dev/null +++ b/apps/ex7.mu @@ -0,0 +1,46 @@ +# Cursor-based motions. +# +# To build a disk image: +# ./translate apps/ex7.mu # emits code.img +# To run: +# qemu-system-i386 code.img +# Or: +# bochs -f bochsrc # bochsrc loads code.img +# +# Expected output: an interactive game a bit like "snakes". Try pressing h, j, +# k, l. + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var space/eax: grapheme <- copy 0x20 + set-cursor-position screen, 0, 0 + { + draw-cursor screen, space + var key/eax: byte <- read-key keyboard + { + compare key, 0x68/h + break-if-!= + draw-code-point-at-cursor screen, 0x2d/dash, 0x31/fg, 0/bg + move-cursor-left 0 + } + { + compare key, 0x6a/j + break-if-!= + draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x31/fg, 0/bg + move-cursor-down 0 + } + { + compare key, 0x6b/k + break-if-!= + draw-code-point-at-cursor screen, 0x7c/vertical-bar, 0x31/fg, 0/bg + move-cursor-up 0 + } + { + compare key, 0x6c/l + break-if-!= + var g/eax: code-point <- copy 0x2d/dash + draw-code-point-at-cursor screen, 0x2d/dash, 0x31/fg, 0/bg + move-cursor-right 0 + } + loop + } +} diff --git a/apps/ex8.mu b/apps/ex8.mu new file mode 100644 index 00000000..c5e695ed --- /dev/null +++ b/apps/ex8.mu @@ -0,0 +1,12 @@ +# Demo of floating-point support. +# +# To build a disk image: +# ./translate apps/ex8.mu # emits code.img +# To run: +# bochs -f bochsrc # bochsrc loads code.img +# Set a breakpoint at 0x7c00 and start stepping. + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var n/eax: int <- copy 0 + var result/xmm0: float <- convert n +} diff --git a/apps/ex9.mu b/apps/ex9.mu new file mode 100644 index 00000000..30853c69 --- /dev/null +++ b/apps/ex9.mu @@ -0,0 +1,51 @@ +# Demo of reading and writing to disk. +# +# Steps for trying it out: +# 1. Translate this example into a disk image code.img. +# ./translate apps/ex9.mu +# 2. Build a second disk image data.img containing some text. +# dd if=/dev/zero of=data.img count=20160 +# echo 'abc def ghi' |dd of=data.img conv=notrunc +# 3. Familiarize yourself with how the data disk looks within xxd: +# xxd data.img |head +# 4. Run in an emulator, either Qemu or Bochs. +# qemu-system-i386 -hda code.img -hdb data.img +# bochs -f bochsrc.2disks +# 5. Exit the emulator. +# 6. Notice that the data disk now contains the word count of the original text. +# xxd data.img |head + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var text-storage: (stream byte 0x200) + var text/esi: (addr stream byte) <- address text-storage + load-sectors data-disk, 0/lba, 1/num-sectors, text + + var word-count/eax: int <- word-count text + + var result-storage: (stream byte 0x10) + var result/edi: (addr stream byte) <- address result-storage + write-int32-decimal result, word-count + store-sectors data-disk, 0/lba, 1/num-sectors, result +} + +fn word-count in: (addr stream byte) -> _/eax: int { + var result/edi: int <- copy 0 + { + var done?/eax: boolean <- stream-empty? in + compare done?, 0/false + break-if-!= + var g/eax: grapheme <- read-grapheme in + { + compare g, 0x20/space + break-if-!= + result <- increment + } + { + compare g, 0xa/newline + break-if-!= + result <- increment + } + loop + } + return result +} diff --git a/apps/hest-life.mu b/apps/hest-life.mu new file mode 100644 index 00000000..62f2d945 --- /dev/null +++ b/apps/hest-life.mu @@ -0,0 +1,1029 @@ +# Conway's Game of Life in a Hestified way +# https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life +# https://ivanish.ca/hest-podcast +# +# To build: +# $ ./translate apps/hest-life.mu +# I run it on my 2.5GHz Linux laptop like this: +# $ qemu-system-i386 code.img +# +# If things seem too fast or too slow on your computer, adjust the loop bounds +# in the function `linger` at the bottom. Its value will depend on how you +# accelerate Qemu (`-accel help`). Mu will eventually get a clock to obviate +# the need for this tuning. +# +# Keyboard shortcuts: +# space: pause/resume +# 0: restart time +# l: start looping from 0 to curren time +# L: stop looping +# +: zoom in +# -: zoom out + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var env-storage: environment + var env/esi: (addr environment) <- address env-storage + initialize-environment env + var second-buffer: screen + var second-screen/edi: (addr screen) <- address second-buffer + initialize-screen second-screen, 0x80, 0x30, 1/include-pixels + render second-screen, env + convert-graphemes-to-pixels second-screen + copy-pixels second-screen, screen + { + edit keyboard, env + var play?/eax: (addr boolean) <- get env, play? + compare *play?, 0/false + { + break-if-= + step env + clear-screen second-screen + render second-screen, env + convert-graphemes-to-pixels second-screen + copy-pixels second-screen, screen + } + linger env + loop + } +} + +type environment { + data: (handle array handle array cell) + zoom: int # 0 = 1024 px per cell; 5 = 4px per cell; each step adjusts by a factor of 4 + tick: int + play?: boolean + loop: int # if non-zero, return tick to 0 after this point +} + +type cell { + curr: boolean + next: boolean +} + +fn render screen: (addr screen), _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var zoom/eax: (addr int) <- get self, zoom + compare *zoom, 0 + { + break-if-!= + clear-screen screen + render0 screen, self + } + compare *zoom, 1 + { + break-if-!= + clear-screen screen + render1 screen, self + } + compare *zoom, 4 + { + break-if-!= + render4 screen, self + } + # clock + var tick-a/eax: (addr int) <- get self, tick + set-cursor-position screen, 0x78/x, 0/y + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, *tick-a, 7/fg 0/bg +} + +# Lots of hardcoded constants for now. +# TODO: split this up into a primitive to render a single cell and its +# incoming edges (but not the neighboring nodes they emanate from) +fn render0 screen: (addr screen), _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + # cell border + draw-vertical-line screen, 0xc0/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey + draw-vertical-line screen, 0x340/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey + draw-horizontal-line screen, 0x40/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey + draw-horizontal-line screen, 0x2c0/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey + # neighboring inputs, corners + var color/eax: int <- state-color self, 0x7f/cur-topleftx, 0x5f/cur-toplefty + draw-rect screen, 0x90/xmin 0x10/ymin, 0xb0/xmax 0x30/ymax, color + color <- state-color self, 0x81/cur-toprightx, 0x5f/cur-toprighty + draw-rect screen, 0x350/xmin 0x10/ymin, 0x370/xmax 0x30/ymax, color + color <- state-color self, 0x7f/cur-botleftx, 0x61/cur-botlefty + draw-rect screen, 0x90/xmin 0x2d0/ymin, 0xb0/xmax 0x2f0/ymax, color + color <- state-color self, 0x81/cur-botrightx, 0x61/cur-botrighty + draw-rect screen, 0x350/xmin 0x2d0/ymin, 0x370/xmax 0x2f0/ymax, color + # neighboring inputs, edges + color <- state-color self, 0x80/cur-topx, 0x5f/cur-topy + draw-rect screen, 0x1f0/xmin 0x10/ymin, 0x210/xmax 0x30/ymax, color + color <- state-color self, 0x7f/cur-leftx, 0x60/cur-lefty + draw-rect screen, 0x90/xmin 0x170/ymin, 0xb0/xmax 0x190/ymax, color + color <- state-color self, 0x80/cur-botx, 0x61/cur-boty + draw-rect screen, 0x1f0/xmin 0x2d0/ymin, 0x210/xmax 0x2f0/ymax, color + color <- state-color self, 0x81/cur-rightx, 0x60/cur-righty + draw-rect screen, 0x350/xmin 0x170/ymin, 0x370/xmax 0x190/ymax, color + # sum node + draw-rect screen, 0x170/xsmin 0x140/ysmin, 0x190/xsmax 0x160/ysmax, 0x40/color + set-cursor-position screen, 0x2d/scol, 0x13/srow + draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "+", 0xf/color, 0/bg + # conveyors from neighboring inputs to sum node + draw-monotonic-bezier screen, 0xa0/x0 0x20/y0, 0x100/x1 0x150/ys, 0x180/xs 0x150/ys, 4/color + draw-monotonic-bezier screen, 0xa0/x0 0x180/y0, 0xc0/x1 0x150/ys, 0x180/xs 0x150/ys, 4/color + draw-monotonic-bezier screen, 0xa0/x0 0x2e0/y0, 0x100/x1 0x150/ys, 0x180/xs 0x150/ys, 4/color + draw-monotonic-bezier screen, 0x200/x0 0x20/y0, 0x180/x1 0x90/y1, 0x180/xs 0x150/ys, 4/color + draw-monotonic-bezier screen, 0x200/x0 0x2e0/y0, 0x180/x1 0x200/y1, 0x180/xs 0x150/ys, 4/color + draw-monotonic-bezier screen, 0x360/x0 0x20/y0, 0x180/x1 0xc0/y1, 0x180/xs 0x150/ys, 4/color + draw-monotonic-bezier screen, 0x360/x0 0x180/y0, 0x35c/x1 0x150/ys, 0x180/xs 0x150/ys, 4/color + draw-monotonic-bezier screen, 0x360/x0 0x2e0/y0, 0x180/x1 0x200/y1, 0x180/xs 0x150/ys, 4/color + # filter node + draw-rect screen, 0x200/xfmin 0x1c0/yfmin, 0x220/xfmax 0x1e0/yfmax, 0x31/color + set-cursor-position screen, 0x40/fcol, 0x1b/frow + draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "?", 0xf/color, 0/bg + # conveyor from sum node to filter node + draw-line screen 0x180/xs, 0x150/ys, 0x210/xf, 0x1d0/yf, 0xa2/color + # cell outputs at corners + var color/eax: int <- state-color self, 0x80/curx, 0x60/cury + draw-rect screen, 0xd0/xmin 0x50/ymin, 0xf0/xmax 0x70/ymax, color + draw-rect screen, 0x310/xmin 0x50/ymin, 0x330/xmax 0x70/ymax, color + draw-rect screen, 0xd0/xmin 0x290/ymin, 0xf0/xmax 0x2b0/ymax, color + draw-rect screen, 0x310/xmin 0x290/ymin, 0x330/xmax 0x2b0/ymax, color + # cell outputs at edges + draw-rect screen, 0x1f0/xmin 0x50/ymin, 0x210/xmax 0x70/ymax, color + draw-rect screen, 0xd0/xmin 0x170/ymin, 0xf0/xmax 0x190/ymax, color + draw-rect screen, 0x1f0/xmin 0x290/ymin, 0x210/xmax 0x2b0/ymax, color + draw-rect screen, 0x310/xmin 0x170/ymin, 0x330/xmax 0x190/ymax, color + # conveyors from filter to outputs + draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf, 0x1c0/x1 0x60/y1, 0xe0/x2 0x60/y2, 0x2a/color + draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf, 0xe0/x1 0x1c0/y1, 0xe0/x2 0x180/y2, 0x2a/color + draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf, 0x1c0/x1 0x2a0/y1, 0xe0/x2 0x2a0/y2, 0x2a/color + draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf, 0x210/x1 0x60/y1, 0x200/x2 0x60/y2, 0x2a/color + draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf, 0x210/x1 0x230/y1, 0x200/x2 0x2a0/y2, 0x2a/color + draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf, 0x320/x1 0x120/y1, 0x320/x2 0x60/y2, 0x2a/color + draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf, 0x320/x1 0x1c0/y1 0x320/x2 0x180/y2, 0x2a/color + draw-monotonic-bezier screen, 0x210/xf 0x1d0/yf, 0x320/x1 0x230/y1, 0x320/x2 0x2a0/y2, 0x2a/color + # time-variant portion: 16 repeating steps + var tick-a/eax: (addr int) <- get self, tick + var progress/eax: int <- copy *tick-a + progress <- and 0xf + # 7 time steps for getting inputs to sum + { + compare progress, 7 + break-if->= + var u/xmm7: float <- convert progress + var six/eax: int <- copy 6 + var six-f/xmm0: float <- convert six + u <- divide six-f + # points on conveyors from neighboring cells + draw-bezier-point screen, u, 0xa0/x0 0x20/y0, 0x100/x1 0x150/ys, 0x180/xs 0x150/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0xa0/x0 0x180/y0, 0xc0/x1 0x150/ys, 0x180/xs 0x150/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0xa0/x0 0x2e0/y0, 0x100/x1 0x150/ys, 0x180/xs 0x150/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x200/x0 0x20/y0, 0x180/x1 0x90/y1, 0x180/xs 0x150/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x200/x0 0x2e0/y0, 0x180/x1 0x200/y1, 0x180/xs 0x150/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x360/x0 0x20/y0, 0x180/x1 0xc0/y1, 0x180/xs 0x150/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x360/x0 0x180/y0, 0x35c/x1 0x150/ys, 0x180/xs 0x150/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x360/x0 0x2e0/y0, 0x180/x1 0x200/y1, 0x180/xs 0x150/ys, 7/color, 4/radius + return + } + # two time steps for getting count to filter + progress <- subtract 7 + { + compare progress, 2 + break-if->= + progress <- increment # (0, 1) => (1, 2) + var u/xmm7: float <- convert progress + var three/eax: int <- copy 3 + var three-f/xmm0: float <- convert three + u <- divide three-f + draw-linear-point screen, u, 0x180/xs, 0x150/ys, 0x210/xf, 0x1d0/yf, 7/color, 4/radius + set-cursor-position screen, 0x3a/scol, 0x18/srow + var n/eax: int <- num-live-neighbors self, 0x80/curx, 0x60/cury + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, n, 0xf/fg 0/bg + return + } + # final 7 time steps for updating output + progress <- subtract 2 + # points on conveyors to outputs + var u/xmm7: float <- convert progress + var six/eax: int <- copy 6 + var six-f/xmm0: float <- convert six + u <- divide six-f + draw-bezier-point screen, u, 0x210/xf 0x1d0/yf, 0x1c0/x1 0x60/y1, 0xe0/x2 0x60/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x210/xf 0x1d0/yf, 0xe0/x1 0x1c0/y1, 0xe0/x2 0x180/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x210/xf 0x1d0/yf, 0x1c0/x1 0x2a0/y1, 0xe0/x2 0x2a0/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x210/xf 0x1d0/yf, 0x210/xf 0x60/y1, 0x200/x2 0x60/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x210/xf 0x1d0/yf, 0x210/xf 0x230/y1, 0x200/x2 0x2a0/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x210/xf 0x1d0/yf, 0x320/x1 0x120/y1, 0x320/x2 0x60/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x210/xf 0x1d0/yf, 0x320/x1 0x1c0/y1, 0x320/x2 0x180/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x210/xf 0x1d0/yf, 0x320/x1 0x230/y1, 0x320/x2 0x2a0/y2, 7/color, 4/radius +} + +fn render1 screen: (addr screen), _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + # cell borders + draw-vertical-line screen, 0xe0/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey + draw-vertical-line screen, 0x200/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey + draw-vertical-line screen, 0x320/x, 0/ymin, 0x300/ymax, 0x16/color=dark-grey + draw-horizontal-line screen, 0x60/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey + draw-horizontal-line screen, 0x180/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey + draw-horizontal-line screen, 0x2a0/y, 0/xmin, 0x400/xmax, 0x16/color=dark-grey + # cell 0: outputs + var color/eax: int <- state-color self, 0x80/curx, 0x60/cury + draw-rect screen, 0xe8/xmin 0x68/ymin, 0x118/xmax 0x98/ymax, color + draw-rect screen, 0xe8/xmin 0xd0/ymin, 0x118/xmax 0x100/ymax, color + draw-rect screen, 0xe8/xmin 0x148/ymin, 0x118/xmax 0x178/ymax, color + draw-rect screen, 0x158/xmin 0x68/ymin, 0x188/xmax 0x98/ymax, color + draw-rect screen, 0x158/xmin 0x148/ymin, 0x188/xmax 0x178/ymax, color + draw-rect screen, 0x1c8/xmin 0x68/ymin, 0x1f8/xmax 0x98/ymax, color + draw-rect screen, 0x1c8/xmin 0xd0/ymin, 0x1f8/xmax 0x100/ymax, color + draw-rect screen, 0x1c8/xmin 0x148/ymin, 0x1f8/xmax 0x178/ymax, color + # cell 1: outputs + var color/eax: int <- state-color self, 0x81/curx, 0x60/cury + draw-rect screen, 0x208/xmin 0x68/ymin, 0x238/xmax 0x98/ymax, color + draw-rect screen, 0x208/xmin 0xd0/ymin, 0x238/xmax 0x100/ymax, color + draw-rect screen, 0x208/xmin 0x148/ymin, 0x238/xmax 0x178/ymax, color + draw-rect screen, 0x278/xmin 0x68/ymin, 0x2a8/xmax 0x98/ymax, color + draw-rect screen, 0x278/xmin 0x148/ymin, 0x2a8/xmax 0x178/ymax, color + draw-rect screen, 0x2e8/xmin 0x68/ymin, 0x318/xmax 0x98/ymax, color + draw-rect screen, 0x2e8/xmin 0xd0/ymin, 0x318/xmax 0x100/ymax, color + draw-rect screen, 0x2e8/xmin 0x148/ymin, 0x318/xmax 0x178/ymax, color + # cell 2: outputs + var color/eax: int <- state-color self, 0x80/curx, 0x61/cury + draw-rect screen, 0xe8/xmin 0x188/ymin, 0x118/xmax 0x1b8/ymax, color + draw-rect screen, 0xe8/xmin 0x1f0/ymin, 0x118/xmax 0x220/ymax, color + draw-rect screen, 0xe8/xmin 0x268/ymin, 0x118/xmax 0x298/ymax, color + draw-rect screen, 0x158/xmin 0x188/ymin, 0x188/xmax 0x1b8/ymax, color + draw-rect screen, 0x158/xmin 0x268/ymin, 0x188/xmax 0x298/ymax, color + draw-rect screen, 0x1c8/xmin 0x188/ymin, 0x1f8/xmax 0x1b8/ymax, color + draw-rect screen, 0x1c8/xmin 0x1f0/ymin, 0x1f8/xmax 0x220/ymax, color + draw-rect screen, 0x1c8/xmin 0x268/ymin, 0x1f8/xmax 0x298/ymax, color + # cell 3: outputs + var color/eax: int <- state-color self, 0x81/curx, 0x61/cury + draw-rect screen, 0x208/xmin 0x188/ymin, 0x238/xmax 0x1b8/ymax, color + draw-rect screen, 0x208/xmin 0x1f0/ymin, 0x238/xmax 0x220/ymax, color + draw-rect screen, 0x208/xmin 0x268/ymin, 0x238/xmax 0x298/ymax, color + draw-rect screen, 0x278/xmin 0x188/ymin, 0x2a8/xmax 0x1b8/ymax, color + draw-rect screen, 0x278/xmin 0x268/ymin, 0x2a8/xmax 0x298/ymax, color + draw-rect screen, 0x2e8/xmin 0x188/ymin, 0x318/xmax 0x1b8/ymax, color + draw-rect screen, 0x2e8/xmin 0x1f0/ymin, 0x318/xmax 0x220/ymax, color + draw-rect screen, 0x2e8/xmin 0x268/ymin, 0x318/xmax 0x298/ymax, color + # neighboring nodes + var color/eax: int <- state-color self, 0x7f/curx, 0x5f/cury + draw-rect screen, 0xa8/xmin 0x28/ymin, 0xd8/xmax 0x58/ymax, color + var color/eax: int <- state-color self, 0x80/curx, 0x5f/cury + draw-rect screen, 0x158/xmin 0x28/ymin, 0x188/xmax 0x58/ymax, color + draw-rect screen, 0x1c8/xmin 0x28/ymin, 0x1f8/xmax 0x58/ymax, color + var color/eax: int <- state-color self, 0x81/curx, 0x5f/cury + draw-rect screen, 0x208/xmin 0x28/ymin, 0x238/xmax 0x58/ymax, color + draw-rect screen, 0x278/xmin 0x28/ymin, 0x2a8/xmax 0x58/ymax, color + var color/eax: int <- state-color self, 0x82/curx, 0x5f/cury + draw-rect screen, 0x328/xmin 0x28/ymin, 0x358/xmax 0x58/ymax, color + var color/eax: int <- state-color self, 0x7f/curx, 0x60/cury + draw-rect screen, 0xa8/xmin 0xd0/ymin, 0xd8/xmax 0x100/ymax, color + draw-rect screen, 0xa8/xmin 0x148/ymin, 0xd8/xmax 0x178/ymax, color + var color/eax: int <- state-color self, 0x82/curx, 0x60/cury + draw-rect screen, 0x328/xmin 0xd0/ymin, 0x358/xmax 0x100/ymax, color + draw-rect screen, 0x328/xmin 0x148/ymin, 0x358/xmax 0x178/ymax, color + var color/eax: int <- state-color self, 0x7f/curx, 0x61/cury + draw-rect screen, 0xa8/xmin 0x188/ymin, 0xd8/xmax 0x1b8/ymax, color + draw-rect screen, 0xa8/xmin 0x1f0/ymin, 0xd8/xmax 0x220/ymax, color + var color/eax: int <- state-color self, 0x82/curx, 0x61/cury + draw-rect screen, 0x328/xmin 0x188/ymin, 0x358/xmax 0x1b8/ymax, color + draw-rect screen, 0x328/xmin 0x1f0/ymin, 0x358/xmax 0x220/ymax, color + var color/eax: int <- state-color self, 0x7f/curx, 0x62/cury + draw-rect screen, 0xa8/xmin 0x2a8/ymin, 0xd8/xmax 0x2d8/ymax, color + var color/eax: int <- state-color self, 0x80/curx, 0x62/cury + draw-rect screen, 0x158/xmin 0x2a8/ymin, 0x188/xmax 0x2d8/ymax, color + draw-rect screen, 0x1c8/xmin 0x2a8/ymin, 0x1f8/xmax 0x2d8/ymax, color + var color/eax: int <- state-color self, 0x81/curx, 0x62/cury + draw-rect screen, 0x208/xmin 0x2a8/ymin, 0x238/xmax 0x2d8/ymax, color + draw-rect screen, 0x278/xmin 0x2a8/ymin, 0x2a8/xmax 0x2d8/ymax, color + var color/eax: int <- state-color self, 0x82/curx, 0x62/cury + draw-rect screen, 0x328/xmin 0x2a8/ymin, 0x358/xmax 0x2d8/ymax, color + # cell 0: sum and filter nodes + draw-rect screen, 0x148/xsmin 0xc8/ysmin, 0x158/xsmax 0xd8/ysmax, 0x40/color + draw-rect screen, 0x180/xfmin 0xf8/yfmin, 0x190/xfmax 0x108/yfmax, 0x31/color + # cell 1: sum and filter nodes + draw-rect screen, 0x268/xsmin 0xc8/ysmin, 0x278/xsmax 0xd8/ysmax, 0x40/color + draw-rect screen, 0x2a0/xfmin 0xf8/yfmin, 0x2b0/xfmax 0x108/yfmax, 0x31/color + # cell 2: sum and filter nodes + draw-rect screen, 0x148/xsmin 0x1e8/ysmin, 0x158/xsmax 0x1f8/ysmax, 0x40/color + draw-rect screen, 0x180/xfmin 0x218/yfmin, 0x190/xfmax 0x228/yfmax, 0x31/color + # cell 3: sum and filter nodes + draw-rect screen, 0x268/xsmin 0x1e8/ysmin, 0x278/xsmax 0x1f8/ysmax, 0x40/color + draw-rect screen, 0x2a0/xfmin 0x218/yfmin, 0x2b0/xfmax 0x228/yfmax, 0x31/color + # neighbor counts + var n/eax: int <- num-live-neighbors self, 0x80/curx, 0x60/cury + set-cursor-position screen, 0x2d, 0xe + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, n, 0xf/fg 0/bg + var n/eax: int <- num-live-neighbors self, 0x81/curx, 0x60/cury + set-cursor-position screen, 0x52, 0xe + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, n, 0xf/fg 0/bg + var n/eax: int <- num-live-neighbors self, 0x80/curx, 0x61/cury + set-cursor-position screen, 0x2d, 0x20 + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, n, 0xf/fg 0/bg + var n/eax: int <- num-live-neighbors self, 0x81/curx, 0x61/cury + set-cursor-position screen, 0x52, 0x20 + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, n, 0xf/fg 0/bg + # cell 0: conveyors from neighboring inputs to sum node + draw-monotonic-bezier screen, 0xc0/x0 0x40/y0, 0x100/x1 0xd0/ys, 0x150/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0xc0/x0 0xe8/y0, 0xc0/x1 0xd0/ys, 0x150/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0xc0/x0 0x1a0/y0, 0xe0/x1 0xd0/ys, 0x150/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x170/x0 0x40/y0, 0x150/x1 0x80/y1, 0x150/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x170/x0 0x1a0/y0, 0x150/x1 0x1a0/y1, 0x150/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x220/x0 0x40/y0, 0x150/x1 0x80/y1, 0x150/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x220/x0 0xe8/y0, 0x220/x1 0xd0/y1, 0x150/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x220/x0 0x1a0/y0, 0x180/x1 0x1a0/y1, 0x150/xs 0xd0/ys, 4/color + # cell 0: conveyors from filter to outputs + draw-monotonic-bezier screen, 0x188/xf 0x100/yf, 0x160/x1 0x8c/y1, 0x100/x2 0x80/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x100/yf, 0x100/x1 0x100/y1, 0x100/x2 0xe8/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x100/yf, 0x100/x1 0x100/y1, 0x100/x2 0x160/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x100/yf, 0x188/x1 0x80/y1, 0x170/x2 0x80/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x100/yf, 0x188/x1 0x160/y1, 0x170/x2 0x160/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x100/yf, 0x1e0/x1 0x100/y1, 0x1e0/x2 0x80/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x100/yf, 0x1e0/x1 0x100/y1 0x1e0/x2 0xe8/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x100/yf, 0x1e0/x1 0x100/y1, 0x1e0/x2 0x160/y2, 0x2a/color + # cell 0: time-variant portion: 16 repeating steps + $render1:cell0: { + var tick-a/eax: (addr int) <- get self, tick + var progress/eax: int <- copy *tick-a + progress <- and 0xf + # cell 0: 7 time steps for getting inputs to sum + { + compare progress, 7 + break-if->= + var u/xmm7: float <- convert progress + var six/eax: int <- copy 6 + var six-f/xmm0: float <- convert six + u <- divide six-f + # points on conveyors from neighboring cells + draw-bezier-point screen, u, 0xc0/x0 0x40/y0, 0x100/x1 0xd0/ys, 0x150/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0xc0/x0 0xe8/y0, 0xc0/x1 0xd0/ys, 0x150/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0xc0/x0 0x1a0/y0, 0xe0/x1 0xd0/ys, 0x150/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x170/x0 0x40/y0, 0x150/x1 0x80/y1, 0x150/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x170/x0 0x1a0/y0, 0x150/x1 0x1a0/y1, 0x150/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x220/x0 0x40/y0, 0x150/x1 0x80/y1, 0x150/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x220/x0 0xe8/y0, 0x220/x1 0xd0/y1, 0x150/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x220/x0 0x1a0/y0, 0x180/x1 0x1a0/y1, 0x150/xs 0xd0/ys, 7/color, 4/radius + break $render1:cell0 + } + # cell 0: two time steps for getting count to filter + progress <- subtract 7 + { + compare progress, 2 + break-if->= + break $render1:cell0 + } + # cell 0: final 7 time steps for updating output + progress <- subtract 2 + # cell 0: points on conveyors to outputs + var u/xmm7: float <- convert progress + var six/eax: int <- copy 6 + var six-f/xmm0: float <- convert six + u <- divide six-f + draw-bezier-point screen, u, 0x188/xf 0x100/yf, 0x160/x1 0x8c/y1, 0x100/x2 0x80/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x100/yf, 0x100/x1 0x100/y1, 0x100/x2 0xe8/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x100/yf, 0x100/x1 0x100/y1, 0x100/x2 0x160/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x100/yf, 0x188/xf 0x80/y1, 0x170/x2 0x80/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x100/yf, 0x188/xf 0x160/y1, 0x170/x2 0x160/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x100/yf, 0x1e0/x1 0x100/y1, 0x1e0/x2 0x80/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x100/yf, 0x1e0/x1 0x100/y1, 0x1e0/x2 0xe8/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x100/yf, 0x1e0/x1 0x100/y1, 0x1e0/x2 0x160/y2, 7/color, 4/radius + } + # cell 1: conveyors from neighboring inputs to sum node + draw-monotonic-bezier screen, 0x1e0/x0 0x40/y0, 0x220/x1 0xd0/ys, 0x270/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x1e0/x0 0xe8/y0, 0x1e0/x1 0xd0/ys, 0x270/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x1e0/x0 0x1a0/y0, 0x200/x1 0xd0/ys, 0x270/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x290/x0 0x40/y0, 0x270/x1 0x80/y1, 0x270/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x290/x0 0x1a0/y0, 0x270/x1 0x1a0/y1, 0x270/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x340/x0 0x40/y0, 0x270/x1 0x80/y1, 0x270/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x340/x0 0xe8/y0, 0x340/x1 0xd0/y1, 0x270/xs 0xd0/ys, 4/color + draw-monotonic-bezier screen, 0x340/x0 0x1a0/y0, 0x2a0/x1 0x1a0/y1, 0x270/xs 0xd0/ys, 4/color + # cell 1: conveyors from filter to outputs + draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf, 0x280/x1 0x8c/y1, 0x220/x2 0x80/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf, 0x220/x1 0x100/y1, 0x220/x2 0xe8/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf, 0x220/x1 0x100/y1, 0x220/x2 0x160/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf, 0x2a8/x1 0x80/y1, 0x290/x2 0x80/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf, 0x2a8/x1 0x160/y1, 0x290/x2 0x160/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf, 0x300/x1 0x100/y1, 0x300/x2 0x80/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf, 0x300/x1 0x100/y1 0x300/x2 0xe8/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x100/yf, 0x300/x1 0x100/y1, 0x300/x2 0x160/y2, 0x2a/color + # cell 1: time-variant portion: 16 repeating steps + $render1:cell1: { + var tick-a/eax: (addr int) <- get self, tick + var progress/eax: int <- copy *tick-a + progress <- and 0xf + # cell 1: 7 time steps for getting inputs to sum + { + compare progress, 7 + break-if->= + var u/xmm7: float <- convert progress + var six/eax: int <- copy 6 + var six-f/xmm0: float <- convert six + u <- divide six-f + # points on conveyors from neighboring cells + draw-bezier-point screen, u, 0x1e0/x0 0x40/y0, 0x220/x1 0xd0/ys, 0x270/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x1e0/x0 0xe8/y0, 0x1e0/x1 0xd0/ys, 0x270/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x1e0/x0 0x1a0/y0, 0x200/x1 0xd0/ys, 0x270/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x290/x0 0x40/y0, 0x270/x1 0x80/y1, 0x270/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x290/x0 0x1a0/y0, 0x270/x1 0x1a0/y1, 0x270/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x340/x0 0x40/y0, 0x270/x1 0x80/y1, 0x270/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x340/x0 0xe8/y0, 0x340/x1 0xd0/y1, 0x270/xs 0xd0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x340/x0 0x1a0/y0, 0x2a0/x1 0x1a0/y1, 0x270/xs 0xd0/ys, 7/color, 4/radius + break $render1:cell1 + } + # cell 1: two time steps for getting count to filter + progress <- subtract 7 + { + compare progress, 2 + break-if->= + break $render1:cell1 + } + # cell 1: final 7 time steps for updating output + progress <- subtract 2 + # cell 1: points on conveyors to outputs + var u/xmm7: float <- convert progress + var six/eax: int <- copy 6 + var six-f/xmm0: float <- convert six + u <- divide six-f + draw-bezier-point screen, u, 0x2a8/xf 0x100/yf, 0x280/x1 0x8c/y1, 0x220/x2 0x80/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x100/yf, 0x220/x1 0x100/y1, 0x220/x2 0xe8/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x100/yf, 0x220/x1 0x100/y1, 0x220/x2 0x160/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x100/yf, 0x2a8/xf 0x80/y1, 0x290/x2 0x80/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x100/yf, 0x2a8/xf 0x160/y1, 0x290/x2 0x160/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x100/yf, 0x300/x1 0x100/y1, 0x300/x2 0x80/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x100/yf, 0x300/x1 0x100/y1, 0x300/x2 0xe8/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x100/yf, 0x300/x1 0x100/y1, 0x300/x2 0x160/y2, 7/color, 4/radius + } + # cell 2: conveyors from neighboring inputs to sum node + draw-monotonic-bezier screen, 0xc0/x0 0x160/y0, 0x100/x1 0x1f0/ys, 0x150/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0xc0/x0 0x208/y0, 0xc0/x1 0x1f0/ys, 0x150/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0xc0/x0 0x2c0/y0, 0xe0/x1 0x1f0/ys, 0x150/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x170/x0 0x160/y0, 0x150/x1 0x1a0/y1, 0x150/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x170/x0 0x2c0/y0, 0x150/x1 0x2c0/y1, 0x150/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x220/x0 0x160/y0, 0x150/x1 0x1a0/y1, 0x150/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x220/x0 0x208/y0, 0x220/x1 0x1f0/y1, 0x150/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x220/x0 0x2c0/y0, 0x180/x1 0x2c0/y1, 0x150/xs 0x1f0/ys, 4/color + # cell 2: conveyors from filter to outputs + draw-monotonic-bezier screen, 0x188/xf 0x220/yf, 0x160/x1 0x1ac/y1, 0x100/x2 0x1a0/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x220/yf, 0x100/x1 0x220/y1, 0x100/x2 0x208/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x220/yf, 0x100/x1 0x220/y1, 0x100/x2 0x280/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x220/yf, 0x188/x1 0x1a0/y1, 0x170/x2 0x1a0/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x220/yf, 0x188/x1 0x280/y1, 0x170/x2 0x280/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x220/yf, 0x1e0/x1 0x220/y1, 0x1e0/x2 0x1a0/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x220/yf, 0x1e0/x1 0x220/y1 0x1e0/x2 0x208/y2, 0x2a/color + draw-monotonic-bezier screen, 0x188/xf 0x220/yf, 0x1e0/x1 0x220/y1, 0x1e0/x2 0x280/y2, 0x2a/color + # cell 2: time-variant portion: 16 repeating steps + $render1:cell2: { + var tick-a/eax: (addr int) <- get self, tick + var progress/eax: int <- copy *tick-a + progress <- and 0xf + # cell 2: 7 time steps for getting inputs to sum + { + compare progress, 7 + break-if->= + var u/xmm7: float <- convert progress + var six/eax: int <- copy 6 + var six-f/xmm0: float <- convert six + u <- divide six-f + # points on conveyors from neighboring cells + draw-bezier-point screen, u, 0xc0/x0 0x160/y0, 0x100/x1 0x1f0/ys, 0x150/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0xc0/x0 0x208/y0, 0xc0/x1 0x1f0/ys, 0x150/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0xc0/x0 0x2c0/y0, 0xe0/x1 0x1f0/ys, 0x150/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x170/x0 0x160/y0, 0x150/x1 0x1a0/y1, 0x150/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x170/x0 0x2c0/y0, 0x150/x1 0x2c0/y1, 0x150/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x220/x0 0x160/y0, 0x150/x1 0x1a0/y1, 0x150/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x220/x0 0x208/y0, 0x220/x1 0x1f0/y1, 0x150/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x220/x0 0x2c0/y0, 0x180/x1 0x2c0/y1, 0x150/xs 0x1f0/ys, 7/color, 4/radius + break $render1:cell2 + } + # cell 2: two time steps for getting count to filter + progress <- subtract 7 + { + compare progress, 2 + break-if->= + break $render1:cell2 + } + # cell 2: final 7 time steps for updating output + progress <- subtract 2 + # cell 2: points on conveyors to outputs + var u/xmm7: float <- convert progress + var six/eax: int <- copy 6 + var six-f/xmm0: float <- convert six + u <- divide six-f + draw-bezier-point screen, u, 0x188/xf 0x220/yf, 0x160/x1 0x1ac/y1, 0x100/x2 0x1a0/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x220/yf, 0x100/x1 0x220/y1, 0x100/x2 0x208/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x220/yf, 0x100/x1 0x220/y1, 0x100/x2 0x280/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x220/yf, 0x188/xf 0x1a0/y1, 0x170/x2 0x1a0/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x220/yf, 0x188/xf 0x280/y1, 0x170/x2 0x280/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x220/yf, 0x1e0/x1 0x220/y1, 0x1e0/x2 0x1a0/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x220/yf, 0x1e0/x1 0x220/y1, 0x1e0/x2 0x208/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x188/xf 0x220/yf, 0x1e0/x1 0x220/y1, 0x1e0/x2 0x280/y2, 7/color, 4/radius + } + # cell 3: conveyors from neighboring inputs to sum node + draw-monotonic-bezier screen, 0x1e0/x0 0x160/y0, 0x220/x1 0x1f0/ys, 0x270/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x1e0/x0 0x208/y0, 0x1e0/x1 0x1f0/ys, 0x270/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x1e0/x0 0x2c0/y0, 0x200/x1 0x1f0/ys, 0x270/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x290/x0 0x160/y0, 0x270/x1 0x1a0/y1, 0x270/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x290/x0 0x2c0/y0, 0x270/x1 0x2c0/y1, 0x270/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x340/x0 0x160/y0, 0x270/x1 0x1a0/y1, 0x270/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x340/x0 0x208/y0, 0x340/x1 0x1f0/y1, 0x270/xs 0x1f0/ys, 4/color + draw-monotonic-bezier screen, 0x340/x0 0x2c0/y0, 0x2a0/x1 0x2c0/y1, 0x270/xs 0x1f0/ys, 4/color + # cell 3: conveyors from filter to outputs + draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf, 0x280/x1 0x1ac/y1, 0x220/x2 0x1a0/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf, 0x220/x1 0x220/y1, 0x220/x2 0x208/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf, 0x220/x1 0x220/y1, 0x220/x2 0x280/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf, 0x2a8/x1 0x1a0/y1, 0x290/x2 0x1a0/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf, 0x2a8/x1 0x280/y1, 0x290/x2 0x280/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf, 0x300/x1 0x220/y1, 0x300/x2 0x1a0/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf, 0x300/x1 0x220/y1 0x300/x2 0x208/y2, 0x2a/color + draw-monotonic-bezier screen, 0x2a8/xf 0x220/yf, 0x300/x1 0x220/y1, 0x300/x2 0x280/y2, 0x2a/color + # cell 3: time-variant portion: 16 repeating steps + $render1:cell3: { + var tick-a/eax: (addr int) <- get self, tick + var progress/eax: int <- copy *tick-a + progress <- and 0xf + # cell 3: 7 time steps for getting inputs to sum + { + compare progress, 7 + break-if->= + var u/xmm7: float <- convert progress + var six/eax: int <- copy 6 + var six-f/xmm0: float <- convert six + u <- divide six-f + # points on conveyors from neighboring cells + draw-bezier-point screen, u, 0x1e0/x0 0x160/y0, 0x220/x1 0x1f0/ys, 0x270/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x1e0/x0 0x208/y0, 0x1e0/x1 0x1f0/ys, 0x270/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x1e0/x0 0x2c0/y0, 0x200/x1 0x1f0/ys, 0x270/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x290/x0 0x160/y0, 0x270/x1 0x1a0/y1, 0x270/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x290/x0 0x2c0/y0, 0x270/x1 0x2c0/y1, 0x270/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x340/x0 0x160/y0, 0x270/x1 0x1a0/y1, 0x270/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x340/x0 0x208/y0, 0x340/x1 0x1f0/y1, 0x270/xs 0x1f0/ys, 7/color, 4/radius + draw-bezier-point screen, u, 0x340/x0 0x2c0/y0, 0x2a0/x1 0x2c0/y1, 0x270/xs 0x1f0/ys, 7/color, 4/radius + break $render1:cell3 + } + # cell 3: two time steps for getting count to filter + progress <- subtract 7 + { + compare progress, 2 + break-if->= + break $render1:cell3 + } + # cell 3: final 7 time steps for updating output + progress <- subtract 2 + # cell 3: points on conveyors to outputs + var u/xmm7: float <- convert progress + var six/eax: int <- copy 6 + var six-f/xmm0: float <- convert six + u <- divide six-f + draw-bezier-point screen, u, 0x2a8/xf 0x220/yf, 0x280/x1 0x1ac/y1, 0x220/x2 0x1a0/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x220/yf, 0x220/x1 0x220/y1, 0x220/x2 0x208/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x220/yf, 0x220/x1 0x220/y1, 0x220/x2 0x280/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x220/yf, 0x2a8/xf 0x1a0/y1, 0x290/x2 0x1a0/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x220/yf, 0x2a8/xf 0x280/y1, 0x290/x2 0x280/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x220/yf, 0x300/x1 0x220/y1, 0x300/x2 0x1a0/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x220/yf, 0x300/x1 0x220/y1, 0x300/x2 0x208/y2, 7/color, 4/radius + draw-bezier-point screen, u, 0x2a8/xf 0x220/yf, 0x300/x1 0x220/y1, 0x300/x2 0x280/y2, 7/color, 4/radius + } +} + +fn draw-bezier-point screen: (addr screen), u: float, x0: int, y0: int, x1: int, y1: int, x2: int, y2: int, color: int, radius: int { + var _cy/eax: int <- bezier-point u, y0, y1, y2 + var cy/ecx: int <- copy _cy + var cx/eax: int <- bezier-point u, x0, x1, x2 + draw-disc screen, cx, cy, radius, color, 0xf/border-color=white +} + +fn draw-linear-point screen: (addr screen), u: float, x0: int, y0: int, x1: int, y1: int, color: int, radius: int { + var _cy/eax: int <- line-point u, y0, y1 + var cy/ecx: int <- copy _cy + var cx/eax: int <- line-point u, x0, x1 + draw-disc screen, cx, cy, radius, color, 0xf/border-color=white +} + +fn edit keyboard: (addr keyboard), _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var key/eax: byte <- read-key keyboard + # space: play/pause + { + compare key, 0x20/space + break-if-!= + var play?/eax: (addr boolean) <- get self, play? + compare *play?, 0/false + { + break-if-= + copy-to *play?, 0/false + return + } + copy-to *play?, 1/true + return + } + # 0: back to start + { + compare key, 0x30/0 + break-if-!= + clear-environment self + return + } + # l: loop from here to start + { + compare key, 0x6c/l + break-if-!= + var tick-a/eax: (addr int) <- get self, tick + var tick/eax: int <- copy *tick-a + var loop/ecx: (addr int) <- get self, loop + copy-to *loop, tick + return + } + # L: reset loop + { + compare key, 0x4c/L + break-if-!= + var loop/eax: (addr int) <- get self, loop + copy-to *loop, 0 + return + } + # -: zoom out + { + compare key, 0x2d/- + break-if-!= + var zoom/eax: (addr int) <- get self, zoom + compare *zoom, 1 + { + break-if-!= + copy-to *zoom, 4 + } + compare *zoom, 0 + { + break-if-!= + copy-to *zoom, 1 + } + # set tick to a multiple of zoom + var tick-a/edx: (addr int) <- get self, tick + clear-lowest-bits tick-a, *zoom + return + } + # +: zoom in + { + compare key, 0x2b/+ + break-if-!= + var zoom/eax: (addr int) <- get self, zoom + compare *zoom, 1 + { + break-if-!= + copy-to *zoom, 0 + } + compare *zoom, 4 + { + break-if-!= + copy-to *zoom, 1 + } + # set tick to a multiple of zoom + var tick-a/edx: (addr int) <- get self, tick + clear-lowest-bits tick-a, *zoom + return + } +} + +fn step _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var tick-a/ecx: (addr int) <- get self, tick + var zoom/edx: (addr int) <- get self, zoom + compare *zoom, 0 + { + break-if-!= + increment *tick-a + } + compare *zoom, 1 + { + break-if-!= + # I wanted to speed up time, but that doesn't seem very usable. +#? add-to *tick-a, 2 + increment *tick-a + } + compare *zoom, 4 + { + break-if-!= + add-to *tick-a, 0x10 + } + var tick/eax: int <- copy *tick-a + tick <- and 0xf + compare tick, 0 + { + break-if-!= + step4 self + } + var loop-a/eax: (addr int) <- get self, loop + compare *loop-a, 0 + { + break-if-= + var loop/eax: int <- copy *loop-a + compare *tick-a, loop + break-if-< + clear-environment self + } +} + +fn initialize-environment _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var zoom/eax: (addr int) <- get self, zoom + copy-to *zoom, 0 + var play?/eax: (addr boolean) <- get self, play? + copy-to *play?, 1/true + var data-ah/eax: (addr handle array handle array cell) <- get self, data + populate data-ah, 0x100 + var data/eax: (addr array handle array cell) <- lookup *data-ah + var y/ecx: int <- copy 0 + { + compare y, 0xc0 + break-if->= + var dest-ah/eax: (addr handle array cell) <- index data, y + populate dest-ah, 0x100 + y <- increment + loop + } + set self, 0x80, 0x5f, 1/alive + set self, 0x81, 0x5f, 1/alive + set self, 0x7f, 0x60, 1/alive + set self, 0x80, 0x60, 1/alive + set self, 0x80, 0x61, 1/alive + flush self +} + +fn clear-environment _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var tick/eax: (addr int) <- get self, tick + copy-to *tick, 0 + # don't touch zoom or play settings + var data-ah/eax: (addr handle array handle array cell) <- get self, data + var data/eax: (addr array handle array cell) <- lookup *data-ah + var y/ecx: int <- copy 0 + { + compare y, 0xc0 + break-if->= + var row-ah/eax: (addr handle array cell) <- index data, y + var row/eax: (addr array cell) <- lookup *row-ah + var x/edx: int <- copy 0 + { + compare x, 0x100 + break-if->= + var dest/eax: (addr cell) <- index row, x + clear-object dest + x <- increment + loop + } + y <- increment + loop + } + set self, 0x80, 0x5f, 1/alive + set self, 0x81, 0x5f, 1/alive + set self, 0x7f, 0x60, 1/alive + set self, 0x80, 0x60, 1/alive + set self, 0x80, 0x61, 1/alive + flush self +} + +fn set _self: (addr environment), _x: int, _y: int, _val: boolean { + var self/esi: (addr environment) <- copy _self + var data-ah/eax: (addr handle array handle array cell) <- get self, data + var data/eax: (addr array handle array cell) <- lookup *data-ah + var y/ecx: int <- copy _y + var row-ah/eax: (addr handle array cell) <- index data, y + var row/eax: (addr array cell) <- lookup *row-ah + var x/ecx: int <- copy _x + var cell/eax: (addr cell) <- index row, x + var dest/eax: (addr boolean) <- get cell, next + var val/ecx: boolean <- copy _val + copy-to *dest, val +} + +fn state _self: (addr environment), _x: int, _y: int -> _/eax: boolean { + var self/esi: (addr environment) <- copy _self + var x/ecx: int <- copy _x + var y/edx: int <- copy _y + # clip at the edge + compare x, 0 + { + break-if->= + return 0/false + } + compare y, 0 + { + break-if->= + return 0/false + } + compare x, 0x100/width + { + break-if-< + return 0/false + } + compare y, 0xc0/height + { + break-if-< + return 0/false + } + var data-ah/eax: (addr handle array handle array cell) <- get self, data + var data/eax: (addr array handle array cell) <- lookup *data-ah + var row-ah/eax: (addr handle array cell) <- index data, y + var row/eax: (addr array cell) <- lookup *row-ah + var cell/eax: (addr cell) <- index row, x + var src/eax: (addr boolean) <- get cell, curr + return *src +} + +fn state-color _self: (addr environment), x: int, y: int -> _/eax: int { + var self/esi: (addr environment) <- copy _self + var color/ecx: int <- copy 0x1a/dead + { + var state/eax: boolean <- state self, x, y + compare state, 0/dead + break-if-= + color <- copy 0xf/alive + } + return color +} + +fn flush _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var data-ah/eax: (addr handle array handle array cell) <- get self, data + var _data/eax: (addr array handle array cell) <- lookup *data-ah + var data/esi: (addr array handle array cell) <- copy _data + var y/ecx: int <- copy 0 + { + compare y, 0xc0/height + break-if->= + var row-ah/eax: (addr handle array cell) <- index data, y + var _row/eax: (addr array cell) <- lookup *row-ah + var row/ebx: (addr array cell) <- copy _row + var x/edx: int <- copy 0 + { + compare x, 0x100/width + break-if->= + var cell-a/eax: (addr cell) <- index row, x + var curr-a/edi: (addr boolean) <- get cell-a, curr + var next-a/esi: (addr boolean) <- get cell-a, next + var val/eax: boolean <- copy *next-a + copy-to *curr-a, val + copy-to *next-a, 0/dead + x <- increment + loop + } + y <- increment + loop + } +} + +fn render4 screen: (addr screen), _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var y/ecx: int <- copy 0 + { + compare y, 0xc0/height + break-if->= + var x/edx: int <- copy 0 + { + compare x, 0x100/width + break-if->= + var state/eax: boolean <- state self, x, y + compare state, 0/false + { + break-if-= + render4-cell screen, x, y, 0xf/alive + } + compare state, 0/false + { + break-if-!= + render4-cell screen, x, y, 0x1a/dead + } + x <- increment + loop + } + y <- increment + loop + } +} + +fn render4-cell screen: (addr screen), x: int, y: int, color: int { + var xmin/eax: int <- copy x + xmin <- shift-left 2 + var xmax/ecx: int <- copy xmin + xmax <- add 4 + var ymin/edx: int <- copy y + ymin <- shift-left 2 + var ymax/ebx: int <- copy ymin + ymax <- add 4 + draw-rect screen, xmin ymin, xmax ymax, color +} + +fn step4 _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var y/ecx: int <- copy 0 + { + compare y, 0xc0/height + break-if->= + var x/edx: int <- copy 0 + { + compare x, 0x100/width + break-if->= + var n/eax: int <- num-live-neighbors self, x, y + # if neighbors < 2, die of loneliness + { + compare n, 2 + break-if->= + set self, x, y, 0/dead + } + # if neighbors > 3, die of overcrowding + { + compare n, 3 + break-if-<= + set self, x, y, 0/dead + } + # if neighbors = 2, preserve state + { + compare n, 2 + break-if-!= + var old-state/eax: boolean <- state self, x, y + set self, x, y, old-state + } + # if neighbors = 3, cell quickens to life + { + compare n, 3 + break-if-!= + set self, x, y, 1/live + } + x <- increment + loop + } + y <- increment + loop + } + flush self +} + +fn num-live-neighbors _self: (addr environment), x: int, y: int -> _/eax: int { + var self/esi: (addr environment) <- copy _self + var result/edi: int <- copy 0 + # row above: zig + decrement y + decrement x + var s/eax: boolean <- state self, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + increment x + s <- state self, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + increment x + s <- state self, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + # curr row: zag + increment y + s <- state self, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + subtract-from x, 2 + s <- state self, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + # row below: zig + increment y + s <- state self, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + increment x + s <- state self, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + increment x + s <- state self, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + return result +} + +fn linger _self: (addr environment) { + var self/esi: (addr environment) <- copy _self + var i/ecx: int <- copy 0 + { + compare i, 0x10000000 # Kartik's Linux with -enable-kvm +#? compare i, 0x8000000 # Kartik's Mac with -accel tcg + break-if->= + i <- increment + loop + } +} diff --git a/apps/img.mu b/apps/img.mu new file mode 100644 index 00000000..65b773c5 --- /dev/null +++ b/apps/img.mu @@ -0,0 +1,1148 @@ +# load an image from disk and display it on screen +# +# To build: +# $ ./translate apps/img.mu # generates code.img +# Load a pbm, pgm or ppm image (no more than 255 levels) in the data disk +# $ dd if=/dev/zero of=data.img count=20160 +# $ dd if=x.pbm of=data.img conv=notrunc +# or +# $ dd if=t.pgm of=data.img conv=notrunc +# or +# $ dd if=snail.ppm of=data.img conv=notrunc +# To run: +# $ qemu-system-i386 -hda code.img -hdb data.img + +type image { + type: int # supported types: + # 1: portable bitmap (P1) - pixels 0 or 1 + # 2: portable greymap (P2) - pixels 1-byte greyscale values + # 3: portable pixmap (P3) - pixels 3-byte rgb values + max: int + width: int + height: int + data: (handle array byte) +} + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var img-storage: image + var img/esi: (addr image) <- address img-storage + load-image img, data-disk + render-image screen, img, 0/x, 0/y, 0x300/width, 0x300/height +} + +fn load-image self: (addr image), data-disk: (addr disk) { + # data-disk -> stream + var s-storage: (stream byte 0x200000) # 512* 0x1000 sectors + var s/ebx: (addr stream byte) <- address s-storage + draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "loading sectors from data disk", 3/fg, 0/bg + move-cursor-to-left-margin-of-next-line 0/screen + load-sectors data-disk, 0/lba, 0x100/sectors, s + load-sectors data-disk, 0x100/lba, 0x100/sectors, s + load-sectors data-disk, 0x200/lba, 0x100/sectors, s + load-sectors data-disk, 0x300/lba, 0x100/sectors, s + load-sectors data-disk, 0x400/lba, 0x100/sectors, s + load-sectors data-disk, 0x500/lba, 0x100/sectors, s + load-sectors data-disk, 0x600/lba, 0x100/sectors, s + load-sectors data-disk, 0x700/lba, 0x100/sectors, s + load-sectors data-disk, 0x800/lba, 0x100/sectors, s + load-sectors data-disk, 0x900/lba, 0x100/sectors, s + load-sectors data-disk, 0xa00/lba, 0x100/sectors, s + load-sectors data-disk, 0xb00/lba, 0x100/sectors, s + load-sectors data-disk, 0xc00/lba, 0x100/sectors, s + load-sectors data-disk, 0xd00/lba, 0x100/sectors, s + load-sectors data-disk, 0xe00/lba, 0x100/sectors, s + load-sectors data-disk, 0xf00/lba, 0x100/sectors, s + draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "parsing", 3/fg, 0/bg + move-cursor-to-left-margin-of-next-line 0/screen + initialize-image self, s +} + +fn initialize-image _self: (addr image), in: (addr stream byte) { + var self/esi: (addr image) <- copy _self + var mode-storage: slice + var mode/ecx: (addr slice) <- address mode-storage + next-word in, mode + { + var P1?/eax: boolean <- slice-equal? mode, "P1" + compare P1?, 0/false + break-if-= + var type-a/eax: (addr int) <- get self, type + copy-to *type-a, 1/ppm + initialize-image-from-pbm self, in + return + } + { + var P2?/eax: boolean <- slice-equal? mode, "P2" + compare P2?, 0/false + break-if-= + var type-a/eax: (addr int) <- get self, type + copy-to *type-a, 2/pgm + initialize-image-from-pgm self, in + return + } + { + var P3?/eax: boolean <- slice-equal? mode, "P3" + compare P3?, 0/false + break-if-= + var type-a/eax: (addr int) <- get self, type + copy-to *type-a, 3/ppm + initialize-image-from-ppm self, in + return + } + abort "initialize-image: unrecognized image type" +} + +# dispatch to a few variants with mostly identical boilerplate +fn render-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int { + var img/esi: (addr image) <- copy _img + var type-a/eax: (addr int) <- get img, type + { + compare *type-a, 1/pbm + break-if-!= + render-pbm-image screen, img, xmin, ymin, width, height + return + } + { + compare *type-a, 2/pgm + break-if-!= + var img2-storage: image + var img2/edi: (addr image) <- address img2-storage + dither-pgm-unordered img, img2 + render-raw-image screen, img2, xmin, ymin, width, height + return + } + { + compare *type-a, 3/ppm + break-if-!= + var img2-storage: image + var img2/edi: (addr image) <- address img2-storage + dither-ppm-unordered img, img2 + render-raw-image screen, img2, xmin, ymin, width, height + return + } + abort "render-image: unrecognized image type" +} + +## helpers + +# import a black-and-white ascii bitmap (each pixel is 0 or 1) +fn initialize-image-from-pbm _self: (addr image), in: (addr stream byte) { + var self/esi: (addr image) <- copy _self + var curr-word-storage: slice + var curr-word/ecx: (addr slice) <- address curr-word-storage + # load width, height + next-word in, curr-word + var tmp/eax: int <- parse-decimal-int-from-slice curr-word + var width/edx: int <- copy tmp + next-word in, curr-word + tmp <- parse-decimal-int-from-slice curr-word + var height/ebx: int <- copy tmp + # save width, height + var dest/eax: (addr int) <- get self, width + copy-to *dest, width + dest <- get self, height + copy-to *dest, height + # initialize data + var capacity/edx: int <- copy width + capacity <- multiply height + var data-ah/edi: (addr handle array byte) <- get self, data + populate data-ah, capacity + var _data/eax: (addr array byte) <- lookup *data-ah + var data/edi: (addr array byte) <- copy _data + var i/ebx: int <- copy 0 + { + compare i, capacity + break-if->= + next-word in, curr-word + var src/eax: int <- parse-decimal-int-from-slice curr-word + { + var dest/ecx: (addr byte) <- index data, i + copy-byte-to *dest, src + } + i <- increment + loop + } +} + +# render a black-and-white ascii bitmap (each pixel is 0 or 1) +fn render-pbm-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int { + var img/esi: (addr image) <- copy _img + # yratio = height/img->height + var img-height-a/eax: (addr int) <- get img, height + var img-height/xmm0: float <- convert *img-height-a + var yratio/xmm1: float <- convert height + yratio <- divide img-height + # xratio = width/img->width + var img-width-a/eax: (addr int) <- get img, width + var img-width/ebx: int <- copy *img-width-a + var img-width-f/xmm0: float <- convert img-width + var xratio/xmm2: float <- convert width + xratio <- divide img-width-f + # esi = img->data + var img-data-ah/eax: (addr handle array byte) <- get img, data + var _img-data/eax: (addr array byte) <- lookup *img-data-ah + var img-data/esi: (addr array byte) <- copy _img-data + var len/edi: int <- length img-data + # + var one/eax: int <- copy 1 + var one-f/xmm3: float <- convert one + var width-f/xmm4: float <- convert width + var height-f/xmm5: float <- convert height + var zero/eax: int <- copy 0 + var zero-f/xmm0: float <- convert zero + var y/xmm6: float <- copy zero-f + { + compare y, height-f + break-if-float>= + var imgy-f/xmm5: float <- copy y + imgy-f <- divide yratio + var imgy/edx: int <- truncate imgy-f + var x/xmm7: float <- copy zero-f + { + compare x, width-f + break-if-float>= + var imgx-f/xmm5: float <- copy x + imgx-f <- divide xratio + var imgx/ecx: int <- truncate imgx-f + var idx/eax: int <- copy imgy + idx <- multiply img-width + idx <- add imgx + # error info in case we rounded wrong and 'index' will fail bounds-check + compare idx, len + { + break-if-< + set-cursor-position 0/screen, 0x20/x 0x20/y + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg + } + var src-a/eax: (addr byte) <- index img-data, idx + var src/eax: byte <- copy-byte *src-a + var color-int/eax: int <- copy src + { + compare color-int, 0/black + break-if-= + color-int <- copy 0xf/white + } + var screenx/ecx: int <- convert x + screenx <- add xmin + var screeny/edx: int <- convert y + screeny <- add ymin + pixel screen, screenx, screeny, color-int + x <- add one-f + loop + } + y <- add one-f + loop + } +} + +# import a greyscale ascii "greymap" (each pixel is a shade of grey from 0 to 255) +fn initialize-image-from-pgm _self: (addr image), in: (addr stream byte) { + var self/esi: (addr image) <- copy _self + var curr-word-storage: slice + var curr-word/ecx: (addr slice) <- address curr-word-storage + # load width, height + next-word in, curr-word + var tmp/eax: int <- parse-decimal-int-from-slice curr-word + var width/edx: int <- copy tmp + next-word in, curr-word + tmp <- parse-decimal-int-from-slice curr-word + var height/ebx: int <- copy tmp + # check and save color levels + next-word in, curr-word + { + tmp <- parse-decimal-int-from-slice curr-word + compare tmp, 0xff + break-if-= + draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "levels of grey is not 255; continuing and hoping for the best", 0x2b/fg 0/bg + } + var dest/edi: (addr int) <- get self, max + copy-to *dest, tmp + # save width, height + dest <- get self, width + copy-to *dest, width + dest <- get self, height + copy-to *dest, height + # initialize data + var capacity/edx: int <- copy width + capacity <- multiply height + var data-ah/edi: (addr handle array byte) <- get self, data + populate data-ah, capacity + var _data/eax: (addr array byte) <- lookup *data-ah + var data/edi: (addr array byte) <- copy _data + var i/ebx: int <- copy 0 + { + compare i, capacity + break-if->= + next-word in, curr-word + var src/eax: int <- parse-decimal-int-from-slice curr-word + { + var dest/ecx: (addr byte) <- index data, i + copy-byte-to *dest, src + } + i <- increment + loop + } +} + +# render a greyscale ascii "greymap" (each pixel is a shade of grey from 0 to 255) by quantizing the shades +fn render-pgm-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int { + var img/esi: (addr image) <- copy _img + # yratio = height/img->height + var img-height-a/eax: (addr int) <- get img, height + var img-height/xmm0: float <- convert *img-height-a + var yratio/xmm1: float <- convert height + yratio <- divide img-height + # xratio = width/img->width + var img-width-a/eax: (addr int) <- get img, width + var img-width/ebx: int <- copy *img-width-a + var img-width-f/xmm0: float <- convert img-width + var xratio/xmm2: float <- convert width + xratio <- divide img-width-f + # esi = img->data + var img-data-ah/eax: (addr handle array byte) <- get img, data + var _img-data/eax: (addr array byte) <- lookup *img-data-ah + var img-data/esi: (addr array byte) <- copy _img-data + var len/edi: int <- length img-data + # + var one/eax: int <- copy 1 + var one-f/xmm3: float <- convert one + var width-f/xmm4: float <- convert width + var height-f/xmm5: float <- convert height + var zero/eax: int <- copy 0 + var zero-f/xmm0: float <- convert zero + var y/xmm6: float <- copy zero-f + { + compare y, height-f + break-if-float>= + var imgy-f/xmm5: float <- copy y + imgy-f <- divide yratio + var imgy/edx: int <- truncate imgy-f + var x/xmm7: float <- copy zero-f + { + compare x, width-f + break-if-float>= + var imgx-f/xmm5: float <- copy x + imgx-f <- divide xratio + var imgx/ecx: int <- truncate imgx-f + var idx/eax: int <- copy imgy + idx <- multiply img-width + idx <- add imgx + # error info in case we rounded wrong and 'index' will fail bounds-check + compare idx, len + { + break-if-< + set-cursor-position 0/screen, 0x20/x 0x20/y + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg + } + var src-a/eax: (addr byte) <- index img-data, idx + var src/eax: byte <- copy-byte *src-a + var color-int/eax: int <- nearest-grey src + var screenx/ecx: int <- convert x + screenx <- add xmin + var screeny/edx: int <- convert y + screeny <- add ymin + pixel screen, screenx, screeny, color-int + x <- add one-f + loop + } + y <- add one-f + loop + } +} + +fn nearest-grey level-255: byte -> _/eax: int { + var result/eax: int <- copy level-255 + result <- shift-right 4 + result <- add 0x10 + return result +} + +fn dither-pgm-unordered-monochrome _src: (addr image), _dest: (addr image) { + var src/esi: (addr image) <- copy _src + var dest/edi: (addr image) <- copy _dest + # copy 'width' + var src-width-a/eax: (addr int) <- get src, width + var tmp/eax: int <- copy *src-width-a + var src-width: int + copy-to src-width, tmp + { + var dest-width-a/edx: (addr int) <- get dest, width + copy-to *dest-width-a, tmp + } + # copy 'height' + var src-height-a/eax: (addr int) <- get src, height + var tmp/eax: int <- copy *src-height-a + var src-height: int + copy-to src-height, tmp + { + var dest-height-a/ecx: (addr int) <- get dest, height + copy-to *dest-height-a, tmp + } + # transform 'data' + var capacity/ebx: int <- copy src-width + capacity <- multiply src-height + var dest/edi: (addr image) <- copy _dest + var dest-data-ah/eax: (addr handle array byte) <- get dest, data + populate dest-data-ah, capacity + var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah + var dest-data/edi: (addr array byte) <- copy _dest-data + # needs a buffer to temporarily hold more than 256 levels of precision + var errors-storage: (array int 0xc0000) + var errors/ebx: (addr array int) <- address errors-storage + var src-data-ah/eax: (addr handle array byte) <- get src, data + var _src-data/eax: (addr array byte) <- lookup *src-data-ah + var src-data/esi: (addr array byte) <- copy _src-data + var y/edx: int <- copy 0 + { + compare y, src-height + break-if->= + var x/ecx: int <- copy 0 + { + compare x, src-width + break-if->= + var curr/eax: byte <- _read-pgm-buffer src-data, x, y, src-width + var curr-int/eax: int <- copy curr + curr-int <- shift-left 0x10 # we have 32 bits; we'll use 16 bits for the fraction and leave 8 for unanticipated overflow + var error/esi: int <- _read-dithering-error errors, x, y, src-width + error <- add curr-int + $_dither-pgm-unordered-monochrome:update-error: { + compare error, 0x800000 + { + break-if->= + _write-raw-buffer dest-data, x, y, src-width, 0/black + break $_dither-pgm-unordered-monochrome:update-error + } + _write-raw-buffer dest-data, x, y, src-width, 1/white + error <- subtract 0xff0000 + } + _diffuse-dithering-error-floyd-steinberg errors, x, y, src-width, src-height, error + x <- increment + loop + } + move-cursor-to-left-margin-of-next-line 0/screen + y <- increment + loop + } +} + +fn dither-pgm-unordered _src: (addr image), _dest: (addr image) { + var src/esi: (addr image) <- copy _src + var dest/edi: (addr image) <- copy _dest + # copy 'width' + var src-width-a/eax: (addr int) <- get src, width + var tmp/eax: int <- copy *src-width-a + var src-width: int + copy-to src-width, tmp + { + var dest-width-a/edx: (addr int) <- get dest, width + copy-to *dest-width-a, tmp + } + # copy 'height' + var src-height-a/eax: (addr int) <- get src, height + var tmp/eax: int <- copy *src-height-a + var src-height: int + copy-to src-height, tmp + { + var dest-height-a/ecx: (addr int) <- get dest, height + copy-to *dest-height-a, tmp + } + # compute scaling factor 255/max + var target-scale/eax: int <- copy 0xff + var scale-f/xmm7: float <- convert target-scale + var src-max-a/eax: (addr int) <- get src, max + var tmp-f/xmm0: float <- convert *src-max-a + scale-f <- divide tmp-f + # transform 'data' + var capacity/ebx: int <- copy src-width + capacity <- multiply src-height + var dest/edi: (addr image) <- copy _dest + var dest-data-ah/eax: (addr handle array byte) <- get dest, data + populate dest-data-ah, capacity + var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah + var dest-data/edi: (addr array byte) <- copy _dest-data + # needs a buffer to temporarily hold more than 256 levels of precision + var errors-storage: (array int 0xc0000) + var errors/ebx: (addr array int) <- address errors-storage + var src-data-ah/eax: (addr handle array byte) <- get src, data + var _src-data/eax: (addr array byte) <- lookup *src-data-ah + var src-data/esi: (addr array byte) <- copy _src-data + var y/edx: int <- copy 0 + { + compare y, src-height + break-if->= + var x/ecx: int <- copy 0 + { + compare x, src-width + break-if->= + var initial-color/eax: byte <- _read-pgm-buffer src-data, x, y, src-width + # . scale to 255 levels + var initial-color-int/eax: int <- copy initial-color + var initial-color-f/xmm0: float <- convert initial-color-int + initial-color-f <- multiply scale-f + initial-color-int <- convert initial-color-f + var error/esi: int <- _read-dithering-error errors, x, y, src-width + # error += (initial-color << 16) + { + var tmp/eax: int <- copy initial-color-int + tmp <- shift-left 0x10 # we have 32 bits; we'll use 16 bits for the fraction and leave 8 for unanticipated overflow + error <- add tmp + } + # nearest-color = nearest(error >> 16) + var nearest-color/eax: int <- copy error + nearest-color <- shift-right-signed 0x10 + { + compare nearest-color, 0 + break-if->= + nearest-color <- copy 0 + } + { + compare nearest-color, 0xf0 + break-if-<= + nearest-color <- copy 0xf0 + } + # . truncate last 4 bits + nearest-color <- and 0xf0 + # error -= (nearest-color << 16) + { + var tmp/eax: int <- copy nearest-color + tmp <- shift-left 0x10 + error <- subtract tmp + } + # color-index = (nearest-color >> 4 + 16) + var color-index/eax: int <- copy nearest-color + color-index <- shift-right 4 + color-index <- add 0x10 + var color-index-byte/eax: byte <- copy-byte color-index + _write-raw-buffer dest-data, x, y, src-width, color-index-byte + _diffuse-dithering-error-floyd-steinberg errors, x, y, src-width, src-height, error + x <- increment + loop + } + y <- increment + loop + } +} + +# Use Floyd-Steinberg algorithm for diffusing error at x, y in a 2D grid of +# dimensions (width, height) +# +# https://tannerhelland.com/2012/12/28/dithering-eleven-algorithms-source-code.html +# +# Error is currently a fixed-point number with 16-bit fraction. But +# interestingly this function doesn't care about that. +fn _diffuse-dithering-error-floyd-steinberg errors: (addr array int), x: int, y: int, width: int, height: int, error: int { + { + compare error, 0 + break-if-!= + return + } + var width-1/esi: int <- copy width + width-1 <- decrement + var height-1/edi: int <- copy height + height-1 <- decrement + # delta = error/16 +#? show-errors errors, width, height, x, y + var delta/ecx: int <- copy error + delta <- shift-right-signed 4 + # In Floyd-Steinberg, each pixel X transmits its errors to surrounding + # pixels in the following proportion: + # X 7/16 + # 3/16 5/16 1/16 + var x/edx: int <- copy x + { + compare x, width-1 + break-if->= + var tmp/eax: int <- copy 7 + tmp <- multiply delta + var xright/edx: int <- copy x + xright <- increment + _accumulate-dithering-error errors, xright, y, width, tmp + } + var y/ebx: int <- copy y + { + compare y, height-1 + break-if-< + return + } + var ybelow: int + copy-to ybelow, y + increment ybelow + { + compare x, 0 + break-if-<= + var tmp/eax: int <- copy 3 + tmp <- multiply delta + var xleft/edx: int <- copy x + xleft <- decrement + _accumulate-dithering-error errors, xleft, ybelow, width, tmp + } + { + var tmp/eax: int <- copy 5 + tmp <- multiply delta + _accumulate-dithering-error errors, x, ybelow, width, tmp + } + { + compare x, width-1 + break-if->= + var xright/edx: int <- copy x + xright <- increment + _accumulate-dithering-error errors, xright, ybelow, width, delta + } +#? show-errors errors, width, height, x, y +} + +fn _accumulate-dithering-error errors: (addr array int), x: int, y: int, width: int, error: int { + var curr/esi: int <- _read-dithering-error errors, x, y, width + curr <- add error + _write-dithering-error errors, x, y, width, curr +} + +fn _read-dithering-error _errors: (addr array int), x: int, y: int, width: int -> _/esi: int { + var errors/esi: (addr array int) <- copy _errors + var idx/ecx: int <- copy y + idx <- multiply width + idx <- add x + var result-a/eax: (addr int) <- index errors, idx + return *result-a +} + +fn _write-dithering-error _errors: (addr array int), x: int, y: int, width: int, val: int { + var errors/esi: (addr array int) <- copy _errors + var idx/ecx: int <- copy y + idx <- multiply width + idx <- add x +#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 7/fg 0/bg +#? move-cursor-to-left-margin-of-next-line 0/screen + var src/eax: int <- copy val + var dest-a/edi: (addr int) <- index errors, idx + copy-to *dest-a, src +} + +fn _read-pgm-buffer _buf: (addr array byte), x: int, y: int, width: int -> _/eax: byte { + var buf/esi: (addr array byte) <- copy _buf + var idx/ecx: int <- copy y + idx <- multiply width + idx <- add x + var result-a/eax: (addr byte) <- index buf, idx + var result/eax: byte <- copy-byte *result-a + return result +} + +fn _write-raw-buffer _buf: (addr array byte), x: int, y: int, width: int, val: byte { + var buf/esi: (addr array byte) <- copy _buf + var idx/ecx: int <- copy y + idx <- multiply width + idx <- add x + var src/eax: byte <- copy val + var dest-a/edi: (addr byte) <- index buf, idx + copy-byte-to *dest-a, src +} + +# some debugging helpers +fn show-errors errors: (addr array int), width: int, height: int, x: int, y: int { + compare y, 1 + { + break-if-= + return + } + compare x, 0 + { + break-if-= + return + } + var y/edx: int <- copy 0 + { + compare y, height + break-if->= + var x/ecx: int <- copy 0 + { + compare x, width + break-if->= + var error/esi: int <- _read-dithering-error errors, x, y, width + psd "e", error, 5/fg, x, y + x <- increment + loop + } + move-cursor-to-left-margin-of-next-line 0/screen + y <- increment + loop + } +} + +fn psd s: (addr array byte), d: int, fg: int, x: int, y: int { + { + compare y, 0x18 + break-if->= + return + } + { + compare y, 0x1c + break-if-<= + return + } + { + compare x, 0x40 + break-if->= + return + } +#? { +#? compare x, 0x48 +#? break-if-<= +#? return +#? } + draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, s, 7/fg 0/bg + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, d, fg 0/bg +} + +fn psx s: (addr array byte), d: int, fg: int, x: int, y: int { +#? { +#? compare y, 0x60 +#? break-if->= +#? return +#? } +#? { +#? compare y, 0x6c +#? break-if-<= +#? return +#? } + { + compare x, 0x20 + break-if->= + return + } +#? { +#? compare x, 0x6c +#? break-if-<= +#? return +#? } + draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, s, 7/fg 0/bg + draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, d, fg 0/bg +} + +# import a color ascii "pixmap" (each pixel consists of 3 shades of r/g/b from 0 to 255) +fn initialize-image-from-ppm _self: (addr image), in: (addr stream byte) { + var self/esi: (addr image) <- copy _self + var curr-word-storage: slice + var curr-word/ecx: (addr slice) <- address curr-word-storage + # load width, height + next-word in, curr-word + var tmp/eax: int <- parse-decimal-int-from-slice curr-word + var width/edx: int <- copy tmp + next-word in, curr-word + tmp <- parse-decimal-int-from-slice curr-word + var height/ebx: int <- copy tmp + next-word in, curr-word + # check color levels + { + tmp <- parse-decimal-int-from-slice curr-word + compare tmp, 0xff + break-if-= + abort "initialize-image-from-ppm: supports exactly 255 levels per rgb channel" + } + var dest/edi: (addr int) <- get self, max + copy-to *dest, tmp + # save width, height + dest <- get self, width + copy-to *dest, width + dest <- get self, height + copy-to *dest, height + # initialize data + var capacity/edx: int <- copy width + capacity <- multiply height + # . multiply by 3 for the r/g/b channels + var tmp/eax: int <- copy capacity + tmp <- shift-left 1 + capacity <- add tmp + # + var data-ah/edi: (addr handle array byte) <- get self, data + populate data-ah, capacity + var _data/eax: (addr array byte) <- lookup *data-ah + var data/edi: (addr array byte) <- copy _data + var i/ebx: int <- copy 0 + { + compare i, capacity + break-if->= + next-word in, curr-word + var src/eax: int <- parse-decimal-int-from-slice curr-word + { + var dest/ecx: (addr byte) <- index data, i + copy-byte-to *dest, src + } + i <- increment + loop + } +} + +# import a color ascii "pixmap" (each pixel consists of 3 shades of r/g/b from 0 to 255) +fn render-ppm-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int { + var img/esi: (addr image) <- copy _img + # yratio = height/img->height + var img-height-a/eax: (addr int) <- get img, height + var img-height/xmm0: float <- convert *img-height-a + var yratio/xmm1: float <- convert height + yratio <- divide img-height + # xratio = width/img->width + var img-width-a/eax: (addr int) <- get img, width + var img-width/ebx: int <- copy *img-width-a + var img-width-f/xmm0: float <- convert img-width + var xratio/xmm2: float <- convert width + xratio <- divide img-width-f + # esi = img->data + var img-data-ah/eax: (addr handle array byte) <- get img, data + var _img-data/eax: (addr array byte) <- lookup *img-data-ah + var img-data/esi: (addr array byte) <- copy _img-data + var len/edi: int <- length img-data + # + var one/eax: int <- copy 1 + var one-f/xmm3: float <- convert one + var width-f/xmm4: float <- convert width + var height-f/xmm5: float <- convert height + var zero/eax: int <- copy 0 + var zero-f/xmm0: float <- convert zero + var y/xmm6: float <- copy zero-f + { + compare y, height-f + break-if-float>= + var imgy-f/xmm5: float <- copy y + imgy-f <- divide yratio + var imgy/edx: int <- truncate imgy-f + var x/xmm7: float <- copy zero-f + { + compare x, width-f + break-if-float>= + var imgx-f/xmm5: float <- copy x + imgx-f <- divide xratio + var imgx/ecx: int <- truncate imgx-f + var idx/eax: int <- copy imgy + idx <- multiply img-width + idx <- add imgx + # . multiply by 3 for the r/g/b channels + { + var tmp/ecx: int <- copy idx + tmp <- shift-left 1 + idx <- add tmp + } + # error info in case we rounded wrong and 'index' will fail bounds-check + compare idx, len + { + break-if-< + set-cursor-position 0/screen, 0x20/x 0x20/y + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg + } + # r channel + var r: int + { + var src-a/eax: (addr byte) <- index img-data, idx + var src/eax: byte <- copy-byte *src-a + copy-to r, src + } + idx <- increment + # g channel + var g: int + { + var src-a/eax: (addr byte) <- index img-data, idx + var src/eax: byte <- copy-byte *src-a + copy-to g, src + } + idx <- increment + # b channel + var b: int + { + var src-a/eax: (addr byte) <- index img-data, idx + var src/eax: byte <- copy-byte *src-a + copy-to b, src + } + idx <- increment + # plot nearest color + var color/eax: int <- nearest-color-euclidean r, g, b + var screenx/ecx: int <- convert x + screenx <- add xmin + var screeny/edx: int <- convert y + screeny <- add ymin + pixel screen, screenx, screeny, color + x <- add one-f + loop + } + y <- add one-f + loop + } +} + +fn dither-ppm-unordered _src: (addr image), _dest: (addr image) { + var src/esi: (addr image) <- copy _src + var dest/edi: (addr image) <- copy _dest + # copy 'width' + var src-width-a/eax: (addr int) <- get src, width + var tmp/eax: int <- copy *src-width-a + var src-width: int + copy-to src-width, tmp + { + var dest-width-a/edx: (addr int) <- get dest, width + copy-to *dest-width-a, tmp + } + # copy 'height' + var src-height-a/eax: (addr int) <- get src, height + var tmp/eax: int <- copy *src-height-a + var src-height: int + copy-to src-height, tmp + { + var dest-height-a/ecx: (addr int) <- get dest, height + copy-to *dest-height-a, tmp + } + # compute scaling factor 255/max + var target-scale/eax: int <- copy 0xff + var scale-f/xmm7: float <- convert target-scale + var src-max-a/eax: (addr int) <- get src, max + var tmp-f/xmm0: float <- convert *src-max-a + scale-f <- divide tmp-f + # allocate 'data' + var capacity/ebx: int <- copy src-width + capacity <- multiply src-height + var dest/edi: (addr image) <- copy _dest + var dest-data-ah/eax: (addr handle array byte) <- get dest, data + populate dest-data-ah, capacity + var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah + var dest-data/edi: (addr array byte) <- copy _dest-data + # error buffers per r/g/b channel + var red-errors-storage: (array int 0xc0000) + var tmp/eax: (addr array int) <- address red-errors-storage + var red-errors: (addr array int) + copy-to red-errors, tmp + var green-errors-storage: (array int 0xc0000) + var tmp/eax: (addr array int) <- address green-errors-storage + var green-errors: (addr array int) + copy-to green-errors, tmp + var blue-errors-storage: (array int 0xc0000) + var tmp/eax: (addr array int) <- address blue-errors-storage + var blue-errors: (addr array int) + copy-to blue-errors, tmp + # transform 'data' + var src-data-ah/eax: (addr handle array byte) <- get src, data + var _src-data/eax: (addr array byte) <- lookup *src-data-ah + var src-data/esi: (addr array byte) <- copy _src-data + var y/edx: int <- copy 0 + { + compare y, src-height + break-if->= + var x/ecx: int <- copy 0 + { + compare x, src-width + break-if->= + # - update errors and compute color levels for current pixel in each channel + # update red-error with current image pixel + var red-error: int + { + var tmp/esi: int <- _read-dithering-error red-errors, x, y, src-width + copy-to red-error, tmp + } + { + var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 0/red, scale-f + add-to red-error, tmp + } + # recompute red channel for current pixel + var red-level: int + { + var tmp/eax: int <- _error-to-ppm-channel red-error + copy-to red-level, tmp + } + # update green-error with current image pixel + var green-error: int + { + var tmp/esi: int <- _read-dithering-error green-errors, x, y, src-width + copy-to green-error, tmp + } + { + var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 1/green, scale-f + add-to green-error, tmp + } + # recompute green channel for current pixel + var green-level: int + { + var tmp/eax: int <- _error-to-ppm-channel green-error + copy-to green-level, tmp + } + # update blue-error with current image pixel + var blue-error: int + { + var tmp/esi: int <- _read-dithering-error blue-errors, x, y, src-width + copy-to blue-error, tmp + } + { + var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 2/blue, scale-f + add-to blue-error, tmp + } + # recompute blue channel for current pixel + var blue-level: int + { + var tmp/eax: int <- _error-to-ppm-channel blue-error + copy-to blue-level, tmp + } + # - figure out the nearest color +#? { +#? compare red-level, 0x80 +#? break-if-> +#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, red-level, 4/fg 0/bg +#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, green-level, 2/fg 0/bg +#? draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, blue-level, 9/fg 0/bg +#? } + var nearest-color-index/eax: int <- nearest-color-euclidean red-level, green-level, blue-level + { + var nearest-color-index-byte/eax: byte <- copy-byte nearest-color-index + _write-raw-buffer dest-data, x, y, src-width, nearest-color-index-byte + } + # - diffuse errors + var red-level: int + var green-level: int + var blue-level: int + { + var tmp-red-level/ecx: int <- copy 0 + var tmp-green-level/edx: int <- copy 0 + var tmp-blue-level/ebx: int <- copy 0 + tmp-red-level, tmp-green-level, tmp-blue-level <- color-rgb nearest-color-index + copy-to red-level, tmp-red-level + copy-to green-level, tmp-green-level + copy-to blue-level, tmp-blue-level + } + # update red-error + var red-level-error/eax: int <- copy red-level + red-level-error <- shift-left 0x10 + subtract-from red-error, red-level-error + _diffuse-dithering-error-floyd-steinberg red-errors, x, y, src-width, src-height, red-error + # update green-error + var green-level-error/eax: int <- copy green-level + green-level-error <- shift-left 0x10 + subtract-from green-error, green-level-error + _diffuse-dithering-error-floyd-steinberg green-errors, x, y, src-width, src-height, green-error + # update blue-error + var blue-level-error/eax: int <- copy blue-level + blue-level-error <- shift-left 0x10 + subtract-from blue-error, blue-level-error + _diffuse-dithering-error-floyd-steinberg blue-errors, x, y, src-width, src-height, blue-error + # + x <- increment + loop + } + y <- increment + loop + } +} + +# convert a single channel for a single image pixel to error space +fn _ppm-error buf: (addr array byte), x: int, y: int, width: int, channel: int, _scale-f: float -> _/eax: int { + # current image pixel + var initial-level/eax: byte <- _read-ppm-buffer buf, x, y, width, channel + # scale to 255 levels + var initial-level-int/eax: int <- copy initial-level + var initial-level-f/xmm0: float <- convert initial-level-int + var scale-f/xmm1: float <- copy _scale-f + initial-level-f <- multiply scale-f + initial-level-int <- convert initial-level-f + # switch to fixed-point with 16 bits of precision + initial-level-int <- shift-left 0x10 + return initial-level-int +} + +fn _error-to-ppm-channel error: int -> _/eax: int { + # clamp(error >> 16) + var result/esi: int <- copy error + result <- shift-right-signed 0x10 + { + compare result, 0 + break-if->= + result <- copy 0 + } + { + compare result, 0xff + break-if-<= + result <- copy 0xff + } + return result +} + +# read from a buffer containing alternating bytes from r/g/b channels +fn _read-ppm-buffer _buf: (addr array byte), x: int, y: int, width: int, channel: int -> _/eax: byte { + var buf/esi: (addr array byte) <- copy _buf + var idx/ecx: int <- copy y + idx <- multiply width + idx <- add x + var byte-idx/edx: int <- copy 3 + byte-idx <- multiply idx + byte-idx <- add channel + var result-a/eax: (addr byte) <- index buf, byte-idx + var result/eax: byte <- copy-byte *result-a + return result +} + +# each byte in the image data is a color of the current palette +fn render-raw-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int { + var img/esi: (addr image) <- copy _img + # yratio = height/img->height + var img-height-a/eax: (addr int) <- get img, height + var img-height/xmm0: float <- convert *img-height-a + var yratio/xmm1: float <- convert height + yratio <- divide img-height + # xratio = width/img->width + var img-width-a/eax: (addr int) <- get img, width + var img-width/ebx: int <- copy *img-width-a + var img-width-f/xmm0: float <- convert img-width + var xratio/xmm2: float <- convert width + xratio <- divide img-width-f + # esi = img->data + var img-data-ah/eax: (addr handle array byte) <- get img, data + var _img-data/eax: (addr array byte) <- lookup *img-data-ah + var img-data/esi: (addr array byte) <- copy _img-data + var len/edi: int <- length img-data + # + var one/eax: int <- copy 1 + var one-f/xmm3: float <- convert one + var width-f/xmm4: float <- convert width + var height-f/xmm5: float <- convert height + var zero/eax: int <- copy 0 + var zero-f/xmm0: float <- convert zero + var y/xmm6: float <- copy zero-f + { + compare y, height-f + break-if-float>= + var imgy-f/xmm5: float <- copy y + imgy-f <- divide yratio + var imgy/edx: int <- truncate imgy-f + var x/xmm7: float <- copy zero-f + { + compare x, width-f + break-if-float>= + var imgx-f/xmm5: float <- copy x + imgx-f <- divide xratio + var imgx/ecx: int <- truncate imgx-f + var idx/eax: int <- copy imgy + idx <- multiply img-width + idx <- add imgx + # error info in case we rounded wrong and 'index' will fail bounds-check + compare idx, len + { + break-if-< + set-cursor-position 0/screen, 0x20/x 0x20/y + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg + draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg + } + var color-a/eax: (addr byte) <- index img-data, idx + var color/eax: byte <- copy-byte *color-a + var color-int/eax: int <- copy color + var screenx/ecx: int <- convert x + screenx <- add xmin + var screeny/edx: int <- convert y + screeny <- add ymin + pixel screen, screenx, screeny, color-int + x <- add one-f + loop + } + y <- add one-f + loop + } +} diff --git a/apps/life.mu b/apps/life.mu new file mode 100644 index 00000000..a65347bf --- /dev/null +++ b/apps/life.mu @@ -0,0 +1,252 @@ +# Conway's Game of Life +# +# To build: +# $ ./translate apps/life.mu +# To run: +# $ qemu-system-i386 code.img + +fn state _grid: (addr array boolean), x: int, y: int -> _/eax: boolean { + # clip at the edge + compare x, 0 + { + break-if->= + return 0/false + } + compare y, 0 + { + break-if->= + return 0/false + } + compare x, 0x100/width + { + break-if-< + return 0/false + } + compare y, 0xc0/height + { + break-if-< + return 0/false + } + var idx/eax: int <- copy y + idx <- shift-left 8/log2width + idx <- add x + var grid/esi: (addr array boolean) <- copy _grid + var result/eax: (addr boolean) <- index grid, idx + return *result +} + +fn set-state _grid: (addr array boolean), x: int, y: int, val: boolean { + # don't bother checking bounds + var idx/eax: int <- copy y + idx <- shift-left 8/log2width + idx <- add x + var grid/esi: (addr array boolean) <- copy _grid + var result/eax: (addr boolean) <- index grid, idx + var src/ecx: boolean <- copy val + copy-to *result, src +} + +fn num-live-neighbors grid: (addr array boolean), x: int, y: int -> _/eax: int { + var result/edi: int <- copy 0 + # row above: zig + decrement y + decrement x + var s/eax: boolean <- state grid, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + increment x + s <- state grid, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + increment x + s <- state grid, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + # curr row: zag + increment y + s <- state grid, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + subtract-from x, 2 + s <- state grid, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + # row below: zig + increment y + s <- state grid, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + increment x + s <- state grid, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + increment x + s <- state grid, x, y + { + compare s, 0/false + break-if-= + result <- increment + } + return result +} + +fn step old-grid: (addr array boolean), new-grid: (addr array boolean) { + var y/ecx: int <- copy 0 + { + compare y, 0xc0/height + break-if->= + var x/edx: int <- copy 0 + { + compare x, 0x100/width + break-if->= + var n/eax: int <- num-live-neighbors old-grid, x, y + # if neighbors < 2, die of loneliness + { + compare n, 2 + break-if->= + set-state new-grid, x, y, 0/dead + } + # if neighbors > 3, die of overcrowding + { + compare n, 3 + break-if-<= + set-state new-grid, x, y, 0/dead + } + # if neighbors = 2, preserve state + { + compare n, 2 + break-if-!= + var old-state/eax: boolean <- state old-grid, x, y + set-state new-grid, x, y, old-state + } + # if neighbors = 3, cell quickens to life + { + compare n, 3 + break-if-!= + set-state new-grid, x, y, 1/live + } + x <- increment + loop + } + y <- increment + loop + } +} + +# color a square of size 'side' starting at x*side, y*side +fn render-square _x: int, _y: int, color: int { + var y/edx: int <- copy _y + y <- shift-left 2/log2side + var side/ebx: int <- copy 1 + side <- shift-left 2/log2side + var ymax/ecx: int <- copy y + ymax <- add side + { + compare y, ymax + break-if->= + { + var x/eax: int <- copy _x + x <- shift-left 2/log2side + var xmax/ecx: int <- copy x + xmax <- add side + { + compare x, xmax + break-if->= + pixel-on-real-screen x, y, color + x <- increment + loop + } + } + y <- increment + loop + } +} + +fn render grid: (addr array boolean) { + var y/ecx: int <- copy 0 + { + compare y, 0xc0/height + break-if->= + var x/edx: int <- copy 0 + { + compare x, 0x100/width + break-if->= + var state/eax: boolean <- state grid, x, y + compare state, 0/false + { + break-if-= + render-square x, y, 3/cyan + } + compare state, 0/false + { + break-if-!= + render-square x, y, 0/black + } + x <- increment + loop + } + y <- increment + loop + } +} + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { +#? # allocate on the stack +#? var grid1-storage: (array boolean 0xc000) # width * height +#? var grid1/esi: (addr array boolean) <- address grid1-storage +#? var grid2-storage: (array boolean 0xc000) # width * height +#? var grid2/edi: (addr array boolean) <- address grid2-storage + # allocate on the heap + var grid1-storage: (handle array boolean) + var grid1-ah/eax: (addr handle array boolean) <- address grid1-storage + populate grid1-ah, 0xc000 # width * height + var _grid1/eax: (addr array boolean) <- lookup *grid1-ah + var grid1/esi: (addr array boolean) <- copy _grid1 + var grid2-storage: (handle array boolean) + var grid2-ah/eax: (addr handle array boolean) <- address grid2-storage + populate grid2-ah, 0xc000 # width * height + var _grid2/eax: (addr array boolean) <- lookup *grid2-ah + var grid2/edi: (addr array boolean) <- copy _grid2 + # initialize grid1 + set-state grid1, 0x80, 0x5f, 1/live + set-state grid1, 0x81, 0x5f, 1/live + set-state grid1, 0x7f, 0x60, 1/live + set-state grid1, 0x80, 0x60, 1/live + set-state grid1, 0x80, 0x61, 1/live + # render grid1 + render grid1 + { + var key/eax: byte <- read-key keyboard + compare key, 0 +#? loop-if-= # press key to step + break-if-!= # press key to quit # comment this out to run under bochs; I'm not sure why there's a newline in the keyboard buffer + # iter: grid1 -> grid2 + step grid1, grid2 + render grid2 + # iter: grid2 -> grid1 + step grid2, grid1 + render grid1 + loop + } +} diff --git a/apps/mandelbrot-fixed.mu b/apps/mandelbrot-fixed.mu new file mode 100644 index 00000000..fc33aae1 --- /dev/null +++ b/apps/mandelbrot-fixed.mu @@ -0,0 +1,262 @@ +# Mandelbrot set using fixed-point numbers. +# +# Install: +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# Build on Linux: +# $ ./translate apps/mandelbrot-fixed.mu +# Build on other platforms (slow): +# $ ./translate_emulated apps/mandelbrot-fixed.mu +# Run: +# $ qemu-system-i386 code.img + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + # Initially the viewport is centered at 0, 0 in the scene. + var scene-cx-f: int + var scene-cy-f: int + # Initially the viewport shows a section of the scene 4 units wide. + var scene-width-f: int + copy-to scene-width-f, 0x400/4 + { + mandelbrot screen scene-cx-f, scene-cy-f, scene-width-f + # move at an angle slowly towards the edge + var adj-f/eax: int <- multiply-fixed scene-width-f, 0x12/0.07 + subtract-from scene-cx-f, adj-f + add-to scene-cy-f, adj-f + # slowly shrink the scene width to zoom in + var tmp-f/eax: int <- multiply-fixed scene-width-f, 0x80/0.5 + copy-to scene-width-f, tmp-f + loop + } +} + +# Since they still look like int types, we'll append a '-f' suffix to variable +# names to designate fixed-point numbers. + +fn int-to-fixed in: int -> _/eax: int { + var result-f/eax: int <- copy in + result-f <- shift-left 8/fixed-precision + { + break-if-not-overflow + abort "int-to-fixed: overflow" + } + return result-f +} + +fn fixed-to-int in-f: int -> _/eax: int { + var result/eax: int <- copy in-f + result <- shift-right-signed 8/fixed-precision + return result +} + +# The process of throwing bits away always adjusts a number towards -infinity. +fn test-fixed-conversion { + # 0 + var f/eax: int <- int-to-fixed 0 + var result/eax: int <- fixed-to-int f + check-ints-equal result, 0, "F - test-fixed-conversion - 0" + # 1 + var f/eax: int <- int-to-fixed 1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, 1, "F - test-fixed-conversion - 1" + # -1 + var f/eax: int <- int-to-fixed -1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, -1, "F - test-fixed-conversion - -1" + # 0.5 = 1/2 + var f/eax: int <- int-to-fixed 1 + f <- shift-right-signed 1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, 0, "F - test-fixed-conversion - 0.5" + # -0.5 = -1/2 + var f/eax: int <- int-to-fixed -1 + f <- shift-right-signed 1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, -1, "F - test-fixed-conversion - -0.5" + # 1.5 = 3/2 + var f/eax: int <- int-to-fixed 3 + f <- shift-right-signed 1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, 1, "F - test-fixed-conversion - 1.5" + # -1.5 = -3/2 + var f/eax: int <- int-to-fixed -3 + f <- shift-right-signed 1 + var result/eax: int <- fixed-to-int f + check-ints-equal result, -2, "F - test-fixed-conversion - -1.5" + # 1.25 = 5/4 + var f/eax: int <- int-to-fixed 5 + f <- shift-right-signed 2 + var result/eax: int <- fixed-to-int f + check-ints-equal result, 1, "F - test-fixed-conversion - 1.25" + # -1.25 = -5/4 + var f/eax: int <- int-to-fixed -5 + f <- shift-right-signed 2 + var result/eax: int <- fixed-to-int f + check-ints-equal result, -2, "F - test-fixed-conversion - -1.25" +} + +# special routines for multiplying and dividing fixed-point numbers + +fn multiply-fixed a-f: int, b-f: int -> _/eax: int { + var result/eax: int <- copy a-f + result <- multiply b-f + { + break-if-not-overflow + abort "multiply-fixed: overflow" + } + result <- shift-right-signed 8/fixed-precision + return result +} + +fn divide-fixed a-f: int, b-f: int -> _/eax: int { + var result-f/eax: int <- copy a-f + result-f <- shift-left 8/fixed-precision + { + break-if-not-overflow + abort "divide-fixed: overflow" + } + var dummy-remainder/edx: int <- copy 0 + result-f, dummy-remainder <- integer-divide result-f, b-f + return result-f +} + +# multiplying or dividing by an integer can use existing instructions. + +# adding and subtracting two fixed-point numbers can use existing instructions. + +fn mandelbrot screen: (addr screen), scene-cx-f: int, scene-cy-f: int, scene-width-f: int { + var a/eax: int <- copy 0 + var b/ecx: int <- copy 0 + a, b <- screen-size screen + var width/esi: int <- copy a + width <- shift-left 3/log2-font-width + var height/edi: int <- copy b + height <- shift-left 4/log2-font-height + var y/ecx: int <- copy 0 + { + compare y, height + break-if->= + var imaginary-f/ebx: int <- viewport-to-imaginary-f y, width, height, scene-cy-f, scene-width-f + var x/eax: int <- copy 0 + { + compare x, width + break-if->= + var real-f/edx: int <- viewport-to-real-f x, width, scene-cx-f, scene-width-f + var iterations/esi: int <- mandelbrot-iterations-for-point real-f, imaginary-f, 0x400/max + iterations <- shift-right 3 + var color/edx: int <- copy 0 + { + var dummy/eax: int <- copy 0 + dummy, color <- integer-divide iterations, 0x18/24/size-of-cycle-0 + color <- add 0x20/cycle-0 + } + pixel screen, x, y, color + x <- increment + loop + } + y <- increment + loop + } +} + +fn mandelbrot-iterations-for-point real-f: int, imaginary-f: int, max: int -> _/esi: int { + var x-f/esi: int <- copy 0 + var y-f/edi: int <- copy 0 + var iterations/ecx: int <- copy 0 + { + var done?/eax: boolean <- mandelbrot-done? x-f, y-f + compare done?, 0/false + break-if-!= + compare iterations, max + break-if->= + var x2-f/edx: int <- mandelbrot-x x-f, y-f, real-f + var y2-f/ebx: int <- mandelbrot-y x-f, y-f, imaginary-f + x-f <- copy x2-f + y-f <- copy y2-f + iterations <- increment + loop + } + return iterations +} + +fn mandelbrot-done? x-f: int, y-f: int -> _/eax: boolean { + # x*x + y*y > 4 + var tmp-f/eax: int <- multiply-fixed x-f, x-f + var result-f/ecx: int <- copy tmp-f + tmp-f <- multiply-fixed y-f, y-f + result-f <- add tmp-f + compare result-f, 0x400/4 + { + break-if-> + return 0/false + } + return 1/true +} + +fn mandelbrot-x x-f: int, y-f: int, real-f: int -> _/edx: int { + # x*x - y*y + real + var tmp-f/eax: int <- multiply-fixed x-f, x-f + var result-f/ecx: int <- copy tmp-f + tmp-f <- multiply-fixed y-f, y-f + result-f <- subtract tmp-f + result-f <- add real-f + return result-f +} + +fn mandelbrot-y x-f: int, y-f: int, imaginary-f: int -> _/ebx: int { + # 2*x*y + imaginary + var result-f/eax: int <- copy x-f + result-f <- shift-left 1/log2 + result-f <- multiply-fixed result-f, y-f + result-f <- add imaginary-f + return result-f +} + +# Scale (x, y) pixel coordinates to a complex plane where the viewport width +# ranges from -2 to +2. Viewport height just follows the viewport's aspect +# ratio. + +fn viewport-to-real-f x: int, width: int, scene-cx-f: int, scene-width-f: int -> _/edx: int { + # 0 in the viewport goes to scene-cx - scene-width/2 + # width in the viewport goes to scene-cx + scene-width/2 + # Therefore: + # x in the viewport goes to (scene-cx - scene-width/2) + x*scene-width/width + # At most two numbers being multiplied before a divide, so no risk of overflow. + var result-f/eax: int <- int-to-fixed x + result-f <- multiply-fixed result-f, scene-width-f + var width-f/ecx: int <- copy width + width-f <- shift-left 8/fixed-precision + result-f <- divide-fixed result-f, width-f + result-f <- add scene-cx-f + var half-scene-width-f/ecx: int <- copy scene-width-f + half-scene-width-f <- shift-right 1 + result-f <- subtract half-scene-width-f + return result-f +} + +fn viewport-to-imaginary-f y: int, width: int, height: int, scene-cy-f: int, scene-width-f: int -> _/ebx: int { + # 0 in the viewport goes to scene-cy - scene-width/2*height/width + # height in the viewport goes to scene-cy + scene-width/2*height/width + # Therefore: + # y in the viewport goes to (scene-cy - scene-width/2*height/width) + y*scene-width/width + # scene-cy - scene-width/width * (height/2 + y) + # At most two numbers being multiplied before a divide, so no risk of overflow. + var result-f/eax: int <- int-to-fixed y + result-f <- multiply-fixed result-f, scene-width-f + var width-f/ecx: int <- copy width + width-f <- shift-left 8/fixed-precision + result-f <- divide-fixed result-f, width-f + result-f <- add scene-cy-f + var second-term-f/edx: int <- copy 0 + { + var _second-term-f/eax: int <- copy scene-width-f + _second-term-f <- shift-right 1 + var height-f/ebx: int <- copy height + height-f <- shift-left 8/fixed-precision + _second-term-f <- multiply-fixed _second-term-f, height-f + _second-term-f <- divide-fixed _second-term-f, width-f + second-term-f <- copy _second-term-f + } + result-f <- subtract second-term-f + return result-f +} diff --git a/apps/mandelbrot-silhouette.mu b/apps/mandelbrot-silhouette.mu new file mode 100644 index 00000000..0d9a137c --- /dev/null +++ b/apps/mandelbrot-silhouette.mu @@ -0,0 +1,150 @@ +# Mandelbrot set +# +# Install: +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# Build on Linux: +# $ ./translate apps/mandelbrot.mu +# Build on other platforms (slow): +# $ ./translate_emulated apps/mandelbrot.mu +# Run: +# $ qemu-system-i386 code.img + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + mandelbrot screen +} + +fn mandelbrot screen: (addr screen) { + var a/eax: int <- copy 0 + var b/ecx: int <- copy 0 + a, b <- screen-size screen + var width/esi: int <- copy a + width <- shift-left 3/log2-font-width + var height/edi: int <- copy b + height <- shift-left 4/log2-font-height + var y/ecx: int <- copy 0 + { + compare y, height + break-if->= + var imaginary/xmm1: float <- viewport-to-imaginary y, width, height + var x/edx: int <- copy 0 + { + compare x, width + break-if->= + var real/xmm0: float <- viewport-to-real x, width + var iterations/eax: int <- mandelbrot-iterations-for-point real, imaginary, 0x400/max + compare iterations, 0x400/max + { + break-if->= + pixel screen, x, y, 0xf/white + } + compare iterations, 0x400/max + { + break-if-< + pixel screen, x, y, 0/black + } + x <- increment + loop + } + y <- increment + loop + } +} + +fn mandelbrot-iterations-for-point real: float, imaginary: float, max: int -> _/eax: int { + var zero: float + var x/xmm0: float <- copy zero + var y/xmm1: float <- copy zero + var iterations/ecx: int <- copy 0 + { + var done?/eax: boolean <- mandelbrot-done? x, y + compare done?, 0/false + break-if-!= + compare iterations, max + break-if->= + var newx/xmm2: float <- mandelbrot-x x, y, real + var newy/xmm3: float <- mandelbrot-y x, y, imaginary + x <- copy newx + y <- copy newy + iterations <- increment + loop + } + return iterations +} + +fn mandelbrot-done? x: float, y: float -> _/eax: boolean { + # x*x + y*y > 4 + var x2/xmm0: float <- copy x + x2 <- multiply x + var y2/xmm1: float <- copy y + y2 <- multiply y + var sum/xmm0: float <- copy x2 + sum <- add y2 + var four/eax: int <- copy 4 + var four-f/xmm1: float <- convert four + compare sum, four-f + { + break-if-float> + return 0/false + } + return 1/true +} + +fn mandelbrot-x x: float, y: float, real: float -> _/xmm2: float { + # x*x - y*y + real + var x2/xmm0: float <- copy x + x2 <- multiply x + var y2/xmm1: float <- copy y + y2 <- multiply y + var result/xmm0: float <- copy x2 + result <- subtract y2 + result <- add real + return result +} + +fn mandelbrot-y x: float, y: float, imaginary: float -> _/xmm3: float { + # 2*x*y + imaginary + var two/eax: int <- copy 2 + var result/xmm0: float <- convert two + result <- multiply x + result <- multiply y + result <- add imaginary + return result +} + +# Scale (x, y) pixel coordinates to a complex plane where the viewport width +# ranges from -2 to +2. Viewport height just follows the viewport's aspect +# ratio. + +fn viewport-to-real x: int, width: int -> _/xmm0: float { + # (x - width/2)*4/width + var result/xmm0: float <- convert x + var width-f/xmm1: float <- convert width + var two/eax: int <- copy 2 + var two-f/xmm2: float <- convert two + var half-width-f/xmm2: float <- reciprocal two-f + half-width-f <- multiply width-f + result <- subtract half-width-f + var four/eax: int <- copy 4 + var four-f/xmm2: float <- convert four + result <- multiply four-f + result <- divide width-f + return result +} + +fn viewport-to-imaginary y: int, width: int, height: int -> _/xmm1: float { + # (y - height/2)*4/width + var result/xmm0: float <- convert y + var height-f/xmm1: float <- convert height + var half-height-f/xmm1: float <- copy height-f + var two/eax: int <- copy 2 + var two-f/xmm2: float <- convert two + half-height-f <- divide two-f + result <- subtract half-height-f + var four/eax: int <- copy 4 + var four-f/xmm1: float <- convert four + result <- multiply four-f + var width-f/xmm1: float <- convert width + result <- divide width-f + return result +} diff --git a/apps/mandelbrot.mu b/apps/mandelbrot.mu new file mode 100644 index 00000000..218fd4fa --- /dev/null +++ b/apps/mandelbrot.mu @@ -0,0 +1,179 @@ +# Mandelbrot set +# +# Install: +# $ git clone https://github.com/akkartik/mu +# $ cd mu +# Build on Linux: +# $ ./translate apps/mandelbrot.mu +# Build on other platforms (slow): +# $ ./translate_emulated apps/mandelbrot.mu +# Run: +# $ qemu-system-i386 code.img + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + # Initially the viewport is centered at 0, 0 in the scene. + var zero: float + var scene-cx/xmm1: float <- copy zero + var scene-cy/xmm2: float <- copy zero + # Initially the viewport shows a section of the scene 4 units wide. + # scene-width-scale = 0.5 + var scene-width-scale: float + var dest/eax: (addr float) <- address scene-width-scale + fill-in-rational dest, 1, 2 + # scene-width = 4 + var four: float + var dest/eax: (addr float) <- address four + fill-in-rational dest, 4, 1 + var scene-width/xmm3: float <- copy four + { + mandelbrot screen scene-cx, scene-cy, scene-width + # move the center some % of the current screen-width + var adj/xmm0: float <- rational 2, 0x1c/28 + adj <- multiply scene-width + scene-cx <- subtract adj + scene-cy <- add adj + # slowly shrink the scene width to zoom in + scene-width <- multiply scene-width-scale + loop + } +} + +fn mandelbrot screen: (addr screen), scene-cx: float, scene-cy: float, scene-width: float { + var a/eax: int <- copy 0 + var b/ecx: int <- copy 0 + a, b <- screen-size screen + var width/esi: int <- copy a + width <- shift-left 3/log2-font-width + var height/edi: int <- copy b + height <- shift-left 4/log2-font-height + var y/ecx: int <- copy 0 + { + compare y, height + break-if->= + var imaginary/xmm1: float <- viewport-to-imaginary y, width, height, scene-cy, scene-width + var x/ebx: int <- copy 0 + { + compare x, width + break-if->= + var real/xmm0: float <- viewport-to-real x, width, scene-cx, scene-width + var iterations/eax: int <- mandelbrot-iterations-for-point real, imaginary, 0x400/max + iterations <- shift-right 3 + var color/edx: int <- copy 0 + iterations, color <- integer-divide iterations, 0x18/24/size-of-cycle-0 + color <- add 0x20/cycle-0 + pixel screen, x, y, color + x <- increment + loop + } + y <- increment + loop + } +} + +fn mandelbrot-iterations-for-point real: float, imaginary: float, max: int -> _/eax: int { + var zero: float + var x/xmm0: float <- copy zero + var y/xmm1: float <- copy zero + var iterations/ecx: int <- copy 0 + { + var done?/eax: boolean <- mandelbrot-done? x, y + compare done?, 0/false + break-if-!= + compare iterations, max + break-if->= + var newx/xmm2: float <- mandelbrot-x x, y, real + var newy/xmm3: float <- mandelbrot-y x, y, imaginary + x <- copy newx + y <- copy newy + iterations <- increment + loop + } + return iterations +} + +fn mandelbrot-done? x: float, y: float -> _/eax: boolean { + # x*x + y*y > 4 + var x2/xmm0: float <- copy x + x2 <- multiply x + var y2/xmm1: float <- copy y + y2 <- multiply y + var sum/xmm0: float <- copy x2 + sum <- add y2 + var four/eax: int <- copy 4 + var four-f/xmm1: float <- convert four + compare sum, four-f + { + break-if-float> + return 0/false + } + return 1/true +} + +fn mandelbrot-x x: float, y: float, real: float -> _/xmm2: float { + # x*x - y*y + real + var x2/xmm0: float <- copy x + x2 <- multiply x + var y2/xmm1: float <- copy y + y2 <- multiply y + var result/xmm0: float <- copy x2 + result <- subtract y2 + result <- add real + return result +} + +fn mandelbrot-y x: float, y: float, imaginary: float -> _/xmm3: float { + # 2*x*y + imaginary + var two/eax: int <- copy 2 + var result/xmm0: float <- convert two + result <- multiply x + result <- multiply y + result <- add imaginary + return result +} + +# Scale (x, y) pixel coordinates to a complex plane where the viewport width +# ranges from -2 to +2. Viewport height just follows the viewport's aspect +# ratio. + +fn viewport-to-real x: int, width: int, scene-cx: float, scene-width: float -> _/xmm0: float { + # 0 in the viewport goes to scene-cx - scene-width/2 + # width in the viewport goes to scene-cx + scene-width/2 + # Therefore: + # x in the viewport goes to (scene-cx - scene-width/2) + x*scene-width/width + # At most two numbers being multiplied before a divide, so no risk of overflow. + var result/xmm0: float <- convert x + result <- multiply scene-width + var width-f/xmm1: float <- convert width + result <- divide width-f + result <- add scene-cx + var two/eax: int <- copy 2 + var two-f/xmm2: float <- convert two + var half-scene-width/xmm1: float <- copy scene-width + half-scene-width <- divide two-f + result <- subtract half-scene-width + return result +} + +fn viewport-to-imaginary y: int, width: int, height: int, scene-cy: float, scene-width: float -> _/xmm1: float { + # 0 in the viewport goes to scene-cy - scene-width/2*height/width + # height in the viewport goes to scene-cy + scene-width/2*height/width + # Therefore: + # y in the viewport goes to (scene-cy - scene-width/2*height/width) + y*scene-width/width + # scene-cy - scene-width/width * (height/2 + y) + # At most two numbers being multiplied before a divide, so no risk of overflow. + var result/xmm0: float <- convert y + result <- multiply scene-width + var width-f/xmm1: float <- convert width + result <- divide width-f + result <- add scene-cy + var two/eax: int <- copy 2 + var two-f/xmm2: float <- convert two + var second-term/xmm1: float <- copy scene-width + second-term <- divide two-f + var height-f/xmm2: float <- convert height + second-term <- multiply height-f + var width-f/xmm2: float <- convert width + second-term <- divide width-f + result <- subtract second-term + return result +} diff --git a/apps/rpn.mu b/apps/rpn.mu new file mode 100644 index 00000000..1f432365 --- /dev/null +++ b/apps/rpn.mu @@ -0,0 +1,151 @@ +# Integer arithmetic using postfix notation +# +# Limitations: +# Division not implemented yet. +# +# To build: +# $ ./translate apps/rpn.mu +# +# Example session: +# $ qemu-system-i386 code.img +# > 4 +# 4 +# > 5 3 - +# 2 +# +# Error handling is non-existent. This is just a prototype. + +fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { + var in-storage: (stream byte 0x80) + var in/esi: (addr stream byte) <- address in-storage + var y/ecx: int <- copy 0 + var space/edx: grapheme <- copy 0x20 + # read-eval-print loop + { + # print prompt + var x/eax: int <- draw-text-rightward screen, "> ", 0/x, 0x80/xmax, y, 3/fg/cyan, 0/bg + # read line from keyboard + clear-stream in + { + draw-cursor screen, space + var key/eax: byte <- read-key keyboard + compare key, 0xa/newline + break-if-= + compare key, 0 + loop-if-= + var key2/eax: int <- copy key + append-byte in, key2 + var g/eax: grapheme <- copy key2 + draw-grapheme-at-cursor screen, g, 0xf/fg, 0/bg + move-cursor-right 0 + loop + } + # clear cursor + draw-grapheme-at-cursor screen, space, 3/fg/never-used, 0/bg + # parse and eval + var out/eax: int <- simplify in + # print + y <- increment + out, y <- draw-int32-decimal-wrapping-right-then-down screen, out, 0/xmin, y, 0x80/xmax, 0x30/ymax, 0/x, y, 7/fg, 0/bg + # newline + y <- increment + # + loop + } +} + +type int-stack { + data: (handle array int) + top: int +} + +fn simplify in: (addr stream byte) -> _/eax: int { + var word-storage: slice + var word/ecx: (addr slice) <- address word-storage + var stack-storage: int-stack + var stack/esi: (addr int-stack) <- address stack-storage + initialize-int-stack stack, 0x10 + $simplify:word-loop: { + next-word in, word + var done?/eax: boolean <- slice-empty? word + compare done?, 0 + break-if-!= + # if word is an operator, perform it + { + var is-add?/eax: boolean <- slice-equal? word, "+" + compare is-add?, 0 + break-if-= + var _b/eax: int <- pop-int-stack stack + var b/edx: int <- copy _b + var a/eax: int <- pop-int-stack stack + a <- add b + push-int-stack stack, a + loop $simplify:word-loop + } + { + var is-sub?/eax: boolean <- slice-equal? word, "-" + compare is-sub?, 0 + break-if-= + var _b/eax: int <- pop-int-stack stack + var b/edx: int <- copy _b + var a/eax: int <- pop-int-stack stack + a <- subtract b + push-int-stack stack, a + loop $simplify:word-loop + } + { + var is-mul?/eax: boolean <- slice-equal? word, "*" + compare is-mul?, 0 + break-if-= + var _b/eax: int <- pop-int-stack stack + var b/edx: int <- copy _b + var a/eax: int <- pop-int-stack stack + a <- multiply b + push-int-stack stack, a + loop $simplify:word-loop + } + # otherwise it's an int + var n/eax: int <- parse-decimal-int-from-slice word + push-int-stack stack, n + loop + } + var result/eax: int <- pop-int-stack stack + return result +} + +fn initialize-int-stack _self: (addr int-stack), n: int { + var self/esi: (addr int-stack) <- copy _self + var d/edi: (addr handle array int) <- get self, data + populate d, n + var top/eax: (addr int) <- get self, top + copy-to *top, 0 +} + +fn push-int-stack _self: (addr int-stack), _val: int { + var self/esi: (addr int-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + var data-ah/edx: (addr handle array int) <- get self, data + var data/eax: (addr array int) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var dest-addr/edx: (addr int) <- index data, top + var val/eax: int <- copy _val + copy-to *dest-addr, val + add-to *top-addr, 1 +} + +fn pop-int-stack _self: (addr int-stack) -> _/eax: int { + var self/esi: (addr int-stack) <- copy _self + var top-addr/ecx: (addr int) <- get self, top + { + compare *top-addr, 0 + break-if-> + return 0 + } + subtract-from *top-addr, 1 + var data-ah/edx: (addr handle array int) <- get self, data + var data/eax: (addr array int) <- lookup *data-ah + var top/edx: int <- copy *top-addr + var result-addr/eax: (addr int) <- index data, top + var val/eax: int <- copy *result-addr + return val +} |