-
Notifications
You must be signed in to change notification settings - Fork 0
/
protocol.file-xfer.zmodem.spin2
431 lines (354 loc) · 13.4 KB
/
protocol.file-xfer.zmodem.spin2
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
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
{
--------------------------------------------
Filename: protocol.file-xfer.zmodem.spin2h
Author: Jesse Burt
Description: ZModem file transfer protocol library (P2 version)
Copyright (c) 2022
Started Dec 19, 2021
Updated Nov 15, 2022
See end of file for terms of use.
--------------------------------------------
}
#ifndef ZM_COMMON
#include "zmodem.common.spinh"
#endif
VAR
long _subpkt_sz
long _file_sz
long _ptr_data
long _crc32
word _crc16
byte _rxflags[4], _txflags[4] ' position/flags
byte _rxbuff[2058] ' accommodate worst case size
' (1k of entirely escaped chars
' plus frame end with all CRC
' bytes escaped)
byte _file_nm[13] ' 8.3 format, NUL
OBJ
crc : "math.crc"
str : "string"
VAR
long putchar, getchar ' pointers to PHY char tx/rx methods
PUB init(ptr_put, ptr_get)
' Set pointers to PHY putchar() and getchar() methods
putchar := ptr_put
getchar := ptr_get
_subpkt_sz := 1024
PUB check_crc(): crc_read | i
' Read CRC32 from remote device and compare it to calculated CRC
repeat i from 0 to 3 ' read the CRC
crc_read.byte[i] := zdl_read()
!_crc32
if (crc_read <> _crc32)
return ERROR
PUB get_file_info(): frm_t | tmp[3], idx, rx
' Read file information subpacket
' Returns: ZFILE frame type on success, ERROR otherwise
if (zget_hdr() == ZFILE)
_crc32 := $ffff_ffff ' init CRC
bytefill(@_file_nm, 0, 13) ' extract filename
idx := 0
repeat
rx := zgetchar{}
_file_nm[idx++] := rx
until (rx == NUL)
longfill(@tmp, 0, 3) ' extract file size
idx := 0
repeat
rx := zgetchar{}
tmp.byte[idx++] := rx
until (rx == " ")
tmp.byte[idx] := NUL ' terminate string
_file_sz := str.atoi(@tmp)
repeat ' don't care about the rest
tmp := zgetchar{} ' of the file info
until (tmp == NUL)
return ZFILE
else
return ERROR ' wrong or bad frame type
PUB file_name(): p_nm
' Get currently set file name
' Returns: pointer to string
return @_file_nm
PUB file_size(): sz
' Get currently set file size
' Returns: integer
return _file_sz
PUB send_file(ptr_status): status | xfer, st_pos, pos, frm_end
' Send a previously set up file
' ptr_status: pointer to 2 longs to provide current status
' ptr_status[0]: total bytes transferred
' ptr_status[1]: ready to transfer
long[ptr_status][1] := 0
zmtx_hex_header(ZRQINIT, 0)
repeat until (zget_hdr() == ZRINIT)
set_tx_flags(ZF0, 0)
set_tx_flags(ZF1, 0)
set_tx_flags(ZF2, 0)
set_tx_flags(ZF3, 0)
zmtx_bin_header(ZBIN32, ZFILE, @_txflags)
zmtx_file_info()
repeat until (zget_hdr() == ZRPOS)
zmtx_bin_header(ZBIN32, ZDATA, @st_pos) ' start position
st_pos := 0
xfer := 0
long[ptr_status][0] := 0
long[ptr_status][1] := 1
repeat pos from 0 to (_file_sz-1) step _subpkt_sz
if (pos < (_file_sz-_subpkt_sz)) ' before last subpacket?
frm_end := ZCRCQ ' Q: ACK, G: no ACK
else
frm_end := ZCRCE ' frame ends
xfer += zmtx_data_subpkt(_ptr_data, pos, _subpkt_sz, frm_end)
long[ptr_status][0] := xfer ' copy total transferred to status
if (frm_end == ZCRCQ)
if (zget_hdr() <> ZACK)
return ERROR ' error from receiver
zmtx_bin_header(ZBIN32, ZEOF, @_file_sz)
repeat until (zget_hdr() == ZRINIT)
zmtx_hex_header(ZFIN, 0)
repeat until (zget_hdr() == ZFIN)
putchar("O") ' over and out
putchar("O")
long[ptr_status][1] := 0
PUB set_file_data_ptr(ptr_data)
' Set location of file data to transfer
' ptr_data: pointer to file's data
_ptr_data := ptr_data
PUB set_file_name(ptr_str)
' Set name of file to transfer
' ptr_str: pointer to string
bytemove(@_file_nm, ptr_str, strsize(ptr_str))
PUB set_file_sz(sz)
' Set size of file to transfer
_file_sz := sz
PUB set_subpkt_sz(sz)
' Set subpacket/frame size
_subpkt_sz := sz
PUB set_tx_flags(zf, val)
' Set transmit flags
' zf: flag number
' val: flag(s) to set
_txflags[zf] := val
PUB subpkt_sz(): sz
' Get currently set subpacket/frame size
' Returns: integer
return _subpkt_sz
PUB zmrx_bin32_hdr(): frm_t | i
' Receive a binary header, with CRC32
' Returns: frame type of header received
frm_t := getchar():1
_crc32 := $ffff_ffff ' init CRC
upd_crc32(frm_t)
repeat i from 0 to 3
_rxflags[i] := getchar():1
upd_crc32(_rxflags[i])
if (check_crc() <> ERROR)
return frm_t
else
return ERROR
PUB zmrx_frame_end(): type | i, crc_read
' Receive frame end
' Returns: frame end type
repeat until getchar():1 == ZDLE
type := zgetchar() ' frame end type
if (check_crc() <> ERROR)
return type
else
return ERROR ' frame end CRC bad
PUB zmrx_hex_hdr(): frm_t | read_crc, i
' Receive a HEX header
' Returns: frame type of header received
_crc16 := 0 ' init CRC
frm_t := zm_gethex() ' get frame type
upd_crc16(frm_t)
' ser.printf(string("RX frm type: %x\n"), frm_t)
repeat i from 0 to 3 ' get pos/flags ASCII
_rxflags[i] := zm_gethex()
upd_crc16(_rxflags[i])
read_crc := 0
read_crc.byte[1] := zm_gethex() ' get CRC16
read_crc.byte[0] := zm_gethex()
' ser.printf(string("read: %x calc: %x\n"), read_crc, _crc16)
if (read_crc == _crc16)
return frm_t ' CRC good; return frame type
else
return ERROR ' CRC bad
PUB zm_gethex(): ihex | tmp
' Receive two hex digits in ASCII and return the integer equivalent
tmp.byte[0] := getchar():1
tmp.byte[1] := getchar():1
tmp.byte[2] := NUL
return str.atoib(@tmp, 16)
PUB zmtx_bin_header(encoding, frm_t, posflag) | i, b
' transmit a binary header
' encoding: ZBIN or ZBIN32
' frm_t: frame type
' posflag: 4-byte position, or flags
putchar(ZPAD)
putchar(ZDLE)
putchar(encoding)
zmtx_enc(frm_t)
_crc32 := $ffff_ffff ' init CRC
upd_crc32(frm_t)
repeat i from 0 to 3 ' transmit position/flags
b := byte[posflag][i]
zmtx_enc(b)
upd_crc32(b)
!_crc32 ' invert CRC
if (encoding == ZBIN32) ' transmit CRC
repeat i from 0 to 3
zmtx_enc(_crc32.byte[i])
else
return ERROR
PUB zmtx_data_subpkt(ptr_data, st, len, frameend): xfer | pos
' Transmit data subpacket
' ptr_data: pointer to buffer of data to transmit
' st: starting position within data
' len: length of data to transmit
' frameend: frame end type
' Returns: number of bytes transferred
_crc32 := $ffff_ffff ' init CRC
xfer := 0
repeat pos from st to (st+len-1)
upd_crc32(byte[ptr_data][pos]) ' calc 'rolling' CRC
xfer += zmtx_enc(byte[ptr_data][pos]) ' transmit ZDLE-encoded data
upd_crc32(frameend) ' add frame end byte to the CRC
!_crc32
zmtx_frame_end(frameend)
PUB zmtx_enc(ch): xfer | ech
' Transmit ZDLE-encoded char
' Returns: number of bytes transferred
if (lookdown(ch: CR, ZDLE, DLE, XON, XOFF, DLE80, XON80, XOFF80))
putchar(ZDLE) ' escape next char
putchar(ch ^ $40) '
else
putchar(ch) ' not a ctrl code; don't escape
xfer++
PUB zmtx_file_info() | i, tmp[3], b
' Send file information
_crc32 := $ffff_ffff ' init CRC
repeat i from 0 to strsize(@_file_nm) ' send filename
b := byte[@_file_nm][i]
zputchar(b)
str.itoa(_file_sz, @tmp) ' send file size
repeat i from 0 to strsize(@tmp)-1
b := byte[@tmp][i]
zputchar(b)
zputchar(" ")
repeat 5 ' send other file info as 0
zputchar("0") ' (don't care)
zputchar(" ")
zputchar(NUL) ' terminate subpacket data
upd_crc32(ZCRCW) ' incorp. frame end into CRC
!_crc32
zmtx_frame_end(ZCRCW)
putchar(XON) ' trailing XON
PUB zmtx_frame_end(type) | i
' Transmit frame end
' type: frame end type
putchar(ZDLE)
putchar(type)
repeat i from 0 to 3 ' send the CRC
zmtx_enc(_crc32.byte[i])
PUB zmtx_hex_header(frm_t, posflag) | i
' Transmit a hex header
' type: frame type
' posflag: 4-byte position, or flags
putchar(ZPAD)
putchar(ZPAD)
putchar(ZDLE)
putchar(ZHEX)
zputhex(frm_t) ' frame type
repeat i from 0 to 3 ' transmit pos/flag bytes
zputhex(posflag.byte[i])
_crc16 := 0 ' init CRC
upd_crc16(frm_t) ' calc CRC over frame type
repeat i from 0 to 3 ' and pos/flag bytes
upd_crc16(posflag.byte[i])
zputhex(_crc16.byte[1]) ' send the CRC
zputhex(_crc16.byte[0])
putchar(CR) ' terminate header
putchar(LF)
if ((frm_t <> ZFIN) and (frm_t <> ZACK)) ' if not ZFIN or ZACK frame,
putchar(XON) ' append an XON
PUB zdl_read(): ch
' Read a data subpacket byte
' NOTE: Checks for ZModem escape encoding to handle
' * aborted transmission (CAN)
' * frame end types
' * other escaped characters
ch := getchar():1
if (ch == ZDLE) ' ZDLE? _could_ be CAN (same)
repeat
ch := getchar():1 ' check for CAN
if (ch == CAN)
ch := getchar():1
if (ch == CAN)
ch := getchar():1
if (ch == CAN)
return GOTCAN ' 4xCAN recv'd: tell the caller
case ch
ZCRCE, ZCRCG, ZCRCQ, ZCRCW: ' frame end
return (ch | GOTOR)
{ZRUB0: ' XXX these really needed?
return $7F
ZRUB1:
return $FF
19, 147, 17, 145: '19=DC3, 147=SET TX ST, 17=DC1, 145=PVT USE 1
next}
other: ' escaped char
if ((ch & $60) == $40)
return (ch ^ $40)
return ERROR ' bad escape sequence
else
return ch ' wasn't escaped; just literal
PUB zgetchar(): ch
' Receive a character (unprocessed) and update the CRC
ch := getchar():1
upd_crc32(ch)
PUB zget_hdr(): frm_t | encoding
' Get frame header, and receive a binary or hex frame type
repeat until (getchar():1 == ZPAD)
repeat until (getchar():1 == ZDLE)
encoding := getchar():1
case encoding
ZBIN: 'XXX not handled yet
return ERROR
ZBIN32:
return zmrx_bin32_hdr()
ZHEX:
return zmrx_hex_hdr()
other:
return ERROR
PUB zputhex(val) | tmp
' Send a two-digit hex number, given integer val
tmp := str.hexs(val, 2)
putchar(byte[tmp][0])
putchar(byte[tmp][1])
PUB zputchar(ch)
' Send a character (unprocessed) and update the CRC
putchar(ch)
upd_crc32(ch)
PUB upd_crc16(crcbyte)
' Update CRC16 with crcbyte
_crc16 := crc.crc16(@crcbyte, 1, _crc16, 0, crc.POLY16_XYZMODEM, false, false)
PUB upd_crc32(crcbyte)
' Update CRC32 with crcbyte
_crc32 := crc.crc32(@crcbyte, 1, _crc32, 0, crc.POLY32_ZMODEM, false, false)
DAT
{
Copyright 2022 Jesse Burt
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute,
sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
}