From d89a9ec5d38294d41399218cef1eab9e9682dafd Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Fri, 16 Jul 2021 09:14:28 -0700 Subject: . --- html/510disk.mu.html | 107 +++++ html/511image.mu.html | 1167 ++++++++++++++++++++++++++++++++++++++++++++++ html/apps/ex13.mu.html | 87 ++++ html/apps/ex9.mu.html | 4 +- html/apps/img.mu.html | 1217 ------------------------------------------------ html/life.mu.html | 320 ------------- 6 files changed, 1363 insertions(+), 1539 deletions(-) create mode 100644 html/510disk.mu.html create mode 100644 html/511image.mu.html create mode 100644 html/apps/ex13.mu.html delete mode 100644 html/apps/img.mu.html delete mode 100644 html/life.mu.html diff --git a/html/510disk.mu.html b/html/510disk.mu.html new file mode 100644 index 00000000..fd32c6f2 --- /dev/null +++ b/html/510disk.mu.html @@ -0,0 +1,107 @@ + + + + +Mu - 510disk.mu + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/510disk.mu +
+ 1 fn load-sectors disk: (addr disk), lba: int, n: int, out: (addr stream byte) {
+ 2   var curr-lba/ebx: int <- copy lba
+ 3   var remaining/edx: int <- copy n
+ 4   {
+ 5     compare remaining, 0
+ 6     break-if-<=
+ 7     # sectors = min(remaining, 0x100)
+ 8     var sectors/eax: int <- copy remaining
+ 9     compare sectors, 0x100
+10     {
+11       break-if-<=
+12       sectors <- copy 0x100
+13     }
+14     #
+15     read-ata-disk disk, curr-lba, sectors, out
+16     #
+17     remaining <- subtract sectors
+18     curr-lba <- add sectors
+19     loop
+20   }
+21 }
+22 
+23 fn store-sectors disk: (addr disk), lba: int, n: int, in: (addr stream byte) {
+24   var curr-lba/ebx: int <- copy lba
+25   var remaining/edx: int <- copy n
+26   {
+27     compare remaining, 0
+28     break-if-<=
+29     # sectors = min(remaining, 0x100)
+30     var sectors/eax: int <- copy remaining
+31     compare sectors, 0x100
+32     {
+33       break-if-<=
+34       sectors <- copy 0x100
+35     }
+36     #
+37     write-ata-disk disk, curr-lba, sectors, in
+38     #
+39     remaining <- subtract sectors
+40     curr-lba <- add sectors
+41     loop
+42   }
+43 }
+
+ + + diff --git a/html/511image.mu.html b/html/511image.mu.html new file mode 100644 index 00000000..86b12469 --- /dev/null +++ b/html/511image.mu.html @@ -0,0 +1,1167 @@ + + + + +Mu - 511image.mu + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/511image.mu +
+   1 # Loading images from disk, rendering images to screen.
+   2 #
+   3 # Currently supports ASCII Netpbm formats.
+   4 #   https://en.wikipedia.org/wiki/Netpbm#File_formats
+   5 
+   6 type image {
+   7   type: int  # supported types:
+   8              #  1: portable bitmap (P1) - pixels 0 or 1
+   9              #  2: portable greymap (P2) - pixels 1-byte greyscale values
+  10              #  3: portable pixmap (P3) - pixels 3-byte rgb values
+  11   max: int
+  12   width: int
+  13   height: int
+  14   data: (handle array byte)
+  15 }
+  16 
+  17 fn initialize-image _self: (addr image), in: (addr stream byte) {
+  18   var self/esi: (addr image) <- copy _self
+  19   var mode-storage: slice
+  20   var mode/ecx: (addr slice) <- address mode-storage
+  21   next-word in, mode
+  22   {
+  23     var P1?/eax: boolean <- slice-equal? mode, "P1"
+  24     compare P1?, 0/false
+  25     break-if-=
+  26     var type-a/eax: (addr int) <- get self, type
+  27     copy-to *type-a, 1/ppm
+  28     initialize-image-from-pbm self, in
+  29     return
+  30   }
+  31   {
+  32     var P2?/eax: boolean <- slice-equal? mode, "P2"
+  33     compare P2?, 0/false
+  34     break-if-=
+  35     var type-a/eax: (addr int) <- get self, type
+  36     copy-to *type-a, 2/pgm
+  37     initialize-image-from-pgm self, in
+  38     return
+  39   }
+  40   {
+  41     var P3?/eax: boolean <- slice-equal? mode, "P3"
+  42     compare P3?, 0/false
+  43     break-if-=
+  44     var type-a/eax: (addr int) <- get self, type
+  45     copy-to *type-a, 3/ppm
+  46     initialize-image-from-ppm self, in
+  47     return
+  48   }
+  49   abort "initialize-image: unrecognized image type"
+  50 }
+  51 
+  52 # dispatch to a few variants with mostly identical boilerplate
+  53 fn render-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int {
+  54   var img/esi: (addr image) <- copy _img
+  55   var type-a/eax: (addr int) <- get img, type
+  56   {
+  57     compare *type-a, 1/pbm
+  58     break-if-!=
+  59     render-pbm-image screen, img, xmin, ymin, width, height
+  60     return
+  61   }
+  62   {
+  63     compare *type-a, 2/pgm
+  64     break-if-!=
+  65     var img2-storage: image
+  66     var img2/edi: (addr image) <- address img2-storage
+  67     dither-pgm-unordered img, img2
+  68     render-raw-image screen, img2, xmin, ymin, width, height
+  69     return
+  70   }
+  71   {
+  72     compare *type-a, 3/ppm
+  73     break-if-!=
+  74     var img2-storage: image
+  75     var img2/edi: (addr image) <- address img2-storage
+  76     dither-ppm-unordered img, img2
+  77     render-raw-image screen, img2, xmin, ymin, width, height
+  78     return
+  79   }
+  80   abort "render-image: unrecognized image type"
+  81 }
+  82 
+  83 ## helpers
+  84 
+  85 # import a black-and-white ascii bitmap (each pixel is 0 or 1)
+  86 fn initialize-image-from-pbm _self: (addr image), in: (addr stream byte) {
+  87   var self/esi: (addr image) <- copy _self
+  88   var curr-word-storage: slice
+  89   var curr-word/ecx: (addr slice) <- address curr-word-storage
+  90   # load width, height
+  91   next-word in, curr-word
+  92   var tmp/eax: int <- parse-decimal-int-from-slice curr-word
+  93   var width/edx: int <- copy tmp
+  94   next-word in, curr-word
+  95   tmp <- parse-decimal-int-from-slice curr-word
+  96   var height/ebx: int <- copy tmp
+  97   # save width, height
+  98   var dest/eax: (addr int) <- get self, width
+  99   copy-to *dest, width
+ 100   dest <- get self, height
+ 101   copy-to *dest, height
+ 102   # initialize data
+ 103   var capacity/edx: int <- copy width
+ 104   capacity <- multiply height
+ 105   var data-ah/edi: (addr handle array byte) <- get self, data
+ 106   populate data-ah, capacity
+ 107   var _data/eax: (addr array byte) <- lookup *data-ah
+ 108   var data/edi: (addr array byte) <- copy _data
+ 109   var i/ebx: int <- copy 0
+ 110   {
+ 111     compare i, capacity
+ 112     break-if->=
+ 113     next-word in, curr-word
+ 114     var src/eax: int <- parse-decimal-int-from-slice curr-word
+ 115     {
+ 116       var dest/ecx: (addr byte) <- index data, i
+ 117       copy-byte-to *dest, src
+ 118     }
+ 119     i <- increment
+ 120     loop
+ 121   }
+ 122 }
+ 123 
+ 124 # render a black-and-white ascii bitmap (each pixel is 0 or 1)
+ 125 fn render-pbm-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int {
+ 126   var img/esi: (addr image) <- copy _img
+ 127   # yratio = height/img->height
+ 128   var img-height-a/eax: (addr int) <- get img, height
+ 129   var img-height/xmm0: float <- convert *img-height-a
+ 130   var yratio/xmm1: float <- convert height
+ 131   yratio <- divide img-height
+ 132   # xratio = width/img->width
+ 133   var img-width-a/eax: (addr int) <- get img, width
+ 134   var img-width/ebx: int <- copy *img-width-a
+ 135   var img-width-f/xmm0: float <- convert img-width
+ 136   var xratio/xmm2: float <- convert width
+ 137   xratio <- divide img-width-f
+ 138   # esi = img->data
+ 139   var img-data-ah/eax: (addr handle array byte) <- get img, data
+ 140   var _img-data/eax: (addr array byte) <- lookup *img-data-ah
+ 141   var img-data/esi: (addr array byte) <- copy _img-data
+ 142   var len/edi: int <- length img-data
+ 143   #
+ 144   var one/eax: int <- copy 1
+ 145   var one-f/xmm3: float <- convert one
+ 146   var width-f/xmm4: float <- convert width
+ 147   var height-f/xmm5: float <- convert height
+ 148   var zero/eax: int <- copy 0
+ 149   var zero-f/xmm0: float <- convert zero
+ 150   var y/xmm6: float <- copy zero-f
+ 151   {
+ 152     compare y, height-f
+ 153     break-if-float>=
+ 154     var imgy-f/xmm5: float <- copy y
+ 155     imgy-f <- divide yratio
+ 156     var imgy/edx: int <- truncate imgy-f
+ 157     var x/xmm7: float <- copy zero-f
+ 158     {
+ 159       compare x, width-f
+ 160       break-if-float>=
+ 161       var imgx-f/xmm5: float <- copy x
+ 162       imgx-f <- divide xratio
+ 163       var imgx/ecx: int <- truncate imgx-f
+ 164       var idx/eax: int <- copy imgy
+ 165       idx <- multiply img-width
+ 166       idx <- add imgx
+ 167       # error info in case we rounded wrong and 'index' will fail bounds-check
+ 168       compare idx, len
+ 169       {
+ 170         break-if-<
+ 171         set-cursor-position 0/screen, 0x20/x 0x20/y
+ 172         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg
+ 173         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg
+ 174         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg
+ 175       }
+ 176       var src-a/eax: (addr byte) <- index img-data, idx
+ 177       var src/eax: byte <- copy-byte *src-a
+ 178       var color-int/eax: int <- copy src
+ 179       {
+ 180         compare color-int, 0/black
+ 181         break-if-=
+ 182         color-int <- copy 0xf/white
+ 183       }
+ 184       var screenx/ecx: int <- convert x
+ 185       screenx <- add xmin
+ 186       var screeny/edx: int <- convert y
+ 187       screeny <- add ymin
+ 188       pixel screen, screenx, screeny, color-int
+ 189       x <- add one-f
+ 190       loop
+ 191     }
+ 192     y <- add one-f
+ 193     loop
+ 194   }
+ 195 }
+ 196 
+ 197 # import a greyscale ascii "greymap" (each pixel is a shade of grey from 0 to 255)
+ 198 fn initialize-image-from-pgm _self: (addr image), in: (addr stream byte) {
+ 199   var self/esi: (addr image) <- copy _self
+ 200   var curr-word-storage: slice
+ 201   var curr-word/ecx: (addr slice) <- address curr-word-storage
+ 202   # load width, height
+ 203   next-word in, curr-word
+ 204   var tmp/eax: int <- parse-decimal-int-from-slice curr-word
+ 205   var width/edx: int <- copy tmp
+ 206   next-word in, curr-word
+ 207   tmp <- parse-decimal-int-from-slice curr-word
+ 208   var height/ebx: int <- copy tmp
+ 209   # check and save color levels
+ 210   next-word in, curr-word
+ 211   {
+ 212     tmp <- parse-decimal-int-from-slice curr-word
+ 213     compare tmp, 0xff
+ 214     break-if-=
+ 215     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
+ 216   }
+ 217   var dest/edi: (addr int) <- get self, max
+ 218   copy-to *dest, tmp
+ 219   # save width, height
+ 220   dest <- get self, width
+ 221   copy-to *dest, width
+ 222   dest <- get self, height
+ 223   copy-to *dest, height
+ 224   # initialize data
+ 225   var capacity/edx: int <- copy width
+ 226   capacity <- multiply height
+ 227   var data-ah/edi: (addr handle array byte) <- get self, data
+ 228   populate data-ah, capacity
+ 229   var _data/eax: (addr array byte) <- lookup *data-ah
+ 230   var data/edi: (addr array byte) <- copy _data
+ 231   var i/ebx: int <- copy 0
+ 232   {
+ 233     compare i, capacity
+ 234     break-if->=
+ 235     next-word in, curr-word
+ 236     var src/eax: int <- parse-decimal-int-from-slice curr-word
+ 237     {
+ 238       var dest/ecx: (addr byte) <- index data, i
+ 239       copy-byte-to *dest, src
+ 240     }
+ 241     i <- increment
+ 242     loop
+ 243   }
+ 244 }
+ 245 
+ 246 # render a greyscale ascii "greymap" (each pixel is a shade of grey from 0 to 255) by quantizing the shades
+ 247 fn render-pgm-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int {
+ 248   var img/esi: (addr image) <- copy _img
+ 249   # yratio = height/img->height
+ 250   var img-height-a/eax: (addr int) <- get img, height
+ 251   var img-height/xmm0: float <- convert *img-height-a
+ 252   var yratio/xmm1: float <- convert height
+ 253   yratio <- divide img-height
+ 254   # xratio = width/img->width
+ 255   var img-width-a/eax: (addr int) <- get img, width
+ 256   var img-width/ebx: int <- copy *img-width-a
+ 257   var img-width-f/xmm0: float <- convert img-width
+ 258   var xratio/xmm2: float <- convert width
+ 259   xratio <- divide img-width-f
+ 260   # esi = img->data
+ 261   var img-data-ah/eax: (addr handle array byte) <- get img, data
+ 262   var _img-data/eax: (addr array byte) <- lookup *img-data-ah
+ 263   var img-data/esi: (addr array byte) <- copy _img-data
+ 264   var len/edi: int <- length img-data
+ 265   #
+ 266   var one/eax: int <- copy 1
+ 267   var one-f/xmm3: float <- convert one
+ 268   var width-f/xmm4: float <- convert width
+ 269   var height-f/xmm5: float <- convert height
+ 270   var zero/eax: int <- copy 0
+ 271   var zero-f/xmm0: float <- convert zero
+ 272   var y/xmm6: float <- copy zero-f
+ 273   {
+ 274     compare y, height-f
+ 275     break-if-float>=
+ 276     var imgy-f/xmm5: float <- copy y
+ 277     imgy-f <- divide yratio
+ 278     var imgy/edx: int <- truncate imgy-f
+ 279     var x/xmm7: float <- copy zero-f
+ 280     {
+ 281       compare x, width-f
+ 282       break-if-float>=
+ 283       var imgx-f/xmm5: float <- copy x
+ 284       imgx-f <- divide xratio
+ 285       var imgx/ecx: int <- truncate imgx-f
+ 286       var idx/eax: int <- copy imgy
+ 287       idx <- multiply img-width
+ 288       idx <- add imgx
+ 289       # error info in case we rounded wrong and 'index' will fail bounds-check
+ 290       compare idx, len
+ 291       {
+ 292         break-if-<
+ 293         set-cursor-position 0/screen, 0x20/x 0x20/y
+ 294         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg
+ 295         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg
+ 296         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg
+ 297       }
+ 298       var src-a/eax: (addr byte) <- index img-data, idx
+ 299       var src/eax: byte <- copy-byte *src-a
+ 300       var color-int/eax: int <- nearest-grey src
+ 301       var screenx/ecx: int <- convert x
+ 302       screenx <- add xmin
+ 303       var screeny/edx: int <- convert y
+ 304       screeny <- add ymin
+ 305       pixel screen, screenx, screeny, color-int
+ 306       x <- add one-f
+ 307       loop
+ 308     }
+ 309     y <- add one-f
+ 310     loop
+ 311   }
+ 312 }
+ 313 
+ 314 fn nearest-grey level-255: byte -> _/eax: int {
+ 315   var result/eax: int <- copy level-255
+ 316   result <- shift-right 4
+ 317   result <- add 0x10
+ 318   return result
+ 319 }
+ 320 
+ 321 fn dither-pgm-unordered-monochrome _src: (addr image), _dest: (addr image) {
+ 322   var src/esi: (addr image) <- copy _src
+ 323   var dest/edi: (addr image) <- copy _dest
+ 324   # copy 'width'
+ 325   var src-width-a/eax: (addr int) <- get src, width
+ 326   var tmp/eax: int <- copy *src-width-a
+ 327   var src-width: int
+ 328   copy-to src-width, tmp
+ 329   {
+ 330     var dest-width-a/edx: (addr int) <- get dest, width
+ 331     copy-to *dest-width-a, tmp
+ 332   }
+ 333   # copy 'height'
+ 334   var src-height-a/eax: (addr int) <- get src, height
+ 335   var tmp/eax: int <- copy *src-height-a
+ 336   var src-height: int
+ 337   copy-to src-height, tmp
+ 338   {
+ 339     var dest-height-a/ecx: (addr int) <- get dest, height
+ 340     copy-to *dest-height-a, tmp
+ 341   }
+ 342   # transform 'data'
+ 343   var capacity/ebx: int <- copy src-width
+ 344   capacity <- multiply src-height
+ 345   var dest/edi: (addr image) <- copy _dest
+ 346   var dest-data-ah/eax: (addr handle array byte) <- get dest, data
+ 347   populate dest-data-ah, capacity
+ 348   var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah
+ 349   var dest-data/edi: (addr array byte) <- copy _dest-data
+ 350   # needs a buffer to temporarily hold more than 256 levels of precision
+ 351   var errors-storage: (array int 0xc0000)
+ 352   var errors/ebx: (addr array int) <- address errors-storage
+ 353   var src-data-ah/eax: (addr handle array byte) <- get src, data
+ 354   var _src-data/eax: (addr array byte) <- lookup *src-data-ah
+ 355   var src-data/esi: (addr array byte) <- copy _src-data
+ 356   var y/edx: int <- copy 0
+ 357   {
+ 358     compare y, src-height
+ 359     break-if->=
+ 360     var x/ecx: int <- copy 0
+ 361     {
+ 362       compare x, src-width
+ 363       break-if->=
+ 364       var curr/eax: byte <- _read-pgm-buffer src-data, x, y, src-width
+ 365       var curr-int/eax: int <- copy curr
+ 366       curr-int <- shift-left 0x10  # we have 32 bits; we'll use 16 bits for the fraction and leave 8 for unanticipated overflow
+ 367       var error/esi: int <- _read-dithering-error errors, x, y, src-width
+ 368       error <- add curr-int
+ 369       $_dither-pgm-unordered-monochrome:update-error: {
+ 370         compare error, 0x800000
+ 371         {
+ 372           break-if->=
+ 373           _write-raw-buffer dest-data, x, y, src-width, 0/black
+ 374           break $_dither-pgm-unordered-monochrome:update-error
+ 375         }
+ 376         _write-raw-buffer dest-data, x, y, src-width, 1/white
+ 377         error <- subtract 0xff0000
+ 378       }
+ 379       _diffuse-dithering-error-floyd-steinberg errors, x, y, src-width, src-height, error
+ 380       x <- increment
+ 381       loop
+ 382     }
+ 383     move-cursor-to-left-margin-of-next-line 0/screen
+ 384     y <- increment
+ 385     loop
+ 386   }
+ 387 }
+ 388 
+ 389 fn dither-pgm-unordered _src: (addr image), _dest: (addr image) {
+ 390   var src/esi: (addr image) <- copy _src
+ 391   var dest/edi: (addr image) <- copy _dest
+ 392   # copy 'width'
+ 393   var src-width-a/eax: (addr int) <- get src, width
+ 394   var tmp/eax: int <- copy *src-width-a
+ 395   var src-width: int
+ 396   copy-to src-width, tmp
+ 397   {
+ 398     var dest-width-a/edx: (addr int) <- get dest, width
+ 399     copy-to *dest-width-a, tmp
+ 400   }
+ 401   # copy 'height'
+ 402   var src-height-a/eax: (addr int) <- get src, height
+ 403   var tmp/eax: int <- copy *src-height-a
+ 404   var src-height: int
+ 405   copy-to src-height, tmp
+ 406   {
+ 407     var dest-height-a/ecx: (addr int) <- get dest, height
+ 408     copy-to *dest-height-a, tmp
+ 409   }
+ 410   # compute scaling factor 255/max
+ 411   var target-scale/eax: int <- copy 0xff
+ 412   var scale-f/xmm7: float <- convert target-scale
+ 413   var src-max-a/eax: (addr int) <- get src, max
+ 414   var tmp-f/xmm0: float <- convert *src-max-a
+ 415   scale-f <- divide tmp-f
+ 416   # transform 'data'
+ 417   var capacity/ebx: int <- copy src-width
+ 418   capacity <- multiply src-height
+ 419   var dest/edi: (addr image) <- copy _dest
+ 420   var dest-data-ah/eax: (addr handle array byte) <- get dest, data
+ 421   populate dest-data-ah, capacity
+ 422   var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah
+ 423   var dest-data/edi: (addr array byte) <- copy _dest-data
+ 424   # needs a buffer to temporarily hold more than 256 levels of precision
+ 425   var errors-storage: (array int 0xc0000)
+ 426   var errors/ebx: (addr array int) <- address errors-storage
+ 427   var src-data-ah/eax: (addr handle array byte) <- get src, data
+ 428   var _src-data/eax: (addr array byte) <- lookup *src-data-ah
+ 429   var src-data/esi: (addr array byte) <- copy _src-data
+ 430   var y/edx: int <- copy 0
+ 431   {
+ 432     compare y, src-height
+ 433     break-if->=
+ 434     var x/ecx: int <- copy 0
+ 435     {
+ 436       compare x, src-width
+ 437       break-if->=
+ 438       var initial-color/eax: byte <- _read-pgm-buffer src-data, x, y, src-width
+ 439       # . scale to 255 levels
+ 440       var initial-color-int/eax: int <- copy initial-color
+ 441       var initial-color-f/xmm0: float <- convert initial-color-int
+ 442       initial-color-f <- multiply scale-f
+ 443       initial-color-int <- convert initial-color-f
+ 444       var error/esi: int <- _read-dithering-error errors, x, y, src-width
+ 445       # error += (initial-color << 16)
+ 446       {
+ 447         var tmp/eax: int <- copy initial-color-int
+ 448         tmp <- shift-left 0x10  # we have 32 bits; we'll use 16 bits for the fraction and leave 8 for unanticipated overflow
+ 449         error <- add tmp
+ 450       }
+ 451       # nearest-color = nearest(error >> 16)
+ 452       var nearest-color/eax: int <- copy error
+ 453       nearest-color <- shift-right-signed 0x10
+ 454       {
+ 455         compare nearest-color, 0
+ 456         break-if->=
+ 457         nearest-color <- copy 0
+ 458       }
+ 459       {
+ 460         compare nearest-color, 0xf0
+ 461         break-if-<=
+ 462         nearest-color <- copy 0xf0
+ 463       }
+ 464       # . truncate last 4 bits
+ 465       nearest-color <- and 0xf0
+ 466       # error -= (nearest-color << 16)
+ 467       {
+ 468         var tmp/eax: int <- copy nearest-color
+ 469         tmp <- shift-left 0x10
+ 470         error <- subtract tmp
+ 471       }
+ 472       # color-index = (nearest-color >> 4 + 16)
+ 473       var color-index/eax: int <- copy nearest-color
+ 474       color-index <- shift-right 4
+ 475       color-index <- add 0x10
+ 476       var color-index-byte/eax: byte <- copy-byte color-index
+ 477       _write-raw-buffer dest-data, x, y, src-width, color-index-byte
+ 478       _diffuse-dithering-error-floyd-steinberg errors, x, y, src-width, src-height, error
+ 479       x <- increment
+ 480       loop
+ 481     }
+ 482     y <- increment
+ 483     loop
+ 484   }
+ 485 }
+ 486 
+ 487 # Use Floyd-Steinberg algorithm for diffusing error at x, y in a 2D grid of
+ 488 # dimensions (width, height)
+ 489 #
+ 490 # https://tannerhelland.com/2012/12/28/dithering-eleven-algorithms-source-code.html
+ 491 #
+ 492 # Error is currently a fixed-point number with 16-bit fraction. But
+ 493 # interestingly this function doesn't care about that.
+ 494 fn _diffuse-dithering-error-floyd-steinberg errors: (addr array int), x: int, y: int, width: int, height: int, error: int {
+ 495   {
+ 496     compare error, 0
+ 497     break-if-!=
+ 498     return
+ 499   }
+ 500   var width-1/esi: int <- copy width
+ 501   width-1 <- decrement
+ 502   var height-1/edi: int <- copy height
+ 503   height-1 <- decrement
+ 504   # delta = error/16
+ 505 #?   show-errors errors, width, height, x, y
+ 506   var delta/ecx: int <- copy error
+ 507   delta <- shift-right-signed 4
+ 508   # In Floyd-Steinberg, each pixel X transmits its errors to surrounding
+ 509   # pixels in the following proportion:
+ 510   #           X     7/16
+ 511   #     3/16  5/16  1/16
+ 512   var x/edx: int <- copy x
+ 513   {
+ 514     compare x, width-1
+ 515     break-if->=
+ 516     var tmp/eax: int <- copy 7
+ 517     tmp <- multiply delta
+ 518     var xright/edx: int <- copy x
+ 519     xright <- increment
+ 520     _accumulate-dithering-error errors, xright, y, width, tmp
+ 521   }
+ 522   var y/ebx: int <- copy y
+ 523   {
+ 524     compare y, height-1
+ 525     break-if-<
+ 526     return
+ 527   }
+ 528   var ybelow: int
+ 529   copy-to ybelow, y
+ 530   increment ybelow
+ 531   {
+ 532     compare x, 0
+ 533     break-if-<=
+ 534     var tmp/eax: int <- copy 3
+ 535     tmp <- multiply delta
+ 536     var xleft/edx: int <- copy x
+ 537     xleft <- decrement
+ 538     _accumulate-dithering-error errors, xleft, ybelow, width, tmp
+ 539   }
+ 540   {
+ 541     var tmp/eax: int <- copy 5
+ 542     tmp <- multiply delta
+ 543     _accumulate-dithering-error errors, x, ybelow, width, tmp
+ 544   }
+ 545   {
+ 546     compare x, width-1
+ 547     break-if->=
+ 548     var xright/edx: int <- copy x
+ 549     xright <- increment
+ 550     _accumulate-dithering-error errors, xright, ybelow, width, delta
+ 551   }
+ 552 #?   show-errors errors, width, height, x, y
+ 553 }
+ 554 
+ 555 fn _accumulate-dithering-error errors: (addr array int), x: int, y: int, width: int, error: int {
+ 556   var curr/esi: int <- _read-dithering-error errors, x, y, width
+ 557   curr <- add error
+ 558   _write-dithering-error errors, x, y, width, curr
+ 559 }
+ 560 
+ 561 fn _read-dithering-error _errors: (addr array int), x: int, y: int, width: int -> _/esi: int {
+ 562   var errors/esi: (addr array int) <- copy _errors
+ 563   var idx/ecx: int <- copy y
+ 564   idx <- multiply width
+ 565   idx <- add x
+ 566   var result-a/eax: (addr int) <- index errors, idx
+ 567   return *result-a
+ 568 }
+ 569 
+ 570 fn _write-dithering-error _errors: (addr array int), x: int, y: int, width: int, val: int {
+ 571   var errors/esi: (addr array int) <- copy _errors
+ 572   var idx/ecx: int <- copy y
+ 573   idx <- multiply width
+ 574   idx <- add x
+ 575 #?   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 7/fg 0/bg
+ 576 #?   move-cursor-to-left-margin-of-next-line 0/screen
+ 577   var src/eax: int <- copy val
+ 578   var dest-a/edi: (addr int) <- index errors, idx
+ 579   copy-to *dest-a, src
+ 580 }
+ 581 
+ 582 fn _read-pgm-buffer _buf: (addr array byte), x: int, y: int, width: int -> _/eax: byte {
+ 583   var buf/esi: (addr array byte) <- copy _buf
+ 584   var idx/ecx: int <- copy y
+ 585   idx <- multiply width
+ 586   idx <- add x
+ 587   var result-a/eax: (addr byte) <- index buf, idx
+ 588   var result/eax: byte <- copy-byte *result-a
+ 589   return result
+ 590 }
+ 591 
+ 592 fn _write-raw-buffer _buf: (addr array byte), x: int, y: int, width: int, val: byte {
+ 593   var buf/esi: (addr array byte) <- copy _buf
+ 594   var idx/ecx: int <- copy y
+ 595   idx <- multiply width
+ 596   idx <- add x
+ 597   var src/eax: byte <- copy val
+ 598   var dest-a/edi: (addr byte) <- index buf, idx
+ 599   copy-byte-to *dest-a, src
+ 600 }
+ 601 
+ 602 # some debugging helpers
+ 603 fn show-errors errors: (addr array int), width: int, height: int, x: int, y: int {
+ 604   compare y, 1
+ 605   {
+ 606     break-if-=
+ 607     return
+ 608   }
+ 609   compare x, 0
+ 610   {
+ 611     break-if-=
+ 612     return
+ 613   }
+ 614   var y/edx: int <- copy 0
+ 615   {
+ 616     compare y, height
+ 617     break-if->=
+ 618     var x/ecx: int <- copy 0
+ 619     {
+ 620       compare x, width
+ 621       break-if->=
+ 622       var error/esi: int <- _read-dithering-error errors, x, y, width
+ 623       psd "e", error, 5/fg, x, y
+ 624       x <- increment
+ 625       loop
+ 626     }
+ 627     move-cursor-to-left-margin-of-next-line 0/screen
+ 628     y <- increment
+ 629     loop
+ 630   }
+ 631 }
+ 632 
+ 633 fn psd s: (addr array byte), d: int, fg: int, x: int, y: int {
+ 634   {
+ 635     compare y, 0x18
+ 636     break-if->=
+ 637     return
+ 638   }
+ 639   {
+ 640     compare y, 0x1c
+ 641     break-if-<=
+ 642     return
+ 643   }
+ 644   {
+ 645     compare x, 0x40
+ 646     break-if->=
+ 647     return
+ 648   }
+ 649 #?   {
+ 650 #?     compare x, 0x48
+ 651 #?     break-if-<=
+ 652 #?     return
+ 653 #?   }
+ 654   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, s, 7/fg 0/bg
+ 655   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, d, fg 0/bg
+ 656 }
+ 657 
+ 658 fn psx s: (addr array byte), d: int, fg: int, x: int, y: int {
+ 659 #?   {
+ 660 #?     compare y, 0x60
+ 661 #?     break-if->=
+ 662 #?     return
+ 663 #?   }
+ 664 #?   {
+ 665 #?     compare y, 0x6c
+ 666 #?     break-if-<=
+ 667 #?     return
+ 668 #?   }
+ 669   {
+ 670     compare x, 0x20
+ 671     break-if->=
+ 672     return
+ 673   }
+ 674 #?   {
+ 675 #?     compare x, 0x6c
+ 676 #?     break-if-<=
+ 677 #?     return
+ 678 #?   }
+ 679   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, s, 7/fg 0/bg
+ 680   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, d, fg 0/bg
+ 681 }
+ 682 
+ 683 # import a color ascii "pixmap" (each pixel consists of 3 shades of r/g/b from 0 to 255)
+ 684 fn initialize-image-from-ppm _self: (addr image), in: (addr stream byte) {
+ 685   var self/esi: (addr image) <- copy _self
+ 686   var curr-word-storage: slice
+ 687   var curr-word/ecx: (addr slice) <- address curr-word-storage
+ 688   # load width, height
+ 689   next-word in, curr-word
+ 690   var tmp/eax: int <- parse-decimal-int-from-slice curr-word
+ 691   var width/edx: int <- copy tmp
+ 692   next-word in, curr-word
+ 693   tmp <- parse-decimal-int-from-slice curr-word
+ 694   var height/ebx: int <- copy tmp
+ 695   next-word in, curr-word
+ 696   # check color levels
+ 697   {
+ 698     tmp <- parse-decimal-int-from-slice curr-word
+ 699     compare tmp, 0xff
+ 700     break-if-=
+ 701     abort "initialize-image-from-ppm: supports exactly 255 levels per rgb channel"
+ 702   }
+ 703   var dest/edi: (addr int) <- get self, max
+ 704   copy-to *dest, tmp
+ 705   # save width, height
+ 706   dest <- get self, width
+ 707   copy-to *dest, width
+ 708   dest <- get self, height
+ 709   copy-to *dest, height
+ 710   # initialize data
+ 711   var capacity/edx: int <- copy width
+ 712   capacity <- multiply height
+ 713   # . multiply by 3 for the r/g/b channels
+ 714   var tmp/eax: int <- copy capacity
+ 715   tmp <- shift-left 1
+ 716   capacity <- add tmp
+ 717   #
+ 718   var data-ah/edi: (addr handle array byte) <- get self, data
+ 719   populate data-ah, capacity
+ 720   var _data/eax: (addr array byte) <- lookup *data-ah
+ 721   var data/edi: (addr array byte) <- copy _data
+ 722   var i/ebx: int <- copy 0
+ 723   {
+ 724     compare i, capacity
+ 725     break-if->=
+ 726     next-word in, curr-word
+ 727     var src/eax: int <- parse-decimal-int-from-slice curr-word
+ 728     {
+ 729       var dest/ecx: (addr byte) <- index data, i
+ 730       copy-byte-to *dest, src
+ 731     }
+ 732     i <- increment
+ 733     loop
+ 734   }
+ 735 }
+ 736 
+ 737 # import a color ascii "pixmap" (each pixel consists of 3 shades of r/g/b from 0 to 255)
+ 738 fn render-ppm-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int {
+ 739   var img/esi: (addr image) <- copy _img
+ 740   # yratio = height/img->height
+ 741   var img-height-a/eax: (addr int) <- get img, height
+ 742   var img-height/xmm0: float <- convert *img-height-a
+ 743   var yratio/xmm1: float <- convert height
+ 744   yratio <- divide img-height
+ 745   # xratio = width/img->width
+ 746   var img-width-a/eax: (addr int) <- get img, width
+ 747   var img-width/ebx: int <- copy *img-width-a
+ 748   var img-width-f/xmm0: float <- convert img-width
+ 749   var xratio/xmm2: float <- convert width
+ 750   xratio <- divide img-width-f
+ 751   # esi = img->data
+ 752   var img-data-ah/eax: (addr handle array byte) <- get img, data
+ 753   var _img-data/eax: (addr array byte) <- lookup *img-data-ah
+ 754   var img-data/esi: (addr array byte) <- copy _img-data
+ 755   var len/edi: int <- length img-data
+ 756   #
+ 757   var one/eax: int <- copy 1
+ 758   var one-f/xmm3: float <- convert one
+ 759   var width-f/xmm4: float <- convert width
+ 760   var height-f/xmm5: float <- convert height
+ 761   var zero/eax: int <- copy 0
+ 762   var zero-f/xmm0: float <- convert zero
+ 763   var y/xmm6: float <- copy zero-f
+ 764   {
+ 765     compare y, height-f
+ 766     break-if-float>=
+ 767     var imgy-f/xmm5: float <- copy y
+ 768     imgy-f <- divide yratio
+ 769     var imgy/edx: int <- truncate imgy-f
+ 770     var x/xmm7: float <- copy zero-f
+ 771     {
+ 772       compare x, width-f
+ 773       break-if-float>=
+ 774       var imgx-f/xmm5: float <- copy x
+ 775       imgx-f <- divide xratio
+ 776       var imgx/ecx: int <- truncate imgx-f
+ 777       var idx/eax: int <- copy imgy
+ 778       idx <- multiply img-width
+ 779       idx <- add imgx
+ 780       # . multiply by 3 for the r/g/b channels
+ 781       {
+ 782         var tmp/ecx: int <- copy idx
+ 783         tmp <- shift-left 1
+ 784         idx <- add tmp
+ 785       }
+ 786       # error info in case we rounded wrong and 'index' will fail bounds-check
+ 787       compare idx, len
+ 788       {
+ 789         break-if-<
+ 790         set-cursor-position 0/screen, 0x20/x 0x20/y
+ 791         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg
+ 792         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg
+ 793         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg
+ 794       }
+ 795       # r channel
+ 796       var r: int
+ 797       {
+ 798         var src-a/eax: (addr byte) <- index img-data, idx
+ 799         var src/eax: byte <- copy-byte *src-a
+ 800         copy-to r, src
+ 801       }
+ 802       idx <- increment
+ 803       # g channel
+ 804       var g: int
+ 805       {
+ 806         var src-a/eax: (addr byte) <- index img-data, idx
+ 807         var src/eax: byte <- copy-byte *src-a
+ 808         copy-to g, src
+ 809       }
+ 810       idx <- increment
+ 811       # b channel
+ 812       var b: int
+ 813       {
+ 814         var src-a/eax: (addr byte) <- index img-data, idx
+ 815         var src/eax: byte <- copy-byte *src-a
+ 816         copy-to b, src
+ 817       }
+ 818       idx <- increment
+ 819       # plot nearest color
+ 820       var color/eax: int <- nearest-color-euclidean r, g, b
+ 821       var screenx/ecx: int <- convert x
+ 822       screenx <- add xmin
+ 823       var screeny/edx: int <- convert y
+ 824       screeny <- add ymin
+ 825       pixel screen, screenx, screeny, color
+ 826       x <- add one-f
+ 827       loop
+ 828     }
+ 829     y <- add one-f
+ 830     loop
+ 831   }
+ 832 }
+ 833 
+ 834 fn dither-ppm-unordered _src: (addr image), _dest: (addr image) {
+ 835   var src/esi: (addr image) <- copy _src
+ 836   var dest/edi: (addr image) <- copy _dest
+ 837   # copy 'width'
+ 838   var src-width-a/eax: (addr int) <- get src, width
+ 839   var tmp/eax: int <- copy *src-width-a
+ 840   var src-width: int
+ 841   copy-to src-width, tmp
+ 842   {
+ 843     var dest-width-a/edx: (addr int) <- get dest, width
+ 844     copy-to *dest-width-a, tmp
+ 845   }
+ 846   # copy 'height'
+ 847   var src-height-a/eax: (addr int) <- get src, height
+ 848   var tmp/eax: int <- copy *src-height-a
+ 849   var src-height: int
+ 850   copy-to src-height, tmp
+ 851   {
+ 852     var dest-height-a/ecx: (addr int) <- get dest, height
+ 853     copy-to *dest-height-a, tmp
+ 854   }
+ 855   # compute scaling factor 255/max
+ 856   var target-scale/eax: int <- copy 0xff
+ 857   var scale-f/xmm7: float <- convert target-scale
+ 858   var src-max-a/eax: (addr int) <- get src, max
+ 859   var tmp-f/xmm0: float <- convert *src-max-a
+ 860   scale-f <- divide tmp-f
+ 861   # allocate 'data'
+ 862   var capacity/ebx: int <- copy src-width
+ 863   capacity <- multiply src-height
+ 864   var dest/edi: (addr image) <- copy _dest
+ 865   var dest-data-ah/eax: (addr handle array byte) <- get dest, data
+ 866   populate dest-data-ah, capacity
+ 867   var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah
+ 868   var dest-data/edi: (addr array byte) <- copy _dest-data
+ 869   # error buffers per r/g/b channel
+ 870   var red-errors-storage: (array int 0xc0000)
+ 871   var tmp/eax: (addr array int) <- address red-errors-storage
+ 872   var red-errors: (addr array int)
+ 873   copy-to red-errors, tmp
+ 874   var green-errors-storage: (array int 0xc0000)
+ 875   var tmp/eax: (addr array int) <- address green-errors-storage
+ 876   var green-errors: (addr array int)
+ 877   copy-to green-errors, tmp
+ 878   var blue-errors-storage: (array int 0xc0000)
+ 879   var tmp/eax: (addr array int) <- address blue-errors-storage
+ 880   var blue-errors: (addr array int)
+ 881   copy-to blue-errors, tmp
+ 882   # transform 'data'
+ 883   var src-data-ah/eax: (addr handle array byte) <- get src, data
+ 884   var _src-data/eax: (addr array byte) <- lookup *src-data-ah
+ 885   var src-data/esi: (addr array byte) <- copy _src-data
+ 886   var y/edx: int <- copy 0
+ 887   {
+ 888     compare y, src-height
+ 889     break-if->=
+ 890     var x/ecx: int <- copy 0
+ 891     {
+ 892       compare x, src-width
+ 893       break-if->=
+ 894       # - update errors and compute color levels for current pixel in each channel
+ 895       # update red-error with current image pixel
+ 896       var red-error: int
+ 897       {
+ 898         var tmp/esi: int <- _read-dithering-error red-errors, x, y, src-width
+ 899         copy-to red-error, tmp
+ 900       }
+ 901       {
+ 902         var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 0/red, scale-f
+ 903         add-to red-error, tmp
+ 904       }
+ 905       # recompute red channel for current pixel
+ 906       var red-level: int
+ 907       {
+ 908         var tmp/eax: int <- _error-to-ppm-channel red-error
+ 909         copy-to red-level, tmp
+ 910       }
+ 911       # update green-error with current image pixel
+ 912       var green-error: int
+ 913       {
+ 914         var tmp/esi: int <- _read-dithering-error green-errors, x, y, src-width
+ 915         copy-to green-error, tmp
+ 916       }
+ 917       {
+ 918         var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 1/green, scale-f
+ 919         add-to green-error, tmp
+ 920       }
+ 921       # recompute green channel for current pixel
+ 922       var green-level: int
+ 923       {
+ 924         var tmp/eax: int <- _error-to-ppm-channel green-error
+ 925         copy-to green-level, tmp
+ 926       }
+ 927       # update blue-error with current image pixel
+ 928       var blue-error: int
+ 929       {
+ 930         var tmp/esi: int <- _read-dithering-error blue-errors, x, y, src-width
+ 931         copy-to blue-error, tmp
+ 932       }
+ 933       {
+ 934         var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 2/blue, scale-f
+ 935         add-to blue-error, tmp
+ 936       }
+ 937       # recompute blue channel for current pixel
+ 938       var blue-level: int
+ 939       {
+ 940         var tmp/eax: int <- _error-to-ppm-channel blue-error
+ 941         copy-to blue-level, tmp
+ 942       }
+ 943       # - figure out the nearest color
+ 944       var nearest-color-index/eax: int <- nearest-color-euclidean red-level, green-level, blue-level
+ 945       {
+ 946         var nearest-color-index-byte/eax: byte <- copy-byte nearest-color-index
+ 947         _write-raw-buffer dest-data, x, y, src-width, nearest-color-index-byte
+ 948       }
+ 949       # - diffuse errors
+ 950       var red-level: int
+ 951       var green-level: int
+ 952       var blue-level: int
+ 953       {
+ 954         var tmp-red-level/ecx: int <- copy 0
+ 955         var tmp-green-level/edx: int <- copy 0
+ 956         var tmp-blue-level/ebx: int <- copy 0
+ 957         tmp-red-level, tmp-green-level, tmp-blue-level <- color-rgb nearest-color-index
+ 958         copy-to red-level, tmp-red-level
+ 959         copy-to green-level, tmp-green-level
+ 960         copy-to blue-level, tmp-blue-level
+ 961       }
+ 962       # update red-error
+ 963       var red-level-error/eax: int <- copy red-level
+ 964       red-level-error <- shift-left 0x10
+ 965       subtract-from red-error, red-level-error
+ 966       _diffuse-dithering-error-floyd-steinberg red-errors, x, y, src-width, src-height, red-error
+ 967       # update green-error
+ 968       var green-level-error/eax: int <- copy green-level
+ 969       green-level-error <- shift-left 0x10
+ 970       subtract-from green-error, green-level-error
+ 971       _diffuse-dithering-error-floyd-steinberg green-errors, x, y, src-width, src-height, green-error
+ 972       # update blue-error
+ 973       var blue-level-error/eax: int <- copy blue-level
+ 974       blue-level-error <- shift-left 0x10
+ 975       subtract-from blue-error, blue-level-error
+ 976       _diffuse-dithering-error-floyd-steinberg blue-errors, x, y, src-width, src-height, blue-error
+ 977       #
+ 978       x <- increment
+ 979       loop
+ 980     }
+ 981     y <- increment
+ 982     loop
+ 983   }
+ 984 }
+ 985 
+ 986 # convert a single channel for a single image pixel to error space
+ 987 fn _ppm-error buf: (addr array byte), x: int, y: int, width: int, channel: int, _scale-f: float -> _/eax: int {
+ 988   # current image pixel
+ 989   var initial-level/eax: byte <- _read-ppm-buffer buf, x, y, width, channel
+ 990   # scale to 255 levels
+ 991   var initial-level-int/eax: int <- copy initial-level
+ 992   var initial-level-f/xmm0: float <- convert initial-level-int
+ 993   var scale-f/xmm1: float <- copy _scale-f
+ 994   initial-level-f <- multiply scale-f
+ 995   initial-level-int <- convert initial-level-f
+ 996   # switch to fixed-point with 16 bits of precision
+ 997   initial-level-int <- shift-left 0x10
+ 998   return initial-level-int
+ 999 }
+1000 
+1001 fn _error-to-ppm-channel error: int -> _/eax: int {
+1002   # clamp(error >> 16)
+1003   var result/esi: int <- copy error
+1004   result <- shift-right-signed 0x10
+1005   {
+1006     compare result, 0
+1007     break-if->=
+1008     result <- copy 0
+1009   }
+1010   {
+1011     compare result, 0xff
+1012     break-if-<=
+1013     result <- copy 0xff
+1014   }
+1015   return result
+1016 }
+1017 
+1018 # read from a buffer containing alternating bytes from r/g/b channels
+1019 fn _read-ppm-buffer _buf: (addr array byte), x: int, y: int, width: int, channel: int -> _/eax: byte {
+1020   var buf/esi: (addr array byte) <- copy _buf
+1021   var idx/ecx: int <- copy y
+1022   idx <- multiply width
+1023   idx <- add x
+1024   var byte-idx/edx: int <- copy 3
+1025   byte-idx <- multiply idx
+1026   byte-idx <- add channel
+1027   var result-a/eax: (addr byte) <- index buf, byte-idx
+1028   var result/eax: byte <- copy-byte *result-a
+1029   return result
+1030 }
+1031 
+1032 # each byte in the image data is a color of the current palette
+1033 fn render-raw-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int {
+1034   var img/esi: (addr image) <- copy _img
+1035   # yratio = height/img->height
+1036   var img-height-a/eax: (addr int) <- get img, height
+1037   var img-height/xmm0: float <- convert *img-height-a
+1038   var yratio/xmm1: float <- convert height
+1039   yratio <- divide img-height
+1040   # xratio = width/img->width
+1041   var img-width-a/eax: (addr int) <- get img, width
+1042   var img-width/ebx: int <- copy *img-width-a
+1043   var img-width-f/xmm0: float <- convert img-width
+1044   var xratio/xmm2: float <- convert width
+1045   xratio <- divide img-width-f
+1046   # esi = img->data
+1047   var img-data-ah/eax: (addr handle array byte) <- get img, data
+1048   var _img-data/eax: (addr array byte) <- lookup *img-data-ah
+1049   var img-data/esi: (addr array byte) <- copy _img-data
+1050   var len/edi: int <- length img-data
+1051   #
+1052   var one/eax: int <- copy 1
+1053   var one-f/xmm3: float <- convert one
+1054   var width-f/xmm4: float <- convert width
+1055   var height-f/xmm5: float <- convert height
+1056   var zero/eax: int <- copy 0
+1057   var zero-f/xmm0: float <- convert zero
+1058   var y/xmm6: float <- copy zero-f
+1059   {
+1060     compare y, height-f
+1061     break-if-float>=
+1062     var imgy-f/xmm5: float <- copy y
+1063     imgy-f <- divide yratio
+1064     var imgy/edx: int <- truncate imgy-f
+1065     var x/xmm7: float <- copy zero-f
+1066     {
+1067       compare x, width-f
+1068       break-if-float>=
+1069       var imgx-f/xmm5: float <- copy x
+1070       imgx-f <- divide xratio
+1071       var imgx/ecx: int <- truncate imgx-f
+1072       var idx/eax: int <- copy imgy
+1073       idx <- multiply img-width
+1074       idx <- add imgx
+1075       # error info in case we rounded wrong and 'index' will fail bounds-check
+1076       compare idx, len
+1077       {
+1078         break-if-<
+1079         set-cursor-position 0/screen, 0x20/x 0x20/y
+1080         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg
+1081         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg
+1082         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg
+1083       }
+1084       var color-a/eax: (addr byte) <- index img-data, idx
+1085       var color/eax: byte <- copy-byte *color-a
+1086       var color-int/eax: int <- copy color
+1087       var screenx/ecx: int <- convert x
+1088       screenx <- add xmin
+1089       var screeny/edx: int <- convert y
+1090       screeny <- add ymin
+1091       pixel screen, screenx, screeny, color-int
+1092       x <- add one-f
+1093       loop
+1094     }
+1095     y <- add one-f
+1096     loop
+1097   }
+1098 }
+
+ + + diff --git a/html/apps/ex13.mu.html b/html/apps/ex13.mu.html new file mode 100644 index 00000000..a5fc7c5e --- /dev/null +++ b/html/apps/ex13.mu.html @@ -0,0 +1,87 @@ + + + + +Mu - apps/ex13.mu + + + + + + + + + + +https://github.com/akkartik/mu/blob/main/apps/ex13.mu +
+ 1 # Load an image from disk and display it on screen.
+ 2 #
+ 3 # Build the code disk:
+ 4 #   $ ./translate apps/ex13.mu                       # generates code.img
+ 5 # Load a pbm, pgm or ppm image (no more than 255 levels) in the data disk
+ 6 #   $ dd if=/dev/zero of=data.img count=20160
+ 7 #   $ dd if=___ of=data.img conv=notrunc
+ 8 # Run:
+ 9 #   $ qemu-system-i386 -hda code.img -hdb data.img
+10 
+11 fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
+12   var img-storage: image
+13   var img/esi: (addr image) <- address img-storage
+14   load-image img, data-disk
+15   render-image screen, img, 0/x, 0/y, 0x300/width, 0x300/height
+16 }
+17 
+18 fn load-image self: (addr image), data-disk: (addr disk) {
+19   var s-storage: (stream byte 0x200000)  # 512* 0x1000 sectors
+20   var s/ebx: (addr stream byte) <- address s-storage
+21   load-sectors data-disk, 0/lba, 0x1000/sectors, s
+22   initialize-image self, s
+23 }
+
+ + + diff --git a/html/apps/ex9.mu.html b/html/apps/ex9.mu.html index 54a158b2..6fa4649b 100644 --- a/html/apps/ex9.mu.html +++ b/html/apps/ex9.mu.html @@ -78,14 +78,14 @@ if ('onhashchange' in window) { 18 fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) { 19 var text-storage: (stream byte 0x200) 20 var text/esi: (addr stream byte) <- address text-storage -21 load-sectors data-disk, 0/lba, 1/num-sectors, text +21 load-sectors data-disk, 0/lba, 1/num-sectors, text 22 23 var word-count/eax: int <- word-count text 24 25 var result-storage: (stream byte 0x10) 26 var result/edi: (addr stream byte) <- address result-storage 27 write-int32-decimal result, word-count -28 store-sectors data-disk, 0/lba, 1/num-sectors, result +28 store-sectors data-disk, 0/lba, 1/num-sectors, result 29 } 30 31 fn word-count in: (addr stream byte) -> _/eax: int { diff --git a/html/apps/img.mu.html b/html/apps/img.mu.html deleted file mode 100644 index e3117aa2..00000000 --- a/html/apps/img.mu.html +++ /dev/null @@ -1,1217 +0,0 @@ - - - - -Mu - apps/img.mu - - - - - - - - - - -https://github.com/akkartik/mu/blob/main/apps/img.mu -
-   1 # load an image from disk and display it on screen
-   2 #
-   3 # To build:
-   4 #   $ ./translate apps/img.mu                       # generates code.img
-   5 # Load a pbm, pgm or ppm image (no more than 255 levels) in the data disk
-   6 #   $ dd if=/dev/zero of=data.img count=20160
-   7 #   $ dd if=x.pbm of=data.img conv=notrunc
-   8 # or
-   9 #   $ dd if=t.pgm of=data.img conv=notrunc
-  10 # or
-  11 #   $ dd if=snail.ppm of=data.img conv=notrunc
-  12 # To run:
-  13 #   $ qemu-system-i386 -hda code.img -hdb data.img
-  14 
-  15 type image {
-  16   type: int  # supported types:
-  17              #  1: portable bitmap (P1) - pixels 0 or 1
-  18              #  2: portable greymap (P2) - pixels 1-byte greyscale values
-  19              #  3: portable pixmap (P3) - pixels 3-byte rgb values
-  20   max: int
-  21   width: int
-  22   height: int
-  23   data: (handle array byte)
-  24 }
-  25 
-  26 fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
-  27   var img-storage: image
-  28   var img/esi: (addr image) <- address img-storage
-  29   load-image img, data-disk
-  30   render-image screen, img, 0/x, 0/y, 0x300/width, 0x300/height
-  31 }
-  32 
-  33 fn load-image self: (addr image), data-disk: (addr disk) {
-  34   # data-disk -> stream
-  35   var s-storage: (stream byte 0x200000)  # 512* 0x1000 sectors
-  36   var s/ebx: (addr stream byte) <- address s-storage
-  37   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "loading sectors from data disk", 3/fg, 0/bg
-  38   move-cursor-to-left-margin-of-next-line 0/screen
-  39   load-sectors data-disk, 0/lba, 0x100/sectors, s
-  40   load-sectors data-disk, 0x100/lba, 0x100/sectors, s
-  41   load-sectors data-disk, 0x200/lba, 0x100/sectors, s
-  42   load-sectors data-disk, 0x300/lba, 0x100/sectors, s
-  43   load-sectors data-disk, 0x400/lba, 0x100/sectors, s
-  44   load-sectors data-disk, 0x500/lba, 0x100/sectors, s
-  45   load-sectors data-disk, 0x600/lba, 0x100/sectors, s
-  46   load-sectors data-disk, 0x700/lba, 0x100/sectors, s
-  47   load-sectors data-disk, 0x800/lba, 0x100/sectors, s
-  48   load-sectors data-disk, 0x900/lba, 0x100/sectors, s
-  49   load-sectors data-disk, 0xa00/lba, 0x100/sectors, s
-  50   load-sectors data-disk, 0xb00/lba, 0x100/sectors, s
-  51   load-sectors data-disk, 0xc00/lba, 0x100/sectors, s
-  52   load-sectors data-disk, 0xd00/lba, 0x100/sectors, s
-  53   load-sectors data-disk, 0xe00/lba, 0x100/sectors, s
-  54   load-sectors data-disk, 0xf00/lba, 0x100/sectors, s
-  55   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, "parsing", 3/fg, 0/bg
-  56   move-cursor-to-left-margin-of-next-line 0/screen
-  57   initialize-image self, s
-  58 }
-  59 
-  60 fn initialize-image _self: (addr image), in: (addr stream byte) {
-  61   var self/esi: (addr image) <- copy _self
-  62   var mode-storage: slice
-  63   var mode/ecx: (addr slice) <- address mode-storage
-  64   next-word in, mode
-  65   {
-  66     var P1?/eax: boolean <- slice-equal? mode, "P1"
-  67     compare P1?, 0/false
-  68     break-if-=
-  69     var type-a/eax: (addr int) <- get self, type
-  70     copy-to *type-a, 1/ppm
-  71     initialize-image-from-pbm self, in
-  72     return
-  73   }
-  74   {
-  75     var P2?/eax: boolean <- slice-equal? mode, "P2"
-  76     compare P2?, 0/false
-  77     break-if-=
-  78     var type-a/eax: (addr int) <- get self, type
-  79     copy-to *type-a, 2/pgm
-  80     initialize-image-from-pgm self, in
-  81     return
-  82   }
-  83   {
-  84     var P3?/eax: boolean <- slice-equal? mode, "P3"
-  85     compare P3?, 0/false
-  86     break-if-=
-  87     var type-a/eax: (addr int) <- get self, type
-  88     copy-to *type-a, 3/ppm
-  89     initialize-image-from-ppm self, in
-  90     return
-  91   }
-  92   abort "initialize-image: unrecognized image type"
-  93 }
-  94 
-  95 # dispatch to a few variants with mostly identical boilerplate
-  96 fn render-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int {
-  97   var img/esi: (addr image) <- copy _img
-  98   var type-a/eax: (addr int) <- get img, type
-  99   {
- 100     compare *type-a, 1/pbm
- 101     break-if-!=
- 102     render-pbm-image screen, img, xmin, ymin, width, height
- 103     return
- 104   }
- 105   {
- 106     compare *type-a, 2/pgm
- 107     break-if-!=
- 108     var img2-storage: image
- 109     var img2/edi: (addr image) <- address img2-storage
- 110     dither-pgm-unordered img, img2
- 111     render-raw-image screen, img2, xmin, ymin, width, height
- 112     return
- 113   }
- 114   {
- 115     compare *type-a, 3/ppm
- 116     break-if-!=
- 117     var img2-storage: image
- 118     var img2/edi: (addr image) <- address img2-storage
- 119     dither-ppm-unordered img, img2
- 120     render-raw-image screen, img2, xmin, ymin, width, height
- 121     return
- 122   }
- 123   abort "render-image: unrecognized image type"
- 124 }
- 125 
- 126 ## helpers
- 127 
- 128 # import a black-and-white ascii bitmap (each pixel is 0 or 1)
- 129 fn initialize-image-from-pbm _self: (addr image), in: (addr stream byte) {
- 130   var self/esi: (addr image) <- copy _self
- 131   var curr-word-storage: slice
- 132   var curr-word/ecx: (addr slice) <- address curr-word-storage
- 133   # load width, height
- 134   next-word in, curr-word
- 135   var tmp/eax: int <- parse-decimal-int-from-slice curr-word
- 136   var width/edx: int <- copy tmp
- 137   next-word in, curr-word
- 138   tmp <- parse-decimal-int-from-slice curr-word
- 139   var height/ebx: int <- copy tmp
- 140   # save width, height
- 141   var dest/eax: (addr int) <- get self, width
- 142   copy-to *dest, width
- 143   dest <- get self, height
- 144   copy-to *dest, height
- 145   # initialize data
- 146   var capacity/edx: int <- copy width
- 147   capacity <- multiply height
- 148   var data-ah/edi: (addr handle array byte) <- get self, data
- 149   populate data-ah, capacity
- 150   var _data/eax: (addr array byte) <- lookup *data-ah
- 151   var data/edi: (addr array byte) <- copy _data
- 152   var i/ebx: int <- copy 0
- 153   {
- 154     compare i, capacity
- 155     break-if->=
- 156     next-word in, curr-word
- 157     var src/eax: int <- parse-decimal-int-from-slice curr-word
- 158     {
- 159       var dest/ecx: (addr byte) <- index data, i
- 160       copy-byte-to *dest, src
- 161     }
- 162     i <- increment
- 163     loop
- 164   }
- 165 }
- 166 
- 167 # render a black-and-white ascii bitmap (each pixel is 0 or 1)
- 168 fn render-pbm-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int {
- 169   var img/esi: (addr image) <- copy _img
- 170   # yratio = height/img->height
- 171   var img-height-a/eax: (addr int) <- get img, height
- 172   var img-height/xmm0: float <- convert *img-height-a
- 173   var yratio/xmm1: float <- convert height
- 174   yratio <- divide img-height
- 175   # xratio = width/img->width
- 176   var img-width-a/eax: (addr int) <- get img, width
- 177   var img-width/ebx: int <- copy *img-width-a
- 178   var img-width-f/xmm0: float <- convert img-width
- 179   var xratio/xmm2: float <- convert width
- 180   xratio <- divide img-width-f
- 181   # esi = img->data
- 182   var img-data-ah/eax: (addr handle array byte) <- get img, data
- 183   var _img-data/eax: (addr array byte) <- lookup *img-data-ah
- 184   var img-data/esi: (addr array byte) <- copy _img-data
- 185   var len/edi: int <- length img-data
- 186   #
- 187   var one/eax: int <- copy 1
- 188   var one-f/xmm3: float <- convert one
- 189   var width-f/xmm4: float <- convert width
- 190   var height-f/xmm5: float <- convert height
- 191   var zero/eax: int <- copy 0
- 192   var zero-f/xmm0: float <- convert zero
- 193   var y/xmm6: float <- copy zero-f
- 194   {
- 195     compare y, height-f
- 196     break-if-float>=
- 197     var imgy-f/xmm5: float <- copy y
- 198     imgy-f <- divide yratio
- 199     var imgy/edx: int <- truncate imgy-f
- 200     var x/xmm7: float <- copy zero-f
- 201     {
- 202       compare x, width-f
- 203       break-if-float>=
- 204       var imgx-f/xmm5: float <- copy x
- 205       imgx-f <- divide xratio
- 206       var imgx/ecx: int <- truncate imgx-f
- 207       var idx/eax: int <- copy imgy
- 208       idx <- multiply img-width
- 209       idx <- add imgx
- 210       # error info in case we rounded wrong and 'index' will fail bounds-check
- 211       compare idx, len
- 212       {
- 213         break-if-<
- 214         set-cursor-position 0/screen, 0x20/x 0x20/y
- 215         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg
- 216         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg
- 217         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg
- 218       }
- 219       var src-a/eax: (addr byte) <- index img-data, idx
- 220       var src/eax: byte <- copy-byte *src-a
- 221       var color-int/eax: int <- copy src
- 222       {
- 223         compare color-int, 0/black
- 224         break-if-=
- 225         color-int <- copy 0xf/white
- 226       }
- 227       var screenx/ecx: int <- convert x
- 228       screenx <- add xmin
- 229       var screeny/edx: int <- convert y
- 230       screeny <- add ymin
- 231       pixel screen, screenx, screeny, color-int
- 232       x <- add one-f
- 233       loop
- 234     }
- 235     y <- add one-f
- 236     loop
- 237   }
- 238 }
- 239 
- 240 # import a greyscale ascii "greymap" (each pixel is a shade of grey from 0 to 255)
- 241 fn initialize-image-from-pgm _self: (addr image), in: (addr stream byte) {
- 242   var self/esi: (addr image) <- copy _self
- 243   var curr-word-storage: slice
- 244   var curr-word/ecx: (addr slice) <- address curr-word-storage
- 245   # load width, height
- 246   next-word in, curr-word
- 247   var tmp/eax: int <- parse-decimal-int-from-slice curr-word
- 248   var width/edx: int <- copy tmp
- 249   next-word in, curr-word
- 250   tmp <- parse-decimal-int-from-slice curr-word
- 251   var height/ebx: int <- copy tmp
- 252   # check and save color levels
- 253   next-word in, curr-word
- 254   {
- 255     tmp <- parse-decimal-int-from-slice curr-word
- 256     compare tmp, 0xff
- 257     break-if-=
- 258     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
- 259   }
- 260   var dest/edi: (addr int) <- get self, max
- 261   copy-to *dest, tmp
- 262   # save width, height
- 263   dest <- get self, width
- 264   copy-to *dest, width
- 265   dest <- get self, height
- 266   copy-to *dest, height
- 267   # initialize data
- 268   var capacity/edx: int <- copy width
- 269   capacity <- multiply height
- 270   var data-ah/edi: (addr handle array byte) <- get self, data
- 271   populate data-ah, capacity
- 272   var _data/eax: (addr array byte) <- lookup *data-ah
- 273   var data/edi: (addr array byte) <- copy _data
- 274   var i/ebx: int <- copy 0
- 275   {
- 276     compare i, capacity
- 277     break-if->=
- 278     next-word in, curr-word
- 279     var src/eax: int <- parse-decimal-int-from-slice curr-word
- 280     {
- 281       var dest/ecx: (addr byte) <- index data, i
- 282       copy-byte-to *dest, src
- 283     }
- 284     i <- increment
- 285     loop
- 286   }
- 287 }
- 288 
- 289 # render a greyscale ascii "greymap" (each pixel is a shade of grey from 0 to 255) by quantizing the shades
- 290 fn render-pgm-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int {
- 291   var img/esi: (addr image) <- copy _img
- 292   # yratio = height/img->height
- 293   var img-height-a/eax: (addr int) <- get img, height
- 294   var img-height/xmm0: float <- convert *img-height-a
- 295   var yratio/xmm1: float <- convert height
- 296   yratio <- divide img-height
- 297   # xratio = width/img->width
- 298   var img-width-a/eax: (addr int) <- get img, width
- 299   var img-width/ebx: int <- copy *img-width-a
- 300   var img-width-f/xmm0: float <- convert img-width
- 301   var xratio/xmm2: float <- convert width
- 302   xratio <- divide img-width-f
- 303   # esi = img->data
- 304   var img-data-ah/eax: (addr handle array byte) <- get img, data
- 305   var _img-data/eax: (addr array byte) <- lookup *img-data-ah
- 306   var img-data/esi: (addr array byte) <- copy _img-data
- 307   var len/edi: int <- length img-data
- 308   #
- 309   var one/eax: int <- copy 1
- 310   var one-f/xmm3: float <- convert one
- 311   var width-f/xmm4: float <- convert width
- 312   var height-f/xmm5: float <- convert height
- 313   var zero/eax: int <- copy 0
- 314   var zero-f/xmm0: float <- convert zero
- 315   var y/xmm6: float <- copy zero-f
- 316   {
- 317     compare y, height-f
- 318     break-if-float>=
- 319     var imgy-f/xmm5: float <- copy y
- 320     imgy-f <- divide yratio
- 321     var imgy/edx: int <- truncate imgy-f
- 322     var x/xmm7: float <- copy zero-f
- 323     {
- 324       compare x, width-f
- 325       break-if-float>=
- 326       var imgx-f/xmm5: float <- copy x
- 327       imgx-f <- divide xratio
- 328       var imgx/ecx: int <- truncate imgx-f
- 329       var idx/eax: int <- copy imgy
- 330       idx <- multiply img-width
- 331       idx <- add imgx
- 332       # error info in case we rounded wrong and 'index' will fail bounds-check
- 333       compare idx, len
- 334       {
- 335         break-if-<
- 336         set-cursor-position 0/screen, 0x20/x 0x20/y
- 337         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg
- 338         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg
- 339         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg
- 340       }
- 341       var src-a/eax: (addr byte) <- index img-data, idx
- 342       var src/eax: byte <- copy-byte *src-a
- 343       var color-int/eax: int <- nearest-grey src
- 344       var screenx/ecx: int <- convert x
- 345       screenx <- add xmin
- 346       var screeny/edx: int <- convert y
- 347       screeny <- add ymin
- 348       pixel screen, screenx, screeny, color-int
- 349       x <- add one-f
- 350       loop
- 351     }
- 352     y <- add one-f
- 353     loop
- 354   }
- 355 }
- 356 
- 357 fn nearest-grey level-255: byte -> _/eax: int {
- 358   var result/eax: int <- copy level-255
- 359   result <- shift-right 4
- 360   result <- add 0x10
- 361   return result
- 362 }
- 363 
- 364 fn dither-pgm-unordered-monochrome _src: (addr image), _dest: (addr image) {
- 365   var src/esi: (addr image) <- copy _src
- 366   var dest/edi: (addr image) <- copy _dest
- 367   # copy 'width'
- 368   var src-width-a/eax: (addr int) <- get src, width
- 369   var tmp/eax: int <- copy *src-width-a
- 370   var src-width: int
- 371   copy-to src-width, tmp
- 372   {
- 373     var dest-width-a/edx: (addr int) <- get dest, width
- 374     copy-to *dest-width-a, tmp
- 375   }
- 376   # copy 'height'
- 377   var src-height-a/eax: (addr int) <- get src, height
- 378   var tmp/eax: int <- copy *src-height-a
- 379   var src-height: int
- 380   copy-to src-height, tmp
- 381   {
- 382     var dest-height-a/ecx: (addr int) <- get dest, height
- 383     copy-to *dest-height-a, tmp
- 384   }
- 385   # transform 'data'
- 386   var capacity/ebx: int <- copy src-width
- 387   capacity <- multiply src-height
- 388   var dest/edi: (addr image) <- copy _dest
- 389   var dest-data-ah/eax: (addr handle array byte) <- get dest, data
- 390   populate dest-data-ah, capacity
- 391   var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah
- 392   var dest-data/edi: (addr array byte) <- copy _dest-data
- 393   # needs a buffer to temporarily hold more than 256 levels of precision
- 394   var errors-storage: (array int 0xc0000)
- 395   var errors/ebx: (addr array int) <- address errors-storage
- 396   var src-data-ah/eax: (addr handle array byte) <- get src, data
- 397   var _src-data/eax: (addr array byte) <- lookup *src-data-ah
- 398   var src-data/esi: (addr array byte) <- copy _src-data
- 399   var y/edx: int <- copy 0
- 400   {
- 401     compare y, src-height
- 402     break-if->=
- 403     var x/ecx: int <- copy 0
- 404     {
- 405       compare x, src-width
- 406       break-if->=
- 407       var curr/eax: byte <- _read-pgm-buffer src-data, x, y, src-width
- 408       var curr-int/eax: int <- copy curr
- 409       curr-int <- shift-left 0x10  # we have 32 bits; we'll use 16 bits for the fraction and leave 8 for unanticipated overflow
- 410       var error/esi: int <- _read-dithering-error errors, x, y, src-width
- 411       error <- add curr-int
- 412       $_dither-pgm-unordered-monochrome:update-error: {
- 413         compare error, 0x800000
- 414         {
- 415           break-if->=
- 416           _write-raw-buffer dest-data, x, y, src-width, 0/black
- 417           break $_dither-pgm-unordered-monochrome:update-error
- 418         }
- 419         _write-raw-buffer dest-data, x, y, src-width, 1/white
- 420         error <- subtract 0xff0000
- 421       }
- 422       _diffuse-dithering-error-floyd-steinberg errors, x, y, src-width, src-height, error
- 423       x <- increment
- 424       loop
- 425     }
- 426     move-cursor-to-left-margin-of-next-line 0/screen
- 427     y <- increment
- 428     loop
- 429   }
- 430 }
- 431 
- 432 fn dither-pgm-unordered _src: (addr image), _dest: (addr image) {
- 433   var src/esi: (addr image) <- copy _src
- 434   var dest/edi: (addr image) <- copy _dest
- 435   # copy 'width'
- 436   var src-width-a/eax: (addr int) <- get src, width
- 437   var tmp/eax: int <- copy *src-width-a
- 438   var src-width: int
- 439   copy-to src-width, tmp
- 440   {
- 441     var dest-width-a/edx: (addr int) <- get dest, width
- 442     copy-to *dest-width-a, tmp
- 443   }
- 444   # copy 'height'
- 445   var src-height-a/eax: (addr int) <- get src, height
- 446   var tmp/eax: int <- copy *src-height-a
- 447   var src-height: int
- 448   copy-to src-height, tmp
- 449   {
- 450     var dest-height-a/ecx: (addr int) <- get dest, height
- 451     copy-to *dest-height-a, tmp
- 452   }
- 453   # compute scaling factor 255/max
- 454   var target-scale/eax: int <- copy 0xff
- 455   var scale-f/xmm7: float <- convert target-scale
- 456   var src-max-a/eax: (addr int) <- get src, max
- 457   var tmp-f/xmm0: float <- convert *src-max-a
- 458   scale-f <- divide tmp-f
- 459   # transform 'data'
- 460   var capacity/ebx: int <- copy src-width
- 461   capacity <- multiply src-height
- 462   var dest/edi: (addr image) <- copy _dest
- 463   var dest-data-ah/eax: (addr handle array byte) <- get dest, data
- 464   populate dest-data-ah, capacity
- 465   var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah
- 466   var dest-data/edi: (addr array byte) <- copy _dest-data
- 467   # needs a buffer to temporarily hold more than 256 levels of precision
- 468   var errors-storage: (array int 0xc0000)
- 469   var errors/ebx: (addr array int) <- address errors-storage
- 470   var src-data-ah/eax: (addr handle array byte) <- get src, data
- 471   var _src-data/eax: (addr array byte) <- lookup *src-data-ah
- 472   var src-data/esi: (addr array byte) <- copy _src-data
- 473   var y/edx: int <- copy 0
- 474   {
- 475     compare y, src-height
- 476     break-if->=
- 477     var x/ecx: int <- copy 0
- 478     {
- 479       compare x, src-width
- 480       break-if->=
- 481       var initial-color/eax: byte <- _read-pgm-buffer src-data, x, y, src-width
- 482       # . scale to 255 levels
- 483       var initial-color-int/eax: int <- copy initial-color
- 484       var initial-color-f/xmm0: float <- convert initial-color-int
- 485       initial-color-f <- multiply scale-f
- 486       initial-color-int <- convert initial-color-f
- 487       var error/esi: int <- _read-dithering-error errors, x, y, src-width
- 488       # error += (initial-color << 16)
- 489       {
- 490         var tmp/eax: int <- copy initial-color-int
- 491         tmp <- shift-left 0x10  # we have 32 bits; we'll use 16 bits for the fraction and leave 8 for unanticipated overflow
- 492         error <- add tmp
- 493       }
- 494       # nearest-color = nearest(error >> 16)
- 495       var nearest-color/eax: int <- copy error
- 496       nearest-color <- shift-right-signed 0x10
- 497       {
- 498         compare nearest-color, 0
- 499         break-if->=
- 500         nearest-color <- copy 0
- 501       }
- 502       {
- 503         compare nearest-color, 0xf0
- 504         break-if-<=
- 505         nearest-color <- copy 0xf0
- 506       }
- 507       # . truncate last 4 bits
- 508       nearest-color <- and 0xf0
- 509       # error -= (nearest-color << 16)
- 510       {
- 511         var tmp/eax: int <- copy nearest-color
- 512         tmp <- shift-left 0x10
- 513         error <- subtract tmp
- 514       }
- 515       # color-index = (nearest-color >> 4 + 16)
- 516       var color-index/eax: int <- copy nearest-color
- 517       color-index <- shift-right 4
- 518       color-index <- add 0x10
- 519       var color-index-byte/eax: byte <- copy-byte color-index
- 520       _write-raw-buffer dest-data, x, y, src-width, color-index-byte
- 521       _diffuse-dithering-error-floyd-steinberg errors, x, y, src-width, src-height, error
- 522       x <- increment
- 523       loop
- 524     }
- 525     y <- increment
- 526     loop
- 527   }
- 528 }
- 529 
- 530 # Use Floyd-Steinberg algorithm for diffusing error at x, y in a 2D grid of
- 531 # dimensions (width, height)
- 532 #
- 533 # https://tannerhelland.com/2012/12/28/dithering-eleven-algorithms-source-code.html
- 534 #
- 535 # Error is currently a fixed-point number with 16-bit fraction. But
- 536 # interestingly this function doesn't care about that.
- 537 fn _diffuse-dithering-error-floyd-steinberg errors: (addr array int), x: int, y: int, width: int, height: int, error: int {
- 538   {
- 539     compare error, 0
- 540     break-if-!=
- 541     return
- 542   }
- 543   var width-1/esi: int <- copy width
- 544   width-1 <- decrement
- 545   var height-1/edi: int <- copy height
- 546   height-1 <- decrement
- 547   # delta = error/16
- 548 #?   show-errors errors, width, height, x, y
- 549   var delta/ecx: int <- copy error
- 550   delta <- shift-right-signed 4
- 551   # In Floyd-Steinberg, each pixel X transmits its errors to surrounding
- 552   # pixels in the following proportion:
- 553   #           X     7/16
- 554   #     3/16  5/16  1/16
- 555   var x/edx: int <- copy x
- 556   {
- 557     compare x, width-1
- 558     break-if->=
- 559     var tmp/eax: int <- copy 7
- 560     tmp <- multiply delta
- 561     var xright/edx: int <- copy x
- 562     xright <- increment
- 563     _accumulate-dithering-error errors, xright, y, width, tmp
- 564   }
- 565   var y/ebx: int <- copy y
- 566   {
- 567     compare y, height-1
- 568     break-if-<
- 569     return
- 570   }
- 571   var ybelow: int
- 572   copy-to ybelow, y
- 573   increment ybelow
- 574   {
- 575     compare x, 0
- 576     break-if-<=
- 577     var tmp/eax: int <- copy 3
- 578     tmp <- multiply delta
- 579     var xleft/edx: int <- copy x
- 580     xleft <- decrement
- 581     _accumulate-dithering-error errors, xleft, ybelow, width, tmp
- 582   }
- 583   {
- 584     var tmp/eax: int <- copy 5
- 585     tmp <- multiply delta
- 586     _accumulate-dithering-error errors, x, ybelow, width, tmp
- 587   }
- 588   {
- 589     compare x, width-1
- 590     break-if->=
- 591     var xright/edx: int <- copy x
- 592     xright <- increment
- 593     _accumulate-dithering-error errors, xright, ybelow, width, delta
- 594   }
- 595 #?   show-errors errors, width, height, x, y
- 596 }
- 597 
- 598 fn _accumulate-dithering-error errors: (addr array int), x: int, y: int, width: int, error: int {
- 599   var curr/esi: int <- _read-dithering-error errors, x, y, width
- 600   curr <- add error
- 601   _write-dithering-error errors, x, y, width, curr
- 602 }
- 603 
- 604 fn _read-dithering-error _errors: (addr array int), x: int, y: int, width: int -> _/esi: int {
- 605   var errors/esi: (addr array int) <- copy _errors
- 606   var idx/ecx: int <- copy y
- 607   idx <- multiply width
- 608   idx <- add x
- 609   var result-a/eax: (addr int) <- index errors, idx
- 610   return *result-a
- 611 }
- 612 
- 613 fn _write-dithering-error _errors: (addr array int), x: int, y: int, width: int, val: int {
- 614   var errors/esi: (addr array int) <- copy _errors
- 615   var idx/ecx: int <- copy y
- 616   idx <- multiply width
- 617   idx <- add x
- 618 #?   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 7/fg 0/bg
- 619 #?   move-cursor-to-left-margin-of-next-line 0/screen
- 620   var src/eax: int <- copy val
- 621   var dest-a/edi: (addr int) <- index errors, idx
- 622   copy-to *dest-a, src
- 623 }
- 624 
- 625 fn _read-pgm-buffer _buf: (addr array byte), x: int, y: int, width: int -> _/eax: byte {
- 626   var buf/esi: (addr array byte) <- copy _buf
- 627   var idx/ecx: int <- copy y
- 628   idx <- multiply width
- 629   idx <- add x
- 630   var result-a/eax: (addr byte) <- index buf, idx
- 631   var result/eax: byte <- copy-byte *result-a
- 632   return result
- 633 }
- 634 
- 635 fn _write-raw-buffer _buf: (addr array byte), x: int, y: int, width: int, val: byte {
- 636   var buf/esi: (addr array byte) <- copy _buf
- 637   var idx/ecx: int <- copy y
- 638   idx <- multiply width
- 639   idx <- add x
- 640   var src/eax: byte <- copy val
- 641   var dest-a/edi: (addr byte) <- index buf, idx
- 642   copy-byte-to *dest-a, src
- 643 }
- 644 
- 645 # some debugging helpers
- 646 fn show-errors errors: (addr array int), width: int, height: int, x: int, y: int {
- 647   compare y, 1
- 648   {
- 649     break-if-=
- 650     return
- 651   }
- 652   compare x, 0
- 653   {
- 654     break-if-=
- 655     return
- 656   }
- 657   var y/edx: int <- copy 0
- 658   {
- 659     compare y, height
- 660     break-if->=
- 661     var x/ecx: int <- copy 0
- 662     {
- 663       compare x, width
- 664       break-if->=
- 665       var error/esi: int <- _read-dithering-error errors, x, y, width
- 666       psd "e", error, 5/fg, x, y
- 667       x <- increment
- 668       loop
- 669     }
- 670     move-cursor-to-left-margin-of-next-line 0/screen
- 671     y <- increment
- 672     loop
- 673   }
- 674 }
- 675 
- 676 fn psd s: (addr array byte), d: int, fg: int, x: int, y: int {
- 677   {
- 678     compare y, 0x18
- 679     break-if->=
- 680     return
- 681   }
- 682   {
- 683     compare y, 0x1c
- 684     break-if-<=
- 685     return
- 686   }
- 687   {
- 688     compare x, 0x40
- 689     break-if->=
- 690     return
- 691   }
- 692 #?   {
- 693 #?     compare x, 0x48
- 694 #?     break-if-<=
- 695 #?     return
- 696 #?   }
- 697   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, s, 7/fg 0/bg
- 698   draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, d, fg 0/bg
- 699 }
- 700 
- 701 fn psx s: (addr array byte), d: int, fg: int, x: int, y: int {
- 702 #?   {
- 703 #?     compare y, 0x60
- 704 #?     break-if->=
- 705 #?     return
- 706 #?   }
- 707 #?   {
- 708 #?     compare y, 0x6c
- 709 #?     break-if-<=
- 710 #?     return
- 711 #?   }
- 712   {
- 713     compare x, 0x20
- 714     break-if->=
- 715     return
- 716   }
- 717 #?   {
- 718 #?     compare x, 0x6c
- 719 #?     break-if-<=
- 720 #?     return
- 721 #?   }
- 722   draw-text-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, s, 7/fg 0/bg
- 723   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, d, fg 0/bg
- 724 }
- 725 
- 726 # import a color ascii "pixmap" (each pixel consists of 3 shades of r/g/b from 0 to 255)
- 727 fn initialize-image-from-ppm _self: (addr image), in: (addr stream byte) {
- 728   var self/esi: (addr image) <- copy _self
- 729   var curr-word-storage: slice
- 730   var curr-word/ecx: (addr slice) <- address curr-word-storage
- 731   # load width, height
- 732   next-word in, curr-word
- 733   var tmp/eax: int <- parse-decimal-int-from-slice curr-word
- 734   var width/edx: int <- copy tmp
- 735   next-word in, curr-word
- 736   tmp <- parse-decimal-int-from-slice curr-word
- 737   var height/ebx: int <- copy tmp
- 738   next-word in, curr-word
- 739   # check color levels
- 740   {
- 741     tmp <- parse-decimal-int-from-slice curr-word
- 742     compare tmp, 0xff
- 743     break-if-=
- 744     abort "initialize-image-from-ppm: supports exactly 255 levels per rgb channel"
- 745   }
- 746   var dest/edi: (addr int) <- get self, max
- 747   copy-to *dest, tmp
- 748   # save width, height
- 749   dest <- get self, width
- 750   copy-to *dest, width
- 751   dest <- get self, height
- 752   copy-to *dest, height
- 753   # initialize data
- 754   var capacity/edx: int <- copy width
- 755   capacity <- multiply height
- 756   # . multiply by 3 for the r/g/b channels
- 757   var tmp/eax: int <- copy capacity
- 758   tmp <- shift-left 1
- 759   capacity <- add tmp
- 760   #
- 761   var data-ah/edi: (addr handle array byte) <- get self, data
- 762   populate data-ah, capacity
- 763   var _data/eax: (addr array byte) <- lookup *data-ah
- 764   var data/edi: (addr array byte) <- copy _data
- 765   var i/ebx: int <- copy 0
- 766   {
- 767     compare i, capacity
- 768     break-if->=
- 769     next-word in, curr-word
- 770     var src/eax: int <- parse-decimal-int-from-slice curr-word
- 771     {
- 772       var dest/ecx: (addr byte) <- index data, i
- 773       copy-byte-to *dest, src
- 774     }
- 775     i <- increment
- 776     loop
- 777   }
- 778 }
- 779 
- 780 # import a color ascii "pixmap" (each pixel consists of 3 shades of r/g/b from 0 to 255)
- 781 fn render-ppm-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int {
- 782   var img/esi: (addr image) <- copy _img
- 783   # yratio = height/img->height
- 784   var img-height-a/eax: (addr int) <- get img, height
- 785   var img-height/xmm0: float <- convert *img-height-a
- 786   var yratio/xmm1: float <- convert height
- 787   yratio <- divide img-height
- 788   # xratio = width/img->width
- 789   var img-width-a/eax: (addr int) <- get img, width
- 790   var img-width/ebx: int <- copy *img-width-a
- 791   var img-width-f/xmm0: float <- convert img-width
- 792   var xratio/xmm2: float <- convert width
- 793   xratio <- divide img-width-f
- 794   # esi = img->data
- 795   var img-data-ah/eax: (addr handle array byte) <- get img, data
- 796   var _img-data/eax: (addr array byte) <- lookup *img-data-ah
- 797   var img-data/esi: (addr array byte) <- copy _img-data
- 798   var len/edi: int <- length img-data
- 799   #
- 800   var one/eax: int <- copy 1
- 801   var one-f/xmm3: float <- convert one
- 802   var width-f/xmm4: float <- convert width
- 803   var height-f/xmm5: float <- convert height
- 804   var zero/eax: int <- copy 0
- 805   var zero-f/xmm0: float <- convert zero
- 806   var y/xmm6: float <- copy zero-f
- 807   {
- 808     compare y, height-f
- 809     break-if-float>=
- 810     var imgy-f/xmm5: float <- copy y
- 811     imgy-f <- divide yratio
- 812     var imgy/edx: int <- truncate imgy-f
- 813     var x/xmm7: float <- copy zero-f
- 814     {
- 815       compare x, width-f
- 816       break-if-float>=
- 817       var imgx-f/xmm5: float <- copy x
- 818       imgx-f <- divide xratio
- 819       var imgx/ecx: int <- truncate imgx-f
- 820       var idx/eax: int <- copy imgy
- 821       idx <- multiply img-width
- 822       idx <- add imgx
- 823       # . multiply by 3 for the r/g/b channels
- 824       {
- 825         var tmp/ecx: int <- copy idx
- 826         tmp <- shift-left 1
- 827         idx <- add tmp
- 828       }
- 829       # error info in case we rounded wrong and 'index' will fail bounds-check
- 830       compare idx, len
- 831       {
- 832         break-if-<
- 833         set-cursor-position 0/screen, 0x20/x 0x20/y
- 834         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg
- 835         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg
- 836         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg
- 837       }
- 838       # r channel
- 839       var r: int
- 840       {
- 841         var src-a/eax: (addr byte) <- index img-data, idx
- 842         var src/eax: byte <- copy-byte *src-a
- 843         copy-to r, src
- 844       }
- 845       idx <- increment
- 846       # g channel
- 847       var g: int
- 848       {
- 849         var src-a/eax: (addr byte) <- index img-data, idx
- 850         var src/eax: byte <- copy-byte *src-a
- 851         copy-to g, src
- 852       }
- 853       idx <- increment
- 854       # b channel
- 855       var b: int
- 856       {
- 857         var src-a/eax: (addr byte) <- index img-data, idx
- 858         var src/eax: byte <- copy-byte *src-a
- 859         copy-to b, src
- 860       }
- 861       idx <- increment
- 862       # plot nearest color
- 863       var color/eax: int <- nearest-color-euclidean r, g, b
- 864       var screenx/ecx: int <- convert x
- 865       screenx <- add xmin
- 866       var screeny/edx: int <- convert y
- 867       screeny <- add ymin
- 868       pixel screen, screenx, screeny, color
- 869       x <- add one-f
- 870       loop
- 871     }
- 872     y <- add one-f
- 873     loop
- 874   }
- 875 }
- 876 
- 877 fn dither-ppm-unordered _src: (addr image), _dest: (addr image) {
- 878   var src/esi: (addr image) <- copy _src
- 879   var dest/edi: (addr image) <- copy _dest
- 880   # copy 'width'
- 881   var src-width-a/eax: (addr int) <- get src, width
- 882   var tmp/eax: int <- copy *src-width-a
- 883   var src-width: int
- 884   copy-to src-width, tmp
- 885   {
- 886     var dest-width-a/edx: (addr int) <- get dest, width
- 887     copy-to *dest-width-a, tmp
- 888   }
- 889   # copy 'height'
- 890   var src-height-a/eax: (addr int) <- get src, height
- 891   var tmp/eax: int <- copy *src-height-a
- 892   var src-height: int
- 893   copy-to src-height, tmp
- 894   {
- 895     var dest-height-a/ecx: (addr int) <- get dest, height
- 896     copy-to *dest-height-a, tmp
- 897   }
- 898   # compute scaling factor 255/max
- 899   var target-scale/eax: int <- copy 0xff
- 900   var scale-f/xmm7: float <- convert target-scale
- 901   var src-max-a/eax: (addr int) <- get src, max
- 902   var tmp-f/xmm0: float <- convert *src-max-a
- 903   scale-f <- divide tmp-f
- 904   # allocate 'data'
- 905   var capacity/ebx: int <- copy src-width
- 906   capacity <- multiply src-height
- 907   var dest/edi: (addr image) <- copy _dest
- 908   var dest-data-ah/eax: (addr handle array byte) <- get dest, data
- 909   populate dest-data-ah, capacity
- 910   var _dest-data/eax: (addr array byte) <- lookup *dest-data-ah
- 911   var dest-data/edi: (addr array byte) <- copy _dest-data
- 912   # error buffers per r/g/b channel
- 913   var red-errors-storage: (array int 0xc0000)
- 914   var tmp/eax: (addr array int) <- address red-errors-storage
- 915   var red-errors: (addr array int)
- 916   copy-to red-errors, tmp
- 917   var green-errors-storage: (array int 0xc0000)
- 918   var tmp/eax: (addr array int) <- address green-errors-storage
- 919   var green-errors: (addr array int)
- 920   copy-to green-errors, tmp
- 921   var blue-errors-storage: (array int 0xc0000)
- 922   var tmp/eax: (addr array int) <- address blue-errors-storage
- 923   var blue-errors: (addr array int)
- 924   copy-to blue-errors, tmp
- 925   # transform 'data'
- 926   var src-data-ah/eax: (addr handle array byte) <- get src, data
- 927   var _src-data/eax: (addr array byte) <- lookup *src-data-ah
- 928   var src-data/esi: (addr array byte) <- copy _src-data
- 929   var y/edx: int <- copy 0
- 930   {
- 931     compare y, src-height
- 932     break-if->=
- 933     var x/ecx: int <- copy 0
- 934     {
- 935       compare x, src-width
- 936       break-if->=
- 937       # - update errors and compute color levels for current pixel in each channel
- 938       # update red-error with current image pixel
- 939       var red-error: int
- 940       {
- 941         var tmp/esi: int <- _read-dithering-error red-errors, x, y, src-width
- 942         copy-to red-error, tmp
- 943       }
- 944       {
- 945         var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 0/red, scale-f
- 946         add-to red-error, tmp
- 947       }
- 948       # recompute red channel for current pixel
- 949       var red-level: int
- 950       {
- 951         var tmp/eax: int <- _error-to-ppm-channel red-error
- 952         copy-to red-level, tmp
- 953       }
- 954       # update green-error with current image pixel
- 955       var green-error: int
- 956       {
- 957         var tmp/esi: int <- _read-dithering-error green-errors, x, y, src-width
- 958         copy-to green-error, tmp
- 959       }
- 960       {
- 961         var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 1/green, scale-f
- 962         add-to green-error, tmp
- 963       }
- 964       # recompute green channel for current pixel
- 965       var green-level: int
- 966       {
- 967         var tmp/eax: int <- _error-to-ppm-channel green-error
- 968         copy-to green-level, tmp
- 969       }
- 970       # update blue-error with current image pixel
- 971       var blue-error: int
- 972       {
- 973         var tmp/esi: int <- _read-dithering-error blue-errors, x, y, src-width
- 974         copy-to blue-error, tmp
- 975       }
- 976       {
- 977         var tmp/eax: int <- _ppm-error src-data, x, y, src-width, 2/blue, scale-f
- 978         add-to blue-error, tmp
- 979       }
- 980       # recompute blue channel for current pixel
- 981       var blue-level: int
- 982       {
- 983         var tmp/eax: int <- _error-to-ppm-channel blue-error
- 984         copy-to blue-level, tmp
- 985       }
- 986       # - figure out the nearest color
- 987 #?       {
- 988 #?         compare red-level, 0x80
- 989 #?         break-if->
- 990 #?         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, red-level, 4/fg 0/bg
- 991 #?         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, green-level, 2/fg 0/bg
- 992 #?         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, blue-level, 9/fg 0/bg
- 993 #?       }
- 994       var nearest-color-index/eax: int <- nearest-color-euclidean red-level, green-level, blue-level
- 995       {
- 996         var nearest-color-index-byte/eax: byte <- copy-byte nearest-color-index
- 997         _write-raw-buffer dest-data, x, y, src-width, nearest-color-index-byte
- 998       }
- 999       # - diffuse errors
-1000       var red-level: int
-1001       var green-level: int
-1002       var blue-level: int
-1003       {
-1004         var tmp-red-level/ecx: int <- copy 0
-1005         var tmp-green-level/edx: int <- copy 0
-1006         var tmp-blue-level/ebx: int <- copy 0
-1007         tmp-red-level, tmp-green-level, tmp-blue-level <- color-rgb nearest-color-index
-1008         copy-to red-level, tmp-red-level
-1009         copy-to green-level, tmp-green-level
-1010         copy-to blue-level, tmp-blue-level
-1011       }
-1012       # update red-error
-1013       var red-level-error/eax: int <- copy red-level
-1014       red-level-error <- shift-left 0x10
-1015       subtract-from red-error, red-level-error
-1016       _diffuse-dithering-error-floyd-steinberg red-errors, x, y, src-width, src-height, red-error
-1017       # update green-error
-1018       var green-level-error/eax: int <- copy green-level
-1019       green-level-error <- shift-left 0x10
-1020       subtract-from green-error, green-level-error
-1021       _diffuse-dithering-error-floyd-steinberg green-errors, x, y, src-width, src-height, green-error
-1022       # update blue-error
-1023       var blue-level-error/eax: int <- copy blue-level
-1024       blue-level-error <- shift-left 0x10
-1025       subtract-from blue-error, blue-level-error
-1026       _diffuse-dithering-error-floyd-steinberg blue-errors, x, y, src-width, src-height, blue-error
-1027       #
-1028       x <- increment
-1029       loop
-1030     }
-1031     y <- increment
-1032     loop
-1033   }
-1034 }
-1035 
-1036 # convert a single channel for a single image pixel to error space
-1037 fn _ppm-error buf: (addr array byte), x: int, y: int, width: int, channel: int, _scale-f: float -> _/eax: int {
-1038   # current image pixel
-1039   var initial-level/eax: byte <- _read-ppm-buffer buf, x, y, width, channel
-1040   # scale to 255 levels
-1041   var initial-level-int/eax: int <- copy initial-level
-1042   var initial-level-f/xmm0: float <- convert initial-level-int
-1043   var scale-f/xmm1: float <- copy _scale-f
-1044   initial-level-f <- multiply scale-f
-1045   initial-level-int <- convert initial-level-f
-1046   # switch to fixed-point with 16 bits of precision
-1047   initial-level-int <- shift-left 0x10
-1048   return initial-level-int
-1049 }
-1050 
-1051 fn _error-to-ppm-channel error: int -> _/eax: int {
-1052   # clamp(error >> 16)
-1053   var result/esi: int <- copy error
-1054   result <- shift-right-signed 0x10
-1055   {
-1056     compare result, 0
-1057     break-if->=
-1058     result <- copy 0
-1059   }
-1060   {
-1061     compare result, 0xff
-1062     break-if-<=
-1063     result <- copy 0xff
-1064   }
-1065   return result
-1066 }
-1067 
-1068 # read from a buffer containing alternating bytes from r/g/b channels
-1069 fn _read-ppm-buffer _buf: (addr array byte), x: int, y: int, width: int, channel: int -> _/eax: byte {
-1070   var buf/esi: (addr array byte) <- copy _buf
-1071   var idx/ecx: int <- copy y
-1072   idx <- multiply width
-1073   idx <- add x
-1074   var byte-idx/edx: int <- copy 3
-1075   byte-idx <- multiply idx
-1076   byte-idx <- add channel
-1077   var result-a/eax: (addr byte) <- index buf, byte-idx
-1078   var result/eax: byte <- copy-byte *result-a
-1079   return result
-1080 }
-1081 
-1082 # each byte in the image data is a color of the current palette
-1083 fn render-raw-image screen: (addr screen), _img: (addr image), xmin: int, ymin: int, width: int, height: int {
-1084   var img/esi: (addr image) <- copy _img
-1085   # yratio = height/img->height
-1086   var img-height-a/eax: (addr int) <- get img, height
-1087   var img-height/xmm0: float <- convert *img-height-a
-1088   var yratio/xmm1: float <- convert height
-1089   yratio <- divide img-height
-1090   # xratio = width/img->width
-1091   var img-width-a/eax: (addr int) <- get img, width
-1092   var img-width/ebx: int <- copy *img-width-a
-1093   var img-width-f/xmm0: float <- convert img-width
-1094   var xratio/xmm2: float <- convert width
-1095   xratio <- divide img-width-f
-1096   # esi = img->data
-1097   var img-data-ah/eax: (addr handle array byte) <- get img, data
-1098   var _img-data/eax: (addr array byte) <- lookup *img-data-ah
-1099   var img-data/esi: (addr array byte) <- copy _img-data
-1100   var len/edi: int <- length img-data
-1101   #
-1102   var one/eax: int <- copy 1
-1103   var one-f/xmm3: float <- convert one
-1104   var width-f/xmm4: float <- convert width
-1105   var height-f/xmm5: float <- convert height
-1106   var zero/eax: int <- copy 0
-1107   var zero-f/xmm0: float <- convert zero
-1108   var y/xmm6: float <- copy zero-f
-1109   {
-1110     compare y, height-f
-1111     break-if-float>=
-1112     var imgy-f/xmm5: float <- copy y
-1113     imgy-f <- divide yratio
-1114     var imgy/edx: int <- truncate imgy-f
-1115     var x/xmm7: float <- copy zero-f
-1116     {
-1117       compare x, width-f
-1118       break-if-float>=
-1119       var imgx-f/xmm5: float <- copy x
-1120       imgx-f <- divide xratio
-1121       var imgx/ecx: int <- truncate imgx-f
-1122       var idx/eax: int <- copy imgy
-1123       idx <- multiply img-width
-1124       idx <- add imgx
-1125       # error info in case we rounded wrong and 'index' will fail bounds-check
-1126       compare idx, len
-1127       {
-1128         break-if-<
-1129         set-cursor-position 0/screen, 0x20/x 0x20/y
-1130         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgx, 3/fg 0/bg
-1131         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, imgy, 4/fg 0/bg
-1132         draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, idx, 5/fg 0/bg
-1133       }
-1134       var color-a/eax: (addr byte) <- index img-data, idx
-1135       var color/eax: byte <- copy-byte *color-a
-1136       var color-int/eax: int <- copy color
-1137       var screenx/ecx: int <- convert x
-1138       screenx <- add xmin
-1139       var screeny/edx: int <- convert y
-1140       screeny <- add ymin
-1141       pixel screen, screenx, screeny, color-int
-1142       x <- add one-f
-1143       loop
-1144     }
-1145     y <- add one-f
-1146     loop
-1147   }
-1148 }
-
- - - diff --git a/html/life.mu.html b/html/life.mu.html deleted file mode 100644 index 31281d9f..00000000 --- a/html/life.mu.html +++ /dev/null @@ -1,320 +0,0 @@ - - - - -Mu - life.mu - - - - - - - - - - -https://github.com/akkartik/mu/blob/main/life.mu -
-  1 # Conway's Game of Life
-  2 #
-  3 # To build:
-  4 #   $ ./translate life.mu
-  5 # To run:
-  6 #   $ qemu-system-i386 code.img
-  7 
-  8 fn state _grid: (addr array boolean), x: int, y: int -> _/eax: boolean {
-  9   # clip at the edge
- 10   compare x, 0
- 11   {
- 12     break-if->=
- 13     return 0/false
- 14   }
- 15   compare y, 0
- 16   {
- 17     break-if->=
- 18     return 0/false
- 19   }
- 20   compare x, 0x100/width
- 21   {
- 22     break-if-<
- 23     return 0/false
- 24   }
- 25   compare y, 0xc0/height
- 26   {
- 27     break-if-<
- 28     return 0/false
- 29   }
- 30   var idx/eax: int <- copy y
- 31   idx <- shift-left 8/log2width
- 32   idx <- add x
- 33   var grid/esi: (addr array boolean) <- copy _grid
- 34   var result/eax: (addr boolean) <- index grid, idx
- 35   return *result
- 36 }
- 37 
- 38 fn set-state _grid: (addr array boolean), x: int, y: int, val: boolean {
- 39   # don't bother checking bounds
- 40   var idx/eax: int <- copy y
- 41   idx <- shift-left 8/log2width
- 42   idx <- add x
- 43   var grid/esi: (addr array boolean) <- copy _grid
- 44   var result/eax: (addr boolean) <- index grid, idx
- 45   var src/ecx: boolean <- copy val
- 46   copy-to *result, src
- 47 }
- 48 
- 49 fn num-live-neighbors grid: (addr array boolean), x: int, y: int -> _/eax: int {
- 50   var result/edi: int <- copy 0
- 51   # row above: zig
- 52   decrement y
- 53   decrement x
- 54   var s/eax: boolean <- state grid, x, y
- 55   {
- 56     compare s, 0/false
- 57     break-if-=
- 58     result <- increment
- 59   }
- 60   increment x
- 61   s <- state grid, x, y
- 62   {
- 63     compare s, 0/false
- 64     break-if-=
- 65     result <- increment
- 66   }
- 67   increment x
- 68   s <- state grid, x, y
- 69   {
- 70     compare s, 0/false
- 71     break-if-=
- 72     result <- increment
- 73   }
- 74   # curr row: zag
- 75   increment y
- 76   s <- state grid, x, y
- 77   {
- 78     compare s, 0/false
- 79     break-if-=
- 80     result <- increment
- 81   }
- 82   subtract-from x, 2
- 83   s <- state grid, x, y
- 84   {
- 85     compare s, 0/false
- 86     break-if-=
- 87     result <- increment
- 88   }
- 89   # row below: zig
- 90   increment y
- 91   s <- state grid, x, y
- 92   {
- 93     compare s, 0/false
- 94     break-if-=
- 95     result <- increment
- 96   }
- 97   increment x
- 98   s <- state grid, x, y
- 99   {
-100     compare s, 0/false
-101     break-if-=
-102     result <- increment
-103   }
-104   increment x
-105   s <- state grid, x, y
-106   {
-107     compare s, 0/false
-108     break-if-=
-109     result <- increment
-110   }
-111   return result
-112 }
-113 
-114 fn step old-grid: (addr array boolean), new-grid: (addr array boolean) {
-115   var y/ecx: int <- copy 0
-116   {
-117     compare y, 0xc0/height
-118     break-if->=
-119     var x/edx: int <- copy 0
-120     {
-121       compare x, 0x100/width
-122       break-if->=
-123       var n/eax: int <- num-live-neighbors old-grid, x, y
-124       # if neighbors < 2, die of loneliness
-125       {
-126         compare n, 2
-127         break-if->=
-128         set-state new-grid, x, y, 0/dead
-129       }
-130       # if neighbors > 3, die of overcrowding
-131       {
-132         compare n, 3
-133         break-if-<=
-134         set-state new-grid, x, y, 0/dead
-135       }
-136       # if neighbors = 2, preserve state
-137       {
-138         compare n, 2
-139         break-if-!=
-140         var old-state/eax: boolean <- state old-grid, x, y
-141         set-state new-grid, x, y, old-state
-142       }
-143       # if neighbors = 3, cell quickens to life
-144       {
-145         compare n, 3
-146         break-if-!=
-147         set-state new-grid, x, y, 1/live
-148       }
-149       x <- increment
-150       loop
-151     }
-152     y <- increment
-153     loop
-154   }
-155 }
-156 
-157 # color a square of size 'side' starting at x*side, y*side
-158 fn render-square _x: int, _y: int, color: int {
-159   var y/edx: int <- copy _y
-160   y <- shift-left 2/log2side
-161   var side/ebx: int <- copy 1
-162   side <- shift-left 2/log2side
-163   var ymax/ecx: int <- copy y
-164   ymax <- add side
-165   {
-166     compare y, ymax
-167     break-if->=
-168     {
-169       var x/eax: int <- copy _x
-170       x <- shift-left 2/log2side
-171       var xmax/ecx: int <- copy x
-172       xmax <- add side
-173       {
-174         compare x, xmax
-175         break-if->=
-176         pixel-on-real-screen x, y, color
-177         x <- increment
-178         loop
-179       }
-180     }
-181     y <- increment
-182     loop
-183   }
-184 }
-185 
-186 fn render grid: (addr array boolean) {
-187   var y/ecx: int <- copy 0
-188   {
-189     compare y, 0xc0/height
-190     break-if->=
-191     var x/edx: int <- copy 0
-192     {
-193       compare x, 0x100/width
-194       break-if->=
-195       var state/eax: boolean <- state grid, x, y
-196       compare state, 0/false
-197       {
-198         break-if-=
-199         render-square x, y, 3/cyan
-200       }
-201       compare state, 0/false
-202       {
-203         break-if-!=
-204         render-square x, y, 0/black
-205       }
-206       x <- increment
-207       loop
-208     }
-209     y <- increment
-210     loop
-211   }
-212 }
-213 
-214 fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
-215 #?   # allocate on the stack
-216 #?   var grid1-storage: (array boolean 0xc000)  # width * height
-217 #?   var grid1/esi: (addr array boolean) <- address grid1-storage
-218 #?   var grid2-storage: (array boolean 0xc000)  # width * height
-219 #?   var grid2/edi: (addr array boolean) <- address grid2-storage
-220   # allocate on the heap
-221   var grid1-storage: (handle array boolean)
-222   var grid1-ah/eax: (addr handle array boolean) <- address grid1-storage
-223   populate grid1-ah, 0xc000  # width * height
-224   var _grid1/eax: (addr array boolean) <- lookup *grid1-ah
-225   var grid1/esi: (addr array boolean) <- copy _grid1
-226   var grid2-storage: (handle array boolean)
-227   var grid2-ah/eax: (addr handle array boolean) <- address grid2-storage
-228   populate grid2-ah, 0xc000  # width * height
-229   var _grid2/eax: (addr array boolean) <- lookup *grid2-ah
-230   var grid2/edi: (addr array boolean) <- copy _grid2
-231   # initialize grid1
-232   set-state grid1, 0x80, 0x5f, 1/live
-233   set-state grid1, 0x81, 0x5f, 1/live
-234   set-state grid1, 0x7f, 0x60, 1/live
-235   set-state grid1, 0x80, 0x60, 1/live
-236   set-state grid1, 0x80, 0x61, 1/live
-237   # render grid1
-238   render grid1
-239   {
-240     var key/eax: byte <- read-key keyboard
-241     compare key, 0
-242 #?     loop-if-=  # press key to step
-243     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
-244     # iter: grid1 -> grid2
-245     step grid1, grid2
-246     render grid2
-247     # iter: grid2 -> grid1
-248     step grid2, grid1
-249     render grid1
-250     loop
-251   }
-252 }
-
- - - -- cgit 1.4.1-2-gfad0