sys7.1-doc-wip/OS/HFS/TFSRFN1.a
2019-07-27 22:37:48 +08:00

611 lines
23 KiB
Plaintext

;
; File: TFSRFN1.a
;
; Contains: This file contains routines using refnums.
;
; Copyright: © 1982-1993 by Apple Computer, Inc., all rights reserved.
;
; Change History (most recent first):
;
; <SM5> 8/27/93 BH Removed <SM4>. The flushing stuff is now in CmdDone.
; <SM4> 8/3/93 BH Flushing critical volume info when changed for a volume in a
; manual-eject drive.
; <SM3> 10/26/92 CSS We now flush on chip caches after we do a read and before we
; do a write. Also, make a short branch into a word branch.
; <SM2> 5/21/92 kc Append "Trap" to the names of GetFPosTrap and SetFPosTrap to
; avoid name conflict with the glue.
; <SM1> 4/1/92 kc Checked in from Reality.
; • Pre-SuperMario comments follow •
; <2> 9/12/91 JSM Add a header.
; <1.4> 4/13/89 DNF Vectored FileRead and FileWrite.
; <1.3> 3/2/89 DNF removed references to forROM; HFS now builds identically for ram
; or rom
; <1.2> 11/21/88 CCH Replaced references to forRAM with “NOT forROM.”
; <1.1> 11/10/88 CCH Fixed Header.
; <1.0> 11/9/88 CCH Adding to EASE.
; <1.0> 2/11/88 BBM Adding file for the first time into EASE…
; 1/22/86 LAK Set CacheFlag before FlushCache call for read-verify flush.
; 1/14/86 LAK TFSBitTst doesn't trash D0 as before (needed for FileAlloc
; change).
; 10/8/85 LAK Fixed bug in DoPart for ROM (vectoring to BlockMove kills D1).
; 10/1/85 LAK Added label for TFSVCBTst. Rewrote to call cache for all I/O.
; Write no longer prefetches when extending the file. Read-verify
; calls cause the file fork to be flushed and blocks to be read
; read from disk rather than the cache.
; 9/21/85 LAK Changed for new TrashBlocks interface.
; 9/18/85 LAK Call TrashBlocks with D2=last block number, not last block+1.
; 9/8/85 LAK Slight modification to read code so it doesn't use FSTemp4.
; 9/6/85 LAK Tag data bug for Write-in-place fixed. MyRWSub uses longword D3
; for start disk block for TFS volumes (for > 32MB volumes).
; 9/2/85 LAK Release cache buffer immediately after read (read-verify error
; path wasn't releasing the buffer). Use MarkBlock to dirty buffer
; for TFS write . . .
; 8/31/85 LAK Removed TFS support of OwnBuf buffers . . . removed Pat's debug
; code.
; 8/19/85 PWD Tempered enthusiam displayed above to save only between calls to
; MyRWSub0 and GetRegs, NEVER AFTER GetRegs.
; 8/15/85 PWD Added code to save A6 before ,ASYNC calls
; 2/25/85 GJC new today
; 6/27/84 GSS Put in line the WrPart patch from MSFileFix
; 8/30/83 LAK Removed redundant set of FCB dirty bit in FileWrite (gets set by
; AdjEOF).
; 8/15/83 LAK Flushes file buffer now if block is dirty and falls within
; read-in-place . . . saved some bytes, too.
; 8/11/83 LAK Added support for a read-verify mode.
; 8/2/83 LAK Fixed read bug (read count 0 with position past EOF). Changed
; file positioning mode 2 (relative to end of file) by defining
; IOPosOffset to be either positive or negative . . .
; 6/2/83 LAK Changes for FCBBfAdr=0 meaning use volume buffer.
; 2/10/83 LAK Fixed bugs in Read with EOL character.
; 2/9/83 LAK Fixed MyWriteIP bug (now invalidates file's buffer if it happens
; to write that block . . .)
; 1/17/83 LAK Latest changes for final structures. Brought SetFPos and GetFPos
; here from FSRFN2. Made GetFPos and SetFPos call FileRead with
; zero bytes requested.
; 12/14/82 LAK Rewrote for new file system data structures.
;
;_______________________________________________________________________
;
; External Routines: FileRead,FileWrite,SetFPos,GetFPos
;
; Internal Routines: MyWrite,Seek,CVFlgs,MyReadBk,MyReadDB,MyReadIP
;
;_______________________________________________________________________
;_______________________________________________________________________
;
; Routines TFSBitTst, TFSVolTst, TFSVCBTst
; Arguments A0.L (input) -- pointer to I/O parameter block
; D0 (output) -- status
; Calls GetVCBRfn
; Called by Create
; Function determines whether or not the volume is TFS format
; (TFSVolTst) and whether or not TFS params are defined
; for this call (TFSBitTst).
;
; Modification History:
;
; 25-Feb-85 GJC new today
;_______________________________________________________________________
BLANKS ON
STRING ASIS
TFSBitTst BTST #HFSBit,IOtrap(A0) ; test TFS bit <14Jan86>
RTS ; on return, status .ne. means TFS
TFSVolTst MOVEA.L FCBVPtr(A1,D1.w),A2
TFSVCBTst CMPI.W #Tsigword,VCBSigWord(A2) ; is sigword that of TFS? <01Oct85>
RTS ; on return, status .eq. TFS vol
;
;_______________________________________________________________________
;
; Routine: Seek
; Arguments: A0.L (input) -- I/O parameter block pointer, uses: IOPosMode,
; IOPosOffset
; (A1,D1) (input) -- FCB ptr for the file in question
; D0.W (output) -- error code
; D2.W (output) -- distance past EOF (0 or - if not past EOF)
; D5.L (output) -- current file position
; D7.L (output) -- set to IOReqCount
; IOActCount cleared
; trashes D4
; Called By: FileRead,FileWrite
; Function: Seek is a utility routine that interprets the positioning
; parameters in an read or write call, updating the current
; file position in the FCB.
;
; There are 4 positioning modes:
;
; mode 0 -> no positioning
; mode 1 -> relative to beginning (absolute)
; mode 2 -> relative to end of file
; mode 3 -> relative to current
;
; If the file positioning would cause the current position to
; become negative, it is not changed and an error
; (PosErr) is returned; otherwise, result code 0
; returned. Read and Write must check for positioning past
; current EOF since they handle that case differently (read
; reports an error, write extends the file). This routine will
; clip the position to the end-of-file but returns a remainder
; in D2.
;
; The IOReqCount field must be positive; IOPosOffset may be
; positive or negative . . .
;
; Modification History:
; 14 Dec 82 LAK Rewrote to reflect new file system data structures. Changed
; to handle positioning past end-of-file differently, reports
; error when positioning to before file start.
; 15 Aug 83 LAK Now trashes D4 and passes back file position in D5.
;
; USoft memo Aug 10: A read with position past end of file should return an
; error; a write should allocate space on the disk up to the specified
; position. Read and write should return the current file position in the
; in the parameter block after reads and writes regardless of positioning mode.
;
;_______________________________________________________________________
Seek
CLR.L IOActCount(A0) ; haven't actually done any bytes yet.
MOVEQ #ParamErr,D0 ; assume bad count
MOVE.L IOReqCount(A0),D7 ; # bytes to read/write
BMI.S SkExitRTS ; exit with error if negative
MOVE.L IOPosOffset(A0),D2 ; position value the user supplied
MOVE.L FCBCrPs(A1,D1),D5 ; current file position
MOVE.L FCBEOF(A1,D1),D4 ; get the current EOF
MOVE.B IOPosMode+1(A0),D0 ; the position mode is in bits 0,1
ROXR.B #2,D0 ; get position bits in carry, sign
BCC.S PM01 ; br if 0 or 1
BMI.S PM3 ; br if 3
; relative to the end of the file (mode 2)
MOVE.L D4,D5 ; current EOF
; relative to current position (mode 3)
PM3 ADD.L D2,D5
BRA.S SkExit
PM01 BPL.S SkExit ; br if no position operation (mode 0)
; relative to start (mode 1)
MOVE.L D2,D5 ; relative to start is absolute
SkExit BPL.S @1 ; did D5 go negative? br if not
MOVEQ #PosErr,D0 ; report positioning error
BRA.S SkExitRTS ; and don't change current position
@1 MOVE.L D5,D2 ; new position
SUB.L D4,D2 ; distance past end of file (pos-EOF)
BLE.S @2 ; br if not past the end
MOVE.L D4,D5 ; pin to EOF
@2 MOVE.L D5,FCBCrPs(A1,D1) ; set the new current position
MOVEQ #0,D0 ; no error
SkExitRTS RTS
;_______________________________________________________________________
;
; Routine: GetFPos
;
; (c) 1983 Apple Computer, Inc.
;
; Arguments: A0.L (input) -- pointer to I/O parameter block; uses:
; IORefNum,IOPosOffset
; D0.W (output) -- 0 if file was found, error code otherwise.
; Calls:
; Function: Get current file position for specified opened file
;
; Modification History:
; 10 Dec 82 LAK Modified for new file system data structures.
; 17 Jan 83 LAK Modified to call FileRead via SetFPos to do the work.
;
;_______________________________________________________________________
GetFPosTrap:
CLR.B IOPosMode+1(A0) ; no file positioning
; Fall thru to SetFPos
;_______________________________________________________________________
;
; Routine: SetFPos
;
; (c) 1983 Apple Computer, Inc.
;
; Arguments: A0.L (input) -- pointer to I/O parameter block; uses:
; IORefNum,IOPosOffset
; D0.W (output) -- 0 if file was found, error code otherwise.
; Calls:
; Function: Set current file position for specified opened file. Will
; not set the position past end of file. Uses positioning modes
; like FileRead.
;
; Modification History:
; 10 Dec 82 LAK New Today.
; 17 Jan 83 LAK Modified to call fileread.
;
;_______________________________________________________________________
SetFPosTrap:
CLR.L IOReqCount(A0) ; no bytes
; fall thru to FileRead
;_______________________________________________________________________
;
; Routine: FileRead
; Arguments: A0.L (input) -- I/O parameter block pointer, uses: IORefnum,
; IOBuffer,IOReqCount,IOActCount,IOPosMode,IOPosOffset
; D0.W (output) -- error code
; Calls: FSQueue,RFnCall,Seek,Lg2Phys,MyRead,CmdDone
; Function: General purpose file read call.
;
; Modification History:
; 15 Dec 82 LAK Rewrote to reflect new file system data structures;
; Separated reading with EOL mode from regular reading (now
; checks for EOL character whenever specified).
;
; 11 Mar 85 GJC Added modifications so FileRead can handle the caching scheme
; Used by TFS
; 16 May 85 PWD Set up options on FlushCache call.
;
; IOPosMode(A0).W = Newline char in bits 8-15, newline flag (1=enabled) in
; bit 7, bits 0-1 are position information
;
;
; - if positions to EOF and 0 bytes are requested, EOF is not reported.
; (reading exactly to EOF will not raise EOF condition unless byte count
; is not satisfied because EOF hit)
;_______________________________________________________________________
IMPORT CacheFlush ; <SM3> CSS
FileRead:
MOVE.L jFileRead,-(SP) ; jumptable entry for vFileRead <dnf v1.4>
RTS ; go there
vFileRead: ; 'vectored' FileRead routine
BSR FSQueue ; wait our turn
BSR RFnCall ; map the reference number
BNE FRdExit1 ; exit on errors (don't set IOPosOffset)
; (also exit for offline, ext fs calls)
BSR.S Seek ; do any positioning - D5 gets cur pos,
; IOActCount cleared, D7 set to
; IOReqCount
BNE.S FRdExit0 ; exit if positioned to before file start
; figure min(requested count,distance to EOF)
NEG.L D2 ; EOF-current pos (from seek)
BMI.S FRdEOFXit ; if negative, report end of file
CMP.L D2,D7 ; number of bytes desired within EOF?
BLE.S @1 ; br if so
MOVE.L D2,D7 ; just read distance to EOF
@1 TST.L D7 ; byte count 0?
BEQ.S FRdOK ; all done if none to read!
MOVE.W IOPosMode(A0),D2 ; get newline stuff
LSR.W #8,D2 ; newline char in low byte, enb in carry
BCS FRNewLine ; if newline enabled, do it differently <SM3> CSS
BTST #6,IOPosMode+1(A0) ; read verify? <01Oct85>
BEQ.S @2 ; br if not <01Oct85>
MOVEM.L A1/D1,-(A6) ; save A1/D1 across FlushCache call <01Oct85>
MOVE.W D1,D0 ; set up to make a flushcache for the file <01Oct85>
MOVEQ #0,D1 ; just normal call . . . <01Oct85>
MOVEA.L VCBBufAdr(A2),A1 ; point to volume buffer <01Oct85>
ST CacheFlag ; make sure we flush <22Jan86>
JSR FlushCache ; write all file blocks before read-verify <01Oct85>
MOVEM.L (A6)+,A1/D1 ; and trash so we actually read them <01Oct85>
@2 MOVE.W D5,D2 ; current file position (low word) <01Oct85>
AND.W #$01FF,D2 ; check lower 9 bits for blk bndry read
BEQ.S RDMain ; br if so (see if we can read into place)
; must read a funny amount of data (not a full block's worth)
RdPart BSR RdFileBlk ; read the data (asynchronously) <01Oct85>
BNE.S FRdExit ; exit on errors
; the data block is at (A5). Move it into user buffer byte by byte.
BTST #6,IOPosMode+1(A0) ; read verify only?
SNE D0 ; $00 if not, $FF if so
BSR DoPart ; share code with similar write code
BNE.S FRdExit ; exit on verify errors
; read multiple blocks into place in user's buffer
RdMain MOVE.L D7,D4 ; number of bytes we want.
BEQ.S FRdOK ; all done if no more bytes.
AND.W #$FE00,D4 ; only read whole blocks (pass D4=max bytes to read)
TST.L D4 ; any whole blocks to read?
BEQ.S RdPart ; br if not . . .
JSR CacheRdIP ; pass: A2=VCB ptr,(A1,D1.W)=FCB pointer
; D5.L=cur file pos, D4.L=max bytes to read
; A0=IO Param Blk ptr
; returns: D6=bytes read, D0=result code
BNE.S FRdExit ; disk errors deserve an exit
BSR UpdteCntPos ; using D6, update D7, D5, and IOActCount(A0)
BRA.s RdMain ; loop.
FRdOK MOVEQ #0,D0 ; no errors (except maybe EOF)
FRdExit TST.W D0 ; other error?
BNE.S FRdExit0 ; br if so
MOVE.L IOActCount(A0),D2 ; if actual count is not equal to
CMP.L IOReqCount(A0),D2 ; requested count, and no other errors,
BEQ.S FRdExit0 ; (br if not true)
FRdEOFXit MOVEQ #EOFErr,D0 ; then we must have hit end of file . . .
FRdExit0 MOVE.L D5,FCBCrPs(A1,D1) ; update current position in FCB
MOVE.L D5,IOPosOffset(A0) ; always return current pos
FRdExit1 BSR.L CacheFlush ; <SM3> CSS
BRA CmdDone ; bye now . . .
; reading while looking for end of line characters . . . do it a block at a time
FRNewLine BSR.S RdFileBlk ; read the data (asynchronously) <01Oct85>
BNE.S FRdExit ; exit on errors
; the data block is at (A5). Move it into user buffer byte by byte.
@5 MOVE.L D5,D2 ; current file position
AND.W #$01FF,D2 ; get the index
MOVE.L IOBuffer(A0),A3 ; user buffer
MOVE.L IOActCount(A0),D4 ; where to start continuing read
@10 MOVE.B 0(A5,D2.W),D0 ; get byte from the buffer
MOVE.B D0,0(A3,D4.L) ; put it into user's area
ADDQ.L #1,IOActCount(A0) ; actually read another byte!
ADDQ.L #1,D4 ; incr destination byte offset
ADDQ.W #1,D2 ; incr source byte offset
ADDQ.L #1,D5 ; incr current position
CMP.B IOPosMode(A0),D0 ; is it the EOL character?
BNE.S @20
MOVEQ #0,D0 ; no error, we found EOL!
BRA.S FRdExit0
@20 SUBQ.L #1,D7 ; decr byte count
BEQ.S FRdOK ; exit, checking for EOF condition
CMP.W D6,D2 ; reached end of buffer?
BCS.S @10 ; loop if not
BRA.S FRNewLine ; get another block
;_______________________________________________________________________
;
; Routine: RdFileBlk, PreRdFileBlk
; Arguments: A0.L (in ) -- I/O Param Blk Ptr (RdFileBlk)
; A2.L (in ) -- VCB ptr
; D5.L (in ) -- file position to read block from
; D0.W (out) -- error code
; D6.L (out) -- const 512 (byte count)
; A5.L (out) -- buffer pointer
; A6.L -- TFS stack pointer
; Clobbers:
; All other registers are preserved
; This routine returns to one level above caller during the
; read(caller must not have anything on the stack when
; issuing the call!)
; Called By: FileRead,FileWrite
; Function: Calls cache routines GetBlock and RelBlock to get a block
; of the file.
;
;
; Modification History:
; <01Oct85> LAK New today. Built from old MyReadBk.
;_______________________________________________________________________
PreRdFileBlk
BSR.S ReadBlock
CMP.L D3,D5 ; read position >= old LEOF rounded? <01Oct85>
BCS.S RdFileB1 ; br if not <01Oct85>
MOVEQ #kGBNoRead,D1 ; don't bother to read it then <01Oct85>
BRA.S RdFileB1 ; <01Oct85>
RdFileBlk
BSR.S ReadBlock ;
BTST #6,IOPosMode+1(A0) ; read verify? <01Oct85>
BEQ.S RdFileB1 ; br if not <01Oct85>
MOVEQ #kGBRead,D1 ; always read it if so <01Oct85>
RdFileB1
MOVEA.L VCBBufAdr(A2),A1 ; Use a TFS volume cache buffer <01Oct85>
MOVE.L D5,D2 ; Byte position <01Oct85>
LSR.L #8,D2 ; 'Divide' by 256 <01Oct85>
LSR.L #1,D2 ; by 512 to get long file block # <01Oct85>
RTS ; return to ReadBlock <01Oct85>
; Called by: MFSDirRead, MFSMapRead, RdFileBlk
ReadBlock
MOVEM.L A0-A1/D1-D2,-(A6) ; save status <01Oct85>
MOVE.L (SP)+,A1 ; 'set up routine' address <01Oct85>
MOVE.L (SP)+,-(A6) ; Save return address <01Oct85>
MOVE.W D1,D0 ; file refnum
MOVEQ #0,D1 ; flag <01Oct85>
MOVEQ #2,D6 ; always return D6=512 for historical <01Oct85>
LSL.L #8,D6 ; reasons . . . <01Oct85>
JSR (A1) ; fill in A1=Cache Ptr, D2.L=block num, <01Oct85>
; D0.W=refnum (file or volume). <01Oct85>
ADDQ.B #kGBrelease,D1 ; immediate release <01Oct85>
JSR GetBlock ; get the cache buffer <01Oct85>
MOVEA.L A0,A5 ; return A5->released buffer
MOVE.L (A6)+,-(SP) ; pop ret address off TFS stack
MOVEM.L (A6)+,A0-A1/D1-D2 ; restore status
TST.W D0 ; any errors?
RTS ; return to caller with error code
;_______________________________________________________________________
;
; Routine: FileWrite
; Arguments: A0.L (input) -- I/O parameter block pointer, uses: IORefnum,
; IOBuffer,IOReqCount,IOActCount,IOPosMode,IOPosOffset
; D0.W (output) -- error code
; Calls: FSQueue,TstMod,RFnCall,Seek,Lg2Phys,MyWriteIP,MyWriteDB,CmdDone
; Function: Write user data to a file, extending the file as necessary.
;
; Modification History:
; 17 Dec 82 LAK Rewrote to reflect new file system data structures;
; When file is initially positioned past end-of-file, the
; file is automatically extended enough for the write.
;
;_______________________________________________________________________
IMPORT CacheFlush ; <SM3> CSS
FileWrite:
MOVE.L jFileWrite,-(SP) ; jumptable entry for vFileWrite <dnf v1.4>
RTS ; go there
vFileWrite: ; 'vectored' FileWrite routine
BSR.L CacheFlush ; flush the chip caches so data is in memory. <SM3> CSS
BSR FSQueue ; don't do the call until time.
BSR TstMod ; test if we may modify this file.
BNE.S FWrExit1 ; br if not.
BSR Seek ; share this routine with read - D5 gets
; cur file position, IOActCount cleared,
; D7 set to IOReqCount
BNE.S @2 ; br if tried to position before start
; (to FWrExit0)
TST.L D2 ; positioned past EOF?
BMI.S @1 ; br if not
ADD.L D2,D5 ; extend added to cur pos
; set up D3 so PreRdFileBlk can tell when a block actually has to be read
@1 MOVE.L #511,D3 ; <01Oct85>
ADD.L D4,D3 ; add current LEOF from Seek <01Oct85>
AND.W #$FE00,D3 ; LEOF rounded to next block boundary <01Oct85>
; see if file has to be made physically longer
MOVE.L D5,D4 ; current position after positioning
ADD.L D7,D4 ; end byte for write + 1
CMP.L FCBEOF(A1,D1),D4 ; will it fit within the old EOF?
BLS.S SameLen ; if so, leave file at same phys length
SUB.L FCBPLen(A1,D1),D4 ; is it past the phys end?
BLS.S @3 ; no, just reset the eof
; ran off the physical end of the file--need to allocate more blocks
; Call alloc with D4=number of extra bytes we need, A2=addr of VCB,
; (A1,D1) ptr to FCB . . .
BSR Alloc ; do it to it (sets FCB PEOF, start blk)
@2 BNE.S FWrExit0 ; exit if didn't get it (return with
; cur file pos = EOF)
; now just update the logical len.
@3 MOVE.L D5,D4 ; current position after positioning
ADD.L D7,D4 ; plus # bytes to write
MOVE.L D4,FCBEOF(A1,D1) ; is new logical end-of-file
; the data will fit into the file. At this point:
; A0=ptr to params,
; (A1,D1) = ptr to file's FCB
; A2 = ptr to VCB
; D7 = # bytes to write total
; D5 = current file position.
SameLen MOVE.W D5,D0 ; check if writing to a block boundary
AND.W #$01FF,D0 ; if so, low 9 bits will be 0
BEQ.S WrMain ; if so, try writing full blocks out
; must write some funny amount of data
WrPart BSR PreRdFileBlk ; get the appropriate block <01Oct85>
BNE.S FWrExit ; just skip if bad read
; the destination data block is at (A5). Move it out of user buffer byte by byte.
MOVEQ #1,D0 ; D0=1 for write
BSR.S DoPart ; share code with fileread
JSR MarkA5Block ; mark the block dirty <01Oct85>
; This section tries to write out entire blocks and/or groups of blocks
WrMain MOVE.L D7,D4 ; number bytes we want.
BEQ.S FWrOK ; all done if no more bytes.
AND.W #$FE00,D4 ; only write whole blocks
TST.L D4 ; must want at least 512 bytes
BEQ.S WrPart ; br if not . . .
JSR CacheWrIP ; pass: A2=VCB ptr,(A1,D1.W)=FCB pointer
; D5.L=cur file pos, D4.L=max bytes to read
; A0=IO param blk ptr
; returns: D6=bytes read, D0=result code
BNE.S FWrExit ; disk errors deserve an exit
BSR.S UpdteCntPos
BRA.S WrMain ; loop.
FWrOK MOVEQ #0,D0 ; no errors
FWrExit BSR AdjEOF ; adjust other FCBs for this file
MOVE.L D5,FCBCrPs(A1,D1) ; update current position in FCB
FWrExit0 MOVE.L FCBCrPs(A1,D1),IOPosOffset(A0) ; always return current pos
FWrExit1 BRA CmdDone ; command is done.
UpdteCntPos
SUB.L D6,D7 ; adjust for number of bytes read
ADD.L D6,IOActCount(A0) ; actually read this many more
ADD.L D6,D5 ; update current position
RTS
; code sharing routine . . . D6 = 512 on entry
DoPart
MOVE.L D5,D2 ; current file position
AND.W #$01FF,D2 ; get the index
SUB.W D2,D6 ; bytes in blk from cur pos
CMP.L D6,D7 ; more than we want?
BCC.S @1
MOVE.L D7,D6 ; if so, only r/w num requested
@1 MOVE.L IOBuffer(A0),A3 ; user buffer
ADD.L IOActCount(A0),A3 ; where to start continuing read/write
BSR.S UpdteCntPos
MOVEM.L D1/A0-A2,-(SP) ; preserve blockmove registers <08Oct85>
MOVE.L A3,A0 ; src for writes
MOVE.L A5,A1
ADD D2,A1 ; dest for writes
TST.B D0 ; read or write?
BGT.S @2 ; br for write
BMI.S @5 ; br for read verify
EXG A0,A1 ; switch src, dest for reads
@2 MOVE.L D6,D0 ; byte count
MOVE.L JBlockMove, A2 ; <01Oct85>
JSR (A2) ; <01Oct85>
@3 MOVEQ #0,D0 ; no errors
@4 MOVEM.L (SP)+,D1/A0-A2 ; restore regs <08Oct85>
RTS
@5 MOVEQ #IOErr,D0 ; assume error
SUBQ.W #1, D6 ; for DBNE loop <01Oct85>
@6 CMPM.B (A0)+,(A1)+ ; compare next byte
DBNE D6,@6 ; exit on errors or done <01Oct85>
BNE.S @4 ; br if err <01Oct85>
BRA.S @3 ; and take the good route if done