mirror of
https://github.com/bobbimanners/Zapple-II.git
synced 2026-10-24 22:02:17 +00:00
9292 lines
382 KiB
NASM
9292 lines
382 KiB
NASM
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; Z80 code running on Softcard
|
|
; Implements CP/M style BDOS interface
|
|
; Requires the companion SOFTCARD65 6502 code
|
|
; Assemble using Udo Munk's Z80asm.
|
|
; Tabstops every 4 chars.
|
|
; Bobbi 2019, 2026
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; References:
|
|
; 1) BDOS System Calls
|
|
; https://www.seasip.info/Cpm/bdos.html
|
|
; 2) Programmer's CP/M Handbook - Johnson-Laird
|
|
; 3) ProDOS 8 Technical Reference Manual
|
|
; http://www.easy68k.com/paulrsm/6502/PDOS8TRM.HTM
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; BDOS TODOs
|
|
; ----------
|
|
; ProDOS handles: two-slot, pathname-keyed cache (see CACHE_* near $A200).
|
|
; Logical CP/M open state is still S2=$7E, never a leaked ProDOS FRN.
|
|
; TODO: Implement missing BDOS system calls:
|
|
; - RS232 (A_READ, A_WRITE)
|
|
; - Printer (LWRITE)
|
|
; IOBYTE routes console, reader, punch and list to Apple firmware.
|
|
; TODO: User number doesn't do anything
|
|
;
|
|
|
|
BDOSADDR EQU 08100H ;
|
|
STCKTOP EQU 0A800H ; Stack still ends at $A800; base-69 uses its low region
|
|
|
|
SOFTCARD EQU 0E400H ; Softcard in slot 4 ($C400)
|
|
|
|
|
|
OFFSET EQU 01000H ; Offset to add to Z80 addr to get 6502
|
|
; address. Correct for Z80 addr <= 0AFFFH
|
|
|
|
; 6502 zero page, in Z80 address space
|
|
CMD EQU 0F006H ; 6502 $06
|
|
AREG EQU 0F007H ; 6502 $07
|
|
XREG EQU 0F008H ; 6502 $08
|
|
YREG EQU 0F009H ; 6502 $09
|
|
ADDR EQU 0F0EBH ; 6502 $EB (LSB)
|
|
ADDRH EQU 0F0ECH ; 6502 $EC (MSB)
|
|
|
|
; Addresses of 6502 routines, in 6502 address space
|
|
COUT EQU 0B803H ; Direct video output entry (B803)
|
|
RDKEY EQU 0B806H ; Direct keyboard/cursor entry (B806)
|
|
BELL EQU 0B809H ; Direct speaker click entry (B809)
|
|
RAWOUT EQU 0B80FH ; Raw BIOS console entry (B80F)
|
|
VTASK EQU 0B821H ; 6502 select running/foreground display
|
|
; B80C selects Soroc (1) or the resident Z80 VT100 translator (0).
|
|
; The VT parser lives in reclaimed MAIN RAM, not the 6502 video bank.
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; Entry point when Z80 cold starts is 0000H
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
ORG 0000H
|
|
JP BDOSINIT ; Initialize BDOS
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; See obsolescence.wix.com/obsolescence/cpm-internals for info
|
|
; on low storage usage ...
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
IOBYTE DEFB 0 ; Maps virtual to real devices
|
|
|
|
CURDRV DEFB 0 ; LS 4 bits; Current drive 0=A: etc
|
|
; MS 4 bits: Current user number
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; BDOS entry point must be at address 0005H for CP/M compatibility
|
|
; Function to invoke is passed in C
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
BDOS JP BDOSENTRY ; Keep page-zero BDOS vector within $81xx
|
|
|
|
RVEC1 DEFW 0000H ; Restart vector 1
|
|
RVEC2 DEFW 0000H ; Restart vector 2
|
|
RVEC3 DEFW 0000H ; Restart vector 3
|
|
RVEC4 DEFW 0000H ; Restart vector 4
|
|
RVEC5 DEFW 0000H ; Restart vector 5
|
|
RVEC6 DEFW 0000H ; Restart vector 6
|
|
RVEC7 DEFW 0000H ; Restart vector 7
|
|
|
|
; Space for private BDOS data (implementation dependent) up to 005BH
|
|
DMAADDR DEFW 0080H ; DMA address defaults to FILEBUF (0080H)
|
|
LOGVEC DEFW 0000H ; Vector of logged in drives
|
|
ROVEC DEFW 0000H ; Vector of read-only drives
|
|
TEMPWORD DEFW 0000H ; Used by routines as a scratch space
|
|
TEMPBYTE DEFB 0 ; Used by routines as a scratch space
|
|
; End of private, implementation dependent space
|
|
|
|
ORG 005CH ; Standard addr of 32 byte FCB1
|
|
FCB1 ; File control block #1
|
|
FCB1DRV DEFB 00H ; FCB Drive (0 current, 1 A:, 2 B: etc)
|
|
FCB1NAM DEFM 'FILENAMEEXT' ; FCB filename and extension
|
|
FCB1EX DEFB 00H ; FCB extent field
|
|
FCB1S1 DEFB 00H ; FCB S1 field
|
|
FCB1S2 DEFB 00H ; FCB S2 field
|
|
FCB1RC DEFB 00H ; FCB RC field (# recs used this extent)
|
|
FCB1MAP ; Map of blocks in file (overlaps FCB2)
|
|
|
|
ORG 006CH ; Standard addr of 32 byte FCB2
|
|
FCB2 ; File control block #2
|
|
FCB2DRV DEFB 00H ; FCB Drive (0 current, 1 A:, 2 B: etc)
|
|
FCB2NAM DEFM 'FILENAMEEXT' ; FCB filename and extension
|
|
FCB2EX DEFB 00H ; FCB extent field
|
|
FCB2S1 DEFB 00H ; FCB S1 field
|
|
FCB2S2 DEFB 00H ; FCB S2 field
|
|
FCB2RC DEFB 00H ; FCB RC field (# recs used this extent)
|
|
FCB2MAP ; Map of blocks in file (overlaps buffer)
|
|
|
|
ORG 0080H ; Standard addr of 128 byte File Buffer
|
|
FILEBUF DEFS 128 ; Command args go here too (Pascal string)
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; The application program proper starts at 0100H
|
|
; in order to be compatible with CP/M .COM programs
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
ORG 0100H
|
|
|
|
PROGSTRT CALL CCP ; Run the CCP
|
|
|
|
; Phase 2: small MAIN-only bank-control helper and shell command.
|
|
; MAIN $0200..$02FF is unused by AUX transients and by SAVE's
|
|
; temporary $1000..$3FFF staging. No program ever runs here in AUX.
|
|
ORG 00200H
|
|
; A = AUX bank number. 6502 vector $B818 selects it and records it.
|
|
AXBANK LD (AREG),A
|
|
LD HL,0B818H
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
RET
|
|
|
|
; Logical sessions 0..F use physical banks 0,1,3,4,6..9,11..14,16..19.
|
|
; Each group of four shares one otherwise reserved screen-snapshot bank.
|
|
TABPHYS LD HL,TABPHY
|
|
LD E,A
|
|
LD D,0
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
RET
|
|
TABPHY DEFB 0,1,3,4,6,7,8,9,11,12,13,14,16,17,18,19
|
|
; Find the first bank that does not physically exist, capped at 16 tabs.
|
|
RWDETECT LD HL,0B815H
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
LD A,(AREG)
|
|
OR A
|
|
JR NZ,RWD_OK
|
|
INC A ; no card still has ordinary AUX bank 0
|
|
RWD_OK LD (RWTOTAL),A
|
|
CP 3
|
|
JR NC,RWD_MULTI
|
|
LD A,1 ; no snapshot bank: single console only
|
|
LD (TABCOUNT),A
|
|
RET
|
|
RWD_MULTI LD A,(RWTOTAL)
|
|
LD C,A
|
|
LD HL,TABPHY
|
|
LD B,0
|
|
RWD_NEXT LD A,(HL)
|
|
CP C
|
|
JR NC,RWD_DONE
|
|
INC B
|
|
INC HL
|
|
LD A,B
|
|
CP 16
|
|
JR C,RWD_NEXT
|
|
RWD_DONE LD A,B
|
|
LD (TABCOUNT),A
|
|
RET
|
|
RWTOTAL DEFB 1
|
|
TABCOUNT DEFB 1
|
|
TABREQ DEFB 0FFH ; Open Apple + 0..9/A..F/arrows; FF = none
|
|
RWREPORT LD DE,RWMSG1
|
|
CALL C_WRITESTR
|
|
LD A,(RWTOTAL)
|
|
CALL RWDECIMAL
|
|
LD DE,RWMSG2
|
|
CALL C_WRITESTR
|
|
LD A,(TABCOUNT)
|
|
CALL RWDECIMAL
|
|
LD DE,RWMSG3
|
|
CALL C_WRITESTR
|
|
LD A,'A' ; MouseText Open Apple, physical $41
|
|
CALL MTCHAR
|
|
LD DE,RWMSG4
|
|
CALL C_WRITESTR
|
|
LD A,'H' ; MouseText left arrow, physical $48
|
|
CALL MTCHAR
|
|
LD A,'U' ; MouseText right arrow, physical $55
|
|
CALL MTCHAR
|
|
LD DE,RWMSG5
|
|
JP C_WRITESTR
|
|
RWMSG1 DEFM 'RamWorks: '
|
|
DEFB '$'
|
|
RWMSG2 DEFM ' banks '
|
|
DEFB '$'
|
|
; Print unsigned 0..255 without leading zeroes.
|
|
RWDECIMAL LD B,'0'
|
|
RWD_HUND CP 100
|
|
JR C,RWD_TENS
|
|
SUB 100
|
|
INC B
|
|
JR RWD_HUND
|
|
RWD_TENS LD C,0 ; hundreds digit was printed?
|
|
PUSH AF
|
|
LD A,B
|
|
CP '0'
|
|
JR Z,RWD_T0
|
|
LD C,1
|
|
LD E,A
|
|
PUSH BC
|
|
CALL C_WRITE
|
|
POP BC
|
|
RWD_T0 POP AF
|
|
LD B,'0'
|
|
RWD_TLOOP CP 10
|
|
JR C,RWD_ONES
|
|
SUB 10
|
|
INC B
|
|
JR RWD_TLOOP
|
|
RWD_ONES PUSH AF
|
|
LD A,B
|
|
CP '0'
|
|
JR NZ,RWD_TPRINT
|
|
LD A,C
|
|
OR A
|
|
JR Z,RWD_TEND
|
|
LD A,'0' ; display 100, not 10
|
|
RWD_TPRINT LD E,A
|
|
CALL C_WRITE
|
|
RWD_TEND POP AF
|
|
ADD A,'0'
|
|
LD E,A
|
|
JP C_WRITE
|
|
|
|
ORG 00300H
|
|
; A queued Open Apple hotkey returns immediately, with no key passed
|
|
; to an application. CCP discards the current unfinished command line.
|
|
CR_SELCMD XOR A
|
|
LD (IX+1),A
|
|
RET
|
|
|
|
; Fullscreen applications poll BDOS 11 or BIOS CONST before reading a
|
|
; character. CMD=3 peeks at $C000 and samples Open Apple ($C061) in XREG.
|
|
; Valid shortcuts consume the keyboard strobe directly, without drawing
|
|
; a cursor or waiting for another character. Ordinary keys are untouched.
|
|
; F3 is the private deferred-status marker on suspension.
|
|
TSKSTAT CALL SCHISFG
|
|
JR Z,TSSTFG
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
TSSTFG LD A,3
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
LD A,(AREG)
|
|
OR A
|
|
JR Z,TSSTEMPTY
|
|
LD A,(YREG)
|
|
AND 07FH
|
|
CALL TSKKEY
|
|
JR C,TSSTSWITCH
|
|
OR A ; OA-current was consumed, no input
|
|
JR Z,TSSTEMPTY
|
|
JR TSSTREADY
|
|
TSSTSWITCH LD A,(0E010H) ; Apple II $C010: clear just this key
|
|
LD A,0F3H ; resume the pending status poll
|
|
LD L,A
|
|
RET
|
|
TSSTREADY LD A,0FFH
|
|
LD L,A
|
|
RET
|
|
TSSTEMPTY XOR A
|
|
LD L,A
|
|
RET
|
|
|
|
; Advertise the modifier and arrows as one-keystroke shortcuts.
|
|
RWMSG3 DEFM ' sessions (0-9,A-F)'
|
|
DEFB 13
|
|
DEFM 'Switch '
|
|
DEFB '$'
|
|
RWMSG4 DEFM ' + 0123456789ABCDEF'
|
|
DEFB '$'
|
|
RWMSG5 DEFB 13
|
|
DEFM 'HELP for commands'
|
|
DEFB 13,'$'
|
|
|
|
; Print the current logical session and drive: [0]A:> ... [F]P:>
|
|
; Kept outside the crowded $81xx-$97xx resident filesystem region.
|
|
CCPPRNT LD E,'['
|
|
CALL C_WRITE
|
|
LD A,(TABMODE)
|
|
CALL PHNYB
|
|
LD E,']'
|
|
CALL C_WRITE
|
|
LD A,(CURDRV)
|
|
AND 0FH
|
|
ADD A,'A'
|
|
LD E,A
|
|
CALL C_WRITE
|
|
LD A,(CURDRV)
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
AND 00FH
|
|
CALL NZ,RWDECIMAL ; users 1..15; user 0 stays A:>
|
|
LD E,':'
|
|
CALL C_WRITE
|
|
LD E,'>'
|
|
JP C_WRITE
|
|
|
|
ORG 003B0H
|
|
; Kill only an inactive saved context. No CPU instruction is preempted.
|
|
; FCB cache has already been flushed by the cooperative park path.
|
|
TB_KILL LD A,B
|
|
CALL TB_HPTR
|
|
LD (HL),0
|
|
LD A,B
|
|
CALL TB_FPTR
|
|
LD (HL),0
|
|
LD HL,SCHWAIT
|
|
ADD HL,DE
|
|
LD (HL),0
|
|
LD HL,CCPPROMS
|
|
ADD HL,DE
|
|
LD (HL),0
|
|
; Mark backing screen invalid, so the next selection clears it.
|
|
LD A,B
|
|
CALL TB_METAP
|
|
LD (HL),0
|
|
; KILL makes the slot unvisited again until explicitly selected.
|
|
LD A,B
|
|
CALL TB_VPTR
|
|
LD (HL),0
|
|
RET
|
|
TB_FPTR LD E,A
|
|
LD D,0
|
|
LD HL,TSKFLAGS
|
|
ADD HL,DE
|
|
RET
|
|
|
|
ORG 00400H
|
|
|
|
; Phase 3A: two independent CCP contexts. TAB 0 / TAB 1 switches
|
|
; shell state (drive/user, IOBYTE, login/R/O vectors, terminal type).
|
|
; The transient for a context uses its corresponding RamWorks bank.
|
|
; Phase 3B: Open Apple shortcuts park a transient without reloading it.
|
|
; TAB n resumes a parked transient, rather than reloading it.
|
|
; Phase 3C: full text-screen/terminal-state snapshots in reserved bank 2.
|
|
; Still cooperative: no switching from a CPU-bound loop.
|
|
CCPTAB JP TBCMD ; extended TAB command parser
|
|
|
|
; Convert a hexadecimal ASCII tab selector to A=0..15, carry on error.
|
|
; Check the detected bank count so an unavailable tab is never touched.
|
|
TABDECODE CP '0'
|
|
JR C,TBD_BAD
|
|
CP '9'+1
|
|
JR NC,TBD_HEX
|
|
SUB '0'
|
|
JR TBD_RANGE
|
|
TBD_HEX AND 0DFH ; lowercase a..f accepted
|
|
CP 'A'
|
|
JR C,TBD_BAD
|
|
CP 'F'+1
|
|
JR NC,TBD_BAD
|
|
SUB 'A'-10
|
|
TBD_RANGE LD C,A
|
|
LD A,(TABCOUNT)
|
|
CP C
|
|
JR Z,TBD_BAD
|
|
JR C,TBD_BAD
|
|
LD A,C
|
|
OR A ; clear carry
|
|
RET
|
|
TBD_BAD SCF
|
|
RET
|
|
|
|
; Drain queued switches before returning to the CCP prompt.
|
|
; TB_SWITCH can resume a suspended program, which can later suspend
|
|
; and queue ANOTHER switch. A tail-JR here returned to CCPL1 after
|
|
; that suspension and printed a CCP prompt into the application's
|
|
; still-visible fullscreen display. Loop until no request remains.
|
|
TBAUTOSW LD A,(TABREQ)
|
|
CP 0FFH
|
|
RET Z
|
|
LD B,A
|
|
LD A,0FFH
|
|
LD (TABREQ),A
|
|
CALL TB_SWITCH
|
|
JR TBAUTOSW
|
|
TB_SWITCH CALL CUOFF ; do not snapshot the cursor glyph
|
|
PUSH BC
|
|
LD A,B
|
|
CALL TBCLEARH ; switching always wakes a held tab
|
|
POP BC
|
|
LD A,(TABMODE)
|
|
CP B
|
|
JR Z,TB_RESUM
|
|
PUSH BC
|
|
CALL VSSTSAVE ; save active 6502 parser/cursor state
|
|
; Diagnostic: force the original reliable Stage 1 physical
|
|
; 80-column capture before leaving the foreground tab.
|
|
; This MUST happen before TABMODE changes, and is deliberately
|
|
; limited to manual foreground switches (not scheduler ticks).
|
|
CALL SCHISFG
|
|
CALL Z,VSSAVE
|
|
CALL TABSAVE
|
|
CALL CACHEALL
|
|
POP BC
|
|
LD A,B
|
|
LD (TABMODE),A
|
|
LD (SCHFG),A ; manual TAB changes visible session
|
|
CALL TABLOAD
|
|
CALL VSSTLOAD
|
|
CALL VSSELECT ; renders new tab to hardware
|
|
CALL VSREST ; copy ALWAYS-LIVE snapshot to video
|
|
JR TB_GO
|
|
; A direct TAB command aimed at the current tab should beep too.
|
|
; Open Apple+current is already consumed and beeped by TSKSAME.
|
|
TB_RESUM CALL UNIMP ; bell, no spurious CCP output
|
|
CALL TSKACTIVE
|
|
OR A
|
|
CALL NZ,VSREST
|
|
TB_GO LD A,(TABMODE)
|
|
LD (SCHFG),A
|
|
CALL TB_VMARK ; visited (regardless of CCP/COM status)
|
|
CALL VSSELECT ; update 6502 foreground ownership
|
|
CALL SCHWAKE ; this tab may be waiting for CONIN
|
|
LD A,SCHFGQ
|
|
LD (SCHQLEFT),A ; a newly selected tab is foreground
|
|
CALL TABRESUM
|
|
RET
|
|
; One byte per CCP session. Saved display may already contain the
|
|
; A> prompt, so do not print it again after restoring that screen.
|
|
CCPPRPTR LD A,(TABMODE)
|
|
LD E,A
|
|
LD D,0
|
|
LD HL,CCPPROMS
|
|
ADD HL,DE
|
|
RET
|
|
TABMODE DEFB 0
|
|
|
|
; Session descriptors are only seven bytes each, stored in MAIN memory.
|
|
; Offsets: 0 current drive/user, 1 IOBYTE, 2..3 LOGVEC,
|
|
; 4..5 ROVEC, 6 personality (0 VT100 / 1 Soroc / 2 H19).
|
|
; DMA is intentionally reset to $0080 at each switch; no BDOS search
|
|
; or pending directory enumeration is allowed across a CCP TAB command.
|
|
TABPICK LD IX,TABCTXS
|
|
LD A,(TABMODE)
|
|
OR A
|
|
RET Z
|
|
LD B,A
|
|
LD DE,7
|
|
TP_NEXT ADD IX,DE
|
|
DJNZ TP_NEXT
|
|
RET
|
|
|
|
TABSAVE CALL TABPICK
|
|
LD A,(CURDRV)
|
|
LD (IX+0),A
|
|
LD A,(IOBYTE)
|
|
LD (IX+1),A
|
|
LD HL,(LOGVEC)
|
|
LD (IX+2),L
|
|
LD (IX+3),H
|
|
LD HL,(ROVEC)
|
|
LD (IX+4),L
|
|
LD (IX+5),H
|
|
LD HL,0B81BH
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
LD A,(IX+6)
|
|
CP 2 ; 6502 knows only Soroc for H19
|
|
RET Z ; keep H19 personality on save
|
|
LD A,(AREG)
|
|
LD (IX+6),A
|
|
RET
|
|
|
|
TABLOAD CALL TABPICK
|
|
LD A,(IX+0)
|
|
LD (CURDRV),A
|
|
LD A,(IX+1)
|
|
LD (IOBYTE),A
|
|
LD L,(IX+2)
|
|
LD H,(IX+3)
|
|
LD (LOGVEC),HL
|
|
LD L,(IX+4)
|
|
LD H,(IX+5)
|
|
LD (ROVEC),HL
|
|
LD HL,FILEBUF
|
|
LD (DMAADDR),HL
|
|
LD HL,0080H
|
|
LD (AXDMAP),HL
|
|
LD A,(IX+6)
|
|
CP 2 ; H19 parser uses Soroc renderer
|
|
JR NZ,TABLD6502
|
|
LD A,1
|
|
TABLD6502 LD (AREG),A
|
|
LD HL,0B80CH
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
RET
|
|
|
|
TABINIT CALL RWDETECT
|
|
LD HL,TABCTXS
|
|
LD B,16
|
|
TI_NEXT XOR A
|
|
LD (HL),A ; CURDRV
|
|
INC HL
|
|
LD A,095H
|
|
LD (HL),A ; default IOBYTE in every tab
|
|
INC HL
|
|
LD A,1
|
|
LD (HL),A ; LOGVEC = 0001H
|
|
INC HL
|
|
XOR A
|
|
LD (HL),A
|
|
INC HL
|
|
LD (HL),A
|
|
INC HL
|
|
LD (HL),A
|
|
INC HL
|
|
INC A
|
|
LD (HL),A ; Soroc default
|
|
INC HL
|
|
DJNZ TI_NEXT
|
|
LD HL,VTSTATES
|
|
LD DE,VTSTATES+1
|
|
LD BC,127
|
|
XOR A
|
|
LD (HL),A
|
|
LDIR
|
|
CALL VSINIT
|
|
CALL VSSAVE ; seed tab 0 backing with startup text
|
|
CALL TB_VINIT ; tab 0 is visited at cold boot
|
|
LD HL,CCPPROMS
|
|
LD DE,CCPPROMS+1
|
|
LD BC,15
|
|
XOR A
|
|
LD (HL),A
|
|
LDIR
|
|
RET
|
|
|
|
; Map a logical tab A to its manual hold flag; preserves B.
|
|
TB_HPTR LD E,A
|
|
LD D,0
|
|
LD HL,TBHOLD
|
|
ADD HL,DE
|
|
RET
|
|
TBCLEARH CALL TB_HPTR
|
|
LD (HL),0
|
|
RET
|
|
TB_TFL LD E,A
|
|
LD D,0
|
|
LD HL,TSKFLAGS
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
RET
|
|
; 8-byte saved executable name for tab A.
|
|
TBNPTR LD L,A
|
|
LD H,0
|
|
ADD HL,HL
|
|
ADD HL,HL
|
|
ADD HL,HL
|
|
LD DE,TBNAMES
|
|
ADD HL,DE
|
|
RET
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; Implementation of CP/M STYLE BDOS
|
|
; Function to invoke is passed in C, as follows:
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
B_C_TERMCPM EQU 00H ; System reset
|
|
B_C_READ EQU 01H ; Console read
|
|
B_C_WRITE EQU 02H ; Console write
|
|
B_C_RAWIO EQU 06H ; Direct console I/O
|
|
B_GET_IOB EQU 07H ; Get IOBYTE
|
|
B_SET_IOB EQU 08H ; Set IOBYTE
|
|
B_C_WRTSTR EQU 09H ; Console write string
|
|
B_C_RDSTR EQU 0AH ; Read console string
|
|
B_C_STAT EQU 0BH ; Console status
|
|
B_S_BDOSVER EQU 0CH ; Return version number
|
|
B_DRV_ALLRST EQU 0DH ; Reset disks
|
|
B_DRV_SET EQU 0EH ; Select disk
|
|
B_F_OPEN EQU 0FH ; Open file
|
|
B_F_CLOSE EQU 10H ; Close file
|
|
B_F_SFIRST EQU 11H ; Search for first match in directory
|
|
B_F_SNEXT EQU 12H ; Search for next match in directory
|
|
B_F_DELETE EQU 13H ; Delete file
|
|
B_F_READ EQU 14H ; Read file sequentially
|
|
B_F_WRITE EQU 15H ; Write file sequentially
|
|
B_F_MAKE EQU 16H ; Create and open file
|
|
B_F_RENAME EQU 17H ; Rename file
|
|
B_DRV_LOGVEC EQU 18H ; Return bitmap of logged-in drives
|
|
B_DRV_GET EQU 19H ; Return current drive
|
|
B_F_DMAOFF EQU 1AH ; Set DMA address
|
|
B_DRV_AVEC EQU 1BH ; Return address of allocation map
|
|
B_DRV_SRO EQU 1CH ; Software write-protect current drive
|
|
B_DRV_ROVEC EQU 1DH ; Return bitmap of read-only drives
|
|
B_DRV_DPB EQU 1FH ; Get Drive Parameter Block address
|
|
B_F_USERNUM EQU 20H ; Get/set user number
|
|
B_F_RDRAND EQU 21H ; Random access read record
|
|
B_F_WRTRAND EQU 22H ; Random access write record
|
|
B_F_SIZE EQU 23H ; Compute file size
|
|
B_F_RANDREC EQU 24H ; Update random access pointer
|
|
B_DRV_RESET EQU 25H ; Selectively reset disk drives
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|
|
; ================================================================
|
|
; AUX56 PHASE 2: MAIN-bank FCB/DMA gateway in otherwise idle MAIN TPA.
|
|
; The executable TPA is in AUX $0100-$AFFF, so MAIN $7000 is free.
|
|
; All caller pointers refer to AUX unless explicitly translated.
|
|
; MAIN $7800-$7BFF holds permanent staging and ProDOS-safe scratch.
|
|
; ================================================================
|
|
|
|
; ================================================================
|
|
; RamWorks Phase 3B: two parked AUX transients, cooperative switching.
|
|
; Resident MAIN-memory code below $1000 is outside both AUX TPAs.
|
|
; Open Apple is sampled alongside each keystroke or status peek.
|
|
; It cooperatively parks applications; no interrupt preemption.
|
|
; ================================================================
|
|
; Foreground-biased scheduler. Quanta count completed BIOS/BDOS calls,
|
|
; not CPU time. A busy foreground gets long uninterrupted bursts;
|
|
; background work gets a short turn. When foreground is idle/blocked,
|
|
; background uses the original 32-call quantum.
|
|
SCHFGQ EQU 128
|
|
SCHBGQ EQU 8
|
|
; Software cursor: foreground physical text only, never saved in a
|
|
; RamWorks backing screen. Polling input normally bypasses VGET, so
|
|
; draw the reverse-video cell while a foreground task waits for a key.
|
|
ORG 005A8H
|
|
CUON LD A,(CUACTIVE)
|
|
OR A
|
|
RET NZ
|
|
LD A,(VSOK)
|
|
OR A
|
|
RET Z
|
|
PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
LD HL,(VSPTR) ; 6502 VCOL,VROW pair
|
|
LD A,(HL)
|
|
LD C,A
|
|
INC HL
|
|
LD A,(HL)
|
|
CP 24
|
|
JR NC,CUONDONE
|
|
ADD A,A
|
|
LD E,A
|
|
LD D,0
|
|
LD HL,CUROWTAB
|
|
ADD HL,DE
|
|
LD E,(HL)
|
|
INC HL
|
|
LD D,(HL)
|
|
LD A,C
|
|
AND 1
|
|
LD (CUHALF),A ; 0 AUX, 1 MAIN
|
|
SRL C
|
|
LD B,0
|
|
EX DE,HL
|
|
ADD HL,BC
|
|
LD (CUADDR),HL
|
|
CALL CUMAP
|
|
LD A,(HL)
|
|
LD (CUUNDER),A
|
|
BIT 7,A
|
|
JR Z,CUONINV
|
|
AND 07FH
|
|
CP 060H
|
|
JR NC,CUONINV
|
|
AND 03FH ; uppercase: avoid MouseText
|
|
CUONINV LD (HL),A
|
|
CALL VSPREP ; leave RAMRD/WRT, PAGE1 normal
|
|
LD A,1
|
|
LD (CUACTIVE),A
|
|
CUONDONE POP HL
|
|
POP DE
|
|
POP BC
|
|
RET
|
|
|
|
SCHIDLEQ EQU 32
|
|
ORG 00600H
|
|
; Stage 1 scheduler. RUNTASK is TABMODE; SCHFG owns the physical screen.
|
|
; These are cooperative slices: no call is interrupted mid-operation.
|
|
; SCHMAIN is called with MAIN/CCP stack active after a transient yields.
|
|
SCHMAIN LD A,(SCHEN)
|
|
OR A
|
|
RET Z
|
|
SMLOOP LD A,(TABREQ)
|
|
CP 0FFH
|
|
RET NZ ; manual tab switch has priority
|
|
LD A,(SCHFG)
|
|
LD E,A
|
|
LD D,0
|
|
LD HL,TSKFLAGS
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR A
|
|
RET Z ; foreground is at CCP, not in .COM
|
|
CALL SCHONE ; optional background quantum
|
|
LD A,(SCHFG)
|
|
LD B,A
|
|
CALL SCHSWAP ; restore foreground BDOS context
|
|
; Stage 3: a foreground .COM parked on CONIN must not
|
|
; enter the blocking 6502 VGET while other jobs are runnable.
|
|
; Poll the *physical* keyboard and keep running background
|
|
; quanta until a key appears (or a manual tab switch arrives).
|
|
LD A,(SCHFG)
|
|
LD E,A
|
|
LD D,0
|
|
LD HL,SCHWAIT
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR A
|
|
JR Z,SMRUN
|
|
CALL SCHHAVE
|
|
OR A
|
|
JR Z,SMRUN ; no background jobs: normal VGET cursor
|
|
CALL CUON
|
|
CALL BIOSCONST
|
|
OR A
|
|
JR Z,SMLOOP ; input not ready; don't enter VGET
|
|
CALL CUOFF
|
|
CALL SCHWAKE ; input can complete: make task runnable
|
|
SMRUN CALL CUOFF ; restore cell before foreground runs
|
|
LD A,SCHFGQ
|
|
LD (SCHQLEFT),A ; foreground receives longer quantum
|
|
CALL TABRESUM ; foreground until next yield/exit
|
|
JR SMLOOP
|
|
|
|
; Called from foreground CCP's console line editor before CONIN.
|
|
; When background work exists, poll keyboard and run background slices.
|
|
; Preserve the editor's live IX, BC, DE and flags across task execution.
|
|
SCHIDLE PUSH AF
|
|
PUSH BC
|
|
PUSH DE
|
|
PUSH IX
|
|
PUSH IY
|
|
SIAGAIN CALL SCHHAVE
|
|
OR A
|
|
JR Z,SIDONE ; no background jobs: normal VGET
|
|
LD A,3
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
LD A,(AREG)
|
|
OR A
|
|
JR NZ,SIDONE ; physical key waiting, let VGET read
|
|
CALL CUON ; show cursor during cooperative polling
|
|
CALL SCHONE
|
|
JR SIAGAIN
|
|
SIDONE CALL CUOFF ; key or blocking VGET takes ownership
|
|
POP IY
|
|
POP IX
|
|
POP DE
|
|
POP BC
|
|
POP AF
|
|
RET
|
|
|
|
; Pick a runnable background tab. Does not touch the screen or terminal
|
|
; personality. Run for one quantum; then restore foreground CCP context.
|
|
SCHONE LD A,(SCHEN)
|
|
OR A
|
|
RET Z
|
|
LD A,(SCHNEXT)
|
|
LD C,A
|
|
LD B,16
|
|
SOSCAN LD A,(TABCOUNT)
|
|
CP C
|
|
JR NZ,SOIDX
|
|
LD C,0
|
|
SOIDX LD A,(SCHFG)
|
|
CP C
|
|
JR Z,SOADV
|
|
LD E,C
|
|
LD D,0
|
|
LD HL,TSKFLAGS
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR A
|
|
JR Z,SOADV
|
|
LD HL,SCHWAIT
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR A
|
|
JR NZ,SOADV
|
|
LD HL,TBHOLD
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR A
|
|
JR NZ,SOADV
|
|
LD A,C
|
|
INC A
|
|
LD (SCHNEXT),A
|
|
LD B,C
|
|
CALL SCHSWAP
|
|
; A busy foreground needs quick redraws; give background
|
|
; only a few calls. When foreground is at CCP or blocked
|
|
; on input, allow full-sized background quanta instead.
|
|
LD A,(SCHFG)
|
|
LD E,A
|
|
LD D,0
|
|
LD HL,TSKFLAGS
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR A
|
|
JR Z,SOFULL
|
|
LD HL,SCHWAIT
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR A
|
|
JR NZ,SOFULL
|
|
LD A,SCHBGQ
|
|
JR SOQUANT
|
|
SOFULL LD A,SCHIDLEQ
|
|
SOQUANT LD (SCHQLEFT),A
|
|
CALL TABRESUM
|
|
LD A,(SCHFG)
|
|
LD B,A
|
|
CALL SCHSWAP
|
|
LD A,1
|
|
RET
|
|
SOADV INC C
|
|
DJNZ SOSCAN
|
|
XOR A
|
|
RET
|
|
|
|
; Boolean: is there any runnable background task? Preserves BC, DE.
|
|
SCHHAVE PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
LD A,(SCHNEXT)
|
|
LD C,A
|
|
LD B,16
|
|
SHSCAN LD A,(TABCOUNT)
|
|
CP C
|
|
JR NZ,SHIDX
|
|
LD C,0
|
|
SHIDX LD A,(SCHFG)
|
|
CP C
|
|
JR Z,SHADV
|
|
LD E,C
|
|
LD D,0
|
|
LD HL,TSKFLAGS
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR A
|
|
JR Z,SHADV
|
|
LD HL,SCHWAIT
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR A
|
|
JR NZ,SHADV
|
|
LD HL,TBHOLD
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR A
|
|
JR NZ,SHADV
|
|
LD A,1
|
|
JR SHDONE
|
|
SHADV INC C
|
|
DJNZ SHSCAN
|
|
XOR A
|
|
SHDONE POP HL
|
|
POP DE
|
|
POP BC
|
|
RET
|
|
|
|
; No terminal snapshot and no physical terminal-mode changes on a
|
|
; background context switch; only COMMON BDOS drive state is exchanged.
|
|
SCHSWAP LD A,(TABMODE)
|
|
CP B
|
|
RET Z
|
|
PUSH BC
|
|
CALL VSSTSAVE ; flush active terminal state to descriptor
|
|
CALL SCHSAVE
|
|
POP BC
|
|
LD A,B
|
|
LD (TABMODE),A
|
|
CALL SCHLOAD
|
|
CALL VSSTLOAD ; restore cursor/escape parser/mode
|
|
JP VSSELECT ; 6502 maps backing screen for this task
|
|
|
|
ORG 00770H
|
|
; 16 eight-character executable names, only valid while TSKFLAGS is set.
|
|
TBNAMES DEFS 128
|
|
; Explicit manual pause is separate from SCHWAIT (console-read parking).
|
|
TBHOLD DEFS 16
|
|
|
|
ORG 00800H
|
|
; BDOS 10 may suspend only at an empty line. A successful hotkey
|
|
; parks the read itself; resume restarts it, without submitting CR.
|
|
AXLSKEY LD A,0FAH ; deferred BDOS 10 input
|
|
LD L,A
|
|
RET
|
|
|
|
; Blocked BDOS 1 and BIOS CONIN sample Open Apple at key-read time.
|
|
TSKCIN CALL BIOSCONIN
|
|
CALL TSKKEY
|
|
JR C,TSKCIKEY
|
|
OR A
|
|
JR Z,TSKCIN ; OA-current was consumed
|
|
CP 3
|
|
JR NZ,TSKECHO
|
|
LD A,1
|
|
LD (LCQUIT),A
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
TSKECHO PUSH AF
|
|
LD E,A
|
|
CALL C_WRITE
|
|
POP AF
|
|
LD L,A
|
|
RET
|
|
TSKCIKEY LD A,0F1H ; deferred cooked input
|
|
LD L,A
|
|
RET
|
|
|
|
TSKBIRD CALL BIOSCONIN
|
|
CALL TSKKEY
|
|
JR C,TSKBIKEY
|
|
OR A
|
|
JR Z,TSKBIRD ; OA-current was consumed
|
|
LD L,A
|
|
RET
|
|
TSKBIKEY LD A,0F2H ; deferred BIOS character input
|
|
LD L,A
|
|
RET
|
|
|
|
TSKINIT LD HL,SCHWAIT
|
|
LD DE,SCHWAIT+1
|
|
LD BC,15
|
|
XOR A
|
|
LD (HL),A
|
|
LDIR
|
|
LD (SCHFG),A
|
|
LD (SCHNEXT),A
|
|
LD A,SCHFGQ
|
|
LD (SCHQLEFT),A
|
|
LD A,(TABCOUNT)
|
|
CP 2
|
|
LD A,0
|
|
JR C,SKINIT0
|
|
INC A
|
|
SKINIT0 LD (SCHEN),A
|
|
LD HL,TSKFLAGS
|
|
LD DE,TSKFLAGS+1
|
|
LD BC,15
|
|
XOR A
|
|
LD (HL),A
|
|
LDIR
|
|
LD A,0FFH
|
|
LD (TABREQ),A
|
|
LD HL,TBHOLD
|
|
LD DE,TBHOLD+1
|
|
LD BC,15
|
|
XOR A
|
|
LD (HL),A
|
|
LDIR
|
|
RET
|
|
|
|
TSKFLPTR LD A,(TABMODE)
|
|
LD E,A
|
|
LD D,0
|
|
LD HL,TSKFLAGS
|
|
ADD HL,DE
|
|
RET
|
|
TSKACTIVE CALL TSKFLPTR
|
|
LD A,(HL)
|
|
RET
|
|
|
|
; A=ASCII key, XREG=Open Apple state sampled by the 6502 at key read.
|
|
; Carry=1 if a DIFFERENT tab was queued. OA+current beeps and returns
|
|
; carry=0, A=0 (consumed), so callers don't suspend or pass the key.
|
|
; Arrow keys wrap around TABCOUNT; numeric/hex keys select directly.
|
|
; Preserve BC (TABDECODE uses C, and AXLINE holds its count there).
|
|
TSKKEY PUSH BC
|
|
PUSH AF
|
|
LD A,(XREG)
|
|
BIT 7,A
|
|
JR Z,TSKIGNORE
|
|
POP AF
|
|
PUSH AF
|
|
CP 08H ; Apple IIe left-arrow
|
|
JR Z,TSKPREV
|
|
CP 15H ; Apple IIe right-arrow
|
|
JR Z,TSKNEXT
|
|
CALL TABDECODE
|
|
JR C,TSKIGNORE
|
|
JR TSKQUEUE
|
|
TSKPREV LD A,(TABMODE)
|
|
OR A
|
|
JR NZ,TSKPREVB
|
|
LD A,(TABCOUNT)
|
|
TSKPREVB DEC A
|
|
JR TSKQUEUE
|
|
TSKNEXT LD A,(TABMODE)
|
|
INC A
|
|
LD B,A
|
|
LD A,(TABCOUNT)
|
|
CP B
|
|
JR Z,TSKWRAP
|
|
JR C,TSKWRAP
|
|
LD A,B
|
|
JR TSKQUEUE
|
|
TSKWRAP XOR A
|
|
TSKQUEUE LD B,A ; B is saved by TSKKEY prologue
|
|
LD A,(SCHFG)
|
|
CP B
|
|
JR Z,TSKSAME
|
|
LD A,B
|
|
LD (TABREQ),A
|
|
LD A,3
|
|
LD (AXQUIT),A
|
|
POP BC ; discard the original character/flags
|
|
POP BC ; restore caller's BC
|
|
SCF
|
|
RET
|
|
TSKSAME PUSH HL ; UNIMP changes HL (6502 vector)
|
|
CALL UNIMP ; beep: Open Apple + current tab
|
|
POP HL
|
|
LD A,(0E010H) ; clear physical key strobe (status path)
|
|
POP BC ; discard saved character/flags
|
|
POP BC ; restore caller BC
|
|
XOR A ; consumed: no input, no suspension
|
|
RET
|
|
TSKIGNORE POP AF ; ordinary key: return it unchanged
|
|
POP BC
|
|
OR A ; clear carry
|
|
RET
|
|
|
|
TSKSET PUSH AF
|
|
CALL TSKFLPTR
|
|
POP AF
|
|
LD (HL),A
|
|
RET
|
|
|
|
; DE = 27 * logical session index + TSKREGS, return HL.
|
|
TSKSLOTP LD A,(TABMODE)
|
|
LD L,A
|
|
LD H,0
|
|
LD E,L
|
|
LD D,H
|
|
ADD HL,HL
|
|
ADD HL,HL
|
|
ADD HL,HL ; 8n
|
|
LD B,H
|
|
LD C,L
|
|
ADD HL,HL ; 16n
|
|
ADD HL,BC ; 24n
|
|
ADD HL,DE ; 25n
|
|
ADD HL,DE ; 26n
|
|
ADD HL,DE ; 27n
|
|
LD DE,TSKREGS
|
|
ADD HL,DE
|
|
RET
|
|
|
|
; Copy 27 bytes of preserved Z80/BDOS context to the current slot.
|
|
; TASKSAVE is called immediately after AUXBDOS, before changing tabs.
|
|
TASKSAVE LD (TSKBC),BC
|
|
LD (TSKDE),DE
|
|
LD (TSKIX),IX
|
|
LD (TSKIY),IY
|
|
EXX
|
|
LD (TSKAB),BC
|
|
LD (TSKAD),DE
|
|
LD (TSKAH),HL
|
|
EXX
|
|
EX AF,AF'
|
|
PUSH AF
|
|
POP HL
|
|
LD (TSKAA),HL
|
|
EX AF,AF'
|
|
LD HL,(AXASP)
|
|
LD (TSKSP),HL
|
|
LD HL,(AXRHL)
|
|
LD (TSKHL),HL
|
|
LD HL,(DMAADDR)
|
|
LD (TSKDM),HL
|
|
LD HL,(AXDMAP)
|
|
LD (TSKDP),HL
|
|
LD A,(AXRES)
|
|
LD (TSKRE),A
|
|
LD HL,(AXLIP)
|
|
LD (TSKLI),HL
|
|
LD A,(AXRES)
|
|
CP 0F1H ; cooked CONIN deferred by Open Apple
|
|
JR Z,TSKWAIT
|
|
CP 0F2H ; raw BIOS CONIN deferred
|
|
JR Z,TSKWAIT
|
|
CP 0FAH ; BDOS 10 read deferred
|
|
JR NZ,TSKNOWT
|
|
TSKWAIT CALL SCHBLCK ; background must not retry VGET
|
|
TSKNOWT LD A,1
|
|
CALL TSKSET
|
|
LD HL,TSKBUFF
|
|
CALL TSKSLOTP
|
|
EX DE,HL
|
|
LD HL,TSKBUFF
|
|
LD BC,TSKSIZE
|
|
LDIR
|
|
RET
|
|
|
|
; Reconstitute the same registers and return values before the saved
|
|
; AUX stack is made active. Caller must have selected the RamWorks bank.
|
|
TASKLOAD CALL TSKSLOTP
|
|
LD DE,TSKBUFF
|
|
LD BC,TSKSIZE
|
|
LDIR
|
|
LD HL,(TSKSP)
|
|
LD (AXASP),HL
|
|
LD HL,(TSKHL)
|
|
LD (AXRHL),HL
|
|
LD HL,(TSKDM)
|
|
LD (DMAADDR),HL
|
|
LD HL,(TSKDP)
|
|
LD (AXDMAP),HL
|
|
LD A,(TSKRE)
|
|
LD (AXRES),A
|
|
LD HL,(TSKLI)
|
|
LD (AXLIP),HL
|
|
LD BC,(TSKAB)
|
|
LD DE,(TSKAD)
|
|
LD HL,(TSKAH)
|
|
EXX
|
|
LD HL,(TSKAA)
|
|
PUSH HL
|
|
EX AF,AF'
|
|
POP AF
|
|
EX AF,AF'
|
|
LD IX,(TSKIX)
|
|
LD IY,(TSKIY)
|
|
LD BC,(TSKBC)
|
|
LD DE,(TSKDE)
|
|
RET
|
|
|
|
; TAB n resumes a parked program. All MLI and cache resources are
|
|
; shared, hence physical handles were flushed when it was parked.
|
|
TABRESUM CALL TSKACTIVE
|
|
OR A
|
|
RET Z
|
|
XOR A
|
|
CALL TSKSET ; clear active flag during execution
|
|
LD A,(TABMODE)
|
|
CALL TABPHYS
|
|
CALL AXBANK
|
|
CALL AXSNAP ; save ProDOS MAIN LC; do not load .COM
|
|
CALL TASKLOAD
|
|
CALL AXCONT ; returns after new ^] or program exit
|
|
LD A,(AXQUIT)
|
|
CP 3
|
|
JR Z,TSKRSUS
|
|
; Completed program retains its own AUX LC/TPA for CCP SAVE.
|
|
CALL CACHEALL
|
|
CALL AXMAINLC
|
|
CALL SVFLPTR
|
|
LD (HL),1
|
|
JR TSKRFIN
|
|
TSKRSUS CALL CACHEALL
|
|
CALL AXMAINLC ; parked bank's LC must survive
|
|
TSKRFIN XOR A
|
|
CALL AXBANK
|
|
LD A,(AXQUIT)
|
|
CP 3
|
|
; Backing display is live; no snapshot at suspension.
|
|
LD HL,FILEBUF
|
|
LD (DMAADDR),HL
|
|
LD HL,0080H
|
|
LD (AXDMAP),HL
|
|
CALL SBWSET ; defer WBOOT until this tab has CCP
|
|
RET
|
|
|
|
TSKMSG DEFM 'Suspended (Open Apple+0-9/A-F/arrows)'
|
|
DEFB 13,'$'
|
|
TSKBUSY DEFM 'Session suspended: select it first'
|
|
DEFB 13,'$'
|
|
; Shared serialization buffer and one copy for each session.
|
|
TSKBUFF
|
|
TSKBC DEFW 0
|
|
TSKDE DEFW 0
|
|
TSKIX DEFW 0
|
|
TSKIY DEFW 0
|
|
TSKAB DEFW 0
|
|
TSKAD DEFW 0
|
|
TSKAH DEFW 0
|
|
TSKAA DEFW 0
|
|
TSKSP DEFW 0
|
|
TSKHL DEFW 0
|
|
TSKDM DEFW 0
|
|
TSKDP DEFW 0
|
|
TSKRE DEFB 0
|
|
TSKLI DEFW 0 ; interrupted BDOS 10 AUX buffer pointer
|
|
TSKEND
|
|
TSKSIZE EQU TSKEND-TSKBUFF
|
|
|
|
; On resume, complete a parked input call *before* returning to AUX.
|
|
; AXRES carries a private marker only when suspended in keyboard I/O.
|
|
; No synthetic NUL/CR is sent to MBASIC, WordStar or the CCP.
|
|
AXRETRY LD A,(AXRES)
|
|
CP 0F1H
|
|
JR Z,ARCOOK
|
|
CP 0F2H
|
|
JR Z,ARBIOS
|
|
CP 0FAH
|
|
JR Z,ARLINE
|
|
CP 0F6H
|
|
JR Z,ARRAW
|
|
CP 0F3H
|
|
JR Z,ARSTAT
|
|
JP AXGO
|
|
ARCOOK CALL TSKCIN
|
|
JR ARDONE
|
|
ARBIOS CALL TSKBIRD
|
|
JR ARDONE
|
|
ARLINE LD DE,(AXLIP)
|
|
CALL AXLINE
|
|
JR ARDONE
|
|
ARRAW LD E,0FFH
|
|
CALL AUXBDOS6
|
|
JR ARDONE
|
|
ARSTAT CALL TSKSTAT ; retry actual BIOS/BDOS status request
|
|
ARDONE LD (AXRES),A
|
|
LD (AXRHL),HL
|
|
LD A,(AXQUIT)
|
|
CP 3
|
|
CALL Z,TASKSAVE
|
|
LD A,(AXQUIT)
|
|
OR A
|
|
JP NZ,AXFIN
|
|
JP AXGO
|
|
|
|
; CCP-only MouseText print. A='A'/$41 (Open Apple), 'H'/$48 (left),
|
|
; 'U'/$55 (right). The stock VPUT path forces normal or inverse ASCII,
|
|
; so print one spacer through the 6502 parser to advance its cursor,
|
|
; then replace that physical text cell with a raw MouseText screen byte.
|
|
; All three sign-on glyphs are deliberately away from row wrap.
|
|
; Reuse the working software-cursor's bank-0 mapping helper; never
|
|
; alter video interpretation for CP/M applications.
|
|
MTCHAR PUSH AF
|
|
CALL CUOFF
|
|
LD E,' '
|
|
CALL C_WRITE
|
|
LD HL,(VSPTR) ; 6502 state: VCOL followed by VROW
|
|
LD A,(HL)
|
|
DEC A ; one cell back, same line (no wrap)
|
|
LD C,A
|
|
INC HL
|
|
LD A,(HL)
|
|
ADD A,A
|
|
LD E,A
|
|
LD D,0
|
|
LD HL,CUROWTAB
|
|
ADD HL,DE
|
|
LD E,(HL)
|
|
INC HL
|
|
LD D,(HL)
|
|
LD A,C
|
|
AND 1
|
|
LD (CUHALF),A ; even=aux, odd=main
|
|
SRL C
|
|
LD B,0
|
|
EX DE,HL
|
|
ADD HL,BC
|
|
LD (CUADDR),HL
|
|
CALL CUMAP ; selects bank 0 physical screen
|
|
POP AF
|
|
LD (HL),A ; low screen code $41/$48/$55 = MouseText
|
|
JP VSPREP ; restore normal main/video switches
|
|
|
|
; ================================================================
|
|
; RamWorks Stage 2: 6502 renderer maintains live screen backing.
|
|
; VSSAVE seeds session 0 once; VSREST redraws a selected snapshot.
|
|
; Physical 6502 $2000-$2FFF in bank 2 (Z80 $1000-$1FFF) holds
|
|
; 1KB MAIN + 1KB AUX video page for each session.
|
|
;
|
|
; $C001/HIRES/PAGE2 maps ONLY the Apple II HGR page ($2000-$3FFF)
|
|
; to RamWorks AUX, so this MAIN resident code ($0B00) can copy directly
|
|
; via Z80 $1000-$1FFF. Z80 $3000-$33FF (physical MAIN $4000) is
|
|
; temporary copy scratch during TAB/suspend; SAVE cannot run then.
|
|
; $C073 banks and $C0xx switches are always restored before returning.
|
|
; The two live CP/M AUX banks 0/1 are never used for screen storage.
|
|
; ================================================================
|
|
ORG 00B00H
|
|
VS80ON EQU 0E001H
|
|
VSPAGE1 EQU 0E054H
|
|
VSPAGE2 EQU 0E055H
|
|
VSHIRES0 EQU 0E056H
|
|
VSHIRES1 EQU 0E057H
|
|
VSTEXT EQU 0F400H
|
|
VSSCRATCH EQU 03000H
|
|
VSMINSIZE EQU 0400H
|
|
VSTLEN EQU 9 ; contiguous VCOL..VATTR in 6502
|
|
|
|
; 6502 $B81E returns VCOL address as A=low, X=high. Convert to
|
|
; SoftCard Z80 address by subtracting $1000 from its high byte.
|
|
VSINIT LD HL,VSMETAS
|
|
LD DE,VSMETAS+1
|
|
LD BC,159
|
|
XOR A
|
|
LD (HL),A
|
|
LDIR
|
|
LD A,(RWTOTAL)
|
|
CP 3
|
|
LD A,0
|
|
JR C,VSNOTRW
|
|
INC A
|
|
VSNOTRW LD (VSOK),A
|
|
LD HL,0B81EH
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
LD A,(AREG)
|
|
LD L,A
|
|
LD A,(XREG)
|
|
SUB 010H
|
|
LD H,A
|
|
LD (VSPTR),HL
|
|
RET
|
|
|
|
; Every snapshot bank provides an 8KB HGR window: 4 x 2KB screens.
|
|
; session 0..3 -> bank 2; 4..7 -> bank 5; 8..11 -> bank 10;
|
|
; 12..15 -> bank 15. Data lives at Z80 $1000..$2FFF.
|
|
VSSLOT LD B,A ; 0 main half, 1 auxiliary half
|
|
LD A,(TABMODE)
|
|
AND 3
|
|
ADD A,A
|
|
ADD A,A
|
|
ADD A,A ; logical slot within group * $0800
|
|
ADD A,010H
|
|
LD D,A
|
|
LD E,0
|
|
LD A,B
|
|
OR A
|
|
RET Z
|
|
LD A,D
|
|
ADD A,4
|
|
LD D,A
|
|
RET
|
|
|
|
VSMETA LD A,(TABMODE)
|
|
LD L,A
|
|
LD H,0
|
|
LD E,L
|
|
LD D,H
|
|
ADD HL,HL ; 2n
|
|
ADD HL,HL ; 4n
|
|
ADD HL,HL ; 8n
|
|
ADD HL,DE ; 9n
|
|
ADD HL,DE ; 10n
|
|
LD DE,VSMETAS
|
|
ADD HL,DE
|
|
RET
|
|
|
|
VSSBANK LD A,(TABMODE)
|
|
CP 4
|
|
JR C,VSB_FIRST
|
|
SRL A
|
|
SRL A ; group 1..3
|
|
LD B,A
|
|
ADD A,A
|
|
ADD A,A
|
|
ADD A,B ; group * 5
|
|
RET
|
|
VSB_FIRST LD A,2
|
|
RET
|
|
|
|
; Explicitly normalise main/aux CPU access before copying video.
|
|
; The scheduler and virtual-terminal code remain untouched.
|
|
VSPREP XOR A
|
|
LD (SW80OFF),A
|
|
LD (RDMAIN),A
|
|
LD (WRMAIN),A
|
|
LD A,(VSHIRES0)
|
|
LD A,(VSPAGE1)
|
|
RET
|
|
|
|
; The live physical screen is always in RamWorks bank 0.
|
|
VSPREP0 XOR A
|
|
CALL AXBANK
|
|
JP VSPREP
|
|
|
|
; Open AUX HGR window for the current session's backing screen.
|
|
VSWINDOW CALL VSPREP
|
|
CALL VSSBANK
|
|
CALL AXBANK
|
|
XOR A
|
|
LD (VS80ON),A
|
|
LD A,(VSHIRES1)
|
|
LD A,(VSPAGE2)
|
|
RET
|
|
|
|
; Close AUX HGR window and return to bank 0 with MAIN reads/writes.
|
|
VSUNWIN CALL VSPREP
|
|
XOR A
|
|
CALL AXBANK
|
|
RET
|
|
|
|
; Read physical MAIN text page from bank 0, not inherited mapping.
|
|
VSFROMMAIN CALL VSPREP0
|
|
LD HL,VSTEXT
|
|
LD DE,VSSCRATCH
|
|
LD BC,VSMINSIZE
|
|
LDIR
|
|
RET
|
|
; Only TEXT1 is AUX; the copy scratch stays MAIN.
|
|
VSFROMAUX CALL VSPREP0
|
|
XOR A
|
|
LD (VS80ON),A
|
|
LD A,(VSPAGE2)
|
|
LD HL,VSTEXT
|
|
LD DE,VSSCRATCH
|
|
LD BC,VSMINSIZE
|
|
LDIR
|
|
JP VSPREP
|
|
|
|
; A=half (0 main, 1 aux): copy staged 1K to bank2 snapshot.
|
|
VSWRITE CALL VSSLOT
|
|
PUSH DE
|
|
CALL VSWINDOW
|
|
POP DE
|
|
LD HL,VSSCRATCH
|
|
LD BC,VSMINSIZE
|
|
LDIR
|
|
JP VSUNWIN
|
|
|
|
; A=half: copy saved 1K to staging scratch, then restore bank0.
|
|
VSREAD CALL VSSLOT
|
|
PUSH DE
|
|
CALL VSWINDOW
|
|
POP HL
|
|
LD DE,VSSCRATCH
|
|
LD BC,VSMINSIZE
|
|
LDIR
|
|
JP VSUNWIN
|
|
|
|
VSSAVE LD A,(VSOK)
|
|
OR A
|
|
RET Z
|
|
CALL VSMETA
|
|
LD A,1
|
|
LD (HL),A
|
|
INC HL
|
|
EX DE,HL
|
|
LD HL,(VSPTR)
|
|
LD BC,VSTLEN
|
|
LDIR
|
|
CALL VSFROMMAIN
|
|
XOR A
|
|
CALL VSWRITE
|
|
CALL VSFROMAUX
|
|
LD A,1
|
|
JP VSWRITE
|
|
|
|
; Restore byte-identical MAIN and AUX text pages, cursor/parser data,
|
|
; then return to 80-col text state. Initial unused tab is cleared.
|
|
VSREST LD A,(VSOK)
|
|
OR A
|
|
JP Z,CCPCLS
|
|
CALL VSMETA
|
|
LD A,(HL)
|
|
OR A
|
|
JR NZ,VRDRAW
|
|
CALL CCPCLS ; new tab: clear physical screen
|
|
CALL VSSAVE ; seed BOTH halves from clean physical screen
|
|
JP VSSTSAVE ; save current cursor/parser state
|
|
VRDRAW XOR A
|
|
CALL VSREAD
|
|
CALL VSPREP0 ; MAIN screen target, bank 0
|
|
LD HL,VSSCRATCH
|
|
LD DE,VSTEXT
|
|
LD BC,VSMINSIZE
|
|
LDIR
|
|
LD A,1
|
|
CALL VSREAD
|
|
CALL VSPREP0 ; reset RAMRD/RAMWRT/PAGE1
|
|
XOR A
|
|
LD (VS80ON),A
|
|
LD A,(VSPAGE2)
|
|
LD HL,VSSCRATCH
|
|
LD DE,VSTEXT
|
|
LD BC,VSMINSIZE
|
|
LDIR
|
|
JP VSPREP
|
|
|
|
VSPTR DEFW 0
|
|
VSOK DEFB 0
|
|
|
|
; Shared MAIN descriptors; never in the application's switchable AUX RAM.
|
|
ORG 00C70H
|
|
; Extended TAB commands. Parsed CCP tail is " N ACTION " or " LIST ".
|
|
TBCMD LD A,(FILEBUF)
|
|
OR A
|
|
JP Z,TB_LIST
|
|
LD A,(FILEBUF+2)
|
|
CP 'L'
|
|
JR NZ,TC_CHECKH
|
|
LD HL,FILEBUF+2
|
|
LD DE,TCLIST
|
|
CALL TCMATCH
|
|
JP Z,TB_LIST
|
|
JP CCPBAD
|
|
TC_CHECKH CP 'H'
|
|
JR NZ,TC_NUM
|
|
LD HL,FILEBUF+2
|
|
LD DE,TCHELP
|
|
CALL TCMATCH
|
|
JP Z,TB_HELP
|
|
JP CCPBAD
|
|
TC_NUM CALL TABDECODE
|
|
JP C,CCPBAD
|
|
LD B,A
|
|
LD A,(FILEBUF+3)
|
|
CP ' '
|
|
JP NZ,CCPBAD
|
|
LD A,(FILEBUF+4)
|
|
OR A
|
|
JP Z,TB_SWITCH
|
|
LD HL,FILEBUF+4
|
|
LD DE,TCSUSP
|
|
CALL TCMATCH
|
|
JP Z,TB_PAUSE
|
|
LD HL,FILEBUF+4
|
|
LD DE,TCRESUM
|
|
CALL TCMATCH
|
|
JP Z,TB_UNPS
|
|
LD HL,FILEBUF+4
|
|
LD DE,TCEXCL
|
|
CALL TCMATCH
|
|
JP Z,TB_EXCL
|
|
LD HL,FILEBUF+4
|
|
LD DE,TCKILL
|
|
CALL TCMATCH
|
|
JP Z,TB_KILL
|
|
JP CCPBAD
|
|
TCMATCH LD A,(HL)
|
|
CP ' ' ; any unambiguous prefix accepted
|
|
RET Z
|
|
LD A,(DE)
|
|
CP (HL)
|
|
RET NZ
|
|
INC DE
|
|
INC HL
|
|
JR TCMATCH
|
|
|
|
|
|
ORG 00D00H
|
|
TABCTXS DEFS 112 ; 16 * 7 bytes
|
|
TSKFLAGS DEFS 16 ; one parked flag per logical session
|
|
TSKREGS DEFS 432 ; 16 * 27-byte saved Z80 contexts
|
|
VSMETAS DEFS 160 ; 16 * (valid + 9 terminal bytes)
|
|
CCPPROMS DEFS 16 ; 1 if restored CCP screen includes prompt
|
|
SCHWAIT DEFS 16 ; background task parked on input
|
|
SCHFG DEFB 0 ; visible/keyboard-owning session
|
|
SCHNEXT DEFB 0 ; round-robin background cursor
|
|
SCHQLEFT DEFB SCHFGQ ; syscall quantum remaining
|
|
SCHEN DEFB 0 ; enabled if >=2 sessions
|
|
|
|
; Reclaimed MAIN $4000-$6FFF: permanent CCP SAVE and helpers.
|
|
; No AUX LC snapshot is ever written here.
|
|
; AUX56 CCP SAVE: dump transient pages from $0100 through $DDFF.
|
|
; Read the owning tab's ordinary AUX RAM and AUX LC in place.
|
|
; Select its RamWorks bank; never use a global high-memory snapshot.
|
|
; Only MAIN $AUXSTAGE is passed as the ProDOS write DMA buffer.
|
|
; ================================================================
|
|
ORG 04000H
|
|
SAVEFILE LD HL,FILEBUF+2 ; decimal pages (the first argument)
|
|
LD B,0
|
|
SVDEC LD A,(HL)
|
|
CP ' '
|
|
JR Z,SVNUM
|
|
SUB '0'
|
|
JP C,SVBAD
|
|
CP 10
|
|
JP NC,SVBAD
|
|
LD C,A
|
|
LD A,B
|
|
CP 23 ; max previous value 22: 22*10+9<256
|
|
JP NC,SVBAD
|
|
ADD A,A
|
|
LD D,A
|
|
ADD A,A
|
|
ADD A,A
|
|
ADD A,D
|
|
ADD A,C
|
|
CP 222 ; legal pages: 0 through 221 (DDFF top)
|
|
JP NC,SVBAD
|
|
LD B,A
|
|
INC HL
|
|
JR SVDEC
|
|
SVNUM LD A,B
|
|
LD (SVPAGES),A
|
|
CP 176 ; higher pages need post-transient LC image
|
|
JR C,SVMAKE
|
|
CALL SVFLPTR
|
|
LD A,(HL)
|
|
OR A
|
|
JP Z,SVBAD ; no completed image in THIS tab
|
|
SVMAKE LD DE,FCB2
|
|
CALL F_MAKE
|
|
OR A
|
|
JP NZ,SVIOERR
|
|
LD HL,0100H
|
|
LD (SVPTR),HL
|
|
LD A,(SVPAGES)
|
|
OR A
|
|
JR Z,SVEMPTY
|
|
LD A,(TABMODE)
|
|
CALL TABPHYS
|
|
CALL AXBANK ; select owner of low RAM and AUX LC
|
|
CALL SVPREP ; preserve ProDOS's full MAIN LC bank2
|
|
SVPAGE LD A,(SVPAGES)
|
|
OR A
|
|
JR Z,SVDONE
|
|
LD B,2 ; two 128-byte records per page
|
|
SVRECORD PUSH BC
|
|
CALL SVCOPY ; ordinary AUX or live AUX LC
|
|
LD HL,AUXSTAGE
|
|
LD (DMAADDR),HL
|
|
LD DE,FCB2
|
|
CALL F_WRITE ; normal MAIN ProDOS write
|
|
POP BC
|
|
OR A
|
|
JR NZ,SVFAIL
|
|
DJNZ SVRECORD
|
|
LD A,(SVPAGES)
|
|
DEC A
|
|
LD (SVPAGES),A
|
|
JR SVPAGE
|
|
SVDONE LD DE,FCB2
|
|
CALL F_CLOSE
|
|
LD (SVSTAT),A
|
|
CALL SVREST ; restore full MAIN LC bank2/common
|
|
LD A,(SVSTAT)
|
|
OR A
|
|
JR NZ,SVIOERR
|
|
JR SVRESET
|
|
SVFAIL LD DE,FCB2
|
|
CALL F_CLOSE
|
|
CALL SVREST ; must restore LC on write failure too
|
|
JR SVIOERR
|
|
SVEMPTY LD DE,FCB2 ; SAVE 0: just create empty file
|
|
CALL F_CLOSE
|
|
OR A
|
|
JR NZ,SVIOERR
|
|
SVRESET XOR A
|
|
CALL AXBANK ; restore bank 0, including error paths
|
|
LD HL,FILEBUF
|
|
LD (DMAADDR),HL
|
|
RET
|
|
SVIOERR LD DE,REMSG
|
|
CALL C_WRITESTR
|
|
JR SVRESET
|
|
SVBAD LD DE,BFMSG
|
|
CALL C_WRITESTR
|
|
JR SVRESET
|
|
|
|
; SAVE temporarily installs the AUX RAM read trampoline in MAIN LC.
|
|
; Preserve ProDOS MAIN LC across that operation and F_WRITE, using
|
|
; MAIN $1000-$3FFF (scratch only after the transient has terminated).
|
|
SVPREP XOR A
|
|
LD (SW80OFF),A
|
|
LD (ZPMAIN),A
|
|
LD (RDMAIN),A
|
|
LD (WRMAIN),A
|
|
LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
LD HL,0B000H
|
|
LD DE,01000H
|
|
LD BC,03000H
|
|
LDIR
|
|
LD A,(LCB1RO)
|
|
RET
|
|
|
|
; Before $B000 use the MAIN LC trampoline to read ordinary AUX RAM.
|
|
; From $B000, use ALTZP to read the selected bank's live AUX LC.
|
|
; This routine itself executes in ordinary MAIN $4000 code and writes
|
|
; to MAIN $AUXSTAGE, so it does not execute from the switched LC.
|
|
SVCOPY LD HL,(SVPTR)
|
|
LD DE,0B000H
|
|
XOR A
|
|
SBC HL,DE
|
|
JR NC,SVHIGH
|
|
LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
LD HL,LCIMG
|
|
LD DE,LCBASE
|
|
LD BC,LCLEN
|
|
LDIR
|
|
LD HL,(SVPTR)
|
|
LD DE,AUXSTAGE
|
|
LD BC,128
|
|
CALL LCA2M ; BC=0 on return; HL advanced 128
|
|
LD (SVPTR),HL
|
|
LD A,(LCB1RO)
|
|
RET
|
|
SVHIGH LD A,(LCB2RW)
|
|
LD A,(LCB2RW) ; select LC bank 2 read/write
|
|
XOR A
|
|
LD (ZPAUX),A ; read high memory from selected AUX LC
|
|
LD HL,(SVPTR)
|
|
LD DE,AUXSTAGE
|
|
LD BC,128
|
|
LDIR ; AUX LC -> ordinary MAIN staging
|
|
LD (ZPMAIN),A ; ProDOS's MAIN LC and zero page
|
|
LD A,(LCB1RO)
|
|
LD HL,(SVPTR)
|
|
LD DE,128
|
|
ADD HL,DE
|
|
LD (SVPTR),HL
|
|
RET
|
|
|
|
; Put back ProDOS's original MAIN LC bank 2/common after closing file.
|
|
SVREST LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
LD HL,01000H
|
|
LD DE,0B000H
|
|
LD BC,03000H
|
|
LDIR
|
|
LD A,(LCB1RO)
|
|
RET
|
|
SVPAGES DEFB 0
|
|
SVPTR DEFW 0100H
|
|
SVSTAT DEFB 0
|
|
; SAVE validity belongs to the logical tab, not to a shared snapshot.
|
|
; Each tab's completed image remains in its own physical RamWorks bank.
|
|
SVVALID DEFS 16
|
|
SVFLPTR LD A,(TABMODE)
|
|
LD L,A
|
|
LD H,0
|
|
LD DE,SVVALID
|
|
ADD HL,DE
|
|
RET
|
|
SVCLEAR LD HL,SVVALID
|
|
LD DE,SVVALID+1
|
|
LD BC,15
|
|
XOR A
|
|
LD (HL),A
|
|
LDIR
|
|
RET
|
|
|
|
; CCP helper routines relocated here to keep resident code below $9800.
|
|
; Uppercase the command input (Pascal string) in place.
|
|
UCASE LD B,(HL)
|
|
UCL1 LD A,B
|
|
OR A
|
|
RET Z
|
|
INC HL
|
|
LD A,(HL)
|
|
CP 'a'
|
|
JR C,UCS1
|
|
CP 'z'+1
|
|
JR NC,UCS1
|
|
SUB 'a'-'A'
|
|
LD (HL),A
|
|
UCS1 DJNZ UCL1
|
|
RET
|
|
|
|
|
|
; Shared confirmation reader. A=0 for Y; A=1 for N.
|
|
; Invalid input is ignored; C_READ echoes typed characters.
|
|
CCPYESNO CALL C_WRITESTR
|
|
CYKEY CALL XSCCPKEY ; check active XSUB stream first
|
|
AND 0DFH
|
|
CP 'Y'
|
|
JR Z,CYYES
|
|
CP 'N'
|
|
JR NZ,CYKEY
|
|
XOR A
|
|
LD (XSLEFT),A ; discard pending synthetic CR of Y/N record
|
|
LD E,13
|
|
CALL C_WRITE
|
|
LD A,1
|
|
RET
|
|
CYYES XOR A
|
|
LD (XSLEFT),A
|
|
LD E,13
|
|
CALL C_WRITE
|
|
XOR A
|
|
RET
|
|
CCPBYE LD DE,BYEASK
|
|
CALL CCPYESNO
|
|
OR A
|
|
RET NZ
|
|
XOR A
|
|
LD (CMD),A
|
|
JP BYERESUME ; Stable SoftCard handoff; next start is cold
|
|
BYEASK DEFM 'Y/N? $'
|
|
|
|
|
|
|
|
; CCP's built-in command table and terminal selector live in the unused
|
|
; MAIN-memory TPA. $7600-$77FF is free of AXBDOS ($7000-$73xx)
|
|
; and the permanent AUX bridge staging region ($7800 onward).
|
|
|
|
; ---------------------------------------------------------------------
|
|
; Reclaimed MAIN ($4400 onward): IOBYTE devices + resident workspaces.
|
|
; This is ordinary, unbanked MAIN RAM: never part of an AUX transient.
|
|
; ---------------------------------------------------------------------
|
|
; ================================================================
|
|
; CP/M IOBYTE / physical serial and printer devices.
|
|
; MAIN $4400: former AUX LC snapshot space, now persistent MAIN code.
|
|
; Apple monitor slot firmware entry $Cn00 via Z80->6502 CMD=1.
|
|
; During ROM call set CSW/KSW appropriately so input/output is
|
|
; unambiguous to slot firmware. Restore monitor vectors on return.
|
|
; No per-card UART registers used. ROM input is blocking and 7-bit.
|
|
; ================================================================
|
|
ORG 04400H
|
|
; Physical LST: from IOBYTE bits 7:6:
|
|
; TTY=serial2, CRT=screen, LPT=printer1, UL1=serial2.
|
|
IOLIST LD A,(IOBYTE)
|
|
AND 0C0H
|
|
CP 040H
|
|
JP Z,IOVOUT
|
|
CP 080H
|
|
JP Z,IOPRN
|
|
JP IOSEROUT
|
|
; RDR and PUN are routed through their own IOBYTE fields.
|
|
; The selectors live at MAIN $4C00 to avoid crowding the slot driver.
|
|
IOPUNCH JP IOPUNSEL
|
|
IOREADER JP IORDRSEL
|
|
; CON: CRT=VRAWPUT, BAT=RDR input / LST output; TTY/UC1=serial2.
|
|
IOCONOUT LD A,(IOBYTE)
|
|
AND 3
|
|
CP 2
|
|
JP Z,IOLIST
|
|
JP IOSEROUT
|
|
IOVOUT LD A,E
|
|
LD HL,RAWOUT
|
|
JP C_SENDA
|
|
; Main/AUX redirected BDOS 1 input. Console echo uses selected device.
|
|
IOCCOOK CALL IOCONIN
|
|
PUSH AF
|
|
LD E,A
|
|
CALL IOCONOUT
|
|
POP AF
|
|
LD L,A
|
|
RET
|
|
; No portable poll-ready entry in arbitrary slot firmware.
|
|
; Conservative CONST = 00; LISTST = FF (blocking output driver).
|
|
IOZERO XOR A
|
|
LD L,A
|
|
RET
|
|
IOSTAT JP IOZERO
|
|
; Firmware output. E=CP/M character. Physical slot selected in A.
|
|
IOPRN LD A,010H
|
|
LD HL,0C100H
|
|
JP IOSEND
|
|
IOSEROUT LD A,020H
|
|
LD HL,0C200H
|
|
IOSEND PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
LD (XREG),A ; Apple slot ROMs conventionally use X=n*16
|
|
LD A,E
|
|
OR 080H ; Apple monitor character convention
|
|
LD (AREG),A
|
|
LD (ADDR),HL
|
|
LD HL,(0F036H) ; 6502 monitor CSW
|
|
PUSH HL
|
|
LD HL,(0F038H) ; KSW
|
|
PUSH HL
|
|
LD HL,(ADDR)
|
|
LD (0F036H),HL ; current slot output
|
|
LD HL,0FD1BH ; native keyboard, not this slot
|
|
LD (0F038H),HL
|
|
; Card ROM invokes Apple Monitor at $Fxxx. Normally the
|
|
; MAIN LC RAM is selected ($C088), so expose ROM briefly.
|
|
LD A,(0E08AH) ; 6502 $C08A: bank 1, read ROM
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
LD A,(LCB1RO) ; restore bank 1 LC RAM for ProDOS
|
|
POP HL
|
|
LD (0F038H),HL
|
|
POP HL
|
|
LD (0F036H),HL
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
; Input from slot 2. Firmware is blocking; high bit is Apple's
|
|
; character marker, not reliable serial 8-bit data, so strip it.
|
|
IOSERIN PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
LD A,020H
|
|
LD (XREG),A
|
|
LD HL,0C200H
|
|
LD (ADDR),HL
|
|
LD HL,(0F036H)
|
|
PUSH HL
|
|
LD HL,(0F038H)
|
|
PUSH HL
|
|
LD HL,0FDF0H ; monitor video COUT1 != slot 2
|
|
LD (0F036H),HL
|
|
LD HL,0C200H
|
|
LD (0F038H),HL ; select slot 2 as input
|
|
; Slot firmware can call the Monitor ROM at $Fxxx.
|
|
LD A,(0E08AH) ; 6502 $C08A: bank 1, read ROM
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
LD A,(LCB1RO) ; restore bank 1 LC RAM for ProDOS
|
|
LD A,(AREG)
|
|
AND 07FH
|
|
LD (IOCHAR),A
|
|
POP HL
|
|
LD (0F038H),HL
|
|
POP HL
|
|
LD (0F036H),HL
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
LD A,(IOCHAR)
|
|
LD L,A
|
|
RET
|
|
IOCHAR DEFB 0
|
|
|
|
ORG 04500H
|
|
; 64 byte buffer for storing a file path as a Pascal style string
|
|
PATHBUF
|
|
PATHLEN DEFB 0
|
|
PATH DEFS 64
|
|
|
|
; 64 byte buffer for storing a second file path as a Pascal style string
|
|
PATHBUF2
|
|
PATHLEN2 DEFB 0
|
|
PATH2 DEFS 64
|
|
|
|
; Record file reference numbers for each I/O buffer
|
|
; Or 0 if no file is using the buffer
|
|
FRN1 DEFB 0
|
|
FRN2 DEFB 0
|
|
FRN3 DEFB 0
|
|
FRN4 DEFB 0
|
|
|
|
; 512 byte buffer for reading directories (used by F_SFIRST and F_SNEXT)
|
|
DIRBUF
|
|
DIRBUF1 DEFS 128
|
|
DIRBUF2 DEFS 128
|
|
DIRBUF3 DEFS 128
|
|
DIRBUF4 DEFS 128
|
|
|
|
ORG 04800H
|
|
; Drive read-only check/reporter is an independent helper. Move it out
|
|
; of resident $8100-$97FF, which must not overlap IOBUF1 at $9800.
|
|
; Its callers use CALL DCHKRO / JP RODENY; all JR targets stay local.
|
|
DCHKRO PUSH BC
|
|
PUSH HL
|
|
XOR A
|
|
LD (ROERROR),A ; Clear stale error from previous call
|
|
LD A,(DE)
|
|
OR A
|
|
JR NZ,DCR_EXPL
|
|
LD A,(CURDRV)
|
|
AND 0FH
|
|
JR DCR_IDX
|
|
DCR_EXPL DEC A
|
|
DCR_IDX CP 16
|
|
JR NC,DCR_BAD
|
|
LD (RODRIVE),A ; Actual effective drive for message
|
|
LD B,A
|
|
LD HL,(ROVEC)
|
|
LD A,B
|
|
OR A
|
|
JR Z,DCR_BIT
|
|
DCR_SHF SRL H
|
|
RR L
|
|
DEC A
|
|
JR NZ,DCR_SHF
|
|
DCR_BIT LD A,L
|
|
AND 01H
|
|
JR DCR_DONE
|
|
DCR_BAD LD A,1
|
|
OR A
|
|
DCR_DONE POP HL
|
|
POP BC
|
|
RET
|
|
RODENY JP RODISK ; CP/M: BDOS Err on d: R/O
|
|
|
|
|
|
|
|
ORG 04900H
|
|
; SUBMIT: diagnostics removed; preserve zero-length-file safeguard.
|
|
; CP/M 2.2 SUBMIT: A:$$$.SUB contains reversed 128-byte records.
|
|
; Record byte 0 is the command length, followed by command characters.
|
|
; On entry from CCPREAD, return A=1 with FILEBUF+1 count and +2 text,
|
|
; or A=0 when there is no pending command. Consume record ON DISK first.
|
|
; All filesystem buffers/ProDOS handles are MAIN-owned; only the CCP
|
|
; invokes this routine. The fixed FCB explicitly selects drive A.
|
|
; Native XSUB: this same record reader is called from the CCP and
|
|
; the AUX BDOS 0Ah handler. A=1 means SUBREC has a valid next line;
|
|
; A=0 means absent/invalid submission (fall back to keyboard).
|
|
CCPSUB XOR A
|
|
LD (XSLEFT),A ; discard a previous program's partial line
|
|
CALL XSNEXT ; preserves the MAIN DMA pointer
|
|
LD (SBFROM),A ; current CCP line came from $$$.SUB
|
|
OR A
|
|
JR NZ,SBCCOPY
|
|
LD (XSFLAG),A ; stream ended, future scripts start disabled
|
|
RET
|
|
SBCCOPY JP SBCOPY
|
|
SBNEXT LD A,(SBHALT)
|
|
OR A
|
|
JR Z,SBSTART
|
|
XOR A
|
|
RET
|
|
SBSTART LD HL,SUBFCB+12
|
|
LD B,24
|
|
XOR A
|
|
SBCLEAR LD (HL),A
|
|
INC HL
|
|
DJNZ SBCLEAR
|
|
LD DE,SUBFCB
|
|
CALL F_OPEN
|
|
OR A
|
|
JR Z,SBOPEN
|
|
XOR A ; no $$$.SUB: normal keyboard input
|
|
RET
|
|
SBOPEN LD DE,SUBFCB
|
|
CALL F_SIZE ; fills FCB random records 21H..23H
|
|
OR A
|
|
JP NZ,SBERROR
|
|
LD HL,(SUBFCB+21H)
|
|
LD A,H
|
|
OR L
|
|
JR NZ,SBHAVE
|
|
LD A,(SUBFCB+23H)
|
|
OR A
|
|
JP Z,SBZERO ; retain anomalous zero-sized file
|
|
SBHAVE ; Move random record index to the LAST existing record.
|
|
LD HL,(SUBFCB+21H)
|
|
LD A,H
|
|
OR L
|
|
JR NZ,SBDEC
|
|
LD HL,SUBFCB+23H
|
|
DEC (HL) ; borrow from high record counter
|
|
SBDEC LD HL,(SUBFCB+21H)
|
|
DEC HL
|
|
LD (SUBFCB+21H),HL
|
|
LD HL,SUBREC
|
|
LD (DMAADDR),HL
|
|
LD DE,SUBFCB
|
|
CALL F_READRAND
|
|
LD HL,FILEBUF
|
|
LD (DMAADDR),HL
|
|
OR A
|
|
JP NZ,SBERROR
|
|
LD A,(SUBREC)
|
|
OR A
|
|
JP Z,SBERROR ; never silently eat an empty/bad record
|
|
CP 121
|
|
JP NC,SBERROR ; cannot fit CCP's 120-char buffer
|
|
; R0..R2 now contain the number of records to retain.
|
|
LD HL,(SUBFCB+21H)
|
|
LD A,H
|
|
OR L
|
|
JR NZ,SBTRUNC
|
|
LD A,(SUBFCB+23H)
|
|
OR A
|
|
JR NZ,SBTRUNC
|
|
JP SBEMPTY
|
|
SBZERO ; Preserve unexpectedly empty $$$.SUB for inspection.
|
|
; Mark halted to prevent a new attempt at every CCP prompt.
|
|
LD DE,SUBFCB
|
|
CALL F_CLOSE
|
|
LD A,1
|
|
LD (SBHALT),A
|
|
LD DE,SBZEROMSG
|
|
CALL C_WRITESTR
|
|
XOR A
|
|
RET
|
|
SBEMPTY LD DE,SUBFCB
|
|
CALL F_CLOSE
|
|
OR A
|
|
JP NZ,SBERROR
|
|
LD DE,SUBFCB
|
|
CALL F_DELETE ; last command: ERA $$$.SUB
|
|
OR A
|
|
JP NZ,SBERROR
|
|
JP SBDONE
|
|
SBTRUNC ; Truncate an open ProDOS file to R0..R2 * 128 bytes.
|
|
; IO_BEGIN resolves logical S2=$7E to a physical ProDOS FRN.
|
|
LD IX,SUBFCB
|
|
CALL IO_BEGIN
|
|
OR A
|
|
JP NZ,SBERROR
|
|
LD A,(IX+0EH)
|
|
LD (SEMLIN),A
|
|
LD HL,(SUBFCB+21H)
|
|
LD A,(SUBFCB+23H)
|
|
LD B,7
|
|
SBSHIFT ADD HL,HL
|
|
RLA
|
|
DJNZ SBSHIFT
|
|
LD (SEMLIE1),HL
|
|
LD (SEMLIE3),A
|
|
LD HL,SEMLI
|
|
CALL PRODOS
|
|
CALL IO_END ; put back virtual S2 marker
|
|
PUSH AF
|
|
LD DE,SUBFCB
|
|
CALL F_CLOSE
|
|
LD B,A
|
|
POP AF
|
|
OR A
|
|
JP NZ,SBERROR
|
|
LD A,B
|
|
OR A
|
|
JP NZ,SBERROR
|
|
SBDONE LD A,1 ; record consumed; SUBREC remains valid
|
|
RET
|
|
SBCOPY LD A,(SUBREC)
|
|
; Length was checked while the on-disk record still existed.
|
|
LD (FILEBUF+1),A
|
|
LD C,A
|
|
LD A,120
|
|
LD (FILEBUF),A
|
|
LD B,0
|
|
LD HL,SUBREC+1
|
|
LD DE,FILEBUF+2
|
|
LDIR
|
|
; Echo the submitted command just as if typed at the prompt.
|
|
LD HL,SUBREC+1
|
|
LD A,(SUBREC)
|
|
LD B,A
|
|
SBECHO LD E,(HL)
|
|
PUSH HL
|
|
PUSH BC
|
|
CALL C_WRITE
|
|
POP BC
|
|
POP HL
|
|
INC HL
|
|
DJNZ SBECHO
|
|
LD E,13
|
|
CALL C_WRITE
|
|
LD A,1
|
|
RET
|
|
SBERROR LD DE,SUBFCB
|
|
CALL F_CLOSE ; best effort, preserve file for diagnosis
|
|
LD A,1
|
|
LD (SBHALT),A ; do not retry corrupt file every prompt
|
|
LD DE,SBERRMSG
|
|
CALL C_WRITESTR
|
|
XOR A
|
|
RET
|
|
SBERRMSG DEFM 'SUBMIT invalid record or I/O error (file retained)'
|
|
DEFB 13,'$'
|
|
SBZEROMSG DEFM 'SUBMIT zero records - $$$.SUB retained for inspection'
|
|
DEFB 13,'$'
|
|
SBHALT DEFB 0
|
|
SUBFCB DEFB 1
|
|
DEFM '$$$ SUB'
|
|
DEFS 24
|
|
SUBREC DEFS 128
|
|
; A WBOOT from a background AUX tab must never reset the foreground
|
|
; CCP stack. Defer it until that tab becomes the visible CCP.
|
|
SBW_PTR LD A,(TABMODE)
|
|
LD E,A
|
|
LD D,0
|
|
LD HL,SBWARM
|
|
ADD HL,DE
|
|
RET
|
|
SBWSET LD A,(AXQUIT)
|
|
CP 1
|
|
RET NZ
|
|
CALL SBW_PTR
|
|
LD (HL),1
|
|
RET
|
|
SBWGET CALL SBW_PTR
|
|
LD A,(HL)
|
|
RET
|
|
SBWCLR CALL SBW_PTR
|
|
LD (HL),0
|
|
RET
|
|
SBWARM DEFS 16
|
|
|
|
; ---------------------------------------------------------------------
|
|
; IOBYTE RDR:/PUN: routing. CP/M physical device names are BIOS-defined.
|
|
; This system has only one serial port, so TTY and PTR/PTP both use slot 2.
|
|
; UP1 = parallel printer (slot 1); UP2 = Zapple screen.
|
|
; UR1 = Apple keyboard (blocking/no echo); UR2 = immediate CP/M EOF (^Z).
|
|
; IOBYTE field bits: RDR 3:2, PUN 5:4.
|
|
; Keep out of the 256-byte slot-firmware bridge at $4400-$44FF.
|
|
; ---------------------------------------------------------------------
|
|
ORG 04C00H
|
|
IOPUNSEL LD A,(IOBYTE)
|
|
AND 030H
|
|
CP 020H
|
|
JP Z,IOPRN ; UP1: slot 1 printer
|
|
CP 030H
|
|
JP Z,IOVOUT ; UP2: built-in video
|
|
JP IOSEROUT ; TTY/PTP: shared slot 2 serial
|
|
IORDRSEL LD A,(IOBYTE)
|
|
AND 00CH
|
|
CP 008H
|
|
JP Z,IORDKEY ; UR1: built-in keyboard
|
|
CP 00CH
|
|
JP Z,IORDEOF ; UR2: no reader, return EOF
|
|
JP IOSERIN ; TTY/PTR: shared slot 2 serial
|
|
IORDKEY LD HL,RDKEY ; 6502 VGET, blocking, no echo
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
LD A,(AREG)
|
|
AND 07FH
|
|
LD L,A
|
|
RET
|
|
IORDEOF LD A,01AH ; CP/M text EOF
|
|
LD L,A
|
|
RET
|
|
; CON:=BAT: uses RDR: for input; other non-CRT console modes use serial.
|
|
IOCONIN LD A,(IOBYTE)
|
|
AND 3
|
|
CP 2
|
|
JP Z,IOREADER
|
|
JP IOSERIN
|
|
|
|
ORG 05000H
|
|
; Native XSUB: opt-in only by an XSUB directive *inside* .SUB.
|
|
; No resident AUX code; submit input stays in MAIN and is owned by one tab.
|
|
CCPXSUB LD A,(SBFROM)
|
|
OR A
|
|
RET Z ; manual XSUB does not enable batch mode
|
|
LD A,(TABMODE)
|
|
LD (XSOWNER),A
|
|
LD A,1
|
|
LD (XSFLAG),A
|
|
XOR A
|
|
LD (XSLEFT),A
|
|
RET
|
|
|
|
; SBNEXT uses the shared MAIN DMA pointer. Don't perturb its caller.
|
|
XSNEXT PUSH BC
|
|
PUSH DE
|
|
PUSH IX
|
|
PUSH IY
|
|
LD HL,(DMAADDR)
|
|
PUSH HL
|
|
CALL SBNEXT
|
|
POP HL
|
|
LD (DMAADDR),HL
|
|
POP IY
|
|
POP IX
|
|
POP DE
|
|
POP BC
|
|
RET
|
|
|
|
; A=1 if native XSUB is enabled for the current tab; A=0 otherwise.
|
|
XSENABL PUSH BC
|
|
LD A,(XSFLAG)
|
|
OR A
|
|
JR Z,XSEOFF
|
|
LD A,(TABMODE)
|
|
LD B,A
|
|
LD A,(XSOWNER)
|
|
CP B
|
|
JR NZ,XSEOFF
|
|
LD A,1
|
|
POP BC
|
|
RET
|
|
XSEOFF XOR A
|
|
POP BC
|
|
RET
|
|
|
|
; Avoid parking a tab on physical keyboard input when a scripted
|
|
; character is actually ready. For other BDOS calls keep normal SCHPRE.
|
|
XSPRE LD A,C
|
|
CP 1 ; cooked BDOS CONIN
|
|
JR Z,XSPCHECK
|
|
CP 10 ; buffered BDOS CONIN
|
|
JR Z,XSPCHECK
|
|
CP 0F2H ; BIOS CONIN
|
|
JP NZ,SCHPRE
|
|
XSPCHECK CALL XSREADY
|
|
OR A ; clear carry on a ready virtual byte
|
|
RET NZ
|
|
JP SCHPRE
|
|
|
|
; Lazy reverse-record fetch: pending line, then CR. Polls may prefetch,
|
|
; but the same record is subsequently consumed by *any* input API.
|
|
; Return A=1 when a character is ready; zero when none.
|
|
XSREADY CALL XSENABL
|
|
OR A
|
|
RET Z
|
|
LD A,(XSLEFT)
|
|
OR A
|
|
JR NZ,XSYES
|
|
CALL XSNEXT
|
|
OR A
|
|
JR Z,XSEOF
|
|
LD A,(SUBREC)
|
|
INC A
|
|
LD (XSLEFT),A
|
|
LD HL,SUBREC+1
|
|
LD (XSPTR),HL
|
|
XSYES LD A,1
|
|
RET
|
|
XSEOF XOR A
|
|
LD (XSFLAG),A ; future submissions require XSUB again
|
|
RET
|
|
|
|
; Carry set and A=byte when scripted, carry clear when keyboard needed.
|
|
XSCHAR PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
CALL XSREADY
|
|
OR A
|
|
JR Z,XSCNONE
|
|
LD A,(XSLEFT)
|
|
DEC A
|
|
LD (XSLEFT),A
|
|
JR Z,XSCR
|
|
LD HL,(XSPTR)
|
|
LD A,(HL)
|
|
INC HL
|
|
LD (XSPTR),HL
|
|
JR XSCGOT
|
|
XSCR LD A,13
|
|
XSCGOT SCF
|
|
JR XSCEXIT
|
|
XSCNONE XOR A ; no virtual byte: carry clear
|
|
XSCEXIT POP HL
|
|
POP DE
|
|
POP BC
|
|
RET
|
|
|
|
; MAIN CCP ERA/BYE Y/N uses C_READ directly, not BDOS 0Ah/01h.
|
|
; Echo synthetic input just like C_READ does, otherwise original read.
|
|
XSCCPKEY CALL XSCHAR
|
|
JP NC,C_READ
|
|
PUSH AF
|
|
LD E,A
|
|
CALL C_WRITE
|
|
POP AF
|
|
RET
|
|
|
|
; AUX BDOS 01: cooked, echoing input; normal scheduler when no script.
|
|
XSCIN CALL XSCHAR
|
|
JP NC,AXCIN
|
|
LD L,A
|
|
PUSH HL
|
|
LD E,A
|
|
CALL C_WRITE
|
|
POP HL
|
|
LD A,L
|
|
RET
|
|
|
|
; AUX BIOS CONIN: raw input, no echo.
|
|
XSBIIN CALL XSCHAR
|
|
JP NC,AXBIREAD
|
|
LD L,A
|
|
RET
|
|
|
|
; AUX BDOS 06: substitute only when E=FF (input mode).
|
|
XSRAW LD A,E
|
|
CP 0FFH
|
|
JP NZ,AUXBDOS6
|
|
CALL XSCHAR
|
|
JP NC,AUXBDOS6
|
|
LD L,A
|
|
RET
|
|
|
|
; AUX BDOS 11 and BIOS CONST: status polls see pending SUBMIT input.
|
|
XSSTAT CALL XSREADY
|
|
OR A
|
|
JP Z,ABDSTAT
|
|
LD A,0FFH
|
|
LD L,A
|
|
RET
|
|
XSBSTAT CALL XSREADY
|
|
OR A
|
|
JP Z,AXBICON
|
|
LD A,0FFH
|
|
LD L,A
|
|
RET
|
|
|
|
; AUX BDOS 0Ah: consume the *same* pending character stream as 01/06.
|
|
; This matters if an app polls before asking for buffered input, or
|
|
; consumes the first few characters via BDOS 01h before using 0Ah.
|
|
; AXLINE has validated caller buffer and fetched its capacity into AXLBUF.
|
|
AXLSUB LD B,0 ; number of chars copied
|
|
LD HL,AXLBUF+2
|
|
XSLNEXT PUSH BC
|
|
PUSH HL
|
|
CALL XSCHAR
|
|
POP HL
|
|
POP BC
|
|
JR NC,XSLDONE
|
|
CP 13
|
|
JR Z,XSLDONE
|
|
LD C,A ; preserve character
|
|
LD A,(AXLBUF)
|
|
CP B
|
|
JR Z,XSLNEXT ; discard beyond caller's capacity
|
|
LD (HL),C
|
|
INC HL
|
|
INC B
|
|
PUSH HL
|
|
PUSH BC
|
|
LD E,C
|
|
CALL C_WRITE
|
|
POP BC
|
|
POP HL
|
|
JR XSLNEXT
|
|
XSLDONE LD (HL),13
|
|
LD A,B
|
|
LD (AXLBUF+1),A
|
|
LD C,A
|
|
LD B,0
|
|
INC BC ; first buffer byte (maximum)
|
|
INC BC ; count byte
|
|
INC BC ; trailing CR
|
|
LD HL,AXLBUF
|
|
LD DE,(AXLIP)
|
|
CALL AXM2A
|
|
LD E,13
|
|
CALL C_WRITE
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
|
|
XSFLAG DEFB 0
|
|
XSOWNER DEFB 0
|
|
SBFROM DEFB 0
|
|
XSLEFT DEFB 0
|
|
XSPTR DEFW 0
|
|
|
|
ORG 05200H
|
|
; Visited tab membership is independent of task activity and video validity.
|
|
; Each entry is 0 (never visited / killed) or 1 (visited).
|
|
TB_VPTR LD E,A
|
|
LD D,0
|
|
LD HL,TBVIS
|
|
ADD HL,DE
|
|
RET
|
|
TB_VMARK CALL TB_VPTR
|
|
LD (HL),1
|
|
RET
|
|
TB_VINIT LD HL,TBVIS
|
|
LD DE,TBVIS+1
|
|
LD BC,15
|
|
XOR A
|
|
LD (HL),A
|
|
LDIR
|
|
LD A,1
|
|
LD (TBVIS),A
|
|
RET
|
|
TBVIS DEFS 16
|
|
|
|
ORG 05300H
|
|
; Listing does not switch banks or enter the paused applications.
|
|
TB_LIST LD B,0
|
|
TL_NEXT LD A,B
|
|
CALL TB_VPTR
|
|
LD A,(HL)
|
|
OR A
|
|
JR Z,TL_SKIP ; slot has not been visited, or was KILLed
|
|
PUSH BC
|
|
LD A,(SCHFG)
|
|
CP B
|
|
LD E,' '
|
|
JR NZ,TL_MARK
|
|
LD E,'*'
|
|
TL_MARK CALL C_WRITE
|
|
LD A,B
|
|
CALL PHNYB
|
|
LD E,' '
|
|
CALL C_WRITE
|
|
CALL TL_DRIVE ; fixed-width drive/user field, 5 columns
|
|
LD A,B
|
|
CALL TB_TFL
|
|
OR A
|
|
JR NZ,TL_PROG
|
|
LD DE,TL_CCP
|
|
JR TL_NAME
|
|
TL_PROG LD A,B
|
|
CALL TBNPTR
|
|
LD C,8
|
|
TL_CHAR LD E,(HL)
|
|
PUSH BC
|
|
PUSH HL
|
|
CALL C_WRITE
|
|
POP HL
|
|
POP BC
|
|
INC HL
|
|
DEC C
|
|
JR NZ,TL_CHAR
|
|
JR TL_STATE
|
|
TL_NAME CALL C_WRITESTR
|
|
TL_STATE LD A,B
|
|
CALL TB_HPTR
|
|
LD A,(HL)
|
|
OR A
|
|
JR Z,TL_WAIT
|
|
LD DE,TL_HOLD
|
|
JR TL_LABEL
|
|
TL_WAIT LD E,B
|
|
LD D,0
|
|
LD HL,SCHWAIT
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR A
|
|
JR Z,TL_EOL
|
|
LD DE,TL_BLOCK
|
|
TL_LABEL CALL C_WRITESTR
|
|
TL_EOL LD E,13
|
|
CALL C_WRITE
|
|
POP BC
|
|
TL_SKIP INC B
|
|
LD A,(TABCOUNT)
|
|
CP B
|
|
JR NZ,TL_NEXT
|
|
RET
|
|
|
|
; Drive for tab B. The currently executing tab has live CURDRV;
|
|
; inactive tabs have their last CURDRV (including user bits) at
|
|
; TABCTXS[tab*7 + 0]. Do NOT temporarily change TABMODE/TABPICK.
|
|
TL_DRIVE LD A,(TABMODE)
|
|
CP B
|
|
JR NZ,TL_DSAVE
|
|
LD A,(CURDRV)
|
|
JR TL_DCHAR
|
|
TL_DSAVE LD A,B
|
|
LD HL,TABCTXS
|
|
LD DE,7
|
|
TL_DLOOP OR A
|
|
JR Z,TL_DGET
|
|
ADD HL,DE
|
|
DEC A
|
|
JR TL_DLOOP
|
|
TL_DGET LD A,(HL)
|
|
; A = drive/user byte. Emit exactly FIVE columns:
|
|
; A:___ (user 0), A1:__ (user 1..9), A15:_ (user 10..15)
|
|
; with underscores representing spaces. B holds the TAB LIST index.
|
|
TL_DCHAR PUSH BC
|
|
LD C,A ; preserve combined user/drive code
|
|
AND 00FH
|
|
ADD A,'A'
|
|
LD E,A
|
|
CALL C_WRITE
|
|
LD A,C
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
AND 00FH
|
|
OR A
|
|
JR Z,TL_UZERO
|
|
CP 10
|
|
JR NC,TL_UTWO
|
|
CALL RWDECIMAL ; one user-number digit
|
|
LD B,2 ; two spaces follow the colon
|
|
JR TL_COLON
|
|
TL_UTWO CALL RWDECIMAL ; two digits, user 10..15
|
|
LD B,1
|
|
JR TL_COLON
|
|
TL_UZERO LD B,3 ; no user-number digits
|
|
TL_COLON LD E,':'
|
|
PUSH BC
|
|
CALL C_WRITE
|
|
POP BC
|
|
TL_DPAD LD E,' '
|
|
PUSH BC ; console output may affect counters
|
|
CALL C_WRITE
|
|
POP BC
|
|
DJNZ TL_DPAD
|
|
POP BC ; restore TAB LIST loop index
|
|
RET
|
|
|
|
|
|
|
|
|
|
TL_CCP DEFM 'CCP $' ; 8 columns, same as .COM names
|
|
|
|
ORG 05500H
|
|
; USER command. CCP normalized FILEBUF as: " n ", or empty.
|
|
; USER with no argument prints current code, USER 0..15 selects it.
|
|
CCPUSER LD A,(FILEBUF)
|
|
OR A
|
|
JR Z,CU_SHOW
|
|
LD HL,FILEBUF+2
|
|
LD A,(HL)
|
|
CP '0'
|
|
JP C,CCPBAD
|
|
CP '9'+1
|
|
JP NC,CCPBAD
|
|
SUB '0'
|
|
LD B,A
|
|
INC HL
|
|
LD A,(HL)
|
|
CP ' '
|
|
JR Z,CU_SET
|
|
; Two-digit values 10..15 only.
|
|
LD A,B
|
|
CP 1
|
|
JP NZ,CCPBAD
|
|
LD A,(HL)
|
|
CP '0'
|
|
JP C,CCPBAD
|
|
CP '6'
|
|
JP NC,CCPBAD
|
|
SUB '0'-10
|
|
LD B,A
|
|
INC HL
|
|
LD A,(HL)
|
|
CP ' '
|
|
JP NZ,CCPBAD
|
|
CU_SET LD E,B
|
|
CALL F_USERNUM
|
|
RET
|
|
CU_SHOW LD E,0FFH
|
|
CALL F_USERNUM
|
|
CALL RWDECIMAL
|
|
LD E,13
|
|
JP C_WRITE
|
|
|
|
; Lazy user-area directory creation. Called ONLY after a file CREATE
|
|
; returned $44 (parent pathname missing), never on ordinary opens.
|
|
; PATHBUF holds A3/FILE etc. Preserve its original Pascal length.
|
|
; ProDOS CREATE folder: filetype $0F and storage type $0D.
|
|
; Return A=0 on success or existing directory, else ProDOS error.
|
|
UD_MKDIR LD A,(CURDRV)
|
|
AND 0F0H
|
|
JP Z,UD_MISS ; user zero's A/ must pre-exist
|
|
LD A,(PATHLEN)
|
|
LD (UD_OLEN),A
|
|
LD HL,PATH+0
|
|
LD B,0
|
|
UD_SLASH LD A,(HL)
|
|
CP '/'
|
|
JR Z,UD_FOUND
|
|
INC B
|
|
INC HL
|
|
LD A,B
|
|
CP 5
|
|
JR C,UD_SLASH
|
|
LD A,044H
|
|
RET
|
|
UD_FOUND LD A,B
|
|
LD (PATHLEN),A
|
|
LD A,00FH
|
|
LD (FMMLIT),A
|
|
LD A,00DH
|
|
LD (FMMLIS),A
|
|
LD HL,FMMLI
|
|
CALL PRODOS
|
|
LD (UD_STAT),A
|
|
XOR A
|
|
LD (FMMLIT),A
|
|
INC A
|
|
LD (FMMLIS),A
|
|
LD A,(UD_OLEN)
|
|
LD (PATHLEN),A
|
|
LD A,(UD_STAT)
|
|
CP 047H ; a concurrent CREATE may have made it
|
|
JR Z,UD_OK
|
|
OR A
|
|
RET
|
|
UD_OK XOR A
|
|
RET
|
|
UD_MISS LD A,044H
|
|
RET
|
|
UD_OLEN DEFB 0
|
|
UD_STAT DEFB 0
|
|
UC_SAVE DEFB 0
|
|
|
|
; FCB file operations keep the USER at OPEN time in S1=$A0+user.
|
|
; S2=$7E denotes a virtual open; other FCBs use current USER.
|
|
; Returns A=0..15 and preserves BC, DE, HL, IX, IY.
|
|
F2P_USER PUSH HL
|
|
LD HL,0EH
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
CP 07EH
|
|
JR NZ,F2P_CUR
|
|
F2P_LOCK DEC HL
|
|
LD A,(HL)
|
|
AND 0F0H
|
|
CP 0A0H
|
|
JR NZ,F2P_CUR
|
|
LD A,(HL)
|
|
AND 00FH
|
|
POP HL
|
|
RET
|
|
F2P_CUR LD A,(CURDRV)
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
AND 00FH
|
|
POP HL
|
|
RET
|
|
|
|
ORG 05800H
|
|
BITABLE DEFM 'DIR '
|
|
DEFB 081H
|
|
DEFW DIRECT
|
|
DEFM 'ERA '
|
|
DEFB 1
|
|
DEFW ERASE
|
|
DEFM 'REN '
|
|
DEFB 1
|
|
DEFW RENAME
|
|
DEFM 'TYPE '
|
|
DEFB 1
|
|
DEFW BITYPE
|
|
DEFM 'SAVE '
|
|
DEFB 2
|
|
DEFW BISAVE
|
|
DEFM 'BYE '
|
|
DEFB 0
|
|
DEFW CCPBYE
|
|
DEFM 'TERM '
|
|
DEFB 1
|
|
DEFW CCPTERM
|
|
DEFM 'CACHE'
|
|
DEFB 081H ; zero or one argument (RESET)
|
|
DEFW CCPCACHE
|
|
DEFM 'CLS '
|
|
DEFB 0
|
|
DEFW CCPCLS
|
|
DEFM 'BANKS'
|
|
DEFB 0
|
|
DEFW CCPBANKS
|
|
DEFM 'TAB '
|
|
DEFB 0FEH ; TAB's variable argument syntax
|
|
DEFW CCPTAB
|
|
DEFM 'HELP '
|
|
DEFB 0
|
|
DEFW CCPHELP
|
|
DEFM 'XSUB '
|
|
DEFB 0
|
|
DEFW CCPXSUB
|
|
DEFM 'USER '
|
|
DEFB 081H ; USER or USER n
|
|
DEFW CCPUSER
|
|
|
|
|
|
ORG 05F00H
|
|
; CP/M 2.2 permits programs to read/write IOBYTE at 0003h directly.
|
|
; Each AUX transient has a private 0003h; resident BIOS uses MAIN 0003h.
|
|
; Synchronize AUX -> MAIN at every syscall and at ordinary .COM exit.
|
|
; Do not modify caller register arguments, return registers or flags.
|
|
AXIOSBD CALL AXIOSYN
|
|
JP AUXBDOS ; tail-call: return to AXGATE
|
|
AXIOSYN PUSH AF
|
|
PUSH HL
|
|
LD HL,IOBYTE
|
|
CALL LCPEEK ; reads one byte from active AUX bank
|
|
LD (IOBYTE),A
|
|
POP HL
|
|
POP AF
|
|
RET
|
|
; AXFIN may be entered directly from AUX LC with the application's SP.
|
|
; Restore the MAIN stack BEFORE calling the synchronizer.
|
|
AXIOSFX LD SP,(AXMSP)
|
|
CALL AXIOSYN
|
|
XOR A
|
|
LD (ZPMAIN),A
|
|
LD A,(LCB1RO)
|
|
XOR A
|
|
LD (AXMODE),A
|
|
RET
|
|
|
|
; Relocated resident routines: ample MAIN memory, separate from AUX TPA.
|
|
ORG 06000H
|
|
; Short CCP help plus explicit blocked-input label for TAB LIST.
|
|
CCPHELP LD DE,CCPH1
|
|
CALL C_WRITESTR
|
|
LD DE,CCPH2
|
|
JP C_WRITESTR
|
|
CCPH1 DEFM 'DIR ERA REN TYPE SAVE'
|
|
DEFB 13
|
|
DEFM 'BYE TERM XSUB USER'
|
|
DEFB 13,'$'
|
|
|
|
ORG 06100H
|
|
; A CP/M FCB is logically open as S2=$7E.
|
|
; Some CP/M software (SORCIM Pascal/M, including SuperCalc INSTALL)
|
|
; deliberately clears S2 immediately after a successful OPEN. Recover
|
|
; the open state from the S1=$A0|USER tag already installed by F_OPEN.
|
|
; Do not accept an untagged S2=0 FCB, which really was never opened.
|
|
; During I/O temporarily substitute the cached physical ProDOS FRN;
|
|
; IO_END restores S2=$7E. Physical directory/loader FCBs are unchanged.
|
|
IO_BEGIN XOR A
|
|
LD (IOVIRT),A
|
|
LD A,(IX+0EH)
|
|
CP 07EH
|
|
JR Z,IO_CACHE
|
|
OR A
|
|
JR NZ,IO_PHYS
|
|
LD A,(IX+0DH) ; S1 tag survives Pascal/M's S2 clear
|
|
AND 0F0H
|
|
CP 0A0H
|
|
JR Z,IO_CACHE
|
|
JR IO_BAD
|
|
IO_PHYS XOR A
|
|
RET
|
|
IO_BAD LD A,9
|
|
RET
|
|
IO_CACHE LD A,1
|
|
LD (IOVIRT),A
|
|
PUSH IX
|
|
PUSH IX
|
|
POP DE
|
|
LD IX,PATHBUF
|
|
CALL FCB2PATH
|
|
CALL CACHEFND
|
|
POP IX
|
|
OR A
|
|
JR Z,IO_REOPEN
|
|
LD (CMRU),A ; LRU bookkeeping on cache hit
|
|
PUSH AF
|
|
LD HL,CHITS
|
|
CALL CINC16
|
|
POP AF
|
|
CALL CACHEREF
|
|
LD (IX+0EH),A
|
|
XOR A
|
|
RET
|
|
IO_REOPEN ; Miss: keep the caller's extent/RC/CR across _F_OPEN.
|
|
PUSH IX
|
|
POP DE
|
|
LD A,(IX+0FH)
|
|
PUSH AF
|
|
LD A,(IX+0CH)
|
|
PUSH AF
|
|
LD A,(IX+20H)
|
|
PUSH AF
|
|
PUSH DE
|
|
PUSH IX
|
|
CALL _F_OPEN
|
|
LD C,A
|
|
POP IX
|
|
POP DE
|
|
POP AF
|
|
LD (IX+20H),A
|
|
POP AF
|
|
LD (IX+0CH),A
|
|
POP AF
|
|
LD (IX+0FH),A
|
|
LD A,C
|
|
OR A
|
|
JR NZ,IO_FAIL
|
|
LD A,(IX+0EH)
|
|
CALL GETIOADDR
|
|
CALL CACHEPUT
|
|
XOR A
|
|
RET
|
|
IO_FAIL XOR A
|
|
LD (IOVIRT),A
|
|
LD A,C
|
|
RET
|
|
|
|
; Preserve the BDOS status and keep the physical ProDOS handle cached.
|
|
IO_END PUSH AF
|
|
LD A,(IOVIRT)
|
|
OR A
|
|
JR Z,IO_DONE
|
|
LD (IX+0EH),07EH
|
|
IO_DONE POP AF
|
|
LD L,A
|
|
RET
|
|
IOVIRT DEFB 0
|
|
|
|
; Increment wrapping 16-bit counter at HL. No file handles are touched.
|
|
CINC16 INC (HL)
|
|
RET NZ
|
|
INC HL
|
|
INC (HL)
|
|
RET
|
|
; Reset counters only on cold start or via the explicit CACHE RESET.
|
|
CACHERST LD HL,0
|
|
LD (COPENS),HL
|
|
LD (CHITS),HL
|
|
LD (CEVICTS),HL
|
|
RET
|
|
COPENS DEFW 0
|
|
CHITS DEFW 0
|
|
CEVICTS DEFW 0
|
|
|
|
|
|
ORG 06400H
|
|
; VT100/H19-to-Soroc translator in MAIN (not banked AUX TPA).
|
|
; TERM VT100=0, SOROC=1, H19/H89/VT52=2 (VT52 is H19 subset).
|
|
; Mode 2 uses 6502 Soroc VMODE=1 for translated escape sequences;
|
|
; the distinct terminal personality lives only in tab IX+6.
|
|
; Never feed translated bytes through VTGATE: VTEMIT uses B80F.
|
|
; Per-tab 8-byte state: parser, value, first argument, semicolon flag,
|
|
; saved row, saved column, private CSI flag, cooked CRLF suppression.
|
|
; H19 uses parser states 4=row, 5=column, 6=one mode parameter,
|
|
; and IX+2 as pending zero-based row. Save/restore remain IX+4/5.
|
|
; Only the Soroc display engine touches the hardware/virtual screen.
|
|
VTISEN PUSH BC
|
|
PUSH IX
|
|
PUSH DE
|
|
CALL TABPICK
|
|
LD A,(IX+6)
|
|
POP DE
|
|
POP IX
|
|
POP BC
|
|
OR A
|
|
RET
|
|
|
|
; In: A=terminal mode. Preserves all caller registers and A.
|
|
VTSETMODE PUSH AF
|
|
PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
PUSH IX
|
|
LD C,A ; TABPICK does not touch C
|
|
CALL TABPICK
|
|
LD (IX+6),C
|
|
; Clear parser state when explicitly selecting a terminal.
|
|
CALL VTPTR
|
|
XOR A
|
|
LD B,8
|
|
VTRSTLOOP LD (IX+0),A
|
|
INC IX
|
|
DJNZ VTRSTLOOP
|
|
POP IX
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
POP AF
|
|
RET
|
|
|
|
; Return IX = state for current TABMODE; preserves BC/DE/HL.
|
|
VTPTR PUSH DE
|
|
PUSH HL
|
|
LD A,(TABMODE)
|
|
LD E,A
|
|
LD D,0
|
|
LD HL,VTSTATES
|
|
ADD HL,DE
|
|
ADD HL,DE
|
|
ADD HL,DE
|
|
ADD HL,DE
|
|
ADD HL,DE
|
|
ADD HL,DE
|
|
ADD HL,DE
|
|
ADD HL,DE
|
|
PUSH HL
|
|
POP IX
|
|
POP HL
|
|
POP DE
|
|
RET
|
|
|
|
; A=original character, bit7 set only for cooked BDOS output.
|
|
VTCHAR PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
PUSH IX
|
|
LD C,A
|
|
CALL VTPTR
|
|
LD A,C
|
|
AND 07FH
|
|
LD B,A
|
|
CALL VTISEN ; preserve IX and BC
|
|
CP 2
|
|
JP Z,H19CHAR ; separate H19 escape decoder
|
|
LD A,(IX+0)
|
|
OR A
|
|
JP Z,VTIDLE
|
|
DEC A
|
|
JP Z,VTESC
|
|
DEC A
|
|
JP Z,VTCSI
|
|
; Ignore exactly one charset-designator byte after ESC ( or ).
|
|
XOR A
|
|
LD (IX+0),A
|
|
JP VTRET
|
|
|
|
VTIDLE LD A,B
|
|
CP 01BH
|
|
JR NZ,VTLITERAL
|
|
LD (IX+0),1
|
|
JP VTRET
|
|
VTLITERAL CP 0AH
|
|
JR NZ,VTNOTLF
|
|
BIT 7,C
|
|
JR Z,VTRAWCHAR
|
|
LD A,(IX+7)
|
|
LD (IX+7),0
|
|
OR A
|
|
JP NZ,VTRET ; discard cooked LF after CR
|
|
JR VTRAWCHAR
|
|
VTNOTLF XOR A
|
|
LD (IX+7),A
|
|
LD A,B
|
|
CP 0DH
|
|
JR NZ,VTRAWCHAR
|
|
BIT 7,C
|
|
JR Z,VTRAWCHAR
|
|
LD (IX+7),1
|
|
CALL VTEMIT ; cooked CR -> CR + LF
|
|
LD A,0AH
|
|
JR VTRAWCHAR
|
|
VTRAWCHAR LD A,B
|
|
CP 0DH
|
|
JR NZ,VTRAWOTHER
|
|
BIT 7,C
|
|
JR Z,VTRAWOTHER
|
|
LD A,0AH ; already emitted cooked CR
|
|
JR VTRAWOTHER
|
|
VTRAWOTHER CALL VTEMIT
|
|
JP VTRET
|
|
|
|
VTESC XOR A
|
|
LD (IX+0),A
|
|
LD A,B
|
|
CP '['
|
|
JR Z,VTBEGIN
|
|
CP '('
|
|
JR Z,VTCHARSET
|
|
CP ')'
|
|
JR Z,VTCHARSET
|
|
CP '7'
|
|
JP Z,VTSAVE
|
|
CP '8'
|
|
JP Z,VTRESTORE
|
|
CP 'D'
|
|
JR NZ,VTESCE
|
|
LD A,0AH ; IND: line feed
|
|
JR VTESCEMIT
|
|
VTESCE CP 'E'
|
|
JR NZ,VTESCC
|
|
LD A,0DH
|
|
CALL VTEMIT
|
|
LD A,0AH
|
|
JR VTESCEMIT
|
|
VTESCC CP 'c'
|
|
JP NZ,VTRET
|
|
XOR A
|
|
LD (IX+7),A
|
|
CALL VTNORMA
|
|
CALL VTCLEARALL
|
|
JP VTRET
|
|
VTESCEMIT CALL VTEMIT
|
|
JP VTRET
|
|
VTCHARSET LD (IX+0),3 ; consume one charset designation
|
|
JP VTRET
|
|
VTBEGIN LD (IX+0),2
|
|
XOR A
|
|
LD (IX+1),A
|
|
LD (IX+2),A
|
|
LD (IX+3),A
|
|
LD (IX+6),A
|
|
JP VTRET
|
|
|
|
VTCSI LD A,B
|
|
CP '?'
|
|
JR NZ,VTNUM
|
|
LD (IX+6),1 ; DEC private CSI: ignore safely
|
|
JP VTRET
|
|
VTNUM CP '0'
|
|
JR C,VTSEP
|
|
CP ':'
|
|
JR NC,VTSEP
|
|
SUB '0'
|
|
LD D,A ; new digit
|
|
LD A,(IX+1)
|
|
CP 26
|
|
JR NC,VTMAX
|
|
LD E,A
|
|
ADD A,A
|
|
ADD A,A
|
|
ADD A,E
|
|
ADD A,A
|
|
ADD A,D
|
|
JR C,VTMAX
|
|
LD (IX+1),A
|
|
JP VTRET
|
|
VTMAX LD (IX+1),255
|
|
JP VTRET
|
|
VTSEP CP 03BH ; ASCII semicolon; quoted ';' confuses z80asm
|
|
JR NZ,VTENDCSI
|
|
LD A,(IX+3)
|
|
OR A
|
|
JP NZ,VTRET ; ignore extra arguments
|
|
LD A,(IX+1)
|
|
LD (IX+2),A
|
|
LD (IX+3),1
|
|
LD (IX+1),0
|
|
JP VTRET
|
|
VTENDCSI XOR A
|
|
LD (IX+0),A
|
|
LD A,(IX+6)
|
|
OR A
|
|
JP NZ,VTRET ; e.g. ESC[?25l
|
|
LD A,B
|
|
CP 'H'
|
|
JP Z,VTCUP
|
|
CP 'f'
|
|
JP Z,VTCUP
|
|
CP 'A'
|
|
JP Z,VTMOVEUP
|
|
CP 'B'
|
|
JP Z,VTMOVEDOWN
|
|
CP 'C'
|
|
JP Z,VTMOVERIGHT
|
|
CP 'D'
|
|
JP Z,VTMOVELEFT
|
|
CP 'J'
|
|
JP Z,VTERASESCREEN
|
|
CP 'K'
|
|
JP Z,VTERASELINE
|
|
CP 'm'
|
|
JP Z,VTSGR
|
|
CP 's'
|
|
JP Z,VTSAVE
|
|
CP 'u'
|
|
JP Z,VTRESTORE
|
|
JP VTRET
|
|
|
|
; Translate one-based ANSI CUP to zero-based Soroc coordinates.
|
|
VTCUP LD A,(IX+3)
|
|
OR A
|
|
JR Z,VTCUPONE
|
|
LD A,(IX+2) ; row argument
|
|
LD D,A
|
|
LD A,(IX+1) ; col argument
|
|
JR VTCUPC
|
|
VTCUPONE LD A,(IX+1) ; row, no column
|
|
LD D,A
|
|
LD A,1
|
|
VTCUPC OR A
|
|
JR NZ,VTCUPC1
|
|
INC A
|
|
VTCUPC1 CP 81
|
|
JR C,VTCUPC2
|
|
LD A,80
|
|
VTCUPC2 DEC A
|
|
LD E,A
|
|
LD A,D
|
|
OR A
|
|
JR NZ,VTCUPR1
|
|
INC A
|
|
VTCUPR1 CP 25
|
|
JR C,VTCUPR2
|
|
LD A,24
|
|
VTCUPR2 DEC A
|
|
LD D,A
|
|
CALL VTPOS
|
|
JP VTRET
|
|
|
|
; Read live 6502 VCOL/VROW, already virtualised by task switcher.
|
|
VTGETXY LD HL,(VSPTR)
|
|
LD E,(HL)
|
|
INC HL
|
|
LD D,(HL)
|
|
RET
|
|
; D=zero-based row (0..23), E=zero-based col (0..79).
|
|
VTPOS PUSH DE
|
|
LD A,01BH
|
|
CALL VTEMIT
|
|
LD A,'='
|
|
CALL VTEMIT
|
|
LD A,D
|
|
ADD A,20H
|
|
CALL VTEMIT
|
|
LD A,E
|
|
ADD A,20H
|
|
CALL VTEMIT
|
|
POP DE
|
|
RET
|
|
VTSTEP LD A,(IX+1)
|
|
OR A
|
|
RET NZ
|
|
INC A
|
|
RET
|
|
VTMOVEUP CALL VTGETXY
|
|
CALL VTSTEP
|
|
LD L,A
|
|
LD A,D
|
|
SUB L
|
|
JR NC,VTMUY
|
|
XOR A
|
|
VTMUY LD D,A
|
|
JR VTMOVEGO
|
|
VTMOVEDOWN CALL VTGETXY
|
|
CALL VTSTEP
|
|
ADD A,D
|
|
JR C,VTMDMAX
|
|
CP 24
|
|
JR C,VTMDY
|
|
VTMDMAX LD A,23
|
|
VTMDY LD D,A
|
|
JR VTMOVEGO
|
|
VTMOVERIGHT CALL VTGETXY
|
|
CALL VTSTEP
|
|
ADD A,E
|
|
JR C,VTMRMAX
|
|
CP 80
|
|
JR C,VTMRX
|
|
VTMRMAX LD A,79
|
|
VTMRX LD E,A
|
|
JR VTMOVEGO
|
|
VTMOVELEFT CALL VTGETXY
|
|
CALL VTSTEP
|
|
LD L,A
|
|
LD A,E
|
|
SUB L
|
|
JR NC,VTMLX
|
|
XOR A
|
|
VTMLX LD E,A
|
|
VTMOVEGO CALL VTPOS
|
|
JP VTRET
|
|
|
|
VTERASESCREEN LD A,(IX+1)
|
|
CP 2
|
|
JR Z,VTEALL
|
|
OR A
|
|
JP NZ,VTRET ; ED 1 deferred
|
|
LD A,'Y' ; Soroc erase to end of page
|
|
CALL VTESCREQ
|
|
JP VTRET
|
|
VTEALL CALL VTCLEARALL
|
|
JP VTRET
|
|
VTCLEARALL LD A,'*' ; Soroc clear and home
|
|
VTESCREQ PUSH AF
|
|
LD A,01BH
|
|
CALL VTEMIT
|
|
POP AF
|
|
JP VTEMIT
|
|
VTERASELINE LD A,(IX+1)
|
|
OR A
|
|
JR Z,VTEOL
|
|
CP 2
|
|
JP NZ,VTRET ; EL 1 deferred
|
|
CALL VTGETXY
|
|
PUSH DE
|
|
LD E,0
|
|
CALL VTPOS
|
|
CALL VTCLREOL
|
|
POP DE
|
|
CALL VTPOS
|
|
JP VTRET
|
|
VTEOL CALL VTCLREOL
|
|
JP VTRET
|
|
VTCLREOL LD A,'T'
|
|
JP VTESCREQ
|
|
|
|
VTSGR LD A,(IX+1)
|
|
CP 7
|
|
JR Z,VTINVERSE
|
|
OR A
|
|
JR Z,VTNORMCALL
|
|
CP 27
|
|
JP NZ,VTRET ; attributes not implemented
|
|
VTNORMCALL CALL VTNORMA
|
|
JP VTRET
|
|
VTINVERSE LD A,'('
|
|
CALL VTATTR
|
|
JP VTRET
|
|
VTNORMA LD A,')'
|
|
VTATTR JP VTESCREQ
|
|
VTSAVE CALL VTGETXY
|
|
LD (IX+4),D
|
|
LD (IX+5),E
|
|
JP VTRET
|
|
VTRESTORE LD D,(IX+4)
|
|
LD E,(IX+5)
|
|
CALL VTPOS
|
|
VTRET POP IX
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
RET
|
|
VTEMIT PUSH HL
|
|
LD HL,RAWOUT ; B80F 6502 raw console
|
|
CALL C_SENDA
|
|
POP HL
|
|
RET
|
|
|
|
|
|
; ---------------------------------------------------------------------------
|
|
; Heathkit H19 / H89 (also VT52) decoder. Called with B=7-bit character,
|
|
; C=original character including cooked bit, and IX=current terminal state.
|
|
; Share VTLITERAL's CR/LF handling and Soroc output primitives with VT100.
|
|
; ESC Y uses row+20h, column+20h, with BOTH parameter bytes consumed.
|
|
; ESC x/y/r require a single parameter; a new ESC in their place
|
|
; starts a new sequence (HITCH.COM emits ESC x ESC E).
|
|
; ---------------------------------------------------------------------------
|
|
H19CHAR LD A,(IX+0)
|
|
OR A
|
|
JP Z,H19IDLE
|
|
CP 1
|
|
JP Z,H19ESC
|
|
CP 4
|
|
JP Z,H19ROW
|
|
CP 5
|
|
JP Z,H19COL
|
|
; Consume an x/y/r configuration parameter, but a new
|
|
; ESC cancels an incomplete command rather than eating it.
|
|
XOR A
|
|
LD (IX+0),A
|
|
LD A,B
|
|
CP 01BH
|
|
JP Z,H19START
|
|
JP VTRET
|
|
H19IDLE LD A,B
|
|
CP 01BH
|
|
JP Z,H19START
|
|
JP VTLITERAL ; shared normal output/CRLF
|
|
H19START LD (IX+0),1
|
|
JP VTRET
|
|
|
|
H19ESC XOR A
|
|
LD (IX+0),A
|
|
LD (IX+1),A ; motion commands are single-step
|
|
LD A,B
|
|
CP 01BH
|
|
JP Z,H19START
|
|
CP 'Y'
|
|
JP Z,H19ADR
|
|
CP 'E'
|
|
JP Z,H19CLEAR
|
|
CP 'H'
|
|
JP Z,H19HOME
|
|
CP 'J'
|
|
JP Z,H19EOP
|
|
CP 'K'
|
|
JP Z,H19EOL
|
|
CP 'A'
|
|
JP Z,VTMOVEUP
|
|
CP 'B'
|
|
JP Z,VTMOVEDOWN
|
|
CP 'C'
|
|
JP Z,VTMOVERIGHT
|
|
CP 'D'
|
|
JP Z,VTMOVELEFT
|
|
CP 'j'
|
|
JP Z,VTSAVE
|
|
CP 'k'
|
|
JP Z,VTRESTORE
|
|
CP 'p'
|
|
JP Z,VTINVERSE
|
|
CP 'q'
|
|
JP Z,H19NORM
|
|
CP 'l'
|
|
JP Z,H19FLN
|
|
CP 'z'
|
|
JP Z,H19RESET
|
|
CP 'x' ; H19 operating mode set
|
|
JP Z,H19PARAM
|
|
CP 'y' ; H19 operating mode reset
|
|
JP Z,H19PARAM
|
|
CP 'r' ; H19 baud rate parameter
|
|
JP Z,H19PARAM
|
|
; ESC F/G graphics, =/> keypad, [ hold etc: safely ignore.
|
|
; Insert/delete line and character are not yet implemented.
|
|
JP VTRET
|
|
H19ADR LD (IX+0),4
|
|
JP VTRET
|
|
H19ROW LD A,B
|
|
SUB 020H ; H19 cursor coordinates are +$20
|
|
JR NC,H19ROWOK
|
|
XOR A
|
|
H19ROWOK CP 24
|
|
JR C,H19ROWIN
|
|
LD A,23
|
|
H19ROWIN LD (IX+2),A
|
|
LD (IX+0),5
|
|
JP VTRET
|
|
H19COL XOR A
|
|
LD (IX+0),A
|
|
LD A,B
|
|
SUB 020H
|
|
JR NC,H19COLOK
|
|
XOR A
|
|
H19COLOK CP 80
|
|
JR C,H19COLIN
|
|
LD A,79
|
|
H19COLIN LD E,A
|
|
LD D,(IX+2)
|
|
CALL VTPOS
|
|
JP VTRET
|
|
H19PARAM LD (IX+0),6 ; discard one mode byte
|
|
JP VTRET
|
|
H19CLEAR CALL VTCLEARALL
|
|
JP VTRET
|
|
H19HOME LD DE,0
|
|
CALL VTPOS
|
|
JP VTRET
|
|
H19EOP LD A,'Y' ; Soroc erase to end of page
|
|
CALL VTESCREQ
|
|
JP VTRET
|
|
H19EOL CALL VTCLREOL
|
|
JP VTRET
|
|
H19NORM CALL VTNORMA
|
|
JP VTRET
|
|
H19RESET CALL VTNORMA
|
|
CALL VTCLEARALL
|
|
JP VTRET
|
|
H19FLN CALL VTGETXY
|
|
PUSH DE
|
|
LD E,0
|
|
CALL VTPOS
|
|
CALL VTCLREOL
|
|
POP DE
|
|
CALL VTPOS
|
|
JP VTRET
|
|
|
|
VTSTATES DEFS 128
|
|
ORG 07000H
|
|
|
|
AXBDOS CALL XSPRE ; skip physical keyboard wait if XSUB
|
|
RET C
|
|
LD A,C
|
|
LD (AXBADFN),A ; diagnostic: remember original BDOS C
|
|
CP 0F0H ; private TERM: 0 VT100 / 1 Soroc / 2 H19
|
|
JP Z,AXTMODE
|
|
OR A
|
|
JP Z,AUXBDOS0
|
|
CP 1
|
|
JP Z,XSCIN
|
|
CP 2
|
|
JP Z,AUXBDOS2
|
|
CP 6
|
|
JP Z,XSRAW
|
|
CP 9
|
|
JP Z,AUXBDOS9
|
|
CP 3
|
|
JP Z,IOREADER
|
|
CP 4
|
|
JP Z,IOPUNCH
|
|
CP 5
|
|
JP Z,IOLIST
|
|
CP 0F4H ; AUX LC BIOS LIST
|
|
JP Z,IOLIST
|
|
CP 0F5H ; AUX LC BIOS PUNCH
|
|
JP Z,IOPUNCH
|
|
CP 0F6H ; AUX LC BIOS READER
|
|
JP Z,IOREADER
|
|
CP 11
|
|
JP Z,XSSTAT
|
|
CP 12
|
|
JP Z,ABDVER
|
|
CP 02DH ; CP/M 3: SET BDOS ERROR MODE (BBC BASIC)
|
|
JP Z,AXERRMODE ; accept request; ignore mode setting
|
|
CP 10
|
|
JP Z,AXLINE
|
|
CP 13
|
|
JP Z,AXRESET
|
|
CP 24
|
|
JP Z,AXLOGV
|
|
CP 27
|
|
JP Z,AXAVEC
|
|
CP 28
|
|
JP Z,AXSETRO
|
|
CP 29
|
|
JP Z,AXROV
|
|
CP 31
|
|
JP Z,AXDPB
|
|
CP 37
|
|
JP Z,AXDRST
|
|
CP 30
|
|
JP Z,AXFSEL ; BDOS SET FILE ATTRIBUTES needs AUX FCB
|
|
CP 0F1H
|
|
JP Z,XSBSTAT
|
|
CP 0F2H
|
|
JP Z,XSBIIN
|
|
CP 0F3H
|
|
JP Z,AXBIOUT
|
|
CP 26
|
|
JP Z,AXSDMA
|
|
CP 17
|
|
JP Z,AXSFIRST
|
|
CP 18
|
|
JP Z,AXSNEXT
|
|
; Compact AUX BDOS FCB dispatch: contiguous function ranges.
|
|
; CP/JR/JP leave A untouched; AXFSEL expects original BDOS A value.
|
|
CP 15
|
|
JR C,AXF17
|
|
CP 17
|
|
JP C,AXFSEL ; 15,16
|
|
AXF17 CP 19
|
|
JR C,AXF24
|
|
CP 24
|
|
JP C,AXFSEL ; 19..23
|
|
AXF24 CP 33
|
|
JR C,AXF37
|
|
CP 37
|
|
JP C,AXFSEL ; 33..36
|
|
AXF37 CP 40
|
|
JP Z,AXFSEL
|
|
CP 14
|
|
JP Z,AXDSET
|
|
CP 25
|
|
JP Z,AXDGET
|
|
CP 32
|
|
JP Z,AXUSER
|
|
CP 7
|
|
JP Z,AXIOGET
|
|
CP 8
|
|
JP Z,AXIOSET
|
|
; CP/M 3 BDOS $2D: Set error mode (E=FE in BBC BASIC).
|
|
; CP/M 2.2-style Zapple-II has no equivalent error-mode switch.
|
|
; The CP/M 3 call returns no value; return zero for compatibility.
|
|
AXERRMODE XOR A
|
|
LD L,A
|
|
RET
|
|
; Unsupported calls must not pass AUX pointers to the MAIN BDOS.
|
|
AXBAD LD A,2
|
|
LD (LCQUIT),A
|
|
LD A,0FFH
|
|
LD L,A
|
|
RET
|
|
; TERM.COM must not switch to the 6502 directly from AUX RAM:
|
|
; the 6502 console lives at MAIN $B800. This dispatcher is called by
|
|
; LCGATE with MAIN RAM selected, and the original LC/stack are restored
|
|
; automatically on return. Private BDOS $F0: 0=VT100, 1=Soroc,
|
|
; 2=H19/VT52. The 6502 video renderer must receive Soroc mode (1)
|
|
; for H19: it cannot recognize the Z80-only personality value 2.
|
|
AXTMODE JP SCHTMCHK
|
|
AXTMRUN CALL VTSETMODE ; save active tab mode and reset CSI
|
|
CP 2 ; H19 translator emits Soroc
|
|
JR NZ,AXTM6502
|
|
LD A,1
|
|
AXTM6502 LD (AREG),A
|
|
LD HL,0B80CH ; 6502 VSETMODE public entry
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A ; invoke 6502 with MAIN selected
|
|
; VSETMODE updates the Microsoft BASIC table in MAIN $0396.
|
|
; BASIC now executes in AUX and reads AUX $0396 instead.
|
|
; Mirror its nine bytes (Z80 $F396-$F39E) to AUX too.
|
|
LD HL,0F396H
|
|
LD DE,0F396H
|
|
LD BC,9
|
|
CALL AXM2A
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
AXTMERR LD A,0FFH
|
|
LD L,A
|
|
RET
|
|
|
|
; Require the full 36-byte FCB to lie below BDOS at $DE00.
|
|
AXCKFCB LD A,D
|
|
CP 0DDH
|
|
JR C,AXCKOK
|
|
JR NZ,AXCKBAD
|
|
LD A,E
|
|
CP 0DDH
|
|
JR NC,AXCKBAD
|
|
AXCKOK XOR A
|
|
RET
|
|
AXCKBAD LD A,1
|
|
RET
|
|
; An entire 128-byte record must fit below $DE00.
|
|
AXCKDMA LD HL,(AXDMAP)
|
|
LD A,H
|
|
CP 0DDH
|
|
JR C,AXCKOK
|
|
JR NZ,AXCKBAD
|
|
LD A,L
|
|
CP 081H
|
|
JR C,AXCKOK
|
|
JR AXCKBAD
|
|
AXSDMA LD (AXDMAP),DE
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
AXDSET CALL DRV_SET
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
AXDGET CALL DRV_GET
|
|
LD L,A
|
|
RET
|
|
AXUSER CALL F_USERNUM
|
|
LD L,A
|
|
RET
|
|
; Cooked CONIN via existing BIOS raw read, with echo and ^C exit.
|
|
; Avoid C_READ's nonreturning main-bank warm boot in AUX mode.
|
|
AXCIN LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1
|
|
JP NZ,IOCCOOK ; redirected CON input
|
|
JP TSKCIN ; CRT: scheduler/Open Apple aware
|
|
AXIOGET JP GET_IOB
|
|
AXIOSET JP SET_IOB
|
|
AXAUXRD LD A,1AH ; no input device: CP/M EOF
|
|
LD L,A
|
|
RET
|
|
AXAUXWR XOR A ; no punch/list device attached
|
|
LD L,A
|
|
RET
|
|
|
|
; Direct BIOS console calls use private C codes $F1..$F3 from the LC
|
|
; jump-table shims. Caller registers are preserved by those shims.
|
|
AXBICON LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1
|
|
JP NZ,IOSTAT
|
|
JP TSKSTAT ; CRT: catch Open Apple on BIOS CONST
|
|
AXBIREAD LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1
|
|
JP NZ,IOCONIN
|
|
JP TSKBIRD ; CRT: handler moved under $0800
|
|
AXBIOUT CALL C_RAWPUT
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
|
|
; CP/M disk control calls not involving AUX pointers.
|
|
AXLOGV JP DRV_LOGVEC
|
|
AXROV JP DRV_ROVEC
|
|
AXSETRO CALL DRV_SETRO
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
AXDRST CALL DRV_RESET
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
AXRESET LD A,(LCB1RO)
|
|
CALL DRV_ALLRST
|
|
LD (AXSTAT),A
|
|
LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
LD HL,0080H
|
|
LD (AXDMAP),HL
|
|
LD A,(AXSTAT)
|
|
LD L,A
|
|
RET
|
|
|
|
; Return pointers to storage outside the contiguous CP/M TPA.
|
|
; $F300 maps to AUX Apple $0300, $F800 to AUX Apple $0800-$0FFF.
|
|
; The caller may freely read these while LC bank2 remains mapped.
|
|
AXDPB LD A,(LCB1RO)
|
|
CALL DRV_DPB
|
|
LD DE,0F300H
|
|
LD BC,15
|
|
CALL AXM2A
|
|
LD HL,0F300H
|
|
XOR A
|
|
RET
|
|
AXAVEC LD A,(LCB1RO)
|
|
CALL DRV_AVEC
|
|
LD DE,0F800H
|
|
LD BC,0800H
|
|
CALL AXM2A
|
|
LD HL,0F800H
|
|
XOR A
|
|
RET
|
|
|
|
; CP/M BDOS 10: buffered line input into an AUX caller's buffer.
|
|
; Check the ACTUAL buffer capacity, not a blanket 260-byte region.
|
|
; The AUX TPA ends at DDFFH; BDOS stub starts at DE00H.
|
|
; For capacity N we may write N data bytes plus one CR at DE+2+N.
|
|
; 260-byte MAIN staging handles all legal one-byte capacities (0..255).
|
|
AXLINE LD (AXLIP),DE
|
|
LD A,D
|
|
CP 0DEH
|
|
JP NC,AXBAD ; buffer header must be below DE00H
|
|
AXLVALID EX DE,HL
|
|
LD DE,AXLBUF
|
|
LD BC,1
|
|
CALL LCA2M ; read caller's capacity byte from AUX
|
|
LD A,(AXLBUF)
|
|
LD C,A
|
|
LD B,0
|
|
LD HL,(AXLIP)
|
|
INC HL
|
|
INC HL
|
|
ADD HL,BC ; address of final possible CR byte
|
|
LD A,H
|
|
CP 0DEH
|
|
JP NC,AXBAD ; disallow touching DE00H+ BDOS page
|
|
; Only consume submitted input after XSUB was explicitly
|
|
; enabled from a script. XSREADY handles prior status polls.
|
|
CALL XSREADY
|
|
OR A
|
|
JP NZ,AXLSUB
|
|
LD A,(AXLBUF)
|
|
LD C,A ; restore capacity after disk calls
|
|
LD B,C ; capacity for line editor
|
|
LD C,0 ; bytes entered
|
|
LD HL,AXLBUF+2
|
|
AXLNEXT PUSH BC
|
|
PUSH HL
|
|
CALL BIOSCONIN
|
|
POP HL
|
|
POP BC
|
|
AND 07FH
|
|
; For BDOS 10, park only at the beginning of a line.
|
|
; Once characters have been entered, don't discard the line.
|
|
PUSH AF
|
|
LD A,C
|
|
OR A
|
|
JR NZ,AXLNOKEY
|
|
POP AF
|
|
CALL TSKKEY
|
|
JP C,AXLSKEY
|
|
JR AXLKEY
|
|
AXLNOKEY POP AF
|
|
AXLKEY CP 13
|
|
JR Z,AXLDONE
|
|
CP 3
|
|
JR Z,AXLCAN
|
|
CP 8
|
|
JR Z,AXLBS
|
|
CP 127
|
|
JR Z,AXLBS
|
|
CP 32
|
|
JR C,AXLNEXT
|
|
PUSH AF
|
|
LD A,B
|
|
CP C
|
|
JR Z,AXLFULL
|
|
POP AF
|
|
LD (HL),A
|
|
INC HL
|
|
INC C
|
|
LD E,A
|
|
PUSH BC
|
|
PUSH HL
|
|
CALL C_WRITE
|
|
POP HL
|
|
POP BC
|
|
JR AXLNEXT
|
|
AXLFULL POP AF
|
|
JR AXLNEXT
|
|
AXLBS LD A,C
|
|
OR A
|
|
JR Z,AXLNEXT
|
|
DEC C
|
|
DEC HL
|
|
PUSH BC
|
|
PUSH HL
|
|
LD E,8
|
|
CALL C_WRITE
|
|
LD E,' '
|
|
CALL C_WRITE
|
|
LD E,8
|
|
CALL C_WRITE
|
|
POP HL
|
|
POP BC
|
|
JR AXLNEXT
|
|
AXLCAN LD A,1
|
|
LD (LCQUIT),A
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
AXLDONE LD (HL),13
|
|
LD A,C
|
|
LD (AXLBUF+1),A
|
|
PUSH BC
|
|
LD E,13
|
|
CALL C_WRITE
|
|
POP BC
|
|
LD B,0
|
|
INC BC
|
|
INC BC
|
|
INC BC
|
|
LD HL,AXLBUF
|
|
LD DE,(AXLIP)
|
|
CALL AXM2A
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
|
|
; Search First: FCB (36 bytes) copied to a persistent MAIN search FCB;
|
|
; F_SNEXT retains TEMPWORD -> AXSRCH and CDBPTR between calls.
|
|
AXSFIRST CALL AXCKFCB
|
|
JP NZ,AXBAD
|
|
CALL AXCKDMA
|
|
JP NZ,AXBAD
|
|
EX DE,HL
|
|
LD DE,AXSRCH
|
|
LD BC,36
|
|
CALL LCA2M
|
|
LD DE,AXSRCH
|
|
LD HL,AXDATA
|
|
LD (DMAADDR),HL
|
|
LD A,(LCB1RO)
|
|
CALL F_SFIRST
|
|
JP AXSRDONE
|
|
AXSNEXT CALL AXCKDMA
|
|
JP NZ,AXBAD
|
|
LD HL,AXDATA
|
|
LD (DMAADDR),HL
|
|
LD A,(LCB1RO)
|
|
CALL F_SNEXT
|
|
AXSRDONE LD (AXSTAT),A
|
|
LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
LD A,(AXSTAT)
|
|
OR A
|
|
JP NZ,AXRETURN
|
|
LD HL,AXDATA
|
|
LD DE,(AXDMAP)
|
|
LD BC,128
|
|
CALL AXM2A
|
|
AXRETURN LD A,(AXSTAT)
|
|
LD L,A
|
|
LD H,0 ; CP/M byte result: clear high byte
|
|
RET
|
|
|
|
; Copy one 36-byte auxiliary FCB into MAIN staging; preserve the caller's
|
|
; AUX pointer to copy back RC/EX/CR/random-record updates afterwards.
|
|
AXFSEL PUSH AF
|
|
CALL AXCKFCB
|
|
POP BC ; B = original function number
|
|
JP NZ,AXBAD
|
|
LD A,B
|
|
LD (AXFN),A
|
|
LD (AXFCBP),DE
|
|
EX DE,HL
|
|
LD DE,AXFCB
|
|
LD BC,36
|
|
CALL LCA2M
|
|
; Writing a record: copy 128 bytes from auxiliary DMA to MAIN DMA first.
|
|
LD A,(AXFN)
|
|
CP 21
|
|
JP Z,AXWPRE
|
|
CP 34
|
|
JP Z,AXWPRE
|
|
CP 40
|
|
JP Z,AXWPRE
|
|
JR AXDISP
|
|
AXWPRE CALL AXCKDMA
|
|
JP NZ,AXBAD
|
|
LD HL,(AXDMAP)
|
|
LD DE,AXDATA
|
|
LD BC,128
|
|
CALL LCA2M
|
|
AXDISP LD DE,AXFCB
|
|
LD HL,AXDATA
|
|
LD (DMAADDR),HL
|
|
; Wildcard F_DELETE uses FILEBUF internally, not DMAADDR: redirect it.
|
|
LD A,(AXFN)
|
|
CP 19
|
|
JP NZ,AXNOTDEL
|
|
LD HL,FILEBUF
|
|
LD (DMAADDR),HL
|
|
AXNOTDEL LD A,(LCB1RO)
|
|
LD A,(AXFN)
|
|
CP 15
|
|
JP Z,AXOP
|
|
CP 16
|
|
JP Z,AXCL
|
|
CP 19
|
|
JP Z,AXDEL
|
|
CP 20
|
|
JP Z,AXRD
|
|
CP 21
|
|
JP Z,AXWR
|
|
CP 22
|
|
JP Z,AXMAKE
|
|
CP 23
|
|
JP Z,AXREN
|
|
CP 30
|
|
JP Z,AXATTR
|
|
CP 33
|
|
JP Z,AXRRD
|
|
CP 34
|
|
JP Z,AXRWR
|
|
CP 40
|
|
JP Z,AXRWR
|
|
CP 35
|
|
JP Z,AXSIZE
|
|
CP 36
|
|
JP Z,AXRREC
|
|
LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
JP AXBAD
|
|
AXOP CALL F_OPEN
|
|
JR AXFEND
|
|
AXCL CALL F_CLOSE
|
|
JR AXFEND
|
|
AXDEL CALL F_DELETE
|
|
JR AXFEND
|
|
AXRD CALL F_READ
|
|
JR AXFEND
|
|
AXWR CALL F_WRITE
|
|
JR AXFEND
|
|
AXMAKE CALL F_MAKE
|
|
JR AXFEND
|
|
AXREN CALL F_RENAME
|
|
JR AXFEND
|
|
AXATTR CALL F_ATTRIB
|
|
JR AXFEND
|
|
AXRRD CALL F_READRAND
|
|
JR AXFEND
|
|
AXRWR CALL F_WRITERAND
|
|
JR AXFEND
|
|
AXSIZE CALL F_SIZE
|
|
JR AXFEND
|
|
AXRREC CALL F_RANDREC
|
|
; Main filesystem has returned: reselect the gateway LC bank BEFORE
|
|
; returning to LIGATE's caller in the $B000 LC page.
|
|
AXFEND LD (AXSTAT),A
|
|
LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
; Copy caller FCB back, including updated extent/record/file-open marker.
|
|
LD HL,AXFCB
|
|
LD DE,(AXFCBP)
|
|
; Sequential BDOS calls use only the 33-byte FCB.
|
|
; Copying 36 bytes would overwrite the next FCB in F80,
|
|
; which stores its source, listing and object FCBs adjacent.
|
|
; Random-record functions (33,34,35,36,40) need 36 bytes.
|
|
LD BC,33
|
|
LD A,(AXFN)
|
|
CP 33
|
|
JR C,AXFCPY
|
|
LD BC,36
|
|
AXFCPY CALL AXM2A
|
|
; A successful read transfers the 128-byte MAIN DMA result to AUX.
|
|
LD A,(AXSTAT)
|
|
OR A
|
|
JP NZ,AXFRET
|
|
LD A,(AXFN)
|
|
CP 20
|
|
JP Z,AXPOSTR
|
|
CP 33
|
|
JP NZ,AXFRET
|
|
AXPOSTR CALL AXCKDMA
|
|
JP NZ,AXBAD
|
|
LD HL,AXDATA
|
|
LD DE,(AXDMAP)
|
|
LD BC,128
|
|
CALL AXM2A
|
|
AXFRET LD A,(AXSTAT)
|
|
LD L,A
|
|
LD H,0 ; CP/M byte result: clear high byte
|
|
RET
|
|
|
|
; MAIN to AUX copy. With RAMRD=MAIN the code remains executable while
|
|
; RAMWRT=AUX, but NO CALLs/PUSHes may occur until RAMWRT is reset.
|
|
AXM2A LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
XOR A
|
|
LD (ZPAUX),A ; AUX LC bank2 for high addresses
|
|
LD (WRAUX),A
|
|
LDIR
|
|
LD (WRMAIN),A
|
|
LD (ZPMAIN),A
|
|
LD A,(LCB1RO)
|
|
XOR A
|
|
RET
|
|
|
|
; ================================================================
|
|
ORG 07590H
|
|
TCLIST DEFM 'LIST '
|
|
DEFB 0
|
|
TCSUSP DEFM 'SUSPEND '
|
|
DEFB 0
|
|
TCRESUM DEFM 'RESUME '
|
|
DEFB 0
|
|
TCEXCL DEFM 'EXCLUSIVE '
|
|
DEFB 0
|
|
TCKILL DEFM 'KILL '
|
|
DEFB 0
|
|
TCHELP DEFM 'HELP '
|
|
DEFB 0
|
|
TL_HOLD DEFM ' [Suspended]$'
|
|
TB_HELP LD DE,TB_HELPMSG
|
|
JP C_WRITESTR
|
|
|
|
|
|
; Small command summary; keep clear of the $7600 command table.
|
|
TB_HELPMSG DEFM 'TAB [LIST], TAB n [SUSP/RES/EXCL/KILL]'
|
|
DEFB 13,'$'
|
|
|
|
ORG 07600H
|
|
; Builtin dispatch table relocated to MAIN $5800.
|
|
|
|
; Native CCP terminal selector. This code runs with MAIN RAM visible,
|
|
; unlike the old AUX .COM, so it calls the 6502 VSETMODE vector directly.
|
|
; The next AUXRUN already mirrors MAIN $0396-$039E to AUX for MBASIC.
|
|
CCPTERM LD HL,FILEBUF+2 ; first argument, after leading space
|
|
LD DE,TMVT100
|
|
CALL TMCHK
|
|
JR Z,TMVT
|
|
LD HL,FILEBUF+2
|
|
LD DE,TMSOROC
|
|
CALL TMCHK
|
|
JR Z,TMSR
|
|
LD HL,FILEBUF+2
|
|
LD DE,TMH19
|
|
CALL TMCHK3
|
|
JR Z,TMHT
|
|
LD HL,FILEBUF+2
|
|
LD DE,TMH89
|
|
CALL TMCHK3
|
|
JR Z,TMHT
|
|
LD HL,FILEBUF+2
|
|
LD DE,TMVT52
|
|
CALL TMCHK4
|
|
JP NZ,CCPBAD ; require complete terminal name
|
|
TMHT LD A,2 ; Heath H19/H89/VT52
|
|
JR TMSET
|
|
TMSR LD A,1 ; Soroc
|
|
JR TMSET
|
|
TMVT XOR A ; VT100
|
|
TMSET CALL VTSETMODE ; update tab mode immediately
|
|
CP 2 ; 6502 renderer requires Soroc
|
|
JR NZ,TM6502
|
|
LD A,1
|
|
TM6502 LD (AREG),A
|
|
LD HL,0B80CH ; 6502 VSETMODE, already in MAIN
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
RET
|
|
; Check five letters and the delimiting space, against a literal.
|
|
; The CCP parser guarantees a trailing space after the argument.
|
|
TMCHK LD B,5
|
|
JR TMCHL
|
|
TMCHK3 LD B,3
|
|
JR TMCHL
|
|
TMCHK4 LD B,4
|
|
TMCHL LD A,(DE)
|
|
CP (HL)
|
|
RET NZ
|
|
INC DE
|
|
INC HL
|
|
DJNZ TMCHL
|
|
LD A,(HL)
|
|
CP ' '
|
|
RET
|
|
TMVT100 DEFM 'VT100'
|
|
TMSOROC DEFM 'SOROC'
|
|
TMH19 DEFM 'H19'
|
|
TMH89 DEFM 'H89'
|
|
TMVT52 DEFM 'VT52'
|
|
|
|
; BDOS 30: Set CP/M file R/O attribute in ProDOS access byte.
|
|
; STAT enumerates wildcards and passes each resolved FCB individually.
|
|
; Leave FCB and DMA unchanged, preserve file type and modification date.
|
|
F_ATTRIB CALL DCHKRO
|
|
JP NZ,RODENY
|
|
PUSH DE
|
|
POP IX
|
|
LD A,(IX+09H) ; first extension byte's high bit
|
|
AND 080H
|
|
LD (ATTR_RO),A
|
|
LD HL,1
|
|
ADD HL,DE
|
|
LD B,11
|
|
FA_VALID LD A,(HL)
|
|
AND 07FH
|
|
CP '?'
|
|
JR Z,FA_ERROR
|
|
INC HL
|
|
DJNZ FA_VALID
|
|
LD IX,PATHBUF
|
|
CALL FCB2PATH
|
|
CALL CACHECLS ; Apply attributes with file physically closed
|
|
OR A
|
|
JP NZ,FA_ERROR
|
|
LD HL,GFIMLI
|
|
CALL PRODOS ; GET_FILE_INFO (param count 10)
|
|
OR A
|
|
JR NZ,FA_ERROR
|
|
LD A,(ATTR_RO)
|
|
OR A
|
|
LD A,(GFIMLIAC)
|
|
JR Z,FA_RW
|
|
AND 03DH ; disable destroy/rename/write ($C2)
|
|
JR FA_CHG
|
|
FA_RW OR 0C2H ; enable destroy/rename/write
|
|
FA_CHG LD (GFIMLIAC),A
|
|
LD A,7
|
|
LD (GFIMLIPL),A
|
|
LD HL,SFIMLI
|
|
CALL PRODOS ; SET_FILE_INFO reuses parameter block
|
|
PUSH AF
|
|
LD A,10
|
|
LD (GFIMLIPL),A ; restore for next GET
|
|
POP AF
|
|
CP 04EH
|
|
JP Z,ROFILE
|
|
OR A
|
|
JR NZ,FA_ERROR
|
|
LD L,A
|
|
RET
|
|
FA_ERROR LD A,0FFH
|
|
LD L,A
|
|
RET
|
|
ATTR_RO DEFB 0
|
|
|
|
; CP/M 2.2 distinguishes file R/O from software drive R/O.
|
|
; Errors still return FF to the caller; they do NOT force a warm boot.
|
|
ROFILE LD DE,ROFTEXT
|
|
JR ROPRINT
|
|
RODISK LD DE,RODTEXT
|
|
ROPRINT PUSH DE
|
|
LD A,1
|
|
LD (ROERROR),A
|
|
LD DE,ROPTEXT
|
|
CALL C_WRITESTR
|
|
LD A,(RODRIVE)
|
|
ADD A,'A'
|
|
LD E,A
|
|
CALL C_WRITE
|
|
POP DE
|
|
CALL C_WRITESTR
|
|
LD E,13
|
|
CALL C_WRITE
|
|
LD A,(AXMODE) ; .COM running in AUX?
|
|
OR A
|
|
JR Z,ROPRET
|
|
LD A,1
|
|
LD (AXQUIT),A ; Abort transient through normal AUX gateway
|
|
ROPRET LD A,0FFH
|
|
LD L,A
|
|
RET
|
|
RODRIVE DEFB 0
|
|
ROERROR DEFB 0
|
|
ROPTEXT DEFM 'BDOS Err on '
|
|
DEFB '$'
|
|
ROFTEXT DEFM ': File R/O'
|
|
DEFB '$'
|
|
RODTEXT DEFM ': R/O'
|
|
DEFB '$'
|
|
|
|
; CCP command argument/file validators relocated from resident code.
|
|
; The CCP parser normalizes arguments as " arg1 arg2 ", always
|
|
; separating words by ONE space and retaining the leading/trailing space.
|
|
; Number of words = number of spaces - 1 (or 0 if tail is empty).
|
|
CCPARGC LD HL,FILEBUF
|
|
LD B,(HL)
|
|
LD C,0
|
|
INC HL
|
|
CALOOP LD A,B
|
|
OR A
|
|
JR Z,CADONE
|
|
LD A,(HL)
|
|
CP ' '
|
|
JR NZ,CANEXT
|
|
INC C
|
|
CANEXT INC HL
|
|
DJNZ CALOOP
|
|
CADONE LD A,C
|
|
OR A
|
|
RET Z
|
|
DEC A
|
|
RET
|
|
|
|
CCPBAD LD DE,BFMSG
|
|
CALL C_WRITESTR
|
|
LD A,1
|
|
OR A
|
|
RET
|
|
|
|
; Disallow ambiguous file specifications in TYPE, REN and SAVE.
|
|
; Returns A=0 for unambiguous name, nonzero for '?' anywhere.
|
|
ISWILD LD B,11
|
|
IWLOOP LD A,(HL)
|
|
CP '?'
|
|
RET Z
|
|
INC HL
|
|
DJNZ IWLOOP
|
|
XOR A
|
|
RET
|
|
CCPNOWILD CALL ISWILD
|
|
OR A
|
|
RET Z
|
|
JP CCPBAD
|
|
|
|
BITYPE LD HL,FCB1NAM
|
|
CALL CCPNOWILD
|
|
RET NZ
|
|
JP TYPEFILE
|
|
BISAVE LD HL,FCB2NAM
|
|
CALL CCPNOWILD
|
|
RET NZ
|
|
JP SAVEFILE
|
|
|
|
|
|
|
|
|
|
; Permanent scratch: main TPA is unused by auxiliary transients.
|
|
; Cached Apple II text row addresses for the software cursor.
|
|
; Apple 80-column row interleave. Z80 $F400 = Apple text $0400.
|
|
CUROWTAB DEFW 0F400H,0F480H,0F500H,0F580H,0F600H,0F680H,0F700H,0F780H
|
|
DEFW 0F428H,0F4A8H,0F528H,0F5A8H,0F628H,0F6A8H,0F728H,0F7A8H
|
|
DEFW 0F450H,0F4D0H,0F550H,0F5D0H,0F650H,0F6D0H,0F750H,0F7D0H
|
|
|
|
|
|
ORG 07800H
|
|
AXDMAP DEFW 0080H
|
|
AXFCBP DEFW 0
|
|
AXFN DEFB 0
|
|
AXSTAT DEFB 0
|
|
AXFCB DEFS 36
|
|
AXSRCH DEFS 36
|
|
AXDATA DEFS 128
|
|
AXLIP DEFW 0
|
|
AXLBUF DEFS 260
|
|
AXBADFN DEFB 0 ; last AUX BDOS function for diagnostics
|
|
BDOSFN DEFB 0 ; last MAIN BDOS function for diagnostics
|
|
|
|
; MAIN-to-AUX-LC resident transition helper, entirely in ordinary MAIN RAM.
|
|
; ALTZP can switch without changing the instruction fetch at $7Axx.
|
|
ORG 07A00H
|
|
AXSTART LD (AXMSP),SP ; called with MAIN RAM and main LC
|
|
XOR A
|
|
LD (AXQUIT),A
|
|
INC A
|
|
LD (AXMODE),A
|
|
LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
XOR A
|
|
LD (ZPAUX),A
|
|
JP LCREN ; enter the AUX LC DF00h gateway
|
|
; Resume a parked BDOS return. Called from TABRESUM on the MAIN stack.
|
|
; The suspended stack frame in its AUX bank is still there.
|
|
AXCONT LD (AXMSP),SP
|
|
XOR A
|
|
LD (AXQUIT),A
|
|
INC A
|
|
LD (AXMODE),A
|
|
JP AXRETRY
|
|
AXGATE LD (AXASP),SP ; user's stack may itself be in AUX LC
|
|
LD SP,(AXMSP) ; restore main resident stack
|
|
XOR A
|
|
LD (ZPMAIN),A ; restore ProDOS's ZP and LC
|
|
LD A,(LCB1RO)
|
|
CALL AXIOSBD ; sync AUX 0003h before dispatch
|
|
LD (AXRES),A
|
|
LD (AXRHL),HL
|
|
CALL SCHTICK ; never schedules INSIDE a syscall
|
|
LD A,(AXQUIT)
|
|
CP 3
|
|
CALL Z,TASKSAVE ; save before the CCP/other session runs
|
|
LD A,(AXQUIT)
|
|
OR A
|
|
JR NZ,AXFIN
|
|
AXGO LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
XOR A
|
|
LD (ZPAUX),A ; executable LC image now AUX
|
|
LD SP,(AXASP)
|
|
LD HL,(AXRHL)
|
|
LD A,(AXRES)
|
|
JP LCRES ; LC shim restores RAMRD/RAMWRT AUX
|
|
AXFIN JP AXIOSFX ; plain RET/JP 0000h must sync too
|
|
; AXMODE is only 1 during AUX transient. SAVE executes outside it,
|
|
; with its temporary LC shim installed into MAIN LC instead of AUX LC.
|
|
LCPEEK LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
LD A,(AXMODE)
|
|
OR A
|
|
JR Z,LPLOCAL
|
|
LD (ZPAUX),A
|
|
LPLOCAL CALL LCPEE0
|
|
PUSH AF
|
|
XOR A
|
|
LD (ZPMAIN),A
|
|
LD A,(LCB1RO)
|
|
POP AF
|
|
RET
|
|
LCA2M LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
LD A,(AXMODE)
|
|
OR A
|
|
JR Z,LMLOCAL
|
|
LD (ZPAUX),A
|
|
LMLOCAL CALL LCA2MR
|
|
XOR A
|
|
LD (ZPMAIN),A
|
|
LD A,(LCB1RO)
|
|
RET
|
|
AXMSP DEFW 0
|
|
AXASP DEFW 0
|
|
AXRES DEFB 0
|
|
AXRHL DEFW 0
|
|
AXQUIT DEFB 0
|
|
AXMODE DEFB 0
|
|
|
|
; Two CCP commands live in otherwise unused MAIN $7B00-$7BFF.
|
|
; CLS invokes the new 6502 $B812 entry, so it works in either mode.
|
|
; Software cursor restore; installed in the free tail of $7A00 region.
|
|
; Hide the physical cursor before key delivery, TAB switching or
|
|
; drawing. The cached address works even after terminal state changes.
|
|
CUOFF LD A,(CUACTIVE)
|
|
OR A
|
|
RET Z
|
|
PUSH AF
|
|
PUSH HL
|
|
XOR A
|
|
LD (CUACTIVE),A
|
|
CALL CUMAP
|
|
LD A,(CUUNDER)
|
|
LD (HL),A
|
|
CALL VSPREP
|
|
POP HL
|
|
POP AF
|
|
RET
|
|
; Select the correct physical Apple II text half (bank 0), not a
|
|
; background session's virtual HGR backing store.
|
|
CUMAP CALL VSPREP0
|
|
XOR A
|
|
LD (VS80ON),A
|
|
LD A,(CUHALF)
|
|
OR A
|
|
JR Z,CUMAUX
|
|
LD A,(VSPAGE1)
|
|
JR CUSEL
|
|
CUMAUX LD A,(VSPAGE2)
|
|
CUSEL LD HL,(CUADDR)
|
|
RET
|
|
CUACTIVE DEFB 0
|
|
CUHALF DEFB 0
|
|
CUADDR DEFW 0F400H
|
|
CUUNDER DEFB 0A0H
|
|
|
|
|
|
ORG 07B00H
|
|
CCPCLS LD HL,0B812H
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
RET
|
|
|
|
; CACHE prints three wrapping 16-bit hexadecimal counters. "CACHE RESET"
|
|
; clears them first, without closing cached files or affecting LRU order.
|
|
CCPCACHE LD A,(FILEBUF)
|
|
OR A
|
|
JR Z,CCSHOW
|
|
CP 7 ; normalized tail is " RESET "
|
|
JP NZ,CCPBAD
|
|
LD HL,FILEBUF+2
|
|
LD DE,CCRESET
|
|
LD B,5
|
|
CCCRLP LD A,(DE)
|
|
CP (HL)
|
|
JP NZ,CCPBAD
|
|
INC DE
|
|
INC HL
|
|
DJNZ CCCRLP
|
|
CALL CACHERST
|
|
CCSHOW LD DE,CCOPENMSG
|
|
CALL C_WRITESTR
|
|
LD HL,COPENS
|
|
CALL CCHEX16
|
|
LD DE,CCHITMSG
|
|
CALL C_WRITESTR
|
|
LD HL,CHITS
|
|
CALL CCHEX16
|
|
LD DE,CCEVMSG
|
|
CALL C_WRITESTR
|
|
LD HL,CEVICTS
|
|
CALL CCHEX16
|
|
LD E,13
|
|
JP C_WRITE
|
|
; Print four uppercase hex digits (high byte then low byte).
|
|
CCHEX16 LD A,(HL)
|
|
PUSH AF
|
|
INC HL
|
|
LD A,(HL)
|
|
CALL CCHEX8
|
|
POP AF
|
|
CCHEX8 PUSH AF
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
CALL PHNYB
|
|
POP AF
|
|
JP PHNYB
|
|
CCRESET DEFM 'RESET'
|
|
CCOPENMSG DEFM 'Cache (hex): opens='
|
|
DEFB '$'
|
|
CCHITMSG DEFM ' hits='
|
|
DEFB '$'
|
|
CCEVMSG DEFM ' evictions='
|
|
DEFB '$'
|
|
|
|
CCPBANKS JP RWREPORT
|
|
|
|
; TAB n SUSPEND/RESUME/EXCLUSIVE operate on manually runnable state.
|
|
TB_PAUSE LD A,(SCHFG)
|
|
CP B
|
|
JP Z,TB_NOBG ; background only
|
|
LD A,B
|
|
CALL TB_HPTR
|
|
LD (HL),1
|
|
RET
|
|
TB_UNPS LD A,B
|
|
CALL TBCLEARH
|
|
RET
|
|
TB_NOBG JP UNIMP ; a foreground tab cannot be paused
|
|
TB_EXCL
|
|
PUSH BC ; LDIR consumes BC; B is target tab
|
|
LD HL,TBHOLD
|
|
LD DE,TBHOLD+1
|
|
LD BC,15
|
|
LD A,1
|
|
LD (HL),A
|
|
LDIR
|
|
POP BC
|
|
LD A,B
|
|
CALL TBCLEARH
|
|
LD A,(TABMODE)
|
|
CP B
|
|
JP NZ,TB_SWITCH
|
|
RET
|
|
|
|
ORG 07C00H
|
|
|
|
; LC entry points are absolute RUNTIME addresses, not source image labels.
|
|
LCBASE EQU 0DF00H
|
|
LCBACK EQU 03000H ; MAIN $3000-$3FFF: saved MAIN LC bank2
|
|
LC_BDOS EQU LCBASE-0100H ; $DE00, below conventional BIOS at $DF00
|
|
SW80OFF EQU 0E000H
|
|
RDMAIN EQU 0E002H
|
|
RDAUX EQU 0E003H
|
|
WRMAIN EQU 0E004H
|
|
WRAUX EQU 0E005H
|
|
ZPMAIN EQU 0E008H
|
|
ZPAUX EQU 0E009H
|
|
LCB2RW EQU 0E083H
|
|
LCB1RO EQU 0E088H
|
|
|
|
; Read records using an ordinary MAIN buffer, copy them to AUX.
|
|
; F_READ owns the file handle/FCB and is called BEFORE LC installation:
|
|
; ProDOS MLI must run with its normal language-card mapping.
|
|
AUXRUN LD DE,PATHBUF2
|
|
LD HL,PATHBUF2
|
|
LD (DMAADDR),HL
|
|
CALL _F_OPEN
|
|
OR A
|
|
JR Z,AUXFOUND
|
|
LD A,(CURDRV)
|
|
AND 0F0H ; current user already 0?
|
|
JP Z,RCOERR
|
|
LD A,(CURDRV)
|
|
LD (UC_SAVE),A
|
|
AND 00FH ; retry only executable lookup in user 0
|
|
LD (CURDRV),A
|
|
LD DE,PATHBUF2
|
|
CALL _F_OPEN
|
|
PUSH AF
|
|
LD A,(UC_SAVE)
|
|
LD (CURDRV),A
|
|
POP AF
|
|
OR A
|
|
JP NZ,RCOERR
|
|
AUXFOUND ; A newly launched program invalidates only its own tab's
|
|
; previous SAVE image. The other tabs retain theirs.
|
|
CALL SVFLPTR
|
|
LD (HL),0
|
|
; The transient's RAM can now be in AUX bank 1 instead of
|
|
; the system/video bank 0. 6502 HANDLR temporarily uses bank
|
|
; zero for ALL MLI, keyboard and video requests.
|
|
LD A,(TABMODE)
|
|
CALL TABPHYS
|
|
CALL AXBANK
|
|
; Protect ProDOS MAIN LC only; AUX LC is application-owned.
|
|
CALL AXSNAP
|
|
LD HL,0100H
|
|
LD (AUXDEST),HL
|
|
AUXLOAD LD HL,AUXSTAGE
|
|
LD (DMAADDR),HL
|
|
LD DE,PATHBUF2
|
|
CALL F_READ
|
|
OR A
|
|
JR Z,AUXREC
|
|
CP 1
|
|
JR Z,AUXLDONE
|
|
JP AUXRERR
|
|
AUXREC LD HL,(AUXDEST)
|
|
LD DE,LC_BDOS
|
|
OR A
|
|
SBC HL,DE
|
|
JP NC,AUX2BIG
|
|
LD HL,(AUXDEST)
|
|
LD DE,0B000H
|
|
OR A
|
|
SBC HL,DE
|
|
JR NC,AUXLCREC ; destination in AUX LC bank2
|
|
LD HL,AUXSTAGE
|
|
LD DE,(AUXDEST)
|
|
LD BC,0080H
|
|
XOR A
|
|
LD (WRAUX),A
|
|
LDIR
|
|
LD (WRMAIN),A
|
|
JR AUXADV
|
|
AUXLCREC LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
XOR A
|
|
LD (ZPAUX),A
|
|
LD HL,AUXSTAGE
|
|
LD DE,(AUXDEST)
|
|
LD BC,0080H
|
|
LDIR ; MAIN src -> AUX LC destination
|
|
LD (ZPMAIN),A
|
|
LD A,(LCB1RO)
|
|
AUXADV LD HL,(AUXDEST)
|
|
LD DE,0080H
|
|
ADD HL,DE
|
|
LD (AUXDEST),HL
|
|
JP AUXLOAD
|
|
AUXRERR LD DE,PATHBUF2
|
|
CALL F_CLOSE
|
|
CALL AXREST
|
|
XOR A
|
|
CALL AXBANK
|
|
JP RCLERR
|
|
AUX2BIG LD DE,PATHBUF2
|
|
CALL F_CLOSE
|
|
CALL AXREST
|
|
XOR A
|
|
CALL AXBANK
|
|
JP AUXSIZEERR
|
|
AUXLDONE LD DE,PATHBUF2
|
|
CALL F_CLOSE
|
|
OR A
|
|
JP NZ,AUXRERR0
|
|
LD HL,FILEBUF
|
|
LD (DMAADDR),HL
|
|
LD HL,0080H
|
|
LD (AXDMAP),HL
|
|
; Install DE00 BDOS stub and DF00 BIOS gateway in AUX LC.
|
|
LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
XOR A
|
|
LD (ZPAUX),A
|
|
LD HL,LCBDJP ; three-byte BDOS jump at $DE00
|
|
LD DE,LC_BDOS
|
|
LD BC,3
|
|
LDIR
|
|
LD HL,LCIMG
|
|
LD DE,LCBASE
|
|
LD BC,LCLEN
|
|
LDIR
|
|
LD (ZPMAIN),A
|
|
LD A,(LCB1RO)
|
|
; The helper can now fetch AUX RAM safely from AUX LC.
|
|
LD A,1
|
|
LD (AXMODE),A
|
|
LD HL,IOBYTE ; CP/M applications also see IOBYTE at 0003
|
|
LD DE,IOBYTE
|
|
LD BC,1
|
|
CALL AXM2A
|
|
LD HL,0F800H
|
|
LD DE,02000H
|
|
LD BC,0800H
|
|
CALL LCA2M
|
|
LD HL,0F300H
|
|
LD DE,02800H
|
|
LD BC,16
|
|
CALL LCA2M
|
|
; Restore the known-working page-zero copy. The SoftCard
|
|
; translates these Z80 addresses into Apple II $1000..$10FF,
|
|
; where RAMWRT=AUX selects the current RamWorks bank.
|
|
LD HL,0000H
|
|
LD DE,0000H
|
|
LD BC,0100H
|
|
XOR A
|
|
LD (WRAUX),A
|
|
LDIR
|
|
LD HL,AUXPZERO
|
|
LD DE,0000H
|
|
LD BC,3
|
|
LDIR
|
|
LD HL,AUXPZERO+5
|
|
LD DE,0005H
|
|
LD BC,3
|
|
LDIR
|
|
LD (WRMAIN),A
|
|
; Refresh Apple CP/M BASIC's terminal table in AUX $0396.
|
|
LD HL,0F396H
|
|
LD DE,0F396H
|
|
LD BC,9
|
|
CALL AXM2A
|
|
CALL TBEXE ; remember the launched .COM name
|
|
LD A,(CURDRV)
|
|
PUSH AF
|
|
CALL AXSTART
|
|
POP AF
|
|
LD (CURDRV),A
|
|
LD A,(AXQUIT)
|
|
CP 3
|
|
JP Z,AUXSUSP ; do NOT destroy the parked AUX LC image
|
|
CALL CACHEALL ; AUX termination also closes/flushed files
|
|
LD A,(AXQUIT)
|
|
LD (AUXEXCD),A
|
|
; Restore AUX low metadata (virtual DPB/ALV are not persistent).
|
|
LD HL,02000H
|
|
LD DE,0F800H
|
|
LD BC,0800H
|
|
CALL AXM2A
|
|
LD HL,02800H
|
|
LD DE,0F300H
|
|
LD BC,16
|
|
CALL AXM2A
|
|
; Retain this tab's completed AUX LC (and ordinary AUX TPA).
|
|
; Only ProDOS's MAIN LC needs restoring.
|
|
CALL AXMAINLC
|
|
CALL SVFLPTR
|
|
LD (HL),1
|
|
XOR A
|
|
CALL AXBANK ; CCP/ProDOS return to bank 0
|
|
LD HL,FILEBUF
|
|
LD (DMAADDR),HL
|
|
LD A,(AUXEXCD)
|
|
CP 2
|
|
JR NZ,AUXEXOK
|
|
LD DE,AUXBADMSG
|
|
CALL C_WRITESTR
|
|
LD A,(AXBADFN)
|
|
LD C,A
|
|
CALL PRHEX ; original call in [XX] format
|
|
AUXEXOK LD E,13
|
|
CALL C_WRITE
|
|
LD A,(AUXEXCD)
|
|
CP 1 ; AUX BDOS 0 -> true CP/M warm boot
|
|
JP Z,BDOSWARM
|
|
RET
|
|
; Returning to the CCP after Ctrl-] must not restore the old AUX LC:
|
|
; the parked transient, including its LC gateway, lives in that bank.
|
|
AUXSUSP CALL CACHEALL
|
|
CALL AXMAINLC ; restore ProDOS MAIN LC, keep AUX LC
|
|
XOR A
|
|
CALL AXBANK ; 6502/video service bank 0
|
|
; Backing display is live; no snapshot at suspension.
|
|
LD HL,FILEBUF
|
|
LD (DMAADDR),HL
|
|
LD HL,0080H
|
|
LD (AXDMAP),HL
|
|
RET
|
|
AUXRERR0 CALL AXREST
|
|
XOR A
|
|
CALL AXBANK
|
|
JP RCLERR
|
|
|
|
; Protect ProDOS's MAIN LC bank2 at $B000-$BFFF only. AUX LC is
|
|
; owned by the currently selected RamWorks tab and remains untouched.
|
|
AXSNAP DI
|
|
XOR A
|
|
LD (SW80OFF),A
|
|
LD (ZPMAIN),A
|
|
LD (RDMAIN),A
|
|
LD (WRMAIN),A
|
|
LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
LD HL,0B000H
|
|
LD DE,LCBACK
|
|
LD BC,01000H
|
|
LDIR
|
|
LD A,(LCB1RO)
|
|
RET
|
|
; Error paths must still restore ProDOS's MAIN LC.
|
|
AXREST JP AXMAINLC
|
|
|
|
; Restore original 4K MAIN LC bank2 after AUX exit or suspension.
|
|
AXMAINLC LD A,(LCB2RW)
|
|
LD A,(LCB2RW)
|
|
LD HL,LCBACK
|
|
LD DE,0B000H
|
|
LD BC,01000H
|
|
LDIR
|
|
LD A,(LCB1RO)
|
|
RET
|
|
|
|
AUXBDOS JP AXBDOS
|
|
AUXBDOS0 LD A,1
|
|
LD (AXQUIT),A
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
ABDVER LD HL,0022H
|
|
LD A,L
|
|
LD B,H
|
|
RET
|
|
AUXBDOS2 PUSH DE
|
|
CALL C_WRITE
|
|
POP DE
|
|
LD A,E
|
|
LD L,A
|
|
RET
|
|
AUXBDOS6 LD A,E
|
|
CP 0FFH
|
|
JR NZ,AB6OUT ; control-character output is not input
|
|
AB6IN CALL SCHISFG
|
|
JR Z,AB6KEY
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
AB6KEY CALL C_RAWIO
|
|
CALL TSKKEY
|
|
RET NC ; no shortcut: return raw character/0
|
|
LD A,0F6H ; deferred BDOS 6 poll
|
|
LD L,A
|
|
RET
|
|
AB6OUT JP C_RAWIO
|
|
ABDSTAT LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1
|
|
JP NZ,IOZERO
|
|
JP TSKSTAT ; CRT: Open Apple aware
|
|
AUXBDOS9 PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
EX DE,HL
|
|
AB9NEXT CALL LCPEEK
|
|
CP '$'
|
|
JR Z,AB9DONE
|
|
PUSH HL
|
|
LD E,A
|
|
CALL C_WRITE
|
|
POP HL
|
|
INC HL
|
|
JR AB9NEXT
|
|
AB9DONE POP HL
|
|
POP DE
|
|
POP BC
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
|
|
|
|
TL_BLOCK DEFM ' [Blocked]$'
|
|
|
|
ORG 07EC0H
|
|
; Stage 1 scheduler helpers in unused MAIN $7EC0-$7FFF.
|
|
; Keep clear of the resident BDOS, which now reaches roughly $9760.
|
|
; Foreground runs original VSETMODE; background remembers the mode.
|
|
SCHTMCHK LD A,E
|
|
CP 3 ; 0 VT100, 1 Soroc, 2 H19
|
|
JP NC,AXTMERR
|
|
JP AXTMRUN ; per-task 6502 mode is now virtual
|
|
|
|
; Basic six-byte per-session BDOS state without touching 6502 VMODE.
|
|
; TASKSAVE/TASKLOAD already handle each program's DMA and register state.
|
|
SCHSAVE CALL TABPICK
|
|
LD A,(CURDRV)
|
|
LD (IX+0),A
|
|
LD A,(IOBYTE)
|
|
LD (IX+1),A
|
|
LD HL,(LOGVEC)
|
|
LD (IX+2),L
|
|
LD (IX+3),H
|
|
LD HL,(ROVEC)
|
|
LD (IX+4),L
|
|
LD (IX+5),H
|
|
RET
|
|
SCHLOAD CALL TABPICK
|
|
LD A,(IX+0)
|
|
LD (CURDRV),A
|
|
LD A,(IX+1)
|
|
LD (IOBYTE),A
|
|
LD L,(IX+2)
|
|
LD H,(IX+3)
|
|
LD (LOGVEC),HL
|
|
LD L,(IX+4)
|
|
LD H,(IX+5)
|
|
LD (ROVEC),HL
|
|
RET
|
|
|
|
; Save a background terminal personality without changing the visible
|
|
; application's 6502 terminal parser state.
|
|
SCHTERM PUSH AF
|
|
CALL SCHISFG
|
|
JR NZ,STBG
|
|
POP AF
|
|
OR A
|
|
RET
|
|
STBG POP AF
|
|
PUSH BC
|
|
PUSH DE
|
|
PUSH IX
|
|
PUSH AF
|
|
CALL TABPICK
|
|
POP AF
|
|
LD (IX+6),A
|
|
POP IX
|
|
POP DE
|
|
POP BC
|
|
XOR A
|
|
LD L,A
|
|
SCF
|
|
RET
|
|
|
|
; Z flag: this task owns foreground screen/keyboard.
|
|
SCHISFG PUSH BC
|
|
LD A,(TABMODE)
|
|
LD B,A
|
|
LD A,(SCHFG)
|
|
CP B
|
|
POP BC
|
|
RET
|
|
|
|
; Mark current session as blocked on input. Both original foreground
|
|
; hotkey deferrals and new background input deferrals use this state.
|
|
SCHBLCK LD A,(TABMODE)
|
|
LD E,A
|
|
LD D,0
|
|
LD HL,SCHWAIT
|
|
ADD HL,DE
|
|
LD (HL),1
|
|
RET
|
|
|
|
; Reactivate a task blocked on background console input when selected.
|
|
SCHWAKE LD A,(TABMODE)
|
|
LD E,A
|
|
LD D,0
|
|
LD HL,SCHWAIT
|
|
ADD HL,DE
|
|
LD (HL),0
|
|
RET
|
|
|
|
; The virtual display is live, so no physical screen snapshot is needed
|
|
; when an application yields or a manual tab change is requested.
|
|
SCHSNAP RET ; live screen always up-to-date
|
|
|
|
; Stage 3: defer blocking console reads if this task is in the
|
|
; background, OR if this is the foreground and another task can run
|
|
; while waiting for a keystroke. A physical key already pending may
|
|
; be consumed normally. With no other runnable task use VGET, which
|
|
; preserves the ordinary visible cursor behaviour.
|
|
; The existing AXRETRY markers re-enter the original read on resume.
|
|
SCHPRE LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1
|
|
JR NZ,SPNO ; serial firmware input cannot keyboard-defer
|
|
LD A,C
|
|
CP 1 ; BDOS 1 cooked input
|
|
JR Z,SPREAD
|
|
CP 10 ; BDOS 10 line input (at line start)
|
|
JR Z,SPREAD
|
|
CP 0F2H ; BIOS CONIN via LC gateway
|
|
JR NZ,SPNO
|
|
SPREAD CALL SCHISFG
|
|
JR NZ,SPPARKCODE ; background: always defer input
|
|
PUSH BC
|
|
CALL SCHHAVE
|
|
OR A
|
|
JR Z,SPALLOW ; no runnable background work
|
|
CALL BIOSCONST ; $C000 peek; never consumes keyboard
|
|
OR A
|
|
JR NZ,SPALLOW ; a character is already waiting
|
|
POP BC
|
|
SPPARKCODE LD A,C
|
|
CP 1
|
|
JR Z,SPCOOK
|
|
CP 10
|
|
JR Z,SPLINE
|
|
JR SPBIOS ; validated only 1/10/F2 above
|
|
SPALLOW POP BC
|
|
SPNO OR A ; clear carry
|
|
RET
|
|
SPCOOK LD A,0F1H
|
|
JR SPPARK
|
|
SPBIOS LD A,0F2H
|
|
JR SPPARK
|
|
SPLINE LD (AXLIP),DE
|
|
LD A,0FAH
|
|
SPPARK PUSH AF
|
|
PUSH DE
|
|
CALL SCHBLCK
|
|
LD A,3
|
|
LD (AXQUIT),A
|
|
POP DE
|
|
POP AF
|
|
LD L,A
|
|
SCF
|
|
RET
|
|
|
|
; Trigger only at an outer AUX BDOS/BIOS completion in AXGATE.
|
|
; AXQUIT=3 follows the preexisting TASKSAVE -> AUXSUSP/TABRESUM route.
|
|
SCHTICK LD A,(AXQUIT)
|
|
OR A
|
|
RET NZ
|
|
LD A,(SCHEN)
|
|
OR A
|
|
RET Z
|
|
LD A,(SCHQLEFT)
|
|
DEC A
|
|
LD (SCHQLEFT),A
|
|
RET NZ
|
|
LD A,SCHFGQ
|
|
LD (SCHQLEFT),A
|
|
CALL SCHISFG
|
|
JR NZ,STYIELD ; background must release CPU
|
|
CALL SCHHAVE
|
|
OR A
|
|
RET Z ; no background work: stay in .COM
|
|
STYIELD LD A,3
|
|
LD (AXQUIT),A
|
|
RET
|
|
|
|
|
|
; Stage 2: keep 6502 parser/cursor state coherent across scheduler
|
|
; and manual TAB switches. VSMETAS retains the nine original bytes.
|
|
; This helper block lives in MAIN $8000-$80FF before BIOS $8100.
|
|
; Remaining built-ins and CCP command syntax.
|
|
CCPH2 DEFM 'CACHE CLS BANKS TAB HELP'
|
|
DEFB 13,'$'
|
|
|
|
ORG 08000H
|
|
VSSTSAVE LD A,(VSOK)
|
|
OR A
|
|
RET Z
|
|
CALL VSMETA
|
|
LD (HL),1 ; initialized backing display
|
|
INC HL
|
|
EX DE,HL
|
|
LD HL,(VSPTR)
|
|
LD BC,VSTLEN
|
|
LDIR
|
|
RET
|
|
|
|
VSSTLOAD LD A,(VSOK)
|
|
OR A
|
|
RET Z
|
|
CALL VSMETA
|
|
LD A,(HL)
|
|
INC HL
|
|
OR A
|
|
JR Z,VSTNEW
|
|
LD DE,(VSPTR)
|
|
LD BC,VSTLEN
|
|
LDIR
|
|
RET
|
|
VSTNEW LD DE,(VSPTR)
|
|
LD HL,VSDEFAULT
|
|
LD BC,VSTLEN
|
|
LDIR
|
|
RET
|
|
VSDEFAULT DEFB 0,0,0,0,1,0,0,0,0 ; cursor 0,0 and Soroc
|
|
|
|
; B821: A=snapshot bank, X=physical flag, Y=offset. 6502 updates routing
|
|
; and Microsoft BASIC's current terminal control-byte table.
|
|
VSSELECT PUSH BC
|
|
CALL VSSBANK ; bank 2 / 5 / 10 / 15
|
|
LD (AREG),A
|
|
CALL SCHISFG
|
|
LD A,0
|
|
JR NZ,VSSEBG
|
|
INC A
|
|
VSSEBG LD (XREG),A ; physical output enabled when fg
|
|
LD A,(TABMODE)
|
|
AND 3
|
|
ADD A,A
|
|
ADD A,A
|
|
ADD A,A
|
|
ADD A,01CH ; high-byte delta from $0400
|
|
LD (YREG),A
|
|
LD HL,VTASK
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
POP BC
|
|
RET
|
|
|
|
|
|
ORG BDOSADDR
|
|
|
|
; CP/M 2.2 BIOS jump table (17 entries, three bytes each).
|
|
; Applications discover the BIOS WBOOT entry from the 16-bit operand
|
|
; at 0001H. The following three JP instructions must be CONST, CONIN,
|
|
; CONOUT in that order; TE_APPLE.COM copies exactly those nine bytes.
|
|
; Keep this table contiguous. The disk-sector entries are safe stubs,
|
|
; NOT a ProDOS block/sector BIOS (BP/M files continue through BDOS).
|
|
BIOS JP BDOSINIT ; BOOT +00H
|
|
BIOSWBOOT JP BDOSWARM ; WBOOT +03H
|
|
BIOSCSTAT JP BIOSCONST ; CONST +06H
|
|
BIOSCKBD JP BIOSCONIN ; CONIN +09H
|
|
BIOSCTTY JP BIOSCONOUT ; CONOUT +0CH
|
|
JP BIOSLIST ; LIST +0FH
|
|
JP BIOSPUNCH ; PUNCH +12H
|
|
JP BIOSRDR ; READER +15H
|
|
JP BIOSHOME ; HOME +18H
|
|
JP BIOSSELDSK ; SELDSK +1BH
|
|
JP BIOSSETTRK ; SETTRK +1EH
|
|
JP BIOSSETSEC ; SETSEC +21H
|
|
JP BIOSSETDMA ; SETDMA +24H
|
|
JP BIOSREAD ; READ +27H
|
|
JP BIOSWRITE ; WRITE +2AH
|
|
JP BIOSLSTST ; LISTST +2DH
|
|
JP BIOSSECTRAN ; SECTRAN +30H
|
|
|
|
; Public BDOS entry must be in the FIRST page of resident memory.
|
|
; MESCC-based .COM programs (including TE_APPLE.COM) calculate their
|
|
; stack top by rounding down the address at CP/M $0006 to a page
|
|
; boundary. An address in $82xx would place SP at $8200 INSIDE BP/M!
|
|
; 17 BIOS JP vectors = 51 bytes, so this is $8100 + $33 = $8133.
|
|
BDOSENTRY JP BDOSIMP
|
|
|
|
; Fixed exit/resume point at $8136, immediately after the $8133 BDOS JP.
|
|
; The Z80 is *suspended*, not reset, when CMD=0 switches to the 6502.
|
|
; On a later ProDOS launch the new image resumes at the instruction after
|
|
; LD (SOFTCARD),A. Jump via the freshly reloaded cold vector at $0000.
|
|
; Keep this stub at $8136 in future releases (BIOS ABI + 3-byte BDOS JP).
|
|
BYERESUME LD (SOFTCARD),A ; CCPBYE already set CMD=0 and A=0
|
|
JP 0000H ; Execute only after ProDOS relaunch
|
|
|
|
; CP/M BIOS console: unlike the cooked BDOS C_READ, CONIN does not
|
|
; echo or treat Ctrl-C specially. CONOUT takes its byte in C, and
|
|
; must not suppress LF at the Z80 layer (C_WRITE deliberately does).
|
|
; Neither routine touches the alternate Z80 register set.
|
|
BIOSCONST LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1 ; CRT uses physical keyboard latch
|
|
JP NZ,IOSTAT ; ROM input has no portable nonblock status
|
|
LD A,3 ; 6502 keyboard peek
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
LD A,(AREG)
|
|
OR A
|
|
RET Z
|
|
LD A,0FFH ; CP/M CONST: FF=ready, 00=not
|
|
RET
|
|
|
|
BIOSCONIN LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1
|
|
JP NZ,IOCONIN ; BAT uses selected RDR:, TTY/UC1 serial
|
|
PUSH HL
|
|
LD HL,RDKEY ; VGET waits but doesn't echo
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
LD A,(AREG)
|
|
AND 7FH ; 6502 keyboard sets bit 7
|
|
POP HL
|
|
RET
|
|
|
|
BIOSCONOUT PUSH DE ; C contains character
|
|
PUSH HL
|
|
LD E,C
|
|
CALL C_RAWPUT ; Uncooked output to VRAWPUT
|
|
POP HL
|
|
POP DE
|
|
RET
|
|
|
|
; Physical CP/M BIOS device entry points (also called through AUX LC).
|
|
BIOSLIST LD E,C
|
|
JP IOLIST
|
|
BIOSPUNCH LD E,C
|
|
JP IOPUNCH
|
|
BIOSRDR JP IOREADER
|
|
; Firmware has no standardized ready query. Blocking writes assumed ready.
|
|
BIOSLSTST LD A,0FFH
|
|
RET
|
|
|
|
; Sector BIOS is deliberately unimplemented: don't tell applications
|
|
; that reads or writes succeeded when no data was transferred.
|
|
; BDOS's existing ProDOS file-based interface remains unchanged.
|
|
BIOSHOME RET
|
|
BIOSSELDSK LD HL,0000H ; no sector-level DPH available
|
|
RET
|
|
BIOSSETTRK RET
|
|
BIOSSETSEC RET
|
|
BIOSSETDMA LD (DMAADDR),BC
|
|
RET
|
|
BIOSREAD LD A,1 ; error, not success
|
|
RET
|
|
BIOSWRITE LD A,1 ; error, not success
|
|
RET
|
|
BIOSSECTRAN LD H,B ; identity mapping
|
|
LD L,C
|
|
RET
|
|
|
|
; Entry at 0000H is a COLD start only until the first initialization.
|
|
; Repoint its JP operand to the real BIOS WBOOT jump (BIOS+3),
|
|
; so CP/M applications can discover CONST/CONIN/CONOUT via (0001H).
|
|
; Subsequent RST 0 / JP 0000H runs an ordinary warm boot.
|
|
BDOSINIT DI
|
|
LD SP,STCKTOP ; Valid stack for BPMSIGN
|
|
; Set CON=CRT before BPMSIGN/RWREPORT print anything.
|
|
; Otherwise reset value 00H routes the boot banner to S2.
|
|
LD A,095H
|
|
LD (IOBYTE),A
|
|
; Cold start: the backing ProDOS unit has not been resolved yet.
|
|
; WBOOT deliberately preserves the cached unit.
|
|
XOR A
|
|
LD (VUCVALID),A
|
|
LD (CURDRV),A ; cold boot only: drive A:, user 0
|
|
LD HL,0001H
|
|
LD (LOGVEC),HL ; cold boot: A: initially logged in
|
|
CALL SVCLEAR ; reset all 16 tab-specific SAVE flags
|
|
LD HL,SBWARM
|
|
LD DE,SBWARM+1
|
|
LD BC,15
|
|
XOR A
|
|
LD (HL),A
|
|
LDIR ; clear per-tab pending WBOOT flags
|
|
LD (TABMODE),A ; cold boot starts in session 0
|
|
LD (XSFLAG),A ; cold boot clears native XSUB
|
|
LD (XSLEFT),A
|
|
LD (SBFROM),A
|
|
CALL TABINIT ; reset both CCP session descriptors
|
|
CALL TSKINIT ; discard suspended programs on cold boot
|
|
CALL CACHERST ; zero cache counters on cold start only
|
|
LD HL,BIOSWBOOT
|
|
LD (0001H),HL ; 0000H -> BIOS WBOOT JP (CP/M ABI)
|
|
CALL BPMSIGN
|
|
CALL RWREPORT ; physical bank count and supported tabs
|
|
|
|
; Warm-boot path for BDOS 0, RST 0, JP 0000H and Ctrl-C.
|
|
; Keep the existing BDOS reset semantics, but do not print BPMSIGN.
|
|
BDOSWARM DI ; Make sure interrupts are off
|
|
LD SP,STCKTOP ; Discard transient's stack
|
|
XOR A
|
|
LD (SBHALT),A ; recheck $$$.SUB on a new warm boot
|
|
CALL SBWCLR ; consume current tab's WBOOT request
|
|
|
|
; Applications such as Apple CP/M MBASIC 5.2 replace the
|
|
; warm-start address at $0001 with an address in the TPA.
|
|
; CP/M WBOOT must restore the page-zero system jump vectors.
|
|
; $0000: JP BIOSWBOOT, $0005: JP BDOSENTRY.
|
|
LD A,0C3H ; Opcode for JP absolute
|
|
LD (0000H),A
|
|
LD (0005H),A
|
|
LD HL,BIOSWBOOT
|
|
LD (0001H),HL
|
|
LD HL,BDOSENTRY
|
|
LD (0006H),HL
|
|
|
|
; WBOOT preserves IOBYTE, current drive/user, and LOGVEC.
|
|
; Only a cold boot initializes these session-owned values.
|
|
XOR A
|
|
LD (FILEBUF),A ; Zero chars in command tail
|
|
CALL CACHEALL ; CLOSE ALL, invalidate cache and all FRN slots
|
|
LD HL,FILEBUF ; Initialize DMAADDR to 0080H
|
|
LD (DMAADDR),HL ; ...
|
|
LD HL,0000H ; No drives write protected
|
|
LD (ROVEC),HL ; ...
|
|
JP CCP ; Start the CCP
|
|
|
|
; The TPA is AUX 0100H..DDFFH. Derive KiB from its top at
|
|
; assembly time, then turn it into decimal at run time. 10 right shifts
|
|
; convert a byte count to KiB; valid for any sensible BDOS placement.
|
|
; We print two digits (CP/M's TPA cannot exceed 63 KiB).
|
|
BPMSIGN LD DE,BPMSG
|
|
CALL C_WRITESTR
|
|
LD HL,LC_BDOS
|
|
LD B,10
|
|
BPMSHIFT SRL H
|
|
RR L
|
|
DJNZ BPMSHIFT
|
|
LD A,L
|
|
LD B,'0'
|
|
BPMTENS CP 10
|
|
JR C,BPMDIGITS
|
|
SUB 10
|
|
INC B
|
|
JR BPMTENS
|
|
BPMDIGITS PUSH AF
|
|
LD E,B
|
|
CALL C_WRITE
|
|
POP AF
|
|
ADD A,'0'
|
|
LD E,A
|
|
CALL C_WRITE
|
|
LD DE,BPMSUFF
|
|
JP C_WRITESTR
|
|
|
|
BPMSG DEFB 13
|
|
DEFM 'BP/M AUX TPA '
|
|
DEFB '$'
|
|
BPMSUFF DEFM 'K'
|
|
DEFB 13,'$'
|
|
|
|
BDOSIMP
|
|
;; CALL PRHEX ; Print sys call number
|
|
|
|
LD A,C ; Prepare to check C is in range
|
|
LD (BDOSFN),A ; preserve function before SLA C / vector lookup
|
|
CP 41 ; Max syscall# for CP/M 2.2 is 40
|
|
JP NC,BDOSUNSP ; unsupported function; report it
|
|
LD HL,BDOSVEC ; Start of vector table
|
|
SLA C ; Multiply C by 2
|
|
LD B,0 ; MSB of BC is zero
|
|
ADD HL,BC ; Address of vector in HL
|
|
LD C,(HL) ; Read LSB of address to jump to
|
|
INC HL ; Read MSB of address to jump to
|
|
LD H,(HL) ; ...
|
|
LD L,C ; Address needs to be in HL
|
|
JP (HL) ; Jump to it!
|
|
|
|
; Vector table
|
|
BDOSVEC DEFW C_TERMCPM ; C=00H
|
|
DEFW C_READ ; C=01H
|
|
DEFW C_WRITE ; C=02H
|
|
DEFW IOREADER ; C=03H RDR:
|
|
DEFW IOPUNCH ; C=04H PUN:
|
|
DEFW IOLIST ; C=05H LST:
|
|
DEFW C_RAWIO ; C=06H
|
|
DEFW GET_IOB ; C=07H
|
|
DEFW SET_IOB ; C=08H
|
|
DEFW C_WRITESTR ; C=09H
|
|
DEFW C_READSTR ; C=0AH
|
|
DEFW C_STAT ; C=0BH
|
|
DEFW S_BDOSVER ; C=0CH
|
|
DEFW DRV_ALLRST ; C=0DH
|
|
DEFW DRV_SET ; C=0EH
|
|
DEFW F_OPEN ; C=0FH
|
|
DEFW F_CLOSE ; C=10H
|
|
DEFW F_SFIRST ; C=11H
|
|
DEFW F_SNEXT ; C=12H
|
|
DEFW F_DELETE ; C=13H
|
|
DEFW F_READ ; C=14H
|
|
DEFW F_WRITE ; C=15H
|
|
DEFW F_MAKE ; C=16H
|
|
DEFW F_RENAME ; C=17H
|
|
DEFW DRV_LOGVEC ; C=18H
|
|
DEFW DRV_GET ; C=19H
|
|
DEFW F_DMAOFF ; C=1AH
|
|
DEFW DRV_AVEC ; C=1BH
|
|
DEFW DRV_SETRO ; C=1CH
|
|
DEFW DRV_ROVEC ; C=1DH
|
|
DEFW F_ATTRIB ; C=1EH (F_ATTRIB)
|
|
DEFW DRV_DPB ; C=1FH
|
|
DEFW F_USERNUM ; C=20H
|
|
DEFW F_READRAND ; C=21H
|
|
DEFW F_WRITERAND ; C=22H
|
|
DEFW F_SIZE ; C=23H
|
|
DEFW F_RANDREC ; C=24H
|
|
DEFW DRV_RESET ; C=25H
|
|
DEFW BDOSUNSP ; C=26H (*nothing* in CP/M 2.2)
|
|
DEFW BDOSUNSP ; C=27H (*nothing* in CP/M 2.2)
|
|
DEFW F_WRITERAND ; C=28H (F_WRITEZF)
|
|
|
|
; Diagnose unknown MAIN BDOS calls; retain old UNIMP bell afterwards.
|
|
BDOSUNSP LD DE,BDOSMSG
|
|
CALL C_WRITESTR
|
|
LD A,(BDOSFN)
|
|
LD C,A
|
|
CALL PRHEX
|
|
LD E,13
|
|
CALL C_WRITE
|
|
JP UNIMP
|
|
|
|
; Unimplemented BDOS call, just ring the bell
|
|
UNIMP LD HL,BELL ; We are going to call BELL
|
|
LD (ADDR),HL ; ...
|
|
LD A,1 ; CMD=1 means call 6502 sub
|
|
LD (CMD),A ; ...
|
|
LD (SOFTCARD),A ; Do it!
|
|
RET ; Return to calling program
|
|
|
|
; System reset. Jump to $0000 - doesn't return
|
|
C_TERMCPM RST 0 ; Quick jump to zero
|
|
|
|
; Wait for a character from the console, return it in A and L
|
|
; Also echoes the char to the console
|
|
C_READ LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1
|
|
JP NZ,IOCCOOK ; redirected console, serial firmware
|
|
LD HL,RDKEY ; We are going to call RDKEY
|
|
LD (ADDR),HL ; ...
|
|
LD A,1 ; CMD=1 means call 6502 sub
|
|
LD (CMD),A ; ...
|
|
LD (SOFTCARD),A ; Do it!
|
|
LD A,(AREG) ; Grab the return value
|
|
CP 83H ; See if it is Ctrl-C
|
|
JP Z,BDOSWARM ; Ctrl-C is a warm boot (no banner)
|
|
PUSH AF ; Preserve A (and F)
|
|
LD HL,COUT ; Echo the character using COUT
|
|
LD (ADDR),HL ; ...
|
|
LD A,1 ; CMD=1 means call 6502 sub
|
|
LD (CMD),A ; ...
|
|
LD (SOFTCARD),A ; Do it!
|
|
POP AF ; Restore A (and F)
|
|
AND 7FH ; Mask high bit
|
|
LD L,A ; Copy A to L
|
|
RET ; Return to calling program
|
|
|
|
; BDOS console output. The 6502 driver handles tabs, CR, backspace,
|
|
; clear-screen and the other minimal console controls. Ignore LF because
|
|
; the direct video driver's CR already moves to the next line.
|
|
C_WRITE LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1
|
|
JR NZ,C_RAWPUT ; serial/BAT must retain LF
|
|
LD A,E
|
|
CP 0AH
|
|
JR NZ,C_PUT
|
|
CALL VTISEN ; only native Soroc swallows LF
|
|
CP 1
|
|
RET Z ; VT100 and H19 process cooked LF
|
|
JR C_PUT
|
|
|
|
; Shared low-level output path for cooked and direct console writes.
|
|
; E = ASCII character; 6502 console driver accepts bit 7 set or clear.
|
|
C_RAWPUT LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1
|
|
JP NZ,IOCONOUT ; redirected console bypasses screen driver
|
|
LD A,E ; raw output also updates virtual display
|
|
AND 07FH ; preserve distinction from cooked BDOS
|
|
LD HL,RAWOUT ; B80F raw 6502 vector
|
|
JR VTGATE
|
|
C_PUT LD A,80H ; cooked output, both background/foreground
|
|
OR E
|
|
LD HL,COUT ; B803 cooked 6502 vector
|
|
VTGATE PUSH AF ; retain raw/cooked flag in bit 7
|
|
PUSH HL
|
|
CALL VTISEN ; 1=native Soroc, 0/2=translated
|
|
CP 1
|
|
JR Z,VTNOGATE ; preserve saved output/flags
|
|
POP HL
|
|
POP AF
|
|
JP VTCHAR ; parse VT before 6502 handoff
|
|
VTNOGATE POP HL
|
|
POP AF
|
|
JR C_SENDA
|
|
; Step 3: CP/M terminal ^S/^Q output flow control. All BDOS and
|
|
; BIOS console output passes through C_SENDA (cooked and raw).
|
|
; Only foreground output polls the keyboard. Preserve a pending
|
|
; ordinary key; consume ^S/^Q, which applications must not see.
|
|
C_SENDA PUSH AF ; A is the character being sent
|
|
PUSH HL ; HL is the 6502 output vector
|
|
CALL CFO_CHECK
|
|
POP HL
|
|
POP AF
|
|
LD (AREG),A
|
|
LD (ADDR),HL
|
|
LD A,1
|
|
LD (CMD),A
|
|
LD (SOFTCARD),A
|
|
RET
|
|
|
|
; Read Apple II $C000 keyboard latch from SoftCard Z80 $E000.
|
|
; Reading $C010 ($E010 to Z80) clears the keyboard strobe.
|
|
; Do not consume any other waiting key while output is running.
|
|
CFO_CHECK CALL SCHISFG ; background tasks must not take input
|
|
RET NZ
|
|
CFO_POLL LD A,(0E000H) ; physical $C000 (key + strobe)
|
|
OR A
|
|
RET P ; bit 7 clear: no key pending
|
|
AND 07FH
|
|
CP 013H ; ^S: stop output
|
|
JR Z,CFO_STOP
|
|
CP 011H ; ^Q without ^S: discard quietly
|
|
RET NZ ; other keys belong to the application
|
|
LD A,(0E010H) ; consume ^Q
|
|
RET
|
|
CFO_STOP LD A,(0E010H) ; consume ^S
|
|
CFO_WAIT LD A,(0E000H)
|
|
OR A
|
|
JP P,CFO_WAIT ; no key: remain paused
|
|
AND 07FH
|
|
CP 011H
|
|
JR Z,CFO_GO
|
|
LD A,(0E010H) ; discard other keys while paused
|
|
JR CFO_WAIT
|
|
CFO_GO LD A,(0E010H) ; consume ^Q and resume this output
|
|
RET
|
|
|
|
; CP/M BDOS function 6: E=FF nonblocking read, else raw output.
|
|
; This route uses the raw 6502 entry (CR and LF independent).
|
|
C_RAWIO LD A,E
|
|
CP 0FFH
|
|
JR Z,RIS1
|
|
JR C_RAWPUT
|
|
|
|
; If character is waiting, read from console & return in A
|
|
; Otherwise, return 00H in A
|
|
RIS1 LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1
|
|
JP NZ,IOZERO ; no portable slot-2 nonblocking read
|
|
LD A,3 ; CMD=3 means peek at keyboard
|
|
LD (CMD),A ; ...
|
|
LD (SOFTCARD),A ; Do it
|
|
LD A,(AREG) ; Grab the return value
|
|
CP 0 ; If zero, no chars are waiting
|
|
JR Z,RIS2 ; ...
|
|
LD HL,RDKEY ; We are going to call RDKEY
|
|
LD (ADDR),HL ; ...
|
|
LD A,1 ; CMD=1 means call 6502 sub
|
|
LD (CMD),A ; ...
|
|
LD (SOFTCARD),A ; Do it!
|
|
LD A,(AREG) ; Grab the return value
|
|
AND 7FH ; Mask high bit
|
|
LD L,A ; Copy A to L
|
|
RET ;
|
|
RIS2 XOR A ; No chars waiting, A=0
|
|
LD L,A ; Return in L also
|
|
RET
|
|
|
|
; Get the IOBYTE in A and L
|
|
GET_IOB LD A,(IOBYTE) ;
|
|
LD L,A ; Copy to L
|
|
RET
|
|
|
|
; Set the IOBYTE
|
|
; E contains the IOBYTE value to set
|
|
SET_IOB LD A,E
|
|
LD (IOBYTE),A
|
|
LD A,(AXMODE) ; CP/M $0003 is in AUX for AUX apps
|
|
OR A
|
|
RET Z
|
|
LD HL,IOBYTE
|
|
LD DE,IOBYTE
|
|
LD BC,1
|
|
JP AXM2A
|
|
|
|
; Write ASCII string to console. '$' is the terminator
|
|
; DE contains the address of the string
|
|
C_WRITESTR LD A,(DE) ; Fetch character from string
|
|
CP '$' ; Is it '$'?
|
|
RET Z ; If so, we are done
|
|
PUSH DE ; We are gonna need E
|
|
LD E,A ; For C_WRITE
|
|
CALL C_WRITE ; Sent char to console
|
|
POP DE ; Recover the pointer
|
|
INC DE ; Advance pointer
|
|
JR C_WRITESTR ; Handle the next char
|
|
|
|
; Read console string
|
|
; DE points to the string buffer. First byte of the buffer is the capacity
|
|
; of the buffer. This function writes the number of bytes written in second
|
|
; byte. Entry finishes on CR or when the buffer is filled up.
|
|
; Supports ^H, DEL for deleting a character
|
|
; Supports ^Z for deleting entire line (since ^X doesn't work with RDKEY
|
|
C_READSTR LD H,D ; HL will be the working pointer
|
|
LD L,E ; ...
|
|
INC HL ; Advance to first character ...
|
|
INC HL ; ... 3rd byte of the buffer
|
|
PUSH DE ; Put DE into IX
|
|
POP IX ; ...
|
|
XOR A ; Set number of chars read to zero
|
|
LD (IX+1),A ; ...
|
|
; The console input routine must NOT echo before we know whether
|
|
; editing is legal. In particular, backspace on an empty line must not
|
|
; move the cursor into the CCP's A> prompt. Both ^H and DEL erase one.
|
|
CRSL1 PUSH HL
|
|
CALL SCHIDLE ; allow background jobs while CCP idle
|
|
CALL BIOSCONIN ; Raw key, no premature echo
|
|
POP HL
|
|
CALL TSKKEY ; includes Open Apple+arrows
|
|
JP C,CR_SELCMD
|
|
OR A
|
|
JP Z,CRSL1 ; OA-current beep: no CCP input
|
|
CP 3 ; Preserve CP/M Ctrl-C abort
|
|
JP Z,BDOSWARM
|
|
CP 13
|
|
JR Z,CRSEND
|
|
CP 10
|
|
JR Z,CRSEND
|
|
CP 8
|
|
JR Z,CRSS2
|
|
CP 7FH
|
|
JR Z,CRSS2
|
|
CP 26
|
|
JR Z,CRSS3
|
|
LD B,A
|
|
LD A,(IX+0) ; Buffer capacity
|
|
SUB (IX+1)
|
|
RET Z ; Full: accept/echo nothing else
|
|
LD (HL),B
|
|
INC HL
|
|
INC (IX+1)
|
|
LD E,B
|
|
PUSH HL
|
|
CALL C_WRITE ; Echo accepted character
|
|
POP HL
|
|
JR CRSL1
|
|
CRSEND LD E,A ; Echo line terminator
|
|
CALL C_WRITE
|
|
RET
|
|
CRSS2 LD A,(IX+1) ; Nothing to erase?
|
|
OR A
|
|
JP Z,CRSL1 ; Do not touch prompt or cursor
|
|
DEC HL
|
|
DEC (IX+1)
|
|
PUSH HL
|
|
LD E,8 ; Back up, erase, back up
|
|
CALL C_WRITE
|
|
LD E,' '
|
|
CALL C_WRITE
|
|
LD E,8
|
|
CALL C_WRITE
|
|
POP HL
|
|
JP CRSL1
|
|
CRSS3 LD A,0 ; Set character count to zero to ...
|
|
LD (IX+1),A ; ... cancel the entire entry
|
|
LD E,13 ; Print a carriage return
|
|
CALL C_WRITE ; ...
|
|
RET ; Done
|
|
|
|
; Returns 0 in A and L if no chars waiting, non zero otherwise
|
|
C_STAT LD A,(IOBYTE)
|
|
AND 3
|
|
CP 1
|
|
JP NZ,IOZERO
|
|
LD A,3 ; CMD=3 means peek at keyboard
|
|
LD (CMD),A ; ...
|
|
LD (SOFTCARD),A ; Do it
|
|
LD A,(AREG) ; Grab the return value
|
|
LD L,A ; Copy A to L
|
|
RET
|
|
|
|
; Returns system type in B and H, BDOS version in A and L
|
|
S_BDOSVER LD B,0 ; System is 8080 CP/M
|
|
LD H,B ; Also in H
|
|
LD A,22H ; Pretend to v2.2
|
|
LD L,A ; Also in A
|
|
RET
|
|
|
|
; Reset disks
|
|
; Makes A: drive the default
|
|
DRV_ALLRST LD HL,0000H ; Reset clears the software R/O vector
|
|
LD (ROVEC),HL
|
|
LD A,(CURDRV) ; Contains both user & current drive
|
|
AND 0F0H ; Set drive to 0, meaning A:
|
|
LD (CURDRV),A ; Store in CURDRV
|
|
LD BC,FILEBUF ; FILEBUF is at 0080H
|
|
LD (DMAADDR),BC ; Reset DMA address
|
|
LD HL,FLMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI to flush all files
|
|
RET
|
|
|
|
; Select disk
|
|
; Disk to select is passed in E (A: is 0, B: is 1 etc.)
|
|
; Return 00 for success, 0FFH for error in A and L
|
|
DRV_SET LD A,E ; Prepare to compare disk number
|
|
CP 16 ; Support 16 'drives' A: - P:
|
|
JR NC,DSERR ; If A>15 ... error
|
|
LD B,A ; Stash in B for now
|
|
LD A,(CURDRV) ; Has both user number & current drive
|
|
AND 0F0H ; Mask out old drive number
|
|
OR B ; Replace with new drive number
|
|
LD (CURDRV),A ; Store the requested drive number
|
|
|
|
; Mark this drive in the 16-bit BDOS login vector.
|
|
; B is drive number 0..15 (A:..P:).
|
|
LD HL,0001H ; Bit 0 means drive A:
|
|
LD A,B
|
|
OR A
|
|
JR Z,DSMARK
|
|
DSMSHIFT ADD HL,HL ; Shift mask left one drive
|
|
DEC A
|
|
JR NZ,DSMSHIFT
|
|
DSMARK LD A,(LOGVEC)
|
|
OR L
|
|
LD (LOGVEC),A
|
|
LD A,(LOGVEC+1)
|
|
OR H
|
|
LD (LOGVEC+1),A
|
|
|
|
XOR A ; A=0: Return code meaning success
|
|
JR DSRET ;
|
|
DSERR LD A,0FFH ; Return code for error
|
|
DSRET LD L,A ; Return code in L too
|
|
RET
|
|
|
|
; Open file
|
|
; DE is the address of the FCB describing the file to open
|
|
; Returns error codes in A and L:
|
|
; Returns 0 for success. The FCB for the file opened is left at DMAADDR (slot 0)
|
|
; Returns 0FFH if file not found
|
|
; Opening a file must NOT replace an ongoing BDOS wildcard directory search.
|
|
; ProDOS OPEN already checks existence; F_SFIRST is not needed here.
|
|
F_OPEN ; Explicit CP/M OPEN replaces any cached handle for this name.
|
|
; This also initializes RC/CR from a fresh ProDOS GET_EOF.
|
|
PUSH DE
|
|
LD IX,PATHBUF
|
|
CALL FCB2PATH
|
|
CALL CACHECLS
|
|
POP DE
|
|
OR A
|
|
JP NZ,FOERR
|
|
; This is a new OPEN, even if the caller reused an old FCB.
|
|
; Close old cached name first; then discard its user binding.
|
|
PUSH DE
|
|
POP IX
|
|
LD (IX+0DH),0
|
|
LD (IX+0EH),0
|
|
CALL _F_OPEN
|
|
OR A
|
|
RET NZ
|
|
LD A,(IY+0EH) ; New physical ProDOS reference
|
|
CALL GETIOADDR ; Find its newly allocated slot
|
|
CALL CACHEPUT ; Cache the pathname in PATHBUF
|
|
; Reserve S1 ($0D) for the USER at OPEN time. PIP [Gn]
|
|
; switches USER again before reading this very same FCB.
|
|
LD A,(CURDRV)
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
AND 00FH
|
|
OR 0A0H
|
|
LD (IY+0DH),A
|
|
LD (IY+0EH),07EH ; Logical CP/M open marker (dBASE-safe)
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
|
|
; Alternative entrypoint used for opening ProDOS directory files
|
|
; and used by the CCP to load .COM files. No directory lookup.
|
|
_F_OPEN LD IX,PATHBUF ; Destination buffer
|
|
CALL FCB2PATH ; Populate PATHLEN and PATH
|
|
|
|
PUSH DE ; Copy pointer to FCB ...
|
|
POP IY ; ... into IY
|
|
|
|
; Work out which IOBUF to allocate for this file
|
|
CALL CACHEFREE ; Free slot or evict LRU cached file
|
|
CP 0FFH ; Check for error
|
|
JP Z,FOFULL ; If no slots available, diagnostic
|
|
|
|
LD (TEMPBYTE),A ; Record the buffer index in local var
|
|
LD BC,OFFSET ; Add offset to convert to 6502 address
|
|
ADD HL,BC ; ...
|
|
LD (FOMLII),HL ; Store in parameter list
|
|
|
|
LD HL,FOMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI
|
|
CP 0 ; See if there was an error
|
|
JP NZ,FOERR ; Handle error
|
|
|
|
; Do not publish S2/claim an FRN slot before GET_EOF succeeds.
|
|
; On failure, OPEN has nevertheless allocated a ProDOS handle:
|
|
; close it using the ref number from FOMLIN, then return error.
|
|
LD A,(FOMLIN)
|
|
LD (GEMLIN),A
|
|
LD (FCMLIN),A ; Prepare cleanup CLOSE (no slot yet)
|
|
LD HL,GEMLI
|
|
CALL PRODOS ; GET_EOF (24-bit size)
|
|
OR A
|
|
JR Z,FO_EOF_OK
|
|
LD HL,FCMLI
|
|
CALL PRODOS ; CLOSE even if GET_EOF failed
|
|
JP FOERR ; Do not trust GEMLIE1..3 or FCB S2
|
|
FO_EOF_OK LD A,(FOMLIN)
|
|
LD (IY+0EH),A ; Publish physical reference only now
|
|
|
|
; CP/M RC counts records in extent zero (maximum 128).
|
|
; Round partial 128-byte records up; honour ProDOS's 24-bit EOF.
|
|
LD A,(GEMLIE3)
|
|
OR A
|
|
JR NZ,FO_RC_FULL
|
|
LD A,(GEMLIE2)
|
|
CP 40H ; >= $4000 bytes (16 KiB)?
|
|
JR NC,FO_RC_FULL
|
|
; ceil(EOF/128) for EOF < $4000, with no 16-bit wrap.
|
|
LD HL,(GEMLIE1)
|
|
LD BC,127
|
|
ADD HL,BC
|
|
LD A,L
|
|
RLCA
|
|
AND 1
|
|
LD B,A
|
|
LD A,H
|
|
ADD A,A
|
|
OR B
|
|
JR FO_RC_STORE
|
|
FO_RC_FULL LD A,80H
|
|
FO_RC_STORE LD (IY+0FH),A
|
|
|
|
; Set sequential record number to zero
|
|
XOR A ; Zero the sequential record number
|
|
LD (IY+20H),A ; ...
|
|
|
|
; Store ProDOS FRN in slot FRN1 or FRN2
|
|
LD A,(TEMPBYTE) ; Obtain IOBUF idx (1,2)
|
|
LD HL,FRN1-1 ; Compute address of FRN slot to use
|
|
LD B,0 ; ...
|
|
LD C,A ; ...
|
|
ADD HL,BC ; ...
|
|
LD A,(FOMLIN) ; Get ProDOS file reference number
|
|
LD (HL),A ; Store in FRN slot
|
|
|
|
XOR A ; Success
|
|
LD L,A ; Copy to L
|
|
RET ; Done
|
|
|
|
; Retain diagnostic on genuine buffer exhaustion while testing.
|
|
FOFULL PUSH DE
|
|
LD DE,FOFULLMSG
|
|
CALL C_WRITESTR
|
|
POP DE
|
|
JR FOERR
|
|
FOFULLMSG DEFM 'NO IOBUF'
|
|
DEFB 13,'$'
|
|
|
|
FOERR LD A,0FFH ; Error return status
|
|
LD L,A ; Copy to L
|
|
RET ; Done (error)
|
|
|
|
; Close file
|
|
; DE is the address of the FCB describing the file to close
|
|
; Returns error codes in A and L:
|
|
F_CLOSE LD H,D ; Pointer to FCB ...
|
|
LD L,E ; ... into HL
|
|
LD BC,0EH ; Offset to S2 field (reserved field)
|
|
ADD HL,BC ; Compute address
|
|
LD A,(HL) ; Obtain file reference num from FCB S2
|
|
CP 07EH ; Virtual CP/M open marker
|
|
JR Z,FCVIRT
|
|
OR A
|
|
JR NZ,FCPHYS
|
|
DEC HL ; S2 was cleared: inspect S1 tag
|
|
LD A,(HL)
|
|
INC HL
|
|
AND 0F0H
|
|
CP 0A0H
|
|
JR Z,FCVIRT ; Pascal/M's cleared S2
|
|
XOR A ; Untagged, unopened FCB
|
|
JR FCPHYS
|
|
FCVIRT ; A virtual-close request must close the matching cache entry.
|
|
; Names, not FCB addresses, identify cache entries (AUX staging).
|
|
PUSH HL
|
|
PUSH DE
|
|
PUSH IX
|
|
LD IX,PATHBUF
|
|
CALL FCB2PATH
|
|
CALL CACHECLS
|
|
POP IX
|
|
POP DE
|
|
POP HL
|
|
OR A
|
|
JP NZ,FCERR
|
|
LD (HL),0 ; Clear virtual S2
|
|
DEC HL
|
|
LD (HL),0 ; Clear S1 tag too: now really closed
|
|
JP FCSUCC
|
|
FCPHYS CP 0 ; If file reference number is zero ...
|
|
JR Z,FCSUCC ; ... Nothing to do, just return
|
|
LD (FCMLIN),A ; Store in parameter list
|
|
LD HL,FCMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI
|
|
CP 0 ; See if there was an error
|
|
JR NZ,FCERR ; Handle error
|
|
|
|
LD A,(FCMLIN) ; Obtain file reference number again
|
|
CALL GETIOADDR ; Returns FRN slot in A, IOBUF in HL
|
|
CP 0FFH ; Check for error
|
|
JR Z,FCERR ; If FRN not found, error out
|
|
|
|
LD HL,FRN1-1 ; Compute addr of FRN slot to set to zero
|
|
LD B,0 ; ...
|
|
LD C,A ; ...
|
|
ADD HL,BC ; ...
|
|
XOR A ; And zero it
|
|
LD (HL),A ; ...
|
|
|
|
PUSH DE ; Copy pointer to FCB ...
|
|
POP IX ; ... into IX
|
|
XOR A ; Zero out the S2 field
|
|
LD (IX+0EH),A ; ...
|
|
|
|
FCSUCC XOR A ; Return zero for error
|
|
LD L,A ; Return in L also
|
|
RET
|
|
|
|
FCERR LD A,0FFH ; 0FFH for error
|
|
LD L,A ; Return code in L also
|
|
RET
|
|
|
|
DIRHDSZ EQU 2BH ; Size of ProDOS directory header
|
|
FILEENTSZ EQU 27H ; Size of ProDOS file entry
|
|
ENTPERBLK EQU 0DH ; Number of file entries per block
|
|
|
|
; Search for first match of filename in directory
|
|
; DE is the address of the FCB describing the file to look for
|
|
; Returns error codes in A and L: 0 for success, 0FFH for not found
|
|
; The matching FCB is always in slot 0, so success return code always 0
|
|
F_SFIRST LD (TEMPWORD),DE ; Store pointer to search FCB
|
|
LD A,(DE) ; Obtain drive number
|
|
LD (DFCBDRV),A ; Copy to directory FCB
|
|
LD DE,DFCB ; Use this FCB to open the directory
|
|
CALL F_CLOSE ; Close the directory, if open
|
|
LD DE,DFCB ; Use this FCB to open the directory
|
|
CALL _F_OPEN ; Open the directory (avoiding recursion!)
|
|
OR A ; _F_OPEN returns status in A, not flags
|
|
JR NZ,FSFS2 ; OPEN failed: return no match, no stale DFCB read
|
|
|
|
FSFL1 LD A,0FFH ; Init CDBEXT to 0FFH (see CHKDIRBLK)
|
|
LD (CDBEXT),A ; ...
|
|
CALL RDDIRBLK ; Read first 512 byte block
|
|
CP 0 ; See if it was an error
|
|
JR NZ,FSFS2 ; If error, assume EOF & just return
|
|
|
|
; RDDIRBLK sets CDBPTR/CDBCOUNT based on the link pointer.
|
|
LD DE,(TEMPWORD) ; Get ptr to search FCB back
|
|
CALL CHKDIRBLK ; Search directory block
|
|
CP 0 ; See if it was a match
|
|
JR Z,FSFS1 ; If so, return
|
|
JR FSFL1 ; Loop
|
|
|
|
FSFS1 LD DE,(TEMPWORD) ; Get ptr to search FCB back
|
|
LD A,(DE) ; Obtain drive number
|
|
LD HL,(DMAADDR) ; Pointer to FCB we are returning
|
|
LD (HL),A ; Copy drive number to FCB
|
|
XOR A ; Match
|
|
RET ;
|
|
FSFS2 JR FSNS2 ; Shared EOF cleanup (also OPEN failure)
|
|
|
|
; Search for next match of filename in directory
|
|
; The address of the FCB describing the file to look for is in TEMPWORD
|
|
; Returns error codes in A and L: 0 for success, 0FFH for not found
|
|
; The matching FCB is always in slot 0, so success return code always 0
|
|
F_SNEXT LD A,(CDBCOUNT) ; Entries already consumed
|
|
CP ENTPERBLK ; Last entry returned on previous search?
|
|
JR NC,FSNNEW ; Advance block before dereferencing
|
|
FSNL1 LD DE,(TEMPWORD) ; Get ptr to search FCB back
|
|
CALL CHKDIRBLK ; Search directory block
|
|
CP 0 ; See if it was a match
|
|
JR Z,FSNS1 ; If so, return
|
|
|
|
FSNNEW LD A,0FFH ; Init CDBEXT to 0FFH (see CHKDIRBLK)
|
|
LD (CDBEXT),A ; ...
|
|
CALL RDDIRBLK ; Read next 512 byte block
|
|
CP 0 ; See if it was an error
|
|
JR NZ,FSNS2 ; If error, assume EOF & just return
|
|
|
|
; RDDIRBLK sets CDBPTR/CDBCOUNT based on the link pointer.
|
|
JR FSNL1 ; Loop
|
|
|
|
FSNS1 LD DE,(TEMPWORD) ; Get ptr to search FCB back
|
|
LD A,(DE) ; Obtain drive number
|
|
LD HL,(DMAADDR) ; Pointer to FCB we are returning
|
|
LD (HL),A ; Copy drive number to FCB
|
|
XOR A ; Match
|
|
RET ;
|
|
FSNS2 JP SEARCH_EOF ; Release directory FRN on end-of-search
|
|
|
|
; Delete file
|
|
; DE is the address of the FCB describing the file to delete
|
|
; Returns error codes in A and L:
|
|
; For an exact name, destroy directly; don't clobber the caller's
|
|
; F_SFIRST/F_SNEXT enumeration. Preserve old logic for wildcards.
|
|
F_DELETE CALL DCHKRO
|
|
JP NZ,RODENY
|
|
LD H,D
|
|
LD L,E
|
|
INC HL ; Skip FCB drive byte
|
|
LD B,11 ; 8 + 3 filename characters
|
|
FD_CHKWILD LD A,(HL)
|
|
CP '?'
|
|
JR Z,FD_WILD
|
|
INC HL
|
|
DJNZ FD_CHKWILD
|
|
LD IX,PATHBUF
|
|
CALL FCB2PATH
|
|
CALL CACHECLS ; ProDOS DESTROY requires no cached OPEN
|
|
OR A
|
|
JR NZ,FD_ERR
|
|
LD HL,FDMLI
|
|
CALL PRODOS
|
|
CP 04EH ; ProDOS access denied, not missing
|
|
JP Z,ROFILE
|
|
OR A
|
|
JR Z,FD_OK
|
|
FD_ERR LD A,0FFH
|
|
FD_OK LD L,A
|
|
RET
|
|
; Wildcard delete: never mutate an open ProDOS directory while using
|
|
; its cached search state. Delete ONE match, close the directory before
|
|
; DESTROY, then restart F_SFIRST against the updated directory.
|
|
; The first search distinguishes "no matches" from "all matches deleted".
|
|
FD_WILD PUSH DE ; Save original wildcard FCB
|
|
CALL F_SFIRST ; Find the first matching file
|
|
CP 0FFH
|
|
JR Z,FDS2 ; Nothing matched initially
|
|
FDL1 LD DE,DFCB
|
|
CALL F_CLOSE ; Release directory before DESTROY
|
|
OR A
|
|
JR NZ,FDS2
|
|
LD DE,FILEBUF ; Matching FCB from F_SFIRST
|
|
LD IX,PATHBUF
|
|
CALL FCB2PATH
|
|
CALL CACHECLS ; Close cached match before DESTROY
|
|
OR A
|
|
JR NZ,FDS2
|
|
LD HL,FDMLI
|
|
CALL PRODOS ; ProDOS DESTROY
|
|
CP 04EH
|
|
JR NZ,FDWCHK
|
|
LD A,1
|
|
LD (ROERROR),A ; Delay report until search is closed
|
|
JR FDS2
|
|
FDWCHK OR A
|
|
JR NZ,FDS2
|
|
POP DE ; Restore original wildcard
|
|
PUSH DE
|
|
CALL F_SFIRST ; Re-read directory after deletion
|
|
CP 0FFH
|
|
JR NZ,FDL1 ; Another match
|
|
FDS1 XOR A ; Success: at least one file deleted
|
|
JR FDSDONE
|
|
FDS2 LD A,0FFH ; No match / ProDOS error
|
|
FDSDONE PUSH AF ; Preserve deletion status
|
|
LD DE,DFCB
|
|
CALL F_CLOSE ; Release directory on every exit
|
|
POP AF
|
|
POP DE ; Balance saved wildcard FCB
|
|
LD L,A
|
|
PUSH AF
|
|
LD A,(ROERROR)
|
|
OR A
|
|
JR Z,FDWRET
|
|
POP AF
|
|
JP ROFILE
|
|
FDWRET POP AF
|
|
RET
|
|
|
|
; Read next record
|
|
; DE is the address of the FCB describing the file from which to read
|
|
; Returns error codes in A and L:
|
|
; 0 OK, 1 EOF, 9 invalid FCB, 10 media changed, 0FFH h/w error
|
|
F_READ PUSH DE ; Copy pointer to FCB ...
|
|
POP IX ; ... into IX
|
|
CALL IO_BEGIN ; Reopen logical CP/M FCB if needed
|
|
OR A
|
|
JR Z,F_READ_GO
|
|
LD L,A ; Invalid/open-error FCB
|
|
JP IO_END ; IO_END relocated to $A200 (out of JR range)
|
|
F_READ_GO
|
|
LD A,(IX+0EH) ; Obtain file reference num from FCB S2
|
|
LD (SMMLIN),A ; Store in parameter list for SET_MARK
|
|
LD (FRMLIN),A ; Store in parameter list for READ
|
|
|
|
LD A,(IX+20H) ; Obtain sequential record number
|
|
LD B,(IX+0CH) ; Obtain extent from FCB
|
|
CALL EXRC2MARK ; Set all three ProDOS position bytes
|
|
LD HL,SMMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI - SET_MARK
|
|
CP 4DH ; See if position was out of range
|
|
JP Z,FREOF ; If so, return EOF (1)
|
|
CP 43H ; See if it was a bad file ref number
|
|
JP Z,FRBFCB ; If so, return invalid FCB code (9)
|
|
CP 0 ; See if there was some other error
|
|
JP NZ,FRERR ; If so, return code 0FFH (h/w error)
|
|
|
|
LD HL,(DMAADDR) ; Write data starting at DMA buffer addr
|
|
LD BC,OFFSET ; Convert to 6502 address
|
|
ADD HL,BC ; ...
|
|
LD (FRMLIDB),HL ; Store I/O buffer address in parm list
|
|
|
|
CALL READ128 ; READ and pad a short record with ^Z
|
|
CP 4CH ; See if it was EOF
|
|
JR Z,FREOF ; If so, return EOF code (1)
|
|
CP 43H ; See if it was a bad file ref number
|
|
JR Z,FRBFCB ; If so, return invalid FCB code (9)
|
|
CP 0 ; See if there was some other error
|
|
JR NZ,FRERR ; If so, return code 0FFH (h/w error)
|
|
|
|
LD A,(IX+20H) ; Get sequential rec num from FCB
|
|
INC (IX+20H) ; Increment sequential record number
|
|
CP 127 ; Accumulator before increment
|
|
JR NZ,FRS1 ; If not, then nothing else to do
|
|
XOR A ; Set sequential rec num to zero
|
|
LD (IX+20H),A ; ...
|
|
INC (IX+0CH) ; Increment the extent
|
|
|
|
FRS1 XOR A ; Zero for success
|
|
JP IO_END ; Close transient handle
|
|
FREOF LD A,1 ; EOF return code
|
|
JP IO_END ; Close transient handle
|
|
FRBFCB LD A,9 ; Invalid FCB return code
|
|
JP IO_END ; Close transient handle
|
|
FRERR LD A,0FFH ; All other errors are 0FFH
|
|
JP IO_END ; Close transient handle
|
|
|
|
; Write next record
|
|
; DE is the address of the FCB describing the file to which to write
|
|
; Returns error codes in A and L:
|
|
; 0 OK, 1 dir full, 2 disk full, 9 invalid FCB, 10 media changed, 0FFH h/w error
|
|
F_WRITE CALL DCHKRO
|
|
JP NZ,RODENY
|
|
PUSH DE ; Copy pointer to FCB ...
|
|
POP IX ; ... into IX
|
|
CALL IO_BEGIN ; Reopen logical CP/M FCB if needed
|
|
OR A
|
|
JR Z,F_WRITE_GO
|
|
JP IO_END ; Close transient handle
|
|
F_WRITE_GO
|
|
LD A,(IX+0EH) ; Obtain file reference num from FCB S2
|
|
LD (SMMLIN),A ; Store in parameter list for SET_MARK
|
|
LD (FWMLIN),A ; Store in parameter list for WRITE
|
|
|
|
LD A,(IX+20H) ; Obtain sequential record number
|
|
LD B,(IX+0CH) ; Obtain extent from FCB
|
|
CALL EXRC2MARK ; Set full 24-bit ProDOS position
|
|
|
|
;; ; DEBUG
|
|
;; PUSH HL
|
|
;; LD DE,HEXBUF ; (obsolete diagnostic)
|
|
;; CALL NUM2HEX ; (obsolete diagnostic)
|
|
;; LD DE,HEXBUF ; Write hex value to console
|
|
;; CALL C_WRITESTR ;
|
|
;; POP HL
|
|
;; ; END DEBUG
|
|
|
|
LD HL,SMMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI - SET_MARK
|
|
CP 4DH ; See if position was out of range
|
|
JP Z,FWBFCB ; If so, return invalid FCB code (9)
|
|
CP 43H ; See if it was a bad file ref number
|
|
JP Z,FWBFCB ; If so, return invalid FCB code (9)
|
|
CP 0 ; See if there was some other error
|
|
JP NZ,FWERR ; If so, return code 0FFH (h/w error)
|
|
|
|
LD HL,(DMAADDR) ; Write data at DMA address
|
|
LD BC,OFFSET ; Convert to 6502 address
|
|
ADD HL,BC ; ...
|
|
LD (FWMLIDB),HL ; Store I/O buffer address in parm list
|
|
|
|
LD HL,FWMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI - WRITE
|
|
CP 04EH ; No write permission
|
|
JP Z,FWRO
|
|
CP 43H ; See if it is a bad reference number
|
|
JR Z,FWBFCB ; If so, return invalid FCB code (9)
|
|
CP 48H ; See if it was an overrun error
|
|
JR Z,FWDF ; If so, return disk full code (2)
|
|
CP 0 ; See if there was some other error
|
|
JR NZ,FWERR ; If so, return code 0FFH (h/w error)
|
|
|
|
LD A,(IX+20H) ; Get sequential rec num from FCB
|
|
INC (IX+20H) ; Increment sequential record number
|
|
CP 127 ; Accumulator before increment
|
|
JR NZ,FWS1 ; If not, then nothing else to do
|
|
XOR A ; Set sequential rec num to zero
|
|
LD (IX+20H),A ; ...
|
|
INC (IX+0CH) ; Increment the extent
|
|
|
|
FWS1 XOR A ; Zero for success
|
|
JP IO_END ; Close transient handle
|
|
FWBFCB LD A,9 ; Invalid FCB return code
|
|
JP IO_END ; Close transient handle
|
|
FWDF LD A,2 ; Disk full return code
|
|
JP IO_END ; Close transient handle
|
|
FWERR LD A,0FFH ; All other errors are 0FFH
|
|
JP IO_END ; Close transient handle
|
|
FWRO LD A,0FFH
|
|
CALL IO_END ; Release ProDOS handle first
|
|
JP ROFILE
|
|
|
|
; Create (and open) file
|
|
; DE is the address of the FCB describing the file to create
|
|
; Returns error codes in A and L:
|
|
; 0 for success, 0FFH if file could not be created
|
|
F_MAKE CALL DCHKRO
|
|
JP NZ,RODENY
|
|
LD IX,PATHBUF ; Destination buffer
|
|
CALL FCB2PATH ; Populate PATHLEN and PATH
|
|
LD HL,FMMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI
|
|
CP 044H ; ProDOS: parent directory not found?
|
|
JR NZ,FM_CHECK
|
|
PUSH DE
|
|
CALL UD_MKDIR ; create A1..A15 etc on first write
|
|
POP DE
|
|
OR A
|
|
JP NZ,FMERR
|
|
LD HL,FMMLI
|
|
CALL PRODOS ; retry the original file creation
|
|
FM_CHECK CP 0 ; See if there was an error
|
|
JR NZ,FMERR ; Handle error
|
|
CALL F_OPEN ; Open same FCB without touching search state
|
|
OR A
|
|
RET Z
|
|
PUSH AF ; Diagnostic for post-CREATE OPEN failure
|
|
PUSH BC
|
|
PUSH DE
|
|
LD C,A
|
|
LD DE,FMOPENMSG
|
|
CALL C_WRITESTR
|
|
CALL PRHEX ; Underlying Zapple-II error code
|
|
POP DE
|
|
POP BC
|
|
POP AF
|
|
RET
|
|
FMERR CP 04EH
|
|
JP Z,ROFILE
|
|
PUSH AF
|
|
PUSH BC
|
|
PUSH DE
|
|
LD C,A
|
|
LD DE,FMCRMSG
|
|
CALL C_WRITESTR
|
|
CALL PRHEX ; Show true ProDOS CREATE error
|
|
POP DE
|
|
POP BC
|
|
POP AF
|
|
LD A,0FFH ; 0FFH for error
|
|
LD L,A ; Return code in L also
|
|
RET
|
|
FMCRMSG DEFM 'Zapple-II CREATE error: '
|
|
DEFB '$'
|
|
FMOPENMSG DEFM 'Zapple-II OPEN-after-CREATE error: '
|
|
DEFB '$'
|
|
|
|
; Rename file
|
|
; DE is the address of the FCB describing the file to be renamed. The new name
|
|
; is stuffed into FCB+16 (where the allocation map usually goes)
|
|
; Returns error codes in A and L - 0 for success, 0FFH for file not found
|
|
; ProDOS RENAME checks that the old file exists. Doing F_SFIRST here
|
|
; would clobber a wildcard enumeration in progress (notably PIP).
|
|
F_RENAME CALL DCHKRO ; Check source FCB drive
|
|
JP NZ,RODENY
|
|
PUSH DE
|
|
LD HL,16
|
|
ADD HL,DE
|
|
EX DE,HL
|
|
CALL DCHKRO ; Check destination FCB drive
|
|
POP DE
|
|
JP NZ,RODENY
|
|
LD IX,PATHBUF ; Destination buffer 1
|
|
CALL FCB2PATH ; Populate PATHLEN and PATH for first file
|
|
PUSH DE
|
|
CALL CACHECLS ; Close source cached handle before RENAME
|
|
POP DE
|
|
OR A
|
|
JP NZ,FRNERR
|
|
LD IX,PATHBUF2 ; Destination buffer 2
|
|
LD H,D ; DE -> HL for addition
|
|
LD L,E ; ...
|
|
LD BC,16 ; Increment by 16 bytes to 2nd part of FCB
|
|
ADD HL,BC ; ...
|
|
LD D,H ; HL back to DE
|
|
LD E,L ; ...
|
|
CALL FCB2PATH ; Populate PATHLEN and PATH for second file
|
|
LD HL,FRNMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI
|
|
CP 0 ; See if there was an error
|
|
JR NZ,FRNERR ; Handle error
|
|
XOR A ; Success
|
|
LD L,A ; Return code in L also
|
|
RET
|
|
FRNERR CP 04EH
|
|
JP Z,ROFILE
|
|
LD A,0FFH ; 0FFH for error
|
|
LD L,A ; Return code in L also
|
|
RET
|
|
|
|
; Return bitmap of logged-in drives in HL
|
|
DRV_LOGVEC LD HL,(LOGVEC) ;
|
|
RET
|
|
|
|
; Return current drive in A
|
|
DRV_GET LD A,(CURDRV) ; Contains user number & current drive
|
|
AND 0FH ; Mask out user number
|
|
RET
|
|
|
|
; Set DMA address
|
|
; DMA address is passed in DE
|
|
F_DMAOFF LD (DMAADDR),DE ;
|
|
RET
|
|
|
|
; Return CP/M allocation map. Build it from the actual ProDOS volume bitmap.
|
|
DRV_AVEC CALL V_GETFREE
|
|
LD HL,ALLOCVEC
|
|
RET
|
|
|
|
; Scan every ProDOS bitmap block, up to the full 65535-block (~32 MiB)
|
|
; volume limit. Set DSM to the actual volume capacity in 2 KiB CP/M blocks.
|
|
; The CP/M allocation vector is held in 6502 $0800-$0FFF (Z80 $F800-$FFFF)
|
|
; and built only AFTER all ProDOS READ_BLOCK calls are finished: the input
|
|
; block buffer and the returned allocation vector intentionally overlap.
|
|
;
|
|
; Volume directory block 2 has:
|
|
; $04 = $Fx storage type, $23 = $27 entry length, $24 = $0D entries
|
|
; $27/$28 = volume bitmap start, $29/$2A = number of 512-byte blocks.
|
|
; ProDOS bitmap: 1 = free, MSB first. CP/M bitmap: 0 = free, MSB first.
|
|
; NOTE: one bit per *virtual* 2K block; the bitmap only conveys a free
|
|
; space count. Block identities are synthetic (see N2FMAP).
|
|
;
|
|
; On errors, leave a fully allocated map, i.e. report zero free space.
|
|
; Last ProDOS READ_BLOCK error is recorded in VERR (for MAME debugger).
|
|
; ProDOS unit discovered from current prefix or drive directory by V_FINDUNIT.
|
|
V_GETFREE XOR A
|
|
LD (VERR),A
|
|
|
|
; Discover the physical unit containing the CP/M directories.
|
|
; This must precede READ_BLOCK of the volume header.
|
|
CALL V_FINDUNIT
|
|
JP NZ,V_ERROR
|
|
|
|
LD HL,2
|
|
LD (VRBLK),HL
|
|
CALL V_READ
|
|
JP NZ,V_ERROR
|
|
|
|
; Validate that READ_BLOCK returned a volume header.
|
|
LD A,(VOLBUF+04H)
|
|
AND 0F0H
|
|
CP 0F0H
|
|
JP NZ,V_BADHDR
|
|
LD A,(VOLBUF+23H)
|
|
CP 027H
|
|
JP NZ,V_BADHDR
|
|
LD A,(VOLBUF+24H)
|
|
CP 00DH
|
|
JP NZ,V_BADHDR
|
|
|
|
; How big is the actual volume? Retain this for debugging.
|
|
LD HL,(VOLBUF+29H)
|
|
LD (VTOTAL),HL
|
|
LD A,H
|
|
OR L
|
|
JP Z,V_BADHDR
|
|
|
|
; All 16-bit ProDOS block numbers are representable.
|
|
; 65535 blocks require 16 consecutive bitmap blocks.
|
|
LD (VLEFT),HL
|
|
|
|
; CP/M virtual capacity: floor(number_of_512B_blocks / 4).
|
|
SRL H
|
|
RR L
|
|
SRL H
|
|
RR L
|
|
LD A,H
|
|
OR L
|
|
JP Z,V_BADHDR
|
|
LD (VNBLOCKS),HL
|
|
DEC HL
|
|
LD (DPBDSM),HL ; Correct DSM, e.g. 399 for 800 KiB
|
|
|
|
; Last CP/M block uses byte DSM/8 and mask $80 >> (DSM%8).
|
|
LD A,L
|
|
AND 07H
|
|
LD B,A
|
|
LD A,080H
|
|
V_SETMASK LD C,A
|
|
LD A,B
|
|
OR A
|
|
LD A,C
|
|
JR Z,V_MASKDONE
|
|
SRL A
|
|
DEC B
|
|
JR V_SETMASK
|
|
V_MASKDONE LD (VFMASK),A
|
|
LD HL,(DPBDSM)
|
|
SRL H
|
|
RR L
|
|
SRL H
|
|
RR L
|
|
SRL H
|
|
RR L
|
|
LD DE,ALLOCVEC
|
|
ADD HL,DE
|
|
LD (VFEND),HL
|
|
|
|
; Reserve blocks 0..8 for CP/M directory / synthetic IDs.
|
|
LD HL,(VNBLOCKS)
|
|
LD DE,9
|
|
OR A
|
|
SBC HL,DE
|
|
JR NC,V_BUDGETOK
|
|
LD HL,0
|
|
V_BUDGETOK LD (VBUDGET),HL
|
|
|
|
; Bitmap pointer must be a real volume block.
|
|
LD HL,(VOLBUF+27H)
|
|
LD A,H
|
|
OR L
|
|
JP Z,V_BADHDR
|
|
LD (VRBLK),HL
|
|
LD DE,(VTOTAL)
|
|
OR A
|
|
SBC HL,DE
|
|
JP NC,V_BADHDR
|
|
LD HL,0
|
|
LD (VFREE),HL
|
|
|
|
V_NEXTMAP LD HL,(VLEFT)
|
|
LD A,H
|
|
OR L
|
|
JP Z,V_READY
|
|
CALL V_READ
|
|
JP NZ,V_ERROR
|
|
LD HL,VOLBUF
|
|
LD BC,512
|
|
V_MAPBYTE LD E,(HL)
|
|
LD D,8
|
|
V_MAPBIT PUSH HL
|
|
LD HL,(VLEFT)
|
|
LD A,H
|
|
OR L
|
|
JR Z,V_DONEPOP
|
|
DEC HL
|
|
LD (VLEFT),HL
|
|
POP HL
|
|
LD A,E
|
|
ADD A,A
|
|
LD E,A
|
|
JR NC,V_USED
|
|
PUSH HL
|
|
LD HL,(VFREE)
|
|
INC HL
|
|
LD (VFREE),HL
|
|
POP HL
|
|
V_USED DEC D
|
|
JR NZ,V_MAPBIT
|
|
INC HL
|
|
DEC BC
|
|
LD A,B
|
|
OR C
|
|
JR NZ,V_MAPBYTE
|
|
LD HL,(VRBLK)
|
|
INC HL
|
|
LD (VRBLK),HL
|
|
JP V_NEXTMAP
|
|
|
|
V_DONEPOP POP HL
|
|
JR V_READY
|
|
|
|
; All input READ_BLOCK calls have finished. Reuse the $0800-$0FFF
|
|
; main-memory scratch area as the complete 2048-byte CP/M allocation map.
|
|
; 65535 ProDOS blocks / 4 = 16383 virtual 2K allocation blocks.
|
|
; Fill with $FF (occupied), then clear the free bits from the high end.
|
|
V_READY CALL V_CLEARVEC
|
|
LD HL,(VFREE)
|
|
SRL H
|
|
RR L
|
|
SRL H
|
|
RR L
|
|
LD B,H
|
|
LD C,L
|
|
|
|
V_MARK LD A,B
|
|
OR C
|
|
RET Z
|
|
LD HL,(VBUDGET)
|
|
LD A,H
|
|
OR L
|
|
RET Z ; Cannot exceed actual disk capacity
|
|
DEC HL
|
|
LD (VBUDGET),HL
|
|
LD HL,(VFEND)
|
|
LD A,(VFMASK)
|
|
CPL
|
|
AND (HL)
|
|
LD (HL),A
|
|
LD A,(VFMASK)
|
|
SRL A
|
|
JR NZ,V_NEXTBIT
|
|
LD A,080H
|
|
DEC HL
|
|
V_NEXTBIT LD (VFMASK),A
|
|
LD (VFEND),HL
|
|
DEC BC
|
|
JR V_MARK
|
|
|
|
V_BADHDR LD A,0FEH ; Invalid/misread volume header
|
|
V_ERROR LD (VERR),A ; Capture MLI status for MAME debug
|
|
; On failure advertise zero free blocks.
|
|
CALL V_CLEARVEC
|
|
RET
|
|
|
|
; Fill the full 2K vector with $FF, not just 64 bytes. A $FF in the
|
|
; CP/M allocation bitmap means all blocks are used. LDIR copies the first
|
|
; $FF forward into each following location, taking 2047 transfers.
|
|
V_CLEARVEC LD HL,ALLOCVEC
|
|
LD (HL),0FFH
|
|
LD DE,ALLOCVEC+1
|
|
LD BC,2047
|
|
LDIR
|
|
RET
|
|
|
|
V_READ LD HL,VRMLI
|
|
CALL PRODOS
|
|
OR A
|
|
RET
|
|
|
|
; Find and cache the one physical ProDOS unit hosting Zapple-II.
|
|
; CP/M A:..P: are subdirectories of the SAME ProDOS volume, so changing
|
|
; CP/M drive must not trigger an expensive ON_LINE enumeration.
|
|
; A nonempty GET_PREFIX identifies the volume; if it is empty, search
|
|
; for the first online volume containing the selected drive directory.
|
|
; Only this FIRST discovery uses GET_PREFIX and ON_LINE (unit=0).
|
|
; VUCVALID is cleared by BDOSINIT on cold start, not by WBOOT.
|
|
; Free-space bitmap reads in V_GETFREE are still done on every request.
|
|
; All MLI scratch space is in 6502 main $0800-$097F; the direct console
|
|
; uses text page 1, and READ_BLOCK overwrites this scratch area later.
|
|
; Returns A=0 on success, else an MLI error or $FD (no matching volume).
|
|
V_FINDUNIT LD A,(VUCVALID)
|
|
OR A
|
|
JR Z,VUCDISC
|
|
XOR A ; VRUNIT still holds our ProDOS unit
|
|
RET
|
|
VUCDISC XOR A
|
|
LD (VRUNIT),A ; Invalidate stale unit on failure
|
|
LD HL,VPMLI ; GET_PREFIX -> $0800
|
|
CALL PRODOS
|
|
OR A
|
|
RET NZ
|
|
LD A,(VOLBUF) ; Pascal prefix length
|
|
OR A
|
|
JP Z,V_NOPREFIX
|
|
LD B,A
|
|
LD HL,VOLBUF+1
|
|
LD A,(HL)
|
|
CP '/'
|
|
JP NZ,V_NOMATCH
|
|
INC HL
|
|
DEC B
|
|
LD C,0
|
|
V_PFXNAME LD A,B
|
|
OR A
|
|
JP Z,V_NOMATCH
|
|
LD A,(HL)
|
|
CP '/'
|
|
JR Z,V_PFXDONE
|
|
INC C
|
|
LD A,C
|
|
CP 16 ; ProDOS volume name is <= 15 chars
|
|
JP NC,V_NOMATCH
|
|
INC HL
|
|
DEC B
|
|
JR V_PFXNAME
|
|
V_PFXDONE LD A,C
|
|
OR A
|
|
JP Z,V_NOMATCH
|
|
LD (V_NAMELEN),A
|
|
LD A,1
|
|
LD (V_HASPFX),A
|
|
JR V_ENUM
|
|
V_NOPREFIX XOR A
|
|
LD (V_HASPFX),A
|
|
|
|
V_ENUM LD HL,VOMLI ; ON_LINE all units -> $0880
|
|
CALL PRODOS
|
|
OR A
|
|
RET NZ
|
|
LD IX,VOLBUF+080H
|
|
LD B,16 ; Up to sixteen 16-byte records
|
|
V_SCAN LD A,(IX+0)
|
|
OR A ; End of returned list
|
|
JP Z,V_NOMATCH
|
|
AND 0FH
|
|
JP Z,V_NEXT ; Device error; no name
|
|
LD C,A ; Name length from ON_LINE
|
|
LD A,(V_HASPFX)
|
|
OR A
|
|
JR Z,V_TRYDIR
|
|
LD A,(V_NAMELEN)
|
|
CP C
|
|
JP NZ,V_NEXT
|
|
PUSH BC
|
|
PUSH IX
|
|
POP DE
|
|
INC DE ; First character of ON_LINE name
|
|
LD HL,VOLBUF+2 ; After leading '/' in prefix
|
|
LD B,C
|
|
V_CMPNAME LD A,(DE)
|
|
AND 0DFH ; Case-insensitive ProDOS name
|
|
LD C,A
|
|
LD A,(HL)
|
|
AND 0DFH
|
|
CP C
|
|
JR NZ,V_DIFFER
|
|
INC DE
|
|
INC HL
|
|
DJNZ V_CMPNAME
|
|
POP BC
|
|
JP V_FOUND
|
|
V_DIFFER POP BC
|
|
JP V_NEXT
|
|
|
|
; Null prefix: see whether /VOLUME/<driveletter> is a ProDOS directory.
|
|
; The volume search order from ON_LINE mirrors relative pathname search.
|
|
V_TRYDIR PUSH BC
|
|
LD A,C
|
|
ADD A,3 ; '/' + volume + '/' + drive
|
|
LD (VOLBUF),A
|
|
LD HL,VOLBUF+1
|
|
LD (HL),'/'
|
|
INC HL
|
|
PUSH IX
|
|
POP DE
|
|
INC DE
|
|
LD B,C
|
|
V_MKPATH LD A,(DE)
|
|
LD (HL),A
|
|
INC HL
|
|
INC DE
|
|
DJNZ V_MKPATH
|
|
LD (HL),'/'
|
|
INC HL
|
|
LD A,(CURDRV)
|
|
AND 0FH
|
|
ADD A,'A'
|
|
LD (HL),A
|
|
LD HL,VFMLI ; GET_FILE_INFO on absolute path
|
|
CALL PRODOS
|
|
OR A
|
|
JR NZ,V_TRYNEXT
|
|
LD A,(VFTYPE)
|
|
CP 0FH ; $0F is a ProDOS subdirectory
|
|
JR NZ,V_TRYNEXT
|
|
POP BC
|
|
JR V_FOUND
|
|
V_TRYNEXT POP BC
|
|
V_NEXT LD DE,16
|
|
ADD IX,DE
|
|
DEC B
|
|
JP NZ,V_SCAN
|
|
V_NOMATCH LD A,0FDH ; Prefix volume / CP/M dir not found
|
|
RET
|
|
V_FOUND LD A,(IX+0)
|
|
AND 0F0H ; ProDOS unit DSSS0000
|
|
LD (VRUNIT),A
|
|
LD A,1
|
|
LD (VUCVALID),A ; Keep it for ALL logical drives
|
|
XOR A
|
|
RET
|
|
|
|
; Software R/O for current drive: BDOS 28, drive 0 -> bit 0, etc.
|
|
DRV_SETRO LD A,(CURDRV)
|
|
AND 0FH
|
|
LD B,A
|
|
LD HL,0001H
|
|
LD A,B
|
|
OR A
|
|
JR Z,DSR_SET
|
|
DSR_LOOP ADD HL,HL
|
|
DEC A
|
|
JR NZ,DSR_LOOP
|
|
DSR_SET LD A,(ROVEC)
|
|
OR L
|
|
LD (ROVEC),A
|
|
LD A,(ROVEC+1)
|
|
OR H
|
|
LD (ROVEC+1),A
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
|
|
; Return Z iff the FCB's effective drive is writable. Preserve DE, HL, BC.
|
|
; FCB drive=0 means CURDRV; 1..16 mean explicitly A:..P:.
|
|
|
|
; Return bitmap of read-only drives in HL
|
|
DRV_ROVEC LD HL,(ROVEC) ; Bit 0 of L is A:, bit 7 of H is P:
|
|
RET
|
|
|
|
; Return pointer to Drive Parameter Block in HL
|
|
DRV_DPB CALL V_GETFREE ; Set DSM from real volume first
|
|
LD HL,DPB ; Pointer to drive parameter block
|
|
RET
|
|
|
|
; Get/set user number
|
|
; E contains the user number to set, or 0FFH to get current user number
|
|
; If E is 0FFH then user number is returned in A
|
|
F_USERNUM LD A,E
|
|
CP 0FFH
|
|
JR Z,FUNS1
|
|
AND 00FH ; CP/M USER numbers modulo 16
|
|
RLCA
|
|
RLCA
|
|
RLCA
|
|
RLCA
|
|
LD B,A
|
|
LD A,(CURDRV)
|
|
AND 00FH
|
|
OR B
|
|
LD (CURDRV),A
|
|
XOR A ; SET has no meaningful return value
|
|
LD L,A
|
|
RET
|
|
FUNS1 LD A,(CURDRV)
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
AND 00FH ; no sign extension for user 8..15
|
|
LD L,A
|
|
RET
|
|
|
|
; Random access read record
|
|
; DE contains address of FCB describing the file to read
|
|
; Return code in A and L:
|
|
; 0 success, 1 reading unwritten data, 4 reading unwritten extent,
|
|
; 6 rec number out of range, 9 invalid FCB, 10 media changed, 0FFH h/w err
|
|
F_READRAND PUSH DE ; Copy pointer to FCB ...
|
|
POP IX ; ...
|
|
CALL IO_BEGIN ; Reopen logical CP/M FCB if needed
|
|
OR A
|
|
JR Z,F_READR_GO
|
|
JP IO_END ; Close transient handle
|
|
F_READR_GO
|
|
LD A,(IX+0EH) ; Obtain file reference num from FCB S2
|
|
LD (SMMLIN),A ; Store in parameter list for SET_MARK
|
|
LD (FRMLIN),A ; Store in parameter list for READ
|
|
|
|
CALL RAND2MARK ; Set full 24-bit ProDOS position
|
|
JR NC,FRR_MARK_OK
|
|
LD A,6 ; Record outside ProDOS's 24-bit range
|
|
JP IO_END
|
|
FRR_MARK_OK
|
|
LD HL,SMMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI - SET_MARK
|
|
CP 4DH ; See if position was out of range
|
|
JP Z,FRRRUD ; If so, return reading unwritten data (1)
|
|
CP 43H ; See if it was a bad file ref number
|
|
JP Z,FRRBFCB ; If so, return invalid FCB code (9)
|
|
CP 0 ; See if there was some other error
|
|
JP NZ,FRRERR ; If so, return code 0FFH (h/w error)
|
|
|
|
LD HL,(DMAADDR) ; Write data starting at DMA buffer addr
|
|
LD BC,OFFSET ; Convert to 6502 address
|
|
ADD HL,BC ; ...
|
|
LD (FRMLIDB),HL ; Store I/O buffer address in parm list
|
|
CALL READ128 ; READ and pad a short record with ^Z
|
|
CP 4CH ; See if it was EOF
|
|
JR Z,FRRRUD ; If so, return EOF code (1)
|
|
CP 43H ; See if it was a bad file ref number
|
|
JP Z,FRBFCB ; If so, return invalid FCB code (9)
|
|
CP 0 ; See if there was some other error
|
|
JR NZ,FRRERR ; If so, return code 0FFH (h/w error)
|
|
|
|
LD L,(IX+21H) ; Load random record number (LSB)
|
|
LD H,(IX+22H) ; Load random record number (MSB)
|
|
CALL RECS2EXRC ; Puts extent in B, recs in A
|
|
LD (IX+20H),A ; Update sequential record number
|
|
LD (IX+0CH),B ; Update sequential extent number
|
|
|
|
XOR A ; Zero for success
|
|
JP IO_END ; Close transient handle
|
|
FRRRUD LD A,1 ; Reading unwritten data return code
|
|
JP IO_END ; Close transient handle
|
|
FRRBFCB LD A,9 ; Invalid FCB return code
|
|
JP IO_END ; Close transient handle
|
|
FRRERR LD A,0FFH ; All other errors are 0FFH
|
|
JP IO_END ; Close transient handle
|
|
|
|
; Random access write record
|
|
; DE contains address of FCB describing the file to write
|
|
; Return code in A and L:
|
|
; 0 success, 1 reading unwritten data, 4 reading unwritten extent,
|
|
; 6 rec number out of range, 9 invalid FCB, 10 media changed, 0FFH h/w err
|
|
F_WRITERAND CALL DCHKRO
|
|
JP NZ,RODENY
|
|
PUSH DE ; Copy pointer to FCB ...
|
|
POP IX ; ...
|
|
CALL IO_BEGIN ; Reopen logical CP/M FCB if needed
|
|
OR A
|
|
JR Z,F_WRTR_GO
|
|
JP IO_END ; Close transient handle
|
|
F_WRTR_GO
|
|
LD A,(IX+0EH) ; Obtain file reference num from FCB S2
|
|
LD (GEMLIN),A ; Store in parameter list for GET_EOF
|
|
LD (SEMLIN),A ; Store in parameter list for SET_EOF
|
|
LD (SMMLIN),A ; Store in parameter list for SET_MARK
|
|
LD (FWMLIN),A ; Store in parameter list for WRITE
|
|
|
|
LD HL,GEMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI - GET_EOF
|
|
OR A ; A nonzero means GEMLIE1..3 are invalid
|
|
JP NZ,FWRERR ; IO_END releases transient handle
|
|
|
|
CALL RAND2MARK ; Full 24-bit offset, carry if out of range
|
|
JR NC,FWR_MARK_OK
|
|
LD A,6
|
|
JP IO_END
|
|
FWR_MARK_OK
|
|
; Compare 24-bit EOF against requested position.
|
|
LD A,(GEMLIE1)
|
|
LD HL,SMMLIP1
|
|
SUB (HL)
|
|
INC HL
|
|
LD A,(GEMLIE2)
|
|
SBC A,(HL)
|
|
INC HL
|
|
LD A,(GEMLIE3)
|
|
SBC A,(HL)
|
|
JR NC,FWRS1
|
|
|
|
LD HL,(SMMLIP1)
|
|
LD (SEMLIE1),HL
|
|
LD A,(SMMLIP3)
|
|
LD (SEMLIE3),A
|
|
LD HL,SEMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI - SET_EOF
|
|
OR A ; On failure, do NOT attempt SET_MARK/WRITE
|
|
JP NZ,FWRERR ; IO_END releases transient handle
|
|
|
|
FWRS1 LD HL,SMMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI - SET_MARK
|
|
CP 4DH ; See if position was out of range
|
|
JP Z,FWRBFCB ; If so, return invalid FCB code (9)
|
|
CP 43H ; See if it was a bad file ref number
|
|
JP Z,FWRBFCB ; If so, return invalid FCB code (9)
|
|
CP 0 ; See if there was some other error
|
|
JP NZ,FWRERR ; If so, return code 0FFH (h/w error)
|
|
|
|
LD HL,(DMAADDR) ; Get DMA buffer address
|
|
LD BC,OFFSET ; Convert to 6502 address
|
|
ADD HL,BC ; ...
|
|
LD (FWMLIDB),HL ; Store I/O buffer address in parm list
|
|
LD HL,FWMLI ; Pass address of 6502 JSR instruction
|
|
CALL PRODOS ; Invoke ProDOS MLI - WRITE
|
|
CP 43H ; See if it was a bad file ref number
|
|
JR Z,FWRBFCB ; If so, return invalid FCB code (9)
|
|
CP 48H ; See if it was a bad file ref number
|
|
JR Z,FWRDF ; If so, return invalid FCB code (9)
|
|
CP 0 ; See if there was some other error
|
|
JR NZ,FWRERR ; If so, return code 0FFH (h/w error)
|
|
|
|
LD L,(IX+21H) ; Load random record number (LSB)
|
|
LD H,(IX+22H) ; Load random record number (MSB)
|
|
CALL RECS2EXRC ; Puts extent in B, recs in A
|
|
LD (IX+20H),A ; Update sequential record number
|
|
LD (IX+0CH),B ; Update sequential extent number
|
|
|
|
XOR A ; Zero for success
|
|
JP IO_END ; Close transient handle
|
|
FWRBFCB LD A,9 ; Invalid FCB return code
|
|
JP IO_END ; Close transient handle
|
|
FWRDF LD A,2 ; Disk fill return code
|
|
JP IO_END ; Close transient handle
|
|
FWRERR CP 04EH
|
|
JR Z,FWRRO
|
|
LD A,0FFH ; All other errors are 0FFH
|
|
JP IO_END ; Close transient handle
|
|
FWRRO LD A,0FFH
|
|
CALL IO_END
|
|
JP ROFILE
|
|
|
|
|
|
; Compute file size (BDOS function 35) from the exact ProDOS EOF.
|
|
; DE points to the caller's FCB. On success, update FCB offsets
|
|
; 21H..23H with ceil(EOF/128), little endian (24-bit record count).
|
|
; A=L=0 on success, A=L=FFH on error. Do not alter the FCB otherwise.
|
|
;
|
|
; GET_FILE_INFO does not return EOF, so temporarily OPEN the file,
|
|
; GET_EOF, then CLOSE it. Reuse a free I/O buffer from the two usable slots,
|
|
; but do not claim a slot or alter the caller's S2 / open-file state.
|
|
; Only the randomly accessed file's FCB is updated; DMAADDR is untouched.
|
|
F_SIZE PUSH DE ; Keep FCB pointer across ProDOS calls
|
|
LD IX,PATHBUF ; FCB2PATH needs its destination in IX
|
|
CALL FCB2PATH
|
|
CALL CACHEFND
|
|
OR A
|
|
JR Z,FS_TEMP
|
|
LD (CMRU),A
|
|
PUSH AF
|
|
LD HL,CHITS
|
|
CALL CINC16
|
|
POP AF
|
|
CALL CACHEREF
|
|
LD (GEMLIN),A
|
|
LD HL,GEMLI
|
|
CALL PRODOS
|
|
OR A
|
|
JP NZ,FSERRPOP
|
|
JP FS_MATH
|
|
FS_TEMP CALL CACHEFREE ; Only open temporarily on a miss
|
|
CP 0FFH
|
|
JP Z,FSERRPOP
|
|
LD BC,OFFSET ; Convert Z80 to 6502 buffer address
|
|
ADD HL,BC
|
|
LD (FOMLII),HL
|
|
LD HL,FOMLI ; ProDOS OPEN
|
|
CALL PRODOS
|
|
OR A
|
|
JP NZ,FSERRPOP ; No file opened: nothing to close
|
|
|
|
LD A,(FOMLIN) ; Temporary ProDOS reference number
|
|
LD (GEMLIN),A
|
|
LD (FCMLIN),A
|
|
LD HL,GEMLI ; ProDOS GET_EOF, 24-bit byte count
|
|
CALL PRODOS
|
|
PUSH AF ; Save GET_EOF result across CLOSE
|
|
LD HL,FCMLI ; Always CLOSE the temporary file
|
|
CALL PRODOS
|
|
LD B,A ; CLOSE error, if any
|
|
POP AF
|
|
OR A
|
|
JP NZ,FSERRPOP ; GET_EOF failed
|
|
LD A,B
|
|
OR A
|
|
JP NZ,FSERRPOP ; CLOSE failed
|
|
|
|
FS_MATH ; Shift 24-bit EOF right 7 to obtain 128-byte records.
|
|
; A:H:L is the 24-bit value (high:middle:low).
|
|
; E remembers whether the discarded low 7 bits are nonzero.
|
|
LD A,(GEMLIE1)
|
|
LD L,A
|
|
AND 7FH
|
|
LD E,A
|
|
LD A,(GEMLIE2)
|
|
LD H,A
|
|
LD A,(GEMLIE3)
|
|
LD B,7
|
|
FS_SHIFT SRL A
|
|
RR H
|
|
RR L
|
|
DJNZ FS_SHIFT
|
|
LD C,A ; High byte of record count
|
|
LD A,E ; Round up partial final record
|
|
OR A
|
|
JR Z,FS_STORE
|
|
INC HL
|
|
LD A,H
|
|
OR L
|
|
JR NZ,FS_STORE
|
|
INC C ; Carry into 3rd record-count byte
|
|
FS_STORE POP DE ; Original caller's FCB
|
|
PUSH DE
|
|
POP IX
|
|
LD (IX+21H),L ; R0 (LSB)
|
|
LD (IX+22H),H ; R1
|
|
LD (IX+23H),C ; R2 (MSB)
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
FSERRPOP POP DE ; Leave FCB unchanged on failure
|
|
LD A,0FFH
|
|
LD L,A
|
|
RET
|
|
|
|
; Update random access pointer
|
|
; DE contains the pointer to the FCB to update
|
|
; Sets the random access record of the FCB to the value of the last record
|
|
; read or written sequentially
|
|
F_RANDREC PUSH DE ; Copy pointer to FCB ...
|
|
POP IX ; ... into IX
|
|
LD A,(IX+20H) ; Obtain sequential record number
|
|
LD B,(IX+0CH) ; Obtain extent from FCB
|
|
CALL EXRC2RECS ; Leaves the length in records in HL
|
|
DEC HL ; Because F_READ/F_WRITE advance this
|
|
LD (IX+21H),L ; Store in random access pointer ...
|
|
LD (IX+22H),H ; ... In little endian format
|
|
LD (IX+23H),0 ; Upper record-count byte
|
|
RET
|
|
|
|
; Selectively reset disk drives
|
|
; DE contains bitmap of drives to reset (bit 0 of E if A:, bit 7 of D is P:)
|
|
; Returns A=00H if okay, A=FFH if error
|
|
; Resetting means removing the read-only status
|
|
DRV_RESET LD A,E
|
|
CPL
|
|
LD B,A
|
|
LD A,(ROVEC)
|
|
AND B
|
|
LD (ROVEC),A
|
|
LD A,D
|
|
CPL
|
|
LD B,A
|
|
LD A,(ROVEC+1)
|
|
AND B
|
|
LD (ROVEC+1),A
|
|
XOR A
|
|
LD L,A
|
|
RET
|
|
|
|
; Make a ProDOS MLI call
|
|
; Address of 6502 JSR instruction in front of ProDOS parameter list is
|
|
; passed in in register pair HL, in Z80 address space
|
|
; Return code is passed back in A
|
|
PRODOS LD BC,OFFSET ; Add offset to convert Z80->6502 address
|
|
ADD HL,BC ; ...
|
|
LD (ADDR),HL ; Store it for 6502
|
|
LD A,2 ; CMD=2 means ProDOS MLI call
|
|
LD (CMD),A ; ...
|
|
LD (SOFTCARD),A ; Do it!
|
|
LD A,(AREG) ; Get return code from the MLI call
|
|
RET
|
|
|
|
; Populate the PATH buffer (and PATHLEN) by copying from FCB
|
|
; If the FCB contains A:TEST____.TXT then the PATH buffer will be A/TEST.TXT
|
|
; Unrepresentable 8.3 names are escaped as Z.<13 base36 digits>
|
|
; DE contains a pointer to the FCB
|
|
; IX contains pointer to the path buffer into which to write
|
|
FCB2PATH PUSH IX
|
|
POP IY ; IY points to Pascal length
|
|
INC IX ; IX points to first pathname byte
|
|
LD A,(DE)
|
|
OR A
|
|
JR NZ,F2P_DRV
|
|
LD A,(CURDRV)
|
|
AND 00FH
|
|
INC A
|
|
F2P_DRV ADD A,'A'-1
|
|
LD (IX+0),A
|
|
INC IX
|
|
LD C,1 ; number of pathname prefix bytes
|
|
CALL F2P_USER ; A=bound user for open FCB; else current
|
|
OR A
|
|
JR Z,F2P_SLASH
|
|
CP 10
|
|
JR C,F2P_ONED
|
|
SUB 10
|
|
PUSH AF
|
|
LD (IX+0),'1'
|
|
INC IX
|
|
INC C
|
|
POP AF
|
|
F2P_ONED ADD A,'0'
|
|
LD (IX+0),A
|
|
INC IX
|
|
INC C
|
|
F2P_SLASH LD (IX+0),'/'
|
|
INC IX
|
|
INC C
|
|
LD H,D
|
|
LD L,E
|
|
INC HL ; FCB first name char
|
|
LD A,(HL)
|
|
AND 07FH
|
|
CP ' '
|
|
JP Z,F2PS7 ; open directory (blank filename)
|
|
CALL B69PTH
|
|
OR A
|
|
RET NZ ; escaped path, dynamic prefix included
|
|
LD B,8
|
|
F2PL1 LD A,(HL)
|
|
AND 07FH
|
|
CP ' '
|
|
JR Z,F2PS3
|
|
LD (IX+0),A
|
|
INC C
|
|
INC HL
|
|
INC IX
|
|
DJNZ F2PL1
|
|
|
|
F2PS3 ; An empty extension has no dot (and never reads beyond FCB).
|
|
PUSH HL
|
|
LD HL,9
|
|
ADD HL,DE ; FCB first extension character
|
|
LD A,(HL)
|
|
AND 07FH
|
|
POP HL
|
|
CP ' '
|
|
JP Z,F2PS7
|
|
F2PL2 LD A,(HL) ; Read next character from FCB
|
|
AND 07FH
|
|
CP ' ' ; Is it space?
|
|
JR NZ,F2PS4 ; If not, we are done eating!
|
|
INC HL ; Otherwise advance to next char
|
|
JR F2PL2 ; And loop
|
|
|
|
F2PS4 LD A,'.' ; Separator is a period
|
|
LD (IX+0),A ; Write to buffer
|
|
INC C ; Count the character!
|
|
INC IX ; Advance to next character in buffer
|
|
LD B,0 ; Use B to track num chars in extension
|
|
|
|
; Handle the extension - up to 3 characters
|
|
F2PL3 LD A,B ; Number of extension chars copied
|
|
CP 3 ; Maximum of three
|
|
JR Z,F2PS7
|
|
LD A,(HL) ; Obtain extension character
|
|
AND 07FH
|
|
CP ' ' ; End of extension?
|
|
JR Z,F2PS7
|
|
LD (IX+0),A ; Copy to PATH buffer (no lossy $/8 swap)
|
|
INC C ; Overall character count
|
|
INC B ; Extension character count
|
|
INC HL ; Next byte of filename in FCB
|
|
INC IX ; Next byte of PATH buffer
|
|
JR F2PL3
|
|
|
|
F2PS7 LD A,C ; Store length of string
|
|
LD (IY+0),A ; We kept size byte in IY at start
|
|
RET ;
|
|
|
|
; Clear FCB pointed to by DMAADDR
|
|
CLRFCB LD HL,(DMAADDR) ; 16-byte CP/M FCB header to zero
|
|
LD C,16
|
|
XOR A
|
|
CFL1 LD (HL),A
|
|
INC HL
|
|
DEC C
|
|
JR NZ,CFL1
|
|
LD HL,(DMAADDR) ; FCB name/extension padded with spaces
|
|
INC HL
|
|
LD C,11
|
|
LD A,' '
|
|
CFL2 LD (HL),A
|
|
INC HL
|
|
DEC C
|
|
JR NZ,CFL2
|
|
RET
|
|
|
|
; This operation is almost the inverse of FCB2PATH. It takes a pointer to the
|
|
; beginning of the ProDOS dirent and converts it to FCB format (8.3 with
|
|
; spaces for any unused characters.)
|
|
; Z.<13 base36> ProDOS escapes are decoded into exact CP/M 8.3 fields.
|
|
; Handles '*' wildcard character
|
|
; HL points to the file entry in the ProDOS directory
|
|
; B contains the drive number (1 for A:, 2 for B: etc)
|
|
; C controls whether file size is populated in FCB (C=0 disables file size)
|
|
; The FCB is written to the buffer pointed to by DMAADDR
|
|
; Trashes pretty much all registers (except IX, IY)
|
|
NAME2FCB XOR A
|
|
LD (B69BAD),A ; only directory search cares about BAD
|
|
PUSH BC ; We will need C later
|
|
PUSH HL ; Remember source ProDOS dir entry for EOF
|
|
EX DE,HL ; Stash HL in DE for call to CLRFCB
|
|
CALL CLRFCB ; Clear FCB at DMAADDR
|
|
EX DE,HL ; Get file entry pointer back in HL
|
|
|
|
LD A,(HL) ; Obtain first char of ProDOS dirent
|
|
AND 0FH ; Mask to obtain length of the name
|
|
LD C,A ; Stash source character count in C
|
|
LD DE,(DMAADDR) ; Initialize DE as write pointer
|
|
LD A,B ; Move drive number to A
|
|
LD (DE),A ; Write drive number
|
|
LD B,0 ; Use B to count chars written
|
|
|
|
; Only 15-character names may use the Z.<13> escape.
|
|
LD A,C
|
|
CP 15
|
|
JR NZ,N2FL1
|
|
PUSH BC
|
|
PUSH HL
|
|
PUSH DE
|
|
CALL B69TRY
|
|
POP DE
|
|
POP HL
|
|
POP BC
|
|
OR A
|
|
JP Z,N2FS6 ; decoded a valid CP/M FCB
|
|
CP 1
|
|
JP NZ,N2FS6 ; malformed alias (B69BAD set)
|
|
N2FL1 INC HL ; Advance source pointer
|
|
INC DE ; Advance destination pointer
|
|
LD A,C ; Get count of chars remaining
|
|
CP 0 ; If none left ...
|
|
JP Z,N2FS6 ; We are done
|
|
LD A,B ; Get count of chars written
|
|
CP 8+3 ; If 8+3 chars have been written ...
|
|
JP Z,N2FS6 ; We are done
|
|
LD A,(HL) ; Read character
|
|
CP ':' ; See if it is a colon - terminates filename
|
|
JR Z,N2FS6 ; Prepare to copy the extension
|
|
CP '.' ; See if it is a period
|
|
JR Z,N2FS2 ; Prepare to copy the extension
|
|
CP '*' ; See if it is an asterix
|
|
JR Z,N2FS3 ; Handle asterix wildcard
|
|
LD (DE),A ; Write character verbatim
|
|
INC B ; Increment count of chars written
|
|
N2FS1 DEC C ; Decrement count of chars remaining
|
|
JP N2FL1 ; Loop
|
|
|
|
; Initialize DE, B to start processing the extension
|
|
N2FS2 LD DE,(DMAADDR) ; Destination is start of extension
|
|
INC DE ;
|
|
INC DE ;
|
|
INC DE ;
|
|
INC DE ;
|
|
INC DE ;
|
|
INC DE ;
|
|
INC DE ;
|
|
INC DE ;
|
|
LD B,8 ; 8 chars have been written
|
|
JR N2FS1 ; Jump back into the read-write loop
|
|
|
|
; Handle asterix wildcard character
|
|
N2FS3 LD A,B ; See how many chars have been written
|
|
CP 8 ; >=8? If so we are in the extension
|
|
JR C,N2FS4 ; If in main filename (not extension)
|
|
|
|
; Asterix in extension
|
|
N2FL2 LD A,'?' ; We will write '?' to rest of extension
|
|
LD (DE),A ; Write character
|
|
INC DE ; Increment write pointer
|
|
INC B ; Increment character count
|
|
LD A,B ; See if we are done
|
|
CP 8+3 ; Filename+extension
|
|
JR Z,N2FS6 ; We are done
|
|
JR N2FL2 ; Loop
|
|
|
|
N2FS4 ; Asterix in main filename
|
|
N2FL3 LD A,'?' ; We will write '?' to rest of filename
|
|
LD (DE),A ; Write character
|
|
INC DE ; Increment write pointer
|
|
INC B ; Increment character count
|
|
LD A,B ; See if we are done
|
|
CP 8 ; Filename+extension
|
|
JR Z,N2FS5 ; We are done - eat chars up until '.'
|
|
JR N2FL3 ; Loop
|
|
|
|
N2FS5 ; Eat rest of chars up until '.'
|
|
N2FL4 LD A,(HL) ; Get character
|
|
CP '.' ; See if it is a period
|
|
JP Z,N2FS2 ; If so, go process the extension
|
|
INC HL ; Increment source pointer
|
|
DEC C ; Decrement count of chars remaining
|
|
LD A,C ; Get count of chars remaining
|
|
CP 0 ; If none left ...
|
|
JR Z,N2FS6 ; We are done
|
|
JR N2FL4 ; Loop
|
|
|
|
; Handle file size info
|
|
N2FS6 POP HL ; Source ProDOS file entry
|
|
POP BC ; Original size-request flag in C
|
|
LD A,C
|
|
OR A
|
|
RET Z ; FCB for parsing only; don't touch allocation bytes
|
|
|
|
; ProDOS dir entry +$1E is access; bit 1 permits writing.
|
|
; Reflect a locked file in the high bit of FCB+9 (T1').
|
|
PUSH HL
|
|
LD DE,01EH
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
AND 02H
|
|
JR NZ,N2FADON
|
|
LD HL,(DMAADDR)
|
|
LD DE,9
|
|
ADD HL,DE
|
|
LD A,(HL)
|
|
OR 080H
|
|
LD (HL),A
|
|
N2FADON
|
|
POP HL
|
|
|
|
; ProDOS file entry EOF is a 24-bit little-endian byte count
|
|
; at offsets 21..23. blocks_used includes sapling/tree
|
|
; index blocks and is *not* the logical length.
|
|
LD DE,21
|
|
ADD HL,DE
|
|
LD E,(HL) ; EOF low byte
|
|
INC HL
|
|
LD D,(HL) ; EOF middle byte
|
|
INC HL
|
|
LD B,(HL) ; EOF high byte
|
|
|
|
; EX is one byte in this implementation. Saturate at
|
|
; 256 * 128 records = 4 MiB, represented by EX=255 RC=128.
|
|
LD A,B
|
|
CP 40H
|
|
JP NC,N2FMAX
|
|
|
|
; Compute ceil(EOF / 128) using EOF >> 7 plus one
|
|
; for any nonzero fractional record. Result in HL.
|
|
LD A,E
|
|
AND 7FH
|
|
LD C,0
|
|
JR Z,N2FALGN
|
|
INC C
|
|
N2FALGN LD A,E
|
|
RLCA
|
|
AND 1
|
|
LD L,A
|
|
LD A,D
|
|
ADD A,A
|
|
OR L
|
|
LD L,A
|
|
LD A,D
|
|
RLCA
|
|
AND 1
|
|
LD H,A
|
|
LD A,B
|
|
ADD A,A
|
|
OR H
|
|
LD H,A
|
|
LD A,C
|
|
OR A
|
|
JR Z,N2FROUND
|
|
INC HL
|
|
|
|
N2FROUND PUSH HL ; Save total records for exact boundary
|
|
CALL RECS2EXRC ; B = quotient /128, A = remainder
|
|
POP HL
|
|
LD C,A
|
|
LD A,H
|
|
OR L
|
|
JR Z,N2FSTORE ; Zero-length file
|
|
LD A,C
|
|
OR A
|
|
JR NZ,N2FSTORE
|
|
DEC B ; Exactly n*128 records: previous extent is full
|
|
LD C,080H
|
|
JR N2FSTORE
|
|
|
|
N2FMAX LD B,0FFH ; Clip files >=4 MiB to largest EX/RC
|
|
LD C,080H
|
|
|
|
N2FSTORE LD HL,(DMAADDR)
|
|
PUSH HL
|
|
POP IX
|
|
LD (IX+0CH),B
|
|
LD (IX+0FH),C
|
|
LD A,C
|
|
CALL N2FMAP
|
|
RET
|
|
|
|
; Generate a synthetic CP/M 2.2 allocation map in the directory FCB.
|
|
; A = records in THIS extent (0..128), DMAADDR = returned FCB.
|
|
; DPB has 2 KB allocation blocks, 2-byte block numbers (DSM=511).
|
|
; Thus 16 records per block, 8 entries per extent.
|
|
; Fake block IDs 1..8 are intentionally reused for each file/extent:
|
|
; sufficient for STAT's per-file size column, NOT a filesystem allocator.
|
|
; Overwrites ALL 16 map bytes, so no separate clearing pass is needed.
|
|
; Trashes AF, BC, DE, HL; preserves IX and IY.
|
|
; N2FMAP relocated to spare MAIN space after SEARCH_EOF.
|
|
|
|
|
|
; Read 512 byte block of directory
|
|
; Used by F_SFIRST and F_SNEXT
|
|
; Trashes HL + registers trashed by F_READ (best to assume all of them!)
|
|
; Returns A=0 for success, A=0FFH on error
|
|
RDDIRBLK LD HL,(DMAADDR) ; Preserve caller's DMA address
|
|
PUSH HL
|
|
LD HL,DIRBUF1 ; Four consecutive 128-byte record buffers
|
|
LD B,4
|
|
RDBREAD PUSH BC ; F_READ may destroy registers
|
|
PUSH HL
|
|
LD (DMAADDR),HL
|
|
LD DE,DFCB
|
|
CALL F_READ
|
|
POP HL
|
|
POP BC
|
|
OR A ; 0=success, any other status=error
|
|
JR NZ,RDBS1
|
|
LD DE,128
|
|
ADD HL,DE
|
|
DJNZ RDBREAD
|
|
|
|
; Every directory block starts with two link pointers ($00-$03).
|
|
; A zero previous-block pointer identifies the key block, whose
|
|
; first 39-byte entry ($04-$2A) is the directory header.
|
|
; Count that header as entry 1 of 13; scan 12 actual files.
|
|
; Other blocks contain 13 files beginning at offset $04.
|
|
LD HL,DIRBUF+4
|
|
XOR A
|
|
LD (CDBCOUNT),A
|
|
LD A,(DIRBUF)
|
|
LD B,A
|
|
LD A,(DIRBUF+1)
|
|
OR B
|
|
JR NZ,RDBPTR
|
|
LD HL,DIRBUF+DIRHDSZ
|
|
LD A,1
|
|
LD (CDBCOUNT),A
|
|
RDBPTR LD (CDBPTR),HL
|
|
XOR A ; Return code for success
|
|
JR RDBS2 ; Successful return
|
|
|
|
RDBS1 LD A,0FFH ; Error return code
|
|
|
|
RDBS2 POP HL ; Reset DMA address as we found it
|
|
LD (DMAADDR),HL ; ...
|
|
RET
|
|
|
|
; Match FCBs
|
|
; Used by CHKDIRENT
|
|
; DE is the address of the FCB describing the file to look for
|
|
; Compare with FCB at DMAADDR already created by NAME2FCB
|
|
; Returns A=0 if entry matches, A=FFH if no match
|
|
; Trashes A,BC,DE,HL
|
|
MATCHFCB INC DE ; Skip drive byte
|
|
LD HL,(DMAADDR)
|
|
INC HL
|
|
LD B,11 ; 8.3 filename bytes
|
|
MFL1 LD A,(DE)
|
|
AND 07FH ; Ignore CP/M attribute high bits
|
|
CP '?' ; Wildcard matches any byte
|
|
JR Z,MFS1
|
|
LD C,A
|
|
LD A,(HL)
|
|
AND 07FH
|
|
CP C
|
|
JR NZ,MFS2
|
|
MFS1 INC DE
|
|
INC HL
|
|
DJNZ MFL1
|
|
XOR A
|
|
RET
|
|
MFS2 LD A,0FFH
|
|
RET
|
|
|
|
; Compare a ProDOS directory file entry to see if it matches
|
|
; Used by F_SFIRST and F_SNEXT
|
|
; DE is the address of the FCB describing the file to look for
|
|
; HL points to the first file entry in the ProDOS directory
|
|
; Returns A=0 if entry matches, A=FFH if no match
|
|
CHKDIRENT LD B,0 ; Hardcode drive A: MATCHFCB ignores it
|
|
LD A,(HL) ; Get first byte of file entry
|
|
AND 0FH ; Mask to get the filename length
|
|
CP 0 ; If zero ...
|
|
JR Z,CDES1 ; File is deleted - no match
|
|
PUSH HL ; Preserve HL
|
|
PUSH DE ; Preserve DE
|
|
LD C,1 ; Do check file sizes and put in FCB
|
|
CALL NAME2FCB ; Create FCB in DMA buffer
|
|
POP DE ; Recover DE
|
|
POP HL ; Recover HL
|
|
LD A,(B69BAD) ; malformed escaped entry cannot match
|
|
OR A
|
|
JR NZ,CDES1
|
|
PUSH HL ; Preserve HL
|
|
PUSH DE ; Preserve DE
|
|
CALL MATCHFCB ; Compare search FCB w/ FCB in DMA buffer
|
|
POP DE ; Recover DE
|
|
POP HL ; Recover HL
|
|
RET ; MATCHFCB returns 0 or $FF
|
|
CDES1 LD A,0FFH ; Deleted entry
|
|
RET
|
|
|
|
; Search a 512 byte block of a ProDOS directory for a matching file entry
|
|
; Used by F_SFIRST and F_SNEXT
|
|
; DE is the address of the FCB describing the file to look for
|
|
; If the extent field of this FCB is '?' then all extents are returned
|
|
; CDBPTR points to the next ProDOS file entry to parse
|
|
; CDBCOUNT is set to the number of file entries already parsed in this block
|
|
; Returns A=0 if entry matches, A=FFH if no match
|
|
; Always reload the ProDOS directory pointer at the top of the loop.
|
|
; EX='?' processing uses HL for the synthetic FCB in DMAADDR; if a
|
|
; candidate does not match, CDBS2 loops here without advancing CDBPTR.
|
|
; Never pass that DMA address to CHKDIRENT as a ProDOS dir entry.
|
|
CHKDIRBLK
|
|
CDBL1 LD HL,(CDBPTR) ; Reload each iteration, including EX='?' retries
|
|
CALL CHKDIRENT ; Match the file entry at HL, build FCB
|
|
PUSH AF ; Stash return code for now
|
|
PUSH DE ; Copy pointer to search FCB ...
|
|
POP IX ; ... into IX
|
|
LD A,(IX+0CH) ; Get extent field of search FCB
|
|
CP '?' ; Is it '?'
|
|
JP NZ,CDBS1 ; If not, we can advance to next entry
|
|
|
|
LD A,(CDBEXT) ; Load CDBEXT
|
|
CP 0FFH ; If it is 0FFH, means this is first ...
|
|
JR Z,CDBS0 ; ... time we have looked at this dirent
|
|
JR CDBS01 ; Not the first extent for this dirent
|
|
|
|
; First extent for directory entry (highest numbered extent)
|
|
; This extent may contain 1-128 records
|
|
CDBS0 LD HL,(DMAADDR) ; Address of FCB created by CHKDIRENT
|
|
PUSH HL ; Copy address of FCB ...
|
|
POP IY ; ... into IY
|
|
LD A,(IY+0CH) ; Get num extents for this entry minus one
|
|
LD (CDBEXT),A ; Store it for next time
|
|
LD (IY+0CH),A ; Store it in the FCB returned to caller
|
|
CP 0 ; If CDBEXT will be zero next time
|
|
JR Z,CDBS1 ; ... we can advance to next entry
|
|
JP CDBS2 ; Return FCB for this extent (it match)
|
|
|
|
; Not the first extent for directory entry
|
|
; Always has 128 records
|
|
CDBS01 LD HL,(DMAADDR) ; CHKDIRENT has rebuilt FCB
|
|
PUSH HL
|
|
POP IY ; Use the current output FCB
|
|
LD B,080H ; Earlier extents have 128 records
|
|
LD (IY+0FH),B ; Store in RC
|
|
DEC A ; Decrement extent counter
|
|
LD (CDBEXT),A ; Store it for next time
|
|
LD (IY+0CH),A ; Store it in EX
|
|
PUSH AF ; Keep extent number across N2FMAP
|
|
PUSH DE ; N2FMAP clobbers DE (search FCB pointer)
|
|
LD A,080H ; Full extent => eight 2 KB blocks
|
|
CALL N2FMAP
|
|
POP DE ; Restore search FCB pointer for CDBL1
|
|
POP AF
|
|
CP 0 ; If CDBEXT will be zero next time
|
|
JR Z,CDBS1 ; ... we can advance to next entry
|
|
JR CDBS2 ; Return FCB for this extent (it match)
|
|
|
|
CDBS1 LD HL,(CDBPTR) ; Pointer to file entry
|
|
LD BC,FILEENTSZ ; Advance to next file entry
|
|
ADD HL,BC ; ...
|
|
LD (CDBPTR),HL ; Store the pointer
|
|
LD A,(CDBCOUNT) ; File entry counter
|
|
INC A ; Increment count of file entries
|
|
LD (CDBCOUNT),A ; Store the counter
|
|
LD A,0FFH ; Initialize CDBEXT to 0FFH ...
|
|
LD (CDBEXT),A ; ... to start processing next dirent
|
|
|
|
CDBS2 POP AF ; Get return code back in A
|
|
CP 0 ; Was it a match?
|
|
JR Z,CDBS3 ; If so, we are done
|
|
LD A,(CDBCOUNT) ; Get the counter back
|
|
CP ENTPERBLK ; Done all of them in this block?
|
|
JP NZ,CDBL1 ; If not, loop
|
|
LD A,0FFH ; No match
|
|
RET ;
|
|
CDBS3 XOR A ; Match
|
|
RET
|
|
|
|
; The following variables are used to preserve state between calls
|
|
CDBPTR DEFW 0000H ; Stores pointer to next file entry
|
|
CDBCOUNT DEFB 0 ; Stores file entry counter
|
|
CDBEXT DEFB 0 ; Stores extent number
|
|
|
|
; Find IOBUF address for a file reference number
|
|
; Scan only FRN1..FRN2: third 1K region is spare, fourth is stack.
|
|
; If found, return FRN slot# 1 or 2 in A, I/O buffer address in HL
|
|
; If not found, return A=0FFH
|
|
; Trashes BC
|
|
GETIOADDR LD B,A ; Requested FRN (0 = first free slot)
|
|
LD HL,FRN1 ; FRN1 and FRN2 only
|
|
LD C,1 ; One-based slot number
|
|
GIASCAN LD A,(HL)
|
|
CP B
|
|
JR Z,GIAFOUND
|
|
INC HL
|
|
INC C
|
|
LD A,C
|
|
CP 3
|
|
JR NZ,GIASCAN
|
|
LD A,0FFH
|
|
RET
|
|
GIAFOUND LD A,C ; Return slot 1 or 2 in A
|
|
PUSH AF
|
|
DEC A ; Buffer address = $9800 + (slot-1)*$400
|
|
ADD A,A
|
|
ADD A,A
|
|
LD HL,IOBUF1
|
|
ADD A,H
|
|
LD H,A
|
|
POP AF
|
|
RET
|
|
|
|
; Form 24-bit ProDOS byte mark from CP/M extent B and record A.
|
|
; Valid record numbers are 0..127. Byte offset = (B*128+A)*128.
|
|
; Updates SET_MARK parameter directly, avoiding any 16-bit overflow.
|
|
EXRC2MARK LD C,A
|
|
AND 1
|
|
RRCA ; bit 0 becomes bit 7
|
|
LD (SMMLIP1),A
|
|
LD A,C
|
|
SRL A
|
|
LD C,A
|
|
LD A,B
|
|
RRCA
|
|
RRCA
|
|
AND 0C0H ; B bits 0..1 become byte-1 bits 6..7
|
|
OR C
|
|
LD (SMMLIP2),A
|
|
LD A,B
|
|
SRL A
|
|
SRL A
|
|
LD (SMMLIP3),A
|
|
RET
|
|
|
|
; Form 24-bit ProDOS byte mark from random FCB record R0/R1/R2.
|
|
; ProDOS supports byte offsets < $1000000, hence record <= $1FFFF.
|
|
; Returns carry set for out-of-range random records.
|
|
RAND2MARK LD A,(IX+23H)
|
|
CP 2
|
|
JR NC,RAND_MARK_BAD
|
|
LD B,A
|
|
LD A,(IX+21H)
|
|
LD C,A
|
|
AND 1
|
|
RRCA
|
|
LD (SMMLIP1),A
|
|
LD A,C
|
|
SRL A
|
|
LD C,A
|
|
LD A,(IX+22H)
|
|
AND 1
|
|
RRCA
|
|
OR C
|
|
LD (SMMLIP2),A
|
|
LD A,(IX+22H)
|
|
SRL A
|
|
LD C,A
|
|
LD A,B
|
|
AND 1
|
|
RRCA
|
|
OR C
|
|
LD (SMMLIP3),A
|
|
OR A ; Clear carry
|
|
RET
|
|
RAND_MARK_BAD SCF
|
|
RET
|
|
|
|
; Convert pos in extent/recs format to a linear position in 128 byte records
|
|
; Extent number is passed in B
|
|
; Records within the extent is passed in A
|
|
; Returns the length in records in HL - HL = (B*128)+A
|
|
EXRC2RECS LD H,B ; 16-bit (extent*128 + record)
|
|
LD L,A ; Record must be 0..127
|
|
LD A,H
|
|
RRCA ; Extent bit 0 -> bit 7 of low byte
|
|
AND 80H
|
|
OR L
|
|
LD L,A
|
|
SRL H ; Extent bits 1..7 -> high byte
|
|
RET
|
|
|
|
; Convert position in terms of random record number to extent/recs
|
|
; Length in records in passed in HL
|
|
; Returns extent number in B and records within extent in A
|
|
RECS2EXRC SLA H ; 7 MS-bits of extent number
|
|
BIT 7,L ; See if MS-bit of L is set
|
|
JR Z,R2ERS1 ; If set ...
|
|
INC H ; ... set LSB of extent number
|
|
R2ERS1 LD B,H ; Return extent in B
|
|
LD A,L ; Now for the recs in the extent
|
|
AND 7FH ; Just need to mask out MS-bit
|
|
RET
|
|
|
|
; Compact diagnostic helper: print C as [XX] without a four-digit
|
|
; conversion buffer. Preserve registers as the old PRHEX did.
|
|
PRHEX PUSH AF
|
|
PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
LD E,'['
|
|
CALL C_WRITE
|
|
LD A,C
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
CALL PHNYB
|
|
LD A,C
|
|
CALL PHNYB
|
|
LD E,']'
|
|
CALL C_WRITE
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
POP AF
|
|
RET
|
|
PHNYB AND 0FH
|
|
ADD A,'0'
|
|
CP 3AH ; One past ASCII '9'
|
|
JR C,PHOUT
|
|
ADD A,7
|
|
PHOUT LD E,A
|
|
JP C_WRITE
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; Compact CP/M 2.2-style CCP
|
|
;
|
|
; Commands:
|
|
; - d: - Change default drive (A:, B: etc.)
|
|
; - DIR - Show directory (DIR, DIR A:, DIR FOO.TXT, DIR *.COM)
|
|
; - ERA - Erase file(s) (ERA FOO.TXT, ERA F*.TXT)
|
|
; - REN - Rename file (REN BAR.TXT=FOO.TXT)
|
|
; - SAVE - Save 256-byte pages of TPA (SAVE 7 FOO.COM)
|
|
; - TYPE - Show text file on console (TYPE TEST.TXT)
|
|
; - BYE - Confirm and exit to the ProDOS selector
|
|
; - TERM VT100/SOROC/H19/H89/VT52 - Set terminal personality (no .COM needed)
|
|
; - CACHE - Show ProDOS handle-cache counters (CACHE RESET to zero)
|
|
; - CLS - Reset video state, clear screen and home cursor
|
|
; - TAB - [LIST|HELP|N [SUSPEND|RESUME|EXCLUSIVE|KILL]]
|
|
; - HELP - List available CCP built-in commands
|
|
; - XSUB - Enables app input redirection inside SUBMIT only
|
|
; - USER [n] - Display/select user area (0..15)
|
|
; - FILENAME.COM - Load and run FILENAME.COM at 0100H
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; Built-in argument counts and common filename mistakes are validated.
|
|
; Full CP/M filename grammar is not yet enforced.
|
|
|
|
; Get a line of text from the console & handle it
|
|
CCP
|
|
; A foreground .COM may yield after a scheduler quantum without
|
|
; actually exiting. Nor may CCP draw a prompt when SCHMAIN returns
|
|
; with a pending hotkey. Dispatch that hotkey, then restart the
|
|
; scheduler if the newly selected foreground still has a live .COM.
|
|
CCPL1 CALL SCHMAIN ; run foreground program + background slices
|
|
CALL TBAUTOSW ; drain hotkeys raised INSIDE SCHMAIN
|
|
CALL TSKACTIVE
|
|
OR A
|
|
JR NZ,CCPL1 ; no CCP prompt over WordStar's cursor
|
|
CALL SBWGET ; background-resumed program requested WBOOT?
|
|
OR A
|
|
JP NZ,BDOSWARM ; MAIN CCP owns stack, so reset is now safe
|
|
CALL CCPPRPTR
|
|
LD A,(HL)
|
|
OR A
|
|
JR NZ,CCPREAD ; restored CCP screen already has A>
|
|
LD (HL),1 ; prompt will now be drawn
|
|
CALL CCPPRNT ; [session]drive:> prompt
|
|
CCPREAD
|
|
CALL CCPSUB ; last record of A:$$$.SUB, if present
|
|
OR A
|
|
JR NZ,CCPGOT ; already copied into console line buffer
|
|
LD DE,FILEBUF ; Use FILEBUF as line buffer
|
|
LD A,120 ; Max line length
|
|
LD (DE),A ; Stored in first char of buffer
|
|
CALL C_READSTR ; Get a line from the user
|
|
CCPGOT LD A,(TABREQ)
|
|
CP 0FFH
|
|
JP NZ,CCPL1 ; selector is pending: NO empty command
|
|
CALL CCPPRPTR
|
|
LD (HL),0 ; an actual input line was completed
|
|
LD HL,FILEBUF+1 ; Use actual count byte, not DE (editing clobbers E)
|
|
CALL UCASE ; Uppercase commands AND transient command tails
|
|
|
|
LD A,(FILEBUF+1) ; Number of chars entered
|
|
CP 0 ; If none ...
|
|
JR Z,CCPL1 ; Loop until user types something!
|
|
|
|
; Two char built-in commands A:, B:, C: etc
|
|
CP 2 ; Check if two chars ...
|
|
JR NZ,CCPS1 ; If not, skip
|
|
LD A,(FILEBUF+3) ; See if second char is ':'
|
|
CP ':' ; ...
|
|
JR NZ,CCPS1 ; If not, skip
|
|
LD A,(FILEBUF+2) ; Get the character before the ':'
|
|
SUB 'A' ; A is 0, B is 1 etc.
|
|
CP 16 ; Valid CP/M drives A: through P:
|
|
JP NC,CCPBADDRV
|
|
LD E,A ; Set the default drive
|
|
CALL DRV_SET ; ...
|
|
JP CCPL1 ; Go again
|
|
|
|
CCPBADDRV LD DE,BFMSG
|
|
CALL C_WRITESTR
|
|
JP CCPL1
|
|
CCPS1 LD HL,FCB1 ; Clear FCB1
|
|
LD (DMAADDR),HL ;
|
|
CALL CLRFCB ;
|
|
|
|
LD HL,FCB2 ; Clear FCB2
|
|
LD (DMAADDR),HL ;
|
|
CALL CLRFCB ;
|
|
|
|
CALL PARSE ; Parse the command line
|
|
; A bare ':' (also '.COM', A::, etc.) creates a blank FCB name.
|
|
; FCB2PATH deliberately maps blank FCBs to the drive directory;
|
|
; never attempt to load that directory as executable Z80 code.
|
|
LD A,(PATHBUF2+1)
|
|
CP ' '
|
|
JP Z,CCPBADDRV ; Bad format; stay in CCP
|
|
CALL BUILTIN ; Check for built-in commands
|
|
CP 0 ; If zero, was a built-in command
|
|
JP Z,CCP ; Go again
|
|
CALL RUNCOM ; Try to run .COM file
|
|
|
|
JP CCP ; Go again
|
|
|
|
; CCP uppercaser routines relocated into spare MAIN memory.
|
|
|
|
; Parse the command line
|
|
; Replaces the contents of FILEBUF with command tail as Pascal-style string
|
|
; Creates FCB for loading .COM program
|
|
; B is used to keep track of the parser state machine:
|
|
; 0 in whitespace before command
|
|
; 1 in command
|
|
; 2 in whitespace segment following command
|
|
; 3 in first argument
|
|
; 4 in whitespace segment following first argument
|
|
; 5 in second argument
|
|
; 6 in whitespace segment following second arg (or later)
|
|
PARSE CALL RSTPATHBUF ; Set IY and E for writing PATHBUF
|
|
LD HL,FILEBUF+1 ; Skip first byte - buffer capacity
|
|
PUSH HL ; HL->IX. Use IX as dest pointer into FILEBUF
|
|
POP IX ; ...
|
|
LD D,0 ; D is the count of chars for FILEBUF
|
|
LD B,0 ; B is the state of the parser
|
|
LD C,(HL) ; Get the length of source string
|
|
CTL1 LD A,C ; See if any chars are left in input
|
|
CP 0 ; ...
|
|
JR Z,CTS2 ; If 0, go update the length byte
|
|
INC HL ; Advance to next char of source string
|
|
DEC C ; Decrement count remaining
|
|
LD A,(HL) ; Get char from buffer
|
|
CP ' ' ; See if it a space
|
|
JR Z,CTS1 ; If space ...
|
|
CALL CMDNSPC ; Handle non-space
|
|
JR CTL1 ; Loop
|
|
CTS1 CALL CMDSPC ; Handle space
|
|
JR CTL1 ; Loop
|
|
CTS2 LD A,' ' ; Fake an additional space on the end ...
|
|
CALL CMDSPC ; ... so parser can get itself in correct state
|
|
LD A,D ; Get string length of command tail
|
|
CP 1 ; If length is 1, is just a space
|
|
JR NZ,CTS3 ; If not length 1, store length, otherwise ...
|
|
LD D,0 ; ... Set length to zero
|
|
CTS3 LD (IX+0),0 ; CP/M command tail needs a zero terminator
|
|
; IX points just past the generated tail.
|
|
LD HL,FILEBUF ; First byte is length of string
|
|
LD (HL),D ; Write string length (excluding zero)
|
|
RET ;
|
|
|
|
; Helper function to reset the PATHBUF buffer pointer & character count
|
|
; PATHBUF is used to store the filename for creating an FCB
|
|
; Sets IY to point to first character to be written to PATHBUF
|
|
; Resets count of chars written E
|
|
RSTPATHBUF PUSH HL ; Preserve HL
|
|
LD HL,PATHBUF+1 ; Use HL to initialize IY
|
|
PUSH HL ; HL->IY. Use IY as dest ptr into PATHBUF
|
|
POP IY ; ...
|
|
POP HL ; Restore original HL
|
|
LD E,0 ; Reset count of chars in PATHBUF
|
|
RET
|
|
|
|
; Examines Pascal string pointed to by HL to see if it begins with a drive
|
|
; letter and a colon. If so, reduces string length by 2 and advances HL two
|
|
; positions, sets B to the drive number. If no drive is specified, sets B
|
|
; to the default drive.
|
|
DRVLETTER LD A,(HL) ; Pascal source length
|
|
CP 2 ; Need at least D: before inspecting it
|
|
JR C,DLSNODRV ; Don't read stale byte past ':' or '.'
|
|
INC HL ; Advance to second char
|
|
INC HL ; ...
|
|
LD A,(HL) ; See if second char typed is a colon
|
|
CP ':' ;
|
|
JR NZ,DLS1 ; If not then skip to default drive
|
|
DEC HL ; Back to first char
|
|
LD A,(HL) ; Get the drive letter
|
|
SUB 'A'-1 ; Convert to 1-based drive number
|
|
LD B,A ; In B for NAME2FCB
|
|
DEC HL ; Back one to the size byte
|
|
LD A,(HL) ; Get the string length
|
|
SUB 2 ; Eat drive letter and colon
|
|
INC HL ; ...
|
|
INC HL ; ...
|
|
LD (HL),A ; Write the reduced length
|
|
RET
|
|
DLS1 DEC HL ; Put HL back to the beginning
|
|
DEC HL ; ...
|
|
DLSNODRV LD B,0 ; Use default drive
|
|
RET
|
|
|
|
; Handle the end of command/argument tokens. State B is 0..6:
|
|
; odd states 1/3/5 finish command / first / second argument.
|
|
CMDSPC LD A,B
|
|
OR A
|
|
RET Z ; Leading whitespace
|
|
CP 6
|
|
JR Z,SPTAIL ; Further arguments stay in command tail
|
|
AND 1
|
|
RET Z ; Inter-argument whitespace
|
|
LD A,' '
|
|
CALL EMTFILBUF
|
|
LD A,E
|
|
LD (PATHBUF),A
|
|
CALL RSTPATHBUF
|
|
PUSH HL
|
|
PUSH BC
|
|
PUSH DE
|
|
LD A,B
|
|
CP 1
|
|
JR Z,SPCOM
|
|
CP 3
|
|
JR Z,SPFIRST
|
|
LD HL,FCB2
|
|
JR SPMKFCB
|
|
SPCOM LD HL,PATHBUF2
|
|
JR SPMKFCB
|
|
SPFIRST LD HL,FCB1
|
|
SPMKFCB LD (DMAADDR),HL
|
|
LD HL,PATHBUF
|
|
CALL DRVLETTER
|
|
LD C,0
|
|
CALL NAME2FCB
|
|
POP DE
|
|
POP BC
|
|
POP HL
|
|
LD A,B
|
|
CP 1
|
|
JR NZ,SPNEXT
|
|
LD A,'C'
|
|
LD (PATHBUF2+9),A
|
|
LD A,'O'
|
|
LD (PATHBUF2+10),A
|
|
LD A,'M'
|
|
LD (PATHBUF2+11),A
|
|
SPNEXT INC B
|
|
RET
|
|
SPTAIL LD A,' '
|
|
JP EMTFILBUF
|
|
|
|
; Emit non-space input byte to command and/or argument buffers.
|
|
; State transitions 0->1, 2->3, 4->5 happen on the first byte.
|
|
CMDNSPC LD A,B
|
|
CP 6
|
|
JR Z,CNSFILE
|
|
LD A,(HL)
|
|
CALL EMTPATHBUF
|
|
LD A,B
|
|
OR A
|
|
JR NZ,CNSNEXT
|
|
INC B
|
|
RET
|
|
CNSNEXT CP 1
|
|
RET Z
|
|
LD A,(HL)
|
|
CALL EMTFILBUF
|
|
LD A,B
|
|
CP 2
|
|
JR Z,CNSINC
|
|
CP 4
|
|
RET NZ
|
|
CNSINC INC B
|
|
RET
|
|
CNSFILE LD A,(HL)
|
|
JP EMTFILBUF
|
|
|
|
; Write character at A to IX (which points into FILEBUF)
|
|
; Helper for CMDSPC/CMDNSPC
|
|
EMTFILBUF LD (IX+0H),A ; Emit char to FILEBUF
|
|
INC IX ; ...
|
|
INC D ; Character count for FILEBUF
|
|
RET
|
|
|
|
; Write character at A to IX (which points into PATHBUF)
|
|
; Helper for CMDNSPC
|
|
EMTPATHBUF PUSH AF
|
|
LD A,E
|
|
CP 63
|
|
JR NC,EPPFULL
|
|
POP AF
|
|
LD (IY+0H),A
|
|
INC IY
|
|
INC E
|
|
RET
|
|
EPPFULL POP AF
|
|
RET
|
|
|
|
; Built-ins are matched against the first five bytes of the parsed
|
|
; command FCB. Table at MAIN $7600: name[5], nargs[1], handler[2].
|
|
; Bit 7 of nargs permits zero arguments as well as one (DIR).
|
|
; BITABLE now resides at MAIN $5800; CCPTERM remains in $7600.
|
|
|
|
; CCP arg-checks routines relocated into spare MAIN memory.
|
|
|
|
; Single table-driven dispatcher instead of six repeated string
|
|
; comparisons. A handler is entered with a synthetic return address.
|
|
BUILTIN LD HL,BITABLE
|
|
LD C,15
|
|
BISCAN LD DE,PATHBUF2+1
|
|
LD B,5
|
|
BICMP LD A,(DE)
|
|
CP (HL)
|
|
JR NZ,BISKIP
|
|
INC DE
|
|
INC HL
|
|
DJNZ BICMP
|
|
LD A,(HL)
|
|
LD (BIREQ),A
|
|
INC HL
|
|
LD E,(HL)
|
|
INC HL
|
|
LD D,(HL)
|
|
PUSH DE
|
|
CALL CCPARGC
|
|
LD B,A
|
|
LD A,(BIREQ)
|
|
CP 0FEH ; TAB validates 0, 1 or 2 arguments itself
|
|
JR Z,BIRUN
|
|
AND 0FH
|
|
CP B
|
|
JR Z,BIRUN
|
|
LD A,(BIREQ)
|
|
BIT 7,A
|
|
JR Z,BIFAIL
|
|
LD A,B
|
|
OR A
|
|
JR NZ,BIFAIL
|
|
BIRUN POP HL
|
|
LD DE,BIRETURN
|
|
PUSH DE
|
|
JP (HL)
|
|
BIRETURN XOR A
|
|
RET
|
|
BIFAIL POP HL
|
|
CALL CCPBAD
|
|
XOR A
|
|
RET
|
|
BISKIP LD E,B ; Skip unmatched portion of 5-char key
|
|
LD D,0
|
|
ADD HL,DE
|
|
LD DE,3 ; nargs and jump target
|
|
ADD HL,DE
|
|
DEC C
|
|
JR NZ,BISCAN
|
|
LD A,0FFH
|
|
RET
|
|
BIREQ DEFB 0
|
|
|
|
; CCP yesno routines relocated into spare MAIN memory.
|
|
|
|
; 44K experiment: CCP stays in MAIN, transients load into AUX $0100-$AFFF.
|
|
; Only console BDOS calls are supported by the temporary LC gateway.
|
|
RUNCOM CALL TSKACTIVE
|
|
OR A
|
|
JP Z,AUXRUN
|
|
LD DE,TSKBUSY
|
|
JP C_WRITESTR
|
|
|
|
RCOERR LD DE,NFMSG
|
|
CALL C_WRITESTR
|
|
RET
|
|
RCLERR LD DE,REMSG
|
|
CALL C_WRITESTR
|
|
RET
|
|
AUXSIZEERR LD DE,AUXBIGMSG
|
|
CALL C_WRITESTR
|
|
RET
|
|
AUXBIGMSG DEFM 'COM exceeds AUX TPA'
|
|
DEFB 13,'$'
|
|
AUXBADMSG DEFM 'AUX BDOS unsupported '
|
|
DEFB '$'
|
|
BDOSMSG DEFM 'BDOS unsupported '
|
|
DEFB '$'
|
|
|
|
NFMSG DEFM 'Not found'
|
|
DEFB 13,'$'
|
|
|
|
REMSG DEFM 'I/O error'
|
|
DEFB 13,'$'
|
|
|
|
BFMSG DEFM 'Bad format'
|
|
DEFB 13,'$'
|
|
|
|
RNEMSG DEFM 'Rename error'
|
|
DEFB 13,'$'
|
|
|
|
; Four names per line, with their drive letters. No sorting is done.
|
|
DIRECT LD A,(FCB1NAM)
|
|
CP ' '
|
|
CALL Z,DIRFCB
|
|
XOR A
|
|
LD (DIRCOL),A
|
|
LD HL,FILEBUF
|
|
LD (DMAADDR),HL
|
|
LD E,13
|
|
CALL C_WRITE
|
|
LD DE,FCB1
|
|
CALL F_SFIRST
|
|
OR A
|
|
JR NZ,DIRDONE
|
|
DIRLOOP CALL PRDIRENT
|
|
LD DE,FCB1
|
|
CALL F_SNEXT
|
|
OR A
|
|
JR Z,DIRLOOP
|
|
DIRDONE LD A,(DIRCOL)
|
|
OR A
|
|
RET Z
|
|
LD E,13
|
|
JP C_WRITE
|
|
DIRCOL DEFB 0
|
|
|
|
; Set up FCB1 to search all files
|
|
; If length of command tail is zero, or length of command tail is 3
|
|
; and 3rd char is ':' then set up FCB for searching
|
|
DIRFCB LD A,(FILEBUF)
|
|
CP 4
|
|
JR NZ,DFS1
|
|
LD A,(FILEBUF+3)
|
|
CP ':'
|
|
JR NZ,DFS1
|
|
LD A,(FILEBUF+2)
|
|
SUB 'A'-1
|
|
JR DFS2
|
|
DFS1 LD A,(CURDRV)
|
|
AND 0FH
|
|
INC A
|
|
|
|
DFS2 LD (FCB1DRV),A
|
|
LD HL,FCB1NAM
|
|
LD B,11
|
|
LD A,'?'
|
|
DFILL LD (HL),A
|
|
INC HL
|
|
DJNZ DFILL
|
|
RET
|
|
|
|
; Print drive: plus 8.3 filename from the first 11 bytes of DMA.
|
|
; Four entries per row, 3-space separation. Existing dot retained.
|
|
PRDIRENT LD A,(FCB1DRV)
|
|
DEC A
|
|
ADD A,'A'
|
|
LD E,A
|
|
CALL C_WRITE
|
|
LD E,':'
|
|
CALL C_WRITE
|
|
LD E,' '
|
|
CALL C_WRITE
|
|
LD HL,FILEBUF+1
|
|
LD B,11
|
|
PRDLOOP LD A,B
|
|
CP 3
|
|
JR NZ,PRDNODOT
|
|
LD E,' '
|
|
PUSH HL
|
|
CALL C_WRITE
|
|
POP HL
|
|
PRDNODOT LD E,(HL)
|
|
PUSH HL
|
|
CALL C_WRITE
|
|
POP HL
|
|
INC HL
|
|
DJNZ PRDLOOP
|
|
LD A,(DIRCOL)
|
|
INC A
|
|
AND 3
|
|
LD (DIRCOL),A
|
|
JR Z,PRDNEWLINE
|
|
LD DE,DIRSEP
|
|
JP C_WRITESTR
|
|
PRDNEWLINE LD E,13
|
|
JP C_WRITE
|
|
DIRSEP DEFM ' $'
|
|
|
|
; Wildcard ERA always asks before deleting, including ERA *.COM.
|
|
; This is deliberately safer than stock CP/M 2.2, which prompts only
|
|
; for ERA *.*. FCB1 contains the wildcard-expanded '?' characters.
|
|
ERASE LD HL,FCB1NAM
|
|
CALL ISWILD
|
|
OR A
|
|
JR Z,ERADO
|
|
LD DE,BYEASK
|
|
CALL CCPYESNO
|
|
OR A
|
|
RET NZ
|
|
ERADO LD HL,FILEBUF
|
|
LD (DMAADDR),HL
|
|
LD DE,FCB1
|
|
CALL F_DELETE
|
|
OR A
|
|
RET Z
|
|
LD A,(ROERROR) ; BDOS already printed R/O reason
|
|
OR A
|
|
RET NZ
|
|
LD DE,NFMSG
|
|
JP C_WRITESTR
|
|
|
|
; Rename a file
|
|
; Here we need to build our own 'special' FCB using the x:dest=src command line
|
|
; Maniplate the command tail to replace the leading space with char count of
|
|
; first string and the = with char count of second string. Then use NAME2FCB
|
|
; twice to build the 'special' FCB used by F_RENAME.
|
|
RENAME LD HL,FILEBUF ; Command tail is in filebuf
|
|
LD C,(HL) ; Read length of string
|
|
INC C ; Makes length compare easier
|
|
LD B,0 ; Use B to count chars
|
|
RENL1 INC HL ; Advance to next char
|
|
INC B ; Increase char count
|
|
LD A,B ; Copy to A for compare
|
|
CP C ; Compare with string length
|
|
JP Z,RENS4 ; If at end then bad format
|
|
LD A,(HL) ; Fetch char
|
|
CP '=' ; See if it is equals sign
|
|
JR Z,RENS1 ; Handle '=' sign
|
|
JR RENL1 ; Loop
|
|
RENS1 LD A,B ; Get char count
|
|
SUB 2
|
|
JP Z,RENS4 ; Empty destination is invalid
|
|
; For initial space and '='
|
|
LD DE,FILEBUF ; Will write through ptr DE
|
|
INC DE ; Advance to initial space
|
|
LD (DE),A ; Store length of first string
|
|
LD (TEMPWORD),HL ; Store address of '=' sign
|
|
RENL2 INC HL ; Advance to next char
|
|
INC B ; Increase char count
|
|
LD A,B ; Copy to A for compare
|
|
CP C ; Compare with string length
|
|
JR Z,RENS3 ; If at end then jump out
|
|
JR RENL2 ; Loop
|
|
RENS3 LD A,B ; Get overall char count into A
|
|
SUB 4 ; Compensate for extra chars
|
|
LD HL,FILEBUF ; Get char count of first string
|
|
INC HL ; ...
|
|
SUB (HL) ; Subtract from char count
|
|
LD HL,(TEMPWORD) ; Address of '=' sign
|
|
LD (HL),A ; Store length of second string
|
|
OR A
|
|
JP Z,RENS4 ; Empty source is invalid
|
|
|
|
LD HL,FCB2 ; Set DMAADDR for destination
|
|
LD (DMAADDR),HL ; ...
|
|
LD HL,FILEBUF ; Set HL to dest file string
|
|
INC HL ; ...
|
|
CALL DRVLETTER ; Handle any x: drive letter prefix
|
|
LD C,0 ; NAME2FCB should not do size lookup
|
|
CALL NAME2FCB ; Make FCB for source file
|
|
|
|
LD HL,FCB1 ; Set DMAADDR for source
|
|
LD (DMAADDR),HL ; ...
|
|
LD HL,(TEMPWORD) ; Set HL to source file string
|
|
LD C,0 ; NAME2FCB should not do size lookup
|
|
CALL NAME2FCB ; Make FCB for source file
|
|
LD A,(FCB2) ; Get drive number of destination
|
|
LD (FCB1),A
|
|
LD HL,FCB1NAM ; Neither REN filename may be ambiguous
|
|
CALL ISWILD
|
|
OR A
|
|
JP NZ,RENS4
|
|
LD HL,FCB2NAM
|
|
CALL ISWILD
|
|
OR A
|
|
JP NZ,RENS4
|
|
|
|
LD DE,FCB1 ; Pass address of FCB to F_RENAME
|
|
LD HL,FILEBUF ; Set DMAADDR to point to FILEBUF
|
|
LD (DMAADDR),HL ; ...
|
|
CALL F_RENAME ; Perform the rename
|
|
|
|
CP 0 ; See if it was successful
|
|
JR NZ,RENS5 ; If not, display message
|
|
RET ;
|
|
|
|
RENS4 JP CCPBAD
|
|
RENS5 LD A,(ROERROR)
|
|
OR A
|
|
RET NZ ; BDOS already printed R/O reason
|
|
LD DE,RNEMSG ; 'Rename error' message
|
|
CALL C_WRITESTR ; ...
|
|
RET
|
|
|
|
; Show a test file on the console
|
|
; File to open is in FCB1
|
|
TYPEFILE LD DE,FCB1 ; Point to FCB1
|
|
LD HL,FILEBUF ; Set DMAADDR to FILEBUF
|
|
LD (DMAADDR),HL ; ...
|
|
CALL F_OPEN ; Open the file with wildcard search
|
|
CP 0 ;
|
|
JR NZ,TFOERR ; Open error
|
|
TFL1 LD HL,FILEBUF ; Write ^Z to buffer in case 0 bytes read
|
|
LD A,26 ; ...
|
|
LD (HL),A ; ...
|
|
LD DE,FCB1 ; Point to FCB1
|
|
CALL F_READ ; Read records until done
|
|
CP 0 ; Check return code from F_READ
|
|
JR NZ,TFS1 ; If non-zero jump out of loop
|
|
CALL TYPEBLK ; Display block on screen
|
|
JR TFL1 ; Loop
|
|
TFS1 CP 1 ; Check return code from F_READ
|
|
JR NZ,TFLERR ; If not EOF (1), then error
|
|
CALL TYPEBLK ; Display last block on screen
|
|
CALL F_CLOSE ; Close the file
|
|
LD E,13 ; Carriage return
|
|
CALL C_WRITE ;
|
|
RET ;
|
|
TFOERR LD DE,NFMSG ; 'Not found' message
|
|
CALL C_WRITESTR ; ...
|
|
RET ;
|
|
TFLERR CALL F_CLOSE ; Close the file
|
|
LD DE,REMSG ; 'Read error' message
|
|
CALL C_WRITESTR ; ...
|
|
RET
|
|
|
|
; Display one 128-byte CP/M record, stopping at ^Z.
|
|
; C_WRITE preserves BC and DE, so only the pointer needs protection.
|
|
TYPEBLK LD HL,FILEBUF
|
|
LD B,128
|
|
TBL1 LD A,(HL)
|
|
CP 26
|
|
RET Z
|
|
LD E,A
|
|
PUSH HL
|
|
CALL C_WRITE
|
|
POP HL
|
|
INC HL
|
|
DJNZ TBL1
|
|
RET
|
|
|
|
; AUX56 SAVEFILE/SVPAGES relocated to MAIN $4000 (free of AUX snapshot).
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; Additional private scratch space for BDOS
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; ProDOS Parameter Lists
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; Parameter list for ProDOS CREATE call
|
|
FMMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0C0H ; ProDOS CREATE call
|
|
DEFW FMMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
FMMLIPL DEFB 7 ; ProDOS PL: Seven parameters
|
|
FMMLIP DEFW PATHBUF+OFFSET ; ProDOS PL: Pointer to path in 6502 addr
|
|
FMMLIA DEFB 0C3H ; ProDOS PL: Access (0C3H full access)
|
|
FMMLIT DEFB 0 ; ProDOS PL: File type (0 means typeless)
|
|
FMMLIAT DEFW 0000H ; ProDOS PL: Aux file type (always 0000H)
|
|
FMMLIS DEFB 1 ; ProDOS PL: Storage type (1 for file)
|
|
FMMLICD DEFW 0000H ; ProDOS PL: Create date (always 0000H)
|
|
FMMLICT DEFW 0000H ; ProDOS PL: Create time (always 0000H)
|
|
|
|
; Parameter list for ProDOS DESTROY call
|
|
FDMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0C1H ; ProDOS DESTROY call
|
|
DEFW FDMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
FDMLIPL DEFB 1 ; ProDOS PL: Seven parameters
|
|
FDMLIP DEFW PATHBUF+OFFSET ; ProDOS PL: Pointer to path in 6502 addr
|
|
|
|
; Parameter list for ProDOS RENAME call
|
|
FRNMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0C2H ; ProDOS RENAME call
|
|
DEFW FRNMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
FRNMLIPL DEFB 2 ; ProDOS PL: Two parameters
|
|
FRNMLIP1 DEFW PATHBUF+OFFSET ; ProDOS PL: Pointer to path in 6502 addr
|
|
FRNMLIP2 DEFW PATHBUF2+OFFSET ; ProDOS PL: Pointer to path in 6502 addr
|
|
|
|
; Parameter list for ProDOS GET_FILE_INFO call
|
|
GFIMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0C4H ; ProDOS GET_FILE_INFO call
|
|
DEFW GFIMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
GFIMLIPL DEFB 0AH ; ProDOS PL: Ten parameters
|
|
GFIMLIP DEFW PATHBUF+OFFSET ; ProDOS PL: Pointer to path in 6502 addr
|
|
GFIMLIAC DEFB 0 ; ProDOS PL: Access
|
|
GFIMLIT DEFB 0 ; ProDOS PL: File type
|
|
GFIMLIAT DEFW 0000H ; ProDOS PL: Aux type
|
|
GFIMLIS DEFB 1 ; ProDOS PL: Storage type
|
|
GFIMLIBU DEFW 0000H ; ProDOS PL: Blocks used
|
|
GFIMLIMD DEFW 0000H ; ProDOS PL: Modification date
|
|
GFIMLIMT DEFW 0000H ; ProDOS PL: Modification time
|
|
GFIMLICD DEFW 0000H ; ProDOS PL: Create date
|
|
GFIMLICT DEFW 0000H ; ProDOS PL: Create time
|
|
|
|
; ProDOS $C3 SET_FILE_INFO uses the same fields as GET_FILE_INFO
|
|
; through modification time. F_ATTRIB sets count=7 temporarily.
|
|
SFIMLI DEFB 20H,00H,0BFH
|
|
DEFB 0C3H
|
|
DEFW GFIMLIPL+OFFSET
|
|
DEFB 60H
|
|
|
|
; Parameter list for ProDOS OPEN call
|
|
FOMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0C8H ; ProDOS OPEN call
|
|
DEFW FOMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
FOMLIPL DEFB 3 ; ProDOS PL: Three parameters
|
|
FOMLIP DEFW PATHBUF+OFFSET ; ProDOS PL: pointer to path in 6502 addr
|
|
FOMLII DEFW 0000H ; ProDOS PL: pointer to IO buffer
|
|
FOMLIN DEFB 0 ; ProDOS PL: File reference number
|
|
|
|
; Parameter list for ProDOS READ call
|
|
FRMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0CAH ; ProDOS READ call
|
|
DEFW FRMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
FRMLIPL DEFB 4 ; ProDOS PL: Four parameters
|
|
FRMLIN DEFB 0 ; ProDOS PL: File reference number
|
|
FRMLIDB DEFW 0000H ; ProDOS PL: Data buffer
|
|
FRMLIRC DEFW 128 ; ProDOS PL: Request count (bytes to read)
|
|
FRMLITC DEFW 0000H ; ProDOS PL: Number of bytes transferred
|
|
|
|
; Parameter list for ProDOS WRITE call
|
|
FWMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0CBH ; ProDOS WRITE call
|
|
DEFW FWMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
FWMLIPL DEFB 4 ; ProDOS PL: Four parameters
|
|
FWMLIN DEFB 0 ; ProDOS PL: File reference number
|
|
FWMLIDB DEFW 0000H ; ProDOS PL: Data buffer
|
|
FWMLIRC DEFW 128 ; ProDOS PL: Request count (bytes to read)
|
|
FWMLITC DEFW 0000H ; ProDOS PL: Number of bytes transferred
|
|
|
|
; Parameter list for ProDOS CLOSE call
|
|
FCMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0CCH ; ProDOS CLOSE call
|
|
DEFW FCMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
FCMLIPL DEFB 1 ; ProDOS PB: One parameter
|
|
FCMLIN DEFB 0 ; ProDOS PB: File reference number
|
|
|
|
; Parameter list for ProDOS FLUSH call
|
|
FLMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0CDH ; ProDOS FLUSH call
|
|
DEFW FLMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
FLMLIPL DEFB 1 ; ProDOS PL: One parameter
|
|
FLMLIN DEFB 0 ; ProDOS PL: File ref num (0 = all files)
|
|
|
|
; Parameter list for ProDOS SET_MARK call
|
|
SMMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0CEH ; ProDOS SET_MARK call
|
|
DEFW SMMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
SMMLIPL DEFB 2 ; ProDOS PL: Two parameters
|
|
SMMLIN DEFB 0 ; ProDOS PL: File reference number
|
|
SMMLIP1 DEFB 0 ; ProDOS PL: Position (LSB)
|
|
SMMLIP2 DEFB 0 ; ProDOS PL: Position
|
|
SMMLIP3 DEFB 0 ; ProDOS PL: Position (MSB)
|
|
|
|
; Parameter list for ProDOS SET_EOF call
|
|
SEMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0D0H ; ProDOS SET_EOF call
|
|
DEFW SEMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
SEMLIPL DEFB 2 ; ProDOS PL: Two parameters
|
|
SEMLIN DEFB 0 ; ProDOS PL: File reference number
|
|
SEMLIE1 DEFB 0 ; ProDOS PL: EOF position (LS-byte)
|
|
SEMLIE2 DEFB 0 ; ProDOS PL: EOF position
|
|
SEMLIE3 DEFB 0 ; ProDOS PL: EOF position (MS-byte)
|
|
|
|
; Parameter list for ProDOS GET_EOF call
|
|
GEMLI DEFB 20H,00H,0BFH ; JSR $BF00 in 6502 code
|
|
DEFB 0D1H ; ProDOS GET_EOF call
|
|
DEFW GEMLIPL+OFFSET ; Pointer to parm list in 6502 addr space
|
|
DEFB 60H ; RTS in 6502 code
|
|
GEMLIPL DEFB 2 ; ProDOS PL: Two parameters
|
|
GEMLIN DEFB 0 ; ProDOS PL: File reference number
|
|
GEMLIE1 DEFB 0 ; ProDOS PL: EOF position (LS-byte)
|
|
GEMLIE2 DEFB 0 ; ProDOS PL: EOF position
|
|
GEMLIE3 DEFB 0 ; ProDOS PL: EOF position (MS-byte)
|
|
|
|
; FCB used for opening ProDOS directory corresponding to drive
|
|
DFCB ; File control block for directory
|
|
DFCBDRV DEFB 00H ; FCB Drive (0 current, 1 A:, 2 B: etc)
|
|
DFCBNAM DEFM ' ' ; FCB filename and extension (all spaces)
|
|
DFCBEX DEFB 00H ; FCB extent field
|
|
DFCBS1 DEFB 00H ; FCB S1 field
|
|
DFCBS2 DEFB 00H ; FCB S2 field
|
|
DFCBRC DEFB 00H ; FCB RC field
|
|
DFCBMAP DEFS 16 ; Map of blocks in file
|
|
DFCBSR DEFB 0 ; FCB seq record number
|
|
DFCBRR DEFB 0,0,0 ; FCB rand record number
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
; End of ProDOS Parameter Lists
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
; Drive parameter block (CP/M 2.2 format)
|
|
; See Johnson-Laird Pg 33-34
|
|
DPB DEFW 32 ; # 128 byte records per track (for Disk II)
|
|
DEFB 4 ; Block shift (2048 byte allocation blocks)
|
|
DEFB 15 ; Block mask (2048 byte allocation blocks)
|
|
DEFB 0 ; Extent mask (16K extents)
|
|
DPBDSM DEFW 511 ; 2K DSM set by V_GETFREE (up to 16382)
|
|
DEFW 255 ; # directory entries
|
|
DEFB 0 ; Directory allocation bitmap byte 1
|
|
DEFB 0 ; Directory allocation bitmap byte 2
|
|
DEFW 0 ; Checksum vector size
|
|
DEFW 0 ; # of reserved tracks
|
|
|
|
; 2048-byte CP/M allocation vector supports up to 16384 x 2 KiB
|
|
; allocation blocks, i.e. ProDOS's 32 MiB maximum. This is synthetic
|
|
; accounting; no claim is made that these are unique physical blocks.
|
|
;
|
|
; Reuse 6502 MAIN $0800-$0FFF (Apple II text page 2 and spare RAM),
|
|
; mapped to Z80 $F800-$FFFF. The 6502 direct console uses text page 1.
|
|
; This avoids sacrificing another 2 KiB of precious Z80 TPA or colliding
|
|
; with the live ProDOS I/O buffers at Z80 $9800-$A7FF.
|
|
;
|
|
; $0800-$09FF is the temporary ProDOS block buffer and GET_PREFIX buffer;
|
|
; ON_LINE uses $0880. Consequently V_GETFREE builds the allocation map
|
|
; ONLY after the final READ_BLOCK completes. A later V_GETFREE refreshes it.
|
|
ALLOCVEC EQU 0F800H
|
|
VOLBUF EQU 0F800H
|
|
VLEFT DEFW 0 ; Volume blocks remaining in scan
|
|
VFREE DEFW 0 ; Number of actual free 512-byte blocks
|
|
VTOTAL DEFW 0 ; ProDOS header total blocks (800K => $0640)
|
|
VNBLOCKS DEFW 0 ; 2K blocks (800K => 400, 32MB => 16383)
|
|
VFEND DEFW 0 ; Bitmap byte for highest CP/M block
|
|
VFMASK DEFB 0 ; Bit mask at VFEND
|
|
VBUDGET DEFW 0 ; Maximum bits available (excl 0..8)
|
|
VERR DEFB 0 ; Last ProDOS error, $FE if header invalid
|
|
|
|
; GET_PREFIX (ProDOS $C7). Returns Pascal absolute path in $0800.
|
|
VPMLI DEFB 20H,00H,0BFH
|
|
DEFB 0C7H
|
|
DEFW VPPL+OFFSET
|
|
DEFB 60H
|
|
VPPL DEFB 1
|
|
DEFW 0800H
|
|
|
|
; ON_LINE (ProDOS $C5), unit=0 enumerates mounted devices.
|
|
; 256-byte list at $0880, well away from the 64-byte prefix at $0800.
|
|
VOMLI DEFB 20H,00H,0BFH
|
|
DEFB 0C5H
|
|
DEFW VOPL+OFFSET
|
|
DEFB 60H
|
|
VOPL DEFB 2
|
|
DEFB 0 ; 0 = enumerate all units
|
|
DEFW 0880H
|
|
|
|
; GET_FILE_INFO (ProDOS $C4), only for the no-prefix fallback.
|
|
; The temporary absolute pathname lives at $0800.
|
|
VFMLI DEFB 20H,00H,0BFH
|
|
DEFB 0C4H
|
|
DEFW VFPL+OFFSET
|
|
DEFB 60H
|
|
VFPL DEFB 0AH
|
|
DEFW 0800H
|
|
DEFB 0 ; Access returned
|
|
VFTYPE DEFB 0 ; File type returned ($0F = DIR)
|
|
DEFS 13 ; Remaining GET_FILE_INFO fields
|
|
|
|
V_HASPFX DEFB 0
|
|
V_NAMELEN DEFB 0
|
|
; The unit in VRUNIT belongs to the one ProDOS volume backing BP/M.
|
|
VUCVALID DEFB 0
|
|
|
|
; ProDOS READ_BLOCK call, read-only, used to inspect volume bitmap.
|
|
VRMLI DEFB 20H,00H,0BFH ; JSR $BF00
|
|
DEFB 080H ; ProDOS READ_BLOCK
|
|
DEFW VRPL+OFFSET
|
|
DEFB 60H ; RTS
|
|
VRPL DEFB 3
|
|
VRUNIT DEFB 0 ; Discovered once; reused for all CP/M drives
|
|
DEFW 0800H ; physical 6502 address (NOT VOLBUF+OFFSET)
|
|
VRBLK DEFW 0 ; Requested ProDOS 512-byte block
|
|
|
|
; ProDOS path buffers and directory workspace have been relocated to MAIN
|
|
; $4500.., in the reclaimed (former AUX LC snapshot) space.
|
|
|
|
; Two 1024 byte ProDOS I/O buffers, 1K spare, then the 1K Z80 stack
|
|
; The two I/O buffers start on page boundaries
|
|
; ProDOS occupies the space from $BF00 up (in 6502 addresses)
|
|
; 6502 code is loaded at $B800 for now
|
|
; RAM $A400-$A7FF is reserved for Z80 stack, not file I/O.
|
|
|
|
|
|
;ORG 9000H ; Try this - WORKS
|
|
|
|
ORG 9800H ; Try this - WORKS
|
|
IOBUF1 DEFS 1024
|
|
IOBUF2 DEFS 1024
|
|
; Put the read completion shim in the otherwise spare $A000-$A3FF area.
|
|
; It cannot overlap ProDOS buffers or the dedicated $A400-$A7FF stack.
|
|
; Keeping it here actually saves 6 bytes in the crowded resident area.
|
|
IOFREE
|
|
; Both F_READ and F_READRAND use this helper after SET_MARK succeeds.
|
|
; ProDOS READ sets FRMLITC even if EOF is reached before 128 bytes.
|
|
; $4C is EOF only when the transfer count is zero; a short read returns
|
|
; success and must be padded in the CP/M DMA buffer, not on disk.
|
|
; Return ProDOS status unchanged for errors other than $4C;
|
|
; otherwise return 0 if 1..128 bytes were read and $4C if none.
|
|
READ128 XOR A
|
|
LD (FRMLITC),A ; No stale transfer count on error
|
|
LD (FRMLITC+1),A
|
|
LD HL,FRMLI
|
|
CALL PRODOS
|
|
OR A
|
|
JR Z,R128_COUNT
|
|
CP 04CH
|
|
RET NZ ; Preserve bad FRN / other error
|
|
R128_COUNT LD A,(FRMLITC+1)
|
|
OR A ; 128 requested: high byte must be 0
|
|
JR NZ,R128_BAD
|
|
LD A,(FRMLITC)
|
|
OR A
|
|
JR Z,R128_EOF
|
|
CP 128
|
|
JR Z,R128_OK
|
|
JR NC,R128_BAD ; ProDOS cannot return >128 bytes
|
|
LD C,A ; Bytes read (1..127)
|
|
LD B,0
|
|
LD HL,(DMAADDR)
|
|
ADD HL,BC ; First unfilled byte
|
|
LD A,128
|
|
SUB C
|
|
LD B,A ; Number of bytes to pad (1..127)
|
|
LD A,01AH ; CP/M text EOF marker (^Z)
|
|
R128_FILL LD (HL),A
|
|
INC HL
|
|
DJNZ R128_FILL
|
|
R128_OK XOR A
|
|
RET
|
|
R128_EOF LD A,04CH
|
|
RET
|
|
R128_BAD LD A,0FFH
|
|
RET
|
|
|
|
; ProDOS's F_SFIRST/F_SNEXT leave the directory open between matches.
|
|
; Once exhausted, release its physical ProDOS handle and associated 1K
|
|
; OPEN buffer. The caller's DMA address and wildcard FCB are unchanged.
|
|
; On an already-closed/failed OPEN, F_CLOSE is harmless (DFCB S2=0).
|
|
; Keep this helper in the spare region rather than crowding $9800.
|
|
SEARCH_EOF LD DE,DFCB
|
|
CALL F_CLOSE
|
|
LD A,0FFH ; CP/M search: no more entries
|
|
LD L,A ; Also return error in L
|
|
RET
|
|
|
|
|
|
; =====================================================================
|
|
; AUX56 PHASE 1: restricted ordinary .COM loader / LC bank gateway.
|
|
; This is deliberately isolated from the working main-bank filesystem.
|
|
; Full aux FCB/DMA pointer translation is NOT implemented.
|
|
; Code and scratch storage reside in the spare $A000-$A3FF main bank.
|
|
; =====================================================================
|
|
|
|
; Size-to-allocation-map synthesis, relocated from resident BDOS.
|
|
; All internal JR/DJNZ targets remain within this block.
|
|
N2FMAP ADD A,15 ; Round record count up to 16
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
RRCA
|
|
AND 0FH ; (records+15)/16 (0..8)
|
|
LD C,A
|
|
LD HL,(DMAADDR)
|
|
LD DE,16
|
|
ADD HL,DE ; Offset 16: start of allocation map
|
|
LD B,8 ; Eight 16-bit block entries
|
|
LD D,1 ; Fake block number, nonzero
|
|
N2FMLP LD A,C
|
|
OR A
|
|
JR Z,N2FMZERO
|
|
LD A,D ; Populated allocation entry
|
|
DEC C
|
|
JR N2FMSTORE
|
|
N2FMZERO XOR A ; Unused block entry
|
|
N2FMSTORE LD (HL),A ; Little endian, low byte
|
|
INC HL
|
|
XOR A
|
|
LD (HL),A ; High byte of block number
|
|
INC HL
|
|
INC D
|
|
DJNZ N2FMLP
|
|
RET
|
|
|
|
ORG 0A080H
|
|
; Auxiliary page zero: WBOOT -> LC DF03, BDOS -> LC DE00.
|
|
AUXPZERO DEFB 0C3H,003H,0DFH,0,0,0C3H,000H,0DEH
|
|
AUXDEST DEFW 0100H
|
|
AUXEXCD DEFB 0
|
|
|
|
; AUX LC BIOS jump table at DF00. BDOS JP lives at DE00.
|
|
; Three-byte BDOS stub copied to AUX LC $DE00 before the BIOS image.
|
|
LCBDJP JP LCGATE
|
|
LCIMG
|
|
JP LCNOP
|
|
JP LCWBOOT
|
|
JP LCBCST
|
|
JP LCBCIN
|
|
JP LCBCOUT
|
|
JP LCBLST
|
|
JP LCBPUN
|
|
JP LCBRDR
|
|
JP LCNOP
|
|
JP LCBNULL
|
|
JP LCNOP
|
|
JP LCNOP
|
|
JP LCNOP
|
|
JP LCBERR
|
|
JP LCBERR
|
|
JP LCBZERO
|
|
JP LCBTRAN
|
|
JP LCGATE
|
|
LIENT XOR A
|
|
LD SP,LCBASE
|
|
LD (WRAUX),A
|
|
LD (RDAUX),A
|
|
CALL 0100H
|
|
JP LCFIN
|
|
LIGATE XOR A
|
|
LD (WRMAIN),A
|
|
LD (RDMAIN),A
|
|
JP AXGATE
|
|
LIFIN XOR A
|
|
LD (WRMAIN),A
|
|
LD (RDMAIN),A
|
|
JP AXFIN
|
|
LIRES LD (WRAUX),A
|
|
LD (RDAUX),A
|
|
RET
|
|
LIPEEK XOR A
|
|
LD (RDAUX),A
|
|
LD A,(HL)
|
|
LD (RDMAIN),A
|
|
RET
|
|
LIA2M XOR A
|
|
LD (RDAUX),A
|
|
LDIR
|
|
LD (RDMAIN),A
|
|
RET
|
|
LINOP RET
|
|
LIBCST PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
LD C,0F1H
|
|
CALL LCGATE
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
RET
|
|
LIBCIN PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
LD C,0F2H
|
|
CALL LCGATE
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
RET
|
|
LIBCOUT PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
LD E,C
|
|
LD C,0F3H
|
|
CALL LCGATE
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
RET
|
|
; AUX LC must preserve caller registers while switching via LCGATE.
|
|
LIBLST PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
LD E,C
|
|
LD C,0F4H
|
|
CALL LCGATE
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
RET
|
|
LIBPUN PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
LD E,C
|
|
LD C,0F5H
|
|
CALL LCGATE
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
RET
|
|
LIBRDR PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
LD C,0F6H
|
|
CALL LCGATE
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
RET
|
|
LIBNULL LD HL,0
|
|
RET
|
|
LIBERR LD A,1
|
|
RET
|
|
LIBZERO XOR A
|
|
RET
|
|
LIBTRAN LD H,B
|
|
LD L,C
|
|
RET
|
|
; Explicit AUX RST 0 / JP 0000H is WBOOT, not ordinary .COM RET.
|
|
; Select MAIN RAM before updating the MAIN-owned AXQUIT flag.
|
|
LIWBOOT XOR A
|
|
LD (WRMAIN),A
|
|
LD (RDMAIN),A
|
|
INC A
|
|
LD (AXQUIT),A
|
|
JP AXFIN
|
|
LIEND
|
|
|
|
; LC runtime addresses; only eight-character distinct symbol prefixes.
|
|
LCREN EQU LCBASE+(LIENT-LCIMG)
|
|
LCGATE EQU LCBASE+(LIGATE-LCIMG)
|
|
LCFIN EQU LCBASE+(LIFIN-LCIMG)
|
|
LCWBOOT EQU LCBASE+(LIWBOOT-LCIMG)
|
|
LCRES EQU LCBASE+(LIRES-LCIMG)
|
|
LCPEE0 EQU LCBASE+(LIPEEK-LCIMG)
|
|
LCA2MR EQU LCBASE+(LIA2M-LCIMG)
|
|
LCNOP EQU LCBASE+(LINOP-LCIMG)
|
|
LCBCST EQU LCBASE+(LIBCST-LCIMG)
|
|
LCBCIN EQU LCBASE+(LIBCIN-LCIMG)
|
|
LCBCOUT EQU LCBASE+(LIBCOUT-LCIMG)
|
|
LCBLST EQU LCBASE+(LIBLST-LCIMG)
|
|
LCBPUN EQU LCBASE+(LIBPUN-LCIMG)
|
|
LCBRDR EQU LCBASE+(LIBRDR-LCIMG)
|
|
LCBNULL EQU LCBASE+(LIBNULL-LCIMG)
|
|
LCBERR EQU LCBASE+(LIBERR-LCIMG)
|
|
LCBZERO EQU LCBASE+(LIBZERO-LCIMG)
|
|
LCBTRAN EQU LCBASE+(LIBTRAN-LCIMG)
|
|
LCQUIT EQU AXQUIT
|
|
LCLEN EQU LIEND-LCIMG
|
|
|
|
; MAIN staging buffer for 128-byte ProDOS record reads.
|
|
AUXSTAGE DEFS 128
|
|
|
|
; DCHKRO/RODENY relocated to MAIN $4800 with the I/O driver.
|
|
|
|
|
|
|
|
|
|
; =====================================================================
|
|
; Two-slot ProDOS handle cache. The existing $9800/$9C00 1K buffers
|
|
; remain the only OPEN buffers. The directory search and CCP loader
|
|
; use uncached, pinned FRNs: cache eviction only considers CSTAT1/2=1.
|
|
; All lookup keys are pathname length plus <=19 bytes (user/drive/name).
|
|
; CMRU=1/2 is last used slot; eviction prefers the other one.
|
|
; This code occupies the otherwise unused MAIN $A200-$A3FF region.
|
|
; =====================================================================
|
|
ORG 0A200H
|
|
; Look up PATHBUF in cached slots. A=1/2 for a hit, A=0 for miss.
|
|
CACHEFND LD A,(CSTAT1)
|
|
OR A
|
|
JR Z,CF_TWO
|
|
LD DE,CNAME1
|
|
CALL CACHECMP
|
|
JR Z,CF_ONE
|
|
CF_TWO LD A,(CSTAT2)
|
|
OR A
|
|
JR Z,CF_MISS
|
|
LD DE,CNAME2
|
|
CALL CACHECMP
|
|
JR Z,CF_TWON
|
|
CF_MISS XOR A
|
|
RET
|
|
CF_ONE LD A,1
|
|
RET
|
|
CF_TWON LD A,2
|
|
RET
|
|
; Compare Pascal-style paths, including length; ignore stale trailing data.
|
|
CACHECMP LD HL,PATHBUF
|
|
LD A,(HL)
|
|
INC A
|
|
LD B,A
|
|
CC_LOOP LD A,(DE)
|
|
CP (HL)
|
|
RET NZ
|
|
INC HL
|
|
INC DE
|
|
DJNZ CC_LOOP
|
|
XOR A
|
|
RET
|
|
|
|
; A=1 or 2, return cached physical ProDOS reference (no register clobber).
|
|
CACHEREF CP 1
|
|
JR Z,CR_ONE
|
|
LD A,(FRN2)
|
|
RET
|
|
CR_ONE LD A,(FRN1)
|
|
RET
|
|
|
|
; A=slot; register a successful _F_OPEN's pathname as cached.
|
|
CACHEPUT LD (CSLOT),A
|
|
PUSH AF
|
|
LD HL,COPENS
|
|
CALL CINC16
|
|
POP AF
|
|
LD HL,PATHBUF
|
|
LD DE,CNAME1
|
|
CP 1
|
|
JR Z,CP_COPY
|
|
LD DE,CNAME2
|
|
CP_COPY LD BC,20
|
|
LDIR
|
|
LD A,(CSLOT)
|
|
LD (CMRU),A
|
|
CP 1
|
|
JR NZ,CP_TWO
|
|
LD (CSTAT1),A
|
|
RET
|
|
CP_TWO LD A,1
|
|
LD (CSTAT2),A
|
|
RET
|
|
|
|
; A=1/2; physically close slot, clear its FRN and cache flag on success.
|
|
CACHESLT LD (CSLOT),A
|
|
CALL CACHEREF
|
|
LD (FCMLIN),A
|
|
LD HL,FCMLI
|
|
CALL PRODOS
|
|
OR A
|
|
RET NZ
|
|
LD A,(CSLOT)
|
|
CP 1
|
|
JR NZ,CS_TWO
|
|
XOR A
|
|
LD (FRN1),A
|
|
LD (CSTAT1),A
|
|
RET
|
|
CS_TWO XOR A
|
|
LD (FRN2),A
|
|
LD (CSTAT2),A
|
|
RET
|
|
|
|
; Close only a cached handle matching PATHBUF; leave raw directory FRNs.
|
|
CACHECLS CALL CACHEFND
|
|
OR A
|
|
RET Z
|
|
JP CACHESLT
|
|
|
|
; When both buffers are occupied, evict only cached files (never DFCB).
|
|
CACHEEV LD A,(CSTAT1)
|
|
OR A
|
|
JR Z,CE_SECOND
|
|
LD A,(CSTAT2)
|
|
OR A
|
|
JR Z,CE_FIRST
|
|
LD A,(CMRU)
|
|
CP 1
|
|
JR Z,CE_SECOND
|
|
CE_FIRST LD A,1
|
|
JP CACHESLT
|
|
CE_SECOND LD A,(CSTAT2)
|
|
OR A
|
|
JR Z,CE_FAIL
|
|
LD A,2
|
|
JP CACHESLT
|
|
CE_FAIL LD A,0FFH
|
|
RET
|
|
|
|
; Return an available IOBUF (A=slot, HL=buffer), evicting LRU if needed.
|
|
CACHEFREE XOR A
|
|
CALL GETIOADDR
|
|
CP 0FFH
|
|
RET NZ
|
|
CALL CACHEEV
|
|
OR A
|
|
JR NZ,CE_FAIL
|
|
LD HL,CEVICTS
|
|
CALL CINC16
|
|
XOR A
|
|
JP GETIOADDR
|
|
|
|
; Warm boot / AUX termination: flush all ProDOS refs, clear slot metadata.
|
|
CACHEALL XOR A
|
|
LD (FCMLIN),A
|
|
LD HL,FCMLI
|
|
CALL PRODOS
|
|
XOR A
|
|
LD (FRN1),A
|
|
LD (FRN2),A
|
|
LD (FRN3),A
|
|
LD (FRN4),A
|
|
LD (DFCB+0EH),A ; No stale physical directory reference
|
|
LD (CSTAT1),A
|
|
LD (CSTAT2),A
|
|
LD (CMRU),A
|
|
RET
|
|
|
|
CSTAT1 DEFB 0
|
|
CSTAT2 DEFB 0
|
|
CMRU DEFB 0
|
|
CSLOT DEFB 0
|
|
CNAME1 DEFS 20
|
|
CNAME2 DEFS 20
|
|
|
|
ORG 0A3D0H
|
|
; Executable base name (8 bytes) is in PATHBUF2+1 after PARSE.
|
|
TBEXE LD A,(TABMODE)
|
|
CALL TBNPTR
|
|
EX DE,HL
|
|
LD HL,PATHBUF2+1
|
|
LD BC,8
|
|
LDIR
|
|
RET
|
|
; Return HL -> first byte of tab B's 10-byte virtual video metadata.
|
|
TB_METAP LD HL,VSMETAS
|
|
LD DE,10
|
|
LD A,B
|
|
OR A
|
|
RET Z
|
|
TM_NEXT ADD HL,DE
|
|
DEC A
|
|
JR NZ,TM_NEXT
|
|
RET
|
|
|
|
ORG 0A400H
|
|
; Experimental base-69 codec lives at the bottom of the resident stack
|
|
; reservation. Z80 SP still starts at $A800. Check available stack!
|
|
; Zapple-II base-69/base-36 arithmetic prototype for Udo Munk Z80asm.
|
|
; ------------------------------------------------------------------
|
|
; This module is NOT installed into the resident BDOS. It is a standalone
|
|
; callable implementation to be placed in available Z80 RAM.
|
|
;
|
|
; B69ENC: HL = 11 FCB name/extension bytes (no drive byte).
|
|
; DE = 15-byte output buffer.
|
|
; Output is always 15 ASCII bytes: Z.<13 base36 chars>.
|
|
; A = 0 success, FF invalid character.
|
|
; Ignores the FCB attribute bits (high bit).
|
|
;
|
|
; B69DEC: HL = 15 ASCII bytes (already verified ProDOS name length = 15).
|
|
; DE = 11-byte output FCB name/extension field.
|
|
; A = 0 success, FF invalid prefix/base36 or number >=69^11.
|
|
; Caller MUST also validate CP/M name format and reserve Z.<13>.
|
|
;
|
|
; Both entries clobber AF BC DE HL IX; preserve IY; use shared scratch.
|
|
; They are NOT reentrant. Name conversion is serialized inside BDOS.
|
|
;
|
|
; To include in source, place the routines/data in RAM without crossing an
|
|
; ORG boundary or the two ProDOS 1K buffers. Label names are <= 8 chars.
|
|
;
|
|
; Use without inserting a separate ORG here. Start with B69ENC label.
|
|
|
|
B69ENC LD (B69INT),HL
|
|
LD (B69OUT),DE
|
|
CALL B69CLR
|
|
LD HL,(B69INT)
|
|
LD B,11
|
|
E69LOOP LD A,(HL)
|
|
AND 07FH ; ignore CP/M attribute flag
|
|
CP 020H
|
|
JP C,B69ERR
|
|
CP 07FH
|
|
JP NC,B69ERR
|
|
CP 061H ; skip lowercase ASCII a-z
|
|
JR C,E69MAP
|
|
CP 07BH
|
|
JP C,B69ERR
|
|
SUB 26
|
|
E69MAP SUB 020H ; 0..68 base69 digit
|
|
PUSH BC
|
|
PUSH HL
|
|
CALL B69M69
|
|
POP HL
|
|
POP BC
|
|
INC HL
|
|
DJNZ E69LOOP
|
|
LD HL,(B69OUT)
|
|
LD (HL),'Z'
|
|
INC HL
|
|
LD (HL),'.'
|
|
INC HL
|
|
LD (B69OUT),HL ; 13-byte payload beginning
|
|
LD DE,12
|
|
ADD HL,DE ; write digits from right to left
|
|
LD B,13
|
|
E69OUT PUSH BC
|
|
PUSH HL
|
|
LD A,36
|
|
CALL B69DIV ; A=number modulo 36; big /= 36
|
|
POP HL
|
|
POP BC
|
|
CP 10
|
|
JR C,E69NUM
|
|
ADD A,'A'-10
|
|
JR E69PUT
|
|
E69NUM ADD A,'0'
|
|
E69PUT LD (HL),A
|
|
DEC HL
|
|
DJNZ E69OUT
|
|
XOR A
|
|
RET
|
|
|
|
B69DEC LD (B69INT),HL
|
|
LD (B69OUT),DE
|
|
LD A,(HL)
|
|
CP 'Z'
|
|
JP NZ,B69ERR
|
|
INC HL
|
|
LD A,(HL)
|
|
CP '.'
|
|
JP NZ,B69ERR
|
|
INC HL
|
|
LD (B69INT),HL ; first payload character
|
|
CALL B69CLR
|
|
LD HL,(B69INT)
|
|
LD B,13
|
|
D69LOOP LD A,(HL)
|
|
CP '0'
|
|
JP C,B69ERR
|
|
CP 03AH
|
|
JR C,D69NUM
|
|
CP 'A'
|
|
JP C,B69ERR
|
|
CP 05BH
|
|
JP NC,B69ERR
|
|
SUB 'A'-10
|
|
JR D69ADD
|
|
D69NUM SUB '0'
|
|
D69ADD PUSH BC
|
|
PUSH HL
|
|
CALL B69M36
|
|
POP HL
|
|
POP BC
|
|
INC HL
|
|
DJNZ D69LOOP
|
|
LD HL,(B69OUT)
|
|
LD DE,10
|
|
ADD HL,DE
|
|
LD B,11
|
|
D69OUT PUSH BC
|
|
PUSH HL
|
|
LD A,69
|
|
CALL B69DIV ; A=number modulo 69; big /=69
|
|
POP HL
|
|
POP BC
|
|
CP 65 ; first 65 chars map to ASCII 32..96
|
|
JR C,D69LOW
|
|
ADD A,26 ; last 4 chars map to {|}~
|
|
D69LOW ADD A,020H
|
|
LD (HL),A
|
|
DEC HL
|
|
DJNZ D69OUT
|
|
; Reject input >= 69^11 by ensuring quotient becomes zero.
|
|
LD HL,B69BIG
|
|
LD B,9
|
|
D69CHK LD A,(HL)
|
|
OR A
|
|
JP NZ,B69ERR
|
|
INC HL
|
|
DJNZ D69CHK
|
|
XOR A
|
|
RET
|
|
|
|
B69ERR LD A,0FFH
|
|
RET
|
|
|
|
; Clear 9-byte big-endian intermediate integer.
|
|
B69CLR LD HL,B69BIG
|
|
LD B,9
|
|
XOR A
|
|
C69LOOP LD (HL),A
|
|
INC HL
|
|
DJNZ C69LOOP
|
|
RET
|
|
|
|
; BIGNUM = BIGNUM*69 + A (0..68).
|
|
; Carry flows from least-significant (highest-address) byte upwards.
|
|
B69M69 LD C,A
|
|
LD HL,B69BIG+8
|
|
LD B,9
|
|
M69LOOP PUSH HL
|
|
LD L,(HL)
|
|
LD H,0
|
|
LD D,H
|
|
LD E,L ; DE=old byte
|
|
ADD HL,HL ; 2x
|
|
ADD HL,HL ; 4x
|
|
ADD HL,HL ; 8x
|
|
ADD HL,HL ; 16x
|
|
ADD HL,HL ; 32x
|
|
ADD HL,HL ; 64x
|
|
ADD HL,DE ; 65x
|
|
SLA E
|
|
RL D
|
|
SLA E
|
|
RL D ; DE=4x
|
|
ADD HL,DE ; 69x
|
|
LD E,C
|
|
LD D,0
|
|
ADD HL,DE ; plus incoming carry
|
|
LD C,H ; outgoing carry
|
|
LD A,L ; new byte
|
|
POP HL
|
|
LD (HL),A
|
|
DEC HL
|
|
DJNZ M69LOOP
|
|
RET
|
|
|
|
; BIGNUM = BIGNUM*36 + A (0..35).
|
|
B69M36 LD C,A
|
|
LD HL,B69BIG+8
|
|
LD B,9
|
|
M36LOOP PUSH HL
|
|
LD L,(HL)
|
|
LD H,0
|
|
ADD HL,HL
|
|
ADD HL,HL ; 4x
|
|
LD D,H
|
|
LD E,L ; DE=4x
|
|
ADD HL,HL
|
|
ADD HL,HL
|
|
ADD HL,HL ; 32x
|
|
ADD HL,DE ; 36x
|
|
LD E,C
|
|
LD D,0
|
|
ADD HL,DE
|
|
LD C,H
|
|
LD A,L
|
|
POP HL
|
|
LD (HL),A
|
|
DEC HL
|
|
DJNZ M36LOOP
|
|
RET
|
|
|
|
; BIGNUM /= A (base 36 or 69); return remainder in A.
|
|
; Eight quotient bits per byte, MSB first, using shift-and-subtract.
|
|
B69DIV LD (B69BASE),A
|
|
PUSH IX
|
|
LD IX,B69BASE
|
|
LD HL,B69BIG
|
|
LD B,9
|
|
LD C,0 ; remainder so far
|
|
V69BYTE PUSH BC ; save byte count
|
|
LD E,(HL) ; original dividend byte
|
|
LD D,0 ; new quotient byte
|
|
LD B,8
|
|
V69BIT SLA E ; incoming dividend bit -> carry
|
|
LD A,C
|
|
ADC A,A ; rem*2 + incoming bit, 0..137
|
|
CP (IX+0) ; compare to divisor
|
|
JR C,V69ZERO
|
|
SUB (IX+0)
|
|
LD C,A ; updated remainder
|
|
SCF ; quotient bit 1
|
|
JR V69Q
|
|
V69ZERO LD C,A
|
|
OR A ; quotient bit 0 (clear carry)
|
|
V69Q RL D ; append quotient bit
|
|
DJNZ V69BIT
|
|
LD (HL),D
|
|
INC HL
|
|
LD A,C ; preserve remainder while popping counter
|
|
POP BC
|
|
LD C,A
|
|
DJNZ V69BYTE
|
|
POP IX
|
|
LD A,C
|
|
RET
|
|
|
|
B69INT DEFW 0
|
|
B69OUT DEFW 0
|
|
B69BASE DEFB 0
|
|
B69BIG DEFS 9
|
|
|
|
; Check whether an FCB name can be represented *unchanged* on ProDOS.
|
|
; HL-> eleven FCB name bytes. A=0 normal; FF requires base69 escape.
|
|
; B,C,DE,HL clobbered. Name and extension must be canonical
|
|
; (spaces only at the end of each field), no lower-case or punctuation.
|
|
B69NEED LD A,(HL)
|
|
AND 07FH
|
|
CP 'A'
|
|
JR C,B69REQ
|
|
CP 'Z'+1
|
|
JR NC,B69REQ
|
|
LD B,8
|
|
CALL B69DIR
|
|
RET NZ
|
|
LD B,3
|
|
CALL B69DIR
|
|
RET
|
|
B69DIR LD E,0 ; seen-padding marker
|
|
B69DL LD A,(HL)
|
|
AND 07FH
|
|
CP ' '
|
|
JR Z,B69PAD
|
|
LD D,A
|
|
LD A,E
|
|
OR A
|
|
JR NZ,B69REQ
|
|
LD A,D
|
|
CP '0'
|
|
JR C,B69REQ
|
|
CP '9'+1
|
|
JR C,B69DG
|
|
CP 'A'
|
|
JR C,B69REQ
|
|
CP 'Z'+1
|
|
JR NC,B69REQ
|
|
JR B69DG
|
|
B69PAD LD E,1
|
|
B69DG INC HL
|
|
DJNZ B69DL
|
|
XOR A
|
|
RET
|
|
B69REQ LD A,0FFH
|
|
RET
|
|
|
|
; Validate a decoded name: printable uppercase canonical FCB, no CP/M
|
|
; directory metacharacters, must actually require escaping.
|
|
; HL -> 11 decoded bytes; A=0 valid, FF invalid; trashes BC/DE/HL.
|
|
B69ILGL DEFB 03CH,03EH,02CH,02EH,03BH,03AH,03DH,03FH,02AH,05BH,05DH
|
|
B69BAD DEFB 0
|
|
; Bridge from FCB2PATH; keep IX/IY and source DE unchanged.
|
|
; Returns A=0 for ordinary name, FF if encoded; DE preserved.
|
|
B69PTH PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
CALL B69NEED
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
OR A
|
|
RET Z
|
|
; Only canonical CP/M 8.3 names can round-trip through DIR.
|
|
PUSH BC
|
|
PUSH DE
|
|
PUSH HL
|
|
CALL B69CAN
|
|
POP HL
|
|
POP DE
|
|
POP BC
|
|
OR A
|
|
JR NZ,B69PFAL
|
|
PUSH BC ; preserve variable user-directory prefix length
|
|
PUSH DE
|
|
PUSH IX
|
|
POP DE
|
|
CALL B69ENC
|
|
POP DE
|
|
POP BC
|
|
OR A
|
|
JR NZ,B69PFAL
|
|
LD A,C ; prefix length (2..4)
|
|
ADD A,15 ; encoded ProDOS component
|
|
LD (IY+0),A
|
|
LD A,0FFH
|
|
RET
|
|
B69PFAL XOR A ; invalid input: safe empty pathname
|
|
LD (IY+0),A
|
|
DEC A
|
|
RET
|
|
|
|
; Decode a 15-character Z.<13> alias, or decline if not our prefix.
|
|
; HL=dirent, DE=DMA FCB drive byte; clobbers BC/DE/HL and IX.
|
|
; Return A=0 valid, A=1 not an alias, A=FF invalid alias (skip entry).
|
|
B69TRY INC HL
|
|
LD A,(HL)
|
|
CP 'Z'
|
|
JR NZ,B69TNOT
|
|
INC HL
|
|
LD A,(HL)
|
|
CP '.'
|
|
JR NZ,B69TNOT
|
|
DEC HL
|
|
INC DE
|
|
CALL B69DEC
|
|
OR A
|
|
JR NZ,B69TBAD
|
|
LD HL,(DMAADDR)
|
|
INC HL
|
|
CALL B69CAN
|
|
OR A
|
|
RET Z
|
|
B69TBAD LD A,0FFH
|
|
LD (B69BAD),A
|
|
RET
|
|
B69TNOT LD A,1
|
|
RET
|
|
B69CAN PUSH HL
|
|
LD A,(HL)
|
|
CP ' '
|
|
JR Z,B69CERR
|
|
LD B,8
|
|
CALL B69PART
|
|
OR A
|
|
JR NZ,B69CERR
|
|
LD B,3
|
|
CALL B69PART
|
|
OR A
|
|
JR NZ,B69CERR
|
|
POP HL
|
|
CALL B69NEED
|
|
OR A
|
|
JP Z,B69REQ ; only non-native CP/M names are escaped
|
|
XOR A
|
|
RET
|
|
B69CERR POP HL
|
|
JP B69REQ
|
|
B69PART LD C,0 ; seen spaces
|
|
B69PLP LD A,(HL)
|
|
CP ' '
|
|
JR Z,B69PSP
|
|
CP 021H
|
|
JP C,B69REQ
|
|
CP 07FH
|
|
JP NC,B69REQ
|
|
LD D,A
|
|
LD A,C
|
|
OR A
|
|
JP NZ,B69REQ
|
|
LD A,D
|
|
CP 061H
|
|
JR C,B69PCK
|
|
CP 07BH
|
|
JP C,B69REQ
|
|
B69PCK PUSH HL
|
|
PUSH BC
|
|
LD HL,B69ILGL
|
|
LD B,11
|
|
B69PCL CP (HL)
|
|
JR Z,B69PERR
|
|
INC HL
|
|
DJNZ B69PCL
|
|
POP BC
|
|
POP HL
|
|
JR B69POK
|
|
B69PERR POP BC
|
|
POP HL
|
|
JP B69REQ
|
|
B69PSP LD C,1
|
|
B69POK INC HL
|
|
DJNZ B69PLP
|
|
XOR A
|
|
RET
|
|
|
|
|
|
B69END
|
|
|
|
; Stack grows from $A800 down to B69END (roughly 0.5KB remaining).
|
|
; Compile guard: negative DEFS if the codec doesn't fit.
|
|
Z80STACK DEFS STCKTOP-B69END
|
|
|
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
|
|
|