Subversion Repositories Spectranet

[/] [trunk/] [rom/] [tnfs_core.asm] - Blame information for rev 442

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 215 winston
;The MIT License
2
;
3
;Copyright (c) 2009 Dylan Smith
4
;
5
;Permission is hereby granted, free of charge, to any person obtaining a copy
6
;of this software and associated documentation files (the "Software"), to deal
7
;in the Software without restriction, including without limitation the rights
8
;to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
;copies of the Software, and to permit persons to whom the Software is
10
;furnished to do so, subject to the following conditions:
11
;
12
;The above copyright notice and this permission notice shall be included in
13
;all copies or substantial portions of the Software.
14
;
15
;THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
;IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
;FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
;AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
;LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
;OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
;THE SOFTWARE.
22 399 winston
.include        "tnfs_defs.inc"
23
.include        "tnfs_sysvars.inc"
24
.include        "spectranet.inc"
25
.include        "sysvars.inc"
26
.include        "fcntl.inc"
27
.include        "sockdefs.inc"
28 442 winston
.include        "errno.inc"
29 215 winston
 
30
; General TNFS core utility functions.
31
;------------------------------------------------------------------------
32
; F_tnfs_strcpy
33
; Copy a null terminated string in HL to the buffer in DE, up to B bytes long
34
; Returns with DE set to the buffer end + 1
35 399 winston
.text
36
.globl F_tnfs_strcpy
37
F_tnfs_strcpy:
38
.loop1:
39 215 winston
        ld a, (hl)
40
        ld (de), a
41
        inc de
42
        and a                   ; NULL terminator?
43
        ret z
44
        inc hl
45 399 winston
        djnz .loop1
46 215 winston
        xor a                   ; if we exhaust the maximum length,
47
        ld (de), a              ; make sure there's a NULL on the end.
48
        inc de
49
        ret
50
 
51
;------------------------------------------------------------------------
52
; F_tnfs_strcat
53
; Concatenates two null terminated strings.
54
; Arguments             HL = pointer to first string buffer
55
;                       DE = pointer to string to concatenate
56
;                       BC = buffer size in bytes
57
; On return, carry is set if the buffer was too small
58 399 winston
.globl F_tnfs_strcat
59
F_tnfs_strcat:
60 215 winston
        ; find the end of the first string
61
        dec bc                  ; guarantee that we can null terminate it
62 399 winston
.findend2:
63 215 winston
        ld a, (hl)
64
        and a
65 399 winston
        jr z, .string_end2
66 215 winston
        inc hl
67
        dec bc
68
        ld a, b
69
        or c
70 399 winston
        jr nz, .findend2
71 215 winston
        scf                     ; buffer too short
72
        ret
73 399 winston
.string_end2:
74 215 winston
        ld a, (de)              ; second string
75
        ld (hl), a
76
        and a                   ; null?
77
        ret z
78
        inc hl
79
        inc de
80
        dec bc
81
        ld a, b
82
        or c
83 399 winston
        jr nz, .string_end2
84 215 winston
        ld (hl), 0              ; add a terminator
85
        scf                     ; buffer too small
86
        ret
87
 
88
;------------------------------------------------------------------------
89
; F_tnfs_abspath
90
; Make an absolute path from a supplied relative path.
91
; Parameters    HL = pointer to path string
92
;               DE = pointer to a buffer to store the resulting path
93 234 winston
; In use mountpoint must be in v_curmountpt
94 215 winston
; On return DE points to the end of the new string.
95 399 winston
.globl F_tnfs_abspath
96
F_tnfs_abspath:
97 230 winston
        ld a, (hl)
98
        and a                   ; Empty string?
99 399 winston
        jr z, .donothing3       ; Do nothing.
100 230 winston
        cp '/'                  ; Absolute path?
101 215 winston
        ld b, 255
102
        jp z, F_tnfs_strcpy     ; yes, so just copy it verbatim.
103
 
104
        push hl
105
        ld (v_desave), de       ; save start of buffer
106 234 winston
        ld a, (v_curmountpt)    ; get the mount point we're working on
107 399 winston
        add a, v_cwd0 / 256     ; calculate the MSB
108 234 winston
        ld h, a
109
        ld l, 0                 ; set HL to the cwd
