-
Notifications
You must be signed in to change notification settings - Fork 30
/
chat4.mu4
270 lines (215 loc) · 8.4 KB
/
chat4.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
| This file is part of muforth: https://muforth.dev/
|
| Copyright 2002-2024 David Frech. (Read the LICENSE for details.)
loading MSP430 serial chat protocol v4 (core)
| This is for FRAM targets *only*. I created it because of the truly
| terrible USB-CDC UART in the ezFET on the MSP430FR6989 Launchpad. Any
| time the host sends bytes and then waits for a response from the target,
| there is a huge latency. Throughput drops to hundreds of bytes per second.
| It feels like working over a 2400 bps modem!
|
| By minimizing these "turnarounds" I hope to speed things up - a lot.
|
| This version is "streaming" style like v2, but uses the GetStatus and Run
| commands - which immediately send the registers over the wire - from v3.
|
| I'm giving up on v3 because:
|
| (1) It's too complicated to do a chat that works for both Flash parts
| with byte-wide I/O *and* FRAM parts, and still fit everything. The FR6989,
| eg, has more vectors - starting at ff80 instead of ffc0 - so we lose 64
| bytes of space.
|
| (2) Streaming style is simpler and takes less code, and even though more
| bytes are going down the wire, as long as they are going in the same
| direction, it's not a problem.
|
| I'm also going to leave out "paranoid" mode (the checksumming dance)
| because I never use it and it seems unnecessary.
| Taking inspiration from the wildly successful HC08 serial chat protocol.
|
| Responds to the following commands. NOTE: these are hex values!
|
| 00 - 3f Idle - these command bytes are ignored
|
| 40 VersionAddr - get the address of the version commit
| 41 SetAddr - set the memory address pointer
| 42 GetStatus - get SP, PC, SR, and CP
| 43 Run - set SP, PC, SR, CP, and execute
| 44 ReadWords - read N words, incrementing by 2 as we go
| 45 WriteWord - write a word to memory, incr pointer by 2
| 46 WriteByte - write a byte to memory, incr pointer by 1
| 47 SetFlashCmd - set flash command, clear flash status
| 48 GetFlashStatus - read accumulated flash status word
| 49 FlashWord - write a word to flash, incr pointer
|
| 4a - 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 fcmd ( flash command)
13 reg fstat ( flash status)
14 reg top ( current accumulator)
15 reg mp ( memory pointer)
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" fcmd fstattop mp " + 5 -trailing type ; is .regname
( cccccdddddeeeeefffff)
hex
__meta
| Compile the first 32 bits of the current muforth Git commit.
| These will always be the first four bytes of the file, so verify can be
| smart about ignoring them.
label version
muforth-commit drop 8 evaluate
dup 10 >> swap ( high low) , ,
( The commit is followed by a pointer to the chat entry code.)
label chat-entry-vector 0 ,
| Load the device-specific init code, and device-specific versions of
| get-byte and put-byte.
( G2553 uses chat2 right now, but having this here might be helpful.)
.ifdef g2553 ld target/MSP430/device/g2553/chat-support.mu4 .then
.ifdef fr6989 ld target/MSP430/device/fr6989/chat-support.mu4 .then
.ifdef f5529 ld target/MSP430/device/f5529/chat-support.mu4 .then
label get-word
get-byte c top push ( save low byte)
get-byte c top swpb sp )+ top bis ( combine high and low)
ret ;c
label put-word
put-byte c top swpb put-byte j ;c
label version-address
version # mp mov ret ;c
label set-address
get-word c top mp mov ret ;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
mp sp mov ( set SP from memory pointer!)
get-word c ( PC) top 2 sp +) mov
get-word c ( SR) top sp ) mov
get-word c ( CP) top cp mov
label empty-handler
reti ;c
label read-words
get-byte c top tst 0!= if ( if count non-zero) top push
begin mp )+ top mov put-word c 1 # sp ) sub 0= until
2 # sp add
then ret ;c
label write-word
get-word c top mp ) mov 2 # mp add ret ;c
.equates. .contains FCTL1 .if
label set-flash-command
get-word c top fcmd mov | receive and save flash command from host
fstat clr | clear flash status
ret ;c
| Return accumulated flash status word to host. For safety, also clear the
| fcmd register.
label get-flash-status
fcmd clr fstat top mov put-word j ;c
| Execute the flash command saved in fcmd. After command completes, read
| the flash status and OR it into fstat. According to the user manual,
| interrupts are automatically disabled during any flash operation.
label flash-word
get-word c | receive word to flash
FKEY # FCTL3 & mov | clear FLOCK
fcmd 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
FCTL3 & top mov | read status word
FKEY FLOCK + # FCTL3 & mov | set FLOCK
top fstat bis | OR resulting flash status into fstat
ret ;c
.else ( FRAM device)
( We need this to change the MPU settings on FRAM devices.)
label write-byte
get-byte c top mp ) movb 1 # mp add ret ;c
.then
( Dispatch.)
label command-table
( 40) version-address ,
( 41) set-address ,
( 42) get-status ,
( 43) run ,
( 44) read-words ,
( 45) write-word ,
.equates. .contains FCTL1 .if ( flash device)
( 46) write-word , ( host never uses this on flash devices)
( 47) set-flash-command ,
( 48) get-flash-status ,
( 49) flash-word ,
.else ( FRAM device)
( 46) write-byte ,
.then
label process
get-byte c ( cmd)
40 # top sub ( 00 to 3f 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
| We no longer disable interrupts by default when entering chat.
begin process c again ;c
chat-entry chat-entry-vector image-! ( patch the vector)
| 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
| If the device has at least 2 KiB of ram, push a vector table of
| "empty" entries. This gives the user the option to have a
| ram-resident vector table for easy debugging of interrupt-driven
| code.
#ram 2 Ki \f u< .not .if ( \f is necessary to get forth, not asm, u< )
#vectors 2/ # top mov ( #cells) begin
empty-handler # push
1 # top sub 0= until
.then
| Call the device-specific initialization code. This will set up ports,
| clocks, UART, etc, and flash leds, if any.
device-init c
.then
| Initialize the UART.
uart-init c
| 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
| If possible, set a 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)
.then .then
goto