-
Notifications
You must be signed in to change notification settings - Fork 30
/
chat-v3-nuttiness.mu4
336 lines (269 loc) · 10.6 KB
/
chat-v3-nuttiness.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
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
| This file is part of muforth: https://muforth.dev/
|
| Copyright 2002-2024 David Frech. (Read the LICENSE for details.)
loading MSP430 serial chat protocol v3 (core) (OBSOLETE and DEPRECATED!)
| NOTE and WARNING!
|
| This code has been retired. It is being left here as an object lesson and
| as an historical artifact.
|
| If you are using a Flash-based MSP430, such as the G2553, you should be
| using chat-v2.
|
| If you are using an FRAM-based MSP430, such as the FR6989, you should be
| using chat-v4.
| Trying something simple and traditional.
|
| Responds to the following commands. NOTE: these are hex values!
|
| 00 - 2f Idle - these command bytes are ignored
|
| 30 GetVersion - get the chat firmware commit
| 31 GetStatus - get SP, PC, SR, and CP
| 32 Run - set PC, SR, and CP and execute
| 33 ReadWords - set address, then read N words
| 34 WriteWords - set address, then write N words
| 35 GetCheck - return accumulated checksum to host
|
| The following are for older - non-CPUX? non-FRAM? - MSP430s that have
| byte-sized i/o from 0020 to 00ff and sport regular Flash memory rather than
| FRAM.
|
| 36 ReadWordsB - set address, then read N words bytewise
| 37 FlashWords - set flash command, set address, then write N words to Flash
|
| 38 - ff Idle - these command bytes are ignored
decimal
assembler
| The chat code passes back and forth between host and target - but does
| not otherwise touch - the context pointer, cp. It is used to pass
| execution context between the host and target. By default we assume it is
| the first "callee-saved" register, r4.
04 reg cp ( context pointer)
| These are all considered "caller saved" registers. They are scratch.
| Called code can freely clobber them.
12 reg top ( current accumulator)
13 reg mp ( memory pointer)
14 reg count ( word count)
15 reg check ( checksum accumulator)
forth
( Tell the disassembler about these register names.)
-: ( reg)
dup 5 u< if ( low special)
2* z" pcspsrr3cp" + 2 type ^ then
( 0011223344)
dup 12 u< if ( not special) ." r" .udec ^ then
12 - 5 * z" top mp countcheck" + 5 -trailing type ; is .regname
( cccccdddddeeeeefffff)
hex
__meta
label get-byte
begin 01 # chat-uart-ifg & bitb 0!= until
chat-uart-rxbuf & top movb ret ;c
label put-byte
begin 02 # chat-uart-ifg & bitb 0!= until
top chat-uart-txbuf & movb ret ;c
label get-word
get-byte c top push ( save low byte)
get-byte c top swpb sp )+ top bis ( combine high and low)
top check xor ( xor into checksum) ret ;c
label get-check
check top mov ( fall thru) ;c | zeroes checksum!
label put-word
top check xor ( xor into checksum)
put-byte c top swpb put-byte j ;c
| Compile the first 32 bits of the current muforth Git commit.
| When asked for the version, return these two 16-bit words, in
| little-endian order.
muforth-commit drop 8 evaluate
dup 10 >> swap ( high low)
label get-version
( commit-lo) # top mov put-word c
( commit-hi) # top mov put-word j ;c
label get-status
sp top mov ( SP)
2 # top add ( skip return address to chat-entry) put-word c
4 sp +) top mov ( PC) put-word c
2 sp +) top mov ( SR) put-word c
cp top mov ( CP) put-word j ;c
label run
2 # sp add ( skip return address to chat-entry)
get-word c ( PC) top 2 sp +) mov
get-word c ( SR) top sp ) mov
get-word c ( CP) top cp mov reti ;c
| comment TI-is-a-bunch-of-fucking-liars!
|
| NOTE! This routine is a bit tricky. The caller has pushed the address of
| the per-word routine onto the stack, so to process each word we want to
| call that routine. Unfortunately the MSP430 documentation _lies_ about how
| this works... So the call looks a little bit weird. The CPU has already
| pushed the return address when it computes the destination, so instead of
| finding the address to call at sp ) it's one word above this at 2 sp +) .
|
| Thanks, TI!
|
| TI-is-a-bunch-of-fucking-liars!
label do-words
top push ( for use of called code)
get-word c top mp mov ( set starting address)
get-byte c top tst 0!= if ( if count non-zero)
top check xor ( xor into checksum) top count mov
begin 4 sp +) call 1 # count sub 0= until
then 4 # sp add ret ;c
label read-words
do-words c ( push address of following code!)
mp )+ top mov put-word j ;c
.ndef in-ram .ndef fr6989 .and .if
label read-words-bytewise
do-words c ( push address of following code!)
mp )+ top movb top push ( save low byte)
mp )+ top movb top swpb sp )+ top bis ( combine high and low)
put-word j ;c
.then
label write-words
do-words c ( push address of following code!)
get-word c top mp ) mov 2 # mp add ret ;c
.equates. .contains FCTL1 .if
label flash-words
get-word c ( get flash command + key into top; do-words pushes this)
do-words c ( push address of following code!)
get-word c ( get data word to write to flash)
FKEY # FCTL3 & mov | clear FLOCK
2 sp +) FCTL1 & mov | write flash command - must have FKEY!
top mp ) mov 2 # mp add | write word and incr pointer
.ifdef in-ram
begin FBUSY # FCTL3 & bit 0= until
.then
FKEY # FCTL1 & mov | clear command
FKEY FLOCK + # FCTL3 & mov | set FLOCK
FCTL3 & top mov put-word j | read status and return it
;c
.else ( dummy version)
label flash-words
get-word c ( get flash command + key into top; in this code we ignore it)
do-words c ( push address of following code!)
get-word c 2 # mp add ( get word, inc memory pointer)
9658 # top mov ( fake flash status) put-word j ;c
.then
( Dispatch.)
label command-table
( 30) get-version ,
( 31) get-status ,
( 32) run ,
( 33) read-words ,
( 34) write-words ,
( 35) get-check ,
.def in-ram .def fr6989 .or .if
( 36) read-words ,
.else
( 36) read-words-bytewise ,
.then
( 37) flash-words ,
label process
get-byte c ( cmd)
top check xor ( xor into checksum)
30 # top sub ( 00 to 2f become large unsigned numbers)
process command-table - 2/ # top cmp u< if
top top add command-table top +) pc mov ( jump!)
then
( unknown... ignore!)
ret ;c
( Push registers and start conversing with host.)
label chat-entry
( call has already pushed PC)
sr push sr clr ( disable interrupts!)
begin process c again ;c
| This creates a slot for the PC which can be replaced with the address of
| native code to execute. That code can end with a return instruction, which
| will return to the call to interact below, and then re-enter chat with
| the same stack layout: a PC slot below a "protective" return address. It's
| easier to visualize than to explain. ;-)
label interact
begin chat-entry c again ;c
Vreset handler
| set SP to end of RAM
@ram #ram + # sp mov
.ifndef in-ram ( the flash-based code already did all this)
| disable watchdog timer
WDTKEY 80 + # WDTCTL & mov
.equates. .contains PM5CTL0 .if
1 # PM5CTL0 & bic | unlock i/o port pins
.then
| If this is an FRAM device, set the controller to insert 1 wait state. We
| are about to switch to a 16 MHz CPU clock, which requires 1 wait state.
.equates. .contains FRCTL0 .if
FRKEY 1 4 << + # FRCTL0 & mov | unlock FRAM controller; set 1 wait state
FRCTL0 1+ ( high byte) & clrb | lock FRAM controller
.then
| If there is a 16 MHz calibration value in the info area, use it and set
| the DCO to generate 16 MHz MCLK and SMCLK.
.equates. .contains CAL_BC1_16MHZ .if ( we need to set up the DCO trim ourselves)
"0ffff # CAL_DCO_16MHZ & cmp | if Info A has been erased, don't use trim
0= if
| Set defaults that might work to get chat going...
%10_00_1111 # BCSCTL1 & movb | RSEL=15
a0 # DCOCTL & movb | not quite the middle?
else
| Set clock to factory-calibrated 16 MHz
| See G2553 datasheet, p15
CAL_BC1_16MHZ & BCSCTL1 & movb
CAL_DCO_16MHZ & DCOCTL & movb
then
BCSCTL2 & clrb
.else
| If device contains a CS module, it has been factory calibrated. All we
| need to do is to set the right divisors for the DCO to generate 16 MHz
| MCLK and SMCLK clocks.
.equates. .contains CSCTL0 .if ( DCO trimmed at factory; just choose divisors)
| Right now this is for the FR6989. But other devices should work too.
CSKEY # CSCTL0 & mov | unlock CS module
%0100_1000 # CSCTL1 & mov | set DCORSEL=1, DCOFSEL=4 (16 MHz)
CSCTL3 & clr | set DIVM and DIVS to /1
CSCTL0 1+ ( high byte) & clrb | lock CS module
.else
error" No BCS calibration or CS module found. Your clock is unsupported."
.then .then
| Configure UART for 115,200 bps, 8 bits, no parity
81 # chat-uart-ctl1 & bisb | enable SMCLK
| hold UART in reset while configuring
chat-uart-ctl0 & clrb | reset to defaults: 8 bits, no parity, 1 stop
chat-uart-br1 & clrb | set baud rate divisor
.ifndef slow-chat
8 # chat-uart-br0 & movb | 16,000,000/16/115,200
.else
#104 # chat-uart-br0 & movb | 16,000,000/16/9600
.then
.ifdef chat-uart-mctlw
chat-uart-mctlw & \f swap # \f swap mov
.else
chat-uart-mctl & \f swap # \f swap movb
.then
01 # chat-uart-ctl1 & bicb | bring UART out of reset
| Connect correct port pins to UART RXD and TXD, based on chat-port-*
| equates in the chip's equates file. Only two of the following lines
| will match and get compiled.
.ifdef chat-port-sel0-ones chat-port-sel0-ones # chat-port-sel0 & bisb .then
.ifdef chat-port-sel1-ones chat-port-sel1-ones # chat-port-sel1 & bisb .then
.ifdef chat-port-sel0-zeros chat-port-sel0-zeros # chat-port-sel0 & bicb .then
.ifdef chat-port-sel1-zeros chat-port-sel1-zeros # chat-port-sel1 & bicb .then
.equates. .contains FCTL2 .if ( Flash control register)
| Set up flash clock; needs to be between 257 and 476 kHz (no joke)
| We're running at 16M, so set it to 16M/40 = 400k.
FKEY #39 ( divisor-1) + %0100_0000 ( MCLK) + # FCTL2 & mov
.then
.then ( not in-ram)
chat-entry # push ( so it's always available at the top of the stack)
| This is here so that native code can simply return and it will
| re-enter the chat code with a proper stack.
begin interact c again ;c
| Set BSL flag to _not_ mass erase if wrong password entered during BSL
| "hello" sequence.
here
.equates. .contains Vbslkey .if
Vbslkey goto 0 ,
.else .equates. .contains Vbslsig1 .if
Vbslsig1 goto 0aaaa dup , , ( sig1 and sig2)
.else
error" Do you have to do something special to prevent the BSL from erasing chip?"
.then .then
goto