-
Notifications
You must be signed in to change notification settings - Fork 30
/
chat-loop.mu4
188 lines (142 loc) · 6.25 KB
/
chat-loop.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
| This file is part of muforth: https://muforth.dev/
|
| Copyright 2002-2024 David Frech. (Read the LICENSE for details.)
( Chat entry and command processing loop.)
__meta
hex
| Compile the first 32 bits of the current muforth Git commit. When asked
| for the version, return these four bytes, in little-endian order.
here
muforth-commit drop 8 evaluate >3210 c, c, c, c,
label version-cmd
( here) z ldiw ret ;c
| Host will use set-addr to set Z to ra of PC to call. run-cmd will push Z, then
| host will send another word for Z, and then execute the code.
label run-cmd
z pushw ( PC to jump to; already in ra form)
( fall through) ;c
label set-addr-cmd
recv-word rcall x z movw ret ;c
| NOTE: This can be called after running a piece of code to get some kind
| of status or address back to the host.
label get-addr-cmd
z x movw send-word rjmp ;c
| If the app RESET vector is non-empty, call it.
label app-call
0 ( app RESET vector) z ldiw
pmz+ g0 ld pmz g1 ld g1 g0 and g0 com 0= not if ( valid vector)
0 jmp
then ret ;c
| Make the chat task the only one in the task loop by pointing its next
| pointer to itself.
label chat-only
chat-user-area h0 ldi
chat-user-area 1+ h0 sts ret ;c
| To start the app, we make the chat task the only one in the task loop and
| then enable the watchdog. In 16ms (or so) it will reset the chip. This
| will clear the ram and then call into the app to create and start the app
| tasks.
label app-start-cmd
chat-only rcall ( fall thru) ;c
label watchdog-on
8 ( WDE) 0 ( 2K = 16ms timeout) + h0 ldi ( fall thru) ;c
label change-watchdog
18 ( WDCE + WDE) h1 ldi wdr WDTCSR h1 out WDTCSR h0 out ret ;c
| NOTE: When we stop the app, we disable interrupts, in case it is
| using them. When it gets restarted, it is up to the app to re-enable
| interrupts.
| To stop the app, we make the chat task the only one in the task loop and
| then disable the watchdog.
label app-stop-cmd
| If the app RESET vector is non-empty, call it to stop the app. Because
| h2 is zero, the app code will do its "stop" behavior rather than its
| "start" behavior (creating tasks).
h2 clr app-call rcall
cli chat-only rcall ( fall thru) ;c
label watchdog-off
h0 clr change-watchdog rjmp ;c
( Command dispatch.)
label command-table
( 10) version-cmd rjmp
( 11) set-addr-cmd rjmp
( 12) get-addr-cmd rjmp
( 13) run-cmd rjmp
| We want to group the read commands together so "du" can read from the
| current memory "space".
( 14) read-flash-cmd rjmp ( space 0)
( 15) read-data-cmd rjmp ( space 1)
( 16) read-eeprom-cmd rjmp ( space 2)
( 17) write-flash-cmd rjmp
( 18) write-data-cmd rjmp
( 19) write-eeprom-cmd rjmp
( 1a) app-start-cmd rjmp
( 1b) app-stop-cmd rjmp
;c
label process
recv-command-byte rcall
10 xl subi ( 00 to 0f become large unsigned numbers)
process command-table - 2/ xl cpi u< if xh clr
command-table 2/ ( word address) negate >hilo xl subi xh sbci
xl push xh push ( big-endian!)
then ( unknown... return, and get called again)
ret ;c
label chat-task
serial-init rcall ( initialize serial interface)
( XXX what about I2C? Is this the right place for this?)
.labels. .contains i2c-restart-hook .if
hooks i2c-restart-hook
.then
begin process rcall again ;c
RESET handler
label system-startup
| First things first. Turn off the watchdog, in case we got a watchdog
| reset and it's still enabled. At reset, SP is set to point to the end
| of RAM, so it's safe to make subroutine calls without setting it.
watchdog-off rcall
| Let's start with a clean slate, by setting every byte of the RAM to a
| known state. We do this in stages. First we set each byte of the task
| stack page - the last page of the RAM - to hex 55 (so it's easier to
| see how much space each task is really taking); then, using a temporary
| stack *below* the stack page, we initialize the task user areas; and,
| lastly, we set the rest of the RAM to zero.
|
| Set up a temporary stack - so we can call code to create the tasks -
| below the task stacks. Since the stacks consume at most a 256 byte
| page, the page below the last one will do fine.
|
| Be careful though: we don't want to clobber the last byte of the page
| below the task stacks, since we are going to keep a count of watchdog
| resets there. That's why we subtract *two* before setting SP - one,
| because the AVR uses an empty descending stack; and one to skip the
| watchdog reset count byte!
@ram #ram + #256 - 2 - x ldiw ( beginning last page of the ram, minus two)
x sp! 2 x adiw
( Set every byte of the stack page to 55 hex.)
55 h0 ldi begin x+ h0 st xl tst 0= until
( Create the tasks, starting with the chat task.)
chat-user-area y ldiw ( loads both yh and yl; subsequently only yl needs loading)
chat-task >ra h0 ldiw ( starting pc, in ra form)
create-task rcall ( leaves h2 pointing to chat-user-area)
| If the app RESET vector is non-empty, call it to create the app tasks.
| Because h2 is non-zero, the app code will do its "start" behavior
| (creating tasks) rather than its "stop" behavior.
app-call rcall
| Close the circle. h2 has pointer to last task created. Put it into the
| chat-task's next pointer, and leave y pointing to the chat-user-area.
chat-user-area yl ldi 1 h2 stu
( Read the reset flags; save them in g1; and zero them.)
MCUSR g1 in g0 clr MCUSR g0 out ( clear all reset flags)
( Finally, write zeros to the ram, stopping 257 bytes from the end.)
@ram x ldiw ( beginning of ram)
#ram #256 - 1- w ldiw ( size of ram, minus the last page, minus one)
begin x+ g0 st 1 w sbiw 0= until
| If we experienced a watchdog reset, count it in the last byte of ram
| before the stack page; otherwise, zero that byte.
3 ( WDRF) g1 sbrs never ( watchdog reset)
x@ g0 ld g0 inc
then
x+ g0 st ( store zero or incremented watchdog reset count)
| The tasking code only changes the low half of the stack pointer (SPL).
| We need to set SPH correctly before we start the first task!
SPH yh out ( point SPH to task stack page)
run-task rjmp ( start chat task) ;c