-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpath.s
516 lines (416 loc) · 12.3 KB
/
path.s
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
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
;;; ============================================================
;;;
;;; PATH
;;;
;;; Build with: ca65 - https://cc65.github.io/doc/ca65.html
;;;
;;; ============================================================
.org $2000
.include "apple2.inc"
.include "more_apple2.inc"
.include "prodos.inc"
;;; ============================================================
cmd_load_addr := $4000
max_cmd_size = $2000
CASE_MASK = $DF
;;; ============================================================
;;; Install the new command
.proc installer
ptr := $06
;; Save previous external command address
lda EXTRNCMD+1
sta next_command
lda EXTRNCMD+2
sta next_command+1
;; Request a buffer for handler.
lda #handler_pages
jsr GETBUFR
bcc :+
lda #BI_ERR_NO_BUFFERS_AVAILABLE
rts
: sta new_page ; A = MSB of new page
;; Reserve buffer permanently.
;; ProDOS Technical Note #9: Buffer Management Using BASIC.SYSTEM
lda RSHIMEM
sec
sbc #handler_pages
sta RSHIMEM
;; Compute move delta in pages
lda new_page
sec
sbc #>handler
sta page_delta
;; Relocatable routine is aligned to page boundary so only MSB changes
ldx #0
: txa
asl
tay
lda relocation_table+1,y
sta ptr
lda relocation_table+2,y
sta ptr+1
lda (ptr)
clc
adc page_delta
sta (ptr)
inx
cpx relocation_table
bne :-
;; Relocate
lda #<handler
sta MOVE_SRC
lda #>handler
sta MOVE_SRC+1
lda #<handler_end
sta MOVE_END
lda #>handler_end
sta MOVE_END+1
lda #0
sta MOVE_DST
lda new_page
sta MOVE_DST+1
ldy #0
jsr MOVE
;; Install new address in external command address
lda new_page
sta EXTRNCMD+2
lda #0
sta EXTRNCMD+1
;; Complete
rts
new_page:
.byte 0
page_delta:
.byte 0
.endproc
;;; ============================================================
;;;
;;; Relocatable Section
;;;
;;; ============================================================
;;; Use `reloc_counter ADDR` anywhere that needs the page updated
;;;
;;; Examples:
;;;
;;; reloc_point *+2 ; update MSB of following JSR
;;; jsr routine
;;;
;;; reloc_point *+1 ; update MSB used in following LDA
;;; lda #>routine
::reloc_counter .set 0
.macro reloc_point addr
::.ident (.sprintf ("RL%04X", ::reloc_counter)) := addr
::reloc_counter .set ::reloc_counter + 1
.endmacro
;;; Align handler to page boundary for easier relocation
.res $2100 - *, 0
;;; ============================================================
;;; Command Handler
;;; ============================================================
.proc handler
ptr := $06 ; pointer into VPATH
tptr := $08 ; pointer into TOKTABL
lda VPATH1
sta ptr
lda VPATH1+1
sta ptr+1
;; Check for this command, character by character.
reloc_point *+2
jsr SkipLeadingSpaces
ldy #0 ; position in command string
reloc_point *+2
nxtchr: jsr ToUpperASCII
reloc_point *+2
cmp command_string,y
bne check_if_token
inx
iny
cpy #command_length
bne nxtchr
;; A match - indicate end of command string for BI's parser.
dey
sty XLEN
;; Point BI's parser at the command execution routine.
lda #<execute
sta XTRNADDR
reloc_point *+1
lda #>execute
sta XTRNADDR+1
;; Set accepted parameter flags (optional name)
lda #PBitsFlags::FNOPT | PBitsFlags::FN1
sta PBITS
lda #0
sta PBITS+1
;; Mark command as external (zero).
sta XCNUM ; A=0 from above
clc ; Success (so far)
rts ; Return to BASIC.SYSTEM
;;; ============================================================
check_if_token:
;; Is a PATH set?
reloc_point *+2
lda path_buffer
beq not_ours
reloc_point *+2
jsr SkipLeadingSpaces
reloc_point *+2
jsr ToUpperASCII
cmp #'A'
bcc not_ours
cmp #'Z'+1
bcs not_ours
;; Check if it's a BASIC token. Based on the AppleSoft BASIC source.
;; Point tptr at TOKTABL less one page (will advance below)
lda #<(TOKTABL-$100)
sta tptr
lda #>(TOKTABL-$100)
sta tptr+1
;; These are immediately incremented
dex
ldy #$FF ; (tptr),y offset TOKTABL
;; Match loop
mloop: iny ; Advance through token table
bne :+
inc tptr+1
: inx
;; Check for match
next_char:
reloc_point *+2
jsr ToUpperASCII ; Next character
;; NOTE: Does not skip over spaces, unlike BASIC tokenizer
sec ; Compare by subtraction..
sbc (tptr),Y
beq mloop
cmp #$80 ; If only difference was the high bit
bne next_token ; then it's end-of-token -- and a match!
;; Only if next command char is not alpha.
;; This allows 'ON' as a prefix (e.g. 'ONLINE'),
;; without preventing 'RUN100' from being typed.
inx
reloc_point *+2
jsr ToUpperASCII
cmp #'A'
bcc not_ours
cmp #'Z'+1
bcs not_ours
;; Otherwise, advance to next token
next_token:
reloc_point *+2
jsr SkipLeadingSpaces
sloop: lda (tptr),y ; Scan table looking for a high bit set
iny
bne :+
inc tptr+1
: asl
bcc sloop ; High bit clear, keep looking
lda (tptr),y ; End of table?
bne next_char ; Nope, check for a match
beq maybe_invoke
not_ours:
fail_invoke:
sec ; Signal failure...
next_command := *+1
jmp $ffff ; Execute next command in chain
;;; ============================================================
maybe_invoke:
ppos := $D6 ; position into path_buffer
lda #0
sta ppos
;; Compose path
compose:
ldx ppos
reloc_point *+2
cpx path_buffer
beq fail_invoke
;; Entry from path list
ldy #1
reloc_point *+2
: lda path_buffer+1,x
inx
cmp #':' ; separator
beq :+
sta (ptr),y
iny
reloc_point *+2
cpx path_buffer
bne :-
;; Slash separator
: stx ppos
lda #'/'
sta (ptr),y
iny
;; Name from command line
reloc_point *+2
jsr SkipLeadingSpaces
reloc_point *+2
: jsr ToUpperASCII
cmp #'.'
beq ok
cmp #'0'
bcc notok
cmp #'9'+1
bcc ok
cmp #'A'
bcc notok
cmp #'Z'+1
bcs notok
ok: sta (ptr),y
iny
inx
cpx #65 ; Maximum path length+1
bcc :-
bcs compose
notok: dey
tya
ldy #0
sta (ptr),y
;; Indicate end of command string for BI's parser (if command uses it)
dex
stx xlen ; assigned to `XLEN` later
;; Check to see if path exists.
lda #$A ; param length
sta SSGINFO
lda #GET_FILE_INFO
jsr GOSYSTEM
bne compose ; no such file - try next path directory
;; Check to see if type is CMD.
lda FIFILID
cmp #FT_CMD
bne compose ; wrong type - try next path directory
;; Tell BASIC.SYSTEM it was handled.
ldx #xtrnaddr_len - 1
: lda xtrnaddr,x
sta XTRNADDR,x
dex
bpl :-
;; MLI/BI trashes part of INBUF (clock driver?), so stash it in upper half.
ldx #$7F
: lda INBUF,x
sta INBUF+$80,x
dex
bpl :-
;; Use BI general purpose buffer for I/O (page aligned)
lda HIMEM+1
sta OSYSBUF+1
;; Now try to open/read/close and invoke it
lda #OPEN
jsr GOSYSTEM
bcs fail_load
lda OREFNUM
sta RWREFNUM
sta CFREFNUM
;; Assign `RWDATA` and `RWCOUNT`
ldx #rwdata_len - 1
: lda rwdata,x
sta RWDATA,x
dex
bpl :-
lda #READ
jsr GOSYSTEM
php ; save C in case it signals failure
pha ; if so, A has error code
lda #CLOSE ; always close
jsr GOSYSTEM
pla
plp
bcs fail_load
;; Restore INBUF now that MLI/BI work is done.
ldx #$7F
: lda INBUF+$80,x
sta INBUF,x
dex
bpl :-
;; Invoke command, allow it to return to BASIC.SYSTEM
jmp cmd_load_addr
fail_load:
rts
;;; Assigned to `XTRNADDR`, `XLEN`, `XCNUM`, and `PBITS`
xtrnaddr:
.addr XRETURN ; assigned to `XTRNADDR`
xlen: .byte 0 ; assigned to `XLEN`
.byte 0 ; assigned to `XCNUM`
.word 0 ; assigned to `PBITS`
xtrnaddr_len = * - xtrnaddr
;;; Assigned to `RWDATA` and `RWCOUNT`
rwdata:
.addr cmd_load_addr ; assigned to `RWDATA`
.word max_cmd_size ; assigned to `RWCOUNT`
rwdata_len = * - rwdata
;;; ============================================================
execute:
;; Verify required arguments
lda FBITS
and #PBitsFlags::FN1 ; Filename?
bne set_path
;;; --------------------------------------------------
;; Show current path
ldx #0
reloc_point *+2
: cpx path_buffer
beq done
reloc_point *+2
lda path_buffer+1,x
ora #$80
jsr COUT
inx
bpl :-
jsr CROUT
done: clc
rts
;;; --------------------------------------------------
;; Set path
set_path:
ldy #0
lda (ptr),y
tay
: lda (ptr),y
reloc_point *+2
sta path_buffer,y
dey
bpl :-
clc
rts
;;; ============================================================
;;; Helpers
;;; Returns INBUF,x with high bit stripped and up-cased
;;; (also converts {|}~DEL to [\]^_ but that's okay)
.proc ToUpperASCII
lda INBUF,x
and #$7F
cmp #'a'
bcc skip
and #CASE_MASK
skip: rts
.endproc
;;; Returns with X pointing at first non-space in INBUF,
;;; and that character loaded in A.
.proc SkipLeadingSpaces
ldx #$FF
: inx
lda INBUF,x
cmp #' '|$80
beq :-
rts
.endproc
;;; ============================================================
;;; Data
command_string:
.byte "PATH"
command_length = *-command_string
path_buffer:
.res 65, 0
.endproc
handler_end := *-1
handler_pages = (.sizeof(handler) + $FF) / $100
next_command := handler::next_command
;;; ============================================================
;;;
;;; Relocation Table
;;;
;;; ============================================================
relocation_table:
.byte ::reloc_counter
.repeat ::reloc_counter, rc
.addr ::.ident (.sprintf ("RL%04X", rc))
.endrepeat
;;; ============================================================