https://github.com/akkartik/mu/blob/master/apps/browse.mu
  1 # Render text with word-wrap.
  2 #
  3 # To run:
  4 #   $ ./translate_mu apps/browse.mu
  5 #   $ ./a.elf __text_file__
  6 #
  7 # Press 'q' to quit. All other keys scroll down.
  8 
  9 fn main args: (addr array (addr array byte)) -> exit-status/ebx: int {
 10   var filename/eax: (addr array byte) <- first-arg args
 11   var file/esi: (addr buffered-file) <- load-file filename
 12   enable-screen-grid-mode
 13   var nrows/eax: int <- copy 0
 14   var ncols/ecx: int <- copy 0
 15   nrows, ncols <- screen-size
 16   enable-keyboard-immediate-mode
 17   {
 18     render file, nrows, ncols
 19     var key/eax: byte <- read-key
 20     compare key, 0x71  # 'q'
 21     loop-if-!=
 22   }
 23   enable-keyboard-type-mode
 24   enable-screen-type-mode
 25   exit-status <- copy 0
 26 }
 27 
 28 type render-state {
 29   current-state: int  # enum 0: normal, 1: bold
 30 }
 31 
 32 # decide how to lay out pages on screen
 33 fn render in: (addr buffered-file), nrows: int, ncols: int {
 34   # Fit multiple pages on screen on separate columns, each wide enough to read
 35   # comfortably.
 36   # Pages are separated horizontally by a 'page margin'. Among other reasons,
 37   # this allows the odd line to bleed out on the right if necessary.
 38   #
 39   # hardcoded parameters:
 40   #   top-margin
 41   #   page-margin
 42   #   page-width
 43   var _r: render-state
 44   var r/edi: (addr render-state) <- address _r
 45   var toprow/eax: int <- copy 2  # top-margin
 46   var botrow/ecx: int <- copy nrows
 47   var leftcol/edx: int <- copy 5  # page-margin
 48   var rightcol/ebx: int <- copy leftcol
 49   rightcol <- add 0x40  # page-width = 64 characters
 50   start-color 0xec, 7  # 236 = darkish gray
 51   {
 52     compare rightcol, ncols
 53     break-if->=
 54     render-page in, toprow, leftcol, botrow, rightcol, r
 55     leftcol <- copy rightcol
 56     leftcol <- add 5  # page-margin
 57     rightcol <- copy leftcol
 58     rightcol <- add 0x40  # page-width
 59     loop
 60   }
 61 }
 62 
 63 fn render-page in: (addr buffered-file), toprow: int, leftcol: int, botrow: int, rightcol: int, r: (addr render-state) {
 64   clear toprow, leftcol, botrow, rightcol
 65   # render screen rows
 66   var row/ecx: int <- copy toprow
 67 $line-loop:  {
 68     compare row, botrow
 69     break-if->=
 70     var col/edx: int <- copy leftcol
 71     move-cursor row, col
 72     {
 73       compare col, rightcol
 74       break-if->=
 75       var c/eax: byte <- read-byte-buffered in
 76       compare c, 0xffffffff  # EOF marker
 77       break-if-= $line-loop
 78       update-attributes c, r
 79       compare c, 0xa  # newline
 80       break-if-=  # no need to print newlines
 81       print-byte c
 82       col <- increment
 83       loop
 84     }
 85     row <- increment
 86     loop
 87   }
 88 }
 89 
 90 fn update-attributes c: byte, _r: (addr render-state) {
 91   var r/edi: (addr render-state) <- copy _r
 92   var state/esi: (addr int) <- get r, current-state
 93 $update-attributes:check-state: {
 94     compare *state, 0  # normal
 95     {
 96       break-if-!=
 97       compare c, 0x2a  # '*'
 98       {
 99         break-if-!=
100         # r->current-state == 0 && c == '*'
101         start-bold
102         copy-to *state, 1
103         break $update-attributes:check-state
104       }
105       compare c, 0x5f  # '_'
106       {
107         break-if-!=
108         # r->current-state == 0 && c == '_'
109         start-bold
110         copy-to *state, 1
111         break $update-attributes:check-state
112       }
113       break $update-attributes:check-state
114     }
115     {
116       break-if-=
117       compare c, 0x2a  # '*'
118       {
119         break-if-!=
120         # r->current-state == 1 && c == '*'
121         reset-formatting
122         copy-to *state, 0
123         break $update-attributes:check-state
124       }
125       compare c, 0x5f  # '_'
126       {
127         break-if-!=
128         # r->current-state == 1 && c == '_'
129         reset-formatting
130         copy-to *state, 0
131         break $update-attributes:check-state
132       }
133       break $update-attributes:check-state
134     }
135   }
136 }
137 
138 fn clear toprow: int, leftcol: int, botrow: int, rightcol: int {
139   var row/ecx: int <- copy toprow
140   {
141     compare row, botrow
142     break-if->=
143     var col/edx: int <- copy leftcol
144     move-cursor row, col
145     {
146       compare col, rightcol
147       break-if->=
148       print-string " "
149       col <- increment
150       loop
151     }
152     row <- increment
153     loop
154   }
155 }
156 
157 fn first-arg args-on-stack: (addr array (addr array byte)) -> out/eax: (addr array byte) {
158   var args/eax: (addr array (addr array byte)) <- copy args-on-stack
159   var result/eax: (addr addr array byte) <- index args, 1
160   out <- copy *result
161 }
162 
163 fn load-file filename: (addr array byte) -> out/esi: (addr buffered-file) {
164   var result: (handle buffered-file)
165   {
166     var tmp1/eax: (addr handle buffered-file) <- address result
167     open filename, 0, tmp1
168   }
169   var tmp2/eax: (addr buffered-file) <- lookup result
170   out <- copy tmp2
171 }
172 
173 fn dump in: (addr buffered-file) {
174   var c/eax: byte <- read-byte-buffered in
175   compare c, 0xffffffff  # EOF marker
176   break-if-=
177   print-byte c
178   loop
179 }