110 399 winston
.cploop3:
111 215 winston
        ld a, (hl)
112
        and a                   ; zero?
113 399 winston
        jr z, .stringend3
114 215 winston
        ld (de), a
115
        inc hl
116
        inc de
117 399 winston
        djnz .cploop3
118
.stringend3:
119 215 winston
        ld a, 255               ; did we actually copy anything?
120
        cp b
121 399 winston
        jr z, .addslash3                ; no, so put on a leading /
122 215 winston
        dec de
123
        ld a, (de)              ; check for trailing /
124
        cp '/'
125
        inc de
126 399 winston
        jr z, .parsepath3       ; trailing / is present, nothing to do
127
.addslash3:
128 215 winston
        ld a, '/'
129
        ld (de), a              ; add the trailing /
130
        inc de
131 399 winston
.parsepath3:
132 215 winston
        pop hl
133
 
134
        ; Build up the actual path. If we encounter a ../, remove the
135
        ; top path entry. If we encounter a ./, skip it.
136 399 winston
.loop3:
137 215 winston
        ld a, (hl)              ; check for end of string
138
        and a
139 399 winston
        jr z, .addnull3         ; finished so add a null to the destination
140
        call .relativepath3     ; check for relative path ../ or ./
141
        jr c, .loop3            ; relative path processed, don't copy any more
142
.copypath3:
143 215 winston
        ld a, (hl)              ; copy the byte
144
        ld (de), a
145
        inc hl
146
        inc de
147
        cp '/'                  ; up to a /
148 399 winston
        jr z, .loop3            ; when we go for the next bit.
149 215 winston
        and a                   ; hit the NULL at the end?
150
        ret z                   ; all done
151 399 winston
        jr .copypath3
152 215 winston
 
153 399 winston
.donothing3:                    ; simply advance DE to the end of
154 230 winston
        ld a, (de)              ; the current path string
155
        and a
156
        ret z
157
        inc de
158 399 winston
        jr .donothing3
159 230 winston
 
160 399 winston
.addnull3:
161 215 winston
        xor a
162
        ld (de), a
163
        inc de
164
        ret
165
 
166
        ; Deal with relative paths. When a ../ is encountered, the last
167
        ; dir should be removed (stopping at the start of the string).
168
        ; If a ./ is encountered it should be skipped.
169 399 winston
.relativepath3:
170 215 winston
        ld (v_hlsave), hl       ; save pointer
171
        ld a, (hl)
172
        cp '.'
173 399 winston
        jr nz, .notrelative3    ; definitely not a relative path
174 215 winston
        inc hl
175
        ld a, (hl)
176
        cp '/'
177 399 winston
        jr z, .omit3            ; It's a ./ - omit it
178 215 winston
        and a                   ; or a . (null), same thing
179 399 winston
        jr z, .omitnull3
180 215 winston
        cp '.'
181 399 winston
        jr nz, .notrelative3    ; It's .somethingelse3
182 215 winston
        inc hl
183
        ld a, (hl)
184
        cp '/'
185 399 winston
        jr z, .relative3                ; relative path
186 215 winston
        and 0                   ; null terminator? also relative path
187 399 winston
        jr z, .relativenull3    ;
188
        jr .notrelative3                ; something else
189
.relative3:
190 215 winston
        inc hl                  ; make HL point at next byte for return.
191 399 winston
.relativenull3:
192 215 winston
        ex de, hl               ; do the work in HL
193
        push hl                 ; save the pointer.
194
        ld bc, (v_desave)       ; get the pointer to the start of the buffer
195
        inc bc                  ; add 1 to it
196
        sbc hl, bc              ; compare with the current address
197
        pop hl                  ; get current address back into HL
198 399 winston
        jr z, .relativedone3    ; at the root already, so do nothing
199 215 winston
 
200
        dec hl                  ; get rid of the topmost path element
201
        ld (hl), 0              ; remove the trailing /
202 399 winston
.chewpath3:
203 215 winston
        dec hl
204
        ld a, (hl)              ; is it a / ?
205
        cp '/'                  ; if so we're nearly done
206 399 winston
        jr z, .relativealmostdone3
207 215 winston
        ld (hl), 0              ; erase char
208 399 winston
        jr .chewpath3
209
.relativealmostdone3:
210 215 winston
        inc hl                  ; HL points at next char
