-
Notifications
You must be signed in to change notification settings - Fork 30
/
chat-iic-host.mu4
161 lines (130 loc) · 6.09 KB
/
chat-iic-host.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
| This file is part of muforth: https://muforth.dev/
|
| Copyright 2002-2024 David Frech. (Read the LICENSE for details.)
loading AVR chat over I2C over USB (via S08JS16) (host)
| The following is code to talk to my simple "debug stub" running on an
| Atmel AVR, using its default slave address, 0fe. NOTE: Slave addresses
| are sent shifted left, with LSB = 0.
|
| You can find the debug stub's code in target/AVR/chat-iic-core.mu4
|
| This code talks I2C to the AVR via an S08JS16, which runs USB firmware
| (!!) allowing it to turn USB control requests into I2C bus packets. That
| code is in target/S08/iic-chat-usb-core.mu4, but there is *yet another*
| intermediate layer: we talk to the USB code via code in
| target/S08/iic-chat-usb-host.mu4. Phew!
hex
| Taking inspiration from the wildly successful HC08 serial chat protocol.
|
| Responds to the following commands. NOTE: these are hex values!
|
| 00 - 0f idle - these command bytes are ignored
| 1c - ff idle - these command bytes are ignored
|
| 10 version-addr - get the address of the version commit
| 11 set-addr - set memory address
| 12 get-addr - get memory address
| 13 run - push mem addr as ra; set mem addr; execute
|
| 14 read-flash - read one byte of flash, incr Z
| 15 read-data - read one byte of ram/data space, incr Z
| 16 read-eeprom - read one byte of xram, incr Z
|
| 17 write-flash - write a word to flash page buffer, incr Z by 2
| 18 write-data - write one byte to ram/data space, incr Z
| 19 write-eeprom - write one byte to eeprom, incr Z
|
| 1a app-start
| 1b app-stop
variable iic-slave 0d0 iic-slave ! ( master)
| "ix.<something>" words are IIC chat *transport* code.
| "i.<something>" words are the high-level IIC chat interface code.
( This uses pad as buffer.)
: ix.setup-pad ( len cmd - cmd slave len buf) swap iic-slave @ swap pad ;
: ix.read ( len cmd) ix.setup-pad iic.Read ;
: ix.write ( len cmd) ix.setup-pad iic.Write ;
: ix.cmd ( cmd) 0 swap ix.write ;
: ix.get-version ( v) 10 ix.cmd 4 14 ix.read pad lew@ ;
: ix.set-addr ( a) pad leh! 2 11 ix.write ;
: ix.get-addr ( - a) 2 12 ix.read pad leh@ ;
: ix.run ( dp) pad leh! 2 13 ix.write ;
( This uses a buffer passed to us.)
: ix.setup-buf ( buf a u cmd - cmd slave u buf)
cr ." ix.setup-buf in " 3 nth u. 2 nth u. over u. dup u.
rot ix.set-addr ( buf u cmd) rot push ( u cmd)
iic-slave @ rot ( cmd slave u) pop
cr ." ix.setup-buf out " 3 nth u. 2 nth u. over u. dup u.
cr
;
: ix.read-buf ( buf a u cmd) ix.setup-buf iic.Read ;
: ix.write-buf ( buf a u cmd) ix.setup-buf iic.Write ;
| Unlike the -buf words above, this is used for byte- or word-at-a-time
| reads or writes.
: ix.setup-chunk ( buf a u - u) swap ix.set-addr swap m ! ;
| ------------------------------------------------------------------------
| Chat interface
| ------------------------------------------------------------------------
: i.hello ( - #chunk)
iic.Hello ( start up USB command loop)
cr ." Chat firmware version " ix.get-version
radix preserve hex sep preserve -sep <# 4# 4# #> type
#128 ;
| NOTE: To execute code on the target, we pass two values to chat: a PC in
| return address (ra) form, and data stack pointer (DP). Generally, the PC
| will point to a trampoline that will marshal and unmarshal argument
| values, one of which will be a pointer to *another* piece of code to
| execute. After *that* code executes, it returns to the trampoline, which
| (possibly) pushes values onto the data stack, and then returns to chat.
|
| It sounds complicated, but moving the trampoline *out* of chat and into
| the user's code makes the chat code simpler and shorter, and
| - importantly - makes the process of executing target code much more
| flexible, since the calling conventions, stack pointer choices, etc, are
| no longer baked into the chat code. Instead, each type of code can have
| a trampoline perfectly suited to it.
|
| As a concrete example, when calling threaded Forth code, the data stack
| pointer (DP) is usually the *machine* stack pointer (MP), and the return
| stack pointer (RP) is a generic pointer register.
|
| When calling assembler or non-threaded Forth code, these are reversed!
|
| Having separate trampolines for each keeps these details out of the chat
| code.
: i.run ( pc dp)
swap \m >ra ix.set-addr ( set mem addr to PC in ra form)
ix.run ( set mem addr to DP and execute code) ;
| Get the current value of the mem address pointer; this is our data stack
| pointer (DP).
: i.get-status ( - dp) ix.get-addr ;
( Interact code handles which memory space we are reading.)
: i.read-space ( buf a u space) 14 + ix.read-buf ;
: ix.write-flash ( word flash-cmd - SPMCSR)
pad 2 + c! pad leh! 3 17 ix.write 1 1c ix.read pad c@ ;
: i.write-data ( buf a u) 18 ix.write-buf ;
: ix.write-eeprom ( byte - EECR)
pad c! 1 19 ix.write 1 1d ix.read pad c@ ;
: i.write-eeprom ( buf a u)
ix.setup-chunk for m* ix.write-eeprom u. next ;
: i.app-start 1a ix.cmd ;
: i.app-stop 1b ix.cmd i.get-status drop ;
: i.flash-begin ;
| After flashing, we have to re-enable the RWW section and start the app
| running again.
: i.flash-end
0 ix.set-addr ( beginning of flash)
0 %0001_0001 ( RWWSRE + SPMEN) ix.write-flash u. ( re-enable RWW section) ;
: i.erase ( a)
ix.set-addr 0 %0000_0011 ( PGERS + SPMEN) ix.write-flash u. ( erase page) ;
| For each word in buf, write it to the chip's flash page buffer, then send
| the page program command.
| Initially, send word + flash command over and over. Later, put them into
| a buffer and do one big write.
: i.program ( buf a u)
over push ( save addr) ix.setup-chunk
1+ 2/ for m* m* lohi> %0000_0001 ( SPMEN) ix.write-flash u. next
pop ix.set-addr ( set address again)
0 %0000_0101 ( PGWRT + SPMEN) ix.write-flash u. ( write page buffer to flash) ;
: iic chat-via
i.hello i.get-status i.run i.read-space i.write-data i.write-eeprom
i.app-start i.app-stop i.flash-begin i.flash-end i.erase i.program ;