<< Home | About Forth | About TurboForth | Download | Language Reference | Resources | Tutorials | YouTube >>
; ____ _ _ _____ ______ __ __ _ ; | _ \| | | | |_ _| / / __ \ \ \ / / | | ; | |_) | | ___ ___| | __ | | / / | | | \ \ /\ / /___ _ __ __| |___ ; | _ <| |/ _ \ / __| |/ / | | / /| | | | \ \/ \/ // _ \| '__/ _` / __| ; | |_) | | (_) | (__| < _| |_ / / | |__| | \ /\ /| (_) | | | (_| \__ \ ; |____/|_|\___/ \___|_|\_\ |_____/_/ \____/ \/ \/ \___/|_| \__,_|___/ ; block file system words & subroutines ; Some heavy stuff in here. In here be demons. ; Turn back all ye faint of heart... ; pab opcodes open equ 0 ; open opcode close equ >1 ; close opcode read equ >2 ; read opcode write equ >3 ; write opcode fwdrew equ >4 ; restore/rewind opcode (fwd/rew) status equ >9 ; status op-code ; USE ( addr len -- ) ; Tells the system which block file to use for block IO ; e.g. USE DSK1.BLOCKS ; Simply sets the filename and length in the blockIO PAB ; Syntax: S" DSKn.FILENAME" USE _use mov *stack+,r2 ; length of filename swpb r2 ; move to MSB mov *stack+,r0 ; address of file name li r9,pabnln ; address of filename length in blockIO PAB movb r2,*r9+ ; write length to PAB length byte, now ; pointing at filename swpb r2 ; move to LSB _use3 movb *r0+,*r9+ ; copy byte of filename to pab dec r2 ; finished copying? jne _use3 ; repeat if not ; clear all blk pointers... clr @lstblk clr @blk0 clr @blk1 clr @blk2 clr @blk3 clr @blk4 clr @blk5 usexit b @retB0 ; BLOCK ( block# -- addr ) ; Brings a block into a buffer, if not already in memory ; 1) If already in memory, the block is not re-loaded from device ; 2) If not in memory: ; 3) Scans for a free buffer ; 4) If no free buffer: ; 5) flush all buffers back to device ; 6) Repeat from 3 ; 7) If free buffer: ; 9) Load block from device into free buffer ; 10) Return address of buffer ; 11) If disk error, or block not found etc, return 0 ; Note: If a block number of 0 is given 0 is returned _block clr @errnum ; clear last disk io error mov *stack,r0 ; block number in r0 for scnblk jeq usexit ; if zero then just exit mov r0,@lstblk ; update last block accessed (for UPDATE) bl @scnblk ; see if the block is already in memory mov r1,r1 ; check returned result jeq blknim ; block is not in memory inct r1 ; block is in memory. point to vdp address ; pointer mov *r1,*stack ; place vdp address on stack jmp usexit ; exit ; look for a free buffer blknim bl @frebuf ; block is not in memory, scan for a buffer mov r0,r0 ; check returned result jeq bnfb ; jump if no free buffers ; we have a free buffer, it's blk address is in r0... blkfb mov *stack,*r0 ; update block indicator in block buffer mov r0,r8 ; copy blk address mov *r0,r2 ; copy block number dec r2 ; reduce by one (so we can use block 0) sla r2,3 ; calculate record number (block no. x 8) inct r0 ; point to vdp address mov *r0,r9 ; save vdp address mov r9,*stack ; place vdp address on stack ; put the pab into vdp ram, with an open opcode and open the file... li r1,>8000 ; logical record length: 128 bytes (in msb) movb r1,@pablrl ; set logical record length in pab mov r2,@pabrec ; set record number in PAB mov r9,@pabbuf ; address to load data into in VDP bl @diskio ; witchcraft byte open,5 ; dis/fix input jeq blkerr ; jump if an an error occurred ; read 8 128 byte records (1K)... li r7,8 ; 8 records to read blknxt bl @diskio ; call disk system byte read,5 ; dis/fix input jeq blkerr ; jump if an an error occurred ai r9,128 ; increment vdp address mov r9,@pabbuf ; address to load data into in VDP inc @pabrec ; set next record in PAB dec r7 ; finished reading all the records? jne blknxt ; repeat if not bl @diskio ; more alchemy byte close,5 ; dis/fix input bl @rstsp ; restore code in scratchpad ; (destroyed by DSR access) jmp usexit ; exit ; no free buffers :-( we need to do a flush... bnfb bl @flush1 ; flush all our buffers to device li r0,blk0 ; point to first (which is now free) block jmp blkfb ; repeat ; an error occurred, return 0 on the stack blkerr clr *stack ; zero the TOS swpb r0 mov r0,@errnum ; set disk io error number bl @diskio ; close the file byte close,5 bl @rstsp ; restore code in scratchpad ; (destroyed by DSR access) jmp usexit ; exit ; FLUSH ( -- ) ; Flushes all dirty blocks back to disk ; If a blocks' DIRTY flag is set, the block is physically written back to disk. ; If the block is NOT dirty, it's (BLK) status is simply set to un-used. ; Sets DSKERR to reflect disk DSR error status (0=no error) _flush bl @flush1 flushx jmp usexit flush1 clr @errnum ; reset last disk io error mov r11,r14 ; save return address of caller ; li r6,6 ; 6 buffers to check mov @totblk,r6 ; number of buffers to check li r7,blk0+2 ; start with the first vdp address pointer flnext mov *r7,r8 ; get address andi r8,>8000 ; check dirty flag jeq flush2 ; if 0, not dirty, just reset pointers ; else flush to disk... li r1,>8000 ; logical record length: 128 bytes (in msb) movb r1,@pablrl ; set logical record length clr @pabrec ; set record number to 0 bl @diskio byte open,1 ; dis/fixed update jeq flerr ; jump if error li r12,8 ; 8 128 byte records (1024 bytes) mov *r7,r8 ; vdp address andi r8,>7fff ; remove dirty bit mov @-2(r7),r1 ; get block number dec r1 ; account for blocks starting at 1 sla r1,3 ; convert to record count mov r1,@pabrec ; set record number flnrec mov r8,@pabbuf ; set source vdp address bl @diskio ; write the record to disk byte write,1 ; dis/fix update jeq flerr ; jump if error ai r8,128 ; next 128 bytes of vdp inc @pabrec ; next record on disk dec r12 ; decrement counter jne flnrec ; loop if not finished bl @diskio ; close the file byte close,1 ; dis/fix update jeq flerr ; jump if error ; reset blk & dirty flag... flush2 clr @-2(r7) ; clear blk indicator mov *r7,r1 ; get vdp address from pointer andi r1,>7fff ; reset dirty bit mov r1,*r7 ; write it back ; loop back for remaining blks... ai r7,4 ; point to next vdp address dec r6 ; finished? jne flnext ; repeat if not flexit li r12,_next ; restore pointer to NEXT bl @rstsp ; restore code in scratchpad (destroyed by ; DSR access) b *r14 ; return to caller ; an error occurred... exit... flerr swpb r0 ; move error into low byte mov r0,@errnum ; set DSKERR with error code bl @diskio ; set the file to closed byte close,1 jmp flexit ; UPDATE ( -- ) ; marks the last accessed block as dirty so that it will subsequently be flushed ; to disk. _updat mov @lstblk,r0 ; get current block bl @scnblk ; locate it (blk address in r1) inct r1 ; point to VDP address pointer mov *r1,r0 ; get the VDP address ori r0,>8000