211 399 winston
.relativedone3:
212 215 winston
        ex de, hl
213
        scf                     ; indicate that path copy shouldn't take place
214
        ret
215
 
216 399 winston
.notrelative3:
217 215 winston
        ld hl, (v_hlsave)
218
        and a                   ; set Z flag if zero
219
        ret
220 399 winston
.omit3:
221 215 winston
        inc hl                  ; advance pointer to next element of the path
222 399 winston
.omitnull3:
223 215 winston
        scf                     ; set carry
224
        ret
225
 
226
;------------------------------------------------------------------------
227
; F_tnfs_header_w
228 216 winston
; Creates a TNFS header at a fixed address, buf_tnfs_wkspc, with the
229 215 winston
; extant session id
230 399 winston
.globl F_tnfs_header_w
231
F_tnfs_header_w:
232 234 winston
        push af
233
        ld a, (v_curmountpt)    ; find the SID for this mount point
234
        rlca                    ; mutiply by two
235 399 winston
        add a, v_tnfs_sid0 % 256        ; and add the offset
236 234 winston
        ld h, v_tnfs_sid0 / 256 ; set HL = pointer to SID
237
        ld l, a
238
        ld e, (hl)              ; set DE to the SID
239
        inc l
240
        ld d, (hl)
241 216 winston
        ld hl, buf_tnfs_wkspc
242 234 winston
        pop af
243 215 winston
; F_tnfs_header
244
; Creates a TNFS header. Session ID in DE. Command in A. HL is a pointer
245
; to the buffer to fill.
246
; HL points to the end of the header on exit.
247 399 winston
.globl F_tnfs_header
248
F_tnfs_header:
249 215 winston
        ld (hl), e
250
        inc hl
251
        ld (hl), d
252
        inc hl
253
        push af
254 234 winston
        ld a, (v_curmountpt)    ; calculate the sequence number storage
255 399 winston
        add a, v_tnfs_seqno0 % 256
256 234 winston
        ld e, a
257
        ld d, v_tnfs_seqno0 / 256
258
        ld a, (de)
259 215 winston
        inc a                   ; pre-increment the sequence number
260
        ld (hl), a
261 234 winston
        ld (de), a              ; so that the seqno in memory = current seq
262 215 winston
        pop af
263
        inc hl
264
        ld (hl), a              ; command
265
        inc hl
266
        ret
267
 
268
;------------------------------------------------------------------------
269 236 winston
; F_tnfs_prepsock
270
; Open the socket for TNFS datagrams if not already open, otherwise
271
; just set up the connection data for the datagrams.
272 215 winston
; Returns with carry set and A=error on error.
273
; HL=pointer to 4 byte IP address
274 399 winston
.globl F_tnfs_prepsock
275
F_tnfs_prepsock:
276 234 winston
        ld a, (v_curmountpt)    ; calculate the offset to the socket info
277
        rlca                    ; multiply by 8
278
        rlca
279
        rlca
280 399 winston
        add a, v_tnfs_sockinfo0 % 256
281 234 winston
        ld e, a                 ; set DE to the address of the sockinfo
282
        ld d, v_tnfs_sockinfo0 / 256
283
 
284
        ldi                     ; copy the IP data into the
285
        ldi                     ; sockinfo structure.
286 215 winston
        ldi
287
        ldi
288 234 winston
        ex de, hl
289
        ld (hl), 0x00           ; dest port = 16384
290
        inc l
291
        ld (hl), 0x40
292
        inc l
293
        ld a, (v_curmountpt)
294
        ld (hl), a              ; LSB of source port = mount point
295
        inc l
296
        ld (hl), 0x78           ; MSB of source port
297 236 winston
 
298
        ld a, (v_tnfs_sock)
299
        and a                   ; if it's zero we need to open the socket
300
        ret nz                  ; socket is already open
301 234 winston
        ld c, SOCK_DGRAM        ; Request a datagram socket.
302 215 winston
        call SOCKET             ; open a UDP socket.
303
        ret c
304 234 winston
        ld (v_tnfs_sock), a
305 215 winston
        ret
306
 
307
;------------------------------------------------------------------------
308
; F_tnfs_message_w
309 216 winston
; Sends the block of data starting at buf_tnfs_wkspc and ending at DE.
310 399 winston
.globl F_tnfs_message_w
311
F_tnfs_message_w:
312 215 winston
        ex de, hl               ; end pointer into HL for length calc
