-
Notifications
You must be signed in to change notification settings - Fork 30
/
interact.mu4
321 lines (251 loc) · 10 KB
/
interact.mu4
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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
| This file is part of muforth: https://muforth.dev/
|
| Copyright 2002-2024 David Frech. (Read the LICENSE for details.)
loading ARM v6-M interaction
variable chatting
variable chat-vector
: chat-cmd ( index - index+1) dup cells constant 1+
does> @ chat-vector @ =if + @execute ^ then 2drop
error" Not connected to a chat-capable target" ;
: chat-fail error" Chat command not implemented" ;
0
chat-cmd t.hello ( - chunk-size)
chat-cmd t.get-status ( - #retries sp pc)
chat-cmd t.run ( pc sp)
chat-cmd t.read ( buf a u)
chat-cmd t.write ( buf a u)
chat-cmd t.flash-begin
chat-cmd t.flash-end
chat-cmd t.erase ( a)
chat-cmd t.program ( buf a u)
drop
: >image
['] image-@ is |@ ( fetch from host memory image)
['] read-host-image ( read from host memory image) du-target ;
-: ( target - word) pad swap 4 t.read pad lew@ ;
: >chat
[ #] is |@ ( fetch from connected target)
['] t.read ( read from connected target) du-target ;
| Chunk size is reported by the debug interface. Always use this when
| copying RAM contents or programming the flash!
variable /chunk ( chunk size, as reported by debug interface)
256 /chunk ! ( sane default)
: copy-chunk ( 'target len - 'target+len)
| cr ." copy-chunk " 2dup swap u. u.
2dup + push over image+ -rot t.write pop ;
: copy-region ( a u)
| cr ." copy-region " 2dup swap u. u.
/chunk @ /mod ( r q) swap push for /chunk @ copy-chunk next
pop =if ( rem) copy-chunk drop ^ then 2drop ;
variable ram-copied ( pointer to first un-copied byte)
: copy-ram
h preserve ram \m align ( make sure ram region is aligned before copying)
ram-copied @ dup 0= if drop region drop then
\m here over - copy-region
\m here ram-copied ! ;
| Define local copies of target registers. Before executing code on the
| target, we "push" these values to the target, and after executing code,
| we "pull" the new values. We used the local, cached values when printing
| the registers with .regs .
( Name these so they don't conflict with assembler names.)
variable tsp ( target SP)
variable trp ( target RP)
variable tip ( target IP)
variable tix ( target loop index counter)
( Count of retries before get-status got a response from the target.)
variable #retries
: get-regs ( ignoring PC right now) t.get-status drop tsp ! #retries ! ;
: 2sp space space ;
: .h32 radix preserve hex <# 4# 4# #> type ;
: .h16_16 radix preserve hex <# 4# char _ hold 4# #> type ;
: .h32__ .h32 2sp ;
: .h16_16__ .h16_16 2sp ;
: .tr ( variable) ( "target register") @ .h32__ ;
defer .regs now nope is .regs ( print nothing by default)
defer verify-quietly ( - diff)
: hi
chatting on >chat t.hello /chunk ! get-regs .regs
ram-copied off copy-ram
.ifndef noverify verify-quietly drop .then
now __chatting is __meta __meta ;
: chat-via pop chat-vector ! hi ;
: run ( pc) copy-ram tsp @ t.run ; ( don't wait for target)
: runwait ( pc) run get-regs ; ( wait for target)
( For running random bits of code.)
: call ( pc) runwait .regs ;
| Because the target "caches" the top of the stack in a register, and
| because the trampoline code loads this register before execution, and
| pushes it afterward, we can deal only with the "memory image" of the target
| stack, rather than also worrying about what to put into the "top" register.
|
| What's a bit mystifying at first is that, to achieve a target stack depth
| of N, the host has to push N+1 items. The top one goes into top; and the
| bottom-most one is the "sentinel" value that gets loaded into top when the
| stack becomes empty.
|
| When pushing from the host, or when "pulling" from the target, we don't
| move more than 8 "user" items - but we pad this value a bit, since we are
| also using the stack to pass some execution context between host and
| target.
| Traditional Forth stack layout:
|
| User area higher memory
| R stack
| D stack lower memory
|
| Bottom of user area is also RP0. User area contains SP0 and SP. RP is
| pushed onto D stack in pause/yield.
|
| Let's do a similar thing - with R stack at higher memory - to ease the
| transition to a tasking version.
meta
@ram #ram + constant rp0 ( R stack is at the end of RAM)
\m rp0 #64 \m cells - constant sp0 ( D stack is *below* R stack)
: depth \m sp0 tsp @ - \m cell/ 1- ;
forth
| stack> *first* builds a local image of the target stack - in the RAM
| image - and *then* copies it, in one chunk, to the target.
: stack> ( "push" stack to target)
depth 0 max 12 min
\m sp0 over 1+ \m cells - dup tsp ! ( top of D stack) swap
for tuck image-! \m cell+ next ( copy each cell as a word to D stack)
"decafbad swap image-! ( sentinel)
tsp @ image+ tsp @ \m sp0 over - t.write ( copy stack image to target) ;
| stack< *first* copies the target stack, in one chunk, to the host's RAM
| image, and *then* pulls the values out and pushes them onto the host's stack.
: stack< ( "pop" stack from target)
\m depth 0 max 12 min =if
push
tsp @ image+ tsp @ r@ \m cells t.read ( read target stack)
pop
tsp @ over ( n sp n)
for dup image-@ pop 2push \m cell+ next ( starting with top, push to R)
drop ( sp)
for 2pop push next ( pop from R to reverse order)
0
then drop ;
| Target always starts by executing the code at continue-forth, with SP
| pointing to the data stack, which contains both the data to be consumed,
| and the "Forth VM" context.
|
| When first executing a word, the host sets things up like this:
| IP = trampoline
| RP = bottom of R stack ie, empty R stack
|
| When instead continuing execution - perhaps inside a loop that contains a
| call to bug - the host sets things up like this:
| IP = saved IP
| RP = saved RP
: ?chat
chatting @ 0= if error" not connected to target" then ;
( These are implement'ed by the kernel code.)
meta
variable continue-forth
variable trampoline
forth
( NOTE: For initial execution of a Forth word, xn is cfa!)
: continue ( x0 .. xn ip rp ix - y0 .. yn ip rp ix)
?chat
stack> p@ continue-forth runwait stack<
tix ! trp ! tip ! .regs ;
meta
: cont ( ) ( continue forth execution)
tip @ trp @ tix @ ( ip rp ix) continue ;
forth
( Set rp to rp0, ix to 0, and ip to trampoline.)
-: ( cfa) ( execute target forth word)
p@ trampoline \m rp0 0 ( ip rp ix) continue ; is remote
128 array cortex-seekeys
( Default key action is to run host key code)
host-seekeys cortex-seekeys 128 cells cmove
: >target
chatting @ if >chat ^ then >image ; ( set up memory access)
( Support for dumping memory)
: 1dump ( a)
hex-bytes
| dup p ! cell* ea ! ( default ea: fetch cell)
dup p ! cell* -4 and ea ! ( default ea: fetch cell and round down)
dup _addr dup .chars
dup .addr dup .hex-bytes
dup _addr dup .hex-cells
drop ;
| Cross-ref image and target. Print line of image memory, then line of
| target memory.
: 1xref ( a)
hex-bytes
>image dup .addr dup .hex-cells
chatting @ if
-valid
>chat dup _addr dup .hex-cells
-valid
then
drop ;
( Support for interactive decompiling.)
: dec+ ( a - a' 0) 4 advance 0 ;
: dec- ( a - a' 0) -4 advance 0 ;
defer 1smart-dec
defer 1smart-dis
cortex-seekeys -4 du-mode dumping >target skip+ skip- 1dump
cortex-seekeys -4 du-mode decompiling >target dec+ dec- 1smart-dec
cortex-seekeys -4 du-mode xrefing >target skip+ skip- 1xref
cortex-seekeys -2 du-mode disasming >target dis+ dis- 1smart-dis
meta
variable target-do-colon
variable target-do-const
variable target-do-var
forth
: .target-code ( a)
p ! cell* ( code field) -2 and ( remove thumb bit)
0 disasming drop
dup p @ = if drop ." code " ^ then
0 decompiling drop ( start decompiling a colon definition)
dup \m target-do-colon @ = if drop ." : " ^ then
dup \m target-do-const @ = if drop ." constant " ^ then
dup \m target-do-var @ = if drop ." variable " ^ then
.hcell_ ." (unknown code field) " ( print value and mark as "unknown") ;
: 1dec ( a)
dup .addr .nesting space
p ! cell* dup .hcell_
dup -4 and ea ! ( default ea: fetch cell and round down)
dup .target-runtime. find-constant-chained if
.target space drop ^ then
equate? if .equate space then ;
: ?switch ( a - a | <nothing> )
dup p ! cell* ea ! ( default ea: fetch cell)
dup .target-runtime. find-constant-chained if push ( 'link)
dup .addr .nesting space
.target-code pop .target space shunt ^ then ;
| Generic decompiler. Switches modes any time a target word points to
| current address.
-: ( a) ?switch 1dis ; is 1smart-dis
-: ( a) ?switch 1dec ; is 1smart-dec
cortex-seekeys 'seekeys ! ( switch over to our bindings)
key: d ( a - a 0) dumping 0 ;
key: e ( a - a 0) decompiling 0 ; ( XXX 'f'? ':'? )
key: x ( a - a 0) xrefing 0 ;
key: i ( a - a 0) disasming 0 ;
| Fetch a vector and start disassembling the code it points to. Treat it
| like a "call" - key c - and push the i-stack.
key: v ( 'vector - vector 0) dup p ! cell* i-push disasming 0 ;
host-seekeys 'seekeys ! ( back to host bindings)
( Interactive)
( Host du available as \f du)
meta
: du ( a - a') dumping inspect ;
: dis ( a - a') disasming inspect ;
: dec ( a - a') decompiling inspect ;
forth
( Batch mode)
( Redirect to stdout to make it easier to write output to a file.)
defer dump-line
: batch-dump ( start limit cfa) is dump-line
>target ( XXX should this be >image ?)
out-channel preserve >stdout
radix preserve
istack-depth preserve istack-depth off
begin swap dump-line drop swap 2dup u< 0= until 2drop ;
( XXX create/does> instead?)
-: ( a - a' 0) dup 1dump 16 advance 0 ;
: dumps ( start limit) [ #] batch-dump ;
-: ( a - a' 0) dup 1dis dis+ ;
: disses ( start limit) [ #] batch-dump ;