https://github.com/akkartik/mu/blob/main/apps/colors.mu
  1 # Return colors 'near' a given r/g/b value (expressed in hex)
  2 # If we did this rigorously we'd need to implement cosines. So we won't.
  3 #
  4 # To build:
  5 #   $ ./translate apps/colors.mu
  6 #
  7 # Example session:
  8 #   $ qemu-system-i386 code.img
  9 #   Enter 3 hex bytes for r, g, b (lowercase; no 0x prefix) separated by a single space> aa 0 aa
 10 #   5
 11 # This means only color 5 in the default palette is similar to #aa00aa.
 12 
 13 fn main screen: (addr screen), keyboard: (addr keyboard), data-disk: (addr disk) {
 14   var in-storage: (stream byte 0x10)
 15   var in/esi: (addr stream byte) <- address in-storage
 16   {
 17     # print prompt
 18     var x/eax: int <- draw-text-rightward screen, "Enter 3 hex bytes for r, g, b (lowercase; no 0x prefix) separated by a single space> ", 0x10/x, 0x80/xmax, 0x28/y, 3/fg/cyan, 0/bg
 19     # read line from keyboard
 20     clear-stream in
 21     {
 22       draw-cursor screen, 0x20/space
 23       var key/eax: byte <- read-key keyboard
 24       compare key, 0xa/newline
 25       break-if-=
 26       compare key, 0
 27       loop-if-=
 28       var key2/eax: int <- copy key
 29       append-byte in, key2
 30       var c/eax: code-point <- copy key2  # TODO: unicode input
 31       draw-code-point-at-cursor-over-full-screen screen, c, 0xf/fg, 0/bg
 32       loop
 33     }
 34     clear-screen screen
 35     # parse
 36     var a/ecx: int <- copy 0
 37     var b/edx: int <- copy 0
 38     var c/ebx: int <- copy 0
 39     # a, b, c = r, g, b
 40     a, b, c <- parse in
 41 #?     set-cursor-position screen, 0x10/x, 0x1a/y
 42 #?     draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, a, 7/fg, 0/bg
 43 #?     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
 44 #?     draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, b, 7/fg, 0/bg
 45 #?     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
 46 #?     draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, c, 7/fg, 0/bg
 47     a, b, c <- hsl a, b, c
 48     # return all colors in the same quadrant in h, s and l
 49     print-nearby-colors screen, a, b, c
 50     # another metric
 51     var color/eax: int <- nearest-color-euclidean-hsl a, b, c
 52     set-cursor-position screen, 0x10/x, 0x26/y
 53     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "nearest (euclidean, h/s/l): ", 0xf/fg, 0/bg
 54     draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, color, 7/fg, 0/bg
 55     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 0xf/fg, 0/bg
 56     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "               ", 0/fg, color
 57     #
 58     loop
 59   }
 60 }
 61 
 62 # read exactly 3 words in a single line
 63 # Each word consists of exactly 1 or 2 hex bytes. No hex prefix.
 64 fn parse in: (addr stream byte) -> _/ecx: int, _/edx: int, _/ebx: int {
 65   # read first byte of r
 66   var tmp/eax: byte <- read-byte in
 67   {
 68     var valid?/eax: boolean <- hex-digit? tmp
 69     compare valid?, 0/false
 70     break-if-!=
 71     abort "invalid byte 0 of r"
 72   }
 73   tmp <- fast-hex-digit-value tmp
 74   var r/ecx: int <- copy tmp
 75 #?   set-cursor-position 0/screen, 0x10/x, 0x10/y
 76 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, r, 7/fg, 0/bg
 77   # read second byte of r
 78   tmp <- read-byte in
 79   {
 80     {
 81       var valid?/eax: boolean <- hex-digit? tmp
 82       compare valid?, 0/false
 83     }
 84     break-if-=
 85     r <- shift-left 4
 86     tmp <- fast-hex-digit-value tmp
 87 #?     {
 88 #?       var foo/eax: int <- copy tmp
 89 #?       set-cursor-position 0/screen, 0x10/x, 0x11/y
 90 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg
 91 #?     }
 92     r <- add tmp
 93 #?     {
 94 #?       set-cursor-position 0/screen, 0x10/x, 0x12/y
 95 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, r, 7/fg, 0/bg
 96 #?     }
 97     tmp <- read-byte in  # skip space
 98   }
 99   # read first byte of g
100   var tmp/eax: byte <- read-byte in
101   {
102     var valid?/eax: boolean <- hex-digit? tmp
103     compare valid?, 0/false
104     break-if-!=
105     abort "invalid byte 0 of g"
106   }
107   tmp <- fast-hex-digit-value tmp
108   var g/edx: int <- copy tmp
109 #?   set-cursor-position 0/screen, 0x10/x, 0x13/y
110 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, g, 7/fg, 0/bg
111   # read second byte of g
112   tmp <- read-byte in
113   {
114     {
115       var valid?/eax: boolean <- hex-digit? tmp
116       compare valid?, 0/false
117     }
118     break-if-=
119     g <- shift-left 4
120     tmp <- fast-hex-digit-value tmp
121 #?     {
122 #?       var foo/eax: int <- copy tmp
123 #?       set-cursor-position 0/screen, 0x10/x, 0x14/y
124 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg
125 #?     }
126     g <- add tmp
127 #?     {
128 #?       set-cursor-position 0/screen, 0x10/x, 0x15/y
129 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, g, 7/fg, 0/bg
130 #?     }
131     tmp <- read-byte in  # skip space
132   }
133   # read first byte of b
134   var tmp/eax: byte <- read-byte in
135   {
136     var valid?/eax: boolean <- hex-digit? tmp
137     compare valid?, 0/false
138     break-if-!=
139     abort "invalid byte 0 of b"
140   }
141   tmp <- fast-hex-digit-value tmp
142   var b/ebx: int <- copy tmp
143 #?   set-cursor-position 0/screen, 0x10/x, 0x16/y
144 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, b, 7/fg, 0/bg
145   # read second byte of b
146   {
147     {
148       var done?/eax: boolean <- stream-empty? in
149       compare done?, 0/false
150     }
151     break-if-!=
152     tmp <- read-byte in
153     {
154       var valid?/eax: boolean <- hex-digit? tmp
155       compare valid?, 0/false
156     }
157     break-if-=
158     b <- shift-left 4
159     tmp <- fast-hex-digit-value tmp
160 #?     {
161 #?       var foo/eax: int <- copy tmp
162 #?       set-cursor-position 0/screen, 0x10/x, 0x17/y
163 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg
164 #?     }
165     b <- add tmp
166 #?     {
167 #?       set-cursor-position 0/screen, 0x10/x, 0x18/y
168 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, b, 7/fg, 0/bg
169 #?     }
170   }
171   return r, g, b
172 }
173 
174 # no error checking
175 fn fast-hex-digit-value in: byte -> _/eax: byte {
176   var result/eax: byte <- copy in
177   compare result, 0x39
178   {
179     break-if->
180     result <- subtract 0x30/0
181     return result
182   }
183   result <- subtract 0x61/a
184   result <- add 0xa/10
185   return result
186 }
187 
188 fn print-nearby-colors screen: (addr screen), h: int, s: int, l: int {
189 #?   set-cursor-position screen, 0x10/x, 0x1c/y
190 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, h, 7/fg, 0/bg
191 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
192 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, s, 7/fg, 0/bg
193 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
194 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, l, 7/fg, 0/bg
195   # save just top 2 bits of each, so that we narrow down to 1/64th of the volume
196   shift-right h, 6
197   shift-right s, 6
198   shift-right l, 6
199 #?   set-cursor-position screen, 0x10/x, 0x1/y
200 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, h, 7/fg, 0/bg
201 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
202 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, s, 7/fg, 0/bg
203 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
204 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, l, 7/fg, 0/bg
205   var a/ecx: int <- copy 0
206   var b/edx: int <- copy 0
207   var c/ebx: int <- copy 0
208   var color/eax: int <- copy 0
209   var y/esi: int <- copy 2
210   {
211     compare color, 0x100
212     break-if->=
213     a, b, c <- color-rgb color
214     a, b, c <- hsl a, b, c
215     a <- shift-right 6
216     b <- shift-right 6
217     c <- shift-right 6
218     {
219       compare a, h
220       break-if-!=
221       compare b, s
222       break-if-!=
223       compare c, l
224       break-if-!=
225       set-cursor-position screen, 0x10/x, y
226       draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, color, 7/fg, 0/bg
227       set-cursor-position screen, 0x14/x, y
228       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
229       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "               ", 0/fg, color
230 #?       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
231 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, a, 7/fg, 0/bg
232 #?       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
233 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, b, 7/fg, 0/bg
234 #?       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
235 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, c, 7/fg, 0/bg
236       y <- increment
237     }
238     color <- increment
239     loop
240   }
241 }