313 399 winston
.globl F_tnfs_message_w_hl
314
F_tnfs_message_w_hl:            ; entry point for when HL is already set
315 216 winston
        ld de, buf_tnfs_wkspc   ; start of block
316 215 winston
        sbc hl, de              ; calculate length
317
        ld b, h
318
        ld c, l
319
;------------------------------------------------------------------------
320
; F_tnfs_message
321
; Sends the block of data pointed to by DE for BC bytes and gets the
322
; response.
323 399 winston
.globl F_tnfs_message
324
F_tnfs_message:
325 215 winston
        ld a, tnfs_max_retries  ; number of retries
326
        ld (v_tnfs_retriesleft), a      ; into memory
327 370 winston
        ld (v_tnfs_tx_de), de   ; save pointer
328
        ld (v_tnfs_tx_bc), bc   ; save size
329 215 winston
 
330 234 winston
        ld a, (v_curmountpt)    ; current mount point
331
        rlca                    ; multiply by 8 to find the sockinfo
332
        rlca
333
        rlca
334 399 winston
        add a, v_tnfs_sockinfo0 % 256   ; LSB
335 234 winston
        ld h, v_tnfs_sockinfo0 / 256    ; MSB
336
        ld l, a
337 370 winston
        ld (v_tnfs_tx_hl), hl   ; save sockinfo pointer
338 399 winston
.retryloop9:
339 370 winston
        ld hl, (v_tnfs_tx_hl)   ; Fetch parameters
340
        ld de, (v_tnfs_tx_de)
341
        ld bc, (v_tnfs_tx_bc)
342 234 winston
        ld a, (v_tnfs_sock)     ; socket descriptor
343 215 winston
        call SENDTO             ; send the data
344 399 winston
        jr nc, .pollstart9
345 370 winston
        ret                     ; error, leave now
346 215 winston
 
347
        ; wait for the response by polling
348 399 winston
.pollstart9:
349 215 winston
        call F_tnfs_poll
350 399 winston
        jr nc, .continue9       ; a message is ready
351 215 winston
        ld a, (v_tnfs_retriesleft) ; get retries left
352
        dec a                   ; decrement it
353
        ld (v_tnfs_retriesleft), a ; and store it
354
        and a                   ; is it at zero?
355 399 winston
        jr nz, .retryloop9
356
        ld a, TTIMEOUT          ; error code - we tried...but9 gave up
357 215 winston
        scf                     ; timed out
358
        ret
359
 
360 399 winston
.continue9:
361 370 winston
        ld ix, (v_tnfs_tx_de)   ; start of the block we sent
362 302 winston
        ld a, (ix+tnfs_cmd_offset)      ; check the command
363
        cp TNFS_OP_READ         ; and see if it's a read operation
364 234 winston
 
365
        ld hl, v_vfs_sockinfo   ; Address to receive remote datagram IP/port
366 215 winston
        ld de, tnfs_recv_buffer ; Address to receive data
367 302 winston
        ld a, (v_tnfs_sock)     ; The tnfs socket
368 399 winston
        jr z, .read9            ; ...if9 the command was READ, handle it
369 370 winston
 
370 215 winston
        ld bc, 1024             ; max message size
371
        call RECVFROM
372
        ld a, (tnfs_recv_buffer + tnfs_seqno_offset)
373 234 winston
 
374 215 winston
        push bc
375
        ld b, a
376 234 winston
        ld a, (v_curmountpt)    ; find the sequence number storage
377 399 winston
        add a, v_tnfs_seqno0 % 256
378 234 winston
        ld l, a
379
        ld h, v_tnfs_seqno0 / 256
380
        ld a, (hl)
381 215 winston
        cp b                    ; sequence number match? if not
382
        pop bc
383 401 winston
        jr nz, .backoff         ; see if the real message is still to come
384 215 winston
        ret
385 401 winston
.backoff:
386
        call F_tnfs_incpolltime
387
        jr .pollstart9
388 215 winston
 
389 302 winston
        ; read is done in two phases - first get the header, then
390
        ; once we've got that copy the data directly to the required
391
        ; memory address instead of via the tnfs buffer.
