From ebd810d5c2914b6ef07a7b4a38be832250648e3f Mon Sep 17 00:00:00 2001 From: markpmlim Date: Wed, 5 Jul 2017 19:33:05 +0800 Subject: [PATCH] Disassembly of ProDOS8 v2.03 --- MLI.SRC/ALLOC.S | 289 ++++++++++++++++ MLI.SRC/BFMGR.S | 402 ++++++++++++++++++++++ MLI.SRC/CCLOCK.S | 137 ++++++++ MLI.SRC/CLOSEEOF.S | 399 +++++++++++++++++++++ MLI.SRC/CREATE.S | 357 +++++++++++++++++++ MLI.SRC/DATATBLS.S | 103 ++++++ MLI.SRC/DESTROY.S | 423 +++++++++++++++++++++++ MLI.SRC/DETREE.S | 259 ++++++++++++++ MLI.SRC/DEVSRCH.S | 667 +++++++++++++++++++++++++++++++++++ MLI.SRC/EQUATES.S | 321 +++++++++++++++++ MLI.SRC/FNDFIL.S | 237 +++++++++++++ MLI.SRC/GLOBALS.S | 200 +++++++++++ MLI.SRC/MEMMGR.S | 298 ++++++++++++++++ MLI.SRC/MLI.MACS.S | 51 +++ MLI.SRC/NEWFNDVOL.S | 453 ++++++++++++++++++++++++ MLI.SRC/POSNOPEN.S | 613 +++++++++++++++++++++++++++++++++ MLI.SRC/PROLDR.S | 823 ++++++++++++++++++++++++++++++++++++++++++++ MLI.SRC/RAM0.S | 372 ++++++++++++++++++++ MLI.SRC/RAM1.S | 43 +++ MLI.SRC/RAM2.S | 98 ++++++ MLI.SRC/READWRITE.S | 692 +++++++++++++++++++++++++++++++++++++ MLI.SRC/RELOC.S | 409 ++++++++++++++++++++++ MLI.SRC/ROM.S | 70 ++++ MLI.SRC/SEL0.S | 356 +++++++++++++++++++ MLI.SRC/SEL1.S | 468 +++++++++++++++++++++++++ MLI.SRC/SEL2.S | 471 +++++++++++++++++++++++++ MLI.SRC/TCLOCK.S | 92 +++++ MLI.SRC/WRKSPACE.S | 180 ++++++++++ MLI.SRC/XDOSMLI.S | 373 ++++++++++++++++++++ MLI.SRC/XRW1.S | 567 ++++++++++++++++++++++++++++++ MLI.SRC/XRW2.S | 644 ++++++++++++++++++++++++++++++++++ P8_SRC.2mg | Bin 0 -> 819264 bytes Readme.md | 6 + 33 files changed, 10873 insertions(+) create mode 100644 MLI.SRC/ALLOC.S create mode 100644 MLI.SRC/BFMGR.S create mode 100644 MLI.SRC/CCLOCK.S create mode 100644 MLI.SRC/CLOSEEOF.S create mode 100644 MLI.SRC/CREATE.S create mode 100644 MLI.SRC/DATATBLS.S create mode 100644 MLI.SRC/DESTROY.S create mode 100644 MLI.SRC/DETREE.S create mode 100644 MLI.SRC/DEVSRCH.S create mode 100644 MLI.SRC/EQUATES.S create mode 100644 MLI.SRC/FNDFIL.S create mode 100644 MLI.SRC/GLOBALS.S create mode 100644 MLI.SRC/MEMMGR.S create mode 100644 MLI.SRC/MLI.MACS.S create mode 100644 MLI.SRC/NEWFNDVOL.S create mode 100644 MLI.SRC/POSNOPEN.S create mode 100644 MLI.SRC/PROLDR.S create mode 100644 MLI.SRC/RAM0.S create mode 100644 MLI.SRC/RAM1.S create mode 100644 MLI.SRC/RAM2.S create mode 100644 MLI.SRC/READWRITE.S create mode 100644 MLI.SRC/RELOC.S create mode 100644 MLI.SRC/ROM.S create mode 100644 MLI.SRC/SEL0.S create mode 100644 MLI.SRC/SEL1.S create mode 100644 MLI.SRC/SEL2.S create mode 100644 MLI.SRC/TCLOCK.S create mode 100644 MLI.SRC/WRKSPACE.S create mode 100644 MLI.SRC/XDOSMLI.S create mode 100644 MLI.SRC/XRW1.S create mode 100644 MLI.SRC/XRW2.S create mode 100644 P8_SRC.2mg create mode 100644 Readme.md diff --git a/MLI.SRC/ALLOC.S b/MLI.SRC/ALLOC.S new file mode 100644 index 0000000..0575439 --- /dev/null +++ b/MLI.SRC/ALLOC.S @@ -0,0 +1,289 @@ +************************************************** +* Free a blk on disk + +Dealloc STX bmCnt ;Save high order address of block to be freed + PHA ;Save it + LDX vcbPtr ; while the bitmap + LDA vcb+vcbTotBlks+1,X; disk address is checked + CMP bmCnt ; to see if it makes sense + PLA ;Restore + BCC DeAllocErr1 ;Branch if impossible + + TAX + AND #$07 ;Get the bit to be OR-ed in + TAY + LDA WhichBit,Y ;(shifting takes 7 bytes, but is slower) + STA noFree ;Save bit pattern + TXA ;Get low block address again + LSR bmCnt + ROR ;Get pointer to byte in bitmap that + LSR bmCnt ; represents the block address + ROR + LSR bmCnt + ROR + STA bmPtr ;Save pointer + LSR bmCnt ;Now transfer bit which specifies which page of bitmap + ROL half + + JSR FindBitMap ;Make absolutely sure we've got the right device + BCS DeAllocErr ;Return any errors + + LDA bmaCurrMap ;What is the current map? + CMP bmCnt ;Is in-core bit map the one we want? + BEQ :1 ;Branch if in-core is correct + + JSR UpdateBitMap ;Put current map away + BCS DeAllocErr ;Pass back any error + + LDA bmCnt ;Get desired map number + LDX vcbPtr + STA vcb+vcbCurrBitMap,X + LDA bmaDev + JSR GetBitMap ;Read it into the buffer + BCS DeAllocErr + +:1 LDY bmPtr ;Index to byte + LSR half + LDA noFree ;(get individual bit) + BCC bmBufHi ;Branch if on page one of bitmap + ORA bmBuf+$100,Y + STA bmBuf+$100,Y + BCS DeAloc3 ;Branch always taken + +bmBufHi ORA bmBuf,Y + STA bmBuf,Y + +DeAloc3 LDA #$80 ;mark bitmap as modified + TSB bmaStat + INC deBlock ;Bump count of blocks deallocated + BNE :11 + INC deBlock+1 +:11 CLC +DeAllocErr RTS + +DeAllocErr1 LDA #damagedBitMap ;bit map block # impossible + SEC ;Say bit map disk address wrong + RTS ;(probably data masquerading as index block) + +************************************************** +* Find a free disk block & allocate it +* Exit +* (Y,A)=disk addr +* (scrtch)=disk addr + +Alloc1Blk JSR FindBitMap ;Get address of bit map in 'bmAdr' + BCS ErrAloc1 ;Branch if error encountered +SrchFree LDY #$00 ;Start search at beginning of bit map block + STY half ;Indicate which half (page) we're searching +GetBits1 LDA bmBuf,Y ;Free blocks are indicated by 'on' bits + BNE BitFound + INY + BNE GetBits1 ;Check all of 'em in first page + + INC half ;Indicate search has progressed to page 2 + INC basVal ;base value=base address/2048 +:loop LDA bmBuf+$100,Y ;Search second half for free block + BNE BitFound + INY + BNE :loop + + INC basVal ;Add 2048 offset for next page + JSR NxtBitMap ;Get next bitmap (if it exists) and update VCB + BCC SrchFree ;Branch if no error encountered +ErrAloc1 RTS ;Return error + +* Calculate blk # represented by first set VBM bit + +BitFound STY bmPtr ;Save index pointer to valid bit group + LDA basVal ;Set up for block address calculation + STA scrtch+1 + TYA ;Get address of bit pattern + ASL ;Multiply this and basVal by 8 + ROL scrtch+1 + ASL + ROL scrtch+1 + ASL + ROL scrtch+1 + TAX ;Now X=low address within 7 of actual address + SEC + LDA half + BEQ Page1Alloc ;Branch if allocating from 1st half + LDA bmBuf+$100,Y ;Get pattern from second page + BCS adcAloc ;Branch always +Page1Alloc LDA bmBuf,Y ;Get bit pattern from first page + +adcAloc ROL ;Find left most 'on' bit + BCS Bounce ;Branch if found + INX ;Adjust low address + BNE adcAloc ;Branch always + +Bounce LSR ;Restore all but left most bit to original position + BCC Bounce ;Loop until mark (set above) moves into Carry + STX scrtch ;Save low address + LDX half ;Which half of bit map? + BNE Page2Alloc + STA bmBuf,Y + BEQ DirtyBitMap ;Branch always +Page2Alloc STA bmBuf+$100,Y ;Update bitmap to show allocated block in use + +DirtyBitMap LDA #$80 ;Indicate map has been + TSB bmaStat ; modified by setting dirty bit + LDY vcbPtr ;Subtract 1 from total free + LDA vcb+vcbFreeBlks,Y; blocks in VCB to account for newly + SBC #$01 ; allocated block (carry is set from 'bounce') + STA vcb+vcbFreeBlks,Y + BCS Ret1Blk ;Branch if hi free count doesn't need adjustment + + LDA vcb+vcbFreeBlks+1,Y;Adjust high count + DEC + STA vcb+vcbFreeBlks+1,Y +Ret1Blk CLC ;Indicate no error encountered + LDA scrtch ;Get address low in A-Reg + LDY scrtch+1 ; & high address in Y-Reg + RTS ;Return address of newly allocated block + +*------------------------------------------------- +* Get next volume bit map block + +NxtBitMap LDY vcbPtr ;Before bumping to next map, + LDA vcb+vcbTotBlks+1,Y; check to be sure there is + LSR ; indeed a next map! + LSR + LSR + LSR + CMP vcb+vcbCurrBitMap,Y;Are there more maps? + BEQ NoMorBM ;Branch if no more to look at + LDA vcb+vcbCurrBitMap,Y + INC ;Add 1 to current map + STA vcb+vcbCurrBitMap,Y + JSR UpdateBitMap + +*------------------------------------------------- +* Read volume bit map block + +FindBitMap LDY vcbPtr ;Get device number + LDA vcb+vcbDevice,Y + CMP bmaDev + BEQ FreshMap + JSR UpdateBitMap ;Save out other volumes' bitmap, and + BCS NoGo + + LDY vcbPtr + LDA vcb+vcbDevice,Y + STA bmaDev ; read in fresh bitmap for this device +FreshMap LDY bmaStat ;Is this one already modified? + BMI BMFound ;Yes, return pointer in 'bmAdr' + JSR GetBitMap ;Otherwise read in fresh bit map + BCS NoGo ;Branch if unsuccessful + +BMFound LDY vcbPtr + LDA vcb+vcbCurrBitMap,Y;Get offset into VBM + ASL + STA basVal ;Save page offset into VBM + CLC ;Indicate all is valid and good! +NoGo RTS + +NoMorBM LDA #volumeFull ;Indicate request can't be filled + SEC ;Indicate error + RTS + +*------------------------------------------------- +* Check point vol bitMap for disk writing + +UpdateBitMap CLC ;Anticipate nothing to do + LDA bmaStat ;Is current map dirty? + BPL NoGo ;No need to do anything + JSR WrtBitMap ;It is dirty, update device! + BCS NoGo ;Error encountered on writing + LDA #$00 + STA bmaStat ;Mark bm buffer as free + RTS ;All done! + +*------------------------------------------------- +* Prepare to read Vol BitMap block + +GetBitMap STA bmaDev ;Read bitmap specified by dev & vcb + LDY vcbPtr ;Get lowest map number with free blocks in it + LDA vcb+vcbCurrBitMap,Y + STA bmaCurrMap ;Associate the offset with the bitmap control block + + CLC ;Add this number to the base + ADC vcb+vcbBitMap,Y ; address of first bit map + STA bmaDskAdr ;Save low address of bit map to be used + LDA vcb+vcbBitMap+1,Y;Now get high disk address of map + ADC #$00 ;Add to this the state of the carry + STA bmaDskAdr+1 ;Save high disk address too + LDA #rdCmd + +* Read/write Volume BitMap block + +DoBMap STA dhpCmd ;Save device command + LDA DevNum ;Preserve current devnum. + PHA + LDA bmaDev ;Get bitmap's device number + STA DevNum + + LDA bmaDskAdr ; & map's disk address + STA blockNum + LDA bmaDskAdr+1 + STA blockNum+1 + + LDA bmBufHi+2 + JSR DoBitMap ;(note: low address is fixed to zero as this is a buffer) + TAX ;Preserve error code, if any + PLA ;Restore the + STA DevNum ; dev # we came in with! + BCC :Ret ;Return devnum if no error + TXA ;Return any errors +:Ret RTS + +*------------------------------------------------- +* Read blk # in A,X regs + +RdBlkAX STA blockNum + STX blockNum+1 + JSR RdGBuf + RTS + +*------------------------------------------------- +* Write Vol BitMap block + +WrtBitMap LDA #wrtCmd ;write bit map + BNE DoBMap ;Branch always + +* Write primary buffer blk + +WrtGBuf LDA #wrtCmd ;Set call for write + BNE SavGCmd ;Branch always + +* Read primary buffer blk + +RdGBuf LDA #rdCmd ;Set call for read + +* Read/Write primary buffer blk + +SavGCmd STA dhpCmd ;Passed to device handler + LDA #>genBuf ;Get high address of general buffer + +*------------------------------------------------- +* Read/Write block + +DoBitMap PHP ;No interupts allowed + SEI + STA bufPtr+1 ;General purpose buffers always + STZ bufPtr ; start on a page boundary + STZ SErr ;Clear global error value + + LDA #$FF ;Also, set to indicate + STA ioAccess ; reg call made to dev handler + LDA DevNum ;transfer the device number for + STA unitNum ; dispatcher to convert to unit number. + JSR DMgr ;Call the driver + BCS :1 ;Branch if error + PLP ;Restore interupts + CLC + RTS + +:1 PLP ;Restore interupts + SEC + RTS \ No newline at end of file diff --git a/MLI.SRC/BFMGR.S b/MLI.SRC/BFMGR.S new file mode 100644 index 0000000..0869464 --- /dev/null +++ b/MLI.SRC/BFMGR.S @@ -0,0 +1,402 @@ + TTL 'ProDOS Block File Manager' +************************************************** +* ProDOS block file manager +* Perform filing or housekeeping functions +* (X)=call # ($00-$13) + +BFMgr LDA Dispatch,X ;Translate into command address + ASL ;(bit 7 indicates a pathname to preprocess) + STA cmdTmp + AND #$3F ;(bit6 is refnum preprocess, 5 is for time, so strip em.) + TAX + LDA cmdTable,X ;Move address for indirect jump + STA goAdr + LDA cmdTable+1,X ;(high byte) + STA goAdr+1 + LDA #backupNeeded ;Init "backup bit flag" + STA bkBitFlg ; to say "file modified" + BCC NoPath + +* For MLI calls $C0-$C4, $C8 + + JSR SetPath ;Go process pathname before calling command + BCS ErrorSys ;Branch if bad name + +NoPath ASL cmdTmp ;Test for refnum preprocessing + BCC NoPreRef + +* For MLI calls $C9-$CB, $CE-$D3 + + JSR FindFCB ;Go set up pointers to fcb and vcb of this file + BCS ErrorSys ;branch if any errors are encountered + +NoPreRef ASL cmdTmp ;Lastly check for necessity of time stamp + BCC Execute + +* For MLI calls $C0-$C4, $CC, $CD + + JSR DateTime ;(No error posible) + +Execute JSR GoCmd ;Execute command + BCC GoodOp ;Branch if successful + +ErrorSys JSR SysErr ;Don't come back +GoodOp RTS ;Good return + +*------------------------------------------------- +* Check caller's pathname & copy to pathname buffer + +SetPath LDY #c_path + LDA (parm),Y ;Get low pointer addr + STA tPath + INY + LDA (parm),Y + STA tPath+1 ; & hi pointer addr + +SynPath EQU * ;Entry used by rename for second pathname + LDX #$00 ;X-reg is used as index to pathBuf + LDY #$00 ;Y-reg is index to input pathname + STX prfxFlg ;Assume prefix is in use + STX pathBuf ;Mark pathbuf to indicate nothing processed + LDA (tPath),Y ;Validate pathname length>0, and <65 + BEQ ErrSyn + CMP #65 + BCS ErrSyn + STA pathCnt ;This is used to compare for + INC pathCnt ; end of pathname processing + INY ;Now check for full pathname... + LDA (tPath),Y ;(Full name if starts with "/") + ORA #$80 + CMP #"/" + BNE NotFullPN ;Branch if prefix appended + STA prfxFlg ;Set prefix flag to indicate prefix not used + INY ;Index to first character of pathname + +NotFullPN LDA #$FF ;Set current position of pathBuf + STA pathBuf,X ; to indicate end of pathname + STA namCnt ;Also indicate no characters processed in local name + STX namPtr ;Preserve pointer to local name length byte + +SynPath3 CPY pathCnt ;done with pathname processing? + BCS EndPath ;Yes + LDA (tPath),Y ;Get character + AND #$7F ;We're not interested in high order bit + INX ;Prepare for next character + INY + CMP #'/' ;Is it a slash delimiter? + BEQ EndName ;Branch if it is + CMP #'a' ;Is it lower case character? + BCC NotLower ;Branch if not + AND #$5F ;Upshift to upper case +NotLower STA pathBuf,X ;Store charcter + INC namCnt ;Is it the first of a local name? + BNE NotFirst ;Branch if not + INC namCnt ;Kick count to 1 + BNE TestAlfa ;First char. Must be alpha (branch always taken) + +NotFirst CMP #'.' ;Is it "."? + BEQ SynPath3 ;It's ok if it is, do next char + CMP #'0' ;Is it at least "0"? + BCC ErrSyn ;Report syntax error if not + CMP #'9'+1 ;Is it numeric? + BCC SynPath3 ;ok if it is, do next character + +TestAlfa CMP #'A' ;Is it at least an "a"? + BCC ErrSyn ;Report err if not + CMP #'Z'+1 ;Is it g.t. "z"? + BCC SynPath3 ;Get next char if valid alpha +ErrSyn SEC ;Make sure carry set + LDA #badPathSyntax + RTS ;Report error + +EndPath LDA #$00 ;End pathname with 0 + BIT namCnt ;Also make sure name count is positive + BPL :1 + STA namCnt ;=0 + DEX +:1 INX + STA pathBuf,X + BEQ ErrSyn ;Report error if "/" only + STX pathCnt ;Save true length of pathname + TAX ;X=0 causes end of process, after endname + +EndName LDA namCnt ;Validate local name <16 + CMP #15+1 + BCS ErrSyn + PHX ;Save current pointer + LDX namPtr ;Get index to beginning of local name + STA pathBuf,X ;Save local name's length + PLX ;Restore x + BNE NotFullPN ;Branch if more names to process + + CLC ;Indicate success! + LDA prfxFlg ; but make sure all pathnames are + BNE EndRTS ; prefixed or begin with a "/" + LDA NewPfxPtr ; must be non-zero + BEQ ErrSyn +EndRTS RTS + +************************************************** +* SETPREFIX Call + +SetPrefix JSR SetPath ;Call is made here so a 'null' path may be detected + BCC :1 ;Branch if pathname ok + LDY pathBuf ;Was it a nul pathname? + BNE PfxErr ;Branch if true syntax error + JSR ZeroPfxPtrs ;Indicate null prefix. NB. (Y)=0 + CLC + RTS + +:1 JSR FindFile ;Go find specified prefix directory + BCC :2 ;Branch if no error + CMP #badPathSyntax + BNE PfxErr ;Branch if error is real (not root dir) + +:2 LDA d_file+d_stor ;Make sure last local name is DIR type + AND #directoryFile*16;(either root or sub) + EOR #directoryFile*16;Is it a directory? + BNE PfxTypErr ;Report wrong type + LDY prfxFlg ;New or appended prefix? + BNE :3 ;(A)=0 if branch taken + LDA NewPfxPtr ;Append new prefix to old +:3 TAY + SEC ;Find new beginning of prefix + SBC pathCnt + CMP #$C0 ;Too long? ($100-$40) + BCC ErrSyn ;Report it if so + TAX + JSR SetPfxPtrs + LDA d_dev ;Save device number + STA pathDev + LDA d_file+d_first ; & addr of first block + STA pathBlok + LDA d_file+d_first+1 + STA pathBlok+1 +MovPrefix LDA pathBuf,Y + STA pathBuf,X + INY + INX + BNE MovPrefix + CLC ;Indicate good prefix + RTS + +PfxTypErr LDA #badStoreType ;Report not a directory +PfxErr SEC ;indicate error + RTS + +************************************************** +* GETPREFIX Call + +GetPrefix CLC ;Calculate how big a buffer is needed to + LDY #c_path ;Get index to user's pathname buffer + LDA (parm),Y + STA userBuf + INY + LDA (parm),Y + STA userBuf+1 + STZ cBytes+1 ;Set buf length at max + LDA #64 ;(64 characters max) + STA cBytes + JSR ValDBuf ;Go validate prefix buffer addr + BCS PfxErr + LDY #$00 ;Y is indirect index to user buffer + LDA NewPfxPtr ;Get address of beginning of prefix + TAX + BEQ NullPrefix ;Branch if null prefix + EOR #$FF ;Get total length of prefix + ADC #$02 ;Add 2 for leading and trailing slashes +NullPrefix STA (userBuf),Y ;Store length in user's buffer + BEQ GotPrefix ;Branch if null prefix +SendPrefix INY ;Bump to next user buf loc + LDA pathBuf,X ;Get next char of prefix +SndLimit STA (userBuf),Y ;Give character to user + AND #$F0 ;Check for length descriptor + BNE :1 ;Branch if regular character + LDA #'/' ;Otherwise, substitute a slash + BNE SndLimit ;Branch always + +:1 INX + BNE SendPrefix ;Branch if more to send + INY + LDA #'/' ;End with slash + STA (userBuf),Y +GotPrefix CLC ;Indicate no error + RTS + +*------------------------------------------------- +* Validity check the ref # passed by caller + +FindFCB LDY #c_refNum ;Index to reference number + LDA (parm),Y ;Is it a valid file number? + BEQ ErrRefNum ;Must not be 0! + CMP #8+1 ;Must be 1 to 8 only + BCS ErrRefNum ;User must be stoned... + PHA + DEC ;(subtracts 1) + LSR ;Shift low 3 bits to high bits + ROR + ROR + ROR ;Effective multiply by 32 + STA fcbPtr ;Later used as an index + TAY ; to FCB like now + PLA ;Restore refnum in A-reg + CMP fcb+fcbRefNum,Y ;Is it an open reference? + BNE ErrNoRef ;Branch if not + +FndFCBuf LDA fcb+fcbFileBuf,Y;Get page addr of file buffer + JSR GetBufAdr ;Get file's address into bufAddrL & H + LDX bufAddrH ;(Y)=fcbptr - preserved + BEQ FCBDead ;Report FCB screwed up!!! + STX dataPtr+1 ;Save pointer to data area of buffer + INX + INX ;Index block always 2 pages after data + STX tIndex+1 + LDA fcb+fcbDevNum,Y ;Also set up device number + STA DevNum + LDA bufAddrL + STA dataPtr ;Index and data buffers + STA tIndex ; always on page boundaries + +SrchVCBs TAX ;Search for associated VCB + LDA vcb+vcbDevice,X + CMP fcb+fcbDevNum,Y ;Is this VCB the same device? + BEQ TestVOpen ;If it is, make sure volume is active + +NxtBufr TXA ;Adjust index to next VCB + CLC + ADC #vcbSize + BCC SrchVCBs ;Loop until volume found + LDA #vcbUnusable ;Report open file has no volume... + JSR SysDeath ; & kill the system + +FCBDead LDA #fcbUnusable ;Report FCB trashed + JSR SysDeath ; & kill the system + +TestVOpen LDA vcb,X ;Make sure this VCB is open + BEQ NxtBufr ;Branch if it is not active + STX vcbPtr ;Save pointer to good VCB + CLC ;Indicate all's well + RTS + +ErrNoRef LDA #$00 ;Drop a zero into this FCB + STA fcb+fcbRefNum,Y ; to show free FCB + +ErrRefNum LDA #invalidRefNum ;Tell user that requested refnum + SEC ; is illegal (out of range) for this call + RTS + +************************************************** +* ONLINE call + +Online JSR MovDBuf ;Move user specified buffer pointer to usrbuf + STZ cBytes ;Figure out how big buffer has to be + STZ cBytes+1 + LDY #c_devNum + LDA (parm),Y ;If zero then cbytes=$100, else =$010 for one device + AND #$F0 + STA DevNum + BEQ :1 ;Branch if all devices + LDA #$10 + STA cBytes + BNE :2 ;Always +:1 INC cBytes+1 ;Allow for up to 16 devices +:2 JSR ValDBuf ;Go validate buffer range against allocated memory + BCS OnlinErr + LDA #$00 ;Zero out user buffer space + LDY cBytes +:loop1 DEY + STA (userBuf),Y ;Zero either 16 or 256 bytes + BNE :loop1 ;Branch if more to zero + STA namPtr ;Use namPtr as pointer to user buffer + LDA DevNum + BNE OnlineZ ;Branch if only 1 device to process + JSR MovDevNums ;Get list of currently recognized devices +:loop2 PHX ;Save index to last item on list + LDA lookList,X ;Get next device # + STA DevNum + JSR OnlineZ ;Log this volume and return it's name to user + LDA namPtr + CLC + ADC #$10 + STA namPtr + PLX ;Restore index to device list + DEX ;Index to next device + BPL :loop2 ;Branch if there is another device + LDA #$00 ;No errors for muliple on-line + CLC ;Indicate good on all volumes +OnlinErr RTS + +* Generate return data for a specific device + +OnlineZ JSR ScanVCB ;See if it has already been logged in + BCS OnlinErr1 ;Branch if VCB is full + LDX #$00 ;Read in root (volume) directory + LDA #$02 ;(X,A)=block # + JSR RdBlkAX ;Read it into general purpose buffer + LDX vcbPtr ;Use x as an index to the vcb entry + +* This fix is to remove VCB entries that correspond to devices that +* are no longer in the device list (i.e. removed by the user). + + BCC VolFound ;Branch if the read was ok + TAY ;Save error value in Y-reg + LDA vcb+vcbStatus,X ;Don't take the VCB off line if + BNE RtrnErr ; there are active files present! + STA vcb,X ;Now take the volume off line + STA vcb+vcbDevice,X +RtrnErr TYA ;Now return error to A + BCS OnlinErr1 + +* 1st vol dir blk has been read successfully + +VolFound LDA vcb,X ;Has it been logged in before? + BEQ :1 ;Branch if not + LDA vcb+vcbStatus,X ;It has, are there active files? + BMI :2 ;Branch if the volume is currently busy +:1 JSR LogVCBZ ;Go log it in + BCS OnlinErr1 ;Branch if there is some problem (like notsos) + LDA #dupVolume ;Anticipate a duplicate active volume exists + BIT duplFlag + BMI OnlinErr1 ;Branch if we guessed right +:2 LDX vcbPtr ;Restore vcbptr just in case we lost it + JSR CmpVCB ;Does read in volume compare with logged volume? + LDA #drvrDiskSwitch ;Anticipate wrong volume mounted in active device + BCC Online2 ;Branch if no problem! + +* On fall thru, (A)=disk switch error +* Store error code in user's data buffer + +OnlinErr1 PHA ;Save error code + JSR SavDevNbr ;Tell user what device we looked at + PLA ;Get error code again + INY ;Tell user what error was encountered on this device + STA (userBuf),Y + CMP #dupVolume ;Was it a duplicate volume error? + BNE :1 ;Branch if not, + INY ;Otherwise tell user which other device has same name + LDX vcbEntry + LDA vcb+vcbDevice,X + STA (userBuf),Y + STZ duplFlag ;Clear duplicate flag + LDA #dupVolume ;Restore error code +:1 SEC ;Indicate error + RTS + +* Make online volume entry + +Online2 LDA vcb,X ;Get volume name count + STA namCnt + LDY namPtr ;Index to user's buffer +:loop LDA vcb,X ;Move name to user's buffer + STA (userBuf),Y + INX + INY + DEC namCnt ;Loop until all characters moved + BPL :loop + +SavDevNbr LDY namPtr ;Index to first byte of this entry + LDA DevNum ;Put device number in upper nibble of this byte + ORA (userBuf),Y ;Lower nibble is name length + STA (userBuf),Y + CLC ;Indicate no errors + RTS diff --git a/MLI.SRC/CCLOCK.S b/MLI.SRC/CCLOCK.S new file mode 100644 index 0000000..4af9382 --- /dev/null +++ b/MLI.SRC/CCLOCK.S @@ -0,0 +1,137 @@ +*********************************************************** +* +* ProDOS 8 CORTLAND CLOCK DRIVER +* +* COPYRIGHT APPLE COMPUTER, INC., 1986 +* +* ALL RIGHTS RESERVED +* +* Written by Kerry Laidlaw, 2/12/86 +* Modified by Mike Askins, 9/6/86 +* Modified by Fern Bachman, 9/7/86 +* +*********************************************************** +* +* This is the ProDOS8 Cortland built-in clock driver. +* Its sole function in life is to fetch the time from the Cortland +* clock via the Read Hex Time misc. tool call, and transfer this +* time into the ProDOS global page time format. +* +* This routine will IGNORE any errors passed back to it from the +* Read Hex Time call. This was done since existing ProDOS8 programs +* cannot deal with some new time error code. +* Thus the only way that a user can tell if his Cortland clock is +* broken, is by noticing that the date and time fields are zeroed. +* +* Note: There are some interesting facts to know regarding the +* slot clock driver for ProDOS8 and the built-in +* Cortland clock. The year value returned from the Cortland clock +* is an offset from the year 1900. Thus Cortland is capable of +* reporting the year correctly until 1900+255=2155. Only 7 bits +* are used for the year in the ProDOS8 global page, so theoretically +* 1900+127=2027 is the last year that ProDOS could represent on a +* Cortland. But this is only if the ProDOS8 year value is interpreted +* as being an offset from 1900. +* +* Historically, the year value has been interpreted as the binary +* representation of the last two digits of the year 19xx. +* So this means that programs that display the year as a concatenation +* of 19 and the ascii equivalent of the year value will work until 1999. +* And programs that just display the last two digits of the year will +* still work correctly until (20)27 if they convert the year value +* correctly, but ignore any hundredths place digit. +* +* Apple //e's that use slot clocks that utilize the slot clock +* driver have further restrictions of the year value. The slot +* clock driver calculates the year given the position of the day +* of the week in the month. This algorithm then uses a year look +* up table that has seven possible values. Leap years are repeated +* in the table. Since 1988 is a leap year, then the updated slot +* clock driver (file TCLOCK) will yield the six year offset values +* rather then seven. +* So before 1992, if ProDOS8 still exists, the slot clock driver +* routine must be updated again! +* +* So, we now have the following definition: +* The value placed in the year field is defined as the +* number of years past the year 1900. +* Numerically speaking: Current Year = 1900 + year value. + + MX %11 + + ORG ClockBegin + +* This mod will force read/write main memory for the tool +* call by resetting the read/write auxillary memory bits +* in the state register (statereg). + + MX %11 +IIgsClock EQU * + SEP #$30 ;Make sure we're in 8 bit mode + LDA STATEREG ;Get the state reg + STA SaveState ;Keep for restore after tool call + AND #%11001111 ;Clear the Read/Write aux memory bits + STA STATEREG ;Make it real + +* First off, lets get into native mode with 16 bit m & x. + + MX %00 + CLC ;Set e = 0, to set native mode + XCE + REP #$30 ;Zero m & x for 16-bit mode + LDA #$0000 ;Zero out result space + PHA ; Push 4 words for hex time result... + PHA + PHA + PHA + _ReadTimeHex + +* Note that no error condition is checked for, so the date will +* be zeroed by default if an error indeed happened. +* +* Back to 8 bit m to access results on stack... + MX %10 + SEP #$20 + LDA SaveState ;Restore the state register + STA STATEREG + +* Now let's pull the time off the stack and stick it in the global page. + + PLA ;Pull off Seconds, and ignore + PLA ;Pull off Minutes + STA TimeLo ;Store in global page + PLA ;Pull off Hours + STA TimeLo+1 ;Store in global page + PLA ;Pull off Year value + +:loop1 CMP #100 ;Adjust for + BCC :1 + SBC #100 ; year 2000 + BRA :loop1 + +:1 STA DateLo+1 ; (year) + PLA ;Pull off Day + INC ;Increment day value for ProDOS8 format + STA DateLo ;Store in global page + PLA ;Pull off Month + INC ;Incr month value for ProDOS8 format + ASL ;Shift month as it sits in between + ASL ; the year and day values + ASL + ASL + ASL + ORA DateLo ;Put all but the top bit of month value + STA DateLo ; in the day byte + ROL DateLo+1 ;Put hi bit of mo. in lo bit of yr byte + PLA ;Pull off unused byte + PLA ;Pull off Day of Week. Stack now clean + SEC ;Now go back to emulation mode + XCE ; to continue with ProDOS8 + RTS ;That's all + +SaveState DB $00 ;Keep the state of state register + ASC 'JIMJAYKERRY&MIKE' +ClockEnd EQU * + DS 125-ClockEnd+ClockBegin,0; Zero rest of 125 bytes +Size EQU *-ClockBegin ;MUST be $7D (125) bytes in length! + DS $80-Size,0 diff --git a/MLI.SRC/CLOSEEOF.S b/MLI.SRC/CLOSEEOF.S new file mode 100644 index 0000000..46e6d89 --- /dev/null +++ b/MLI.SRC/CLOSEEOF.S @@ -0,0 +1,399 @@ +*********************************************************** +* Close Call + +Close LDY #c_refNum ;Close all? + LDA (parm),Y + BNE Close1 ;No, just one of 'em + STA clsFlshErr ;Clear global close error + LDA #$00 ;Begin at the beginning +ClsAll STA fcbPtr ;Save current low byte of pointer + TAY ;Fetch the level at which + LDA fcb+fcbLevel,Y ; file was opened + CMP Level ;Test against current global level + BCC NxtClose ;Don't close if files level is < global level + LDA fcb+fcbRefNum,Y ;Is this reference file open? + BEQ NxtClose ;No, try next + JSR FlushZ ;Clean it out... + BCS ClosErr ;Return flush errors + + JSR CloseZ ;Update FCB & VCB + LDY #c_refNum + LDA (parm),Y + BEQ NxtClose ;No err if close all + BCS ClosErr +NxtClose LDA fcbPtr + CLC + ADC #fcbSize + BCC ClsAll ;Branch if within same page + + LDA clsFlshErr ;On final close of close all report logged errors + BEQ ClosEnd ;Branch if errors + RTS ;Carry already set (see BCC) + +Close1 JSR Flush1 ;Flush file first (including updating bit map) + BCS ClosErr ;Report errors immediately!! + +CloseZ LDY fcbPtr + LDA fcb+fcbFileBuf,Y;Release file buffer + JSR ReleaseBuf + BCS ClosErr + LDA #$00 + LDY fcbPtr + STA fcb+fcbRefNum,Y ;Free file control block too + LDA fcb+fcbDevNum,Y + STA DevNum + JSR ScanVCB ;Go look for associated VCB + LDX vcbPtr ;Get vcbptr + DEC vcb+vcbOpenCnt,X;Indicate one less file open + BNE ClosEnd ;Branch if that wasn't the last... + LDA vcb+vcbStatus,X + AND #$7F ;Strip 'files open' bit + STA vcb+vcbStatus,X +ClosEnd CLC + RTS + +ClosErr BCS FlushErr ;Don't report close all err now + +Flush LDY #c_refNum ;Flush all? + LDA (parm),Y + BNE Flush1 ;No, just one of 'em + + STA clsFlshErr ;Clear global flush error + LDA #$00 ;Begin at the beginning +:loop STA fcbPtr ;Save current low byte of pointer + TAY ;Index to reference number + LDA fcb+fcbRefNum,Y ;Is this reference file open? + BEQ :1 ;No, try next + JSR FlushZ ;Clean it out.. + BCS FlushErr ;Return any errors +:1 LDA fcbPtr ;Bump pointer to next file control block + CLC + ADC #fcbSize + BCC :loop ;Branch if within same page + +FlushEnd CLC + LDA clsFlshErr ;On last flush of a flush(0) + BEQ :Ret ;Branch if no logged errors + SEC ;Report error now +:Ret RTS + +FlushZ JSR FndFCBuf ;Must set up assoc vcb & buffer locations first + BCC Flush2a ;Branch if no error encountered +FlushErr JMP GlbErr ;Check for close or flush all + +Flush1 STZ clsFlshErr ;Clear gbl flush error for normal refnum flush + JSR FindFCB ;set up pointer to fcb user references + BCS FlushErr ;return any errors + +Flush2a EQU * ;Test to see if file is modified + LDA fcb+fcbAttr,Y ;First test write enabled + AND #writeEnable + BEQ FlushEnd ;Branch if 'read only' + LDA fcb+fcbDirty,Y ;See if eof has been modified + BMI :11 ;Branch if it has + + JSR GetFCBStat ;now test for data modified + AND #useMod+eofMod+dataMod; was written to while it's been open? + BEQ FlushEnd ;Branch if file not modified + +:11 JSR GetFCBStat ;Now test for data modified + AND #dataMod ;Does current data buffer need + BEQ :12 ; to be written? Branch if not + JSR WrFCBData ;If so, go write it stupid! + BCS FlushErr + +:12 JSR GetFCBStat ;Check to see if the index block + AND #idxMod ; (tree files only) needs to be written + BEQ :13 ;Branch if not... + JSR WrFCBIdx + BCS FlushErr ;Return any errors + +:13 LDA #fcbEntNum ;Now prepare to update directory + TAX + ORA fcbPtr ;(This should preserved Carry-bit) + TAY +OwnerMov LDA fcb,Y ;Note: this code depends on the + STA d_dev-1,X ; defined order of the file control + DEY ; block and the temporary directory + DEX ; area in 'workspc'! ************* + BNE OwnerMov + + STA DevNum + LDA d_head ;Read the directory header for this file + LDX d_head+1 + JSR RdBlkAX ;Read it into the general purpose buffer + BCS FlushErr ;Branch if error + JSR MoveHeadZ ;Move header info + LDA d_entBlk ;Get address of directory block + LDY d_entBlk+1 ; that contains the file entry + CMP d_head ;Test to see if it's the same block that + BNE FlsHdrBlk ; the header is in. Branch if not + CPY d_head+1 + BEQ Flush5 ;Branch if header block = entry block + +FlsHdrBlk STA blockNum + STY blockNum+1 + JSR RdGBuf ;Get block with file entry in general buffer + +Flush5 JSR EntCalc ;Set up pointer to entry + JSR MovEntry ;Move entry to temp entry buffer in 'workspc' + + LDY fcbPtr ;Update 'blocks used' count + LDA fcb+fcbBlksUsed,Y + STA d_file+d_usage + LDA fcb+fcbBlksUsed+1,Y + STA d_file+d_usage+1;hi byte too... + + LDX #$00 ;and move in end of file mark +EOFupdate LDA fcb+fcbEOF,Y ; whether we need to or not + STA d_file+d_eof,X + INX ;Move all three bytes + CPX #$03 + BEQ :21 + LDA fcb+fcbFirst,Y ;Also move in the address of + STA d_file+d_first-1,X; the file's first block since + INY ; it might have changed since the file + BNE EOFupdate ; first opened. Branch always taken + +:21 LDA fcb+fcbStorTyp-2,Y;the last thing to update + ASL ; is storage type (y=fcbPtr+2) + ASL ;(shift it into the hi nibble) + ASL + ASL + STA scrtch + LDA d_file+d_stor ;Get old type byte (it might be the same) + AND #$0F ;Strip off old type + ORA scrtch ;Add in the new type, + STA d_file+d_stor ; & put it away + JSR ReviseDir ;Go update directory! + BCS GlbErr + + LDY fcbPtr ;Mark + LDA fcb+fcbDirty,Y ; FCB/directory + AND #$FF-fcbMod ; as not + STA fcb+fcbDirty,Y ; dirty + LDA d_dev ;See if bitmap should be written + CMP bmaDev ;Is it in same as current file? + BNE :22 ;Yes, put it on the disk if necessary + JSR UpdateBitMap ;Go put it away + BCS GlbErr +:22 CLC + RTS + +GlbErr LDY #c_refNum ;Report error immediately + PHA ; only if not a close all or flush all + LDA (parm),Y + BNE :31 ;Not an 'all' so report now + CLC + PLA + STA clsFlshErr ;Save for later + RTS +:31 PLA + RTS + +* Get status of FCB + +GetFCBStat LDY fcbPtr ;Index to fcb + LDA fcb+fcbStatus,Y ;Return status byte + RTS ;That is all... + +SetErr LDA #invalidAccess + SEC +EOFret RTS + +*********************************************************** +* SETEOF Call + +SetEOF JSR GfcbStorTyp ;Only know how to move eof of tree, sapling, or seed + CMP #tree+1 + BCS SetErr + ASL + ASL + ASL + ASL ;=$10,$20,$30 + STA storType ;May be used later for trimming the tree... + LDA fcb+fcbAttr,Y ;Now check to insure write is enabled + AND #writeEnable ;Can we set new eof? + BEQ SetErr ;Nope, access error + + JSR TestWrProt ;Find out if mod is posible (H/W write protect) + BCS SetErr + + LDY fcbPtr ;Save old EOF + INY + INY + LDX #$02 ; so it can be seen +SetSave LDA fcb+fcbEOF,Y ; whether blocks need + STA oldEOF,X ; to be released + DEY ; upon + DEX ; contraction + BPL SetSave ;All three bytes of the eof + + LDY #c_eof+2 + LDX #$02 +NewEOFPos LDA (parm),Y ;Position mark to new EOF + STA tPosll,X + DEY + DEX + BPL NewEOFPos + + LDX #$02 ;Point to third byte +PurgeTest LDA oldEOF,X ;See if EOF moved backwards + CMP tPosll,X ; so blocks can + BCC EOFset ; be released (branch if not) + BNE Purge ;Branch if blocks to be released + DEX + BPL PurgeTest ;All three bytes + +EOFset LDY #c_eof+2 + LDX fcbPtr ;Place new end of file into FCB + INX + INX +:loop LDA (parm),Y + STA fcb+fcbEOF,X + DEX + DEY + CPY #c_eof ;All three bytes moved? + BCS :loop ;Branch if not... + JMP FCBUsed ;Mark fcb as dirty... all done + +Purge JSR Flush1 ;Make sure file is current + BCS EOFret + LDX dataPtr+1 ;Restore pointer to index block + INX + INX ;(zero page conflict with dirPtr) + STX tIndex+1 + LDX dataPtr + STX tIndex + LDY fcbPtr ;Find out if eof < mark + INY + INY + LDX #$02 +NewEOFtest LDA fcb+fcbMark,Y + CMP tPosll,X ;Compare until not equal or carry clear + BCC SetEOF1 ;branch if eof>mark (mark is b4 new EOF) + BNE SetEOF0 ;branch if eof$En) last byte is fileKind/namlen + STA genBuf+4 ;Make it a directory header mark + + LDX #$07 ;Now overwrite password area +:loop3 LDA Pass,X ; and other header info + STA genBuf+4+hPassEnable,X + LDA XDOSver,X + STA genBuf+4+hVer,X + DEX + BPL :loop3 + + LDX #$02 ; & include info about 'parent directory + STX d_file+d_eof+1 +:loop4 LDA d_entBlk,X + STA genBuf+4+hOwnerBlk,X + DEX + BPL :loop4 + + LDA h_entLen ;Lastly the length of parent's dir entries + STA genBuf+4+hOwnerLen + +CrAlocBlk JSR Alloc1Blk ;Get address of file's data block + BCS CrErr3 ;Branch if error encountered + STA d_file+d_first + STY d_file+d_first+1 + STA blockNum + STY blockNum+1 + JSR WrtGBuf ;Go write data block of file + BCS CrErr3 + INC h_fileCnt ;Add 1 to total # of files in this directory + BNE :1 + INC h_fileCnt+1 +:1 JSR ReviseDir ;Go revise directories with new file + BCS CrErr3 + JMP UpdateBitMap ;Lastly, update volume bitmap + +*------------------------------------------------- +* Point dirBufPtr ($48/$49) at directory entry + +EntCalc LDA #genBuf/256 ;Set high address of directory + STA dirBufPtr+1 ; entry index pointer + LDA #$04 ;Calculate address of entry based + LDX d_entNum ; on the entry number +:loop1 CLC +:loop2 DEX ;addr=genBuf+((entnum-1)*entlen) + BEQ :exitLoop + ADC h_entLen + BCC :loop2 + INC dirBufPtr+1 ;Bump hi address + BCS :loop1 ;Branch always +:exitLoop STA dirBufPtr ;Save newly calculated low address +CrErr3 EQU * +DError2 RTS ;Return errors + +*------------------------------------------------- +* Update directory(s) + +ReviseDir LDA DateLo ;If no clock, + BEQ ReviseDirZ ; then don't touch mod time/date + + LDX #$03 +:loop LDA DateLo,X ;Move last modification date/time + STA d_file+d_modDate,X; to entry being updated + DEX + BPL :loop + +ReviseDirZ LDA d_file+d_attr ;Mark entry as backupable + ORA bkBitFlg ; bit 5 = backup needed bit + STA d_file+d_attr + LDA d_dev ;Get device number of directory + STA DevNum ; to be revised + LDA d_entBlk ; & address of directory block + LDX d_entBlk+1 + JSR RdBlkAX ;Read block into general purpose buffer + BCS DError2 + + JSR EntCalc ;Fix up pointer to entry location within gbuf + LDY h_entLen ;Now move 'd_' stuff to directory + DEY +:loop1 LDA d_file+d_stor,Y + STA (dirBufPtr),Y + DEY + BPL :loop1 + + LDA d_head ;Is the entry block the same as the + CMP blockNum ; entry's header block? + BNE SavEntDir ;No, save entry block + LDA d_head+1 ;Maybe, test high addresses + CMP blockNum+1 + BEQ UpHead ;Branch if they are the same block + +SavEntDir JSR WrtGBuf ;Write updated directory block + BCS DError2 ;Return any error + LDA d_head ;Get address of header block + LDX d_head+1 + JSR RdBlkAX ;Read in header block for modification + BCS DError2 + +UpHead LDY #$01 ;Update current # of files in this directory +:loop2 LDA h_fileCnt,Y + STA genBuf+hFileCnt+4,Y;(current entry count) + DEY + BPL :loop2 + + LDA h_attr ;Also update header's attributes + STA genBuf+hAttr+4 + JSR WrtGBuf ;Go write updated header + BCS DError1 + +Ripple LDA genBuf+4 ;Test for 'root' directory + AND #$F0 ;If it is root, then dir revision is complete + EOR #$F0 ;(leaves carry clear) + BEQ DirRevDone ;Branch if ripple done + + LDA genBuf+hOwnerEnt+4;Get entry number & + STA d_entNum + LDA genBuf+hOwnerEnt+5; the length of entries in that dir + STA h_entLen + + LDA genBuf+hOwnerBlk+4;Get addr of parent entry's dir block + LDX genBuf+hOwnerBlk+5 + JSR RdBlkAX ;Read that sucker in + BCS DError1 + + JSR EntCalc ;Get indirect ptr to parent entry in genBuf + LDA DateLo ;Don't touch mod + BEQ RUpdate ; if no clock... + + LDX #$03 ;Now update the modification date + LDY #d_modDate+3 ; & time for this entry too +RipTime LDA DateLo,X + STA (dirBufPtr),Y + DEY + DEX + BPL RipTime ;Move all for bytes... + +* Write updated entry back to disk. (Assumes blockNum undisturbed) + +RUpdate JSR WrtGBuf + BCS DError1 ;Give up on any error + + LDY #d_dHdr ;Now compare current block number to + LDA (dirBufPtr),Y ; this entry's header block + INY + CMP blockNum ;Are low addresses the same? + STA blockNum ;(save it in case it's not) + BNE :1 ;Branch if entry does not reside in same block as header + LDA (dirBufPtr),Y ;Check high address just to be sure + CMP blockNum+1 + BEQ Ripple ;They are the same, continue ripple to root directory + +:1 LDA (dirBufPtr),Y ;They aren't the same, + STA blockNum+1 ; read in this directory's header + JSR RdGBuf + BCC Ripple ;Continue if read was good +DError1 RTS + +TestErr LDA #unknownVol ;Not tree or dir - not a recognized type! + SEC + RTS + +*------------------------------------------------- +* Is this a ProDOS vol? + +TestSOS LDA genBuf ;Test SOS stamp + ORA genBuf+1 + BNE TestErr + LDA genBuf+4 ;Test for header + AND #$E0 + CMP #$E0 + BNE TestErr ;Branch if not SOS header (no error number) +DirRevDone CLC ;Indicate no error + RTS diff --git a/MLI.SRC/DATATBLS.S b/MLI.SRC/DATATBLS.S new file mode 100644 index 0000000..2b8fce7 --- /dev/null +++ b/MLI.SRC/DATATBLS.S @@ -0,0 +1,103 @@ +*********************************************************** +* ---- Added call $41 & its count - see rev note 20 -------- + +scNums EQU * + DFB $D3,0,0,0 ;(zeros are reserved for bfm) + DFB $40,$41,$00,0 ;(zero is reserved for interrupt calls) + DFB $80,$81,$82,$65 + DFB $C0,$C1,$C2,$C3 + DFB $C4,$C5,$C6,$C7 + DFB $C8,$C9,$CA,$CB + DFB $CC,$CD,$CE,$CF + DFB $00,$D0,$D1,$D2 ;zero is non-existent. + +pCntTbl EQU * ;parameter counts for the calls + HEX 02FFFFFF + HEX 0201FFFF + HEX 03030004 + HEX 07010207 + HEX 0A020101 + HEX 03030404 + HEX 01010202 + HEX FF020202 + +*------------------------------------------------- +* JMP table + +cmdTable EQU * + DA Create + DA Destroy + DA Rename + DA SetInfo + DA GetInfo + DA Online + DA SetPrefix + DA GetPrefix + DA Open + DA NewLine + DA Read + DA Write + DA Close + DA Flush + DA SetMark + DA GetMark + DA SetEOF + DA GetEOF + DA SetBuf + DA GetBuf + +*------------------------------------------------- +* Function bits for MLI codes $C0-$D3 + +Dispatch EQU * + DB prePath+preTime+0;create + DB prePath+preTime+1;destroy + DB prePath+preTime+2;rename + DB prePath+preTime+3;setinfo + DB prePath+4 ;getinfo + DB $05 ;volume + DB $06 ;setprefix, pathname moved to prefix buffer + DB $07 ;getprefix + DB prePath+8 ;open + DB preRef+$9 ;newline + DB preRef+$a ;read + DB preRef+$b ;write + DB preTime+$c ;close + DB preTime+$d ;flush, refnum may be zero to flush all + DB preRef+$e ;setmark + DB preRef+$f ;getmark + DB preRef+$10 ;set eof + DB preRef+$11 ;get eof + DB preRef+$12 ;set buffer address (move) + DB preRef+$13 ;get buffer address + +*------------------------------------------------- +* Constants + +dIncTbl DB 1,0,0,2,0 ;Table to increment directory usage/EOF counts +Pass DB $75 +XDOSver DB $0,0,$C3,$27,$0D,0,0,0 +compat EQU XDOSver+1 + +rootStuff DB $0F,2,0,4 + DB 0,0,8,0 + +WhichBit HEX 8040201008040201 + +* The following table is used in the 'Open:loop1' (posn/open). +* Offsets into file control blocks (FCBs) + +oFCBTbl DFB fcbFirst,fcbFirst+1,fcbBlksUsed,fcbBlksUsed+1 + DFB fcbEOF,fcbEOF+1,fcbEOF+2 + +* Set/Get file info offsets +* The following with $80+ are ignored by SetInfo + +InfoTabl DFB d_attr,d_fileID,d_auxID,d_auxID+1 + DFB $80+d_stor,$80+d_usage,$80+d_usage+1,d_modDate + DFB d_modDate+1,d_modTime,d_modTime+1,d_creDate + DFB d_creDate+1,d_creTime,d_creTime+1 + +Death ASC ' ' + ASC "RESTART SYSTEM-$01" + ASC ' ' \ No newline at end of file diff --git a/MLI.SRC/DESTROY.S b/MLI.SRC/DESTROY.S new file mode 100644 index 0000000..8ab394e --- /dev/null +++ b/MLI.SRC/DESTROY.S @@ -0,0 +1,423 @@ +*********************************************************** +* Newline Call + +NewLine LDY #c_isNewln ;Adjust newline status for open file + LDA (parm),Y ;on or off? + LDX fcbPtr ;It will be zero if off + STA fcb+fcbNLMask,X ;Set new line mask + INY + LDA (parm),Y ; & move in new 'new-line' byte + STA fcb+fcbNewLin,X + CLC + RTS ;No error possible + +GetInfo JSR FindFile ;Look for file they want to know about + BCC :1 ;Branch if no errors + CMP #badPathSyntax ;Was it a root directory file? + SEC ;(in case of no match) + BNE :Ret + LDA #$F0 + STA d_file+d_stor ;For get info, report proper storage type + STZ reqL ;Force a count of free blocks + STZ reqH + + LDX vcbPtr + JSR TakeFreeCnt ;Take a fresh count of free blocks on this volume + LDX vcbPtr + LDA vcb+vcbFreeBlks+1,X;Return total blocks and total in use + STA reqH ;First transfer 'free' blocks to zpage for later subtract + LDA vcb+vcbFreeBlks,X; to determine the 'used' count + STA reqL + + LDA vcb+vcbTotBlks+1,X;Transfer to 'd_' table as auxID + STA d_file+d_auxID+1;(total block count is considered auxID for the volume) + PHA + LDA vcb+vcbTotBlks,X + STA d_file+d_auxID + + SEC ;Now subtract and report the number of blocks 'in use' + SBC reqL + STA d_file+d_usage + PLA + SBC reqH + STA d_file+d_usage+1 + +:1 LDA d_file+d_stor ;Transfer bytes from there internal order + LSR ; to call spec via 'infoTabl' translation table + LSR + LSR ; but first change storage type to + LSR ; external (low nibble) format + STA d_file+d_stor + + LDY #c_creTime+1 ;Index to last of user's spec table +:CpyLoop LDA InfoTabl-3,Y + AND #$7F ;Strip bit used by setinfo + TAX + LDA d_file,X ;Move directory info to call spec. table + STA (parm),Y + DEY + CPY #c_attr ;have all info bytes been sent? + BCS :CpyLoop +:Ret RTS + +SetInfo JSR FindFile ;Find what user wants... + BCS SInfoErr ;Return any failure + + LDA BUBit ;Discover if backup bit can be cleared + EOR #backupNeeded + AND d_file+d_attr + AND #backupNeeded + STA bkBitFlg ; or preserve current... + + LDY #c_modTime+1 ;Init pointer to user supplied list +:loop1 LDX InfoTabl-3,Y ;Get index into coresponding 'd_' table + BMI :11 ;Branch if we've got a non-setable parameter + LDA (parm),Y + STA d_file,X +:11 DEY ;Has user's request been satisfied? + CPY #c_attr + BCS :loop1 ;No, move next byte + +* Make sure no illegal access bits were set! + + AND #$FF-destroyEnable-renameEnable-backupNeeded-fileInvisible-writeEnable-readEnable + BEQ SetInfo3 ;Branch if legal access + LDA #invalidAccess ;Otherwise, refuse to do it + SEC ;Indicate error +SInfoErr RTS + +SetInfo3 LDY #c_modDate+1 + LDA (parm),Y ;Was clock null input? + BEQ :Jump + JMP ReviseDirZ ;End by updating directory +:Jump JMP ReviseDir ;Update with clock also... + +*------------------------------------------------- +* RENAME call +* Only the final name in the path specification +* may be renamed. In other words, the new name +* must be in the same DIRectory as the old name. + +Rename JSR LookFile ;Look for source (original) file + BCC Rename0 ;Branch if found + CMP #badPathSyntax ;Trying to rename a volume? + BNE :1 ;No, return other error + JSR RenamePath ;Syntax new name + BCS :1 + + LDY pathBuf ;Find out if only rootname for new name + INY + LDA pathBuf,Y ;Must be $ff if v-name only + BNE RenBadPath ;Branch if not single name + + LDX vcbPtr ;Test for open files before changing + LDA vcb+vcbStatus,X + BPL RenameVol ;Branch if volume not busy + LDA #fileBusy +:1 SEC + RTS + +RenameVol LDY #$00 ;Get newname's length + LDA pathBuf,Y + ORA #$F0 ;(root file storage type) + JSR MovRootName ;Update root directory + BCS RenErr + + LDY #$00 + LDX vcbPtr ;Update VCB also +:loop LDA pathBuf,Y ;Move new name to VCB + BEQ :ExitLoop + STA vcb,X + INY ;Bump to next character + INX + BNE :loop ;Branch always taken +:ExitLoop CLC ;No errors + RTS + +Rename0 JSR GetNamePtr ;Set Y-reg to first char of path, X=0 +:loop1 LDA pathBuf,Y ;Move original name to genBuf + STA genBuf,X ; for later comparison with new name + BMI :11 ;Branch if last character has been moved + INY ;Otherwise, get the next one + INX + BNE :loop1 ;Branch always taken + +:11 JSR RenamePath ;Get new name syntaxed + BCS RenErr + JSR GetNamePtr ;Set Y to path, X to 0 + LDA pathBuf,Y ;Now compare new name with old name +:loop2 CMP genBuf,X ; to make sure that they are in the same dir + PHP ;Save result of compare for now + AND #$F0 ;Was last char really a count? + BNE :12 ;Branch if not + STY rnPtr ;Save pointer to next name, it might be the last + STX pnPtr +:12 PLP ;What was the result of the compare? + BNE NoMatch ;Branch if different character or count + INX ;Bump pointers + INY + LDA pathBuf,Y ;Was that the last character? + BNE :loop2 ;Branch if not + CLC ;No-operation, names were the same + RTS + +NoMatch LDY rnPtr ;Index to last name in the chains + LDA pathBuf,Y ;Get last name length + SEC + ADC rnPtr + TAY + LDA pathBuf,Y ;This byte should be $00! + BNE RenBadPath ;Branch if not + + LDX pnPtr ;Index to last of original name + LDA genBuf,X + SEC + ADC pnPtr + TAX + LDA genBuf,X ;This byte should also be $00 + BEQ GoodNames ;Continue processing if it is + +RenBadPath LDA #badPathSyntax +RenErr SEC + RTS ;Report error + +GoodNames JSR LookFile ;Test for duplicate file name + BCS :21 ;Branch if file not found, which is what we want! + LDA #dupPathname ;New name already exists + SEC ;Report duplicate + RTS + +:21 CMP #fileNotFound ;Was it a valid "file not found"? + BNE RenErr ;No, return other error code + JSR SetPath ;Now syntax the pathname of the file to be changed + JSR FindFile ;Get all the info on this one + BCS RenErr + + JSR TestOpen ;Don't allow rename to occur if file is in use + LDA #fileBusy ;Anticipate error + BCS RenErr + LDA d_file+d_attr ;Test bit that says it's ok to rename + AND #renameEnable + BNE Rename8 ;Branch if it's alright to rename + LDA #invalidAccess ;Otherwise report illegal access +RenErr1 SEC + RTS + +Rename8 LDA d_file+d_stor ;Find out which storage type + AND #$F0 ;Strip off name length + CMP #directoryFile*16;Is it a directory? + BEQ :31 + CMP #{tree+1}*16 ;Is it a seed, sapling, or tree? + BCC :31 + LDA #badFileFormat + BNE RenErr1 + +:31 JSR RenamePath ;Well... since both names would go into the dir, + BCS RenErr ; re-syntax the new name to get local name address + + LDY rnPtr ;(Y contains index to local name length) + LDX pathBuf,Y ;Adjust Y to last char of new name + TYA + ADC pathBuf,Y + TAY +:loop LDA pathBuf,Y ;Move local name to dir entry workspace + STA d_file+d_stor,X + DEY + DEX + BNE :loop + + LDA d_file+d_stor ;Preserve file storage type + AND #$F0 ;Strip off old name length + TAX + ORA pathBuf,Y ;Add in new name's length + STA d_file+d_stor + CPX #directoryFile*16; that file must be changed also + BNE RenameDone ;Branch if not directory type + +* Renaming a DIR file + + LDA d_file+d_first ;Read in 1st (header) block of sub-dir + LDX d_file+d_first+1 + JSR RdBlkAX + BCS RenErr ;Report errors + + LDY rnPtr ;Change the header's name to match the owner's new name + LDA pathBuf,Y ;Get local name length again + ORA #$E0 ;Assume it's a vol/subdir header + JSR MovRootName + BCS RenErr +RenameDone JMP ReviseDirZ ;End by updating all path directories + +MovRootName LDX #$00 +:loop STA genBuf+4,X + INX + INY + LDA pathBuf,Y + BNE :loop + JMP WrtGBuf ;Write changed header block + +*------------------------------------------------- + +RenamePath LDY #c_newPath ;Get address to new pathname + LDA (parm),Y + INY + STA tPath + LDA (parm),Y ;Set up for syntaxing routine (SynPath) + STA tPath+1 + JMP SynPath ;Go syntax it. (Ret last local name length in Y) + +GetNamePtr LDY #$00 ;Return pointer to first name of path + BIT prfxFlg ;Is this a prefixed name? + BMI :1 ;Branch if not + LDY NewPfxPtr +:1 LDX #$00 + RTS + +*********************************************************** +* Destroy Call + +Destroy JSR FindFile ;Look for file to be wiped out + BCS DstryErr ;Pass back any error + JSR TestOpen ;Is this file open? + LDA totEnt + BNE :3 ;Branch if file open + + STZ reqL ;Force proper free count in volume + STZ reqH ;(no disk access occurs if already proper) + JSR TestFreeBlk + BCC :1 + CMP #volumeFull ;Was it just a full disk? + BNE DstryErr ;Nope, report error + +:1 LDA d_file+d_attr ;Make sure it's ok to destroy this file + AND #destroyEnable + BNE :2 ;Branch if ok + LDA #invalidAccess ;Tell user it's not kosher + JSR SysErr ;(returns to caller of destroy) + +:2 LDA DevNum ;Before going thru deallocation, + JSR TestWrProtZ ; test for write protected hardware + BCS DstryErr + LDA d_file+d_first ;"DeTree" needs first block addr + STA firstBlkL ; which is file's keyblk + LDA d_file+d_first+1 + STA firstBlkH + LDA d_file+d_stor ;Find out which storage type + AND #$F0 ;Strip off name length + CMP #{tree+1}*16 ;Is it a seed, sapling, or tree? + BCC DstryTree ;Branch if it is + BRA DestroyDir ;Otherwise test for directory destroy + +:3 LDA #fileBusy +DstryErr SEC ;Inform user that file can't + RTS ; be destroyed at this time + +DstryTree EQU * ;Destroy a tree file + STA storType ;Save storage type + LDX #$05 + LDA #$00 ;Set "DeTree" input variables +:loop STA storType,X ;Variables must be + DEX ; in order:deBlock, dTree, dSap, dSeed + BNE :loop ;Loop until all set to zero + LDA #$02 + STA dSeed+1 ;This avoids an extra file i/o + +********************** see rev note #73 ********************** +********************* see rev note #49 ********************** +********************** see rev note #41 ********************* + + INC delFlag ;Don't allow DeTree to zero index blocks + JSR DeTree ;Make trees and saplings into seeds + DEC delFlag ;Reset flag + BCS DstryErr1 ;(de-evolution) + +DstryLast LDX firstBlkH + LDA firstBlkL ;Now deallocate seed + JSR Dealloc + BCS DstryErr1 + JSR UpdateBitMap + +DstryErr1 PHA ;Save error code (if any) + LDA #$00 ;Update directory to free entry space + STA d_file+d_stor + CMP h_fileCnt ;File entry wrap? + BNE :2 ;Branch if no carry adjustment + DEC h_fileCnt+1 ;Take carry from high byte of file entries +:2 DEC h_fileCnt ;Mark header with one less file + JSR DvcbRev ;Go update block count in VCB + JSR ReviseDir ;Update directory last... + TAX + PLA + BCC :3 + TXA +:3 CMP #badSystemCall + RTS + +*------------------------------------------------- +* Update free block count in VCB + +DvcbRev LDY vcbPtr + LDA deBlock ;Add blks freed to + ADC vcb+vcbFreeBlks,Y; total free blks + STA vcb+vcbFreeBlks,Y;Update current free block count + LDA deBlock+1 + ADC vcb+vcbFreeBlks+1,Y + STA vcb+vcbFreeBlks+1,Y + LDA #$00 ;Force rescan for free blks + STA vcb+vcbCurrBitMap,Y; from first bitmap + RTS + +ToDstLast BCC DstryLast ;Always + +DestroyDir CMP #directoryFile*16;Is this a directory file? + BNE DirCompErr ;No, report file incompatible + JSR FindBitMap ;Make sure a buffer is available for the bitmap + BCS DstryDirErr + + LDA d_file+d_first ;Read in first block + STA blockNum ; of directory into genBuf + LDA d_file+d_first+1 + STA blockNum+1 + JSR RdGBuf + BCS DstryDirErr + + LDA genBuf+4+hFileCnt;Find out if any files exist on this directory + BNE DstryDirAccs ;Branch if any exist + LDA genBuf+4+hFileCnt+1 + BEQ DstryDir1 +DstryDirAccs LDA #invalidAccess + JSR SysErr + +DstryDir1 STA genBuf+4 ;Make it an invalid subdir + JSR WrtGBuf + BCS DstryDirErr +:loop LDA genBuf+2 ;Get forward link + CMP #$01 ;Test for no link + LDX genBuf+3 + BNE :1 + BCC ToDstLast ;If no link, then finished + +:1 JSR Dealloc ;Free this block + BCS DstryDirErr + LDA genBuf+2 + LDX genBuf+3 + JSR RdBlkAX + BCC :loop ;Loop until all are freed +DstryDirErr RTS + +DirCompErr LDA #badFileFormat ;File is not compatible + JSR SysErr + +* Mark as FCB as dirty so the directory will be flushed on 'flush' + +FCBUsed PHA ;Save regs + TYA + PHA + LDY fcbPtr + LDA fcb+fcbDirty,Y ;Fetch current fcbDirty byte + ORA #fcbMod ;Mark FCB as dirty + STA fcb+fcbDirty,Y ;Save it back + PLA + TAY ; & restore regs + PLA + RTS \ No newline at end of file diff --git a/MLI.SRC/DETREE.S b/MLI.SRC/DETREE.S new file mode 100644 index 0000000..873785e --- /dev/null +++ b/MLI.SRC/DETREE.S @@ -0,0 +1,259 @@ +************************************************** +* "DeTree" deallocates blocks from tree files. +* +* It is assumed that the device preselected and the 'genBuf' may be used. +* +* On entry the following values must be set: +* storType = storage type in upper nibble, lower nibble is undisturbed. +* firstBlkL & firstBlkH = first block of file (index or data) +* deBlock = 0 (see below) +* dTree = ptr to 1st block with stuff to be deallocated at tree level. +* dSap = ptr to 1st block at sapling level +* dSeed = byte (0-511) position to be zeroed from (inclusive). +* NB. There is 2 special cases when dSeed = 512 +* Case 1) when EOF is set at a block boundary +* Case 2) when file is destroyed +* +* On exit: +* storType = modified result of storage type (if applicable) +* firstBlkL & H = modified if storage type changed. +* deBlock = total number of blocks freed at all levels. +* dTree, dSap, dSeed unchanged. +* +* To trim a tree to a seed file, both dTree and dSap must be zero. +* To go from tree to sapling, dTree alone must be zero. + +DeTree LDA storType ;Which flavor of tree? + CMP #sapling*16 ;Is it a 'seed' (<=$1F) + BCC SeedDeAlloc ;Yes + CMP #tree*16 ;Maybe a 'sapling'? + BCC SapDeAlloc + CMP #{tree+1}*16 ;Well, at least be certain it's a 'tree' + BCC TreeDeAlloc ;Branch if it is + LDA #badBlockErr ;Block allocation error + JSR SysDeath ;Should never have been called + +SeedDeAlloc LDA dSap + ORA dTree + BNE BummErr + JMP SeedDeAlloc0 ;Trim to a seed + +SapDeAlloc LDA dTree + BNE BummErr + JMP SapDeAlloc0 ;Trim to a sapling + +TreeDeAlloc LDA #128 + STA topDest ;For tree top, start at end, work backwards +:loop1 JSR RdKeyBlk ;Read specified first block into genBuf + BCS BummErr ;Return all errors + LDY topDest ;Get current pointer to top indexes + CPY dTree ;Have enough sapling indexes been deallocated? + BEQ TreeDel17 ;Yes, now deallocate top guys! + + LDX #$07 ;Buffer up to 8 sapling index block addrs +:loop2 LDA genBuf,Y ;Fetch low block address + STA deAlocBufL,X ; and save it + ORA genBuf+$100,Y ;Is it a real block that is allocated? + BEQ :1 ;It's a phantom block + LDA genBuf+$100,Y ;Fetch hi block addr + STA deAlocBufH,X ; and save it + DEX ;Decrement and test for dealoc buf filled + BMI :2 ;Branch if we've fetched 8 addresses +:1 DEY ;Look now for end of deallocation limit + CPY dTree ;Is this the last position on tree level? + BNE :loop2 ;No + + INY + LDA #$00 ;Fill rest of deAloc buffer with NULL addresses +:loop3 STA deAlocBufL,X + STA deAlocBufH,X + DEX + BPL :loop3 ;Loop until filled + +:2 DEY ;Decrement to prepare for next time + STY topDest ;save index + + LDX #$07 +:loop4 STX dTempX ;Save index to deAloc buf + LDA deAlocBufL,X + STA blockNum + ORA deAlocBufH,X ;Are we finished? + BEQ :loop1 ;Branch if done with this level + + LDA deAlocBufH,X ;Complete address with hi byte + STA blockNum+1 + JSR RdGBuf ;Read sapling level into genBuf + BCS BummErr ;Return any errors + JSR DeAllocBlk ;Go free all data indexes in this block + BCS BummErr + JSR WrtGBuf + BCS BummErr + LDX dTempX ;Restore index to dealloc buff + DEX ;Are there more to free? + BPL :loop4 ;Branch if there are + BMI :loop1 ;Branch always to do next bunch + +deAlocTreeDone EQU * +deAlocSapDone EQU * +BummErr RTS + +TreeDel17 LDY dTree ;Now deallocate all tree level + INY ; blocks greater than specified block + JSR DeAlocBlkZ ;(tree top in genBuf) + BCS BummErr ;Report any errors + JSR WrtGBuf ;Write updated top back to disk + BCS BummErr + LDY dTree ;Now figure out if tree can become sapling + BEQ :11 ;Branch if it can! + + LDA genBuf,Y ;Otherwise, continue with partial + STA blockNum ; deallocation of last sapling index + ORA genBuf+$100,Y ;Is there such a sapling index block? + BEQ deAlocTreeDone ;All done if not! + LDA genBuf+$100,Y ;Read in sapling level to be modified + STA blockNum+1 + JSR RdGBuf ;Read 'highest' sapling index into genBuf + BCC SapDeAllocZ + RTS + +:11 JSR Shrink ;Shrink tree to sapling + BCS BummErr + +* Deallocate a sapling file + +SapDeAlloc0 JSR RdKeyBlk ;Read specified only sapling level index into gbuf + BCS BummErr +SapDeAllocZ LDY dSap ;fetch pointer to last of desirable indexes + INY ;Bump to the first undesirable + BEQ :21 ;branch if all are desirable + JSR DeAlocBlkZ ;Deallocate all indexes above appointed + BCS BummErr + JSR WrtGBuf ;Update disk with remaining indexes + BCS BummErr + +:21 LDY dSap ;Now prepare to cleanup last data block + BEQ :22 ;Branch if there is a posiblity of making it a seed +:loop LDA genBuf,Y ;Fetch low order data block addr + STA blockNum + ORA genBuf+$100,Y ;Is it a real block? + BEQ deAlocSapDone ;We're done if not + LDA genBuf+$100,Y + STA blockNum+1 + JSR RdGBuf ;Go read data block into gbuf + BCC SeedDeAllocZ ;Branch if good read + RTS ;Otherwise return error + +:22 LDA dTree ;Are both tree and sap levels zero? + BNE :loop ;Branch if not. + JSR Shrink ;Reduce this sap to a seed + BCS BumErr1 + +* If no error, drop into SeedDeAlloc0 + +SeedDeAlloc0 JSR RdKeyBlk ;Go read only data block + BCS BumErr1 ;Report any errors +SeedDeAllocZ LDY dSeed+1 ;Check hi byte for no deletion + BEQ :31 ;Branch if all of second page is to be deleted + DEY ;If dseed>$200 then were all done! + BNE BumErr1 ;Branch if that's the case + + LDY dSeed ;Clear only bytes >= dseed +:31 LDA #$00 +:loop1 STA genBuf+$100,Y ;Zero out unwanted data + INY + BNE :loop1 + LDY dSeed+1 ;Was that all? + BNE :32 ;Branch if it was + LDY dSeed +:loop2 STA genBuf,Y + INY + BNE :loop2 +:32 JMP WrtGBuf ;Update data block to disk +BumErr1 RTS + +RdKeyBlk LDA firstBlkL ;Read specified first + LDX firstBlkH ; block into genBbuf + JMP RdBlkAX ;Go do it! + +************************************************** +* Beware that dealloc may bring in a new bitmap block +* and may destroy locations 46 and 47 which use to +* point to the current index block. +************************************************** +Shrink LDX firstBlkH ;First deallocate top block + TXA + PHA + LDA firstBlkL + PHA ;Save block address of this index block + JSR Dealloc ;Go do it + PLA + STA blockNum ;Set master of sapling index block address + PLA + STA blockNum+1 + BCS :Ret ;report any errors + + LDA genBuf ;Get first block at lower level + STA firstBlkL + LDA genBuf+$100 + STA firstBlkH + LDY #$00 + JSR SwapMe + SEC ;Now change file type, from + LDA storType ; tree to sapling, + SBC #$10 ; or from sapling to seed! + STA storType + JSR WrtGBuf +:Ret RTS + +*------------------------------------------------- +* Free master index/index block entries +* If DeAlockBlkZ is used, (Y) must be set correctly + +DeAllocBlk LDY #$00 ;Start at the beginning +DeAlocBlkZ LDA blockNum ;Save disk address + PHA ; of genBuf's data + LDA blockNum+1 + PHA + +:loop STY sapPtr ;Save current index + LDA genBuf,Y ;Get address (low) of block to be deallocated + CMP #$01 ;Test for NULL block + LDX genBuf+$100,Y ;Get the rest of the block address + BNE :1 ;Branch if not NULL + BCC :2 ;Skip it if NULL + +:1 JSR Dealloc ;Free it up on volume bitMap + BCS :3 ;Return any error + LDY sapPtr ;Get index to sapling level index block again + JSR SwapMe + +:2 INY ;Point at next block address + BNE :loop ;Branch if more to deallocate (or test) + + CLC ;Indicate no error +:3 TAX ;Save error code, if any + PLA + STA blockNum+1 + PLA + STA blockNum + TXA ;Restore return code + RTS + +************************************************** +* delFlag = 0 - Not called by Destroy +* delFlag = 1 - Called Destroy ie swapping +* Swap the Lo & Hi indices making up a disk addr +* so that disk recovery programs may be able +* to undelete a destroyed file + +SwapMe LDA delFlag ;Are we swapping or zeroing ? + BNE :1 ;Skip if swapping + TAX ;Make X a 0 + BEQ :2 ;0 the index (always taken) + +:1 LDX genBuf+$100,Y ;Get index, hi + LDA genBuf,Y ;Get index, lo +:2 STA genBuf+$100,Y ;Save index, hi + TXA + STA genBuf,Y ;Save index, lo + RTS ;We're done \ No newline at end of file diff --git a/MLI.SRC/DEVSRCH.S b/MLI.SRC/DEVSRCH.S new file mode 100644 index 0000000..4903fd4 --- /dev/null +++ b/MLI.SRC/DEVSRCH.S @@ -0,0 +1,667 @@ +*********************************************************** +Greet LDA SPKR ;Give a 'click' + STA CLR80VID ;Disable 80 columns (rev-e) + STA CLR80COL ;Disable '80store' + JSR SetNorm + JSR Init + JSR SetVid + JSR SetKBD + CLD + +* Note: interrupts are not turn off + + JSR HOME ;Clear screen + LDX #p8VerMsg-apple2Msg-1;Move greeting to screen +:loop1 LDA apple2Msg,X ;"apple ii" + STA SLIN09+20-4,X ;$4A8 starts the line + DEX + BPL :loop1 + + LDX #blanks-p8VerMsg-1 +:loop2 LDA p8VerMsg,X ;"prodos (version) (date)" + STA SLIN11+20-grtLnZ,X + DEX + BPL :loop2 + + LDX #cpyRhtMsg-blanks-1 +:loop3 LDA blanks,X ;(blank line) + STA SLIN13+20-grtLnZZ,X + DEX + BPL :loop3 + + LDX #rsvdMsg-cpyRhtMsg-1 +:loop4 LDA cpyRhtMsg,X ;Copyright Message + STA SLIN22,X + DEX + BPL :loop4 + + LDX #endGreetMsg-rsvdMsg-1 +:loop5 LDA rsvdMsg,X ;Rights message + STA SLIN23+20-grtLnZZZ,X + DEX + BPL :loop5 + + SEC + JSR IDroutine + BCS NotIIgs ;Not IIgs + LDA #$80 + TRB NEWVIDEO ;Enable SuperHires +NotIIgs LDA SPKR + RTS +*************************************************** +* +* This routine finds all disk devices plugged into the +* system's slots that follow apple's disk id convention. +* The routine sets up the address and device table in +* ProDOS's system global page. 13-sector disk ]['s are +* not configured since they may not function properly in +* the 16 sector mode... +* +* Profiles, and other intelligent devices, are expected to +* have ROMs containing the disk I/O drivers. +* +* This routine was revved 8/21/86 to make sure that correct number +* of smartport devices are installed, and that disk ][s are always +* searched last by placing them at the end of the list. Disk ][ unit +* numbers are stacked at the end of the device table, while regular +* devices are stacked at the beginning. After all slots have been +* searched the two lists are combined. see note 60. +* +statusList EQU * +spDevStatus EQU * +numDevices DS 8,0 ;Eight bytes for smartport call + +maxUnitP1 EQU * ;# of units connected to SP+1 +driverAdr DA $0000 +dsk2Idx DB $00 +diskInSlot2 DB $00 ;msb set if drive in slot 2 + +*------------------------------------------------- +DevSrch STZ dst + STZ dst+1 + STZ indrcn ;Set up for search + LDX #$FF + STX DevCnt ;Device count=none + LDA #14 ;Start disk ][ area at the end of devlist + STA dsk2Idx +*********************** see note #65 *********************** +* +* Make a quick check of slot 2. If there is a disk card there, +* clear the msb of diskInSlot2. This will limit the number of +* devices in any slot 5 smartport card to 2. +* + LDA #$C2 + STA indrcn+1 ;Check slot 2 + JSR CmpID ;Is there a disk card in slot 2? + ROR diskInSlot2 ;Clear msb if so, set otherwise + LDA #$C7 ;Search slots from high to low + STA indrcn+1 +FindDsk EQU * + +***************** code here became a subroutine **************** +************************* see note #65 ************************* + + JSR CmpID + BCS NxtDsk ;Carry set means no card this slot + LDA (indrcn),Y ;Check last byte of $Cn ROM + BEQ DiskII ;If =00 then 16 sector disk ][ + CMP #$FF ;If =ff then 13 sector disk ][ + BCS NxtDsk ;Ignore if 13 sector boot ROM + STA driverAdr ;else, assume it's an intelligent disk for now. + + LDY #$07 ;Check for a SmartPort device + LDA (indrcn),Y + BNE NoSPort + JMP SmartPort + +* Ref ProDOS Tech Ref (ROM Code conventions) + +NoSPort LDY #$FE ;Get attributes byte & verify it + LDA (indrcn),Y + AND #$03 ; provides read, write, and status calls + CMP #$03 ; (should be #$07) + SEC ;Assume it is a bozo brand disk... + BNE NxtDsk + JSR SetDevID + CLC + PHP ;Remember that it's not a disk ][ + LSR ;Move # of units (0=1, 1=2) to carry + LDA indrcn+1 ;(driverAdr)=low entry addr, save hi addr + BNE ADevice ;Always + +DiskII STA devID ;Disk ]['s have null attributes + SEC + PHP ;Remember it's a disk ][ + LDA D2BlkIO + STA driverAdr + LDA D2BlkIO+1 + +* The carry is already set telling InstallDev +* to install two devices for disk ][s. + +ADevice STA driverAdr+1 + JSR InstallDev ;Install one or two devices this slot + PLP ;Get back if it's disk ][ + BCC NoDisk2 + DEX ;Move the list pointer back + DEX ;(installdev left X set) + STX DevCnt + DEC dsk2Idx ;Increase the disk ][ index + DEC dsk2Idx + LDY dsk2Idx + INX ;Increase X in case =$FF + LDA DevLst+1,X + STA DevLst,Y + LDA DevLst,X + STA DevLst+1,Y + DEX ;Back to DevCnt again + +NoDisk2 EQU * +NxtDsk2 CLC +NxtDsk JSR SlotROM ;Test for clock & other devices + DEC indrcn+1 ;Set up for next lower slot + LDA indrcn+1 ;Have all slots been checked? + AND #$07 + BNE FindDsk ;No + + JSR ScanAll4SP ;Mirror smartport devices + +* Now copy the disk ][ list to the end of the regular list +* start by making the device count include disk ][s + + LDX DevCnt ;Load up current devcnt-1 + LDA #14 + SEC + SBC dsk2Idx + BEQ D2Snone ;If there were no disk ][s, forget it + CLC + ADC DevCnt ;Sum of disk ][s and others + STA DevCnt + INX ;Move to open space in regular list + + LDY #13 ;First disk ][ entry +MLab LDA DevLst,Y + PHA + LDA DevLst,X + STA DevLst,Y + PLA + STA DevLst,X + INX + DEY + STY dsk2Idx ;Use as a temp + CPX dsk2Idx + BCC MLab ;Continue 'til indices cross... + +D2Snone LDY #$00 + LDX DevCnt ;Now change the device order +:loop LDA DevLst,X ; so that the boot device + PHA + AND #$7F + EOR DevNum ; will have highest priority + ASL + BNE :1 + PLA + INY +:1 DEX + BPL :loop + + LDX DevCnt ;Now reverse order of search, hi to lo + TYA ;Was boot device found? + BEQ :2 ;No + LDA DevNum ;Make boot device first in search order + STA DevLst,X + DEX + BMI DevSrchEnd ;Branch if only one device + DEY ;Is this a 2-drive device? + BEQ :2 ;No + EOR #$80 ;Make boot device, drive 2 next + STA DevLst,X + DEX + BMI DevSrchEnd ;Branch if only one device, 2 drives +:2 PLA + STA DevLst,X + DEX + BPL :2 + +DevSrchEnd JSR Fndtrd ;Save accumuated machine ID + BEQ WhosIt + STA MachID + RTS +WhosIt JMP WhatsIt + +staDrv ORA devID ;Combine with attributes (0SSS IIII) + LDX DevCnt + INX ;Put device number into device list + STA DevLst,X + ASL ;Now form drive 2 device number, if any (SSSI III0) + RTS + +SlotROM BCC IsROM ;Branch if disk drive + LDY #$06 ;Test this slot for clock card +:1 LDA (indrcn),Y + CMP clkID,Y ;Branch if not clock + BNE NotClk + DEY + DEY + BPL :1 + + LDA indrcn+1 ;transfer high slot addr minus $c1 (default) + SBC #$C1 ; to relocate references to clock ROM + STA clock64 + LDA #$4C ;Also enable jump vector in globals + STA DateTime + LDA apple ;Mark clock as present + BEQ DevSrchEnd + ORA #$01 + STA apple + BNE IsROM ;Always + +NotClk LDY #$05 ;Test for 80-col card + LDA (indrcn),Y ;One byte at a time + CMP #$38 + BNE NotCons + LDY #$07 ;Test values are same as Pascal's + LDA (indrcn),Y + CMP #$18 + BNE NotCons + LDY #$0B + LDA (indrcn),Y + DEC + BNE NotCons + INY + LDA (indrcn),Y + AND #$F0 ;Mask off low nibble + CMP #$80 ;Generic for 80-col card + BNE NotCons + LDA apple + BEQ DevSrchEnd + ORA #$02 + STA apple ;Mark config for 80 col. present + BNE IsROM +NotCons LDX #$00 ;Test for any ROM + LDA (indrcn) + CMP #$FF ;Test for apple /// non slot + BEQ NoROM ;Branch if invalid ROM + +TestROM CMP (indrcn) ;Look for floating bus + BNE NoROM + INX ;Loop 256 times + BNE TestROM + +IsROM LDA indrcn+1 ;Mark a bit in slot byte + AND #$07 ; to indicate rom present + TAX + LDA SltBit,X + ORA SltByt + STA SltByt +NoROM RTS + +*------------------------------------------------- +D2BlkIO DA RWTS ;Addr of Disk ][ driver rtn +diskID EQU * +clkID HEX 082028005803703C +SltBit HEX 0002040810204080 + +*------------------------------------------------- +* Compute autostart ROM checksum + +Fndtrd CLC + LDY SltBit ;Should be zero +:loop LDA (look),Y ;Point to $FB09 ("APPLE II" in ROM) + AND #%11011111 ;To uppercase + ADC SltBit + STA SltBit + ROL SltBit + INY + CPY SltBit+3 ;Do for 8 bytes + BNE :loop + + TYA ;(A)=$08 now + ASL + ASL + ASL + ASL + TAY + EOR SltBit ;Turn msb on + ADC #$0B ;Add a fudge factor + BNE :1 ;That's a clone + LDA apple ;Pass the test + RTS + +:1 LDA #$00 + RTS + +*------------------------------------------------- +* Install the appropriate device-driver +* address in the system global page +* (driverAdr)= addr of driver + +InstallDev EQU * ;Made a sub and + PHP ; how many drives (carry) + LDA indrcn+1 ;Get index to global device table + AND #$07 ; for this slot + ASL + TAY ; ... into Y-reg + ASL + ASL ;Now form device number + ASL ;(A)=0SSS 0000 + JSR staDrv + PLP + ROR ;If 2 drives, then bit7=1 (DSSS IIII) + BPL :1 ;Branch if a 1-drive device (i.e. profile) + INX ; else presume that second drive is present + STA DevLst,X + +:1 STX DevCnt ;Save updated device count + ASL ;Shift # of drives back into carry + LDA driverAdr ;Get low address of device driver + STA DevAdr01,Y + BCC :2 ;Branch if single drive + STA DevAdr02,Y +:2 LDA driverAdr+1 ;Get high address of device driver + STA DevAdr01+1,Y + BCC :Ret + STA DevAdr02+1,Y +:Ret RTS + +*------------------------------------------------- +* +* This piece of code (not a subroutine) is branched to if the slot +* search code finds a smartport device. It does a smartport status +* call (code = 0) to determine the number of devices connected to +* the "card". It then installs from 0..4 units in the table. +* +SmartPort JSR SetDevID ;Set up the devID byte from attributes +* +* Only map more than two devices if card is in slot 5 +* + LDA indrcn+1 ;indrcn+1 + STA driverAdr+1 ;Didn't set this yet + +* Do the call to smartport to get the number of devices + + LDA driverAdr + STA psCall+1 ;Modify operand + CLC + ADC #$03 + STA spCall+1 + LDA driverAdr+1 ;No page cross possible + STA spCall+2 +********************************************* +* patch 74 +********************************************* + STA psCall+2 ;Modify operand + ASL ;Convert $Cn to $n0 + ASL + ASL + ASL + STA unitNum + STZ dhpCmd ;Force a ProDOS status call + STZ bufPtr ;Dummy pointer + STZ blockNum ;Number of bytes to transfer + STZ blockNum+1 ;Number of bytes to transfer + LDA #$10 ;Dummy pointer should be <>0 + STA bufPtr+1 ;Dummy pointer +********************************************* +* patch 74 +********************************************* +psCall EQU * ;ProDOS status call + JSR $0000 ;Filled in by above code + LDY #$FB + LDA (indrcn),Y ;SmartPort ID type byte + AND #$02 ;Is it a SCSI card? + BEQ :1 ;No + + STA unitNbr ;=2 + JSR spCall + DB $00 ; Do status call on SCSI unit + DA spCallParms + +* Determine how many devices are connected +* to the interface @ this slot +* Ref SmartPort TN #20, #21 + +:1 STZ unitNbr ;Get status of the + JSR spCall ; SmartPort host + DB $00 + DA spCallParms + +* Don't add devices if there are none connected + + LDA numDevices ;Get dev cnt + BEQ DoneSP + +* Do the first and second device if they exist + + CMP #$02 ;C=1 if if 2,3,4 + JSR InstallDev + +* Do the third and fourth drives if they exist +* They cannot exist for any card other than one in slot 5 + + LDA indrcn+1 + CMP #$C5 ;If not slot 5, no mirroring + BNE DoneSP +********************* see note #65 ********************* +* +* If this is slot 5, and if there is a disk card in slot 2, +* only install 2 devices this slot. Thank you. + + BIT diskInSlot2 ;If there a disk card in slot 2? + BPL DoneSP ;Yes + + LDA numDevices + CMP #$03 ;More than 2 devices in slot 5? + BCC DoneSP ;No (C=1 if 3,4,...) + CMP #$04 ;C=1 if 4,5,6,...(More than 3 devices are connected) + LDA #$C2 ;Make it think it's slot 2 + STA indrcn+1 + JSR InstallDev + LDA #$C5 ;Reset back to slot 5 + STA indrcn+1 +DoneSP JMP NxtDsk2 ;We know it's a disk device + +* Ref ProDOS Tech Ref (ROM Code conventions) +* pg 7-14 BAP + +SetDevID LDY #$FE +SetDevIDZ LDA (indrcn),Y ;Get attribute byte + LSR + LSR + LSR + LSR + STA devID + RTS +************** see note #65 **************** +* +* input: indrcn - point to $Cn00 of mystery card +* output: carry clear if disk card here, set ow +* y $ff +* +CmpID LDA CLRROM ;Release $C800 space from previous slot + LDY #$05 +:loop LDA (indrcn),Y ;Compare ID bytes + CMP diskID,Y ;$Cn07=don't care + SEC + BNE :Ret ;($Cn05)=03 + DEY ;($Cn03)=00 + DEY ;($Cn01)=20 + BPL :loop ;Loop until all 4 ID bytes match + CLC +:Ret RTS + +*------------------------------------------------- +* SmartPort parameter area + +spCallParms DB $03 ;number of parameters +unitNbr DB $00 ;=$00,$01-$7E + DA statusList + DB $00 ;status code = 0 (code for general status) + +* Each offsets below can be considered as 000D SSS0 +* Offsets for slot 0 dr1/2 & slot 3 dr 2 +* are not represented here +* Slot 3 dr 2 is reserved for /RAM + +mapOffset EQU * + DB $06 ;slot 3, dr 1 + DB $1E ;slot 7, dr 2 + DB $0E ;slot 7, dr 1 + DB $1C ;slot 6, dr 2 + DB $0C ;slot 6, dr 1 + DB $1A ;slot 5, dr 2 + DB $0A ;slot 5, dr 1 + DB $14 ;slot 2, dr 2 + DB $04 ;slot 2, dr 1 + DB $12 ;slot 1, dr 2 + DB $02 ;slot 1, dr 1 + DB $18 ;slot 4, dr 2 + DB $08 ;slot 4, dr 1 + +spCall JMP $0000 + +*------------------------------------------------- +* This routine will scan all slots for SmartPort +* interfaces. If more than 2 block devices are +* connected to the SmartPort Host, it will mirror +* at most 2 more block devices + +ScanAll4SP STZ indrcn + LDA #$C7 ;Start w/slot 7 + STA indrcn+1 +ScanLoop JSR Chk4SP ;Does slot have a SP host? + BCS ChkNxtSlot ;No + + LDY #$FF + LDA (indrcn),Y ;Get LSB of block dev driver + CLC + ADC #$03 ;Add 3 to get the + STA spCall+1 ; SmartPort entry point + LDA indrcn+1 + STA spCall+2 + DEY ;=$FE + JSR SetDevIDZ ;Get attributes + STZ unitNbr ;Get status of SmartPort Host + JSR spCall + DB $00 + DA spCallParms + + LDA numDevices + CMP #$03 ;More than 2 devices? + BCC ChkNxtSlot ;No + INC ;1 more for easier comparision + STA maxUnitP1 ;=4,5,6,... + LDA #$03 + LDX spCall+2 + CPX #$C5 ;Slot 5? + BNE ChkDevLoop ;No + BIT diskInSlot2 ;If there a disk card in slot 2? + BPL ChkDevLoop ;Yes + +* Slot 5 and no disk card in slot 2. +* 4 of the devices connected to this +* slot had already been dealt with. + + LDA #$05 +ChkDevLoop CMP maxUnitP1 + BCS ChkNxtSlot + + STA unitNbr ;Get device status of this unit + JSR spCall ;Ref pg 122 IIGS Firmware + DB $00 + DA spCallParms + LDA spDevStatus ;Is it block dev? + BMI MirrorDev ;Yes + +* No, it's a char dev + +CkNxtChnDev LDA unitNbr + INC + BRA ChkDevLoop ;Loop to check next dev in chain + +ChkNxtSlot DEC indrcn+1 ;Set up for next lower slot + LDA indrcn+1 + CMP #$C0 ;Have all slots been checked? + BNE ScanLoop ;No + RTS + +*------------------------------------------------- +* We have more than 2 devices connected to +* the SmartPort Host in this slot + +MirrorDev LDX #12 ;Search thru 13 entries +MirrorLoop LDY mapOffset,X ;Get offset + LDA DevAdr01,Y ;Check if there is an + CMP #gNoDev + BEQ MapDevice ;Got an available entry +NoMapping DEX + BPL MirrorLoop + RTS + +*------------------------------------------------- +* Install SmartPort driver for additional +* block devices connected to slot + +MapDevice LDA indrcn+1 ;Save slot # + PHA + PHX + PHY + TYA + LSR ;0000 DSSS + AND #$07 ;0000 0SSS + ORA #$C0 + STA indrcn+1 ;Chk if there is a SmartPort + JSR Chk4SP ; Host in this slot + PLY + PLX + PLA + STA indrcn+1 + BCC NoMapping ;Yes, don't mirror the dev + + JSR LC1In ;Switch in LC bank 1 + TYA ;000D SSS0 + LSR + TAX ;0000 DSSS ($01-$0F; $00,$08,$0B-invalid) + LDA unitNbr + STA spUnits-1,X + LDA spCall+1 ;Save actual SmartPort driver + STA spDrvAdrL-1,X + LDA spCall+2 + STA spDrvAdrH-1,X + LDA RDROM2 + INC DevCnt + LDX DevCnt + TYA + LSR ;0000 DSSS + CMP #$08 + BCC :1 ;Drive 1 + SBC #$08 + ORA #$08 ;Add back drive 2 bit +:1 ASL + ASL + ASL + ASL + ORA devID ;DSSS IIII + STA DevLst,X + LDA #MirrorDevEntry + STA DevAdr01+1,Y + BRA CkNxtChnDev + +*------------------------------------------------- +* Exit +* C=0 if there is a SmartPort card/interface +* in the slot + +Chk4SP JSR CmpID + BCS :Rtn ;No disk card in this slot + SEC + LDY #$07 ;Is this the SmartPort + LDA (indrcn),Y ; signature byte? + BNE :Rtn ;No + CLC +:Rtn RTS \ No newline at end of file diff --git a/MLI.SRC/EQUATES.S b/MLI.SRC/EQUATES.S new file mode 100644 index 0000000..0f42fe7 --- /dev/null +++ b/MLI.SRC/EQUATES.S @@ -0,0 +1,321 @@ +*------------------------------------------------- +* Disassembler: The Flaming Bird Disassembler +* Assembler : Merlin16+ +* Merlin16+ is chosen because it can assemble +* 65816 opcodes unlike EdAsm (ProDOS) which is +* an 8-bit assembler. Furthermore, local labels +* may be used; that should ease the need to +* create trivial labels. +* NB. Merlin16+ defaults to case-sensitive labels +* & using blank lines should improve readibility. +* Most of the comments and labels are from the +* source code of ProDOS v1.7 +* Whenever possible a more descriptive label is +* used in place of the original. +*------------------------------------------------- +* Global Equates + +MSLOT EQU $07F8 +KBD EQU $C000 +CLR80COL EQU $C000 ;Disable 80-column memory mapping (Write) +SET80COL EQU $C001 ;Enable 80-column memory mapping (WR-only) +RDMAINRAM EQU $C002 +RDCARDRAM EQU $C003 +WRMAINRAM EQU $C004 ;Write data to main ram +WRCARDRAM EQU $C005 ;Write data to card ram +SETSTDZP EQU $C008 ;Enable regular ZP,STK,LC +SETALTZP EQU $C009 ;Enable alternate ZP,STK,LC +SETINTC3ROM EQU $C00A ;Internal 80-col card ROM +SETSLOTC3ROM EQU $C00B ;External slot 3 ROM +CLR80VID EQU $C00C ;Disable 80 column hardware. +CLRALTCHAR EQU $C00E ;Switch in primary character set. +KBDSTROBE EQU $C010 +RD80COL EQU $C018 +NEWVIDEO EQU $C029 +SPKR EQU $C030 +TXTSET EQU $C051 +TXTPAGE1 EQU $C054 +TXTPAGE2 EQU $C055 +STATEREG EQU $C068 ;Cortland memory state register +ROMIN2 EQU $C081 ;swap rom in w/o w-prot ram +RDROM2 EQU $C082 ;swap rom in, write protect ram +LCBANK2 EQU $C083 ;Enable 2nd bank of LC +LCBANK1 EQU $C08B ;Enable 1st bank of LC +CLRROM EQU $CFFF + +* 80 col card subroutines + +AuxMove EQU $C311 ;monitor move data routine +Xfer EQU $C314 ;monitor XFER control + +* Apple // Monitor subroutines + +ROMIrq EQU $FA41 ;monitor rom irq entry +Init EQU $FB2F ;Text pg1;text mode;sets 40/80 col +SETTXT EQU $FB39 +TABV EQU $FB5B +SETPWRC EQU $FB6F +VERSION EQU $FBB3 +BELL1 EQU $FBDD +HOME EQU $FC58 +CLREOL EQU $FC9C +RDKEY EQU $FD0C +CROUT EQU $FD8E +COUT EQU $FDED +IDroutine EQU $FE1F ;IIgs ID routine +SetInv EQU $FE80 +SetNorm EQU $FE84 ;Normal white text on black backround. +SetKBD EQU $FE89 ;Does an IN#1. +SetVid EQU $FE93 ;Puts COUT1 in CSW. +BELL EQU $FF3A +OLDRST EQU $FF59 ;monitor reset entry + +* GS/OS vectors/flags + +GSOS EQU $E100A8 +GSOS2 EQU $E100B0 +OS_BOOT EQU $E100BD + +inBuf EQU $0200 ;Input buffer +pnBuf EQU $0280 ;pathname buffer +EnterCard EQU $0200 ;AuxMem +RAMdest EQU $0200 ;AuxMem +RAMsrc EQU $5100 ;Load addr +LCdest EQU $FF00 ;Execution addr of RAM disk handler + +* Page 3 vectors + +SOFTEV EQU $03F2 +PWREDUP EQU $03F4 +NMI EQU $03FB +PassIt EQU $03ED + +* load addr exec addr Description +*========================================================================================= +MLI_0 EQU $2000 ;$2000-$2c7F $2000-$2c7F MLI loader/relocater +RAM_1 EQU MLI_0+$C80 ;$2c80-$2cff $2c80-$2cbc installer for /RAM +RAM_2 EQU RAM_1+$080 ;$2d00-$2d8f $ff00-$ff8f /RAM driver in main lc +MLI_3 EQU RAM_2+$09B ;$2d9b-$2dff $ff9b-$ffff interrupts +MLI_1 EQU MLI_3+$065 ;$2E00-$2eff $bf00-bfff global page +TCLOCK_0 EQU MLI_1+$100 ;$2f00-$2f7f $d742-$d7be TCLOCK driver +CCLOCK_0 EQU TCLOCK_0+$080 ;$2f80-$2fff $d742-$d7be CCLOCK driver +MLI_2 EQU CCLOCK_0+$080 ;$3000-$4fff $de00-$feff MLI itself +RAM_0 EQU MLI_2+$2100 ;$5100-$52ff $0200-$03ff /RAM driver in aux mem +XRW_0 EQU RAM_0+$200 ;$5300-$59FF $d000-$d6ff disk core routines +SEL_0 EQU XRW_0+$700 ;$5A00-$5cff $1000-$12ff original dispatcher +SEL_1 EQU SEL_0+$300 ;$5d00-$5fff $1000-$12ff better bye dispatcher +SEL_2 EQU SEL_1+$300 ;$6000-$2c7F $1000-$12ff gs/os dispatcher + +* ProDOS 8 equates + +ABuf EQU $0C00 ;Temporary buffer +VBlock1 EQU $0E00 ;Where the Vol Dir goes +VolNameStr EQU $0F00 ;Use by SEL2 (p-string) +DispAdr EQU $1000 ;Execution address of dispatcher +RWTS EQU $D000 ;Addr of Disk ][ driver +IOBuf EQU $1C00 +Srce EQU $2C80 +LCSrc EQU Srce+$80 +LCDest EQU $FF00 +ClockBegin EQU $D742 ;Entry address of clock + +LoadIntrp EQU $0800 ;Execution addr of load interpreter +orig EQU $D700 +orig1 EQU $DE00 +Globals EQU $BF00 ;ProDOS's global page +IntHandler EQU $FF9B ;Start of interrupt handler +pathBuf EQU orig +fcb EQU orig+$100 ;File Control Blocks +vcb EQU orig+$200 ;Volume Control Blocks +bmBuf EQU orig+$300 ;Bitmap buffer +genBuf EQU pathBuf+$500 ;General purpose buffer +* +* Constants +* +preTime EQU $20 ;command needs current date/time stamp +preRef EQU $40 ;command requires fcb address and verification +prePath EQU $80 ;command has pathname to preprocess +* +* volume status constants (bits) +* +* file status constants +* +dataAloc EQU $1 ;data block not allocated. +idxAloc EQU $2 ;index not allocated +topAloc EQU $4 ;top index not allocated +storTypMod EQU $8 ;storage type modified +useMod EQU $10 ;file usage modified +eofMod EQU $20 ;end of file modified +dataMod EQU $40 ;data block modified +idxMod EQU $80 ;index block modified +fcbMod EQU $80 ;has fcb/directory been modified? (flush) +* +* header index constants +* +hNamLen EQU $0 ;header name length (offset into header) +hName EQU $1 ;header name +hPassEnable EQU $10 ;password enable byte +hPassWord EQU $11 ;encoded password +hCreDate EQU $18 ;header creation date +* hCreTime EQU $1A ;header creation time +hVer EQU $1C ;sos version that created directory +hComp EQU $1D ;backward compatible with sos version +hAttr EQU $1E ;header attributes- protect etc. +hEntLen EQU $1F ;length of each entry +hMaxEnt EQU $20 ;maximum number of entries/block +hFileCnt EQU $21 ;current number of files in directory +hOwnerBlk EQU $23 ;owner's directory disk address +hOwnerEnt EQU $25 ;owner's directory entry number +hOwnerLen EQU $26 ;owner's directory entry length +vBitMap EQU hOwnerBlk +vTotBlk EQU hOwnerEnt ;(used for root directory only) +* +* Volume Control Block index constants +* +vcbSize EQU $20 ;Current VCB is 32 bytes per entry (ver 0) +vcbNamLen EQU 0 ;Volume name length byte +vcbName EQU 1 ;Volume name +vcbDevice EQU $10 ;Volume's device # +vcbStatus EQU $11 ;Volume status. (80=files open. 40=disk switched.) +vcbTotBlks EQU $12 ;Total blocks on this volume +vcbFreeBlks EQU $14 ;Number of unused blocks +vcbRoot EQU $16 ;Root directory (disk) address +*vcbBitMapOrg EQU $18 ;map organization (not supported by v 0) +*vcbBitMapBuf EQU $19 ;bit map buf num +vcbBitMap EQU $1A ;First (disk) address of bitmap(s) +vcbCurrBitMap EQU $1C ;Rel addr of bitmap w/space (add to vcbBitMap) +*vcbmnum EQU $1D ; relative bit map currently in memory +vcbOpenCnt EQU $1E ;Current number of open files. +*vcbaddr EQU $1F reserved +* +* File Control Block index constants +* +fcbSize EQU $20 ;Current FCB is 32 bytes per entry (ver 0) +fcbRefNum EQU 0 ;file reference number (position sensitive) +fcbDevNum EQU 1 ;device (number) on which file resides +*fcbHead EQU 2 ;block address of file's directory header +*fcbDirBlk EQU 4 ;block address of file's directory +fcbEntNum EQU 6 ;file entry number within dir block +fcbStorTyp EQU 7 ;storage type - seed, sapling, tree, etc. +fcbStatus EQU 8 ;status - index/data/eof/usage/type modified. +fcbAttr EQU 9 ;attributes - read/write enable, newline enable. +fcbNewLin EQU $A ;new line terminator (all 8 bits significant). +fcbFileBuf EQU $B ;buffer number +fcbFirst EQU $C ;first block of file (Master index/key blk) +fcbIdxBlk EQU $E ;curr block address of index (0 if no index) +fcbDataBlk EQU $10 ;curr block address of data +fcbMark EQU $12 ;current file marker. +fcbEOF EQU $15 ;logical end of file. +fcbBlksUsed EQU $18 ;actual number of blocks allocated to this file. +*fcbAddr EQU $1a reserved +fcbLevel EQU $1B ;level at which this file was opened +fcbDirty EQU $1C ;fcb marked as modified +fcbNLMask EQU $1F ;NewLine enabled mask +* +* zero page stuff +* +look EQU $0A +apple EQU $0C +relocTbl EQU $10 +idxl EQU $10 +indrcn EQU $10 +devID EQU $12 +src EQU $12 +dst EQU $14 +cnt EQU $16 +code EQU $18 +endCode EQU $1A +WNDLFT EQU $20 +WNDWDTH EQU $21 +WNDTOP EQU $22 +WNDBTM EQU $23 +CH EQU $24 +CV EQU $25 +INVFLG EQU $32 +A1 EQU $3C ;SOURCE OF TRANSFER +A2 EQU $3E ;END OF SOURCE +A3 EQU $40 +A4 EQU $42 ;DESTINATION OF TRANSFER +Acc EQU $45 +ErrNum EQU $DE +OURCH EQU $057B ;80-col horizontal coord + +* ProDOS block I/O equates +statCmd EQU $00 ;request status, no error=ready +rdCmd EQU $1 +wrtCmd EQU $2 + DUM $40 +parm DS 2,0 +device DS 1,0 ;parm+2 +dhpCmd EQU device ;Command from ProDOS8 +unitNum DS 1,0 ;Unit # from ProDOS 8 (DSSS 0000) +bufPtr DS 2,0 ;512-byte user's I/O buffer +blockNum DS 2,0 ;block # requested + DEND +* +intCmd EQU dhpCmd ;Interrupt command +* + DUM parm+8 +zTemps DS 2,0 +tPath EQU zTemps +dirBufPtr EQU zTemps +tIndex EQU zTemps ;Ptr to index blk buffer +dataPtr DS 2,0 ;Ptr to data blk buffer +posPtr DS 2,0 ;Position marker +userBuf DS 2,0 ;Ptr to user's buffer + DEND +* +* xdos parameters: +* +c_pCnt EQU $0 ; (count) +c_devNum EQU $1 ; (value) +c_refNum EQU $1 ; (value) +c_intNum EQU $1 ; (value) +c_path EQU $1 ;&2 (pointer) +c_isNewln EQU $2 ; (mask) +c_dataBuf EQU $2 ;&3 (value) +c_bufAdr EQU $2 ;&3 (address) +c_intAdr EQU $2 ;&3 (address) +c_mark EQU $2 ;->4 (value) +c_eof EQU $2 ;->4 (value) +c_attr EQU $3 ; (flags) +c_newl EQU $3 ; (character) +c_bufPtr EQU $3 ;&4 (pointer) +c_newPath EQU $3 ;&4 (pointer) +c_fileID EQU $4 ; (value) +c_reqCnt EQU $4 ;&5 (value) +c_blkNum EQU $4 ;&5 (address) +c_outRef EQU $5 +c_auxID EQU $5 ;&6 (value) +c_xferCnt EQU $6 ;&7 (value) +c_fileKind EQU $7 ; (value) +c_date EQU $8 ;&9 (value) +c_outBlk EQU $8 ;&9 (count) +c_time EQU $a ;&b (value) +c_modDate EQU $a ;&b (value) +c_modTime EQU $c ;&d (value) +c_creDate EQU $e ;&f (value) +c_creTime EQU $10 ;&11 (value) + +* Starting addresses of screen lines + +SLIN04 EQU $0600 ;4th line of screen (starting from 0) +SLIN09 EQU $04A8 +SLIN10 EQU $0528 +SLIN11 EQU $05A8 +SLIN12 EQU $0628 +SLIN13 EQU $06A8 +SLIN15 EQU $07A8 +SLIN22 EQU $0750 +SLIN23 EQU $07D0 + +* Error Codes specific to ProDOS 8 +* Other error codes are in the GS/OS equate file + +unclaimedIntErr EQU $01 +vcbUnusable EQU $0A +fcbUnusable EQU $0B +badBlockErr EQU $0C ;Block allocated illegally +fcbFullErr EQU $42 +vcbFullErr EQU $55 +badBufErr EQU $56 \ No newline at end of file diff --git a/MLI.SRC/FNDFIL.S b/MLI.SRC/FNDFIL.S new file mode 100644 index 0000000..991e739 --- /dev/null +++ b/MLI.SRC/FNDFIL.S @@ -0,0 +1,237 @@ +************************************************** +* Get File entry + +FindFile JSR LookFile ;See if file exists + BCS NoFind ;Branch if an error was encountered +MovEntry LDY h_entLen ;Move entire entry info +:loop LDA (dirBufPtr),Y + STA d_file+d_stor,Y ; to a safe area + DEY + BPL :loop + LDA #$00 ;To indicate all is well +NoFind RTS ;Return condition codes + +*------------------------------------------------- +* Follow path to a file + +LookFile JSR PrepRoot ;Find volume and set up other boring stuff + BCS FndErr ;Pass back any error encountered + BNE LookFile0 ;Branch if more than root + LDA #>genBuf ;Otherwise, report a badpath error + STA dirBufPtr+1 ;(but first create a phantom entry for open) + LDA #genBuf ;Reset indirect pointer + STA dirBufPtr+1 + LDA genBuf+2 ;Get link to next directory block + BNE NxtDir0 ;(if there is one) + CMP genBuf+3 ;Are both zero, i.e. no link? + BEQ ErrDir ;If so, then not all entries were accounted for +NxtDir0 LDX genBuf+3 ;A has value for block # (low) + JSR RdBlkAX ;Go read the next linked directory in + BCC ScanDirLoop ;Branch if no error + RTS ;Return error (in A) + +* No more file entries + +ErrFNF LDA noFree ;Was any free entry found? + BNE FNF0 + + LDA genBuf+2 ;Test link + BNE TellFree + CMP genBuf+3 ;If both are zero, then give up + BEQ FNF0 ; simply report 'not found' + +TellFree STA d_entBlk + LDA genBuf+3 + STA d_entBlk+1 ;Assume first entry of next block + LDA #$01 ; is free for use + STA d_entNum + STA noFree ;Mark d_entNum as valid (for create) +FNF0 JSR NxtPNameZ ;Test for 'file not found' versus 'path not found' + +ErrPath1 SEC ;If non-zero then 'path not found' + BEQ :21 + LDA #pathNotFound ;Report no such path + RTS + +:21 LDA #fileNotFound ;Report file not found + RTS + +* File entry found + +NameFound JSR NxtPName ;Adjust index to next name in path + BEQ FileFound ;Branch if that was last name + LDY #d_stor ;Be sure this is a directory entry + LDA (dirBufPtr),Y ;High nibble will tell + AND #$F0 + CMP #directoryFile*16;Is it a sub-directory? + BNE ErrPath1 ;Report the user's mistake + + LDY #d_first ;Get address of first sub-directory block + LDA (dirBufPtr),Y + STA blockNum ;(no checking is done here for a valid + INY ; block number... ) + STA d_head ;Save as file's header block too + LDA (dirBufPtr),Y + STA blockNum+1 + STA d_head+1 + JSR RdGBuf ;Read sub-directory into gbuf + BCS FndErr1 ;Return immediately any error encountered + + LDA genBuf+4+hFileCnt;Get the number of files + STA entCnt ; contained in this directory + LDA genBuf+4+hFileCnt+1 + STA entCnt+1 + LDA genBuf+4+hPassEnable;Make sure password disabled + LDX #$00 + SEC + ROL +TestPass0 BCC :1 + INX +:1 ASL + BNE TestPass0 + CPX #$05 ;Is password disabled? + BEQ MovHead + LDA #badFileFormat ;Tell them this directory is not compatible +FndErr1 SEC + RTS + +MovHead JSR MoveHeadZ ;Move info about this directory + JMP LookFile0 ;Do next local pathname + +*------------------------------------------------- +* Copy directory (vol/sub) header + +MoveHeadZ LDX #$0A ;move info about this directory +:loop1 LDA genBuf+4+hCreDate,X + STA h_creDate,X + DEX + BPL :loop1 + + LDA genBuf+4 ;If this is root, then nothing to do + AND #$F0 + EOR #$F0 ;Test header type + BEQ :11 ;Branch if root + + LDX #$03 ;Otherwise, save owner info about this header +:loop2 LDA genBuf+4+hOwnerBlk,X + STA ownersBlock,X + DEX + BPL :loop2 +:11 RTS + +*------------------------------------------------- +* Save dir entry # & block + +FileFound EQU * +EntAdr LDA h_maxEnt ;Figure out which is entry number this is + SEC + SBC cntEnt ;max entries - count entries + 1 = entry number + ADC #$00 ;(carry is/was set) + STA d_entNum + + LDA blockNum + STA d_entBlk + LDA blockNum+1 ; & indicate blk # of this dir + STA d_entBlk+1 + CLC + RTS + +*------------------------------------------------- +* Search one dir block for file + +LookName LDA h_maxEnt ;reset count of files per block + STA cntEnt + LDA #>genBuf + STA dirBufPtr+1 + LDA #$04 +LookName1 STA dirBufPtr ;Reset indirect pointer to genBuf + BCS LookName2 ;Branch if this block contains a header + LDY #$00 + LDA (dirBufPtr),Y ;Get length of name in dir + BNE IsName ;Branch if there is a name + + LDA noFree ;Test to see if a free entry has been declared + BNE LookName2 ;Yes bump to next entry + JSR EntAdr ;Set address for current entry + INC noFree ;Indicate a free spot has been found + BNE LookName2 ;Branch always + +IsName AND #$0F ;Strip type (this is checked by 'FileFound') + INC totEnt ;(bump count of valid files found) + STA namCnt ;Save name length as counter + LDX pnPtr ;Get index to current path + CMP pathBuf,X ;Are both names of the same length? + BNE LookName2 ;No, bump to next entry + +CmpNames INX ;(first) next letter index + INY + LDA (dirBufPtr),Y ;Compare names letter by letter + CMP pathBuf,X + BNE LookName2 + DEC namCnt ;Have all letters been compared? + BNE CmpNames ;No, continue.. + CLC ;By golly, we got us a match! +NoName RTS + +LookName2 DEC cntEnt ;Have we checked all possible entries in this blk? + SEC + BEQ NoName ;Yes, give up + + LDA h_entLen ;Add entry length to current pointer + CLC + ADC dirBufPtr + BCC LookName1 ;Branch if we're still in the first page + INC dirBufPtr+1 ;Look on second page + CLC ;Carry should always be clear before looking at next + BCC LookName1 ;Branch always... \ No newline at end of file diff --git a/MLI.SRC/GLOBALS.S b/MLI.SRC/GLOBALS.S new file mode 100644 index 0000000..c9afd39 --- /dev/null +++ b/MLI.SRC/GLOBALS.S @@ -0,0 +1,200 @@ + TTL 'Global pages - 64K' + + ORG Globals +************************************************** + +GoPro JMP mliEnt1 ;MLI call entry point + +************ see rev note #36 ******************** +* Jump vector to cold start/selector program, etc. Will +* be changed to point to dispatcher caller by the loader + +jSpare JMP jSpare +*------------------------------------------------- +DateTime DB $60 ;Changed to $4C (JMP) if clock present + DA ClockBegin ;Clock routine entry address +SysErr JMP SysErr1 ;Error reporting hook +SysDeath JMP SysDeath1 ;System failure hook +SErr DB $00 ;Error code, 0=no error + +*------------------------------------------------- +DevAdr01 EQU * + DA gNoDev ;slot zero reserved + DA gNoDev ;slot 1, drive 1 + DA gNoDev ;slot 2, drive 1 + DA gNoDev ;slot 3, drive 1 + DA gNoDev ;slot 4, drive 1 + DA gNoDev ;slot 5, drive 1 + DA gNoDev ;slot 6, drive 1 + DA gNoDev ;slot 7, drive 1 +DevAdr02 DA gNoDev ;slot zero reserved + DA gNoDev ;slot 1, drive 2 + DA gNoDev ;slot 2, drive 2 +DevAdr32 DA gNoDev ;slot 3, drive 2 + DA gNoDev ;slot 4, drive 2 + DA gNoDev ;slot 5, drive 2 + DA gNoDev ;slot 6, drive 2 + DA gNoDev ;slot 7, drive 2 + +*------------------------------------------------- +* Configured device list by device number +* Access order is last in list first. + +DevNum DB $00 ;Most recently accessed device +DevCnt DB $FF ;Number of on-line devices (minus 1) +DevLst HEX 0000000000 ;Up to 14 units may be active + HEX 0000000000 + HEX 00000000 + DB 0 ;Unused? + ASC '(C)APPLE ' ; AppleTALK writes over this area! DO NOT MOVE! + +*------------------------------------------------- +mliEnt1 PHP + SEI ;Disable interrupts + JMP mliCont +aftIrq STA LCBANK1 + JMP fix45 ;Restore $45 after interrupt in lang card* + +old45 DB $00 +afBank DB $00 + +*------------------------------------------------- +* Memory map of the lower 48K. Each bit represents one page +* (256 bytes) of memory. Protected areas are marked with a +* 1, unprotected with a 0. ProDOS dis-allows reading or +* buffer allocation in protected areas. + +memTabl HEX C000000000000000 + HEX 0000000000000000 + HEX 0000000000000001 + +* The addresses contained in this table are buffer addresses +* for currently open files. These are informational only, +* and should not be changed by the user except through the +* MLI call setbuf. + +GblBuf DA $0000 ;file number 1 + DA $0000 ;file number 2 + DA $0000 ;file number 3 + DA $0000 ;file number 4 + DA $0000 ;file number 5 + DA $0000 ;file number 6 + DA $0000 ;file number 7 + DA $0000 ;file number 8 + +*------------------------------------------------- +* Interrupt vectors are stored here. Again, this area is +* informational only, and should be changed only by calls +* to the MLI to allocate_interrupt. Values of the A, X, Y, +* stack, and status registers at the time of the most recent +* interrupt are also stored here. In addition, the address +* interrupted is also preserved. These may be used for +* performance studies and debugging, but should not be changed +* by the user. + +Intrup1 DA $0000 ;interupt routine 1 +Intrup2 DA $0000 ;interupt routine 2 +Intrup3 DA $0000 ;interupt routine 3 +Intrup4 DA $0000 ;interupt routine 4 +IntAReg DB $00 ;A-register +IntXReg DB $00 ;X-register +IntYReg DB $00 ;Y-register +IntSReg DB $00 ;Stack register +IntPReg DB $00 ;Status register +IntBankID DB $01 ;ROM, RAM1, or RAM2 ($D000 in LC) +IntAddr DA $0000 ;program counter return addr + +*------------------------------------------------- +* The user may change the following options +* prior to calls to the MLI. + +DateLo DW $0000 ;bits 15-9=yr, 8-5=mo, 4-0=day +TimeLo DW $0000 ;bits 12-8=hr, 5-0=min; low-hi format +Level DB $00 ;File level: used in open, flush, close +BUBit DB $00 ;Backup bit disable, setfileinfo only +Spare1 DB $00 ; Used to save A reg +NewPfxPtr DB $00 ;Used as AppleTalk alternate prefix ptr + +* The following are informational only. MachID identifies +* the system attributes: +* (bit 3 off) bits 7,6- 00=ii 01=ii+ 10=iie 11=/// emulation +* (bit 3 on) bits 7,6- 00=na 01=na 10=//c 11=na +* bits 5,4- 00=na 01=48k 10=64k 11=128k +* bit 3 modifier for machid bits 7,6. +* bit 2 reserved for future definition. +* bit 1=1- 80 column card +* bit 0=1- recognizable clock card +* +* SltByt indicates which slots are determined to have ROMs. +* PfixPtr indicates an active prefix if it is non-zero. +* mliActv indicates an mli call in progress if it is non-zero. +* CmdAdr is the address of the last mli call's parameter list. +* SaveX and SaveY are the values of x and y when the MLI +* was last called. + +MachID DB $00 ;Machine identification +SltByt DB $00 ;'1' bits indicate rom in slot(bit#) +PfixPtr DB $00 ;If = 0, no prefix active... +mliActv DB $00 ;If <> 0, MLI call in progress +CmdAdr DA $0000 ;Return address of last call to MLI +SaveX DB $00 ;X-reg on entry to MLI +SaveY DB $00 ;Y-reg on entry to MLI + +*------------------------------------------------- +* The following space is reserved for language card bank +* switching routines. All routines and addresses are +* subject to change at any time without notice and will, +* in fact, vary with system configuration. +* The routines presented here are for 64K systems only. + +Exit EOR $E000 ;Test for ROM enable + BEQ Exit1 ;Branch if RAM enabled + STA RDROM2 ;else enable ROM and return + BNE Exit2 ;Branch always + +Exit1 LDA BnkByt2 ;For alternate RAM enable + EOR $D000 ; (mod by mliEnt1) + BEQ Exit2 ;Branch if not alternate RAM + LDA LCBANK2 ;else enable alt $D000 + +Exit2 PLA ;Restore return code + RTI ;Re-enable interrupts and return + +mliCont SEC + ROR mliActv ;Indicate to interrupt routines MLI active +rpmCont LDA $E000 ;Preserve language card / ROM + STA BnkByt1 ; orientation for proper + LDA $D000 ; restoration when MLI exits... + STA BnkByt2 + LDA LCBANK1 ;Now force ram card on + LDA LCBANK1 ; with RAM write allowed + JMP EntryMLI + +irqXit LDA IntBankID ;Determine state of RAM card +IrqXit0 BEQ IrqXit2 ; if any. Branch if enabled + BMI IrqXit1 ;Branch if alternate $D000 enabled + LSR ;Determine if no RAM card present + BCC ROMXit ;Branch if ROM only system + LDA ROMIN2 ;else enable ROM first + BCS ROMXit ;Branch always taken... +IrqXit1 LDA LCBANK2 ;Enable alternate $D000 +IrqXit2 LDA #$01 ;Preset bankid for ROM + STA IntBankID ;(reset if RAM card interupt) +ROMXit LDA IntAReg ;Restore accumulator... + RTI ; and exit! + +IrqEnt BIT LCBANK1 ;This entry only used when ROM + BIT LCBANK1 ; was enabled at time of interupt + JMP IrqRecev ; A-reg is stored at $45 in zpage + +*------------------------------------------------- +BnkByt1 DB $00 +BnkByt2 DB $00 + DS $BFFA-*,0 ; pad + DB $04 ;Referenced by GS/OS + DB $00 + +iBakVer DB $00 ;Reserved +iVersion DB $00 ;Version # of currently running interpreter +kBakVer DB $00 ;Undefined: reserved for future use +kVersion DB $23 ;Represents release 2.03 diff --git a/MLI.SRC/MEMMGR.S b/MLI.SRC/MEMMGR.S new file mode 100644 index 0000000..3f6a0f9 --- /dev/null +++ b/MLI.SRC/MEMMGR.S @@ -0,0 +1,298 @@ +************************************************** +* Allocate I/O buf + +AllocBuf LDY #c_bufPtr+1 ;Index to user specified buffer +AllocBufZ LDA (parm),Y ;This buffer must be on a page boundary + TAX ;Save in X-reg for validation + CMP #$08 + BCC BadBufr ;Cannot be lower than video! + CMP #$BC ;Nor greater than $BB00 + BCS BadBufr ; since it would wipe out globals... + STA dataPtr+1 + DEY + LDA (parm),Y ;Low addr should be zero! + STA dataPtr + BNE BadBufr ;Branch if it isn't + INX ;Add 4 pages for 1K buffer + INX + INX + INX +:loop1 DEX ;Test for conflicts + JSR CalcMemBit ;Test for free buffer space + AND memTabl,Y ;Report memory conflict + BNE BadBufr ; if any... + CPX dataPtr+1 ;Test all four pages + BNE :loop1 + INX ;Add 4 pages again for allocation + INX + INX + INX + +:loop2 DEX ;Set proper bits to 1 + JSR CalcMemBit + ORA memTabl,Y ; to mark it's allocation + STA memTabl,Y + CPX dataPtr+1 ;Set all four pages + BNE :loop2 + + LDY fcbPtr ;Now calculate buffer number + LDA fcb+fcbRefNum,Y + ASL ;buffer number=(entnum)*2 + STA fcb+fcbFileBuf,Y;Save it in FCB + TAX ;Use entnum*2 as index to global buffer addr tables + LDA dataPtr+1 ;Get addr already validated as good + STA GblBuf-1,X ;Store hi addr (entnums start at 1, not zero) + CLC + RTS ;All done allocating buffers + +BadBufr LDA #badBufErr ;Tell user buf is in use or not legal otherwise + SEC ;Indicate error + RTS + +************************************************** +* Locate ptr to I/O buf in global page + +GetBufAdr TAX ;Index into global buffer table + LDA GblBuf-2,X ;Low buffer addr + STA bufAddrL + LDA GblBuf-1,X ;and high addr + STA bufAddrH + RTS + +************************************************** +* Free I/O buf + +ReleaseBuf JSR GetBufAdr ;Preserve buf adr in 'bufAddr' + TAY ;Returns high buffer addr in A + BEQ RelBufX ;Branch if unallocated buffer space + + STZ GblBuf-1,X ;Take address out of buffer list + STZ GblBuf-2,X ;(X was set up by GetBufAdr) + +FreeBuf LDX bufAddrH ;Get hi addr of buffer again + INX ;Add 4 pages to account for 1k space + INX + INX + INX +:loop DEX ;Drop to next lower page + JSR CalcMemBit ;Get bit and posn to memTabl of this page + EOR #$FF ;Invert mask + AND memTabl,Y ;Mark addr as free space now + STA memTabl,Y + CPX bufAddrH ;All pages freed yet? + BNE :loop ;Branch if not +RelBufX CLC ;Indicate no error + RTS + +************************************************** +* Calculate memory allocation bit position +* entry: (X)=hi addr of buffer, low addr assumed zero. +* +* exit: (A)=allocation bit mask, (X)=unchanged, +* (Y)=pointer to memTabl byte + +CalcMemBit TXA ;Move page address to A + AND #$07 ;Which page in any 2k set? + TAY ;Use as index to determine + LDA WhichBit,Y ; bit position representation + PHA ;Save bit position mask for now + TXA ;Get page address again + LSR + LSR ;Now determine 2K set + LSR + TAY ;Return it in Y + PLA ;Restore bit mask + RTS ;Return bit position in A&Y, ptr to memtabl in X + +************************************************** +* Check buffer validity + +ValDBuf LDA userBuf+1 ;Get high addr of user's buffer + CMP #$02 ;Must be greater than page 2 + BCC BadBufr ;Report bad buffer + LDX cBytes+1 + LDA cBytes ;Get cbytes-1 value + SBC #$01 ;(carry is set) + BCS :1 + DEX +:1 CLC + ADC userBuf ;Calculate end of request addr + TXA ;Do hi addr + ADC userBuf+1 ;All we care about is final addr + TAX ;Must be less than $BF (globals) + CPX #$BF + BCS BadBufr + INX ;Loop thru all affected pages + +ValDBufZ DEX ;Check next lower page + JSR CalcMemBit + AND memTabl,Y ;If zero then no conflict + BNE BadBufr ;Branch if conflict... + CPX userBuf+1 ;Was that the last (lowest) page? + BNE ValDBufZ ;Branch if not + CLC ;Indicate all pages ok + RTS ;All done here + +************************************************** +* GETBUF Call + +GetBuf LDY #c_bufAdr ;Give user address of file buffer + LDA bufAddrL ; referenced by refnum + STA (parm),Y + INY + LDA bufAddrH + STA (parm),Y ;No errors possible if this rtn is called + CLC + RTS + +************************************************** +* SETBUF Call + +SetBuf LDY #c_bufAdr+1 + JSR AllocBufZ ;Allocate new buffer address over old one + BCS SetBufErr ;Report any conflicts immediately + + LDA bufAddrH + STA userBuf+1 + LDA bufAddrL + STA userBuf + JSR FreeBuf ;Now free address space of old buffer + + LDY #$00 + LDX #$03 +:loop LDA (userBuf),Y ;Move all four pages of + STA (dataPtr),Y ; the buffer to new location + INY + BNE :loop + INC dataPtr+1 + INC userBuf+1 + DEX + BPL :loop + CLC +SetBufErr RTS + +************************************************** +* +* This is the routine that moves the 3 pages of dispatcher 1 +* from $D100 of the alt 4k bank to its execution address ($1000). +* Since it is in the MLI and must swap the $D000-$DFFF banks, +* it must be resident at all times above $E000. +* +************************************************** + +* NB. There is a vector @ $FEFD which points to this rtn + +CallDisp LDA LCBANK2 + LDA LCBANK2 ;Bring in the other $D000 space + LDA #>DispAdr ;Destination address of user-code + STA A2+1 + LDA #DispAdr + STA SOFTEV+1 ; to dispatch entry + EOR #$A5 ;Set up power up byte + STA PWREDUP + JMP DispAdr + +************************************************** +* Handles calls to mirror devices +* ProDOS unit #s are of the form DSSS xxxx +* where the bits of the low nibble are the +* attributes of the device. +* D=0/1 (drive 1/drive 2) +* The handler only supports 14 mirror devices +* A statusCmd ($00) call will return the # +* of blocks in (Y,X) + +MirrorDevEntry LDX #$03 ;Default parm cnt + LDA dhpCmd ;Get cmd + STA spCmdNum + BNE :1 + + LDY #spStatList + STY bufPtr+1 + STZ blockNum +:1 CMP #$03 ;FormatCmd? + BNE :2 ;No + + LDX #$01 ;parm cnt for a formatCmd +:2 STX spCmdList + LDA unitNum ;(dsss 0000) + LSR + LSR + LSR + LSR + TAX ;0000 dsss ($01-$0F; $00,$08,$0B-invalid) + LDA spUnits-1,X ;Get actual SP unit # which + STA spUnitNum ; corr to this ProDOS unit # + LDA spDrvAdrL-1,X + STA CallSPort+1 ;Get addr of SP dev driver + LDA spDrvAdrH-1,X + STA CallSPort+2 ; which handles this SP unit + + LDX #$04 +:CpyLoop LDA bufPtr-1,X + STA blkIOParms-1,X + DEX + BNE :CpyLoop + +CallSPort JSR $0000 ;Go do it! +spCmdNum DB $00 + DA spCmdList + BCS :Rtn + + LDX spCmdNum ;Was a SP STATUS call executed? + BNE :Rtn ;No + LDX spDevTotBlks ;# of blocks + LDY spDevTotBlks+1 + LDA genStatus + BIT #$10 ;Is dev online/disk in drive? + BNE :1 ;Yes + LDA #drvrOffLine + BRA :2 + +:1 AND #$44 ;Retain bits 6,2 + EOR #$40 ;Is it write-protected? + BEQ :Rtn ;No + LDA #drvrWrtProt +:2 SEC +:Rtn RTS + +*------------------------------------------------- +* This table was built during a P8 boot + +spDrvAdrL DS $F,0 ;Actual entry points +spDrvAdrH DS $F,0 ; of a device's driver + +* Command List used for all 4 commands viz +* 0-Status, 1-Read Block, 2-Write Block, 3-Format +* The caller must pass the parms using the +* usual zp locations $42-$47 + +spCmdList DB $03 ;parm count +spUnitNum DB $00 ;unit # +blkIOParms DA $0000 ;Data I/O buf +blokNum DB 0,0,0 ;blk # (only 2 bytes used) \ No newline at end of file diff --git a/MLI.SRC/MLI.MACS.S b/MLI.SRC/MLI.MACS.S new file mode 100644 index 0000000..dc4a6be --- /dev/null +++ b/MLI.SRC/MLI.MACS.S @@ -0,0 +1,51 @@ +_ReadTimeHex MAC + Tool $D03 + <<< +_Int2Hex MAC + Tool $220B + <<< +_TLTextMountVol MAC + Tool $1201 + <<< +_MessageCenter MAC + Tool $1501 + <<< +_MMStartUp MAC + Tool $202 + <<< +_MMShutDown MAC + Tool $302 + <<< +_NewHandle MAC + Tool $902 + <<< +_DisposeHandle MAC + Tool $1002 + <<< +PushLong MAC + IF #=]1 + PushWord #^]1 + ELSE + PushWord ]1+2 + FIN + PushWord ]1 + <<< +PushWord MAC + IF #=]1 + PEA ]1 + ELSE + IF MX/2 + LDA ]1+1 + PHA + FIN + LDA ]1 + PHA + FIN + <<< +_PtrToHand MAC + Tool $2802 + <<< +Tool MAC + LDX #]1 + JSL $E10000 + <<< \ No newline at end of file diff --git a/MLI.SRC/NEWFNDVOL.S b/MLI.SRC/NEWFNDVOL.S new file mode 100644 index 0000000..bd9a02e --- /dev/null +++ b/MLI.SRC/NEWFNDVOL.S @@ -0,0 +1,453 @@ +************************************************** +* Get directory data + +PrepRoot JSR FindVol ;Search VCB's and devices for specified volume + BCS NoVolume ;Branch if not found + + LDA #$00 ;Zero out directory temps + LDY #$42 +ClrDsp STA ownersBlock,Y ; & owner info + DEY + BPL ClrDsp + + LDA DevNum ;Set up device number for this directory + STA d_dev + JSR MoveHeadZ ;Set up other header info from directory + + LDY #$01 ; in genBuf & clean up misc + LDX vcbPtr + INX +RootMisc LDA vcb+vcbTotBlks,X;Misc info includes + STA h_totBlk,Y ; total # of blocks, + LDA vcb+vcbBitMap,X ; the disk addr of the first bitmap, + STA h_bitMap,Y + LDA |blockNum,Y ; directory's disk address, + STA d_head,Y + LDA h_fileCnt,Y ; & lastly, setting up a counter for + STA entCnt,Y ; the # of files in this directory + DEX ;Move low order bytes too + DEY + BPL RootMisc + +NxtPName JSR NxtPNameZ ;Get new pnPtr in Y & next namlen in A + STY pnPtr ;Save new pathname pointer + RTS ;(status reg according to ACC) + +*------------------------------------------------- +* Advance to next dir name + +NxtPNameZ LDY pnPtr ;Bump pathname pointer to + LDA pathBuf,Y ; next name in the path... + SEC + ADC pnPtr ;If this addition results in zero + TAY ; then prefixed directory has been moved + BNE :1 ; to another device. Branch if not + + LDA DevNum ;Revise devnum for prefixed directory + STA pathDev +:1 LDA pathBuf,Y ;Test for end of name (Z=1) + CLC ;Indicate no errors +NoVolume RTS + +*------------------------------------------------- +* Find base dir + +FindVol LDA #$00 + LDY PfixPtr ;Use prefix volume name to look up VCB + BIT prfxFlg ;Is this a prefixed path? + BPL :1 ;Branch if it is + TAY ;Set ptr to volume name + +:1 STY vnPtr ;Save pointer + STA DevNum ;Zero out device number until VCB located + +Adv2NxtVCB PHA ;Acc now used as VCB lookup index + TAX ;Move pointer to X-reg for index + LDA vcb+vcbNamLen,X ;Get volume name length + BNE MatchVol ;Branch if claimed VCB to be tested +NxtVCB LDY vnPtr ;Restore ptr to requested volume name + PLA ;Now adjust VCB index to next vcb entry + CLC + ADC #vcbSize + BCC Adv2NxtVCB ;Branch if more VCB's to check + BCS LookVol ;Otherwise go look for unlogged volumes + +MatchVol STA namCnt ;Save length of vol name to be compared +:loop1 CMP pathBuf,Y ;Is it the same as requested vol name? + BNE NxtVCB ;branch if not + INX + INY + LDA vcb+vcbName-1,X ;Bump to next character + DEC namCnt ;Was that the last character? + BPL :loop1 ;Branch if not + + PLX ;Restore pointer to VCB that matches + STX vcbPtr ;Save it for future reference + LDA vcb+vcbDevice,X ;Get its device number + STA DevNum ;Save it + STZ blockNum+1 ;Assume prefix is not used and + LDA #$02 ; that root directory is to be used + STA blockNum + + LDA vnPtr ;= 0 if no prefix +PfxDir TAY ;If prefix, then find ptr to prefixed dir name + STA pnPtr ;Save path ptr + BEQ ChkVolName ;Branch if no prefix + SEC ;Bump to next dir in prefix path + ADC pathBuf,Y + BCC PfxDir ;Branch if there is another dir in prefix + + LDA pathBlok ;Volume verification will occur + STA blockNum ; at sub directory level + LDA pathBlok+1 + STA blockNum+1 + +*------------------------------------------------- +******verify volume name****** + +ChkVolName JSR RdGBuf ;Read in directory (or prefix directory) + BCS WrgVol ;If error then look on other devices + JSR CmpPName ;Compare directory name with pathname + BCC WrgVolErr ;If they match, don't look elsewhere + +WrgVol LDX vcbPtr ;Find out if current (matched) vcb is active + LDA vcb+vcbStatus,X ; i.e. does it have open files? + BMI LookVolErr ;Report not found if active + +LookVol LDA vnPtr ;Make path pointer same as volume ptr + STA pnPtr + JSR MovDevNums ;Copy all device numbers to be examined + LDA DevNum ;Log current device first, before searching others + BNE WrgVol3 + +TryNxtUnit LDX DevCnt ;Scan look list for devices we need +:loop LDA lookList,X ; to search for the requested volume + BNE WrgVol4 ;Branch if we've a device to look at + DEX + BPL :loop ;Look at next guy + +LookVolErr LDA #volNotFound ;Report that no mounted volume + SEC ; matches the requested +WrgVolErr RTS + +WrgVol3 LDX DevCnt ;Now remove the device from the list +WrgVol4 CMP lookList,X ; of prospective devices (so we don't look twice) + BEQ :1 ;Branch if match + DEX ;Look until found + BPL WrgVol4 ;Branch always taken! (usually!) * * * + BMI LookVolErr ;Never unless device was manually removed from devlst (/ram) + +:1 STA DevNum ;Preserve device we're about to investigate + STZ lookList,X ;Mark this one as tested + JSR ScanVCB ;Find VCB that claims this device, if any + BCS FndVolErr ;Branch if VCB full + LDX vcbPtr ;Did 'fnddvcb' find it or did it return free vcb? + LDA vcb,X + BEQ :2 ;Branch if free VCB + LDA vcb+vcbStatus,X ;Is this volume active? + BMI TryNxtUnit ;If so, no need to re-log + +:2 LDA #$02 ;Go read root directory into genBuf + LDX #$00 + JSR RdBlkAX + BCS TryNxtUnit ;Ignore if unable to read + JSR LogVCB ;Go log in this volume's proper name + BCS TryNxtUnit ;Look at next if non xdos disk was mounted + JSR CmpPName ;Is this the volume we're looking for? + BCS TryNxtUnit ;Branch if not +FndVolErr RTS ;return to caller + +MovDevNums LDX DevCnt ;Copy all device numbers to be examined +:loop LDA DevLst,X + AND #$F0 ;Strip device type info + STA lookList,X ;Copy them to a temporary workspace + DEX + BPL :loop + LDX DevCnt + RTS + +************************************************** +* Scan VCBs' for device # +* Input +* (DevNum) - Look for vcb with this device number +* Output +* C = - Got a match/Got a free slot + +ScanVCB LDA #$00 + LDY #$FF +ScanNxtVCB TAX ;New index to next VCB + LDA vcb+vcbDevice,X ;Check all devnums + CMP DevNum ;Is this the VCB were looking for? + BNE NotThisVCB ;Branch if not + STX vcbPtr + CLC ;Indicate found + RTS + +NotThisVCB LDA vcb,X ;Is this a free VCB? + BNE :1 ;Branch if not + INY + STX vcbPtr +:1 TXA ;now... + CLC ; bump index to next VCB + ADC #vcbSize + BNE ScanNxtVCB + TYA ;Were any free VCB's available? + BPL :3 ;Yes + + LDA #$00 +:loop TAX ;Save index + LDA vcb+vcbStatus,X ;Any files opened? + BPL :2 ;No + TXA + CLC + ADC #vcbSize + BNE :loop + BEQ :ErrExit ;Always + +:2 STX vcbPtr ;This slot can be used + STZ vcb,X ;Prepare it for use + STZ vcb+vcbDevice,X +:3 CLC ;Indicate no errors +:ErrExit LDA #vcbFullErr + RTS + +*------------------------------------------------- +* Compare dir name with path level + +CmpPName LDX #$00 ;Index to directory name + LDY pnPtr ;Index to pathname + LDA genBuf+4+hNamLen;Get directory name length (and type) + CMP #$E0 ;Also make sure it's a directory + BCC :1 ;Branch if not a directory + AND #$0F ;Isolate name length + STA namCnt ;Save as counter + BNE :2 ;Branch if valid length +:1 SEC ;Indicate not what were looking for + RTS + +:loop LDA genBuf+4+hName-1,X;Get next char +:2 CMP pathBuf,Y + BNE :1 ;Branch if not the same + INX ;Check nxt char + INY + DEC namCnt + BPL :loop ;Branch if more to compare + CLC ;Otherwise we got a match!!! + RTS + +*------------------------------------------------- +* Mount new volume + +LogVCB LDX vcbPtr ;Is this a previously logged in volume + LDA vcb,X ;(A=0?) + BEQ LogVCBZ ;No, go ahead and prepare vcb + JSR CmpVCB ;Does VCB match volume read? + BCC VCBLogged ;Yes, don't disturb it + +LogVCBZ LDY #vcbSize-1 +ZeroVCB STZ vcb,X ;Zero out VCB entry + INX + DEY + BPL ZeroVCB + + JSR TestSOS ;Make sure it's an xdos diskette + BCS VCBLogged ;If not, return carry set + + JSR TestDupVol ;find out if a duplicate with open files already exists + BCS NotLog0 + LDA genBuf+4+hNamLen;Move volume name to VCB + AND #$0F ;Strip root marker + TAY ;len byte to Y-reg + PHA + ORA vcbPtr ;Add in offset to VCB record + TAX +MovVolNam LDA genBuf+4+hNamLen,Y + STA vcb+hNamLen,X + DEX + DEY + BNE MovVolNam + + PLA ;Get length again + STA vcb+hNamLen,X ;Save that too. + LDA DevNum + STA vcb+vcbDevice,X ;Save device number also + LDA genBuf+4+vTotBlk; & totol # of blocks on this unit, + STA vcb+vcbTotBlks,X + LDA genBuf+4+vTotBlk+1 + STA vcb+vcbTotBlks+1,X + LDA blockNum ; & address of root directory + STA vcb+vcbRoot,X + LDA blockNum+1 + STA vcb+vcbRoot+1,X + + LDA genBuf+4+vBitMap; & lastly, the address + STA vcb+vcbBitMap,X ; of the first bitmap + LDA genBuf+4+vBitMap+1 + STA vcb+vcbBitMap+1,X +NotLog0 CLC ;Indicate that it was logged if possible +VCBLogged RTS + +*------------------------------------------------- +* Compare vol names to make sure they match + +CmpVCB LDA genBuf+4+hNamLen;Compare volume name in VCB + AND #$0F ; with name in directory + CMP vcb+hNamLen,X ;Are they same length? + STX xvcbPtr + BNE :1 + + TAY + ORA xvcbPtr + TAX +:CmpLoop LDA genBuf+4+hNamLen,Y + CMP vcb+hNamLen,X +:1 SEC ;Anticipate different names + BNE NotSame + DEX + DEY + BNE :CmpLoop + + CLC ;Indicate match +NotSame LDX xvcbPtr ;Get back offset to start of vcb + RTS + +*------------------------------------------------- +* Look for duplicate vol + +TestDupVol LDA #$00 ;Look for other logged in volumes with same name +:loop TAX + JSR CmpVCB + BCS :1 ;Branch if no match + + LDA vcb+vcbStatus,X ;Test for any open files + BMI FoundDupVol ;Tell the sucker he can't look at this volume! + + LDA #$00 ;Take duplicate off line if no open file + STA vcb,X + STA vcb+vcbDevice,X + BEQ NoDupVol ;Return that all is ok to log in new + +:1 TXA ;Index to next VCB + CLC + AND #$E0 ;Strip odd stuff + ADC #vcbSize ;Bump to next entry + BCC :loop ;Branch if more to look at + +NoDupVol CLC + RTS + +FoundDupVol STA duplFlag ;A duplicate has been detected + STX vcbEntry ;Save pointer to conflicting vcb + SEC ;Indicate error + RTS +*------------------------------------------------- +* See if a quantity of free blks is available on volume +* Input +* (reqL,H) = # of blks required + +TestFreeBlk LDX vcbPtr ;Find out if enough free blocks + LDA vcb+vcbFreeBlks+1,X; available to accomodate the request + ORA vcb+vcbFreeBlks,X; but first find out if we got a proper cnt for this vol + BNE CmpFreeBlk ;Branch if count is non-zero + +* Compute VCB free blk count + +TakeFreeCnt JSR CntBMs ;Get # of bitmaps + STA bmCnt ;Save it + STZ scrtch ;Start count at zero + STZ scrtch+1 + LDA #$FF ;Mark 'first free' temp as unknown + STA noFree + JSR UpdateBitMap ;(nothing happens if it don't hafta.) + BCS TFBErr ;Branch if we got trouble + + LDX vcbPtr ;Get address of first bit map + LDA vcb+vcbBitMap,X + STA blockNum + LDA vcb+vcbBitMap+1,X + STA blockNum+1 + +BitMapRd JSR RdGBuf ;Use g(eneral)buff(er) for temporary + BCS TFBErr ; space to count free blocks (bits) + JSR FreeCount ;Go count 'em + DEC bmCnt ;Was that the last bit map? + BMI ChgVCB ;If so, go change VCB to avoid doing this again! + INC blockNum ;Note: the organization of the bit maps + BNE BitMapRd ; are contiguous for sos version 0 + INC blockNum+1 ;If some other organization is implemented, + BRA BitMapRd ; this code must be changed! + +ChgVCB LDX vcbPtr ;Mark which block had first free space + LDA noFree + BMI DskFull ;Branch if no free space was found + STA vcb+vcbCurrBitMap,X;Update the free count + LDA scrtch+1 ;Get high count byte + STA vcb+vcbFreeBlks+1,X;Update volume control block + LDA scrtch + STA vcb+vcbFreeBlks,X; & low byte too... + +CmpFreeBlk LDA vcb+vcbFreeBlks,X;Compare total available + SEC + SBC reqL ; free blocks on this volume + LDA vcb+vcbFreeBlks+1,X + SBC reqH + BCC DskFull + CLC + RTS + +DskFull LDA #volumeFull + SEC +TFBErr RTS + +*------------------------------------------------- +* Scan and count bitMap blks + +FreeCount LDY #$00 ;Begin at the beginning +:loop LDA genBuf,Y ;Get bit pattern + BEQ :1 ;Don't bother counting nothin' + JSR CntFree +:1 LDA genBuf+$100,Y ;Do both pages with same loop + BEQ :2 + JSR CntFree +:2 INY + BNE :loop ;Loop till all 512 bytes counted + BIT noFree ;Has first block with free space been found yet? + BPL :3 ;Branch if it has + + LDA scrtch ;Test to see if any blocks were counted + ORA scrtch+1 + BEQ :3 ;Branch if none counted + + JSR CntBMs ;Get total # of maps + SEC ;Subtract countdown from total bit maps + SBC bmCnt + STA noFree +:3 RTS + +*------------------------------------------------- +* Count # of 1 bits in a byte + +CntFree ASL + BCC :1 ;Not a 1-bit + INC scrtch + BNE :1 + INC scrtch+1 +:1 ORA #$00 ;Loop until all bits counted + BNE CntFree + RTS + +*------------------------------------------------- +* Compute # of bit map blks-1 + +CntBMs LDX vcbPtr + LDY vcb+vcbTotBlks+1,X;Return the # of bit maps + LDA vcb+vcbTotBlks,X; posible with the total count + BNE :1 ; found in the vcb... + DEY ;Adjust for bitmap block boundary + +:1 TYA + LSR ;Divide by 16. The result is + LSR ; the number of bit maps + LSR + LSR + RTS \ No newline at end of file diff --git a/MLI.SRC/POSNOPEN.S b/MLI.SRC/POSNOPEN.S new file mode 100644 index 0000000..29f6a24 --- /dev/null +++ b/MLI.SRC/POSNOPEN.S @@ -0,0 +1,613 @@ +************************************************** +* GETMARK Call + +GetMark LDX fcbPtr ;Get index to open file control block + LDY #c_mark ; & index to user's mark parameter +:loop LDA fcb+fcbMark,X + STA (parm),Y ;Transfer current position + INX ; to user's parameter list + INY + CPY #c_mark+3 ;Have all three bytes been transferred? + BNE :loop ;Branch if not... + CLC ;No errors + RTS + +ErrMrkEOF LDA #outOfRange ;Report invalid position. + SEC + RTS + +************************************************** +* SETMARK Call + +SetMark LDY #c_mark+2 ;Get index to user's desired position + LDX fcbPtr ; & file's control block index + INX ;(bump by 2 for index to hi EOF) + INX + SEC ;Indicate comparisons are necessary +:loop LDA (parm),Y ;Move it to 'tPos' + STA tPosll-c_mark,Y + BCC :1 ;Branch if we already know markEOF + DEX + +:1 DEY dey ;Prepare to move/compare next lower byte of mark + TYA ;Test for all bytes moved/tested + EOR #c_mark-1 ;To preserve carry status + BNE :loop ;Branch if more + +* Still in same data block + +RdPosn LDY fcbPtr ;First test to see if new position is + LDA fcb+fcbMark+1,Y ; within the same (current) data block + AND #%11111110 + STA scrtch ;(At a block boundary) + LDA tPoslh ;Get middle byte of new position + SEC + SBC scrtch + STA scrtch + BCC TypMark ;Branch if possibly l.t. current position + CMP #$02 ;Must be within 512 bytes of beginning of current + BCS TypMark ;No + LDA tPosHi ;Now make sure we're talking + CMP fcb+fcbMark+2,Y ; about the same 64K chunk! + BNE TypMark ;Branch if we aren't + JMP SavMark ;If we are, adjust FCB, posPtr and return + +TypMark EQU * ;Now find out which type + LDA fcb+fcbStorTyp,Y; of file we're positioning on + BEQ :11 ;There is no such type as zero, branch never! + CMP #tree+1 ;Is it a tree class file? + BCC TreePos ;Yes, go position + JMP DirMark ;No, test for directory type + +:11 LDY #fcbPtr ;Clear illegally typed FCB entry + STA fcb+fcbRefNum,Y + LDA #invalidRefNum ;Tell 'em there is no such file + SEC + RTS + +* Need different data blk + +TreePos EQU * ;Use storage type as number of index levels + LDA fcb+fcbStorTyp,Y; (since 1=seed, 2=sapling, and 3=tree) + STA levels + LDA fcb+fcbStatus,Y ;Must not forget previous data + AND #dataMod ;Therefore, see if previous data was modified + BEQ :21 ; then disk must be updated + JSR WrFCBData ;Yes, so go write current data block + BCS PosErr ;Return any error encountered + +:21 LDY fcbPtr ;Test to see if current + LDA fcb+fcbMark+2,Y ; index block is going to be usable... + AND #%11111110 ; or in other words - is new position + STA scrtch ; within 128K of the beginning + LDA tPosHi ; of current sapling level chunk? + SEC + SBC scrtch + BCC PosNew2 ;Branch if a new index block is also needed + CMP #$02 ;New position is > begining of old. Is it within 128K? + BCS PosNew2 ;Branch if not + LDX levels ;Is the file we're dealing with a seed? + DEX + BNE DataLevel ;No, use current indexes + +TestTiny LDA tPoslh ;Is new position under 512? + LSR + ORA tPosHi + BNE NoIdxData ;No, mark both data and index block as un-allocated + LDA fcb+fcbFirst,Y ;First block is only block and it's data! + STA blockNum + LDA fcb+fcbFirst+1,Y;(high block address) + JMP RdNewPos + +PosNew2 EQU * ;Gota check to see if previous + LDA fcb+fcbStatus,Y ; index block was modified + AND #idxMod + BEQ PosnIdx ;Read in over it if current is up to date + JSR WrFCBIdx ;Go update index on disk (block addr in fcb) + BCS PosErr + +PosnIdx LDX levels ;Before reading in top index, check + CPX #tree ; to be sure that there is a top index... + BEQ PosIndex ;Branch if file is full blown tree + + LDA tPosHi ;Is new position within range + LSR ; of a sapling file (l.t. 128k)? + PHP ;Anticipate no good + LDA #topAloc+idxAloc+dataAloc;(to indicate no level is allocated for new posn) + PLP ;Z flag tells all... + BNE NoData ;Go mark 'em all dummy + + JSR ClrStats ;Go clear status bits 0,1,2 (index/data alloc status) + DEX ;(unaffected since loaded above) Check for seed + BEQ TestTiny ;If seed, check for position l.t. 512... + + JSR RdFCBFst ;Go get only index block + BCS PosErr ;Branch if error + + LDY fcbPtr ;Save newly loaded index block's address + LDA blockNum + STA fcb+fcbIdxBlk,Y + LDA blockNum+1 + STA fcb+fcbIdxBlk+1,Y + BCC DataLevel ;Branch always... +PosErr RTS ;Carry always set when branched to + +PosIndex JSR ClrStats ;Clear all allocation requirements for prev posn + JSR RdFCBFst ;Get highest level index block + BCS PosErr + + LDA tPosHi ;Then test for a sap level index block + LSR + TAY + LDA (tIndex),Y + INC tIndex+1 + CMP (tIndex),Y ;(both hi and lo will be zero if no index exists) + BNE SapLevel + TAX ;Are both bytes zero? + BNE SapLevel ;No + DEC tIndex+1 ;Don't leave wrong pointers laying around! +NoIdxData LDA #idxAloc+dataAloc + BRA NoData + +SapLevel STA blockNum ;Read in next lower index block + LDA (tIndex),Y ;(hi address) + STA blockNum+1 + DEC tIndex+1 + JSR RdFCBIdx ;Read in sapling level + BCS PosErr + +DataLevel LDA tPosHi ;Now get block address of data block + LSR + LDA tPoslh ;( if there is one ) + ROR + TAY + LDA (tIndex),Y ;Data block address low + INC tIndex+1 + CMP (tIndex),Y + BNE PosNew3 + TAX ;Are both bytes zero? + BNE PosNew3 ;No + LDA #dataAloc ;Show data block has never been allocated + DEC tIndex+1 + +NoData LDY fcbPtr ;Set status to show what's missing + ORA fcb+fcbStatus,Y + STA fcb+fcbStatus,Y + LSR ;Throw away bit that says data block un-allocated + LSR ; cuz we know that. Carry now indicates if index block + JSR ZipData ; also is invalid and needs to be zeroed (Carry undisturbed) + BCC SavMark ;Branch if index block doesn't need zipping + + JSR ZeroIndex ;Go zero index block in user's i/o buffer + BRA SavMark + +ZeroIndex LDA #$00 + TAY +:loop1 STA (tIndex),Y ;Zero out the index half + INY + BNE :loop1 ; of the user's i/o buffer + INC tIndex+1 +:loop2 STA (tIndex),Y + INY + BNE :loop2 ;Restore proper address + DEC tIndex+1 + RTS ;That's all + +ZipData LDA #$00 ;Also is invalid and needs to be zeroed + TAY +:loop1 STA (dataPtr),Y ;Zero out data area + INY + BNE :loop1 + INC dataPtr+1 +:loop2 STA (dataPtr),Y + INY + BNE :loop2 + DEC dataPtr+1 + RTS + +* Read file data blk + +PosNew3 STA blockNum ;Get data block of new position + LDA (tIndex),Y ;(hi address) + DEC tIndex+1 +RdNewPos STA blockNum+1 + JSR RdFCBData + BCS pritz ;Return any error + JSR ClrStats ;Show whole chain is allocated + +* Got data blk wanted + +SavMark LDY fcbPtr ;Update position in file control block + INY + INY + LDX #$02 +:loop LDA fcb+fcbMark,Y ;Remember oldmark in case + STA oldMark,X ; calling routine fails later + LDA tPosll,X ;Set new mark + STA fcb+fcbMark,Y ; in FCB + DEY + DEX ;Move 3-byte position marker + BPL :loop + + CLC ;Last, but not least, set up + LDA dataPtr ; indirect address to buffer page pointed + STA posPtr ; to by the current position marker + LDA tPoslh + AND #$01 ;(A)=0/1 + ADC dataPtr+1 ;(posPtr) = start of pg in + STA posPtr+1 ; data blk which contains the mark +pritz RTS ;Carry set indicates error! + +*------------------------------------------------- +* Reset block allocate flags + +ClrStats LDY fcbPtr ;Clear allocation states for data block + LDA fcb+fcbStatus,Y ; and both levels of indexes + AND #$FF-topAloc-idxAloc-dataAloc + STA fcb+fcbStatus,Y ;This says that either they exist currently + RTS ; or that they're unnecessary for current position. + +*------------------------------------------------- +* Set dir file position + +DirMark CMP #directoryFile ;Is it a directory? + BEQ DirPos ;Yes... + LDA #badFileFormat ;No, there is a compatiblity problem - + JSR SysErr ; the damn thing should never been opened! + +DirPos LDA scrtch ;Recover results of previous subtraction + LSR ;Use difference as counter as to how many + STA cntEnt ; blocks must be read to get to new position + LDA fcb+fcbMark+1,Y ;Test for position direction + CMP tPoslh ;Carry indicates direction... + BCC DirFwrd ;If set, position forward + +DirReverse LDY #$00 ;Otherwise, read directory file in reverse order + JSR DirPos1 ;Read previous block + BCS DirPosErr ;Branch if anything goes wrong + INC cntEnt ;Count up to 128 + BPL DirReverse ;Loop if there is more blocks to pass over + BMI SavMark ;Branch always + +DirFwrd LDY #$02 ;Position is forward from current position + JSR DirPos1 ;Read next directory block + BCS DirPosErr + DEC cntEnt + BNE DirFwrd ;Loop if position not found in this bloc + BEQ SavMark ;Branch always + +DirPos1 LDA (dataPtr),Y ;Get link address of previous + STA blockNum ; or next directory block + CMP #$01 ; but first be sure there is a link + INY + LDA (dataPtr),Y + BNE DirPos2 + BCS DirPos2 ;Branch if certain link exists + LDA #eofEncountered ;Something is wrong with this directory file! +DirPosErr SEC ;Indicate error + RTS + +DirPos2 STA blockNum+1 ;(high order block address) +* +* Drop into rfcbdat (Read file's data block) +* +* +* Note: for directory positioning, no optimization has been done since +* since directory files will almost always be less than 6 blocks. +* If more speed is required or directory type files are to be used +* for other purposes requiring more blocks, then the recommended +* method is to call RdFCBData for the first block and go directly to +* device (via jmp (iounitl)) handler for subsequent accesses. +* Also note that no checking is done for read/write enable since a +* directory file can only be opened for read access. +* +RdFCBData LDA #rdCmd ;Set read command + STA dhpCmd + LDX #dataPtr ;Use X to point at address of data buffer + JSR FileIOZ ;Go do file input + BCS :Ret ;Return any error + + LDY fcbPtr + LDA blockNum + STA fcb+fcbDataBlk,Y;Save block number just read in FCB + LDA blockNum+1 + STA fcb+fcbDataBlk+1,Y +:Ret RTS ;Carry set indicates error + +*------------------------------------------------- +* Read sub index blk + +RdFCBIdx LDA #rdCmd ;Prepare to read in index bloc + STA dhpCmd + LDX #tIndex ;Point at address of current index buffer + JSR FileIOZ ;Go read index block + BCS :Ret ;Report error + LDY fcbPtr + LDA blockNum + STA fcb+fcbIdxBlk,Y ;Save block address of this index in fcb + LDA blockNum+1 + STA fcb+fcbIdxBlk+1,Y + CLC +:Ret RTS + +*------------------------------------------------- +* Write key index blk + +WrFCBFst1 LDA #wrtCmd ;Set write mode for device + DB $2C ;Skip next 2 bytes for pseudo-branch always to RWFst + +* Read key index blk + +RdFCBFst LDA #rdCmd ;Set read mode for device +RWFst PHA ;Save command + LDA #fcbFirst + ORA fcbPtr ;Add offset to fcbPtr + TAY + PLA + LDX #tIndex ;Read block into index portion of file buffer +* +* Drop into DoFileIO +* +DoFileIO STA dhpCmd ;Save command + LDA fcb,Y ;Get disk block address from FCB + STA blockNum ;Block zero not legal + CMP fcb+1,Y + BNE FileIO + CMP #$00 ;Are both bytes zero? + BNE FileIO ;No, continue with request + LDA #badBlockErr ;Otherwise report allocation error + JSR SysDeath ;Never returns... + +FileIO LDA fcb+1,Y ;Get high address of disk block + STA blockNum+1 + +*------------------------------------------------- +* Set up and do file block I/O +* Entry +* (X) = buf ptr in page zero + +FileIOZ PHP ;No interupts from here on out + SEI + LDA $00,X ;Get memory address of buffer from + STA bufPtr ; zero page pointed to by + LDA $01,X ; the X-register + STA bufPtr+1 ; & pass address to device handler + + LDY fcbPtr + LDA fcb+fcbDevNum,Y ;Of course having the device number + STA DevNum ; would make the whole operation more meaningful... + LDA #$FF ;Also, set to + STA ioAccess ; indicate reg call made to dev handler + LDA DevNum ;xfer the device # for dispatcher to convert to unit # + STA unitNum + STZ SErr ;Clear global error value + JSR DMgr ;Call the driver + BCS :1 ;Branch if error + PLP ;Restore interupts + CLC + RTS + +:1 PLP ;Restore interupts + SEC + RTS + +*------------------------------------------------- +* Check point bit map & write key blk + +WrFCBFirst JSR UpdateBitMap ;First update the bitmap + BRA WrFCBFst1 ; and go write file's first block! + +*------------------------------------------------- +* Check point data blk buffer + +WrFCBData LDX #dataPtr + LDA #fcbDataBlk ;Point at mem addr with X and disk addr with Y + ORA fcbPtr ;Add offset to fcbptr + TAY ; and put it in Y-reg + LDA #wrtCmd ;Write data block + JSR DoFileIO + BCS FileIOerr ;Report any errors + LDA #$FF-dataMod ;Mark data status as current + BRA FCBUpdate + +*------------------------------------------------- +* Check point index blk buffer + +WrFCBIdx JSR UpdateBitMap ;Go update bitmap + LDX #tIndex ;Point at address of index buffer + LDA #fcbIdxBlk ; & block address of that index block + ORA fcbPtr + TAY + LDA #wrtCmd + JSR DoFileIO ;Go write out index block + BCS FileIOerr ;Report any errors + LDA #$FF-idxMod ;Mark index status as current +FCBUpdate LDY fcbPtr ;Change status byte to + AND fcb+fcbStatus,Y ; reflect successful disk file update + STA fcb+fcbStatus,Y ;(carry is unaffected) +FileIOerr RTS + +************************************************** +* ProDOS8 OPEN Call + +Open JSR FindFile ;First of all look up the file... + BCC :1 + CMP #badPathSyntax ;Is an attempt to open a root directory? + BNE ErrOpen ;No, pass back error +:1 JSR TestOpen ;Find out if any other files are writing + BCC Open1 ; to this same file. (branch if not) +ErrBusy LDA #fileBusy +ErrOpen SEC + RTS + +WrgStorTyp LDA #badStoreType ;Report file is of wrong storage type! + SEC + RTS + +Open1 LDY fcbPtr ;Get address of first free FCB found + LDA fcbFlg ;This byte indicates that a free FCB found + BNE AssignFCB ; if non-zero is available for use + LDA #fcbFullErr ;Report FCB full error + SEC + RTS + +AssignFCB LDX #fcbSize-1 ;Assign fcb, but first + LDA #$00 ; clean out any old +ClrFCB STA fcb,Y ; rubbish left around... + INY + DEX + BPL ClrFCB + + LDA #fcbEntNum ;Now begin claim by moving in file info + TAX ;Use X as source index + ORA fcbPtr + TAY ; and Y as destination (FCB) +FCBOwner LDA d_dev-1,X ;Move ownership information + STA fcb,Y ;Note: this code depends upon the defined + DEY ; order of both the FCB and directory entry + DEX + BNE FCBOwner ; buffer (d.). beware of changes!!! ************* + + LDA d_file+d_stor ;Get storage type + LSR ;Strip off file name length + LSR + LSR + LSR ;(by dividing by 16) + TAX ;Save in X for later type comparison + STA fcb+fcbStorTyp,Y; and in FCB for future access + + LDA d_file+d_attr ;Get files attributes & use + AND #readEnable+writeEnable; it as a default access request + CPX #directoryFile ;If directory, don't allow write enable + BNE SavAttr1 + AND #readEnable ;(Read-only) +SavAttr1 STA fcb+fcbAttr,Y + AND #writeEnable ;Check for write enabled requested + BEQ :1 ;Branch if read only open + LDA totEnt ;Otherwise, be sure no one else is reading + BNE ErrBusy ; same file (set up by TestOpen) + +:1 CPX #tree+1 ;Is it a tree type file? + BCC :2 ;Test for further compatiblity. It must + CPX #directoryFile ; be either a tree or a directory + BNE WrgStorTyp ;Report file is of wrong storage type + +:2 LDX #$06 ;Move address of first block of file, +:loop1 STA blockNum+1 ; end of file, and current usage count + LDA fcbPtr + ORA oFCBTbl,X ;This is done via a translation + TAY ; table between directory info and FCB + LDA d_file+d_first,X + STA fcb,Y + DEX ;Has all info been moved? + BPL :loop1 + + STA blockNum ;Last loop stored hi addr of first block + LDY fcbPtr + LDA cntEnt ;This was set up by 'TestOpen'... + STA fcb+fcbRefNum,Y ;Claim fcb for this file + JSR AllocBuf ;Go allocate buffer in memtables + BCS ErrOpen2 ;Give up if any errors occurred + + JSR FndFCBuf ;Returns addrs of bufs in data & index pointers + LDA Level ;Mark level at which + STA fcb+fcbLevel,Y ; file was opened + LDA fcb+fcbStorTyp,Y;File must be positioned to beginning + CMP #tree+1 ;Is it a tree file? + BCS OpenDir ;No, assume it's a directory + LDA #$FF ;Fool the position routine into giving a + STA fcb+fcbMark+2,Y ; valid position with preloaded data, etc + LDY #$02 ;Set desired position to zero + LDA #$00 +:loop2 STA tPosll,Y + DEY + BPL :loop2 + + JSR RdPosn ;Let tree position routine do the rest + BCC OpenDone ;Branch if successful + +ErrOpen2 PHA ;Save error code + LDY fcbPtr ;Return buffer to free space + LDA fcb+fcbFileBuf,Y + BEQ :1 ;Branch if no buf # + + JSR ReleaseBuf ;Doesn't matter if it was never allocated + LDY fcbPtr ; since error was encountered before file +:1 LDA #$00 ; was successfully opened, then + STA fcb+fcbRefNum,Y ; it's necessary to release FCB + PLA + SEC + RTS + +OpenDir JSR RdFCBData ;Read in first block of directory file + BCS ErrOpen2 ;Return any error after freeing buffer & FCB +OpenDone LDX vcbPtr ;Index to volume control block + INC vcb+vcbOpenCnt,X;Add 1 to the number of files currently open + LDA vcb+vcbStatus,X ; & indicate that this volume has + ORA #$80 ; at least 1 file active + STA vcb+vcbStatus,X + + LDY fcbPtr ;Index to file control block + LDA fcb+fcbRefNum,Y ;Return reference number to user + LDY #c_outRef + STA (parm),Y + CLC ;Indicate successful open! + RTS ;All done... + +*------------------------------------------------- +* Test if file can be opened +* C=1 +* Already opened with write access +* NB. Multiple write access is not allowed +* C=0 +* File may be opened/already opened +* If fcbFlag <> 0, got a free FCB & +* (fcbPtr) = index into FCB table +* NB. Multiple read access is allowed + +TestOpen LDA #$00 + STA cntEnt ;This temp returns the refnum of a free FCB + STA totEnt ;This is used as a flag to indicate file is already open + STA fcbFlg ;This is a flag to indicate a free FCB is available + +TestOpen1 TAY ;Index to next FCB + LDX fcbFlg ;Test for free FCB found + BNE :1 ;Branch if already found + INC cntEnt +:1 LDA fcb+fcbRefNum,Y ;Is this FCB in use? + BNE ChkActive ;Branch if it is + TXA ;If not, should we claim it? + BNE TestNxtFCB ;Branch if free FCB already found + STY fcbPtr ;Save index to free FCB + LDA #-1 ;Set fcb flag to indicate free FCB found + STA fcbFlg + BNE TestNxtFCB ;Branch always to test next FCB + +ChkActive TYA ;Add offset to index to ownership info + ORA #fcbEntNum + TAY ;Put it back in Y-reg + LDX #fcbEntNum ;Index to directory entry owner info +WhoOwns LDA fcb,Y + CMP d_dev-1,X ;All bytes must match to say that its + BNE TestNxtFCB ; the same file again + DEY ;Index to next lower bytes + DEX + BNE WhoOwns ;Loop to check all owner info + + INC totEnt ;File is already open, + LDA fcb+fcbAttr,Y ;Now see if it's already opened for write + AND #writeEnable + BEQ TestNxtFCB ;Branch if this file is read access only + SEC ;Multiple write access not allowed + RTS + +TestNxtFCB TYA ;Calc position of next FCB + AND #%11100000 ;First strip any possible index offsets + CLC + ADC #fcbSize ;Bump to next FCB + BNE TestOpen1 ;Branch if more to compare + CLC ;Report no conflicts + RTS \ No newline at end of file diff --git a/MLI.SRC/PROLDR.S b/MLI.SRC/PROLDR.S new file mode 100644 index 0000000..0cc3d9b --- /dev/null +++ b/MLI.SRC/PROLDR.S @@ -0,0 +1,823 @@ + TTL 'ProDOS Kernel Loader' + +*---------------------------------------------------------* +* Disassembled with The Flaming Bird Disassembler * +* (c) Phoenix corp. 1992,93 - All rights reserved * +*---------------------------------------------------------* + ORG $2000 + MX %11 + +* There are 3 boot entry points here + +NormalBoot JMP ProStart ;Normal boot entry point... + JMP NetBootP8 ;Network booted into P8 + JMP NetBootGSOS ;Network-booted into GS/OS + +*------------------------------------------------- +* Messages + +apple2Msg ASC "Apple II" +p8VerMsg ASC "ProDOS 8 V2.0.3 06-May-93" +blanks ASC " " +cpyRhtMsg ASC "Copyright Apple Computer, Inc., 1983-93" +rsvdMsg ASC "All Rights Reserved." +endGreetMsg EQU * +grtLnZ EQU blanks-p8VerMsg/2 +grtLnZZ EQU cpyRhtMsg-blanks/2 +grtLnZZZ EQU endGreetMsg-rsvdMsg/2 + +NetBootGSOS INC SetupRTS ;Setup and +NetBootP8 INC SetupRTS ; RTS entry point... + +*------------------------------------------------- +ProStart LDA unitNum ;Get boot device number + STA bUnit ;Save it for later 'prefix' + JSR Greet ;Put up greeting message + SED + LDA #$99 ;Check we have a 65C02 + CLC + ADC #$01 ; by using chip's decimal mode + CLD + BMI m48K ;Error + + LDA #$01 + TRB STATEREG + LDX #tablIntrp + JSR Reloc + BCS m48K ;Branch if error + + LDY #$00 + LDA #$FF ;Make sure there is + STA $BFFF ; at least 48K + EOR $BFFF + SEC + BNE m48K ;Branch if not + STA $BFFF ;Try again. Once may have been lucky! + LDA $BFFF + BNE m48K + LDA RDROM2 ;Enable Motherboard ROM + JSR WhichROM ;Get preliminary system configuration + BCS m48K ;Branch if apple /// emulation + LDA apple ;Test for 48K configuration + AND #$20 ; by testing for 64K plus + BNE m64K ;Branch if >48k +m48K JMP ReqEnh2 ;Must have at least 64K + +m64K LDX #tabl64 + JSR Reloc + LDA kVersion ;Get current revision number + STA XDOSver ; & save it for directory use +NoGood0 BCC :1 + JMP NoGood + +:1 LDA RDROM2 ;Enable Motherboard ROM + LDX VERSION ;Look for //e family + CPX #$06 + BNE ItsAIIe + LDA #%11100000 ;phylum check on high 2 bits + BIT $FBC0 ;Another approved location + PHP ;Save the results from the bit + LDA apple + AND #%00110111 ;Mask off bits 7,6 and 3 + PLP ;Get results back + BVC Set3 ;//c or //x + BMI Set7 ;Branch if //e + +Set3 PHP ;Save the results from the bit again + ORA #%00001000 ;Set bit 3 on + PLP + BPL Mach2 ;Branch if //c + ORA #%01000000 + BPL SaveMach ;Always... + +Mach2 INC cFlag-LIcode+LoadIntrp;Make it easy to see if we're on //c later + BVS SaveMach +Set7 ORA #%10000000 ;Set bit 7 on + +SaveMach STA apple + LDA RDROM2 ;Enable ROM for Cortland ID routine + SEC ;Carry will determine if cortland or not + JSR IDroutine ;RTS in all a //'s prior to Cortland + BCS ItsAIIe ;Branch if really a //e + INC cortLand ;Set loader's cortland flag! + STZ $04FB ;Screenhole + JSR SetVid + +* If SetupRTS is zero, zero out OS_BOOT for AppleTalk. +* (SetupRTS reflects whether we're ProDOS 8 regular or +* running with the GSOS.) + + LDA SetupRTS + BNE ItsP8 + STAL OS_BOOT ;Flag system was booted w/P8 + JSR GSPatches ;Patch GS/OS vectors +ItsP8 EQU * +ItsAIIe LDA bUnit ;Place boot devnum in globals + STA bbUnit + STA DevNum + JSR DevSrch ;Finish setting up globals + LDA bbUnit + STA DevNum + JSR LC1In + LDX #TClkStuff + JSR Reloc +NoGood1 BCS NoGood0 ;Give up any time we got problems + +* Dispatcher 1 must go in bank 2 of language card +* in a 64K or larger system. + + LDA #CallDisp ; into jspare vector + STA jSpare+2 + LDA LCBANK2 + LDA LCBANK2 ;Switch in bank 2 + LDX #DispGS + LDA SetupRTS + CMP #$02 ;GS/OS boot? + BEQ RelocDisp ;Yes + + LDX #DispBB + LDA MachID + BIT #$00 + BNE RelocDisp ;Never! + AND #%1100_0010 ;IIe/III emul/IIc + CMP #%1000_0010 ;IIe/IIc & 80-col card? + BEQ RelocDisp ;Go install BB dispatcher + LDX #Disp64 + INC No80Col + +RelocDisp JSR Reloc + LDA #$EE ;Nonsense byte to distinguish bank 2 + STA $D000 + JSR LC1In ;Switch bank 1 back in + BCS NoGood1 + +* Test for 128K so /RAM disk can be installed + +ChkRAM LDA MachID + AND #$30 + EOR #$30 + BNE NoRAMdsk + +************ see rev note #45 ************* + + LDX #$FF ;X used to init Aux SP to $FF + PHP ;Save interrupt status + PLA ; in A-reg + SEI ;No interrupts for safety's sake + STA SETALTZP ;Swap in Aux LC & zp & stack + STX $0101 ;Init Aux SP to $FF + STA SETSTDZP ;Back to main LC, zp, and stack + PHA ;Restore + PLP ; interrupt status + STA SETINTC3ROM ;Make sure internal slot 3 ROM is in + JSR RAM_1 ;Go install /RAM + +* Now check for interrupt vector. If vector <$D000 then we +* have new ROMs and should re-point vector in language card to +* ROM vector and set a flag byte. If vector is >$D000, reset +* flag byte and do nothing. + +NoRAMdsk LDA ROMIN2 ;Switch in ROM + LDY IrqVect + LDX IrqVect+1 ;Get hi byte of irq vector + +* The jsr LC1In was moved here from after the BCS Chk4Card so the +* sta IrqFlag is written to the proper bank. + + JSR LC1In + +*--------------------- see rev note #29 ------------------------ + + CPX #$D0 ;Is it >$D000 (old ROMs) + LDA #$00 ;Anticipate not + BCS Chk4Card ; but branch if they are old ROMs + STA SETALTZP ;Swap Aux LC, zpg and stack + LDA #$FF ;Set Aux stack pointer at $FF + STA $0101 ; while we're here + STX IrqVect+1 + STY IrqVect ;Save ROM vector in Aux lang. card + STA SETSTDZP ;Swap in main lc, zpg and stack + STX IrqVect+1 + STY IrqVect ;Save ROM vector in main lang. card + LDA #$01 ;Set IrqFlag to show new ROMs +Chk4Card STA IrqFlag + STZ cortFlag ;Assume we're not on a cortland + LDA cortLand ;Are we running on a cortland? + BEQ NoCort ;If not branch, and muck w/slot 3! + INC cortFlag ;Make it a one if we're on cortland + BRA DoCard + +* Check for a ROM in slot 3. Switch in internal +* $C300 firmware if no ROM seen + +NoCort STA SETINTC3ROM ;Start with internal firmware switched in + LDA SltByt ;Get slots ROM pattern + AND #%00001000 ;Mask off all but slot 3 + BNE IsROMin3 ;Branch if there is rom in slot three + BRA NoSlot3ROM ;Continue with boot.... + +* We've seen a ROM in slot 3. Is it an external, identifiable +* 80-col card with interrupt routines? If so, enable it. +* If not, switch in the internal $C300 firmware. + +IsROMin3 STA SETSLOTC3ROM ;Switch in slot 3 ROM + LDA $C305 ;1st generic terminal card ID byte + CMP #$38 + BNE HitSwtch ;Branch if not a terminal card + LDA $C307 ;2nd generic terminal card ID byte + CMP #$18 + BNE HitSwtch ;Branch if not a terminal card + LDA $C30B ;3rd generic terminal card ID byte + CMP #$01 + BNE HitSwtch ;Branch if not a terminal card + LDA $C30C ;Is it an Apple 80-col card compatible? + AND #$F0 ;Mask off lo nibble + CMP #$80 ; and check for $8n + BNE HitSwtch ;Branch if not an 80-col card + LDA MachID ;Get the machine ID + AND #%11001000 + CMP #$C0 ;Is it a //+? + BEQ DoCard ;Branch if it is + LDA $C3FA ;Check for interrupt handler routine + CMP #$2C ; in the magic $C3FA spot + BEQ DoCard ;Branch if interrupt handler is there! +HitSwtch STA SETINTC3ROM ;Switch in internal $C300 ROM + +* Verify that the card in the aux slot is actually there. + + STA SET80COL ;80-store on + STA TXTPAGE2 + LDA #$EE + STA $0400 + ASL + ASL $0400 + CMP $0400 + BNE Maybee ;Branch if not there + LSR + LSR $0400 + CMP $0400 +Maybee STA TXTPAGE1 ;Main memory + STA CLR80COL ;80-store off + BEQ DoCard ;Branch if card is there + LDA MachID ;Get machine id byte + AND #%11111101 ;Mask off 80-col bit + BNE DoCard1 + +* OK, the card's good. Leave it enabled and update the MachID + +DoCard LDA MachID + ORA #%00000010 +DoCard1 STA MachID +NoSlot3ROM LDA cortLand ;Are we running on a cortland? + BEQ NotCortLand ;Branch if not + LDA #$4C ;Enable clock routine by + STA DateTime ; putting a JMP in front of clock vector + LDX #cortClock ; the cortland clock driver + JSR Reloc ; and relocate it + LDA #$01 ;Denote clock present in MachID byte! + TSB MachID ; bit 0 set to 1 +NotCortLand LDA SetupRTS ;Get value of setup entry point flag... + BEQ NoRTS ;Branch if normal boot... + LDA RDROM2 ;Make sure the ROM is in for consistency... + RTS ;Return to the caller at the setup entry point. ($2003/$2006) + +SetupRTS DB $00 ;0-Normal Boot, 1-Ret 2-Ret to GS/OS + +NoRTS EQU * +************************************************* +* Now set prefix to boot device. +* + JSR GoPro ;First 'online'(was labled bootpfx,#en3) + DB $C5 + DA olParm + BCS NoGood ;Branch if problems + LDA pnBuf+1 ;Get volume name length + AND #$0F ;strip devnum + BEQ NoGood ;Branch if error + INC ;Add 1 for leading '/' + STA pnBuf ;Save prefix length + LDA #'/' ;Place leading '/' in path name buf + STA pnBuf+1 + JSR GoPro ;Set prefix + DB $C6 + DA PfxParm + BCS NoGood ;Branch if problems + TAX ;(A) = 0 after successful MLI call + STX dst ;(Zerored) + LDY #$02 ;Read root directory into buffer + LDA #>ABuf ; starting at $0C00 +RdDirBlks STA dst+1 + STA dbBufr+1 ;(using a pointer in zero page also) + STY dbBlock + STX dbBlock+1 + JSR GoPro + DB $80 ;Block read + DA dbParms + BCS NoGood + + LDY #$03 ;Get next block number from link + LDA (dst),Y + TAX + DEY + ORA (dst),Y ;If both bytes are the same i.e. 0, 0 + BEQ ExitDirBlkRd ; then no more blocks of directory + LDA (dst),Y + TAY + LDA dst+1 + CLC + ADC #$02 ;Add $200 to buffer pointer until + CMP #$14 ; it points past $13FF + BCC RdDirBlks ;If ok, read next block +ExitDirBlkRd JMP LoadIntrp ;All is well, load interpreter!!! + +NoGood STA RDROM2 ;Make sure rom is there + JSR HOME ;Clear video + LDY #mesLen ;Print message centered on screen +:loop LDA errMess,Y + STA SLIN11+4,Y + DEY + BPL :loop +Hang BMI Hang + +mesLen EQU 29 +errMess ASC "Relocation/Configuration Error" + +ReqEnh2 LDY #mes2Len +:loop2 LDA errMess2,Y ;Requires enhanced // + STA SLIN13+2,Y + DEY + BPL :loop2 +Hang2 BMI Hang2 + +*------------------------------------------------- +mes2Len EQU 35 +errMess2 ASC "REQUIRES ENHANCED APPLE IIE OR LATER" + +olParm DB $02 +bUnit DB $60 ;Boot Unit + DA pnBuf+1 + +PfxParm DB $01 + DA pnBuf + +* Dir block read + +dbParms DB $03 +bbUnit DB $00 +dbBufr DA $0000 +dbBlock DW $0000 + +cortLand DB $00 ;Non-zero if IIgs +No80Col DB $00 ;Flag 40-col dispatcher to be installed + +*------------------------------------------------- +AuxGo EQU $03F0 ;Entry point to Aux LC driver call routine +cZero EQU $00 +cMove EQU $01 +cReloc EQU $04 +cDone EQU $FF +entLen EQU $0C23 + +* Code move tables are explained in file Reloc.s + +tablIntrp DB cMove ;Move interpreter loader code & tables + DA LoadIntrp ;Code is address independent + DW pcLen + DA LIcode + +Pg3Tbl DB cMove + DA AuxGo ;was $3D6 + DW $0010 ; and $002A + DA pg3Stuff + + DB cMove + DA look ;dest addr + DW $0002 ;# of bytes to move + DA dst ;src addr + + DB cMove ;Move 128K test to zero page + DA Test128 + DW End128 + DA Strt128 + DB cDone + +Disp64 DB cMove + DW $D100 ;lang card bank 2 + DW $0300 ;3 pages + DA SEL_0 ;$5A00 + DB cDone + +DispBB DB cMove + DA $D100 ;lang card bank 2 + DW $0300 + DA SEL_1 ;$5D00 + DB cDone + +DispGS DB cMove + DA $D100 ;lang card bank 2 + DW $0300 + DA SEL_2 ;$6000 + + DB cMove + DA DispAdr ;$1000 + DW $0300 + DA SEL_2 ;$6000 + DB cDone + +*------------------------------------------------- +* The following table is for moving the 64K version of +* the MLI to its execution address. + +tabl64 DB cMove ;Relocate the interrupt/break/reset + DA IntHandler ; handler and associated vectors + DW $0065 ;Number of bytes to relocate + DA MLI_3 ;Source address of code to relocate + + DB cMove ;Move preset 64K version of globals + DA Globals + DW $0100 + DA MLI_1 + + DB cZero ;Clear buffers/workspace + DA orig ;dest + DW $0700 ;# of bytes to zero + + DB cMove ;Move 64k version of MLI to language card + DA orig1 ;See #45..put rwts in lc bnk2 to make MLI_2 + DW $2100 ;MLI length + DA MLI_2 + + DB cMove ;Move 64K version of + DA RWTS ; Disk ][ routines + DW $0700 + DA XRW_0 + DB cDone ;Clock moved later + +TClkStuff DB cMove ;Lastly move/relocate thunderclock + DA ClockBegin ; whether needed or not + DW $007D + DW TCLOCK_0 + DB cReloc ;Adjust slot addresses + DA ClockBegin + DW $0069 + DA ClockBegin + DB $00 +clock64 EQU *+2 + DB $C1 ;Last changed by DevSrch to correct slot# + DB $C1 + DB $00 + DB cDone + +********** see rev note #50 ********* + +cortClock DB cMove ;Cortland clock relocating table + DA ClockBegin ;Destination address + DW $007D ;Length of 125 bytes + DW CCLOCK_0 ;Source load address of driver + DB cDone + +****************** see rev note #56 ************* +* +* Let's load and jsr to the appletalk configuaration file "atinit" +* if it is found. If it is not found, just continue with the loading +* and running of the ".SYSTEM" file. + +LIcode EQU * + JSR GoPro ;Make a get file info call to make + DB $C4 ; atInit file is there and is + DA gfiList ; of the proper file type + BCC GFI_ok ;Branch if call successful... + CMP #fileNotFound ;Was error "file not found"? + BEQ LoadInt + BNE ATLoadErr ;Otherwise fatal i/o error in loading atInit +GFI_ok LDA gfiType ;Now see if atInit file is of proper type... + CMP #$E2 ;Is it the correct file type? + BNE ATLoadErr ;Error if wrong file type! + + JSR GoPro ;Open atInit file + DB $C8 + DA atOpen ; parameter list... + BNE ATLoadErr ; branch if error... + LDA #$9F ;39.75K + STA rdLen+1 + STZ rdLen + JSR GoPro + DB $CA + DA rdParm + BNE ATLoadErr + JSR GoPro + DB $CC + DA clParm + BNE ATLoadErr + LDA RDROM2 ;Put ROM on line for atInit.... + JSR $2000 ;Call the atInit routine to set up appletalk stuff +LoadInt JMP GoLoadInt ;Go execute the .SYSTEM file + +ATLoadErr LDX atErr +:1 LDA atErr,X + STA SLIN15,X + DEX + BNE :1 +ATerrHang BEQ ATerrHang +atErr STR "Unable to load ATInit file" + +gfiList EQU *-LIcode+LoadIntrp + DB $0A ;Parameter count + DA atInitName ;Pointer to "atinit" file name + DB $00 ;access +gfiType EQU *-LIcode+LoadIntrp + DB $00 ;File type + DS 13,0 ;Space for rest of parameters... + +atOpen EQU *-LIcode+LoadIntrp + DB $03 + DW atInitName ;Pointer to "atinit" file name + DA $1400 ;Address of I/O buffer + DB $01 ;Reference number hard coded since no other files + +atInitName EQU *-LIcode+LoadIntrp + STR "atinit" ;Name of appletalk config file + +GoLoadInt EQU *-LIcode+LoadIntrp + LDA #>ABuf ;Search directory already in + STA idxl+1 ; memory between $0C00 & $13FF + LDA #tCmd,tUnit,R2L,R2,R1 + DEX + BPL :loop + + AND FormatFlag ;Format the volume first time + BNE doCommand ; thru, or when requested + +doFormat LDX blockNum ;Save R1 during format + LDA #>VBlock1 ;Block to be cleared + JSR ClrBuf1 ;ClrBuf clears all buffers + LDY #$03 ;Format volume in 2 chunks +:loop1 LDA VDir,Y + STA VBlock1+4,Y + DEY + BPL :loop1 + + LDA #$FE ;Set last block as unusable to protect vectors + STA BitMap+$F + TYA ;Set bitmap bits to $FF + LDY #$0E ;15 bytes to set +:loop2 STA BitMap,Y + DEY + BNE :loop2 + STY BitMap ;First byte=0 + + LDY #$07 ;Do other chunk +:loop3 LDA Access,Y + STA VBlock1+34,Y + DEY + BPL :loop3 + + LDA FormatFlag ;If 0, set to FF + BNE DFX ;else exitcard + + STY FormatFlag ;Y=FF, won't format next time + STX R1 ;restore R1 + +* Now use the requested block number to determine +* which routine performs the transfer + +doCommand ASL R1 ;Block requested->page requested + LDA R1 ;Get page requested + CMP #$BF ;In Language card? + BCS XferLC1 ;Yes, do it + CMP #$06 ;Bit map? + BNE :1 + JMP TBMap ;Yes, transfer bitmap +:1 JMP TReg ;else normal transfer + +* When a block between $60 and $7F is requested, it must +* be spirited into/from the language card area of the +* 64K card. This requires a two-stage move: into the temp +* buffer and then to its real destination. + +XferLC1 TAX ;Save R1 for later + JSR SetPtr ;Get direction + PHP ;Save direction + BCS LCwrt ;It is a write +LCrd TXA ;Get R1 back + CMP #$CF ;Which bank is it in? + BCS XferLC2 ;In main bank + ORA #$10 ;In secondary bank + BNE XferLC ;Branch always + +XferLC2 STA LCBANK2 ;Turn on main $D000 + STA LCBANK2 + +XferLC STA R1 ;Restore R1 + LDA R2H ;Save R2 for later + PHA + LDX R2L + STA SETALTZP ;Now switch to other ZP + LDA #>ABuf ;Set R2 to Abuf + STA R2H + LDA #R1); +* if CMD is 1, a read is done (R1->R2). + +BlockDo0 LDA #>ABuf ;Set up R1 = Abuf +BlockDo1 STA R1 +BlockDo JSR SetPtr ;set pointers + BCS BlockWrite ;it's a write + STA WRMAINRAM ;transfer buffer directly to main ram + TAY ;0 left from SETPTR +:CpyLoop LDA (A1),Y ;Transfer A1,A2 to A4,A3 + STA (A4),Y + LDA (A2),Y + STA (A3),Y + DEY + BNE :CpyLoop + STA WRCARDRAM ;Back the way it was +DoneWrt RTS ;MainWrt returns here + +BlockWrite LDA #MainWrt + JMP Ex1 ;Set PassIt+1 and transfer + +*------------------------------------------------- +* SETPTR is used by other routines to set up +* pointers and to detect read or write. + +SetPtr LDA tCmd ;The rest depends on read + LSR ;or write. Which is it? + BCS CmdWrt ;It's write + +CmdRd LDA R2H ;Get dest page + STA A4+1 ;1st dest page (MOVE) + STA A3+1 ;2nd dest page + LDA R2L ;Low byte dest page + STA A4 ;1st dest page low + STA A3 ;2nd dest page low + LDA R1 ;Get source page + STA A1+1 ;1st source page + STA A2+1 ;2nd source page + LDA #$00 ;Source page aligned + STA A1 ;1st source page + STA A2 ;2nd source page + BEQ CmdBoth ;Update second pages + +CmdWrt LDA R2H ;Get source page + STA A1+1 ;1st source page + STA A2+1 ;2nd source page + LDA R2L ;Get source page low + STA A1 ;1st source page low + STA A2 ;2nd source page low + LDA R1 ;Get dest page + STA A4+1 ;1st dest page + STA A3+1 ;2nd dest page + LDA #$00 ;Dest page aligned + STA A4 ;1st dest page + STA A3 ;2nd dest page + +CmdBoth INC A2+1 ;Update 2nd source page + INC A3+1 ;Update 2nd dest page + RTS + +*------------------------------------------------- +* TZIP is called if Blocks 0,1,4,5 are requested. +* On write it does nothing, on read, it returns 0's + +TZip JSR ClrBuf0 ;Fill ABUF with 0's + JSR BlockDo ;Transfer them 0's + JMP ExitCard ; & return + +*------------------------------------------------- +* ClrBuf fills the buffer indicated by R1 to 0's +* Should only be called on a read or format. + +ClrBuf0 LDA #>ABuf ;ABUF is temp buffer +ClrBuf1 STA R1 ;Assign to BLOCK +ClrBuf2 JSR SetPtr ;Set pointers + TAY ;A set to 0 by setptr +:CpyLoop STA (A1),Y + STA (A2),Y + DEY + BNE :CpyLoop + RTS + +*------------------------------------------------- +* TREG maps the requested block into the aux card +* so that 8K data files will be contiguous (the index +* blocks will not be placed within data). + +TReg CMP #$04 ;page 4 = vdir + BNE :1 ;Not vdir, continue + LDA #$07 ;Else xfer block 7 + BNE GoTimes2 + +***************** See Rev Note #43 ******************** + +:1 CMP #$0F ;If any pageABuf ;Use temporary buffer as BLOCK + STA R1 + JSR SetPtr ;Set pointers/test read-write + BCS BitWrt ;Its a write! + +BitRd JSR ClrBuf2 + + LDY #$0F ;Now put real bitmap there +:CpyLoop LDA BitMap,Y + STA (A1),Y + DEY + BPL :CpyLoop + JSR BlockDo ;Move temp buf to user buf + JMP ExitCard + +BitWrt JSR BlockDo ;move user buf to temp buf + JSR SetPtr ;Set pointers + LDY #$0F ;move temp buf to bitmap +:CpyLoop LDA (A4),Y ;(pointer set by SETPTR) + STA BitMap,Y + DEY + BPL :CpyLoop + JMP ExitCard + +*------------------------------------------------- +FormatFlag DB $00 ;Not formatted yet + +tCmd DS 1,0 ;Command byte +tUnit DS 1,0 ;Unit byte (Not used) +R2L DS 1,0 ;Low byte of user buffer +R2H DS 1,0 ;Hi byte of user buffer +R1 DS 1,0 ;Page requested + +BitMap EQU * + HEX 00FFFFFF ;Blocks 0-7 used + HEX FFFFFFFF + HEX FFFFFFFF + HEX FFFFFFFE + +VDir EQU * ;Start of virt dir +TypeNameLen DB $F3 ;Storage type F, namelength 3 + ASC 'RAM' +Access DB $C3 ;Destroy, Rename, Read enabled + DB $27 ;entry length + DB $0D ;Entries/Blk + DW $0000 ;File Count + DW $0003 ;Map_Pointer=Block 3 + DB $7F ;Total_Blocks=128 blocks + +*------------------------------------------------- + +ExitCard LDA LCBANK1 ;Restore lang card + LDA LCBANK1 + PLA ;Get 80STORE + BPL Ex0 ;80STORE wasn't on + STA SET80COL +Ex0 JMP $03EF ;Jump around PassIt (3ED,3EE) + + DS $3EF-*,0 ;Pad thru $3EE + + LDA #NoErr + +Ex1 STA PassIt+1 ;Also used by BlockWrite + CLC ;Transfer card to main + CLV ;Use standard zp/stk + JMP Xfer ;There's no place like home... + +* NOTE: The previous section of code MUST NOT use $3FE & $3FF +* since the Interrupt Vector must go there if AUX interrupts +* are to be used. + + DS 2,0 ;Pad to end of mem page diff --git a/MLI.SRC/RAM1.S b/MLI.SRC/RAM1.S new file mode 100644 index 0000000..5d92794 --- /dev/null +++ b/MLI.SRC/RAM1.S @@ -0,0 +1,43 @@ +************************************************** + ORG Srce + MX %11 + +* Move LCSrc to LCDest + + LDY #$99 ;Move $9A bytes +:MovLoop LDA LCSrc,Y ;Get a byte of source + STA LCDest,Y + DEY + CPY #$FF + BNE :MovLoop + +* Move RAMSrc to RAMDest + + LDX #RAMsrc ;Source high + STX A1+1 + INX + STX A2+1 ;End high + LDA #RAMdest + STA A4+1 + SEC ;RAM to Card + JSR AuxMove ;Use built-in rtn to move + +* Now install it into the system + + LDA #LCdest + STA DevAdr32+1 + INC DevCnt + LDX DevCnt + LDA #%10110000+$F ;Unit # of /RAM ($B0+$F) + STA DevLst,X ;NB. $B0 ($30+hi-bit set) + RTS + + DS \,0 ;Pad to end of mem page \ No newline at end of file diff --git a/MLI.SRC/RAM2.S b/MLI.SRC/RAM2.S new file mode 100644 index 0000000..ed8a152 --- /dev/null +++ b/MLI.SRC/RAM2.S @@ -0,0 +1,98 @@ +************************************************** +* Ram Disk function Handler + + ORG LCDest + MX %11 +EnterRAM CLD ;/RAM entry point + LDX #12-1 ;Save 12 bytes of params +:CpyLoop1 LDA A1,X + STA A1L1,X + DEX + BPL :CpyLoop1 + + LDX #1 ;Save XFER Vectors too +:CpyLoop2 LDA PassIt,X + STA SP1,X + DEX + BPL :CpyLoop2 + + LDA dhpCmd ;Get command + BEQ STAT ;0=STATUS + CMP #$04 ;Check for command too high + BCS IOErr ;If it is, IO ERR + EOR #$03 ;0=FORMAT,2=READ,1=WRITE + STA $42 ;CMD=>0=Format,2=Read,1=Write + BEQ Format ;Format the volume + + LDY blockNum+1 ;Check for enormous blocknum + BNE IOErr ;I/O error if too big + LDA blockNum + BMI IOErr ;Largest block is $7F + +* At this point, control is passed to the code in the +* alternate 64K. It is used for read, write, and +* format. After the request is completed, control +* is always passed back to NoErr. + +Format LDA #EnterCard +GoCard STA PassIt+1 ;Also used by MainWrt + SEC ;RAM->Card + CLV ;start with original z.p. + JMP Xfer ;transfer control + +IOErr LDA #drvrIOError ;Get err num + BNE ErrOut ; & return (always) + +WPErr LDA #drvrWrtProt +ErrOut SEC ;Flag error + BCS Restore ;Restore cmd and unitnum + +STAT EQU * +NoErr LDA #$00 ;No error + CLC ;Flag no error + +Restore PHP ;Save status + PHA ;Save error code + + LDX #12-1 ;Restore 12 bytes of params +:CpyLoop LDA A1L1,X + STA A1,X + DEX + BPL :CpyLoop + + LDA SP1 ;Restore XFER params + BIT $6060 ;This instruction is to put + STA PassIt ; an RTS at $FF58 as in ROM + LDA SP1+1 + STA PassIt+1 + +* -------------------- See rev note 21 ----------------------- + + PLA ;Get error + PLP ;Get status + RTS + +*------------------------------------------------- +* Write file buffer in MAIN to AUX block +* Assume A1,A2,A3,A4 ptrs are set in Aux Driver + +MainWrt STA WRCARDRAM ;Xfer data to card + LDY #$00 +:MovLoop LDA (A1),Y ;Pointers set in card by SETPTR + STA (A4),Y + LDA (A2),Y + STA (A3),Y + DEY + BNE :MovLoop + STA WRMAINRAM ;Done writing Card + + LDA #DoneWrt + JMP GoCard + +SP1 DS 2,0 +A1L1 DS 12,0 ;12 bytes of storage + DS 11,0 ;Pad to int handler \ No newline at end of file diff --git a/MLI.SRC/READWRITE.S b/MLI.SRC/READWRITE.S new file mode 100644 index 0000000..0673a35 --- /dev/null +++ b/MLI.SRC/READWRITE.S @@ -0,0 +1,692 @@ +************************************************** +* READ Call + +Read JSR MovDBuf ;First transfer buffer adr & request count to a + JSR MovCBytes ; more accessible location, also get fcbAttr, CLC + PHA ;Save attributes for now + JSR CalcMark ;Calc mark after read, test mark>eof + PLA ;Carry Set indicates end mark>eof + AND #readEnable ;Test for read enabled first + BNE :1 ;Branch if ok to read + LDA #invalidAccess ;Report illegal access + BNE GoFix1 ;Branch always taken + +:1 BCC Read2 ;Branch if result mark is used to set V flag) + +*------------------------------------------------- +* Cleanup after direct I/O + +FixDataPtr LDA dataPtr ;Put current user buffer + STA userBuf ; address back to normal + LDA dataPtr+1 + STA userBuf+1 ;Bank pair byte should be moved also + LDY fcbPtr ;Restore buffer address + JMP FndFCBuf + +*------------------------------------------------- +* Read directory file... + +DirRead JSR RdPosn + BCS ErrDirRd ;Pass back any errors + JSR PrepRW ;Prepare for transfer + JSR ReadPart ;Move data to user's buffer + BVC DirRead ;Repeat until request is satisfied + JSR RWDone ;Update FCB as to new position + BCC :1 ;Branch if all is well + CMP #eofEncountered ;Was last read to end of file? + SEC ;Anticipate some other problem + BNE :Ret ;Branch if not EOF error + + JSR SavMark + JSR ZipData ;Clear out data block + LDY #$00 ;Provide dummy back pointer for future re-position + LDX fcbPtr ;Get hi byte of last block +:loop LDA fcb+fcbDataBlk,X + STA (dataPtr),Y + LDA #$00 ;Mark current block as imposible + STA fcb+fcbDataBlk,X + INX + INY ;Bump indexes to do both hi & low bytes + CPY #$02 + BNE :loop +:1 CLC ;Indicate no error +:Ret RTS + +ErrDirRd JMP ErrFixZ + +*------------------------------------------------- +* Copy caller's I/O len +* Exit +* (A)=attributes +* (Y)=(fcbptr) + +MovCBytes LDY #c_reqCnt ;Move request count + LDA (parm),Y ; to a more accessible location + STA cBytes + STA rwReqL + INY + LDA (parm),Y + STA cBytes+1 + STA rwReqH + LDY fcbPtr ;Also return (Y)=val(fcbptr) + LDA fcb+fcbAttr,Y ; & (A)=attributes + CLC ; & carry clear... + RTS + +*------------------------------------------------- +* Point userBuf ($4E,$4F) to caller's data buffer +* Exit +* (A) = file's storage type + +MovDBuf LDY #c_dataBuf ;Move pointer to user's buffer to bfm + LDA (parm),Y + STA userBuf + INY + LDA (parm),Y + STA userBuf+1 + +GfcbStorTyp LDY fcbPtr ;Also return storage type + LDA fcb+fcbStorTyp,Y;(on fall thru) + RTS + +*------------------------------------------------- +* Copy file mark, compute and compare end mark + +CalcMark LDX #$00 ;This subroutine adds the requested byte + LDY fcbPtr + CLC +:loop LDA fcb+fcbMark,Y ;Count to mark, and returns sum + STA tPosll,X ; in scrtch and also returns mark in tPos + STA oldMark,X ; and oldMark + ADC cBytes,X + STA scrtch,X ;On exit: Y, X, A=unknown + TXA ;Carry set indicates scrtch>eof + EOR #$02 ;(cBytes+2 always = 0) + BEQ EOFtest + INY + INX + BNE :loop ;Branch always + +EOFtest LDA scrtch,X ;New mark in scrtch! + CMP fcb+fcbEOF,Y ;Is new position > eof? + BCC :Ret ;No, proceed + BNE :Ret ;Yes, adjust 'cBytes' request + DEY + DEX ;Have we compared all three bytes? + BPL EOFtest +:Ret RTS + +*------------------------------------------------- +* Set new mark & eof + +WrErrEOF JSR Plus2FCB ;Reset EOF to pre-error position +:loop LDA oldEOF,X ;Place oldEOF back into fcb + STA fcb+fcbEOF,Y + LDA oldMark,X ;Also reset mark to last best write position + STA fcb+fcbMark,Y + STA scrtch,X ; & copy mark to scrtch for + DEY ; test of EOF less than mark + DEX + BPL :loop + JSR Plus2FCB ;Get pointers to test EOFEOF!! + +* Drop into WrAdjEOF to adjust EOF to mark if necessary. + +WrAdjEOF JSR Plus2FCB ;Get (Y)=fcbPtr+2, (X)=2,(A)=(Y) +:loop1 LDA fcb+fcbEOF,Y ;Copy EOF to oldEOF + STA oldEOF,X + BCC :1 ; & if carry set... + LDA scrtch,X ; copy scrtch to fcb's EOF + STA fcb+fcbEOF,Y +:1 DEY + DEX ;Copy all three bytes + BPL :loop1 + RTS + +*------------------------------------------------- +* Set 3-byte indices +* Exit +* (A)=(Y)=(fcbPtr)+2 +* (X)=2 + +Plus2FCB LDA #$02 + TAX + ORA fcbPtr + TAY + RTS + +************************************************** +* WRITE Call + +Write EQU * ;First determine if requested + JSR MovCBytes ; write is legal + PHA ;Save attributes temporarily + JSR CalcMark ;Save a copy of EOF to oldEOF, set/clr Carry + JSR WrAdjEOF ; to determine if new mark > EOF + PLA ;Get attributes again + AND #writeEnable + BNE Write1 ;It's write enabled + +ErrAccess LDA #invalidAccess ;Report illegal access + BNE WrtError ;Always + +Write1 JSR TestWrProt ;Otherwise, ensure device is not write protected + BCS WrtError ;Report write potected and abort operation + LDA cBytes + ORA cBytes+1 ;Anything to write? + BNE :1 ;branch if write request definitely non-zero + JMP RWDone ;Do nothing + +:1 JSR MovDBuf ;Move pointer to user's buffer to bfm + CMP #tree+1 ; zpage area, also get storage type + BCS ErrAccess ;If not tree, return an access error! + +TreeWrite JSR RdPosn ;Read block we're in + BCS WrtError + JSR GetFCBStat ;Get file's status + AND #dataAloc+idxAloc+topAloc;Need to allocate? + BEQ TreeWrt1 ;No + + LDY #$00 ;Find out if enough disk space is available +:loop INY ; for indexes and data block + LSR ;Count # of blks needed + BNE :loop + + STY reqL ;Store # of blks needed + STA reqH ;(A)=0 + JSR TestFreeBlk + BCS WrtError ;Pass back any errors + + JSR GetFCBStat ;Now get more specific + AND #topAloc ;Are we lacking a tree top? + BEQ TestSapWr ;No, test for lack of sapling level index + JSR MakeTree ;Go allocate tree top and adjust file type + BCC AllocDataBlk ;Continue with allocation of data block + +WrtError PHA ;Save error + JSR ErrFixZ + JSR WrErrEOF ;Adjust EOF and mark to pre-error state + PLA ;Restore error code + SEC ;Flag error + RTS + +TestSapWr JSR GetFCBStat ;Get status byte again + AND #idxAloc ;Do we need a sapling level index block? + BEQ AllocDataBlk ;No, assume it's just a data block needed + + JSR AddNewIdxBlk ;Go allocate an index block and update tree top + BCS WrtError ;Return any errors + +AllocDataBlk JSR AllocWrBlk ;Go allocate for data block + BCS WrtError + + JSR GetFCBStat ;Clear allocation required bits in status + ORA #idxMod ; but first tell 'em index block is dirty + AND #$FF-dataAloc-idxAloc-topAloc;Flag these have been allocated + STA fcb+fcbStatus,Y + LDA tPosHi ;Calculate position within index block + LSR + LDA tPoslh + ROR + TAY ;Now put block address into index block + INC tIndex+1 ;High byte first + LDA scrtch+1 + TAX + STA (tIndex),Y + DEC tIndex+1 ;(Restore pointer to lower page of index block) + LDA scrtch ;Get low block address + STA (tIndex),Y ;Now store low address + + LDY fcbPtr ;Also update file control block to indicate + STA fcb+fcbDataBlk,Y; that this block is allocated + TXA ;Get high address again + STA fcb+fcbDataBlk+1,Y + +TreeWrt1 JSR PrepRW ;Write on + JSR WrtPart + BVC TreeWrite + JMP RWDone ;Update FCB with new position + +*------------------------------------------------- +* Copy write data to I/O blk +* Logic is similar to ReadPart rtn +* Exit +* V = 1 - done +* V = 0 - More to write + +WrtPart TXA + BNE WrtPart1 ;Branch if request is not an even page + LDA rwReqH ;A call of zero bytes should never get here! + BEQ SetWrDone ;Do nothing! + + DEC rwReqH +WrtPart1 DEX + LDA (userBuf),Y ;Move data from user's buffer + STA (posPtr),Y ; one byte at a time + TXA + BEQ EndWReqChk +WrtPart2 INY ;Page crossed? + BNE WrtPart1 ;No, move next byte + + LDA posPtr+1 ;Test for end of buffer + INC userBuf+1 ; but first adjust user buffer + INC tPoslh ; pointer and position + BNE :1 + INC tPosHi + +* Don't wrap around on file! + + BNE :1 + LDA #outOfRange ; Say out of range if >32 meg + BNE WrtError ;Always +:1 INC posPtr+1 ; and sos buffer high address + EOR dataPtr+1 ;(carry has been cleverly undisturbed.) + BEQ WrtPart1 ;Crunch if more to write to buffer + + CLV ;Indicate not finished + BVC WrPartDone ;Branch always + +EndWReqChk LDA rwReqH + BEQ WrtReqDone ;Branch if request satisfied + INY ;Are we done with this block of data? + BNE :11 ;Branch if not + LDA posPtr+1 + EOR dataPtr+1 ;While this is redundant, it's necessary for + BNE :12 ; proper adjustment of request count +:11 DEC rwReqH ;(not finished- ok to adjust hi byte.) +:12 DEY ;Reset modified Y-reg + BRA WrtPart2 + +WrtReqDone INY ; and position + BNE SetWrDone + INC userBuf+1 ;bump pointers + INC tPoslh + BNE SetWrDone + INC tPosHi +SetWrDone BIT SetVFlag ;(set V flag) + +WrPartDone STY tPosll ;Save low position + STX rwReqL ; and remainder of request count + PHP ;Save statuses + JSR GetFCBStat + ORA #dataMod+useMod + STA fcb+fcbStatus,Y + CLC ;Adjust user's low buffer address + LDA tPosll + ADC userBuf + STA userBuf + BCC :21 + INC userBuf+1 ;Adjust hi address as needed +:21 JSR FCBUsed ; Set directory flush bit + PLP ;Restore return statuses + RTS + +*------------------------------------------------- +* Make a tree file by adding a new master index blk + +MakeTree JSR SwapDown ;First make curr 1st blk an entry in new top + BCS ErrMakeTree ;Return any errors + + JSR GfcbStorTyp ;Find out if storage type has been changed to 'tree' + ;(if not, assume it was originally a seed and + CMP #tree ; both levels need to be built + BEQ MakeTree1 ; Otherwise, only an index need be allocated) + JSR SwapDown ;Make previous swap a sap level index block + BCS ErrMakeTree + +MakeTree1 JSR AllocWrBlk ;Get another block address for the sap level index + BCS ErrMakeTree + LDA tPosHi ;Calculate position of new index block + LSR ; in the top of the tree + TAY + LDA scrtch ;Get address of newly allocated index block again + TAX + STA (tIndex),Y + INC tIndex+1 + LDA scrtch+1 + STA (tIndex),Y ;Save hi address + DEC tIndex+1 + + LDY fcbPtr ;Make newly allocated block the current index block + STA fcb+fcbIdxBlk+1,Y + TXA + STA fcb+fcbIdxBlk,Y + JSR WrFCBFirst ;Save new top of tree + BCS ErrMakeTree + JMP ZeroIndex ;Zero index block in user's i/o buffer + +*------------------------------------------------- +* Add new index blk + +AddNewIdxBlk JSR GfcbStorTyp ;Find out if we're dealing with a tree + CMP #seedling ;If seed then an adjustment to file type is necessary + BEQ SwapDown ;Branch if seed + JSR RdFCBFst ;Otherwise read in top of tree. + BCC MakeTree1 ;Branch if no error +ErrMakeTree RTS ;Return errors + +* Add a higher index level to file + +SwapDown EQU * ;Make current seed into a sapling + JSR AllocWrBlk ;Allocate a block before swap + BCS SwapErr ;Return errors immediately + LDY fcbPtr ;Get previous first block + LDA fcb+fcbFirst,Y ; address into index block + PHA ;Save temporarly while swapping in new top index + LDA scrtch ;Get new block address (low) + TAX + STA fcb+fcbFirst,Y + LDA fcb+fcbFirst+1,Y + PHA + LDA scrtch+1 ; and high address too + STA fcb+fcbFirst+1,Y + STA fcb+fcbIdxBlk+1,Y;Make new top also the current index in memory + TXA ;Get low address again + STA fcb+fcbIdxBlk,Y + INC tIndex+1 ;Make previous the first entry in sub index + PLA + STA (tIndex) + DEC tIndex+1 + PLA + STA (tIndex) + JSR WrFCBFirst ;Save new file top + BCS SwapErr + JSR GfcbStorTyp ;Now adjust storage type by adding 1 + ADC #$01 ; (thus seed becomes sapling becomes tree) + STA fcb+fcbStorTyp,Y + LDA fcb+fcbStatus,Y ;Mark storage type modified + ORA #storTypMod + STA fcb+fcbStatus,Y + CLC ;Return 'no error' status +SwapErr RTS + +*------------------------------------------------- + +AllocWrBlk JSR Alloc1Blk ;Allocate 1 block + BCS AlocErr + JSR GetFCBStat ;Mark usage as modified + ORA #useMod + STA fcb+fcbStatus,Y + LDA fcb+fcbBlksUsed,Y;Bump current usage count by 1 + CLC + ADC #$01 + STA fcb+fcbBlksUsed,Y + LDA fcb+fcbBlksUsed+1,Y + ADC #$00 + STA fcb+fcbBlksUsed+1,Y +WrOK CLC ;Indicate no error +AlocErr RTS ;All done + +*------------------------------------------------- +* Do Status if not I/O yet + +TestWrProt JSR GetFCBStat ;Check for a 'never been modified' condition + AND #useMod+dataMod+idxMod+eofMod + BNE WrOK ;Ordinary RTS if known write ok + + LDA fcb+fcbDevNum,Y ;Get file's device number + STA DevNum ;Get current status of block device + +* Status call + +TestWrProtZ STA unitNum ;Make the device status call + LDA blockNum+1 + PHA + LDA blockNum ;Save the current block values + PHA + STZ dhpCmd ;=statCmd + STZ blockNum ;Zero the block # + STZ blockNum+1 + PHP + SEI + JSR DMgr ;Branch if write protect error + BCS :1 + LDA #$00 ; Otherwise, assume no errors +:1 PLP ;Restore interrupt status + CLC + TAX ;Save error + BEQ :2 ;Branch if no error + SEC ; else, set carry to show error +:2 PLA + STA blockNum ;Restore the block # + PLA + STA blockNum+1 + TXA + RTS ;Carry is indeterminate \ No newline at end of file diff --git a/MLI.SRC/RELOC.S b/MLI.SRC/RELOC.S new file mode 100644 index 0000000..8e5b148 --- /dev/null +++ b/MLI.SRC/RELOC.S @@ -0,0 +1,409 @@ +******************************************************* +* Program/data relocation routine is driven by a table +* describing program, vectors, and data segments. The +* table has the general format: +* (1)command: 0= zero destinaton range. +* 1= move data to from src to dst. +* 2= high address ref tbl, relocate & move. +* 3= lo-hi addr ref tbl, relocate & move. +* 4= program, relocate & move. +* >4= end of table. +* (2)dest addr: address of where segment is to be moved. +* (2)byte count: length of segment. +* (2)source addr: start address segment to be operated on, +* n/a if type=0, code does not have to be +* assembled at this address. +* (1)segments: number of address ranges to be tested +* and altered, n/a if type=0 or 1. +* limit and offset lists should each +* contain segments+1 (s) bytes. +* (s)limitlow: list of low page addresses to be tested. +* (s)limithigh: list of high page addresses to be tested. +* (s)offset: list of amounts to be added if +* low & high limits have been met. +* +* on entry: (X)=table address low, (Y)=table address high +* on exit: carry clear if no error; else carry set, +* (X)=addrLo, (Y)=addrHi of error causing source. +* (A)=0 if input table error, =$ff if illegal opcode. +******************************************************* + +Reloc STX relocTbl + STY relocTbl+1 ;Save address of control table +RelLoop LDA (relocTbl) ;Get relocation command + CMP #$05 ;If 5 or greater then done... + BCS RelocEnd ;Branch if done + TAX + LDY #$01 ;Move destination address to + LDA (relocTbl),Y ; zero page for indirect access + STA dst + INY + LDA (relocTbl),Y + STA dst+1 + INY + LDA (relocTbl),Y ;Also the length (byte count) + STA cnt ; of the destination area + INY + LDA (relocTbl),Y + STA cnt+1 + BMI RelocErr ;Branch if >=32K + TXA ;Request to zero out destination? + BEQ Zero ;Branch if it is + INY + LDA (relocTbl),Y ;Now get the source address + STA src + STA code ;src is used for the move, 'code' is + INY ; used for relocation + CLC ;Add length to get final address + ADC cnt + STA endCode + LDA (relocTbl),Y + STA src+1 + STA code+1 + ADC cnt+1 + STA endCode+1 + DEX ;Now that set-up is done, test for 'move' + BEQ MovEm ;Branch if move only (no relocation) + + STX wSize ;Save element size (1,2,3) + INY + LDA (relocTbl),Y ;Now get the number of ranges + STA segCnt ; that are valid relocation target addresses + TAX ;Separate serial range groups into tables +RLimLo INY ;Transfer low limits to 'limlo' table + LDA (relocTbl),Y + STA LimLo,X + DEX + BPL RLimLo + + LDX segCnt +RLimHi INY + LDA (relocTbl),Y ;Transfer high limits to 'limhi' table + STA LimHi,X + DEX + BPL RLimHi + + LDX segCnt +Rofset INY + LDA (relocTbl),Y ;Transfer offsets to 'ofset' table + STA Ofset,X + DEX + BPL Rofset + + JSR AdjTbl ;Adjust 'relocTbl' to point at next spec + LDX wSize ;Test for machine code relocation + CPX #$03 + BEQ RelCode ;Branch if program relocation + +* 2/3 - Relocate addresses + + JSR RelAdr ;Otherwise, relocate addresses in +RelocEnd1 JSR Move ; one or two byte tables, then move to destination + BRA RelLoop ;Do next table entry... +RelocEnd CLC + RTS +RelocErr JMP TblErr + +*------------------------------------------------- +* 4 - Relocate instructions + +RelCode JSR RelProg ;Go relocate machine code references + BRA RelocEnd1 + +* 0 - Zero block + +Zero JSR AdjTbl ;Adjust 'relocTbl' pointer to next entry + LDA #$00 ;Fill destination range with zeros + LDY cnt+1 ;Is it at least a page? + BEQ ZPart ;Branch if less than 256 bytes + TAY +:ZeroLoop STA (dst),Y + INY + BNE :ZeroLoop + INC dst+1 ;bump to next page + DEC cnt+1 + BNE :ZeroLoop +ZPart LDY cnt ;any bytes left to zero? + BEQ Zeroed ;branch if not + TAY +:loop STA (dst),Y + INY + CPY cnt + BCC :loop +Zeroed JMP RelLoop + +* 1 - Copy block + +MovEm JSR AdjTbl + BRA RelocEnd1 + +*------------------------------------------------- +* Advance table ptr + +AdjTbl TYA ;Add previous table length to 'relocTbl' + SEC ; to get position of next entry in table + ADC relocTbl + STA relocTbl + BCC :Rtn + INC relocTbl+1 +:Rtn RTS + +*------------------------------------------------- + +Move LDA src+1 ;Determine if move is up, down + CMP dst+1 ; or not at all + BCC MovUp ;Branch if definitely up... + BNE MovDown ;Branch if definitely down... + LDA src + CMP dst + BCC MovUp ;Branch if definitely up... + BNE MovDown ;Branch if definitely down... + RTS ;Otherwise, don't move nuting + +*------------------------------------------------- +* src addr < dest addr + +MovUp LDY cnt+1 ;Calc highest page of move up + TYA + CLC + ADC src+1 + STA src+1 ; & adjust src & dst accordingly + TYA + CLC + ADC dst+1 + STA dst+1 + LDY cnt ;Move partial page first + BEQ :1 ;Branch if no partial pages +:MovLoop DEY + LDA (src),Y + STA (dst),Y + TYA ;End of page transfer? + BNE :MovLoop ;No +:1 DEC dst+1 + DEC src+1 + DEC cnt+1 ;Done with all pages? + BPL :MovLoop ;Branch if not + RTS + +*------------------------------------------------- +* src addr > dest addr + +MovDown LDY #$00 + LDA cnt+1 ;Partial page move only? + BEQ :1 ;Branch if less than a page to be moved +:MovLoop1 LDA (src),Y + STA (dst),Y + INY + BNE :MovLoop1 + INC dst+1 ;Bump addresses + INC src+1 + DEC cnt+1 ;More pages? + BNE :MovLoop1 ;Branch if more pages +:1 LDA cnt ;Move partial page + BEQ :Rtn ;Branch if no more to move +:MovLoop2 LDA (src),Y + STA (dst),Y + INY + CPY cnt + BNE :MovLoop2 +:Rtn RTS ;All done... + +*------------------------------------------------- +* Address/page relocate + +RelAdr LDY wSize ;Determine 1 or 2 byte reference + DEY + LDA (code),Y + JSR AdjAdr ;Relocate reference + LDA wSize ;Update and test 'code' pointer + JSR AdjCode + BCC RelAdr ;Branch if more to do + RTS + +*------------------------------------------------- +* Instructions relocate + +RelProg LDY #$00 ;Fetch next opcode + LDA (code),Y + JSR GetOpLen ;Determine if it's a 3-byte instruction + BEQ RPerr ;Branch if not an opcode + CMP #$03 + BNE :1 + LDY #$02 + JSR AdjAdr ;Relocate address + LDA #$03 +:1 JSR AdjCode ;Update and test 'code' for done + BCC RelProg ;Loop if more to do + RTS + +*------------------------------------------------- +* Error handling... + +RPerr PLA ;Return bad code address + PLA ;First un-do stack + LDX code + LDY code+1 + LDA #$FF ;Indicate bad opcode + SEC ;Indicate error + RTS + +*------------------------------------------------- +* Error return + +TblErr LDX relocTbl ;Return table address error + LDY relocTbl+1 + LDA #$00 ;Indicate input table error + SEC + RTS + +*------------------------------------------------- +* Relocate absolute addr + +AdjAdr LDA (code),Y ;Get page address + LDX segCnt ; and test against limits +:AdjLoop CMP LimLo,X ;Is it >= low? + BCC :Next ;Branch if not + CMP LimHi,X ;Is it =< highest page limit + BCC :1 ;Branch if it is + BEQ :1 +:Next DEX ;Try next limit set + BPL :AdjLoop + RTS ;Teturn without adjustment + +:1 CLC ;Add offset to form relocated + ADC Ofset,X ; page address + STA (code),Y ; & replace old address with result + RTS + +*------------------------------------------------- +* Bump ptr to next addr + +AdjCode CLC ;Update 'code' pointer + ADC code + LDY code+1 + BCC :1 ;Branch if not page cross + INY ;Update high order address too +:1 CPY endCode+1 ;Has all code/data been processed? + BCC :2 ;Branch if definitely not + CMP endCode ;If carry results set, end of code +:2 STA code + STY code+1 ;Save updated values + RTS ;Return result (carry set=done) + +*------------------------------------------------- +* Compute instruction len + +GetOpLen PHA ;Form index to table and which 2-bit group + AND #$03 ;Low 2 bits specify group + TAY + PLA + LSR ;Upper 6 bits specify byte in table + LSR + TAX + LDA OpCodeLen,X +NxtGroup DEY ;Is opcode length in lowest 2 bits of A-reg? + BMI RtnLen ;Branch if it is + LSR + LSR ;Shift to next group + BNE NxtGroup ;If len=0 then error... +RtnLen AND #$03 ;Strip other garbage + RTS ;If z-flag true, then error!!! + +*------------------------------------------------- +* The following table contains the length of each +* machine instruction (in two-bit groups). + +OpCodeLen HEX 0928193C + HEX 0A280D3C + HEX 0B2A193F + HEX 0A280D3C + HEX 0928193F + HEX 0A280D3C + HEX 0928193F + HEX 0A280D3C + HEX 082A113F + HEX 0A2A1D0C + HEX 2A2A193F + HEX 0A2A1D3F + HEX 0A2A193F + HEX 0A280D3C + HEX 0A2A193F + HEX 0A280D3C + +*------------------------------------------------- +* Relocation Data + +wSize DB $00 +segCnt DB $00 +LimLo HEX 0000000000000000;Start of range pages +LimHi HEX 0000000000000000;End of pages+1 +Ofset HEX 0000000000000000;Additive factors + +*------------------------------------------------- +* Install an exit code +* The locations GSOS($E100A8) & GSOS2 ($E100B0) +* are patched if the boot OS is P8 + +GSPatches PHP + SEI + CLC + XCE + REP #$30 + PHB + PHA + PHA ;long result + PushLong #ZZSize ;size 16 bytes + PushWord #$3101 ;userID + PushWord #attrLocked+attrNoCross+attrNoSpec + PHA + PHA + _NewHandle + LDA $01,S ;Let handle remain on stack + TAX ; since we need it later but + LDA $03,S ; move a copy to (Y,X) + TAY + PushLong #ExitPatch ; srcPtr + PHY + PHX ;destHndl + PushLong #ZZSize ;# of bytes to be copied + _PtrToHand + PLX ;Put 24 bits of the 32-bit + PLB ; handle that was left on stack + LDA |$0001,X ; deref + TAY ;mid & hi-byte of 24-bit ptr + LDA |$0000,X ; low 16-bit of 24-bit ptr + AND #$00FF ; Mask off mid byte + XBA ;Lobyte of ptr to Hi-byte in ACC + ORA #$005C ;Add in JMPL inst + STAL GSOS2 ;$5C xx + CLC + ADC #$000B ; ADC[] + STAL GSOS + TYA ; mid & hi byte of 24-bit ptr + STAL GSOS2+2 ;yy zz + ADC #$0000 + STAL GSOS+2 + PLB ;Discard the rest of the handle + PLB + SEC + XCE + PLP + RTS + +*------------------------------------------------- +* Remove 3 words & adjust stack +* to return to caller + + MX %00 +ExitPatch LDA $01,S ;RTL-1 addr + STA $07,S + LDA $02,S + STA $07+1,S + PLA + PLA + PLA + LDA #$00FF + SEC + RTL +ZZSize EQU *-ExitPatch + DS $23,0 \ No newline at end of file diff --git a/MLI.SRC/ROM.S b/MLI.SRC/ROM.S new file mode 100644 index 0000000..38f8861 --- /dev/null +++ b/MLI.SRC/ROM.S @@ -0,0 +1,70 @@ + TTL 'lang. cd. irq, nmi, & reset' + +*********************************************************** +* This code is used when an IRQ happens while the RAM +* at $D000-$FFFF is switched on (inside an MLI +* call, for example) if we have the "new style" +* monitor ROMs + + ORG $FF9B + +LanIrq PHA + LDA Acc ;Save ($45) + STA old45 ;Now put A-reg into loc $45 + PLA + STA Acc + PLA ;Get status register from stack + PHA ; (and restore it!) + AND #$10 ;Is it a break or interupt + BNE lBreak ;Branch if break + LDA $D000 ;Get bankID + EOR #$D8 ;Is the system active? (CLD) + BEQ SysActv ;Branch if it is + LDA #$FF ;In $D000 bank 2 +SysActv STA IntBankID ;Update bank ID (=$00/$FF) + STA afBank + LDA #>aftIrq ;Push fake "RTI" vector + PHA ; with IRQ disabled + LDA #ROMIrq ;Push ROM entry also + PHA + LDA # $88 and the char may be acceptable +NotGud JSR BELL ;Ring the bell (speaker) + JMP Loop1 + +GetIn1 CMP #$8D ;Is it a CR? + BEQ GetInpDone + CMP #$DB ;> than "Z" + BCC *+4 ;No + AND #$DF ;Make sure its Upper case + CMP #$AE ;Is it "."? + BCC NotGud ;Branch if less + CMP #$DB ;Must be less than "[" + BCS NotGud + CMP #$BA ;OK if less than or equal to "9" + BCC ItsGud + CMP #$C1 ;Else must be > than "A" + BCC NotGud +ItsGud PHA + JSR CLREOL + PLA + JSR COUT ;No, print it + INX + CPX #39 + BCS ExtndBr + STA pnBuf,X + JMP Loop1 ;Go get the next one + +GetInpDone LDA #" " + JSR COUT ;After the CR, blank out the cursor + STX pnBuf ;Put the length in front of the name + +* At this point the specified Pathname is in pnBuf ($280) +* and we can do a GET_FILE_INFO on it + + JSR GoPro + DB $C4 + DA Info + BCC InfoOK + JMP Error + +InfoOK LDA Type + CMP #$FF ;Is it a type SYS file? + BEQ DoIt + LDA #$01 ;Not SYS File + JMP Error + +DoIt LDA #$00 ;It's a type SYS all right! + STA ClsNum + JSR GoPro + DB $CC + DA Cls ;CLOSE all open files first + BCC ChkAcs + JMP Error + +* Now check for the proper access + +ChkAcs LDA Acess ;Get the allowed access + AND #readEnable ;Is READ disabled? + BNE :1 ;No. Access ok + LDA #drvrIOError ;I/O error + JMP Error ;Never returns! + +:1 JSR GoPro + DB $C8 + DA Opn ;OPEN it + BCC *+5 + JMP Error + + LDA RefNum + STA ReedNum ;Spread REFNUM around + STA eofNum + +* Ok it's OPEN, let's get the EOF + + JSR GoPro + DB $D1 + DA EOF + BCS Error + LDA eofB+2 ;3rd of 3 bytes + BEQ EOFOK + LDA #drvrIOError ;I/O ERROR even though the + BNE Error ; file is simply too large +EOFOK LDA eofB ;Move EOF to Read # bytes + STA RCount + LDA eofB+1 + STA RCount+1 + JSR GoPro + DB $CA ;Do the READ + DA Reed + PHP ;Push the processor status + JSR GoPro + DB $CC ;Close it + DA Cls + BCC *+6 + PLP ;Get status back (it is irrevalent now) + BNE Error ;(if CLOSE generated an error) + PLP ;We're here if CLOSE was OK + BCS *-4 ;JMP ERROR + JMP $2000 + +EatEm LDA CH ;Is the cursor in col 0? + BEQ EatEmBak ;Yes, ignore it + DEX + LDA #" " + JSR COUT ;Blank out the cursor + DEC CH ;Point to last character + DEC CH ; entered... + JSR COUT ; and blank it too + DEC CH ;Point to that location +EatEmBak JMP Loop1 ;Go back & get the next char + +****************** See Rev Note #55 ***************** + +PrntLoop LDA Msg0,X ;Display string; offset is in X. + BEQ :Ret ;Branch if done. + JSR COUT ;Output character... + INX + BNE PrntLoop ;Branch always. +:Ret RTS + +Error STA ErrNum + LDA #$0C ;Put error message on line 13 + STA CV + JSR CROUT + LDA ErrNum + CMP #badSystemCall + BNE NextErr +*************** See Rev Note #55 ************** + LDX #BetterBye + STA SOFTEV+1 + JSR SETPWRC ;Set powerup byte + LDA #$A0 ;Switch on $C3 video + JSR $C300 + + LDX #23 +:ClrLoop STZ memTabl,X + DEX + BPL :ClrLoop + + INC memTabl+23 ;Protect $BF page + LDA #%11001111 ;Flag $00,$01 & $04-$07 mem + STA memTabl ; pages as being used + LDA #$02 + STA SMrkPBlk ;Set pCount + LDX DevCnt ;Get # of devices-1 + STX numActvDev + LDA DevNum ;Is there a last accessed dev? + BNE IsDevOL ;Yes, start with that one + +*------------------------------------------------- + +NxtActvDev LDX numActvDev ;Start search fr this + LDA DevLst,X ;=DSSS IIII + CPX #$01 ;Last on the list? + BCS :1 ;No + LDX DevCnt ;Start all over again + INX +:1 DEX + STX numActvDev + +IsDevOL STA OLUnit ;Chk if vol is online + JSR GoPro + DB $C5 + DA OLinPBlk + BCS NxtActvDev ;No, try next device + + STZ depth + LDA pnBuf+1 ;=DSSS LLLL + AND #$0F ;Isolate name len of vol + BEQ NxtActvDev ;Not online + ADC #$02 ;For the 2 added slashes + TAX + +OpenFolder STX pnBuf ;len of PN + LDA #'/' ;Add a slash to the + STA pnBuf+1 + STA pnBuf,X ; beginning & end of PN + STZ pnBuf+1,X + JSR GoPro ;Open the vol/sub dir + DB $C8 + DA OpenPBlk + BCC FolderOpened + + LDA depth + BEQ NxtActvDev + JSR BELL1 + JSR ChopName + STX pnBuf + JMP GetKeyPress ;Pause + +*------------------------------------------------- + +FolderOpened INC depth + STZ listTotal ;# of SYS/DIR entries + LDA OpenRef + STA ReadRef + STA SMrkPBlk+c_refNum;Set ref # + LDA #$27+4 ;Read in the link blk ptrs + STA RdReqLen ; & vol/subdir header rec + STZ RdReqLen+1 + JSR ReadEntry + BCS ScanDirDone + + LDX #$03 ;Copy the file count, +:CpyLoop LDA entryBuf+hEntLen+4,X; entries/blk + STA Entry_Len,X ; & entry len + DEX + BPL :CpyLoop + + STA RdReqLen ;=entry_len ($27) + LDA #$01 ;Start w/first file entry + STA entryNum ; skipping the header entry + STZ SMrkPBlk+c_mark+1 + STZ SMrkPBlk+c_mark+2 + LDA FileCount ;Is vol/sub dir empty? + ORA FileCount+1 + BNE NxtFilEnt ;No +ScanDirDone BRA ClsDirFile ;Done with reading dir + +NxtFilEnt BIT FileCount+1 ;Any more file entries? + BMI ScanDirDone ;Done + +SkipEnt LDA SMrkPBlk+c_mark+1 + AND #%11111110 ;Do a MOD 512 to force + STA SMrkPBlk+c_mark+1; a block alignment + LDY entryNum + LDA #$00 + CPY EntPerBlk + BCC CalcOffset + + TAY ;Y=0 + STY entryNum + INC SMrkPBlk+c_mark+1;On fall thru next block +NxtPage INC SMrkPBlk+c_mark+1;2nd page of block + +CalcOffset DEY ;Compute offset to file entry + CLC + BMI SetFilePos + ADC Entry_Len + BCC CalcOffset + BCS NxtPage ;Always + +SetFilePos ADC #$04 ;Skip 4-byte header + STA SMrkPBlk+c_mark + JSR GoPro + DB $CE + DA SMrkPBlk + BCS ScanDirDone + + JSR ReadEntry + BCS ScanDirDone + INC entryNum + LDA entryBuf+d_stor ;Get storType/namelen + AND #$F0 ;Isolate storage type + BEQ SkipEnt ;Deleted entry + DEC FileCount + BNE GoodRead + DEC FileCount+1 + +GoodRead ROR entryBuf+d_attr ;Check readEnable bit + BCC NxtFilEnt ;File cannot be read + LDA entryBuf+d_fileID;Get file type + CMP #$0F ;Is it a DIR file? + BEQ :1 ;Yes + CMP #$FF ;SYS file? + BNE NxtFilEnt ;No + +:1 LDX listTotal ;# of SYS/DIR entries + CPX #128 + BCS ClsDirFile + STA fTypeTbl,X ;Store filetype + JSR GetNameSlot + + LDY #15 +:CpyLoop LDA entryBuf+d_stor,Y;Copy filename including + STA (fnamePtr),Y ; the storType/namelen byte + DEY + BPL :CpyLoop + + INY ;Y=0 ;(A)=storType/namelen + AND #$0F ;Isolate len byte + STA (fnamePtr),Y ;Save it + INC listTotal ;# of SYS/DIR entries + BNE NxtFilEnt + +CantCls JMP NxtActvDev ;Hitch a ride + +ClsDirFile JSR GoPro + DB $CC + DA ClsPBlk + BCS CantCls + +* Display list of files in vol/subdir + + JSR SETTXT + JSR HOME ;Clear scrn & posn cursor @ top of scrn + LDA #23 + JSR TABV ;Posn cursor @ btm of scrn + LDY #helpStr-helpStr + LDA #20 ;Display Help Message + JSR ShowHelp ; starting @ scrn coords (23, 20) + + JSR HomeCursor + LDX #$00 +:loop LDA pnBuf+1,X ;Display full PN + BEQ :1 ; at top of screen + JSR PrtChar + INX + BNE :loop + +:1 STZ currEnt + STZ scrolledNum + LDA listTotal ;# of SYS/DIR entries + BEQ GetKeyPress + + CMP #21 + BCC :2 + LDA #20 ;Only 20 will be displayed +:2 STA dispCnt + LDA #2 ;Set the dimensions + STA WNDTOP ; of our display window + STA WNDLFT ; which is (2, 2) to (22, 24) + LDA #22 + STA WNDWDTH + STA WNDBTM + +:DspLoop JSR ShowEntry + INC currEnt + DEC dispCnt + BNE :DspLoop + + STZ currEnt ;Highlight 1st entry + BEQ InvDsp ;Always + +UpArwHit JSR ShowEntry + LDX currEnt ;Are we at the top of the list? + BEQ InvDsp ;Yes -> No entries to scroll down + DEC currEnt + LDA CV ;Are we at the top of our window? + CMP #$02 + BNE InvDsp ;No, proceed to highlight entry + DEC scrolledNum ;Less 1 "scrolled up" entry + LDA #$16 ;Scroll down 1 line + BNE Scroll ;always + +DwnArwHit JSR ShowEntry + LDX currEnt ;Is this the last SYS/DIR + INX + CPX listTotal ; entry in the dir? + BCS InvDsp ;Yes -> No entries to scroll up + STX currEnt + LDA CV ;Are we beyond the end of our window? + CMP #21 + BNE InvDsp ;No + INC scrolledNum ;We have scrolled up 1 entry + LDA #$17 ;Scroll up 1 line +Scroll JSR COUT +InvDsp JSR SetInv ;Set 80-col card to inverse mode + JSR ShowEntry + +GetKeyPress LDA KBD + BPL GetKeyPress + + STA KBDSTROBE ;Clear keyboard strobe + JSR SetNorm + LDX listTotal ;Is vol/subdir empty? + BEQ :1 ;Yes, no entries were displayed + + CMP #$8D ;CR? + BEQ AcceptEnt + CMP #$8A ;Down arrow? + BEQ DwnArwHit + CMP #$8B ;Up arrow? + BEQ UpArwHit +:1 CMP #$89 ;TAB? + BEQ NxtVol + CMP #$9B ;ESC? + BNE GetKeyPress + + JSR ChopName + DEC depth + BRA ToOpenDir2 ;Go open parent dir + +* Scans the full pathname, and chops +* off characters until it gets to a / + +ChopName LDX pnBuf ;Get len of PN +:loop DEX ;Bump to previous char + LDA pnBuf,X + CMP #'/' + BNE :loop + CPX #$01 ;Have we reached the root? + BNE :Rtn ;No + LDX pnBuf ;Stay at root level +:Rtn RTS + +NxtVol JMP NxtActvDev ;Hitch a ride + +ToOpenDir1 INX ;1 more for the ending slash +ToOpenDir2 JMP OpenFolder + +AcceptEnt JSR GoPro + DB $C6 + DA SPfxPBlk + BCS NxtVol + +* Extend the pathname + + LDX currEnt + JSR GetNameSlot + LDX pnBuf ;Append filename +:CpyLoop INY + LDA (fnamePtr),Y ; to the PN + INX + STA pnBuf,X + CPY fnameLen + BCC :CpyLoop + + STX pnBuf + LDY currEnt + LDA |fTypeTbl,Y ;Get filetype + BPL ToOpenDir1 ;Dir file + + JSR SETTXT + JSR HOME + LDA #$95 ;Deactivate 80-col, home cursor & clrsrcn + JSR COUT + JSR GoPro + DB $C8 + DA OpenPBlk + BCS NxtVol + + LDA OpenRef + STA ReadRef + LDA #$FF ;Prepare to read the + STA RdReqLen ; entire file whose + STA RdReqLen+1 ; len is unknown + JSR ReadEntry + PHP ;Save err status + JSR GoPro + DB $CC + DA ClsPBlk + PLP + BCS NxtVol ;Read errs + JMP $2000 ;Transfer control to Applic + +*------------------------------------------------- + +ShowHelp STA CH +ShowIcon EQU * +:loop LDA helpStr,Y + BEQ :Rtn + JSR COUT + INY + BNE :loop +:Rtn RTS + +*------------------------------------------------- +* Each file name 16 bytes/entry +* Allow up to 128 SYS/DIR names to be +* stored @ $1400-$1BFF +* Entry +* (X) = entry # +* Exit +* (Y) = 0 +* (fnamePtr) = Ptr to name of entry + +GetNameSlot STZ fnamePtr+1 + TXA + ASL ;x16 + ROL fnamePtr+1 + ASL + ROL fnamePtr+1 + ASL + ROL fnamePtr+1 + ASL + ROL fnamePtr+1 + STA fnamePtr + + LDA #>namesBuf + CLC + ADC fnamePtr+1 + STA fnamePtr+1 + LDY #$00 + LDA (fnamePtr),Y + STA fnameLen + RTS + +*------------------------------------------------- +* Display name of an entry + +ShowEntry LDA #2 + STA OURCH + LDX currEnt + TXA + SEC + SBC scrolledNum + INC + INC + JSR TABV + LDA fTypeTbl,X + BMI Its_Sys ;SYS file + STZ OURCH ;DIR file + LDA INVFLG + PHA + LDY #FolderIcon-helpStr + JSR ShowIcon + PLA + STA INVFLG + +Its_Sys JSR PrtBlnk ;Print a space instead + JSR GetNameSlot ; followed the file/subdir name +:loop INY + LDA (fnamePtr),Y + JSR PrtChar + CPY fnameLen + BCC :loop + +PrtBlnk LDA #" " + BNE PrtAsIs ;Always + +HomeCursor LDA #$99 ;ctrl-Y +PrtChar ORA #$80 +PrtAsIs JMP COUT + +ReadEntry JSR GoPro + DB $CA + DA RdPBlk + RTS + +*------------------------------------------------- +* Data area + +helpStr ASC "RETURN: Select | TAB: Chg Vol | ESC: " + ASC "Back" + DB $00 + +FolderIcon DB $0F ;Set Inverse Display mode + DB $1B ;Enable MouseText Mapping + DB $D8 ;MouseText chars + DB $D9 + DB $18 ;Disable MouseText Mapping + DB $0E ;Set Normal Display mode + DB $00 ;end of string + +OpenPBlk DB $03 + DA pnBuf + DA P8IOBuf +OpenRef DB $00 + +ClsPBlk DB $01 + DB $00 + +OLinPBlk DB $02 +OLUnit DB $60 + DA pnBuf+1 + +SPfxPBlk DB $01 + DA pnBuf + +RdPBlk DB $04 +ReadRef DB $01 + DA entryBuf + ;RdReqLen DW 0 +RdReqLen DB 0 ;Overflow \ No newline at end of file diff --git a/MLI.SRC/SEL2.S b/MLI.SRC/SEL2.S new file mode 100644 index 0000000..6475ca4 --- /dev/null +++ b/MLI.SRC/SEL2.S @@ -0,0 +1,471 @@ +************************************************** +* New GS/OS Dispatcher for P8 + +GQuit8 EQU $E0D000 + + ORG DispAdr + XC + XC +GS_Disp LDA LCBANK1 ;Enable read + CLC + XCE + JMPL GQuit8 + + DS 5,0 ;Pad with spaces + +gqSign ASC 'GQ' ;Signature word + +*------------------------------------------------- +* Control is passed back here by GQuit8 +* after it has setup the required parameters + + MX %10 +GS_DispZ SEP #$20 ;8-bit A, 16-bit index regs + PHA + LDX #inBuf ;Boot Volname + JSR GetVolName + PLA + SEC ;Emulation mode + XCE + ORA #$00 ;Nil prefix? + BEQ :1 + + MX %11 +:loop JSR GoPro + DB $C6 + DA SPfxParm + BCC :1 + + JSR ShowAlrt + BRA :loop + +:1 XCE + MX %10 + REP #$10 ;16-bit index regs + LDA pnBuf+1 ;Application's name is passed here + CMP #'/' ;Does it begin with a slash? + BNE :2 ;No, so a partial PN is passed + LDX #pnBuf ;Full PN is passed + JSR GetVolName ;Application's vol name + +:2 SEC ;Full emulation mode + XCE + +Try2Open JSR GoPro + DB $C8 + DA OpenParm + BCC Opened + JSR ShowAlrt + BRA Try2Open + +Opened LDA OpenRefNbr + STA eofRefNbr + STA ReadRefNbr + STA ClsRefNbr + +GetFileLen JSR GoPro + DB $D1 + DA eofParm + BCC GotFileLen + JSR ShowAlrt + BRA GetFileLen + +GotFileLen LDA theEOF + STA ReadLen + LDA theEOF+1 + STA ReadLen+1 + +Try2Read JSR GoPro + DB $CA + DA ReadParm + BCC Try2Cls + JSR ShowAlrt + BRA Try2Read + +Try2Cls JSR GoPro + DB $CC + DA ClsParm + BCC ClsOK + JSR ShowAlrt + BRA Try2Cls + +ClsOK JSR Chk4Intrp + BNE RunApp ;It's not an Interpreter eg BI + JSR GetStartup ;Get the startup pgm + BCC RunApp ;Transfer control to Interpreter + LDA #volNotFound + BRA ShwErrAlrt + +RunApp LDA RDROM2 ;Enable motherboard ROM + JMP $2000 ;Pass control to SYS application + +*------------------------------------------------- +* (A)=Error Code +* Report Err & Quit + +ShowAlrt CLC + XCE + REP #$30 ;Full 16-bit native mode + JSR Ask4Disk + BCS ShwErrAlrt + SEC ;Back to emulation mode + XCE + RTS + +*------------------------------------------------- +* Put up a text box showing an error code +* (A)=err code. It calls P8's quit code + +ShwErrAlrt CLC + XCE + REP #$30 ;Full 16-bit native mode + AND #$00FF + PHA ;Convert err code + PushLong #ErrNumStr + PushWord #4 ; into 4-byte ASCIIs char + _Int2Hex + + PHA + PushLong #CantRunStr ;line1Ptr + PushLong #P8ErrStr ;line2Ptr + PushLong #acceptStr ;button1Ptr + PushLong #nullStr ;button2Ptr + _TLTextMountVol + PLA ;Not used + SEC ;Emulate 65C02 + XCE + + JSR GoPro + DB $65 + DA QuitParms + +*------------------------------------------------- +* On entry +* (A)=Error Code + + MX %00 +Ask4Disk LDY #$0000 ;ptr to volname + LDX #VolNameStr + AND #$00FF ;Err # + CMP #volNotFound + BEQ :1 + CMP #drvrOffLine + BEQ :1 + SEC + RTS + +* Prompt for correct vol + +:1 PHA ;Err code + PHY + PHX + TSC + PHD + TCD + LDA [$01] ;Get len byte + DEC + XBA + STA [$01] + PHA ;word result + PushLong #insDskStr ;line1Ptr + PHY + INX + PHX ;(Y,X) line2Ptr + PushLong #acceptStr ;button1Ptr + PushLong #cancelStr ;button2Ptr + _TLTextMountVol + LDA [$01] + XBA + INC + STA [$01] + PLA ;button # chosen + PLD + PLX + PLX + CMP #$0001 ;Return? + BNE NotRet ;No, Esc + CLC + PLA ;err # + RTS + +NotRet SEC + PLA ;err # + RTS + +*------------------------------------------------- +* Called with 8-bit Acc but 16-bit index regs +* (X)=16-bit mem location to PN +* Copies just the volname to our buf +* On return, (A)=len of volname +* This rtn will hang if there is no +* trailing slash + + MX %10 +GetVolName LDA |$0001,X ;Get prefix char (if any) + STA VolNameStr+1 + LDY #$0002 +:loop LDA |$0002,X ;Get char + CMP #'/' ;Is it a trailing slash? + BEQ :Done ;Yes + STA VolNameStr,Y + INX + INY + BRA :loop + +:Done DEY ;backup 1 + TYA + STA VolNameStr ;Set len byte + RTS + +*------------------------------------------------- +* Application File was already loaded in mem @ $2000 +* If it is an interpreter, the Tool Locator is check +* for the name of the program which will be launched +* by the interpreter. For example, click on a file +* with type BAS with cause the BI to be loaded & +* executed. BI will launch the BASIC program which +* is passed via MessageCenter call of Tool Locator +* +* Z=0 Application is not an interpreter +* Z=1 Success +* Ref: pg 88 ProDOS 8 Technical Reference Manual + +Chk4Intrp LDA $2000 ;Check if it's an interpreter + CMP #$4C ;JMP inst + BNE :Rtn + LDA #$EE ;INC inst + CMP $2003 + BNE :Rtn + CMP $2003+1 + BEQ :1 +:Rtn RTS + +:1 LDA #$FF ;Init ErrFlag & push + PHA ; onto stack for later + CLC + XCE + REP #$30 ;Full native mode + PHA + _MMStartUp + PLA ;user ID + +* Ref IIGS Toolbox Vol 2 pg 24-14 +* IIGS Toolbox Vol 3 pg 52-4 +* Any size for the message handle will do +* since MessageCenter will resize it + + PHA ;Push back for later + PHA ;long result + PHA + PushLong #10 ;# of bytes to allocate + PHA ;userID + PushWord #$0000 ;attr + PHA + PHA + _NewHandle + PLA ;msg hndl + PLX + BCS ShutDnMM + + PHX + PHA + PushWord #2 ;action=Get + PushWord #1 ;msgID=file + PHX ;msg Hndl + PHA + _MessageCenter + BCS DumpMsgHndl + + PHA ;work space + PHA ; for ptr + TSC + PHD + INC + TCD +* +* DP Space: +* |--------------| +* | ErrFlag | B +* |--------------| +* | userID | 9-A +* |--------------| +* | msgHndl | 4-7 +* |--------------| +* | 2 PHA's | 0-3 +* |--------------| +* + + LDA [$04] ;Deref the mem handle + STA $00 + LDY #$0002 + LDA [$04],Y + STA $00+2 + + ; Ref Vol 2 pg 24-15 + + LDY #$0006 + LDA [$00],Y ;Get printFlag + BNE DelMsg + LDA $00 ;Open + CLC + ADC #$0008 + STA $00 ;Point @ name (pString) + BCC :2 + INC $00+2 + +:2 LDA [$00] + AND #$00FF ;Isolate len byte + SEP #$20 ;NB. 8-bit Acc + CMP $2003+2 ;Are the lens same? + BEQ :3 ;Yes + BCS DelMsg ;No + +:3 TAY +:CpyLoop LDA [$00],Y ;Copy PN of + STA $2000+6,Y ; pgm for Interpreter + STA inBuf,Y ; to run + DEY + BPL :CpyLoop + +* +* Stack contents: +* | | +* |--------------| +* | ErrFlag | D +* |--------------| +* | userID | B-C +* |--------------| +* | msgHndl | 7-A +* |--------------| +* | 2 PHA's | 3-6 +* |--------------| +* | DP reg | 1-2 +* |--------------| +* | |<- SP +* + LDA #$00 ;Overwrite ErrFlag + STA $0D,S ; which was $FF + +DelMsg REP #$20 ;16-bit Acc + PLD + PLA + PLA + PushWord #3 ;action=delete + PushWord #1 ;type=file + PHA + PHA + _MessageCenter + +DumpMsgHndl _DisposeHandle ;msgHndl +ShutDnMM _MMShutDown ;userID still on stack + + MX %11 + SEC ;Full emulation mode + XCE + PLA ;Get ErrFlag + BNE :Ret + + LDX inBuf ;Get len byte + LDA #'/' +:CpyLoop CMP inBuf,X ;Look for a trailing slash + BEQ :1 ;Got one + DEX + BNE :CpyLoop + BRA :Ret + +:1 DEX ;Backup 1 + STX inBuf ;len byte of vol name + JSR GoPro + DB $C6 + DA SPfxParm + LDA #$00 ;Flag no errs +:Ret RTS + +*------------------------------------------------- +* Get the name of startup program that will be +* launched by an interpreter. Verify it's there. +* BI will look for a BASIC program called STARTUP +* C=0 - Success + +GetStartup CLC ;Native mode + XCE + REP #$10 + LDX #$2000+6 ;Get full/partial PN + JSR GetVolName ; of startup program + +:loop SEC ;Full emulation mode + XCE + JSR GoPro + DB $C4 + DA GFIParm + BCC :Rtn + + CLC + XCE + REP #$30 ;Full 16-bit native mode + JSR Ask4Disk ;Ask for disk w/startup pgm + BCC :loop + + SEC ;Back to emulation mode + XCE + SEC ;Flag failure +:Rtn RTS + +*------------------------------------------------- +* ProDOS8 Parm tables + +SPfxParm DB $01 + DA inBuf + +OpenParm DB $03 + DA pnBuf ;Application's PN + DA IOBuf ;1024-byte I/O buf +OpenRefNbr DB $00 + +eofParm DB $02 +eofRefNbr DB $00 +theEOF DB 0,0,0 + +ReadParm DB $04 +ReadRefNbr DB $00 + DA $2000 ;Read into this location +ReadLen DW $0000 + DW $0000 + +ClsParm DB $01 +ClsRefNbr DB $00 + +QuitParms DB $04 + DB $00 ;=$EE for enhanced Quit + DA $0000 ;Addr of pathname + DB $00 + DW $0000 + +GFIParm DB $0A + DA VolNameStr + DB $00 + DB $00 + DW $0000 + DB $00 + DW $0000 + DW $0000 + DW $0000 + DW $0000 + DW $0000 + +* Messages/Button strings + +CantRunStr STR 'Can'27't run next application.' +P8ErrStr DB 20 ;len byte + ASC 'ProDOS Error = $' +ErrNumStr ASC ' ' +nullStr DB $00 +insDskStr STR 'Please insert the disk:' +acceptStr DB 13 + ASC 'Accept: ' + HEX 1B ;Enable mousetext chars + HEX 0F ;Inverse + ASC 'M' ;Return Icon + HEX 0E ;Normal + HEX 18 ; ;Disable mousetext chars +cancelStr STR 'Cancel: Esc' \ No newline at end of file diff --git a/MLI.SRC/TCLOCK.S b/MLI.SRC/TCLOCK.S new file mode 100644 index 0000000..40f66eb --- /dev/null +++ b/MLI.SRC/TCLOCK.S @@ -0,0 +1,92 @@ +*********************************************************** +* * +* PRODOS 8 CLOCK DRIVER INTERFACE ROUTINE * +* * +* COPYRIGHT APPLE COMPUTER, INC., 1983-86 * +* * +* ALL RIGHTS RESERVED * +* * +*********************************************************** + DUM $3A +TENS EQU * ;NO CONFLICT SINCE MONTH IS LAST PROCESSED. +MONTH DS 1 +WKDAY DS 1 +DAY DS 1 +HOUR DS 1 +MINUTE DS 1 + DEND + +WrtTCP EQU $C10B +RdTCP EQU $C108 ;CLOCK READ ENTRY POINTS +ClkMode EQU $0538 ;(+$CN=$5F8+N) + + ORG ClockBegin + +ReadClk LDX ClkSlt ;PRESERVE CURRENT MODE FOR THUNDERCLOCK + LDA ClkMode,X + PHA + LDA #$A3 ;SEND NUMERIC MODE BYTE TO THUNDERCLOCK + JSR WrtTCP +ClkSlt EQU *+2 + JSR RdTCP ;READ MONTH, DAY OF WEEK, DAY OF MONTH + CLC ; AND TIME INTO INPUT BUFFER + LDX #$04 ;INDEX FOR 5 VALUES + LDY #$0C ;READ MINUTES FIRST, MONTH LAST +Convrt LDA inBuf,Y ;CONVERT VALUES TO BINAR + AND #$07 ;NO VALUE > 5 DECIMAL + STA TENS ;MULTIPLY 'TENS' PLACE VALUE + ASL + ASL + ADC TENS ;NOW IT'S TIMES 5 + ASL ;NOW IT IS TIMES 10! + ADC inBuf+1,Y ;ADD TO ASCII 'ONES' PLACE + SEC ;AND SUBTRACT OUT THE ASCII... + SBC #"0" + STA MONTH,X ;SAVE CONVERTED VALUE + DEY ;INDEX TO NEXT LOWER VALUE + DEY + DEY + DEX ;ARE THERE MORE VALUES? + BPL Convrt ;BRANCH IF THERE ARE + + TAY ;A STILL CONTAINS MONTH, SAVE IN Y FOR NOW + LSR + ROR + ROR + ROR ;(HI BIT OF MONTH HELD IN CARRY) + ORA DAY + STA DateLo ;SAVE LOW VALUE OF DATE + PHP ;SAVE HI BIT OF MONTH FOR NOW + AND #$1F ;ISOLATE DAY AGAIN + ; (WHEN MONTH >7 CARRY SET ACCOUNTED FOR IN FOLLOWING ADD) + ADC TDays-1,Y ;REMEMBER THAT Y=MONTH + BCC :1 ;BRANCH NOT SEPT 13 THRU 30 + ADC #$03 ;ADJUST FOR MOD 7 WHEN DAY > 256 +:1 SEC +:loop SBC #$07 + BCS :loop ;LOOP UNTIL LESS THAN 0 + ADC #$07 ;NOW MAKE IT IN THE RANGE OF 0-6 + SBC WKDAY ; THE DELTA PROVIDES YEARS OFFSET + BCS :2 ;BRANCH IF POSITIVE + ADC #$07 ;ELSE MAKE IT POSITIVE AGAIN +:2 TAY ;LOOK UP YEAR! + LDA YrAdj,Y + PLP ;LASTLY, COMBINE WITH HI BIT OF MONTH + ROL + STA DateLo+1 ;AND SAVE IT + LDA HOUR + STA TimeLo+1 ;MOVE HOUR AND MINUTE TO PRODOS GLOBALS + LDA MINUTE + STA TimeLo + PLA + LDX ClkSlt ;RESTORE PREVIOUS MODE + STA ClkMode,X + RTS ;ALL DONE... + +TDays HEX 001F3B5A + HEX 7897B5D3 + HEX F2143351 + +YrAdj HEX 605F5E5D + HEX 626160 + DS $80-$7D,0 \ No newline at end of file diff --git a/MLI.SRC/WRKSPACE.S b/MLI.SRC/WRKSPACE.S new file mode 100644 index 0000000..ee5c222 --- /dev/null +++ b/MLI.SRC/WRKSPACE.S @@ -0,0 +1,180 @@ +*********************************************************** +* Global Data + +ownersBlock DW $0000 +ownersEnt DB $00 +ownersLen DB $00 + +*------------------------------------------------- +* Part of volume dir header + +h_creDate DW $0000 ;Directory creation date + DW $0000 ;Directory creation time + DB $00 ;Version under which this directory was created + DB $00 ;Earliest version that it's compatible with +h_attr DB $00 ;Attributes (protect bit, etc.) +h_entLen DB $00 ;Length of each entry in this dir +h_maxEnt DB $00 ;Maximum # of entries per block +h_fileCnt DW $0000 ;Current # of files in this dir +h_bitMap DW $0000 ;Disk Addr of first allocation bit map +h_totBlk DW $0000 ;Total number of blocks on this unit + +d_dev DB $00 ;Device number of this directory entry +d_head DW $0000 ;Disk Addr of directory header +d_entBlk DW $0000 ;Disk Addr of block which contains this entry +d_entNum DB $00 ;Entry number within block + +*------------------------------------------------- +* Layout of file entry + +d_file EQU * +d_stor EQU *-d_file + DB $00 ;storage type * 16 + file name length + DS 15,0 ;file name +d_fileID EQU *-d_file + DB $00 ;User's identification byte +d_first EQU *-d_file + DW $0000 ;First block of file +d_usage EQU *-d_file + DW $0000 ;# of blks currently allocated to this file +d_eof EQU *-d_file + DB 0,0,0 ;Current end of file marker +d_creDate EQU *-d_file + DW $0000 ;date of file's creation +d_creTime EQU *-d_file + DW $0000 ;time of file's creation +d_sosVer EQU *-d_file + DB $00 ;SOS version that created this file +d_comp EQU *-d_file + DB $00 ;Backward version compatablity +d_attr EQU *-d_file + DB $00 ;'protect', read/write 'enable' etc. +d_auxID EQU *-d_file + DW $0000 ;User auxillary identification +d_modDate EQU *-d_file + DW $0000 ;File's last modification date +d_modTime EQU *-d_file + DW $0000 ;File's last modification time +d_dHdr EQU *-d_file + DW $0000 ;Header block address of file's directory + +*------------------------------------------------- + +scrtch DS 4,0 ;Scratch area for allocation address conversion +oldEOF DS 3,0 ;Temp used in w/r +oldMark DS 3,0 ;Used by 'RdPosn' and 'Write' + +xvcbPtr DB $00 ;Used in 'cmpvcb' as a temp +vcbPtr DB $00 ;Offset into VCB table +fcbPtr DB $00 ;Offset into FCB table +fcbFlg DB $00 ;This is a flag to indicate a free FCB is available + +reqL DB $00 ;# of free blks required +reqH DB $00 +levels DB $00 ;Storage type (seedling,sapling etc) + +* # of entries examined or +* used as a flag to indicate file is already open + +totEnt DB $00 ;(0=open) +entCnt DW $0000 ;File count + +* entries/block loop count or +* as a temp to return the refnum of a free FCB + +cntEnt DB $00 + +* Free entry found flag (if > 0) +* # of 1st bitMap block with free bit on +* or bit for free + +noFree DB $00 + +*------------------------------------------------- +* Variable work area + +bmCnt DB $00 ;# in bitMap left to search +sapPtr DB $00 +pathCnt DB $00 ;Pathname len +pathDev DB $00 ;Dev num for prefix dir header +pathBlok DW $0000 ;Block of prefix dir header +bmPtr DB $00 ;VBM byte offset in page +basVal DB $00 ;VBM page offset +half DB $00 ;VBM buf page (0 or 1) + +* bit map info tables (a & b) + +bmaStat DB $00 ;VBM flag (If $80, needs writing) +bmaDev DB $00 ;VBM device +bmaDskAdr DW $00 ;VBM blk number +bmaCurrMap DB $00 ;bitMap blk offset for multiblk VBM + +* New mark to be positioned to for SetMark +* or new moving mark (for Read) +* or new EOF for SETEOF + +tPosll DB $00 +tPoslh DB $00 +tPosHi DB $00 +rwReqL DB $00 ;Request count (R/W etc) +rwReqH DB $00 +nlChar DB $00 +nlMask DB $00 +ioAccess DB $00 ;Has a call been made to disk device handler? +bulkCnt EQU * +cmdTmp DB $00 ;Test refnum, time, and dskswtch for (pre)processing + +bkBitFlg DB $00 ;Used for ReviseDir to set or clear back up bit +duplFlag DB $00 ;Difference between volNotFound and dupVol by synPath +vcbEntry DB $00 ;Pointer to current VCB entry + +* xdos temporaries added.... + +namCnt DB $00 ;ONLINE: vol len - loop index + +* Characters in current pathname index level or +* New pathname : index to last name + +rnPtr DB $00 +pnPtr EQU * ; Old pathname: index to last name or +namPtr DB $00 ;ONLINE: index to data buffer +vnPtr DB $00 ;Old PfixPtr value +prfxFlg DB $00 ;Pathname fully qualified flag (if $FF) +clsFlshErr EQU * ;Close-all err code +tempX DB $00 ;ONLINE: devcnt + +* The following are used for deallocation temps. + +firstBlkL DB $00 +firstBlkH DB $00 +storType DB $00 +deBlock DW $0000 ;Count of freed blks +dTree DB $00 ;EOFblk # (MSB) +dSap DB $00 ;EOFblk # (LSB) +dSeed DW $0000 ;EOF byte offset into blk +topDest DB $00 ;EOF-master index counter +dTempX DB $00 ;ONLINE: devcnt + +* Device table built by Online +* Also used by SetEOF to keep track +* of 8 blks to be freed at a time + +deAlocBufL DS 8,0 +deAlocBufH DS 8,0 + +lookList EQU deAlocBufL + +cBytes DW $0000 ;Len of path, etc + DB $00 ;cbytes+2 must always be zero. See "CalcMark" +bufAddrL DB $00 +bufAddrH DB $00 ;Buffer allocation, getbuffr, and release buffer temps. +goAdr DA $0000 ;Jump vector used for indirect JMP +delFlag DB $00 ;Used by DeTree to know if called from delete + DS $FEFD-*,0 +dispVect EQU * + DA CallDisp ;Dispatcher/Selector + +* This flag is set in the loader indicating +* whether the machine is a cortland + +cortFlag DB 0 ;0=>non-cortland / 1=>cortland \ No newline at end of file diff --git a/MLI.SRC/XDOSMLI.S b/MLI.SRC/XDOSMLI.S new file mode 100644 index 0000000..f4ac55c --- /dev/null +++ b/MLI.SRC/XDOSMLI.S @@ -0,0 +1,373 @@ +* * * * * * * * * * * * * * * * * +* The xdos machine language * +* interface (MLI) * +* system call processor * +* * * * * * * * * * * * * * * * * + + ORG orig1 + MX %11 +EntryMLI CLD ;Cannot deal with decimal mode!!! + PLA ;Get processor status + STA Spare1 ;Save it on the global page temporarily + STY SaveY ;Preserve the y and x registers + STX SaveX + PLA ;Find out the address of the caller + STA parm + CLC ; & preserve the address of the call spec + ADC #$04 + STA CmdAdr + PLA + STA parm+1 + ADC #$00 + STA CmdAdr+1 ;CmdAdr is in the globals + +* Check cmd code + + LDA Spare1 + PHA + PLP ;Pull processor to re-enable interrupts + CLD ;No decimal! (** #46 **) + LDY #$00 + STY SErr ;Clear any previous errors... + +* Hashing algorithm used here adds high nibble +* (div by 16 first) to the whole cmd code +* and then masks it to 5 lower bits +* This compresses range of codes without any overlapping + + INY ;Find out if we've got a valid command + LDA (parm),Y ;Get command # + LSR ;Hash it to a range of $00-$1F + LSR + LSR + LSR + CLC + ADC (parm),Y + AND #$1F + TAX + LDA (parm),Y ;Now see if result is a valid command number + CMP scNums,X ;Should match if it is + BNE scnErr ;Branch if not + +* Get IOB address + + INY ;Index to call spec parm list addr + LDA (parm),Y ;Make parm point to parameter count byte + PHA ; in parameter block + INY + LDA (parm),Y + STA parm+1 + PLA + STA parm + +* Check parm count + + LDY #c_pCnt ;Now make sure parameter list has the + LDA pCntTbl,X ; proper number of parameters + BEQ GoClock ;Clock has 0 parameters + CMP (parm),Y + BNE scpErr ;Report error if wrong count + +* Check class of function + + LDA scNums,X ;Get call number again + CMP #$65 + BEQ Special + ASL ;Carry set if bfm or dev mgr + BPL GoDevMgr + BCS GoBFMgr + LSR ;Shift back down for interupt mgr + +* Isolate type +* 0-alloc, 1-dealloc, 2-special + + AND #$03 ;Valid calls are 0 & 1 + JSR IntMgr + BRA ExitMLI ;Command processed, all done +Special JMP jSpare ;QUIT + +************************************************** +* Command $82 - Get the date and time + +GoClock JSR DateTime ;go read clock + BRA ExitMLI ;No errors posible! + +************************************************** +* READBLOCK and WRITEBLOCK commands ($80 and $81) + +GoDevMgr LSR ;Save command # + ADC #$01 ;Valid commands are 1 & 2 + STA dhpCmd ;(READ & WRITE) + JSR DevMgr ;Execute read or write request + BRA ExitMLI + +*------------------------------------------------- +* Commands $C0 thru $D3 + +GoBFMgr LSR + AND #$1F ;Valid commands in range of $00-$13 + TAX + JSR BFMgr ;Go do it... + +ExitMLI STZ BUBit ;First clear bubit + LDY SErr ;Y holds error code thru most of exit + CPY #$01 ;If >0 then set carry + TYA ; & set z flag + PHP ;Disable interupts + SEI ; until exit complete + LSR mliActv ;Indicate MLI done.(** #46 **) (** #85 **) + PLX ;Save status register in X + LDA CmdAdr+1 ; until return address is placed + PHA ; on the stack returning is done via 'RTI' + LDA CmdAdr ; so that the status register is + PHA ; restored at the same time + PHX ;Place status back on the stack + TYA ;Return error, if any + LDX SaveX ;Restore x & y registers + LDY SaveY +ExitRPM PHA ; (exit point for rpm **en3**) + LDA BnkByt1 ;Restore language card status & return + JMP Exit + +*------------------------------------------------- + +gNoDev LDA #drvrNoDevice ;Report no device connected + JSR SysErr +scnErr LDA #badSystemCall ;Report no such command + BNE scErr1 ;Branch always +scpErr LDA #invalidPcount ;report parameter count is invalid +scErr1 JSR GoSysErr + BCS ExitMLI ;Branch always taken + + TTL 'ProDOS Device Manager' + +*------------------------------------------------- +* ProDOS device manager +* Block I/O setup + +DevMgr LDY #$05 ;The call spec for devices must + PHP ;(do not allow interupts) + SEI +:loop LDA (parm),Y ; be passed to drivers in zero page + STA |dhpCmd,Y ;dhpCmd,unitNum,bufPtr,blockNum + DEY + BNE :loop + LDX bufPtr+1 + STX userBuf+1 + INX + INX ;Add 2 for 512 byte range + LDA bufPtr ;Is buffer page alligned? + BEQ :1 ;Branch if it is + INX ;Else account for 3-page straddle... +:1 JSR ValDBufZ ;Make sure user is not conflicting + BCS DevMgrErr ; with protected RAM + JSR DMgr ;Call internal entry for device dispatch + BCS DevMgrErr ;Branch if error occured + PLP + CLC ;Make sure carry is clear (no error) + RTS + +DevMgrErr PLP +GoSysErr JSR SysErr + +*------------------------------------------------- +* NOTE: interrupts must always be off when entering here +* Do block I/O rtn + +DMgr LDA unitNum ;Get device number + AND #$F0 ;Strip misc lower nibble + STA unitNum ; & save it back + LSR ;Use as index to device table + LSR + LSR + TAX + LDA DevAdr01,X ;Fetch driver address + STA goAdr + LDA DevAdr01+1,X + STA goAdr+1 +GoCmd JMP (goAdr) ;Goto driver (or error if no driver) + + TTL 'ProDOS Interrupt Manager' +*------------------------------------------------- +* ProDOS interrupt manager +* Handle ALLOC_INTERRUPTS ($40) and +* DEALLOC_INTERRUPTS ($41) Calls + +IntMgr STA intCmd ;Allocate intrupt or deallocate? + LSR ;(A=0, carry set=dealloc) + BCS DeAlocInt ;Branch if deallocation + LDX #$03 ;Test for a free interupt space in table +AlocInt LDA Intrup1-2,X ;Test high addr for zero + BNE :1 ;Branch if spot occupied + LDY #c_intAdr+1 ;Fetch addr of routine + LDA (parm),Y ;Must not be in zero page!!!! + BEQ BadInt ;Branch if the fool tried it + STA Intrup1-2,X ;Save high address + DEY + LDA (parm),Y + STA Intrup1-3,X ; & low address + TXA ;Now return interupt # in range of 1 to 4 + LSR + DEY + STA (parm),Y ;Pass back to user + CLC ;Indicate success! + RTS + +:1 INX + INX ;Bump to next lower priority spot + CPX #$0B ;Are all four allocated already? + BNE AlocInt ;Branch if not + + LDA #irqTableFull ;Return news that four devices are active + BNE IntErr1 + +BadInt LDA #paramRangeErr ;Report invalid parameter +IntErr1 JSR SysErr + +DeAlocInt LDY #c_intNum ;Zero out interupt vector + LDA (parm),Y ; but make sure it is valid # + BEQ BadInt ;Branch if it's <1 + CMP #$05 ; or >4 + BCS BadInt + ASL + TAX + LDA #$00 ;Now zip it + STA Intrup1-2,X + STA Intrup1-1,X + CLC + RTS + +*------------------------------------------------- +* IRQ Handler - If an IRQ occurs, we eventually get HERE + +IrqRecev LDA Acc ;Get Acc from 0-page where old ROM put it + STA IntAReg + STX IntXReg ;Entry point on RAM card interupt + STY IntYReg + TSX + STX IntSReg + LDA IrqFlag ;Irq flag byte = 0 if old ROMs + BNE :1 ; and 1 if new ROMs + PLA + STA IntPReg + PLA + STA IntAddr + PLA + STA IntAddr+1 +:1 TXS ;Restore return addr & p-reg to stack + LDA MSLOT ;Set up to re-enable $cn00 rom + STA IrqDev+2 + TSX ;Make sure stack has room for 16 bytes + BMI NoStkSave ;Branch if stack safe + LDY #16-1 +StkSave PLA + STA SvStack,Y + DEY + BPL StkSave + +NoStkSave LDX #$FA ;Save 6 bytes of zero page +ZPgSave LDA $00,X + STA SvZeroPg-$FA,X + INX + BNE ZPgSave + +* Poll interupt routines for a claimer + + LDA Intrup1+1 ;Test for valid routine + BEQ :1 ;Branch if no routine + JSR goInt1 + BCC IrqDone +:1 LDA Intrup2+1 ;Test for valid routine + BEQ :2 ;Branch if no routine + JSR goInt2 ;Execute routine + BCC IrqDone +:2 LDA Intrup3+1 ;Test for valid routine + BEQ :3 ;Branch if no routine + JSR goInt3 + BCC IrqDone +:3 LDA Intrup4+1 ;Test for valid routine + BEQ IrqDeath ;Branch if no routine + JSR goInt4 ;Execute routine + BCC IrqDone + +*************** see rev note #35 ************************* + +IrqDeath INC IrqCount ;Allow 255 unclaimed interrupts + BNE IrqDone ; before going to system death... + LDA #unclaimedIntErr + JSR SysDeath + +* IRQ processing complete + +IrqDone LDX #$FA +:loop LDA SvZeroPg-$FA,X + STA $00,X + INX + BNE :loop + LDX IntSReg ;Test for necessity of restoring stack elements + BMI :1 + LDY #$00 +:loop2 LDA SvStack,Y + PHA + INY + CPY #16 + BNE :loop2 + +:1 LDA IrqFlag ;Check for old ROMs + BNE IrqDoneX ;Branch if new ROMs + +* Apple II or II+ monitor + + LDY IntYReg ;Restore registers + LDX IntXReg + LDA CLRROM ;Re-enable I/O card +IrqDev LDA $C100 ;Warning, self modified + LDA IrqDev+2 ;Restore device ID + STA MSLOT +IrqDoneX JMP irqXit + +IrqFlag DB $00 ;irq flag byte. 0=old ROMs; 1=new ROMs +IrqCount DB $00 ;Unclaimed interrupt counter.(note #35) +SvStack DS 16,0 +SvZeroPg DS 6,0 + +goInt1 JMP (Intrup1) +goInt2 JMP (Intrup2) +goInt3 JMP (Intrup3) +goInt4 JMP (Intrup4) + +*------------------------------------------------- +* System error handler + +SysErr1 STA SErr + PLX + PLX ;Pop 1 level of return + SEC + RTS + +*------------------------------------------------- +* System death handler + +SysDeath1 TAX ;System death!!! + STA CLR80VID ;Force 40 columns on rev-e + LDA TXTSET ;Text mode on + LDA cortFlag ;Check if we're on a cortland + BEQ NoSupHires + STZ NEWVIDEO ;Force off SuperHires +NoSupHires LDA TXTPAGE1 ;Display page 1 on + LDY #$13 +DspDeath LDA #' ' + STA SLIN10+10,Y + STA SLIN12+10,Y + LDA Death,Y + STA SLIN11+10,Y + DEY + BPL DspDeath + TXA + AND #$0F + ORA #"0" + CMP #"9"+1 + BCC :1 ;Branch if not >9 + ADC #$06 ;Bump to alpha A-F +:1 STA SLIN11+28 +Halt BRA Halt ;Hold forever diff --git a/MLI.SRC/XRW1.S b/MLI.SRC/XRW1.S new file mode 100644 index 0000000..c840842 --- /dev/null +++ b/MLI.SRC/XRW1.S @@ -0,0 +1,567 @@ + TTL 'xdos R/W routines revised' +**************************************************** +* +* PRODOS 8 DISK II DRIVER (RWTS) +* +* COPYRIGHT APPLE COMPUTER INC., 1980-1986 +* +* ALL RIGHTS RESERVED +* +* REVISED 11/8/82 BY J.R.H. +* +**************************************************** +**************************** +* * +* critical timing * +* requires page bound * +* considerations for * +* code and data * +* code----- * +* virtually the entire * +* 'write' routine * +* must not cross * +* page boundaries. * +* critical branches in * +* the 'write', 'read', * +* and 'read adr' subrs * +* which must not cross * +* page boundaries are * +* noted in comments. * +* * +**************************** +* * +* Equates * +* * +maxCmd EQU 4 ;Commands 0-3 only +dvMot EQU $E8 + DUM $3A +wTemp DS 1 +midNib1 DS 1 +midNib2 DS 1 +lstNib DS 1 +slotZ DS 1 +yEnd DS 1 + DEND + +************************ +* * +* device address * +* assignments * +* * +************************ +phaseOff EQU $C080 ;stepper phase off. +*phaseOn EQU $C081 ;stepper phase on. +q6l EQU $C08C ;q7l,q6l=read +q6h EQU $C08D ;q7l,q6h=sense wprot +q7l EQU $C08E ;q7h,q6l=write +q7h EQU $C08F ;q7h,q6h=write store +**************************************** +* +* Equates for rwts and block +* +**************************************** +motorOff EQU $C088 +motorOn EQU $C089 +drv0En EQU $C08A +*drv1en EQU $C08B + + ORG $D000 +************************ +* * +* block i/o * +* * +************************ +BlockIO CLD ;This must be first as it is an ID value + JSR ResetPhase + +* this is patch 76 part 1. part 2 is in xrw2. +* this is patch 77, use to be 'lda q7l+14,x'. + + LDA q7l,X ;Turn off write enable please!!! +* (pad is just spare bytes but the number is critical for page + NOP + NOP + JSR doCheck + BCS BadBlk ;Branch if block # is out of range + LDY #$05 +:loop ASL + ROL ibTrk + DEY + BNE :loop + ASL + BCC :1 + ORA #$10 ;Adjust for upper 4 blks of trk +:1 LSR + LSR + LSR + LSR + PHA ;Save sector# + JSR RegRWTS + PLA ;Restore sector # + BCS Quit ;Branch if error encountered + INC bufPtr+1 + ADC #$02 + JSR RegRWTS ;Get second half of block + DEC bufPtr+1 +Quit LDA ibStat + RTS + +BadBlk LDA #drvrIOError + SEC + RTS +************************** +* * +* read/write a * +* track and sector * +* * +************************** +RegRWTS LDY #$01 ;Retry count + STY seekCnt ;Only one recalibrate per call + STA ibSect + LDA unitNum ;Get slot # for this operation + AND #$70 + STA slotZ + JSR ChkPrev ;Make sure other drives in other slots are stopped +* +* Now check if the motor is on, then start it +* + JSR ChkDrv ;Set zero flag if motor stopped + PHP ;Save test results + LDA #dvMot + STA monTimeH + LDA unitNum ;Determine drive one or two + CMP iobPrevDn ;Same drive used before? + STA iobPrevDn ;Save it for next time + PHP ;Keep results of compare + ASL ;Get drive number into carry + LDA motorOn,X ;Turn on the drive + BCC DrvSel ;Branch if drive 1 selected + INX ;Select drive 2 +DrvSel LDA drv0En,X + PLP ;Was it same drive? + BEQ :1 ;Yes + PLP ;Must indicate drive off by setting zero flag + LDY #$07 ;Delay 150 ms before stepping +:WaitLoop JSR msWait ;(on return A=0) + DEY + BNE :WaitLoop + PHP ;Now zero flag set +:1 LDA dhpCmd ;Make sure this command needs seeking + BEQ :2 ;Branch if status check + LDA ibTrk ;Get destination track + JSR MySeek ; & go to it + +* Now at the desired track. Was the motor +* on to start with? + +:2 PLP ;Was motor on? + BNE TryTrk ;If so, don't delay, get it today! + +* Motor was off, wait for it to speed up. + +MotOff LDA #$01 ;Wait exactly 100 us for each count in monTime + JSR msWait + LDA monTimeH + BMI MotOff ;count up to 0000 +**************************************** +* +* Motor should be up to speed. +* If it still looks stopped then +* the drive is not present. +* +**************************************** + JSR ChkDrv ;Is drive present? + BEQ HndlErr ;Branch if no drive + +* Now check: if it is not the format disk command, +* locate the correct sector for this operation. + +TryTrk LDA dhpCmd ;Get command code # + BEQ StatCmd ;If $00, then status command + LSR ;Set carry=1 for read, 0 for write + BCS :1 ;Must prenibblize for write + JSR PreNibl16 +:1 LDY #$40 ;Only 64 retries of any kind + STY retryCnt +TryAdr LDX slotZ ;Get slot num into X-reg + JSR RdAdr16 ;Read next address field + BCC RdRight ;If read it right, hurrah! +TryAdr2 DEC retryCnt ;Another mistake!! + BPL TryAdr ;Well, let it go this time... + LDA #drvrIOError ;Anticipate a bad drive error + DEC seekCnt ;Only recalibrate once! + BNE HndlErr ;Tried to recalibrate a second time, error! + LDA curTrk ;Save track we really want + PHA + ASL + ADC #$10 ;Pretend track is 8>curtrk + LDY #$40 + STY retryCnt ;Reset retries to 64 max + BNE ReCal1 ;Branch always + +* Have now read an address field correctly. +* Make sure this is the desired track, sector, and volume. + +RdRight LDY track ;On the right track? + CPY curTrk + BEQ RtTrk ;if so, good + +* Recalibrating from this track + + LDA curTrk ;Preserve destination track + PHA + TYA + ASL ;(washing machine fix!) +ReCal1 JSR SetTrk + PLA + JSR MySeek + BCC TryAdr ;Go ahead and recalibrate + +* Drive is on right track, check volume mismatch + +RtTrk LDA sector ;Check if this is the right sector + CMP ibSect + BNE TryAdr2 ;No, try another sector + LDA dhpCmd ;read or write? + LSR ;The carry will tell + BCC WriteIt ;Carry was set for read operation, + JSR Read16 ; cleared for write + BCS TryAdr2 ;Carry set upon return if bad read +AllDone EQU * ;Was CLC + LDA #$00 ;No error + DB $D0 ;Branch never (skip 1 byte) +HndlErr SEC ;Indicate an error + STA ibStat ;Give him error # + LDX slotZ ;Get the slot offset + LDA motorOff,X ;Turn it off... + RTS ;All finished! + +WriteIt JSR Write16 ;Write nybbles now +StatDone BCC AllDone ;If no errors + LDA #drvrWrtProt ;Disk is write protected!! + BNE HndlErr ;Branch always + +StatCmd LDX slotZ + LDA q6h,X ;Test for write protected + LDA q7l,X + ROL ;Write protect-->carry-->bit-0=1 + LDA q6l,X ;Keep in read mode... + JMP StatDone ;Branch always taken + +* This is the 'seek' routine +* seeks track 'n' in slot #x/$10 +* If drivno is negative, on drive 0 +* If drivno is positive, on drive 1 + +MySeek ASL ;Assume two phase stepper + STA track ;Save destination track(*2) + JSR AllOff ;Turn all phases off to be sure + JSR DrvIndx ;Get index to previous track for current drive + LDA drv0Trk,X + STA curTrk ;This is where i am + LDA track ; & where i'm going to + STA drv0Trk,X + JSR Seek ;Go there! +AllOff LDY #$03 ;Turn off all phases before returning +NxtOff TYA ;(send phase in Acc) + JSR ClrPhase ;Carry is clear, phases should be turned off + DEY + BPL NxtOff + LSR curTrk ;Divide back down + CLC + RTS ;All off... now it's dark +************************** +* * +* fast seek subroutine * +************************** +* * +* on entry ---- * +* * +* x-reg holds slotnum * +* times $10. * +* * +* a-reg holds desired * +* halftrack. * +* (single phase) * +* * +* curtrk holds current * +* halftrack. * +* * +* on exit ----- * +* * +* a-reg uncertain. * +* y-reg uncertain. * +* x-reg undisturbed. * +* * +* curtrk and trkn hold * +* final halftrack. * +* * +* prior holds prior * +* halftrack if seek * +* was required. * +* * +* montimel and montimeh * +* are incremented by * +* the number of * +* 100 usec quantums * +* required by seek * +* for motor on time * +* overlap. * +* * +* --- variables used --- * +* * +* curtrk, trkn, count, * +* prior, slottemp * +* montimel, montimeh * +* * +************************** + +Seek STA trkNbr ;Save target track + CMP curTrk ;On desired track? + BEQ SetPhase ;Yes,energize phase and return + LDA #$00 + STA trkCnt ;Halftrack count +SeekLoop LDA curTrk ;Save curTrk for + STA prior ; delayed turnoff + SEC + SBC trkNbr ;delta-tracks + BEQ SeekEnd ;branch if curTrk=destination + BCS Out ;(move out, not in) + EOR #$FF ;Calc trks to go + INC curTrk ;Incr current track (in) + BCC MinTst ;(always taken) +Out ADC #$FE ;Calc trks to go + DEC curTrk ;Decr current track (out) +MinTst CMP trkCnt + BCC MaxTst ; & 'trks moved' + LDA trkCnt +MaxTst CMP #$09 + BCS Step2 ;If trkcnt>$8 leave y alone (y=$8) + TAY ;else set acceleration index in y + SEC +Step2 JSR SetPhase + LDA OnTable,Y ;For 'ontime' + JSR msWait ;(100 usec intervals) + LDA prior + CLC ;For phaseoff + JSR ClrPhase ;Turn off prior phase + LDA OffTable,Y + JSR msWait + INC trkCnt ;'tracks moved' count + BNE SeekLoop ;(always taken) +SeekEnd JSR msWait ;Settle 25 msec + CLC ;Set for phase off +SetPhase LDA curTrk ;Get current track +ClrPhase AND #$03 ;Mask for 1 of 4 phases + ROL ;Double for phaseon/off index + ORA slotZ + TAX + LDA phaseOff,X ;Turn on/off one phase + LDX slotZ ;Restore x-reg + RTS ; & return +************************** +* * +* 7-bit to 6-bit * +* 'deniblize' tabl * +* (16-sector format) * +* * +* valid codes * +* $96 to $ff only. * +* * +* codes with more than * +* one pair of adjacent * +* zeroes or with no * +* adjacent ones (except * +* bit 7) are excluded. * +* * +* * +* nibls in the ranges * +* of $a0-$a3, $c0-$c7, * +* $e0-$e3 are used for * +* other tables since no * +* valid nibls are in * +* these ranges. * +************************** + +dNibble EQU *-$96 + HEX 0004 + HEX FFFF080C + HEX FF101418 +TwoBit3 HEX 008040C0 ;Used in fast prenib as + HEX FFFF1C20 ; lookup for 2-bit quantities + HEX FFFFFF24 + HEX 282C3034 + HEX FFFF383C + HEX 4044484C + HEX FF505458 + HEX 5C606468 +TwoBit2 HEX 00201030 ;Used in fast prenib +EndMarks HEX DEAAEBFF ;Table using 'unused' + HEX FFFFFF6C ; nibls ($c4,$c5,$c6,$c7) + HEX FF707478 + HEX FFFFFF7C + HEX FFFF8084 + HEX FF888C90 + HEX 94989CA0 +TwoBit1 HEX 0008040C ;Used in fast prenib + HEX FFA4A8AC + HEX FFB0B4B8 + HEX BCC0C4C8 + HEX FFFFCCD0 + HEX D4D8DCE0 + HEX FFE4E8EC + HEX F0F4F8FC +* page align the following tables. +*************************** +* * +* 6-bit to 2-bit * +* conversion tables. * +* * +* dnibl2 abcdef-->0000fe * +* dnibl3 abcdef-->0000dc * +* dnibl4 abcdef-->0000ba * +* * +*************************** +* * +* 6-bit to 7-bit * +* nibl conversion table * +* * +* codes with more than * +* one pair of adjacent * +* zeroes or with no * +* adjacent ones (except * +* b7) are excluded. * +* * +*************************** + +dNibble2 DB $00 +dNibble3 DB $00 +dNibble4 DB $00 +Nibbles HEX 9602000097 + HEX 0100009A0300009B + HEX 0002009D0202009E + HEX 0102009F030200A6 + HEX 000100A7020100AB + HEX 010100AC030100AD + HEX 000300AE020300AF + HEX 010300B2030300B3 + HEX 000002B4020002B5 + HEX 010002B6030002B7 + HEX 000202B9020202BA + HEX 010202BB030202BC + HEX 000102BD020102BE + HEX 010102BF030102CB + HEX 000302CD020302CE + HEX 010302CF030302D3 + HEX 000001D6020001D7 + HEX 010001D9030001DA + HEX 000201DB020201DC + HEX 010201DD030201DE + HEX 000101DF020101E5 + HEX 010101E6030101E7 + HEX 000301E9020301EA + HEX 010301EB030301EC + HEX 000003ED020003EE + HEX 010003EF030003F2 + HEX 000203F3020203F4 + HEX 010203F5030203F6 + HEX 000103F7020103F9 + HEX 010103FA030103FB + HEX 000303FC020303FD + HEX 010303FE030303FF + +nBuf2 DS $56,0 ;nibble buffer for R/W of low 2-bits of each byte + +ibTrk DB $00 +ibSect DB $00 +ibStat DB $00 +iobPrevDn DB $00 +curTrk DB $00 +drv0Trk EQU *-2 + HEX 00000000000000 ;for slots 1 thru 7 + HEX 00000000000000 ;drives 1 & 2 +retryCnt DS 1,0 +seekCnt DS 1,0 +************************ +* * +* readadr---- * +* * +************************ +count EQU * ;'must find' count +last DS 1,0 ;'odd bit' nibls +chkSum DS 1,0 ;Used for address header cksum +csSTV DS 4,0 +* checksum, sector, track, and volume. +sector EQU csSTV+1 +track EQU csSTV+2 +volume EQU csSTV+3 + +trkCnt EQU count ;Halftracks moved count +prior DS 1,0 +trkNbr DS 1,0 +************************ +* * +* mswait ---- * +* * +************************ +monTimeL equ csSTV+2 ;Motor-on time +monTimeH equ monTimeL+1 ;counters. +************************** +* * +* phase on-, off-time * +* tables in 100-usec * +* intervals. (seek) * +* * +************************** +OnTable HEX 013028 + HEX 24201E + HEX 1D1C1C +OffTable HEX 702C26 + HEX 221F1E + HEX 1D1C1C + +************************** +* * +* mswait subroutine * +* * +************************** +* * +* delays a specified * +* number of 100 usec * +* intervals for motor * +* on timing. * +* * +* on entry ---- * +* * +* a-reg: holds number * +* of 100 usec * +* intervals to * +* delay. * +* * +* on exit ----- * +* * +* a-reg: holds $00. * +* x-reg: holds $00. * +* y-reg: unchanged. * +* carry: set. * +* * +* montimel, montimeh * +* are incremented once * +* per 100 usec interval* +* for moton on timing. * +* * +* assumes ---- * +* * +* 1 usec cycle time * +* * +************************** +msWait LDX #$11 +:loop DEX ;Delay 86 usec + BNE :loop + INC monTimeL + BNE :1 ;double-byte + INC monTimeH ; increment +:1 SEC + SBC #$01 ;Done 'n' intervals? + BNE msWait ;(A-reg counts) + RTS diff --git a/MLI.SRC/XRW2.S b/MLI.SRC/XRW2.S new file mode 100644 index 0000000..9065ad1 --- /dev/null +++ b/MLI.SRC/XRW2.S @@ -0,0 +1,644 @@ +**************************** +* * +* read address field * +* subroutine * +* (16-sector format) * +* * +**************************** +* * +* reads volume, track * +* and sector * +* * +* on entry ---- * +* * +* xreg: slotnum times $10 * +* * +* read mode (q6l, q7l) * +* * +* on exit ----- * +* * +* carry set if error. * +* * +* if no error: * +* a-reg holds $aa. * +* y-reg holds $00. * +* x-reg unchanged. * +* carry clear. * +* * +* csstv holds chksum, * +* sector, track, and * +* volume read. * +* * +* uses temps count, * +* last, csum, and * +* 4 bytes at csstv. * +* * +* expects ---- * +* * +* original 10-sector * +* normal density nibls * +* (4-bit), odd bits, * +* then even. * +* * +* caution ---- * +* * +* observe * +* 'no page cross' * +* warnings on * +* some branches!! * +* * +* assumes ---- * +* * +* 1 usec cycle time * +* * +**************************** + +RdAdr16 LDY #$FC + STY count ;'must find' count +RdASyn INY + BNE :loop1 ;Low order of count + INC count ;(2k nibbles to find + BEQ RdErr ; adr mark, else err) +:loop1 LDA q6l,X ;Read nibble + BPL :loop1 ;*** no page cross! *** +RdASyn1 CMP #$D5 ;Adr mark 1? + BNE RdASyn ;(loop if not) + NOP ;Added nibble delay + LDA q6l,X + BPL *-3 ;*** no page cross! *** + CMP #$AA ;Adr mark 2? + BNE RdASyn1 ; (if not, is it am1?) +* (added nibl delay) + LDY #$03 ;Index for 4-byte read + LDA q6l,X + BPL *-3 ;*** no page cross! *** + CMP #$96 ;Adr mark 3? + BNE RdASyn1 ; (if not, is it am1?) + SEI ;No interupts until address is tested.(carry is set) + LDA #$00 ;Init checksum +RdAddrFld STA chkSum + LDA q6l,X ;Read 'odd bit' nibble + BPL *-3 ;*** no page cross! *** + ROL ;Align odd bits, '1' into lsb + STA last ; (save them) + LDA q6l,X ;Read 'even bit' nibble + BPL *-3 ;*** no page cross! *** + AND last ;Merge odd and even bits + STA csSTV,Y ;Store data byte + EOR chkSum + DEY + BPL RdAddrFld ;Loop on 4 data bytes + TAY ;If final checksum + BNE RdErr ; nonzero, then error + LDA q6l,X ;First bit-slip nibble + BPL *-3 ;*** no page cross! *** + CMP #$DE + BNE RdErr ;Error if nonmatch + NOP ;delay + LDA q6l,X ;Second bit-slip nibble + BPL *-3 ;*** no page cross! *** + CMP #$AA + BNE RdErr ;Error if nonmatch + CLC ;Clear carry on + RTS ;Normal read exits + +RdErr SEC + RTS +************************** +* * +* read subroutine * +* (16-sector format) * +* * +************************** +* * +* reads encoded bytes * +* into nbuf1 and nbuf2 * +* * +* first reads nbuf2 * +* high to low, * +* then reads nbuf1 * +* low to high. * +* * +* on entry ---- * +* * +* x-reg: slotnum * +* times $10. * +* * +* read mode (q6l, q7l) * +* * +* on exit ----- * +* * +* carry set if error. * +* * +* if no error: * +* a-reg holds $aa. * +* x-reg unchanged. * +* y-reg holds $00. * +* carry clear. * +* caution ----- * +* * +* observe * +* 'no page cross' * +* warnings on * +* some branches!! * +* * +* assumes ---- * +* * +* 1 usec cycle time * +* * +************************** + +Read16 TXA ;Get slot # + ORA #$8C ;Prepare mods to read routine + STA Read4+1 ;Warning: the read routine is self modified!!! + STA Read5+1 + STA Read6+1 + STA Read7+1 + STA Read8+1 + LDA bufPtr ;Modify storage addresses also + LDY bufPtr+1 + STA Ref3+1 + STY Ref3+2 + SEC + SBC #$54 + BCS :1 + DEY +:1 STA Ref2+1 + STY Ref2+2 + SEC + SBC #$57 + BCS :2 ;Branch if no borrow + DEY +:2 STA Ref1+1 + STY Ref1+2 + LDY #$20 ;'must find count' +rSync DEY + BEQ RdErr2 ;Branch if can't find data header marks + LDA q6l,X + BPL *-3 +rSync1 EOR #$D5 ;First data mark + BNE rSync + NOP ;Waste a little time... + LDA q6l,X + BPL *-3 + CMP #$AA ;Data mark 2 + BNE rSync1 ;If not, check for first again + NOP + LDA q6l,X + BPL *-3 + CMP #$AD ;Data mark 3 + BNE rSync1 ;If not, check for data mark 1 again + LDY #$AA + LDA #$00 +RdData1 STA wTemp ;Use zpage for checksum keepin +Read4 LDX $C0EC ;Warning: self modified + BPL Read4 + LDA dNibble,X + STA nBuf2-$AA,Y ;Save the two-bit groups in nbuf + EOR wTemp ;Update checksum + INY ;Bump to next nBuf2 position + BNE RdData1 ;Loop for all $56 two-bit groups + LDY #$AA ;Now read directly into user buffer + BNE Read5 ;Branch always taken!!! +RdErr2 SEC + RTS + +Ref1 STA $1000,Y ;Warning: self modified! + +Read5 LDX $C0EC + BPL Read5 + EOR dNibble,X ;Get actual 6-bit data from dNibble table + LDX nBuf2-$AA,Y ;Get associated two-bit pattern + EOR dNibble2,X ; & combine to form whole byte + INY + BNE Ref1 ;Loop for $56 bytes + PHA ;Save this byte for now, no time to store... + AND #$FC ;Strip low 2 bits... + LDY #$AA ;Prepare for next $56 bytes +Read6 LDX $C0EC + BPL Read6 + EOR dNibble,X + LDX nBuf2-$AA,Y + EOR dNibble3,X +Ref2 STA $1000,Y ;Warning: self modified + INY + BNE Read6 ;Loop until this group of $56 read in +* +Read7 LDX $C0EC + BPL Read7 + AND #$FC + LDY #$AC ;Last group is $54 long +RdData2 EOR dNibble,X + LDX nBuf2-$AC,Y + EOR dNibble4,X ;Combine to form full byte +Ref3 STA $1000,Y +Read8 LDX $C0EC ;Warning: self modified + BPL Read8 + INY + BNE RdData2 + AND #$FC + EOR dNibble,X ;Check sum ok? + BNE RdErr1 ;Branch if not + LDX slotZ ;Test end marks + LDA q6l,X + BPL *-3 + CMP #$DE + CLC + BEQ RdOK ;Branch if good trailer... +RdErr1 SEC +RdOK PLA + LDY #$55 ;Place last byte into user buffer + STA (bufPtr),Y + RTS + +* This subroutine sets the slot +* dependent track location. + +SetTrk JSR DrvIndx ;Get index to drive number + STA drv0Trk,X + RTS +***************************************** +* +* Subrtn to tell if motor is stopped +* +* If motor is stopped, controller's +* shift reg will not be changing. +* +* return y=0 and zero flag set if it is stopped. +* +***************************************** +ChkDrv LDX slotZ +ChkDrv0 LDY #$00 ;init loop counter +:loop LDA q6l,X ;read the shift reg + JSR :ChkDrvRTS ;delay + PHA + PLA ;more delay + CMP q6l,X + BNE :ChkDrvRTS + LDA #$28 + DEY + BNE :loop +:ChkDrvRTS RTS + +DrvIndx PHA ;Preserve Acc + LDA unitNum ;DSSS xxxx where D=0/1 & SSS=slot # + LSR + LSR + LSR + LSR ;0000 DSSS + CMP #$08 ;C=1 -> drive 2 + AND #$07 ;0000 0SSS + ROL ;0000 SSSD + TAX ;Into X for index to table + PLA ;Restore A + RTS +************************ +* * +* write subr * +* (16-sector format) * +* * +************************ +* * +* writes data from * +* nbuf1 and buf * +* * +* first nbuf2, * +* high to low. * +* then direct from * +* (buf), low to high. * +* self modified code!! * +* on entry ---- * +* * +* x-reg: slotnum * +* times $10. * +* * +* * +* on exit ----- * +* * +* carry set if error. * +* (w prot violation) * +* * +* if no error: * +* * +* a-reg uncertain. * +* x-reg unchanged. * +* y-reg holds $00. * +* carry clear. * +* * +* assumes ---- * +* * +* 1 usec cycle time * +* * +************************ + +Write16 SEC ;Anticipate wprot err + LDA q6h,X + LDA q7l,X ;Sense wprot flag + BPL :1 + JMP WExit ;Exit if write protected + +:1 LDA nBuf2 + STA wTemp + LDA #$FF ;Sync data + STA q7h,X ;(5) Goto write mode + ORA q6l,X ;(4) + LDY #$04 ;(2) For five nibbles + NOP ;(2) + PHA ;(3) + PLA ;(4) +WSync PHA ;(3) exact timing + PLA ;(4) exact timing + JSR WrNibl7 ;(13,9,6) write sync + DEY ;(2) + BNE WSync ;(3-) must not cross page! + LDA #$D5 ;(2) 1st data mark + JSR WrNibl9 ;(15,9,6) + LDA #$AA ;(2) 2nd data mark + JSR WrNibl9 ;(15,9,6) + LDA #$AD ;(2) 3rd data mark + JSR WrNibl9 ;(15,9,6) + TYA ;(2) zero checksum + LDY #$56 ;(2) nbuf2 index + BNE wData1 ;(3) branch always taken +wData0 LDA nBuf2,Y ;(4) prior 6-bit nibble +wData1 EOR nBuf2-1,Y ;(5) xor with current + TAX ;(2) index to 7-bit nibl (nBuf2 must be on page bdry) + LDA Nibbles,X ;(4) must not cross page boundary + LDX slotZ ;(3) restore slot index + STA q6h,X ;(5) store encoded byte + LDA q6l,X ;(4) time must = 32 us per byte! + DEY ;(2) + BNE wData0 ;(3-) must not cross page boundary + LDA wTemp ;(3) get prior nibble (from nBuf2) +WRefDr1 LDY #$00 ;(2) warning: load value modified by prenib! +WData2 EQU * +WRefAdr1 EOR $1000,Y ;(4) warning: address modified by prenib! + AND #$FC ;(2) + TAX ;(2) index to Nibbles table + LDA Nibbles,X ;(4) +WRefDr2 LDX #$60 ;(2) warning: load value modified by prenib + STA q6h,X ;(5) write nibl + LDA q6l,X ;(4) handshake +WRefAdr2 LDA $1000,Y ;(4) prior nibl. warning: address modified by prenib + INY ;(2) all done with this page? + BNE WData2 ;(3-) loop until page end + + LDA midNib1 ;(3) get next (precalculated and translated) nibl + BEQ WrtDone ;(2+) branch if code writen was page aligned + LDA yEnd ;(3) get byte address of last byte to be written + BEQ WData4 ;(2+) branch if only 1 byte left to write + LSR ;(2) test for odd or even last byte (carry set or clear) + LDA midNib1 ;(3) restore nibl to a + STA q6h,X ;(5) + LDA q6l,X ;(4) + LDA midNib2 ;(3) =byte 0 of second page. xor'd with byte 1 if above test set carry + NOP ;(2) waste time + INY ;(2) y=1 + BCS WrtOdd ;(2+) branch if last byte to be odd + +WData3 EQU * +WRefAdr3 EOR $1100,Y ;(4) warning: address modified by prenib + AND #$FC ;(2) strip low 2 bits + TAX ;(2) index to Nibbles table + LDA Nibbles,X ;(4) get nibble +WRefDr3 LDX #$60 ;(2) restore slot index. warning: modified by prenib + STA q6h,X ;(5) + LDA q6l,X ;(4) +WRefAdr4 LDA $1100,Y ;(4) warning: modified by prenib + INY ;(2) got prior nibble, bump to next +WRefAdr5 EOR $1100,Y ;(4) warning: modified by prenib +WrtOdd CPY yEnd ;(3) set carry if this is last nibble + AND #$FC ;(2) + TAX ;(2) + LDA Nibbles,X ;(4) +WRefDr4 LDX #$60 ;(2) restore slot. warning: modified by prenib + STA q6h,X ;(5) + LDA q6l,X ;(4) +WRefAdr6 LDA $1100,Y ;(4) get prior. warning: these warnings are all the same + INY ;(2) + BCC WData3 ;(3-) branch if that was not the las + BCS *+2 ;(3) waste 3 cycles, branch always + BCS WrtDone ;(3) branch always + +WData4 LDA |midNib1 ;(4) absolute reference to zero page + STA q6h,X ;(5) + LDA q6l,X ;(4) + PHA ;(3) waste 14 us total + PLA ;(4) + PHA ;(3) + PLA ;(4) +WrtDone LDX lstNib ;(3) use last nibl (anded with $fc) for checksum + LDA Nibbles,X ;(4) +WRefDr5 LDX #$60 ;(2) restore slot. warning: see above warnings... + STA q6h,X ;(5) + LDA q6l,X ;(4) + LDY #$00 ;(2) set y to index end mark table + PHA ;(3) waste another 11 us + PLA ;(4) + NOP ;(2) + NOP ;(2) +WrEndMrk LDA EndMarks,Y ;(4) dm4, dm5, dm6, and turn off byte + JSR WrNibl ;(15,6) write it + INY ;(2) + CPY #$04 ;(2) have all end marks been written? + BNE WrEndMrk ;(3) + CLC ;(2,9) +WExit LDA q7l,X ;Out of write mode + LDA q6l,X ;Into read mode + RTS ;Return from write +**************************** +* * +* 7-bit nibl write subrs * +* * +* a-reg or'd prior exit * +* carry cleared * +* * +**************************** + +WrNibl9 CLC ;(2) 9 cycles, then write +WrNibl7 PHA ;(3) 7 cycles, then write + PLA ;(4) +WrNibl STA q6h,X ;(5) nibble write subrtn + ORA q6l,X ;(4) clobbers acc, not carry + RTS ;(6) +**************************** +* * +* preniblize subr * +* (16-sector format) * +* * +**************************** +* * +* converts 256 bytes of * +* user data in (buf) into * +* 6 bit nibls into nbuf2 * +* high 6 bits are trans- * +* lated directly by the * +* write routines. * +* * +* on entry ---- * +* * +* buf is 2-byte pointer * +* to 256 bytes of user * +* data. * +* * +* on exit ----- * +* * +* a,x,y undefined. * +* write routine modified * +* to do direct conversion * +* of high 6 bits of users * +* buffer data. * +**************************** + +PreNibl16 LDA bufPtr ;First self modify addresses so we can be fast! + LDY bufPtr+1 ;Y contains high order address + CLC ;All offsets are -$AA... + ADC #$02 ;The highest set is bufPtr+$AC + BCC :1 ;Branch if no carry + INY ;Otherwise add carry to high address +:1 STA PrN3+1 ;Self mod 3 + STY PrN3+2 + SEC + SBC #$56 ;middle set is buf+$56 + BCS :2 ;branch if no borrow + DEY ;otherwise deduct from high... +:2 STA PrN2+1 ;self mod 2 + STY PrN2+2 + SEC + SBC #$56 ;Low set is exactly bufPtr + BCS :3 + DEY +:3 STA PrN1+1 ;self mod 1 + STY PrN1+2 + + LDY #$AA ;Count up to 0 +PreNib4 EQU * +PrN1 LDA $1000,Y ;Fetch byte from lowest group. warning: self modified + AND #%00000011 ;Strip high 6 bits + TAX ;Index to 2 bit equiv + LDA TwoBit1,X + PHA ;save pattern +PrN2 LDA $1056,Y ;fetch from middle group + AND #%00000011 + TAX + PLA ;Restore pattern + ORA TwoBit2,X ;Combine second group with first + PHA ;Save new pattern +PrN3 LDA $10AC,Y ;Get highest group + AND #%00000011 + TAX + PLA ;Restore new pattern + ORA TwoBit3,X ; & form final nibble + PHA + TYA + EOR #$FF + TAX + PLA + STA nBuf2,X ;Save in nibble buffer! + INY ;Bump to next set + BNE PreNib4 ;Loop until all $56 nibbles formed + LDY bufPtr ;Now prepare data bytes for write16 routine + DEY ;Prepare end addr + STY yEnd + LDA bufPtr + STA WRefDr1+1 ;warning: the following storage addresses starting + BEQ WrMod1 ; with 'wref' are referces into code space, + EOR #$FF ; changed by this routine + TAY ;Index to last byte of page pointed to by buf + LDA (bufPtr),Y ;Pre-niblize the last byte of the page with + INY ; the first byte of the next page + EOR (bufPtr),Y + AND #$FC + TAX + LDA Nibbles,X ;Get disk 7-bit nibble equivalent +WrMod1 STA midNib1 + BEQ WrMod3 ;Branch if data to be written is page aligned + LDA yEnd ;Find out if last byte is even or odd address + LSR ;Shift even/oddness into carry + LDA (bufPtr),Y ;If even, then leave intact + BCC WrMod2 ;Branch if odd + INY ;If even, then pre-xor with byte 1 + EOR (bufPtr),Y +WrMod2 STA midNib2 ;Save result for write routine +WrMod3 LDY #$FF ;Index to last byte of data to be writen + LDA (bufPtr),Y ; to be used as checksum + AND #$FC ;Strip extra bits + STA lstNib ;Save it + LDY bufPtr+1 ;Now modify address reference to user data + STY WRefAdr1+2 + STY WRefAdr2+2 + INY + STY WRefAdr3+2 + STY WRefAdr4+2 + STY WRefAdr5+2 + STY WRefAdr6+2 + LDX slotZ ; & lastly index references to controller + STX WRefDr2+1 + STX WRefDr3+1 + STX WRefDr4+1 + STX WRefDr5+1 + RTS ;All done + +ChkPrev EOR iobPrevDn ;Same slot as last? + ASL + BEQ :Rtn ;Yes + LDA #$01 + STA monTimeH +:CkLoop LDA iobPrevDn ;=DSSS xxxx + AND #$70 ;=0SSS 0000 + TAX + BEQ :Rtn ;Branch if no previous ever (boot only) + JSR ChkDrv0 ;Find out if previous drive running + BEQ :Rtn ;Branch if stopped + LDA #$01 ;Waste some time + JSR msWait + LDA monTimeH + BNE :CkLoop +:Rtn RTS + +* ----------------- see rev notes 14, 18 & 70 ------------- + +ResetPhase LDA unitNum ;Get unit number + AND #$7F ;Map off hi bit (drive bit) + TAX + +* clear all the phases and force read mode +* patch 76 part 2. part 1 is in xrw1. + + LDA phaseOff,X ;make sure all motor + LDA phaseOff+2,X ; phases are off + LDA phaseOff+4,X + LDA phaseOff+6,X + RTS + +* --------------------------------------------------------- +doCheck LDA dhpCmd ;Get the command number + CMP #maxCmd ;Is the command allowable? + BCS doChkBad ;Branch if not! + LDA blockNum + LDX blockNum+1 + STX ibTrk ;Calculate block's track & sector + BEQ doChkOK ;Branch if block # in range + DEX ;else test further + BNE doChkBad ;Bad range + CMP #$18 ;Must be <$118 (280) + BCC doChkOK +doChkBad SEC ;Set carry for an error + RTS + +doChkOK CLC + RTS + +LD6EA DS 2,0 ;Not used + +* --------------------------------------------------------- +* Variables for handling mirror devices +* NB. Values of SmartPort unit #s: $00, $01-$7E +* Status List - ref pg 122 IIGS Firmware + +spStatList EQU * +genStatus DB 0 +spDevTotBlks DB 0,0,0 ;# of blocks + +spUnits DS $F,0 ;table of SmartPort unit #s + DB 0 \ No newline at end of file diff --git a/P8_SRC.2mg b/P8_SRC.2mg new file mode 100644 index 0000000000000000000000000000000000000000..c30f3e0d2d139d5769b2190054053b47246da43a GIT binary patch literal 819264 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