]> git.sur5r.net Git - cc65/blob - libsrc/atari/fdtable.s
allocate local vars in .data, don't use the stack (we don't need to
[cc65] / libsrc / atari / fdtable.s
1 ;
2 ; Christian Groessler, May-2000
3 ;
4 ; fd indirection table & helper functions
5 ;
6
7         .include "atari.inc"
8         .importzp tmp1,tmp2,tmp3,ptr4,sp
9         .import subysp,addysp
10         .export fdtoiocb
11         .export fdtoiocb_down
12         .export fd_table
13         .export fddecusage
14         .export newfd
15         .export getfd
16
17         .export _fd_table,_fd_index     ; for test purposes
18
19         .data
20 MAX_FD_INDEX    =       12
21 _fd_index:
22 fd_index:       ; fd number is index into this table, entry's value specifies the fd_table entry
23         .res    MAX_FD_INDEX,$ff
24
25 _fd_table:
26 fd_table:       ; each entry represents an open iocb
27         .byte   0,0,'E',0       ; system console, app starts with opened iocb #0 for E:
28         .byte   0,$ff,0,0
29         .byte   0,$ff,0,0
30         .byte   0,$ff,0,0
31         .byte   0,$ff,0,0
32         .byte   0,$ff,0,0
33         .byte   0,$ff,0,0
34         .byte   0,$ff,0,0
35
36 MAX_FD_VAL      =       (* - fd_table) / 4
37
38 ft_entrylen = 4 ; length of table entry (it's not sufficient to change here!
39                 ; the code sometimes does two bit shifts to multiply/divide by
40                 ; this length)
41
42 ft_usa  = 0     ; usage counter
43 ft_iocb = 1     ; iocb index (0,$10,$20,etc.), $ff for empty entry
44 ft_dev  = 2     ; device of open iocb (0 - device not remembered, eg. filename specified)
45 ft_flag = 3     ; flags
46                 ; lower 3 bits: device number (for R: and D:)
47
48         .code
49
50 ; gets fd in ax, decrements usage counter
51 ; return iocb index in X
52 ; return N bit set for invalid fd
53 ; return Z bit set if last user
54 ; all registers destroyed
55 .proc   fdtoiocb_down
56
57         cpx     #0
58         bne     inval
59         cmp     #MAX_FD_INDEX
60         bcs     inval
61         tax
62         lda     fd_index,x              ; get index
63         tay
64         lda     #$ff
65         sta     fd_index,x              ; clear entry
66         tya
67         asl     a                       ; create index into fd table
68         asl     a
69         tax
70         lda     #$ff
71         cmp     fd_table+ft_iocb,x      ; entry in use?
72         beq     inval                   ; no, return error
73         lda     fd_table+ft_usa,x       ; get usage counter
74         beq     ok_notlast              ; 0?
75         sec
76         sbc     #1                      ; decr usage counter
77         sta     fd_table+ft_usa,x
78 retiocb:php
79         txa
80         tay
81         lda     fd_table+ft_iocb,x      ; get iocb
82         tax
83         plp
84         bne     cont
85         php
86         lda     #$ff
87         sta     fd_table+ft_iocb,y      ; clear table entry
88         plp
89 cont:   rts
90
91 ok_notlast:
92         lda     #1                      ; clears Z
93         jmp     retiocb
94
95 .endproc
96
97 inval:  ldx     #$ff                    ; sets N
98         rts
99
100
101 ; gets fd in ax
102 ; return iocb index in X
103 ; return N bit set for invalid fd
104 ; all registers destroyed
105 .proc   fdtoiocb
106
107         cpx     #0
108         bne     inval
109         cmp     #MAX_FD_INDEX
110         bcs     inval
111         tax
112         lda     fd_index,x
113         asl     a                       ; create index into fd table
114         asl     a
115         tax
116         lda     #$ff
117         cmp     fd_table+ft_iocb,x      ; entry in use?
118         beq     inval                   ; no, return error
119         lda     fd_table+ft_usa,x       ; get usage counter
120         beq     inval                   ; 0? should not happen
121         lda     fd_table+ft_iocb,x      ; get iocb
122         rts
123
124 .endproc
125
126 ; decrements usage counter for fd
127 ; if 0 reached, it's marked as unused
128 ; get fd index in tmp2
129 ; Y register preserved
130 .proc   fddecusage
131
132         lda     tmp2                    ; get fd
133         cmp     #MAX_FD_INDEX
134         bcs     ret                     ; invalid index, do nothing
135         tax
136         lda     fd_index,x
137         pha
138         lda     #$ff
139         sta     fd_index,x
140         pla
141         asl     a                       ; create index into fd table
142         asl     a
143         tax
144         lda     #$ff
145         cmp     fd_table+ft_iocb,x      ; entry in use?
146         beq     ret                     ; no, do nothing
147         lda     fd_table+ft_usa,x       ; get usage counter
148         beq     ret                     ; 0? should not happen
149         sec
150         sbc     #1                      ; decrement by one
151         sta     fd_table+ft_usa,x
152         bne     ret                     ; not 0
153         lda     #$ff                    ; 0, table entry unused now
154         sta     fd_table+ft_iocb,x      ; clear table entry
155 ret:    rts
156
157 .endproc
158
159 ; newfd
160 ;
161 ; called from open() function
162 ; finds a fd to use for an open request
163 ; checks whether it's a device or file (file: characters follow the ':')
164 ; files always get an exclusive slot
165 ; for devices it is checked whether the device is already open, and if yes,
166 ; a link to this open device is returned
167 ;
168 ; Calling parameters:
169 ;       tmp3 - length of filename + 1
170 ;       AX   - points to filename
171 ;       Y    - iocb to use (if we need a new open)
172 ; Return parameters:
173 ;       tmp2 - fd num ($ff and C=0 in case of error - no free slot)
174 ;       C    - 0/1 for no open needed/open should be performed
175 ; all registers preserved!
176
177 ; local variables:
178 ;   AX     - 0 (A-0,X-1)
179 ;   Y      - 2
180 ;   ptr4   - 3,4  (backup)
181 ;   devnum - 5
182
183         .data
184 loc_Y:          .res    1
185 loc_ptr4_l:     .res    1
186 loc_ptr4_h:     .res    1
187 loc_tmp1:       .res    1
188 loc_devnum:     .res    1
189 loc_size:       .res    1
190
191         .code
192
193 .proc   newfd
194
195         pha
196         txa
197         pha
198         tya
199         pha
200
201         ldx     #0
202         stx     loc_devnum
203         lda     tmp1
204         sta     loc_tmp1
205         stx     tmp1            ; init tmp1
206         stx     tmp2            ; init tmp2
207         lda     ptr4+1
208         sta     loc_ptr4_h
209         lda     ptr4
210         sta     loc_ptr4_l
211         pla
212         sta     loc_Y
213         pla
214         sta     ptr4+1
215         pla
216         sta     ptr4
217
218         ; ptr4 points to filename
219
220         ldy     #1
221         lda     #':'
222         cmp     (ptr4),y        ; "X:"
223         beq     colon1
224         iny
225         cmp     (ptr4),y        ; "Xn:"
226         beq     colon2
227
228         ; no colon there!? OK, then we use a fresh iocb....
229         ; return error here? no, the subsequent open call should fail
230
231 do_open_nd:     ; do open and don't remember device
232         lda     #2
233         sta     tmp1
234 do_open:lda     tmp1
235         ora     #1
236         sta     tmp1            ; set flag to return 'open needed' : C = 1
237         ldx     #ft_iocb
238         ldy     #$ff
239 srchfree:
240         tya
241         cmp     fd_table,x
242         beq     freefnd         ; found a free slot
243         txa
244         clc
245         adc     #ft_entrylen
246         tax
247         cmp     #(MAX_FD_VAL*4)+ft_iocb ; end of table reached?
248         bcc     srchfree
249
250 ; error: no free slot found
251 noslot: ldx     #0
252         stx     tmp1            ; return with C = 0
253         dex
254         stx     tmp2            ; iocb: $ff marks error
255         jmp     finish
256
257 ; found a free slot
258 freefnd:txa
259         sec
260         sbc     #ft_iocb        ; normalize
261         tax
262         lsr     a
263         lsr     a
264         sta     tmp2            ; return fd
265         lda     #2
266         bit     tmp1            ; remember device?
267         beq     l1              ; yes
268         lda     #0              ; no, put 0 in field
269         beq     l2
270
271 l1:     ldy     #0
272         lda     (sp),y                  ; get device
273 l2:     sta     fd_table+ft_dev,x       ; set device
274         lda     #1
275         sta     fd_table+ft_usa,x       ; set usage counter
276         lda     loc_Y
277         sta     fd_table+ft_iocb,x      ; set iocb index
278         lda     loc_devnum
279         and     #7                      ; only 3 bits
280         sta     fd_table+ft_flag,x
281         lda     tmp2
282         jsr     fdt_to_fdi              ; get new index
283         bcs     noslot                  ; no one available
284         ;cmp    #$ff                    ; no one available
285         ;beq    noslot  ;@@@ cleanup needed
286         sta     tmp2                    ; return index
287         jmp     finish
288
289 ; string in "Xn:xxx" format
290 colon2: dey
291         lda     (ptr4),y        ; get device number
292         sec
293         sbc     #'0'
294         and     #7
295         sta     loc_devnum
296         sta     tmp2            ; save it for speed later here also
297         lda     #4              ; max. length if only  device + number ("Xn:")
298         cmp     tmp3
299         bcc     do_open_nd      ; string is longer -> contains filename
300         bcs     check_dev       ; handle device only string
301
302 ; string in "X:xxx" format
303 colon1: lda     #3              ; max. length if device only ("X:")
304         cmp     tmp3
305         bcc     do_open_nd      ; string is longer -> contains filename
306
307 ; get device and search it in fd table
308 check_dev:
309         ldy     #0
310         lda     (ptr4),y        ; get device id
311         tay
312         ldx     #(MAX_FD_VAL*4) - ft_entrylen
313 srchdev:lda     #$ff
314         cmp     fd_table+ft_iocb,x      ; is entry valid?
315         beq     srch2                   ; no, skip this entry
316         tya
317         cmp     fd_table+ft_dev,x
318         beq     fnddev
319 srch2:  txa
320         sec
321         sbc     #ft_entrylen+1
322         tax
323         bpl     srchdev
324
325 ; not found, open new iocb
326         jmp     do_open
327
328 ; helper for branch out of range
329 noslot1:jmp     noslot
330
331 ; found device in table, check device number (e.g R0 - R3)
332 fnddev: lda     fd_table+ft_flag,x
333         and     #7
334         cmp     tmp2                    ; contains devnum
335         bne     srch2                   ; different device numbers
336
337 ; found existing open iocb with same device
338         txa
339         lsr     a
340         lsr     a
341         sta     tmp2
342         inc     fd_table+ft_usa,x       ; increment usage counter
343         jsr     fdt_to_fdi              ; get new index
344         bcs     noslot1                 ; no one available
345         sta     tmp2                    ; return index
346
347 ; clean up and go home
348 finish: lda     ptr4
349         pha
350         lda     ptr4+1
351         pha
352         lda     loc_Y
353         pha
354         lda     tmp1
355         pha
356         lda     loc_tmp1
357         sta     tmp1
358         pla
359         lsr     a                       ; set C as needed
360
361         pla
362         tay
363         pla
364         tax
365         pla
366         rts
367
368 .endproc
369
370 ; ftp_to_fdi
371 ; returns a fd_index entry pointing to the given ft_table entry
372 ; get fd_table entry in A
373 ; return C = 0/1 for OK/error
374 ; return fd_index entry in A if OK
375 ; registers destroyed
376 .proc   fdt_to_fdi
377
378         tay
379         lda     #$ff
380         tax
381         inx
382 loop:   cmp     fd_index,x
383         beq     found
384         inx
385         cpx     #MAX_FD_INDEX
386         bcc     loop
387         rts
388
389 found:  tya
390         sta     fd_index,x
391         txa
392         clc
393         rts
394
395 .endproc
396
397 ; getfd
398 ; get a new fd pointing to a ft_table entry
399 ; usage counter of ft_table entry incremented
400 ; A - fd_table entry
401 ; return C = 0/1 for OK/error
402 ; returns fd in A if OK
403 ; registers destroyed, tmp1 destroyed
404 .proc   getfd
405
406         sta     tmp1            ; save fd_table entry
407         jsr     fdt_to_fdi
408         bcs     error
409
410         pha
411         lda     tmp1
412         asl     a
413         asl     a                       ; also clears C
414         tax
415         inc     fd_table+ft_usa,x       ; increment usage counter
416         pla
417 error:  rts
418
419 .endproc