392 399 winston
.read9:
393 302 winston
        ld bc, TNFS_READHEADERSZ ; just pull out the header
394
        call RECVFROM
395 312 winston
        ret c                   ; leave on error
396 302 winston
        ld a, (tnfs_recv_buffer + tnfs_seqno_offset)
397
        ld b, a
398
        ld a, (v_curmountpt)    ; find the sequence number storage
399 399 winston
        add a, v_tnfs_seqno0 % 256
400 302 winston
        ld l, a
401
        ld h, v_tnfs_seqno0 / 256
402 370 winston
        ld a, (hl)              ; Consume rest of packet when no match
403 302 winston
        cp b                    ; sequence number match? if not
404 399 winston
        jr nz, .consume9                ; consume and discard the non-match data
405 302 winston
 
406
        ; check for error conditions
407
        ld a, (tnfs_recv_buffer+tnfs_err_offset)
408
        and a
409 399 winston
        jr nz, .leaveread9
410 302 winston
 
411
        ld de, (v_read_destination)     ; get destination address
412
        push de
413
        ld bc, (tnfs_recv_buffer+tnfs_msg_offset)       ; length
414
        ld a, (v_tnfs_sock)
415
        call F_restorepage      ; restore original RAM
416
        call RECV               ; use recv, not recvfrom for the remains
417
        pop hl                  ; calculate the new value
418 312 winston
        push af
419
        call F_fetchpage        ; get our page back
420
        pop af
421 302 winston
        ret c                   ; exit on error
422
        add hl, bc              ; that DE should have on exit.
423
        ex de, hl
424
        or a                    ; ensure carry is reset
425
        ret
426 399 winston
.leaveread9:
427 311 winston
        scf                     ; indicate error
428
        ret
429 302 winston
 
430 399 winston
.consume9:                      ; consume and discard data we don't want
431 367 winston
        ld a, (v_tnfs_sock)     ; if there is any data to be consumed
432
        call POLLFD             ; (a non data datagram or an error datagram
433 399 winston
        jp z, .retryloop9       ; may not have any more data)
434 367 winston
 
435 370 winston
        ld de, tnfs_recv_buffer
436 367 winston
        ld bc, 256
437
        ld a, (v_tnfs_sock)
438
        call RECV               ; unload any remaining data from the socket
439 401 winston
        call F_tnfs_incpolltime ; increase initial poll time
440 399 winston
        jr .consume9
441 367 winston
 
442 215 winston
;-------------------------------------------------------------------------
443
; F_tnfs_poll
444
; Polls the tnfs fd for 1 time unit. Returns with carry set if the time
445
; expired.
446 399 winston
.globl F_tnfs_poll
447
F_tnfs_poll:
448 367 winston
        ld a, (v_tnfs_backoff)
449
        and a
450 399 winston
        jr z, .setstart10
451 367 winston
        ld a, (v_tnfs_polltime)
452
        ld c, a
453
        rla                     ; multiply backoff time by 2
454 399 winston
        jr nc, .setsysvar10
455 367 winston
        ld a, c                 ; can't back off any more, restore value
456 399 winston
        jr .setb10
457
.setsysvar10:
458 367 winston
        ld (v_tnfs_polltime), a ; save new poll time
459 399 winston
.setb10:
460 367 winston
        ld b, a
461 399 winston
.loop10:
462 215 winston
        push bc
463 400 winston
        ld bc, 0x7ffe           ; check BREAK
464
        in a, (c)
465
        cp 0xBE
466
        jr z, .break
467
 
468
        ld a, (v_tnfs_sock)     ; Poll for data
469 215 winston
        call POLLFD
470 400 winston
        jr nz, .done10          ; data has arrived
471
.wait:
472
        ld bc, TNFS_POLLITER
473
.waitloop:
474
        dec bc
475
        ld a, b
476
        or c
477
        jr nz, .waitloop
478
 
479 215 winston
        pop bc
480 399 winston
        djnz .loop10
481 367 winston
        scf                     ; poll time has expired
482 215 winston
        ret
483 399 winston
.setstart10:
484 367 winston
        inc a
485
        ld (v_tnfs_backoff), a  ; set backoff flag
486 401 winston
        ld a, (v_tnfs_initpolltime)     ; get current initial poll time
