https://github.com/akkartik/mu/blob/main/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 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 g/eax: grapheme <- copy key2
 31       draw-grapheme-at-cursor screen, g, 0xf/fg, 0/bg
 32       move-cursor-right 0
 33       loop
 34     }
 35     clear-screen screen
 36     # parse
 37     var a/ecx: int <- copy 0
 38     var b/edx: int <- copy 0
 39     var c/ebx: int <- copy 0
 40     # a, b, c = r, g, b
 41     a, b, c <- parse in
 42 #?     set-cursor-position screen, 0x10/x, 0x1a/y
 43 #?     draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, a, 7/fg, 0/bg
 44 #?     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
 45 #?     draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, b, 7/fg, 0/bg
 46 #?     draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
 47 #?     draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, c, 7/fg, 0/bg
 48     a, b, c <- hsl a, b, c
 49     # return all colors in the same quadrant in h, s and l
 50     print-nearby-colors screen, a, b, c
 51     #
 52     loop
 53   }
 54 }
 55 
 56 # read exactly 3 words in a single line
 57 # Each word consists of exactly 1 or 2 hex bytes. No hex prefix.
 58 fn parse in: (addr stream byte) -> _/ecx: int, _/edx: int, _/ebx: int {
 59   # read first byte of r
 60   var tmp/eax: byte <- read-byte in
 61   {
 62     var valid?/eax: boolean <- hex-digit? tmp
 63     compare valid?, 0/false
 64     break-if-!=
 65     abort "invalid byte 0 of r"
 66   }
 67   tmp <- fast-hex-digit-value tmp
 68   var r/ecx: int <- copy tmp
 69 #?   set-cursor-position 0/screen, 0x10/x, 0x10/y
 70 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, r, 7/fg, 0/bg
 71   # read second byte of r
 72   tmp <- read-byte in
 73   {
 74     {
 75       var valid?/eax: boolean <- hex-digit? tmp
 76       compare valid?, 0/false
 77     }
 78     break-if-=
 79     r <- shift-left 4
 80     tmp <- fast-hex-digit-value tmp
 81 #?     {
 82 #?       var foo/eax: int <- copy tmp
 83 #?       set-cursor-position 0/screen, 0x10/x, 0x11/y
 84 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg
 85 #?     }
 86     r <- add tmp
 87 #?     {
 88 #?       set-cursor-position 0/screen, 0x10/x, 0x12/y
 89 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, r, 7/fg, 0/bg
 90 #?     }
 91     tmp <- read-byte in  # skip space
 92   }
 93   # read first byte of g
 94   var tmp/eax: byte <- read-byte in
 95   {
 96     var valid?/eax: boolean <- hex-digit? tmp
 97     compare valid?, 0/false
 98     break-if-!=
 99     abort "invalid byte 0 of g"
100   }
101   tmp <- fast-hex-digit-value tmp
102   var g/edx: int <- copy tmp
103 #?   set-cursor-position 0/screen, 0x10/x, 0x13/y
104 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, g, 7/fg, 0/bg
105   # read second byte of g
106   tmp <- read-byte in
107   {
108     {
109       var valid?/eax: boolean <- hex-digit? tmp
110       compare valid?, 0/false
111     }
112     break-if-=
113     g <- shift-left 4
114     tmp <- fast-hex-digit-value tmp
115 #?     {
116 #?       var foo/eax: int <- copy tmp
117 #?       set-cursor-position 0/screen, 0x10/x, 0x14/y
118 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg
119 #?     }
120     g <- add tmp
121 #?     {
122 #?       set-cursor-position 0/screen, 0x10/x, 0x15/y
123 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, g, 7/fg, 0/bg
124 #?     }
125     tmp <- read-byte in  # skip space
126   }
127   # read first byte of b
128   var tmp/eax: byte <- read-byte in
129   {
130     var valid?/eax: boolean <- hex-digit? tmp
131     compare valid?, 0/false
132     break-if-!=
133     abort "invalid byte 0 of b"
134   }
135   tmp <- fast-hex-digit-value tmp
136   var b/ebx: int <- copy tmp
137 #?   set-cursor-position 0/screen, 0x10/x, 0x16/y
138 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, b, 7/fg, 0/bg
139   # read second byte of b
140   {
141     {
142       var done?/eax: boolean <- stream-empty? in
143       compare done?, 0/false
144     }
145     break-if-!=
146     tmp <- read-byte in
147     {
148       var valid?/eax: boolean <- hex-digit? tmp
149       compare valid?, 0/false
150     }
151     break-if-=
152     b <- shift-left 4
153     tmp <- fast-hex-digit-value tmp
154 #?     {
155 #?       var foo/eax: int <- copy tmp
156 #?       set-cursor-position 0/screen, 0x10/x, 0x17/y
157 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, foo, 7/fg, 0/bg
158 #?     }
159     b <- add tmp
160 #?     {
161 #?       set-cursor-position 0/screen, 0x10/x, 0x18/y
162 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen 0/screen, b, 7/fg, 0/bg
163 #?     }
164   }
165   return r, g, b
166 }
167 
168 # no error checking
169 fn fast-hex-digit-value in: byte -> _/eax: byte {
170   var result/eax: byte <- copy in
171   compare result, 0x39
172   {
173     break-if->
174     result <- subtract 0x30/0
175     return result
176   }
177   result <- subtract 0x61/a
178   result <- add 0xa/10
179   return result
180 }
181 
182 fn print-nearby-colors screen: (addr screen), h: int, s: int, l: int {
183 #?   set-cursor-position screen, 0x10/x, 0x1c/y
184 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, h, 7/fg, 0/bg
185 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
186 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, s, 7/fg, 0/bg
187 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
188 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, l, 7/fg, 0/bg
189   # save just top 2 bits of each, so that we narrow down to 1/64th of the volume
190   shift-right h, 6
191   shift-right s, 6
192   shift-right l, 6
193 #?   set-cursor-position screen, 0x10/x, 0x1/y
194 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, h, 7/fg, 0/bg
195 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
196 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, s, 7/fg, 0/bg
197 #?   draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
198 #?   draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, l, 7/fg, 0/bg
199   var a/ecx: int <- copy 0
200   var b/edx: int <- copy 0
201   var c/ebx: int <- copy 0
202   var color/eax: int <- copy 0
203   var y/esi: int <- copy 2
204   {
205     compare color, 0x100
206     break-if->=
207     a, b, c <- color-rgb color
208     a, b, c <- hsl a, b, c
209     a <- shift-right 6
210     b <- shift-right 6
211     c <- shift-right 6
212     {
213       compare a, h
214       break-if-!=
215       compare b, s
216       break-if-!=
217       compare c, l
218       break-if-!=
219       set-cursor-position screen, 0x10/x, y
220       draw-int32-decimal-wrapping-right-then-down-from-cursor-over-full-screen screen, color, 7/fg, 0/bg
221       set-cursor-position screen, 0x14/x, y
222       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
223       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, "               ", 0/fg, color
224 #?       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
225 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, a, 7/fg, 0/bg
226 #?       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
227 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, b, 7/fg, 0/bg
228 #?       draw-text-wrapping-right-then-down-from-cursor-over-full-screen screen, " ", 7/fg, 0/bg
229 #?       draw-int32-hex-wrapping-right-then-down-from-cursor-over-full-screen screen, c, 7/fg, 0/bg
230       y <- increment
231     }
232     color <- increment
233     loop
234   }
235 }