about summary refs log tree commit diff stats
path: root/078emit-hex.subx
blob: 075f8e4b6ce52c230f866867745abde6d9f6fa18 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
== code
#   instruction                     effective address                                                   register    displacement    immediate
# . op          subop               mod             rm32          base        index         scale       r32
# . 1-3 bytes   3 bits              2 bits          3 bits        3 bits      3 bits        2 bits      2 bits      0/1/2/4 bytes   0/1/2/4 bytes

# print 'n' in hex in 'width' bytes in lower-endian order, with a space after every byte
emit-hex:  # out: (addr buffered-file), n: int, width: int
    # . prologue
    55/push-ebp
    89/copy                         3/mod/direct    5/rm32/ebp    .           .             .           4/r32/esp   .               .                 # copy esp to ebp
    # . save registers
    50/push-eax
    51/push-ecx
    52/push-edx
    53/push-ebx
    57/push-edi
    # edi = out
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           7/r32/edi   8/disp8         .                 # copy *(ebp+8) to edi
    # ebx = n
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           3/r32/ebx   0xc/disp8       .                 # copy *(ebp+12) to ebx
    # edx = width
    8b/copy                         1/mod/*+disp8   5/rm32/ebp    .           .             .           2/r32/edx   0x10/disp8      .                 # copy *(ebp+16) to edx
    # var curr/ecx: int = 0
    31/xor                          3/mod/direct    1/rm32/ecx    .           .             .           1/r32/ecx   .               .                 # clear ecx
$emit-hex:loop:
    # if (curr >= width) break
    39/compare                      3/mod/direct    1/rm32/ecx    .           .             .           2/r32/edx   .               .                 # compare ecx with edx
    7d/jump-if->=  $emit-hex:end/disp8
    # print-byte-buffered(out, ebx)  # only BL used
    # . . push args
    53/push-ebx
    57/push-edi
    # . . call
    e8/call  print-byte-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # write-byte-buffered(out, ' ')
    # . . push args
    68/push  0x20/imm32/space
    57/push-edi
    # . . call
    e8/call  write-byte-buffered/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               8/imm32           # add to esp
    # ebx = ebx >> 8
    c1/shift    5/subop/logic-right 3/mod/direct    3/rm32/ebx    .           .             .           .           .               8/imm8            # shift ebx right by 8 bits, while padding zeroes
$emit-hex:continue:
    # ++curr
    41/increment-ecx
    eb/jump  $emit-hex:loop/disp8
$emit-hex:end:
    # . restore registers
    5f/pop-to-edi
    5b/pop-to-ebx
    5a/pop-to-edx
    59/pop-to-ecx
    58/pop-to-eax
    # . epilogue
    89/copy                         3/mod/direct    4/rm32/esp    .           .             .           5/r32/ebp   .               .                 # copy ebp to esp
    5d/pop-to-ebp
    c3/return

test-emit-hex-single-byte:
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # . clear-stream($_test-output-buffered-file->buffer)
    # . . push args
    68/push  $_test-output-buffered-file->buffer/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # emit-hex(_test-output-buffered-file, 0xab, 1)
    # . . push args
    68/push  1/imm32
    68/push  0xab/imm32
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # check-ints-equal(*_test-output-stream->data, 'ab ', msg)
    # . . push args
    68/push  "F - test-emit-hex-single-byte"/imm32
    68/push  0x206261/imm32
    # . . push *_test-output-stream->data
    b8/copy-to-eax  _test-output-stream/imm32
    ff          6/subop/push        1/mod/*+disp8   0/rm32/eax    .           .             .           .           0xc/disp8       .                 # push *(eax+12)
    # . . call
    e8/call  check-ints-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . end
    c3/return

test-emit-hex-multiple-byte:
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # . clear-stream($_test-output-buffered-file->buffer)
    # . . push args
    68/push  $_test-output-buffered-file->buffer/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # emit-hex(_test-output-buffered-file, 0x1234, 2)
    # . . push args
    68/push  2/imm32
    68/push  0x1234/imm32
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # check-stream-equal(_test-output-stream, "34 12 ", msg)
    # . . push args
    68/push  "F - test-emit-hex-multiple-byte/1"/imm32
    68/push  "34 12 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . end
    c3/return

test-emit-hex-zero-pad:
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # . clear-stream($_test-output-buffered-file->buffer)
    # . . push args
    68/push  $_test-output-buffered-file->buffer/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # emit-hex(_test-output-buffered-file, 0xab, 2)
    # . . push args
    68/push  2/imm32
    68/push  0xab/imm32
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # check(_test-output-stream->data == 'ab 00 ')
    # . . push args
    68/push  "F - test-emit-hex-zero-pad/1"/imm32
    68/push  "ab 00 "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . end
    c3/return

test-emit-hex-negative:
    # setup
    # . clear-stream(_test-output-stream)
    # . . push args
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # . clear-stream($_test-output-buffered-file->buffer)
    # . . push args
    68/push  $_test-output-buffered-file->buffer/imm32
    # . . call
    e8/call  clear-stream/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # emit-hex(_test-output-buffered-file, -1, 2)
    # . . push args
    68/push  2/imm32
    68/push  -1/imm32
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  emit-hex/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # flush(_test-output-buffered-file)
    # . . push args
    68/push  _test-output-buffered-file/imm32
    # . . call
    e8/call  flush/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               4/imm32           # add to esp
    # check-stream-equal(_test-output-stream == "ff ff ")
    # . . push args
    68/push  "F - test-emit-hex-negative/1"/imm32
    68/push  "ff ff "/imm32
    68/push  _test-output-stream/imm32
    # . . call
    e8/call  check-stream-equal/disp32
    # . . discard args
    81          0/subop/add         3/mod/direct    4/rm32/esp    .           .             .           .           .               0xc/imm32         # add to esp
    # . end
    c3/return

# . . vim:nowrap:textwidth=0


                                                              
                                    


                                                                                              
                               



                                                                                        
           
                  
                                                             
                                               
           






                                                                                                      
 









                                                           
                                                      










                                                              
                                            
    
                                        


                                                        
                                                                                    
                                                                  
 
                        
               
                                                                                                   



                                                                                                                                             
                                                                                       
                                 
                                                      

                        


                                                                                     
                                                                                                     
                                  
                                                                








                                                                 






                                                           
" Highlighting literate directives in C++ sources.
function! HighlightTangledFile()
  " Tangled comments only make sense in the sources and are stripped out of
  " the generated .cc file. They're highlighted same as regular comments.
  syntax match tangledComment /\/\/:.*/ | highlight link tangledComment Comment
  syntax match tangledSalientComment /\/\/::.*/ | highlight link tangledSalientComment SalientComment
  set comments-=://
  set comments-=n://
  set comments+=n://:,n://

  " Inside tangle scenarios.
  syntax region tangleDirective start=+:(+ skip=+".*"+ end=+)+
  highlight link tangleDirective Delimiter
  syntax match traceContains /^+.*/
  highlight traceContains ctermfg=22
  syntax match traceAbsent /^-.*/
  highlight traceAbsent ctermfg=darkred
  syntax match tangleScenarioSetup /^\s*% .*/ | highlight link tangleScenarioSetup SpecialChar
  highlight Special ctermfg=160

  syntax match subxString %"[^"]*"% | highlight link subxString Constant
  " match globals but not registers like 'EAX'
  syntax match subxGlobal %\<[A-Z][a-z0-9_-]*\>% | highlight link subxGlobal SpecialChar