487 367 winston
        ld (v_tnfs_polltime), a
488 399 winston
        jr .setb10
489
.done10:
490 400 winston
        pop bc
491 367 winston
        xor a
492
        ld (v_tnfs_backoff), a  ; reset backoff flag
493
        ret
494 400 winston
.break:
495
        pop bc
496
        ld a, 0xCB              ; TODO: error inc file
497
        scf
498
        ret
499 215 winston
 
500
;-------------------------------------------------------------------------
501 401 winston
; F_tnfs_incpolltime
502
; Increase the initial poll time for any subsequent command.
503
; This is called when we start getting duplicate packets, meaning we're
504
; not suffering congestion or packet loss but we're overwhelming the
505
; server.
506
.globl F_tnfs_incpolltime
507
F_tnfs_incpolltime:
508
        ld a, (v_tnfs_initpolltime)
509
        rla                             ; double it
510
        jr c, .max
511
        ld (v_tnfs_initpolltime), a
512
.resetcount:
513
        xor a
514
        ld (v_tnfs_lastpolladj), a
515
        ret
516
.max:
517
        ld a, 255
518
        ld (v_tnfs_initpolltime), a
519
        jr .resetcount
520
 
521
;-------------------------------------------------------------------------
522 215 winston
; F_tnfs_mounted
523
; Ask if a volume is mounted. Returns with carry reset if so, and
524
; carry set if not, with A set to the error number.
525 399 winston
.globl F_tnfs_mounted
526
F_tnfs_mounted:
527 234 winston
        ld a, (v_tnfs_sock)
528 215 winston
        and a
529
        ret nz                  ; valid handle exists, return
530
        scf                     ; no valid handle - set carry flag
531
        ld a, TNOTMOUNTED       ; No filesystem mounted
532
        ret
533
 
534
;----------------------------------------------------------------------------
535
; F_tnfs_pathcmd
536
; Many TNFS commands are just the command id + null terminated path.
537
; This routine handles the assembly of the data block for all of these.
538
; Arguments: A = command
539
;           HL = pointer to string argument
540 399 winston
.globl F_tnfs_pathcmd
541
F_tnfs_pathcmd:
542 215 winston
        push hl
543
        call F_tnfs_header_w    ; create the header in the workspace area
544
        ex de, hl               ; de now points at current address
545
        pop hl
546
        call F_tnfs_abspath     ; create absolute path
547
        call F_tnfs_message_w   ; send the message and get the reply.
548
        ret
549
 
550
; As above but handles the return code too.
551 234 winston
; A = current mount point
552
; B = command
553 399 winston
.globl F_tnfs_simplepathcmd
554
F_tnfs_simplepathcmd:
555 216 winston
        call F_fetchpage
556
        ret c
557 234 winston
        ld (v_curmountpt), a
558 286 winston
        ld a, b
559 215 winston
        call F_tnfs_pathcmd
560
; Entry point for simple exit handler
561 399 winston
.globl F_tnfs_simpleexit
562
F_tnfs_simpleexit:
563 216 winston
        jp c, F_leave
564 215 winston
        ld a, (tnfs_recv_buffer+tnfs_err_offset)
565
        and a
566 216 winston
        jp z, F_leave
567 215 winston
        scf
568 216 winston
        jp F_leave
569 215 winston
 
570 236 winston
;---------------------------------------------------------------------------
571
; F_tnfs_checkclose
572
; Checks to see if the socket should be closed, and if so, closes it.
573 399 winston
.globl F_tnfs_checkclose
574
F_tnfs_checkclose:
575 236 winston
        push hl
576
        push bc
577
        push af
578
        ld b,4
579
        ld hl, v_tnfs_sid0
580 399 winston
.loop15:
581 236 winston
        ld a, (hl)
582
        inc hl
583
        or (hl)
584 399 winston
        jr nz, .done15
585 236 winston
        inc hl
586 399 winston
        djnz .loop15
587 236 winston
 
588
        ; no SIDs were found, so the socket isn't needed; close it.
589
        ld a, (v_tnfs_sock)
590
        call CLOSE
591
        xor a
592
        ld (v_tnfs_sock), a             ; clear down the socket storage
593 399 winston
.done15:
594 236 winston
        pop af
595
        pop bc
596
        pop hl
597
        ret