endfunction
augroup LocalVimrc
  autocmd BufRead,BufNewFile *.cc call HighlightTangledFile()
  autocmd BufRead,BufNewFile *.subx set ft=subx
augroup END

" Scenarios considered:
"   opening or starting vim with a new or existing file without an extension (should interpret as C++)
"   opening or starting vim with a new or existing file with a .mu extension
"   starting vim or opening a buffer without a file name (ok to do nothing)
"   opening a second file in a new or existing window (shouldn't mess up existing highlighting)
"   reloading an existing file (shouldn't mess up existing highlighting)

command! -nargs=1 E call EditSubx("edit", <f-args>)
if exists("&splitvertical")
  command! -nargs=1 S call EditSubx("vert split", <f-args>)
  command! -nargs=1 H call EditSubx("hor split", <f-args>)
else
  command! -nargs=1 S call EditSubx("vert split", <f-args>)
  command! -nargs=1 H call EditSubx("split", <f-args>)
endif

function! EditSubx(cmd, arg)
  exec "silent! " . a:cmd . " apps/" . a:arg . ".subx"
endfunction

" we often want to crib lines of machine code from other files
function! GrepSubX(regex)
  " https://github.com/mtth/scratch.vim
  Scratch!
  silent exec "r !grep -h '".a:regex."' *.subx */*.subx"
endfunction
command! -nargs=1 G call GrepSubX(<q-args>)

if exists("&splitvertical")
  command! -nargs=0 P hor split subx_opcodes
else
  command! -nargs=0 P split subx_opcodes
endif

" useful for inspecting just the control flow in a trace
" see https://github.com/akkartik/mu/blob/master/Readme.md#a-few-hints-for-debugging
command! -nargs=0 L exec "%!grep label |grep -v clear-stream:loop"

" run test around cursor
if empty($TMUX)
  " hack: need to move cursor outside function at start (`{`), but inside function at end (`<C-o>`)
  " this solution is unfortunate, but seems forced:
  "   can't put initial cursor movement inside function because we rely on <C-r><C-w> to grab word at cursor
  "   can't put final cursor movement out of function because that disables the wait for <CR> prompt; function must be final operation of map
  "   can't avoid the function because that disables the wait for <CR> prompt
  noremap <Leader>t {:keeppatterns /^[^ #]<CR>:call RunTestMoveCursor("<C-r><C-w>")<CR>
  function RunTestMoveCursor(arg)
    exec "!./run_one_test ".expand("%")." '".a:arg."'"
    exec "normal \<C-o>"
  endfunction
else
  " we have tmux; we don't need to show any output in the Vim pane so life is simpler
  " assume the left-most window is for the shell
  noremap <Leader>t {:keeppatterns /^[^ #]<CR>:silent! call RunTestInFirstPane("<C-r><C-w>")<CR><C-o>
  function RunTestInFirstPane(arg)
    call RunInFirstPane("./run_one_test ".expand("%")." ".a:arg)
  endfunction
  function RunInFirstPane(arg)
    exec "!tmux select-pane -t :0.0"
    exec "!tmux send-keys '".a:arg."' C-m"
    exec "!tmux last-pane"
    " for some reason my screen gets messed up, so force a redraw
    exec "!tmux send-keys 'C-l'"
  endfunction
endif

set switchbuf=useopen
if exists("&splitvertical")
  command! -nargs=0 T badd last_run | sbuffer last_run
else
  command! -nargs=0 T badd last_run | vert sbuffer last_run
endif