IMD 1.16: 1/06/2007 13:24:22 FOGCPM.194 --FOGCPM194-06-12 88 -CPM194 DOCBIOSTIMEZ8DCONTENTSZ8D DATEDEMOZ80% DATEHL RELDATEHL Z80FUNCTIONZ8DPPIP-CPMCOMPPIP-Z3 COM! RCPCP LIB8!"#$%&'SAVESTMPCOM(SAVESTMPZ80%)*+,-TDIR-CPMCOM./0TDIR-Z3 COM12TELL COM 34TIME COM5Z80DCHARZ806789:;<=>?@ABCDEZ80DCHARZ80FZ80DPROGNOTjZ80DDISKZ80GHIJKLMNOPQRSTUVZ80DDISKZ80WXYZ[\]^_`abcdefZ80DDISKZ80[ghijklmnopqrZ80DHDR LIB!stuvwZ80DOS BLD=xyz{|}~Z80DOS10DOCZ80DOS10DOCZ80DOS10FORZ80DOS10Z80Z80DTIMEZ806ZF10GD5 COMxThis is the disk name. ts from assembly language source files STRIP .COM: C0 38 STRIP .DOC: 35 3E WSDODDD(DTTT(D((DDDDD<8| | ` ` ` TUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU This is the release date of the disk. Z80DCHARZ80 6C80DCHARZ80 FZ80DPROGNOT 5Z80DDISKZ80 G80DDISKZ80 W@80DDISKZ80 g-Z80DHDR LIB sZ80DOS BLD xZ80DOS10DOC @80DOS10DOC Z80DOS10FOR Z80DOS10Z80 Z80DTIMEZ80 ZF10GD5 COM <BIOSTIME.Z8D 42 92 1920 15 FUNCTION.Z8D 8B CC 3200 25 RCPCP .LIB 55 D8 7168 56 DATEDEMO.Z80 DD D3 4736 37 DATEHL .REL A8 7E 256 2 DATEHL .Z80 9C FF 2816 22 PPIP-CPM.COM 68 52 3840 30 PPIP-Z3 .COM 51 E5 4224 33 SAVESTMP.COM C9 19 896 7 SAVESTMP.Z80 97 A2 4736 37 TDIR-CPM.COM 5C 94 2304 18 TDIR-Z3 .COM 28 4B 2048 16 TELL  Fog Library Disk FOG-CPM.194 Copyright (1988) by Fog International Computer Users Group to the extent not copyrighted by the original author for the exclusive use and enjoyment of its members. Any reproduction or distribution for profit or personal gain is strictly forbidden. For information, contact FOG, P. O. Box 3474, Daly City, CA. 94015-0474. as part of the description of a file indicates that the program is distributed on a "try first, pay if you like it" basis. If you find the program(s) meet your need, please refer to the author's documentation for information on becoming a registered user. Only by registering and paying for the programs you like and use will the authors of such programs continue development. Often, more complete documentation, additional modules, and new releases are available only to registered users. This disk contains a replacement BDOS for CP/M systems. Filename Description -06-12 .88 This is the release date of the disk. -CPM194 .DOC This is the description of the disk contents. Z80DOS10.DOC 5B01 17K ver. 1.0 [Z80DOS 1 of 25] A CP/M compatible Z-80 Disk Operating System featuring enhanced integral file date and time stamping. Supports all standard CP/M 2.2 BDOS functions with some additions. Z80DOS10.FOR F397 1K ver. 1.0 [Z80DOS 2 of 25] Z80DOS10.Z80 04F9 1K ver. 1.0 [Z80DOS 3 of 25] Z80DPROG.NOT 9808 14K ver. 1.0 [Z80DOS 4 of 25] Z80DTIME.Z80 7A7C 7K ver. 1.0 [Z80DOS 5 of 25] ZF10GD5 .COM 28B8 15K ver. 1.0 [Z80DOS 6 of 25] BIOSTIME.Z8D 4292 2K ver. 1.0 [Z80DOS 7 of 25] CONTENTS.Z8D 330D 2K ver. 1.0 [Z80DOS 8 of 25] DATEDEMO.Z80 DDD3 5K ver. 1.0 [Z80DOS 9 of 25] DATEHL .REL A87E 1K ver. 1.0 [Z80DOS 10 of 25] DATEHL .Z80 9CFF 3K ver. 1.0 [Z80DOS 11 of 25] FUNCTION.Z8D 8BCC 4K ver. 1.0 [Z80DOS 12 of 25] PPIP-CPM.COM 6852 4K ver. 1.0 [Z80DOS 13 of 25] PPIP-Z3 .COM 51E5 5K ver. 1.0 [Z80DOS 14 of 25] RCPCP .LIB 55D8 7K ver. 1.0 [Z80DOS 15 of 25] SAVESTMP.COM C919 1K ver. 1.0 [Z80DOS 16 of 25] SAVESTMP.Z80 97A2 5K ver. 1.0 [Z80DOS 17 of 25] TDIR-CPM.COM 5C94 3K ver. 1.0 [Z80DOS 18 of 25] TDIR-Z3 .COM 284B 2K ver. 1.0 [Z80DOS 19 of 25] TELL .COM 568C 2K ver. 1.0 [Z80DOS 20 of 25] TIME .COM 8EAC 1K ver. 1.0 [Z80DOS 21 of 25] Z80DCHAR.Z80 0B5E 17K ver. 1.0 [Z80DOS 22 of 25] Z80DDISK.Z80 947D 44K ver. 1.0 [Z80DOS 23 of 25] Z80DHDR .LIB 4C5A 5K ver. 1.0 [Z80DOS 24 of 25] Z80DOS .BLD E824 8K ver. 1.0 [Z80DOS 25 of 25] 0DOS 16 of 25] SAVESTMP.Z80 97A2 5K ver. 1.0 [Z80DOS 17 of 25] TDIR-CPM.COM 5C94 3K ver. 1.0 [Z80DOS 18 of 25] TDIR-Z3 .COM 284B 2K ver. 1.0 [Z80DOS 19 of 25] TELL .COM 568C 2K ver. 1.0 [Z80DOS 20 of 25] TIME .COM 8EAC 1K ver. 1.0 [Z80DOS 21 of 25] Z80DCHAR.Z80 0B5E 17K ver. 1.0 [Z80DOS 22 of 25] Z80DDISK.Z80 947D 44K ver. 1.0 [Z80DOS; Z80DOS - Z80 Disk Operating System ; ; Version 1.0 - 05 Sept. 87 by Carson Wilson ; ; ; BIOSTIME.Z8D - Example BIOS inserts to implement Z80DOS time ; routine at BIOS+72. This is from the Morrow MD3 BIOS. Address ; of "jp time" will vary with system. ; ; timebuf equ 50h ; Address of time storage at page zero ; or other available protected RAM area ; ; Begin BIOS code: ; START: JP BOOT ;BIOS WBOT: JP WBOOT ;BIOS+3 JP CONST ;BIOS+6 JP CONIN ;+9 JP CONOUT ;+12 JP LST ;+15 JP PUN ;+18 JP PTR ;+21 JP HOME ;+24 JP SELDSK ;+27 JP SETTRK ;+30 JP SETSEC ;+33 JP SETDMA ;+36 JP READ ;+39 JP WRITE ;+42 JP LISTST ;+45 JP SECTRAN ;+48 ; JP CVMSG ;+51 CHANGE VIRTUAL DRIVE MESG. ; JP RDBLK ;+54 DIRECT DISK READ JP WRBLK ;+57 DIRECT DISK WR JP DISCIO ;+60 DIRECT DISK I/O ; DB REV DW RAMDATX DW RAMYDAT DW MTAB DW XLTAB ; ; Insert jump to time routine at first available place in BIOS: ; jp time ;BIOS + 72 = get time ; ; BIOS + 72 will be the number for BIOStim in Z80DHDR.LIB. ; ; ; Insert time routine between other routines in BIOS. ; ; Example time routine for systems without real time clock: ; ; Inputs: C <> 0: 5-byte entry pointed to by HL sets system time ; HL points to 5-byte time buffer of the form: ; ; HL + 0 = low byte days since December 31, 1977 in hex ; HL + 1 = high byte " " " " " " " ; HL + 2 = BCD hours ; HL + 3 = BCD minutes ; HL + 4 = BCD seconds ; ; C = 0: On return HL points to 5-byte time entry ; time: ld a,c ; Test flag or a jr nz,set ; C <> 0, set time ld hl,timebuf ; Point HL to buffer ret set: ld de,timebuf ; Point to storage ld b,5 ; Copy five bytes ldir ret ; ; ; END BIOSTIME.Z8D RAMYDAT DW MTAB DW XLTAB ; ; Insert jump to time routine at first available place in BIOS: ; jp time ;BIOS + 72 = get  Z80DOS10.LBR contains these files: ---------------------------------- BIOSTIME.Z8D - Sample BIOS time routine. CONTENTS.Z8D - This file. DATEDEMO.Z80 - Example program using DATEHL, SYSLIB, and Z80DOS. DATEHL .REL - Linkable routine to interpret Z80DOS date stamps. DATEHL .Z80 - Source for above. FUNCTION.Z8D - Function table for Z80DOS. INITDIR .COM - Initializes directories for time stamping. PPIP-CPM.COM - File copy utility for Z80DOS and CP/M. PPIP-Z3 .COM - File copy utility for Z80DOS and ZCPR3. RCPCP .LIB - Copy module for ZCPR3 RCP which copies Z80DOS stamps. SAVESTMP.COM - Copies Z80DOS file create dates. SAVESTMP.Z80 - Source. TDIR-CPM.COM - Directory utility for Z80DOS and CP/M. TDIR-Z3 .COM - Directory utility for Z80DOS and ZCPR3. TELL .COM - Tells where your system is in memory. TIME .COM - Sets clock on Z80DOS system. Z80DOS10.FOR - Short message describing Z80DOS10.LBR. Z80DOS10.Z80 - Source code for Z80DOS, main module. Z80DCHAR.Z80 - Jump table, character I/O routines, and error routines. Z80DDISK.Z80 - Disk routines. Z80DHDR .LIB - User customizable equates and options. Z80DTIME.Z80 - Time routines and RAM area. Z80DOS .BLD - Sample build of Z80DOS system from CP/M system. Z80DOS10.DOC - Introduction to Z80DOS. Z80DPROG.NOT - Notes for programmers using the Z80DOS environment. ZF10GD5 .COM - ZFILER which copies Z80DOS date and time stamps. y utility for Z80DOS and ZCPR3. RCPCP .LIB - Copy module for ZCPR3 RCP which copies Z80DOS stamps. SAVESTMP.COM - Copies Z80DOS file create dates. SAVESTMP.Z80 - Source. TDIR-CPM.COM - Directory utility for Z80DOS and CP/M. TDIR-Z3 .COM - Directory utility for Z80DOS and ZCPR3. TELL .COM - Tells where your system is in memory. TIME .COM - Sets clock on Z80DOS system. Z80DOS10.FOR - Short message describing Z80DOS10.LBR. Z80DOS10.Z80 - Source code for Z80DOS, main module. Z80; ; Program: DATEDEMO ; Author: Carson Wilson ; Date: September 19, 1987 ; Version: 1.0 ; Assembly: Z80ASM, PDLN, SYSLIB.REL, DATEHL.REL ; ; Purpose: Example program using Z80DOS and DATEHL module. ; ; Comments: Produces a non-alphabetized listing of matching filenames ; and their Z80DOS time stamps on console. ; ; Usage: ; DATEDEMO / display help (both versions) ; ; DATEDEMO [d:][afn] non-ZCPR version ; DATEDEMO [du: or dir:][afn] ZCPR version no equ 0 yes equ not no Z80ASM equ no ; Assemble with Z80ASM tab equ 09h cr equ 0dh lf equ 0ah fcb1 equ 05ch dskbuf equ 80h bdos equ 5 sfirst equ 17 snext equ 18 zuser equ 069h ; ZCPR location getstp equ 54 ; Z80DOS call vers equ 10 .request syslib,datehl ; Linker request for PDLN.COM external datehl ; DATEHL module external pa2hc,print,crlf,cout ; SYSLIB routines external pfn1,sua,cin,condin ; SYSLIB routines if Z80ASM ; Z80ASM pseudo-op .accept 'Assemble for ZCPR ("yes" or "no"): ',ZCPR else ZCPR equ no ; Assemble for ZCPR endif ; ; Begin ; if ZCPR ld a,(zuser) ; Log to ZCPR user # call sua endif ; ; Test for help command ; ld a,(dskbuf) ; # chars on command line cp 1 ; One character? jr nz,nohelp ; No ld a,(fcb1+1) ; Yes cp '/' ; Help command? jr nz,nohelp ; No call print ; Display help and exit db 'DATEDEMO Version ' db vers / 10 + '0','.', vers mod 10 + '0' db ' for Z80DOS',cr,lf db ' Syntax:',cr,lf if ZCPR db tab,'DATEDEMO [du: or dir:][afn]',cr,lf,0 else db tab,'DATEDEMO [d:][afn]',cr,lf,0 endif ; ZCPR ret ; Return to user ; ; Begin output ; nohelp: ld a,(fcb1+1) cp ' ' ; Filespec given? jr nz,test ld hl,fcb1+1 ; No, match all files ld a,'?' ld b,11 fill: ; Fill with '?' ld (hl),a inc hl djnz fill ; ; Search for match ; test: ld de,fcb1 ld c,sfirst call bdos cp 0ffh ; No matches jp nz,found call print db 'DATEDEMO: no matching files.',cr,lf,0 ret ; ; Matches found - print header: ; found: call print db tab,tab,'----Access---- ----Update---- -Create-' db cr,lf,0 call process ; Display name and stamp of first match ; ; Search for next match ; next: call condin ; Test pause call nz,cin ; Get input to restart ld de,fcb1 ld c,snext call bdos cp 0ffh ; No more matches ret z call process ; Display name and stamp of next match jr next ; ; Process - Get date/time stamp, display filename and stamp ; process: push af ; save dir. return code from search ; ; Store time stamp ; ld c,getstp ; Z80DOS call call bdos ; HL --> stamp ld b,10 ; copy 10 byte stamp ld de,cdate ldir ; to cdate, udate, utime, adate, atime ; ; Print file name and stamp ; pop af ; Get return code add a,a add a,a add a,a add a,a add a,a add 81h ; Point to matching filename in DMA ld e,a ld d,0 call pfn1 ; SYSLIB print file name pointed to by DE call ptab call printstp ; Display stamp call crlf ret ; ; PrintStp - Display file's time stamp from buffers ; PrintStp: ld hl,(adate) ; Print last access date call datedisp call space ld hl,(atime) ; ..and time call timedisp call space call space call space ld hl,(udate) ; Print update date call datedisp call space ld hl,(utime) ; ..and time call timedisp call space call space call space ld hl,(cdate) ; Print create date call datedisp ret ; ; DateDisp - Display mm-dd-yy on console ; ; Inputs: HL = Days since Dec. 31, 1977 in hex. ; DateDisp: call datehl ; --> H = BCD year, L = BCD month, ; ..A = BCD day ld b,a ; Save day ld a,l ; Get month call pa2hc ; Print 2 digit month call dash ld a,b ; Get day call pa2hc ; Print 2 digit day call dash ld a,h ; Get year call pa2hc ; Print 2 digit year ret ; Done with date ; ; TimeDisp - Display hh:mm on console ; ; Inputs: H = minutes in BCD, L = hours in BCD ; TimeDisp: ld a,l call pa2hc ; Print 2 digit hour ld a,':' ; Print ':' call cout ld a,h call pa2hc ; Print 2 digit minute ret ; Done with time ; ptab: ; Print a tab on console ld a,tab call cout ret ; space: ; Print a space on console ld a,' ' call cout ret ; dash: ; Print a dash on console ld a,'-' call cout ret ; ----------------------- ; Data Area ; ----------------------- dseg ; Or else we collide with code segment cdate: ds 2 ; Create date udate: ds 2 ; Update date utime: ds 2 ; Update time adate: ds 2 ; Last access date atime: ds 2 ; Last access time ; end ; END of DATEDEMO.Z80 c ; Print 2 digit month call dash ld a,b ; Get day call pa2hc ; Print 2 digit day call dash ld a,h ; Get year call pa2hc ; Print 2 digit year ret ; Done with date ; ; TimeDisp - Display hh:mm on console ; ; Inputs: H = minutes in BCD, L = hours in BCD ; TimeDisp: ld a,l call pa2hc ; Print 2 digit hour ld a,':' ; Print ':' call cout ld UR dDDT@<d`ͭDa">-YTEc:xf@fL1X?hXHLxڨqU>-XUt6 dž)]mt7pxOgᒷd1H0N}Y.#q8\.' Ȉ* ld hl,(utime) ; ..and time call timedisp call space call space call space ld hl,(cdate) ; Print create date call datedisp ret ; ; DateDisp - Display mm-dd-yy on console ; ; Inputs: HL = Days since Dec. 31, 1977 in hex. ; DateDisp: call datehl ; --> H = BCD year, L = BCD month, ; ..A = BCD day ld b,a ; Save day ld a,l ; Get month call pa2hc ; Print 2 digit month call dash ld a,b ; Get day call pa2hc ; Print 2 digit day call dash ld a,h ; Get year call pa2hc ; Print 2 digit year ret ; Done with date ; ; TimeDisp - Display hh:mm on console ; ; Inputs: H = minutes in BCD, L = hours in BCD ; TimeDisp: ld a,l call pa2hc ; Print 2 digit hour ld a,':' ; Print ':' call cout ld ; Module Name: DATEHL ; Author: Carson Wilson ; Version: 1.0 ; Date: 25 Sept 87 Public DateHL ; ; DATEHL converts the value in HL to BCD year, month, day ; for use with Z80DOS time stamps. ; ; Inputs: HL contains hex days since December 31, 1977 ; ; Outputs: H contains BCD 20th century year ; L contains BCD month ; A contains BCD day ; ; Zero flag set (Z) and A=0 if invalid date (zero) detected, ; Zero flag reset (NZ) and A=0ffh otherwise. ; Adapted from B5C-CPM3.INS DateHL: ld a,h or l ; Test blank date (zero) ret z ; Return Z and A=0 if so ld (days),hl ; Save initial value ld b,78 ; Set years counter loop: call ckleap ld de,-365 ; Set up for subtract jp nz,nolpy ; Skip if no leap year dec de ; Set for leap year nolpy: add hl,de ; Subtract jp nc,ydone ; Continue if years done ld a,h or l jp z,ydone ld (days),hl ; Else save days count inc b ; Increment years count jp loop ; And do again ; ; The years are now finished, the years count is in 'B' (HL is invalid) ; ydone: ld a,b call binbcd ld (year),a ; save BCD year ; call ckleap ; Check if leap year ld a,-28 jp nz,febno ; February not 29 days ld a,-29 ; Leap year febno: ld (feb),a ; Set february ld hl,(days) ; Get days count ld de,mtable ; Point to months table ld b,0ffh ; Set up 'B' for subtract ld a,0 ; Set a for # of months mloop: push af ld a,(de) ; Get month ld c,a ; Put in 'C' for subtract pop af ld (days),hl ; save days count add hl,bc ; Subtract inc de ; Increment months counter inc a jp c,mloop ; Loop for next month ; ; The months are finished, days count is on stack. First, calculate ; month. ; mdone: ld b,a ; Save months ld hl,(days) ld a,h or l jp nz,nzd dec de dec de ld a,(de) cpl inc a ld l,a dec b nzd: ld a,l ; Retrieve binary day of month call binbcd ; Convert to BCD push af ; Save day in A ; ld a,b ; Retrieve the binary month call binbcd ; Convert binary month to BCD ld l,a ; Return month in L ; ld a,(year) ld h,a ; Return year in H ; or 0ffh ; Return no error pop af ; Restore day ret ; ; Support Routines: ; ; ; Check for leap years. ; ckleap: ld a,b and 0fch cp b ret ; ; Convert A to BCD & store back in A ; BinBCD: or a ret z push bc ld b,a xor a BinBCD1: add a,1 daa djnz BinBCD1 pop bc ret ; ; Buffers: ; ; ; Months table ; mtable: db -31 ;January feb: db -28 ;February db -31,-30,-31,-30 ;Mar-Jun db -31,-31,-30 ;Jul-Sep db -31,-30,-31 ;Oct-Dec days: ds 2 ; temporary buffers year: ds 1 end ; END DATEHD.Z80 z,nzd dec de dec de ld a,(de) cpl inc a ld l,a dec b nzd: ld a,l ; Retrieve binary day of month call binbcd ; Convert to BCD push af ; Save day in A ; ld a,b ; Retrieve the binary month call binbcd ; Convert binary month to B Z80DOS Function Summary ----------------------- No. ------ Name ------- ------ Input ------- ---------- Output ---------- 0 Boot None None 1 Console input None A = Character 2 Console output E = Character A = 00h 3 Reader input None A = Character 4 Punch output E = Character A = 00h 5 List output E = Character A = 00h 6 Direct console I/O E = 0ffh A = Input character or 00h if no character present E = 0feh A = Console status E = 000h..0fdh A = 00h 7 Get I/O byte None A = I/O byte (ramlow+03h) 8 Set I/O byte E = I/O byte A = 00h 9 Print string DE = Address string A = 00h 10 Read console buffer DE = Address buffer A = 00h 11 Get console status None A = 00h no character present A = 01h character present 12 Return CP/M version None A = Version number (22h) 13 Reset disk system None A = 00h no $*.* file A = 0ffh $*.* file present 14 Select disk E = Disk number A = 00h 15 Open file DE = Address FCB A = Directory code 16 Close file DE = Address FCB A = Directory code 17 Search for first DE = Address FCB A = Directory code 18 Search for next DE = Address FCB A = Directory code 19 Delete file DE = Address FCB A = Error code 20 Read sequential DE = Address FCB A = Read/write code 21 Write sequential DE = Address FCB A = Read/write code 22 Make file DE = Address FCB A = Directory code 23 Rename file DE = Address FCB A = Error code 24 Return login vector None HL = Login vector 25 Return current disk None A = Current disk 26 Set DMA address DE = DMA address A = 00h 27 Get alloc. address None HL = Alloc. address 28 Write protect disk None A = 00h 29 Get R/O vector None HL = R/O vector 30 Set file attributes DE = Address FCB A = Error code 31 Get address DPB None HL = Address DPB 32 Set/get user code E = 0ffh A = User number  E = User number A = 00h 33 Read random DE = Address FCB A = Read/write code 34 Write random DE = Address FCB A = Read/write code 35 Compute file size DE = Address FCB A = Error code 36 Set random record DE = Address FCB A = 00h 37 Reset drive DE = Mask A = 00h 38 Not implemented None A = 00h 39 Not implemented None A = 00h 40 Write random w/fill DE = Address FCB A = Read/write code 54 Get Stamp None HL = Address stamp 55 Use Stamp None Stored stamp used next call 104 Set time DE = Address time A = 00h 105 Get time DE = Address for time A = 00h Return Codes: ------------- Directory code: A = 00h,01h,02h,03h if no error A = 0ffh if error Error code: A = 00h if no error A = 0ffh if error Read/write code: A = 00h if no error A = 01h read ==> end of file write ==> directory full A = 02h disk full A = 03h close error during random record read/write A = 04h read empty record during random record read A = 05h directory full error during random record write Read/write code 35 Compute file size DE = Address FCB A = Error code 36 Set random record DE = Address FCB A = 00h 37 Reset drive DE = Mask A = 00h 38 Not implemented None A = 00h 39 Not implemented None A = 00h 40 Write random w/fill DE = Address FCB A = Read/write code 54 Get Stamp None HL = Address stamp 55 Use Stamp None Stored stamp used next call 104 Set time DE = Address time A = 00h 105 Get time DE = Address for time A = 00h Return Codes: ------------- Directory code: A = 00h,01h,02h,03h if no error A = 0ffh if error Error code: A = 00h if no error A = 0ffh if error Read/write code: A = 00h if no error A = 01h read ==> end of file write ==> directory full A = 02h disk full A = 03h close error during random record read/write A = 04h read empty record duriP/VCAEWM!)6# x!"J!g"D!9"+1)!6 ͻ :]/ t:G %ڋO Ignoring: > x  xv͑ x2)y2*!~=ʧ#™ó:Gڳͫ :G>25< No destination file specified. d]͎d:G':*:)<!^# <>͢d *-*Dͤ c!ͤ YO`i"/|z Out of memory. d:57>͢d:!>?!͏  Wildcards not allowed. d!* d File is open. Use ^Z to quit. *Dʹ *D 1 Disk is full. Deleting file. g dͰ d͉  > ͈ :23!g COPYING: ^  !]!]* to !*:;¤:hʤ Archived0>PPҮ !\ ͫ  Can't copy to same drive/user.d! ͫ  Duplicate!0!]x:Fd0:7ʭ*" - Verifying # d**ͤ ʒ failed! :3„ Please check your disk. d=23g D OK :23:9ʭ*$ :A - Erasing !]*͂ g !  :1:2 :2 ^! ~w怶w#:;*:h2h:2]s s *H+"H|d   [ [ User abort :)G:*O͢ *+ɯ2G!~#~#=ʁ ʁ+͗:G<2G~!ں=´#ú#å6#:GO!J ~#fos#rG:G=2GOx<G~#~x<=<:GOx:<'x#~'6WzI~ - unrecognized option @M‡>27É/@͢ I no file I bad drive/user spec in >I invalid characters in :)G:*O>:͏ 0=:::~A G#n !O#n !W#yOO~::#::yx<7Ͷ~²~#.~*s!ڣF~#.ʃ!ڣd>?ud~~²~#.ʲ*ʧ!ڣ…>?§ã7 > ͈ ! ͈ !͏ ,;:='"Ͷ  #>>.>!> ?~ "#=F+NI##?>. ~# ?xA y/ _0 r _ Ox y0 >: ɯ2422n ҡ!I can't open source6 */DM*DB :4/24DM*D g !I Disk full. Copy deleted.:4ʭͰ !I can't close destination͂ !x@ ͂ ͏ @ # % ͂ !xl ͂ ͢ l :7] # I ͂ 767ɯ2122͵ 2  >21! ~~w >22:? R/O! s / :=/ :?/ Exists! g N !I directory is full͂ 7͂ _  OGXY !͂ ͂ =Շo&͵ #*D  "D*H#"H*ͤ <=  #  !"n I I verify file open error!͂ ͢ [ ] O ~o #b *O! ~W$^"!!yg)Ҥ >g>!o˜ r$s%# Ž ͷ \ r r ʓ  O » x6#= BKͷ v @ ` @ \ þ _  >^ @ yO0 {O M ; F{M M > O M > w#xZ */ͤ >> >x û y+~ ҆ ͯ ͯ x͜ û > O > O  ʺ  ~# _ Delete? v Y7  |1 }1 :  C 0 ERROR:  |  >2F7>9~0a{_~# ˆ O~#ʡ ” x{_zW~O#« **ͤ "- PPIP, Version 1.8 Z80DOS version Usage: PPIP [du:]source.typ[ [du:][dest.typ]]k or PPIP [[du:]dest.typ]=[du:]source.typkÇ [ : options] 6! Options (default): o:  (ON) -  (OFF) - ãdo CRC Verificationprint the CRC valuecopy only unArchived filesdelete (Erase) R/W files without askingdelete (Wipe) R/W and R/O files without askingMove files - delete source after copyCON:PPIP - version 1.8Copyright (C) David Jewett,III - 1986 \ þ _  >^ @ yO0 {O M ; F{M M > O M > w#xZ */ͤ >> >x û y+~ ҆ ͯ ͯ x͜ û > O > O  ʺ  ~# _ Delete? v YZ3ENVP/VCAEWM!6# x#!i"!"!9"1! /* |~#~!,~@2 #~2 #~2#~=…#####~2:]/ͺ:kO? Ignoring: > O xE v xڹ x2y2!~=#:Y<:>2? No destination file specified. êڪ:j(::<!# >)ڪ _**ڦ!ڜÒ`i"|½? Out of memory. ê:z>ڪ:!>?!? Wildcards not allowed. ê!͎. ڪ? File is open. Use ^Z to quit. * *͂ t? Disk is full. Deleting file. ê ê M > : 2!? COPYING:  !)L!͎? to !͎::? Archivedv>P:Pv ! (? Can't copy to same drive/user.ê!M H? Duplicate!v!^:ªv:*e"g? - Verifying ͇ ڪ*g*e? failed! :? Please check your disk. ê=2 Ê? OK : 2:*e͘ :? - Erasing !͎ !M :;:R: 2 R! ~w怶w#C:p:2:2  *+"|ʪv  ͋ ʞ ž? User abort :G:O *ɯ2!~#~#= +:<2~!=##6#:"O! ~#fo s#rG:U=2OxU < G~#~x><="<":Ox:ʂmx-#~mʝÏ~O ? - unrecognized option ÆM>2/ÆA ͽ no file * ͽ bad drive/user spec in ͢)ͽ invalid characters in :G:O>:ʔ=ʞG{:=ž: ڞ~A`G# څO# څW#yOOÅ: @ڞ~:ž#: ڞyx<7~<~#.*!ª~#.!>?~<~#.* !>? 7 > *!  !E,;:='"  #X>u>.>u!X͢ ?~ ʆ#=uF+Nͭ##ͣ>.O ~#O £xAO y/ 0O   OxO y0O >:O ɯ22e2f  !ͽ can't open so urce6. *DM*ͦ ( :/2DM*͂ Y !ͽ Disk full. Copy deleted.:  !ͽ can't close destination !xʤ ڤ # É !x  : # í * 767ɯ22 ږ v >2! ~~ws >2:m ? R/O!^ Ó :ʓ :ʓ ? Exists!^ Ͳ !ͽ directory is full* 7 _  OGXY *! =Շo& 2 #* "*#"*<=E 2 # !"e ҭ ͽ verify file open error!  ڿ ó ~ # *eO!e ~W$^"e!e!yg) >g>!o r$s%# + \R    G o G  x6#=H BK+ @g `g @; \; " _ Ҁ >^O @O :W yOښ { ï F{ > O  > O w#x *>>O >  y+~ # # x G  > O  > O ?  . E ~#O E _? Delete?  Y7?  |ͥ }ͥ ͮ  ڷ 0O ? ERROR: E |E v >27>9~0a{_~#  O~#x{_zW~O#**C"~ͧg#M~#ͧ[+o> g}ѯ~y##ž#ˆF#Nѯ= }aڵ{ҵ_0:A[GxG=x* ^#V#~ѷ? PPIP for ZCPR3, Version 1.8 Z80DOS version Usage: PPIP [du: or dir:]source.typ[ [du: or dir:][dest.typ]]ͭ? or PPIP [[du: or dir:]dest.typ]=[du: or dir:]source.typͭ? [ :O ? options] !'? Options (default):? ʵ:O O ? (ON) - !? (OFF) - E do CRC Verificationprint the CRC valuecopy only unArchived filesdelete (Erase) R/W files without askingdelete (Wipe) R/W and R/O files without askingMove files - delete source after copy? Not a ZCPR3 system or not installed: ABORTING!õCON:PPIP - version 1.8Copyright (C) David Jewett,III - 1986 |ͥ }ͥ ͮ  ڷ 0O ? ERROR: E |E v >27>9~0a{_~#  O~#x{_zW~O#**C"~ͧg#M~#ͧ[+o> g}ѯ~y##ž#ˆF#Nѯ= }aڵ{ҵ_0:A[GxG=x* ^#V#~ѷ? PPIP for ZCPR3, Version 1.8 Z80DOS version Usage: PPIP [du: or dir:]source.typ[ [du: or dir:][dest.typ]]ͭ? or PPIP [[du: or dir:]dest.typ]=[du: or dir:]source.typͭ? [ :O ? options] !'? Options (default):? ʵ:O O ? (ON) - !? (OFF) - E do CRC Verificationprint the CRC valuecopy only unArchived filesdelete (Erase) R/W files without askingdelete (Wipe) R/W and R/O files without askingMove files - delete source after copy? Not a ZCPR3 system or ; Z80DOS - Z80 Disk Operating System ; ; Version 1.0 - 05 Sept. 87 by Carson Wilson ; ; RCP-CP.Z80 (from Z33RCP01.LBR, for ZCPR version 3.3) ; Modified for Z80DOS date stamping by Carson Wilson, Sept. 1, 1987. ; This version of CP copies Z80DOS's Get Stamp (54) and Use Stamp (55) ; functions to copy the source file's Z80DOS or DOS+25 format ; create date and modify time/date to the destination file. The ; last access time on both files is set to the current system time when ; the copy was made. ; Z80DOS' Get Stamp function saves the time stamp, if present, of a ; successfully opened file. The Use Stamp function can then be used ; to set the date and time for the next file write or close command. ; Only sections marked have been altered. Z80DOS equ true ; Copy Z80DOS stamps (costs 26 bytes) GetStp equ 54 ; Store file's date and time stamp UseStp equ 55 ; Use stored stamp for write/close ;============================================================================= ; ; F I L E C O P Y C O M M A N D ; ;============================================================================= ; Command: CP ; Function: Copy a file from one place to another ; Syntax: CP destfile srcfile, CP destfile=srcfile ; CP : srcfile, CP =srcfile ; CP . srcfile (if '.' = current dir. in CCP) ; Comments: Both file specifications can include a directory specification. ; If only one file name is given, then the current directory and ; the source file name are assumed for the destination. copy: call retsave ; If new is blank, make it the same name and type as old ld de,fcb1+1 ; Point to destination file name ld a,(de) ; Get first character cp ' ' ; If not blank (no name) jr nz,copy0 ; ..then branch to copy ld hl,fcb2+1 ; Copy source name into destination FCB ld b,11 ; Name and type are 11 bytes call blkmov ; See if destination is same as source, and abort if so copy0: ld hl,fcb1 ; Set up pointers to two files ld de,fcb2   push hl push de inc hl ; Point to names of files inc de ld b,13 ; Compare 13 bytes (name, type, and user #) copy1: call comp jr nz,copy2 ; If they differ, go on with copy ld c,25 ; Get-current-disk BDOS function call bdos ; Get it in case no drive given explicitly inc a ; Shift to range 1..16 ld b,a ; ..and keep value in B pop de ; Restore pointers to FCBs pop hl ld a,(de) ; Get drive of source file ld c,a ; ..and save it in C or a ; Is it default drive? jr nz,copy1a ; Branch if drive made explicit ld c,b ; Otherwise, copy default drive into C copy1a: ld a,(hl) ; Get drive of destination file or a ; Is it default drive? jr nz,copy1b ; Branch if drive made explicit ld a,b ; Otherwise, get current drive copy1b: cp c ; Compare the two drives specified jr nz,copy3 ; Branch if they are different jr cperr ; Branch to error code if they are the same copy2: pop de ; Clean up the stack pop hl ; Make note of the user numbers of the two files copy3: ld a,(fcb1+13) ; Get destination user number ld (usrdest),a ld a,(fcb2+13) ; Get source user number ld (usrsrc),a ; Set up new FCB for source file and open the source call define ; Define buffer addresses dynamically ld hl,(srcfcb) ; Get address to use for new source FCB push hl ex de,hl ; Copy file data to new FCB ld b,12 call blkmov call logsrc ; Log in user number of source file pop hl ; Initialize the source file FCB call initfcb2 ld c,15 ; Open source file call bdos inc a ; Check for error jp z,prfnf ; Branch if file not found if Z80DOS ld c,GetStp ; Save source's stamp call bdos endif ; Make sure destination file does not already exist call logdest ; Log into destination user area call extest ; Test for existence of file in fcb1 jp z,exit ; Branch if it exists ; Create destination file if Z80DOS ld c,UseStp ; Use stored stamp call bdos endif ld de,fcb1 ; Point to destination FCB ld c,22 ; BDOS make-file function call bdos inc a ; Test for error (no directory space) jr nz,copy5 ; Branch if OK ; Report file error cperr: call print db ' Copy','?'+80h jp exit ; Copy source to destination with buffering ;++++++++++ this should be done by changing DMA address to save all the ; buffer swapping copy5: call logsrc ; Log in source user area ld b,0 ; Initialize counter ld hl,(cbuff) ; Initialize buffer pointer copy5a: push hl ; Save address and counter push bc ; ld hl,(srcfcb) ; Point to source file FCB ; ex de,hl ; Put it in DE for BDOS call ld de,(srcfcb) ; ld c,20 ; BDOS read-sequential function call bdos pop bc ; Get counter and address pop de or a ; Read Ok? jr nz,copy5b ; Branch if end of file push bc ; Save counter ld hl,tbuff ; Copy from 80h to buffer ld b,128 ; 128 bytes call blkmov ex de,hl ; HL points to next buffer address pop bc ; Get counter back inc b ; Increment it ld a,b ; See if buffer full cp cpblocks jr nz,copy5a ; If not, go back for more copy5b: ld a,b ; Get count of blocks loaded into buffer or a ; Are there any? jr z,copy6 ; Branch if not (we are done) push bc ; Save count call logdest ; Log into destination user number cbuff equ $+1 ; Pointer for in-the-code modification ld hl,0 ; Point to beginning of copy buffer copy5c: ld de,tbuff ; Copy into tbuff ld b,128 ; 128 bytes call blkmov push hl ; Save pointer to next block if Z80DOS ld c,UseStp ; Use store stamp call bdos endif ld de,fcb1 ; Point to destination file FCB ld c,21 ; Write the block call bdos or a jr nz,cperr ; Branch on error (disk full or write error) pop hl ; Get back pointer to next block pop bc ; Get count ; djnz copy5 ; Work through the blocks dec b ; jr z,copy5 ; push bc ; Save count jr copy5c ; Back for another bufferful ; Close the destination file copy6: call logdest ; Log into destination user number if Z80DOS ld c,UseStp ; Use stored stamp for close call bdos endif ld de,fcb1 ; Point to destination FCB if Z80DOS push de pop ix ; point to s2 byte of FCB ld (ix+14),0 ; zero it to force close & stamp endif ; ..of 0-length file ld c,16 ; Close file call bdos inc a ; 0ffh --> 0 if error jr z,cperr ; close file error call print db ' Don','e'+80h if cpsp and spaceon jp spaexit ; Report space remaining on destination drive else jp exit endif ;cpsp and spaceon ; Log into user number of source file logsrc: usrsrc equ $+1 ; Pointer for in-the-code modification ld a,0 ; Get user number jr setusrrel ; Local jump to save code ; Log into user number of destination file logdest: usrdest equ $+1 ; Pointer for in-the-code modification ld a,0 ; Get user number setusrrel: jp setusr ; ; End RCP-CP.Z80 ;   Z3ENV SAVESTAMP: :(:]/:^ Version 1.0. Copies Z80DOS file create date. Syntax: SAVESTMP [du: or dir:]dest.fil [du: or dir:]source.fil - ZCPR usage SAVESTMP [d:]dest.fil [d:]source.fil - CP/M usage * |2E( :y_ l<6F :E( :i_ \<=:!(óAltering creation date of ]o . . . :\(=__'''_W!!F >Done. No time stamps present on destination disk! o not found. Write error! ~#k U ͓ B GGK y yG>GO> aͅ>.ͅ …   >^@O*. "*+++=* H  H|2#}2~# x# ~+ x8-TYPELZ COM,-UNARC COM ./UNCR COM01WL3 COM23456789:; Program: SAVESTAMP for Z80DOS systems. ; Author: Carson Wilson ; Assembly: Z80ASM, PDLN, SYSLIB ; Date: September 26, 1987 ; Version: 1.0 ; ; Purpose: Copies create date from one file to another under Z80DOS. ; ; Usage: By using SAVESTAMP, the creation date of modified files ; can be preserved after using an editor or other program which ; renames the original file to filename.BAK. ; ; Syntax: ; ; SAVESTMP or SAVESTMP / - Display help. ; SAVESTMP [du: or dir:]dest.fil [du: or dir:]source.fil - ZCPR usage ; SAVESTMP [d:]dest.fil [d:]source.fil - CP/M usage ; bdos equ 5 bell equ 7 tab equ 9 cr equ 0dh lf equ 0ah fcb1 equ 5ch fcb2 equ 6ch defDMA equ 80h dmastp equ 0e0h ; Stamp indicator in DMA after search bwrite equ 14 ; BIOS write block call with SYSLIB seldsk equ 14 ; BDOS select disk sfirst equ 17 user equ 32 ; BDOS set/get user GetStp equ 54 ; Z80DOS function call user1 equ 69h ; ZCPR user # for fcb1 user2 equ 79h vers equ 10 .request SYSLIB external moveb,bios,print,pfn2 begin: jp start db 'Z3ENV' db 3 envaddr: dw 00000h ; Environment address dw begin start: call print db cr,lf,'SAVESTAMP: ',0 ; sign-on ld a,(defdma) or a jr z,help ; No input? ld a,(fcb1+1) ; Get 1st char. after possible spaces cp '/' ; Help? jp nz,copystmp ; No ld a,(fcb1+2) cp ' ' ; Next char. non-blank? jp nz,copystmp ; Yes ; ; Display help ; help: call print db 'Version ',vers/10+'0','.',vers mod 10+'0','.' db tab,'Copies Z80DOS file create date.',cr,lf db ' Syntax:',cr,lf db tab,'SAVESTMP [du: or dir:]dest.fil [du: or dir:]source.fil' db tab,'- ZCPR usage',cr,lf db tab,'SAVESTMP [d:]dest.fil [d:]source.fil' db tab,tab,tab,'- CP/M usage',cr,lf,0 ret ; ; Copy create date, if any ; copystmp: ld hl,(envaddr) ; Get environment address ld a,h ; Test if running under Z3 or l ld (z3flg),a ; Zero if not ZCPR environment ; ..non-zero if ZCPR jr z,getsrc ; Don't set user ; ; Search for source file ; ld a,(user2) ; Get ZCPR source user ld e,a ld c,user call bdos ; Select source user getsrc: ld de,fcb2 push de ; Save for error message ld c,sfirst ; Search for first call bdos pop de ; Retrieve file name inc a jp z,missing ; A = 0ffh --> 0 = error ; ld c,getstp ; Z80DOS call call bdos ; HL --> file's stamp if any ld de,stamp ; Point to buffer for source stamp ld b,2 call moveb ; SYSLIB copy 2 byte stamp to buffer ; ; Search for dest. file ; ld a,(z3flg) ; ZCPR? or a jr z,getdest ; No, don't select user ld a,(user1) ; Get ZCPR dest. user ld e,a ld c,user call bdos ; Select dest. user getdest: ld de,fcb1 push de ; For error message ld c,sfirst ; Load BIOS dirbuf with dir. sector call bdos pop de inc a jp z,missing ; 0ffh --> 0 = dest. file not found ; dec a ; Save directory return code push af ; ; Got both files, now test destination for stamps ; ld a,(dmastp) ; Test time stamps on dest. cp '!' jr z,stamps ; Stamps found pop af jp nostamps ; Not found, abort ; ; Stamps exist on destination, so copy stamp ; stamps: call print db 'Altering creation date of ',0 ld de,fcb1+1 call pfn2 call print db ' . . . ',0 ; ; First select disk, if necessary ; ld a,(fcb1) ; Dest. file's disk or a ; Default disk? jr z,copy ; Yes, so don't select disk dec a ; 0 = A for SelDsk ld e,a ; Non-default disk ld c,seldsk ; Select disk so that BIOS sector call bdos ; ..write goes to right disk! copy: pop af ; Retrieve dir. search code ld e,a ; (A = 0, 1, or 2) sla a ; Multiply by 10 sla a add a,e sla a add a,2 ; A = offset to create date (2,12,22) ld e,a ; Save in E xor a ld d,a ; Zero D ld hl,dmastp ; Dir. stamp location in DMA buffer add hl,de ex de,hl ; DE --> Dest file's create stamp ld hl,stamp ; Point to buffer holding source stamp ld b,2 call moveb ; SYSLIB copy 2 bytes from buffer to DMA ld c,1 ; Force directory sector write of DMA ld a,bwrite ; BIOS write sector  call bios or a ; BIOS return code: A = 0 --> okay jp nz,badwrite call print ; Write ok db 'Done.',cr,lf,0 ret ; Done ; ; Error Routines ; nostamps: call print db bell,'No time stamps present on destination disk!' db cr,lf,0 ret ; missing: inc de call pfn2 ; SYSLIB print file name call print db ' not found.',cr,lf,bell,0 ret ; badwrite: call print db bell,'Write error!',cr,lf,0 ret ; ; Data area ; dseg z3flg: ds 1 ; ZCPR environment stamp: ds 2 ; Save date stamp end ; END SAVESTMP.Z80 reate date (2,12,22) ld e,a ; Save in E xor a ld d,a ; Zero D ld hl,dmastp ; Dir. stamp location in DMA buffer add hl,de ex de,hl ; DE --> Dest file's create stamp ld hl,stamp ; Point to buffer holding source stamp ld b,2 call moveb ; SYSLIB copy 2 bytes from buffer to DMA ld c,1 ; Force directory sector write of DMA ld a,bwrite ; BIOS write sector  [VIDON>$[VIDOFF>$s 1 :Ɓo&62/ʨS  2 <r͚^ è: Illegal file name$:] !] 6?#>?2h2j! " !" 2 2 2 2 :\ <=_: _  " \2 ͚ { : No files$ƀo&[ 6 ~#S \2 /ƀo&  #­###~?O?++8 &~O8 ###N+++0   5 K B R  #O~( * RDM" (6 ~#5 K B  ! [ R#~j #>.x ~j #> x ####9C++++;     2 2 [ S > x > x : <2 :2 [ R() ʕ> x > x  : Ax : S >:x : G: O: W; * ́ * K ~#8 x( : <(JS: ( "= ; {  TDIR$͚  Version 1.1 Syntax: TDIR [du:][afn] - display files and date stamps TDIR / - display this help $ Name Bytes Recs Last Access Update Create ------------ ------ ------ ----------------- ----------------- ----------- $[more] $ Drive $k in$ files$k free$~ ~#~##~7+w> ###~+++W~O##~++GO#F#N#V+++: W0 : /W! ~w+~w+~waj́>kx ɯ'͢͢d͢ ͢͢B0 (z0x > x !@B!!'!!d! !> x [ !\w2 .(LA8Q808 :0: -2 A8 @?w:(:( 0 Oy2 (#* 6?w(#6 . (#* 6?w(#6 #6S  !8=_.:,;<>ɯK x : > x /͵!m͉ {z0 yOxG#!V{ ͉ z>0 yOxG#+8d x > x ! ~#x ~#x ~x >-x yG S >-x >d x : S >:x : S JanFebMarAprMayJunJulAugSepOctNovDecG'0x 0x  8 >?(  _ _~7+w> ###~+++W~O##~++GO#F#N#V+++: W0 : /W! ~w+~w+~waj́>kx ɯ'͢͢d͢ ͢͢B0 (z0x > x !@B!!'!!d! !> x [ !\w2 .(LA8Q808 :0: -2 A8 @?w:(:( 0 Oy2 (#* 6?w(#6 . (#* 6?w(#6 #6S  !8=_.:,;<>ɯK x : > x /͵!m͉ {z0 yOxG#!V{ ͉ z>0 yOxG#+8d x > x ! ~#x ~#x ~x >-x yG S >-x >d$[VIDOFF>$s1 :Ɓo&6]/zS:i2:] !] 6?#>?2h2j! "!"2222:\ <=_:_ "\2 l {: No files$ƀo&[6 ~#S\2ƀo&  ####~?O?++8 &~O8 ###N+++0    KB $  #O~( *RDM"(6 ~# KB —î ! [R#~n#>.|~n#> |####&++++͡22[S> |> |:<2 2[R() g> |> |   A|:W>:|:G:O:W͡ *d *K~#8 x( :<(JS:( "= ͡ {u TDIR$l  Version 1.1 for ZCPR Syntax: TDIR [du: or dir:][afn] - display files and date stamps TDIR / - display this help   $ Name Bytes Recs Last Access Update Create ------------ ------ ------ ----------------- ----------------- ----------- $[more] $ Drive $k in$ files$k free$~ ~#~##~7+w> ###~+++W~O##~++GO#F#N#V+++:W0 :/W!~w+~w+~wajd>k|ɯ'ͅͅdͅ ͅͅB0 (z0|> |!@B!!'!!d! !> |Kx : > |3͹!m͍ {z0 yOxG#!V{ ͍ z>0 yOxG#+8d |> |!$~#|~#|~|>-|yKW>-|>d |:W>:|:WJanFebMarAprMayJunJulAugSepOctNovDecG'0|0| 8 >?(  _ _ Pö This is a utility that tells you where your CCP starts, what your BDOS entry add- ress is, and where your CBIOS jump table begins, as well as other useful info. $*+++"|g"}"f *}_ *_ *_  "Cold start routine: $6 LWarmstart routine: $` {Console status routine: $ øConsole Input routine (waits for char.):$  Console out routine: $  List device out routine: $' =Punch out routine: $Q gReader in routine: ${ ÑHome disk routine: $ ýSelect disk routine: $ The set track routine: $ Set the sector routine: $!. FSet the dma routine: $$Z pRead disk routine: $' ÛWrite disk routine: $* List status routine: $- Sector translate routine: $0*#V#^",0 B0:;_H.  \U"Your CCP starts at: $Your BDOS entry address is: $Your CBIOS jump table begins at: $  "Cold start routine: $6 LWarmstart routine: $` {Console status routine: $ øConsole Input routine (waits for char.):$  Console out routine: $  List device out routine: $' =Punch out routine: $Q gReader in routine: ${ ÑHome disk routine: $ ýSelect disk routine: $ The set track routine: $ Set the sector routine: $!. FSet the dma routine: $$Z pRead disk routine: $' ÛWrite disk routine: $* List status routine: $- Sector translate routine: $0!"910:]?/ $%!~#og6"/#"/ #"  Q":#"zQ! ҚDMxZ"x~2y<~2h i2*|Aͬ*`>:ͮ:j  *.~ #0:?~DM)) )ͧ#~*?|͑yZzS{>/ͮ~j}j>:ͮ|sƐ'@'î ҂Gy))~#ͮ ͬSat.Sun.Mon.Tue.Wed.Thu.Fri.dddO><}o|gL% PK|1+6#>2!#{_zW={_WP]>2!yNGOxGz{!ͧ}yzҏ+A]”GͧɅo$> _ Bad time specification. TIME 1.0 for Z80DOS, adapted from C. B. Falconer Usage: TIME - Show 24 hour time TIME yy/mm/dd hh:mm - Set 24 hour time Example: TIME 87/09/23 13:20 $Needs DOS+, CPM3, or Z80DOS$No time set$; Z80DOS - Z80 Disk Operating System ; ; Version 1.0 - 05 Sept. 87 by Carson Wilson ; ; ------------------------------------------------------------------- ; ; Z80DCHAR.Z80 - DOS Entry Points, Character I/O, and Error Routines ; ; ------------------------------------------------------------------- org DOS+6 ; Preserve serial number for CP/M ; ..overlay ; ; Start DOS ; start: jp entry ; Jump to entry point DOS ; ; Error messages DOS - for programs which intercept DOS errors ; defw 0 ; Bad Sector - not implemented StSel: defw SelErr ; Select error stro: defw rdonly ; Drive read only sfilro: defw filro ; File read only ; ; Run-time configurable options: ; timead: defw BIOStim ; Dummy or address of BIOS routine ; ..configurable at run time flags: defb options ; Flag bite ; ; Entry point for DOS commands ; Entry: ld a,c ; Get function number ld (funct),a ; Save it for later use ld hl,0 ; Set hl to zero ld (pexit),hl ; Clear exit code xor a ; Clear a ld (fldrv),a ; Reset drive select done flag ld (rdwr),a ; Reset read/write flag ld (spsave),sp ; Save stack pointer ld sp,DOSs ; Get internal stack pointer push ix ; Save index register push de ; Save parameter register pop ix ; Get it back in ix ld hl,p2exit ; Get exit address DOS push hl ; Save it on stack to return from DOS ld a,c ; Get function code cp 105 ; Test get time jp z,gettim ; Yes then get time cp 104 ; Test set time jp z,settim ; Yes then set time cp 55 ; Test use time stamp jp z,UseStp cp 54 ; Test get time stamp jp z,Getstp cp MaxCmd+1 ; Test greater then MaxCmd ret nc ; If so return to caller and do nothing ld hl,ctable ; Load table ld b,0 ; Prepare 16 bit add add hl,bc ; Add add hl,bc ; Add twice to get word value ld a,(hl) ; Get LSB inc hl ; Pointer to MSB ld h,(hl) ; Get MSB ld l,a ; Save LSB in l jp (hl) ; Jump to routine ; ; Command Jump Table ; CTable: defw WBoot ; Warm boot defw rdcon ; Console input defw bwrcon ; Console output defw rdrdr ; Reader input defw wpunch ; Punch output defw wlist ; List output defw dcio ; Direct console I/O defw giost ; Get I/O byte defw siost ; Set I/O byte defw mess ; Print string defw rdbuf ; Read console buffer defw tstcs ; Get console status defw cmnd12 ; Return version number defw cmnd13 ; Reset disk system defw cmnd14 ; Select disk defw cmnd15 ; Open file defw cmnd16 ; Close file defw cmnd17 ; Search for first defw cmnd18 ; Search for next defw cmnd19 ; Delete file defw cmnd20 ; Read sequential defw cmnd21 ; Write sequential defw cmnd22 ; Make file defw cmnd23 ; Rename file defw cmnd24 ; Return login vector defw cmnd25 ; Return current disk defw cmnd26 ; Set DMA address defw cmnd27 ; Get address allocation vector defw cmnd28 ; Write protect disk defw cmnd29 ; Get R/O vector defw cmnd30 ; Set file attributes defw cmnd31 ; Get address disk parameter header(dph) defw cmnd32 ; Get/set user code defw cmnd33 ; Read random defw cmnd34 ; Write random defw cmnd35 ; Compute file size defw cmnd36 ; Set random record defw cmnd37 ; Reset multiple drive defw dummy ; Access drive - not implemented defw dummy ; Free drive - not implemented defw cmnd40 ; Write random with zero fill ; --------------------------------- ; Character Routines ; --------------------------------- ; DOS console input ; ; Read character from console and echo ; if char=cr, lf, tab, ContH or >=space ; RdCon: call getch ; Get character call tstch ; Test if cr,lf,tab,ContH or >=space jr c,exit ; No then exit call wrcon ; Echo character exit: ld (pexit),a ; Return character dummy: ret ; And exit DOS ; ; DOS write console ; bwrcon: ld a,e ; Copy character jr wrcon ; And output it ; ; read reader ; rdrdr: call reader ; Get character from reader  jr exit ; And return it to caller ; ; write punch ; wpunch: ld c,e ; Copy character jp punch ; And output it to punch device ; ; Write list ; wlist: ld c,e ; Copy character jp list ; And output it to list device ; ; Direct console input/output ; DCIO: ld c,e ; Copy character inc e ; Test if 0ffh jr z,dcio0 ; Yes do input inc e ; Test if 0feh jp nz,ConOut ; No then output character call ConSt ; Get console status and 1 ; Test it jr exit ; And return it to caller DCIO0: call ConSt ; Get console status and 1 ; Test it ret z ; Exit if no character present call ConIn ; Get character jr exit ; And return it to caller ; ; Get I/O status byte ; giost: ld a,(RamLow+00003h) ; Get I/O byte from ram jr exit ; And return it to caller ; ; Set I/O status byte ; siost: ld a,e ; Copy I/O byte ld (RamLow+00003h),a ; And save it in ram ret ; Exit to caller ; ; Test console status ; tstcs: call gConSt ; Get console status jr exit ; And return it to caller ; ; Output char (control char = ^char) ; outch: call tstch ; Test it cr,lf,tab,ContH or >=space jr nc,wrcon ; Yes then jump push af ; Save character ld a,'^' ; Load a with '^' call wrcon ; Output it pop af ; Get character back push af ; Save it again add a,'A'-1 ; Add offset call wrcon ; Output it pop af ; Get character ret ; Return to caller ; ; Echo cr,lf ; CROut: ld a,cr ; A=carriage return call wrcon ; Output it ld a,lf ; A=line feed ; Fall through to output routine ; ; Write character on console ; wrcon: cp tab ; Test if tab jr nz,wrcon1 ; No then jump wrcon0: ld a,' ' ; Expand tab with spaces call wrcon ; Write space ld a,(tabcnt) ; Get tab count and 7 ; Test if done jr nz,wrcon0 ; No then repeat ld a,tab ; Return tab ret ; Return to caller wrcon1: push af ; Save character call gConSt ; Test status and ContS/ContC pop af ; Get character back push af ; Save it again ld c,a ; Copy it call ConOut ; Output it pop af ; Get character back push af ; Save it again ld c,a ; Copy it ld a,(fContP) ; Get printer echo flag or a ; Test it call nz,list ; Non zero => output char to printer ld a,(flags) ; Get flag byte bit 1,a ; Test delay 256 bytes active jr z,wrcon2 ; No then exit ld hl,delay ; Get delay counter xor a ; A=0 or (hl) ; Test counter=0 jr z,wrcon2 ; Yes then exit dec (hl) ; Else decrement counter wrcon2: pop af ; Restore character ; Fall through to count routine ; ; Count characters in line ; countc: ld hl,tabcnt ; Get pointer to tab counter inc (hl) ; Increment tab counter cp ' ' ; Test if char >= ' ' ret nc ; Yes, normal character then exit dec (hl) ; Control character, decrement tab count cp ContH ; Test backspace jr nz,count0 ; No backspace then jump dec (hl) ; Decrement tab counter ret ; And exit count0: cp cr ; Test carriage return jr nz,count1 ; No then jump ld (hl),0 ; Reset tab count ret ; And exit count1: cp tab ; Test tab character ret nz ; No then exit push af ; Save character ld a,(hl) ; Get tab count add a,8 ; Advance it 8 position and 0f8h ; Set it to next tab position ld (hl),a ; Save it pop af ; Restore character ret ; And exit ; ; Get character from console ; getch: ld hl,lastch ; Get pointer to last input character ld a,(hl) ; Get character ld (hl),0 ; Reset last character or a ; Test if character present ret nz ; Return if so jp ConIn ; Else get character ; ; Get console status ; gConSt: ld a,(delay) ; Get 256 bytes delay or a ; Test it jr nz,gcons0 ; Non zero, delay stil active or disabled call ConSt ; Get console status and 1 ; Test it jr nz,gcons1 ; Non zero then get character gcons0: ld a,(lastch) ; Get last character or a ; Test it jr nz,gcons3 ; Non zero then character present call ConSt ; Get console status and 1 ; Test it  ret z ; Return if no character present gcons1: call ConIn ; Get character cp ContS ; Test stop character jr nz,gcons2 ; Not then exit character call ConIn ; Get next character cp ContC ; Test if user wants to exit jp z,RamLow+00000h ; Yes then warm boot jr gConSt ; Test again gcons2: ld (lastch),a ; Save character ld a,0ffh ; Set delay counter ld (delay),a ; And save it gcons3: ld a,1 ; Character present code ret ; Return to caller ; ; Test character ; exit carry=0: cr,lf,tab,ContH or >=space ; carry=1: all other characters ; tstch: cp cr ; Test carriage return ret z ; Return if so cp tab ; Test tab ret z ; Return if so cp ContH ; Test backspace ret z ; Return if so cp rubout ret z cp ' ' ; Test >=space ret ; Return to caller ; ; Write backspace, space, backspace ; wContH: call wcont0 ; Write backspace ld c,' ' ; Load space call ConOut ; And output it wcont0: ld c,ContH ; Load backspace jp ConOut ; And output it ; ; Output message ; mess: ld a,(de) ; Get byte from buffer cp '$' ; Test last byte ret z ; Yes, then return to caller inc de ; Point to next byte push de ; Save pointer call wrcon ; Output character pop de ; Restore pointer jr mess ; And test again ; again: ld hl,tabcnt ; Get tab count pointer ld a,(tabcx1) ; Get position first character line cp (hl) ; Check it ret z ; Return if on same position ld a,' ' ; Load space call wrcon ; Output it jr again ; And test again ; ; Delete char ; entry : HL = start buffer - 1 ; B = character counter (always > 0) ; delch: dec b ; Decrement character counter ld a,(tabcnt) ; Get tab counter push af ; Save it push bc ; Save character counter ld a,(tabcx1) ; Get position first character line ld (tabcnt),a ; Save it in tab counter delch0: ld a,b ; Copy character counter or a ; Test if 0 jr z,delch2 ; Yes then jump dec b ; Decrement it inc hl ; Increment buffer pointer ld a,(hl) ; Get character from buffer push hl ; Save buffer pointer call tstch ; Test if cr,lf,tab,ContH or >=sp jr nc,delch1 ; Yes then jump rra ; Else must be control character call countc ; Count control character twice delch1: call countc ; Count character pop hl ; Get buffer pointer jr delch0 ; And test again delch2: pop bc ; Restore character counter pop af ; And tab counter push hl ; Save buffer pointer push bc ; And character counter ld hl,tabcnt ; Get tab counter pointer sub (hl) ; Calculate difference delch3: dec a ; Decrement it cp 8 ; Compare with 8 jr nc,delch4 ; Jump if >=8 push af ; Save difference call wContH ; Remove character end line pop af ; Restore counter jr delch3 ; Remove more characters delch4: pop bc ; Restore character counter pop hl ; Restore buffer pointer ret ; And return to caller ; ; Read buffer ; rdbuf: ld a,(tabcnt) ; Get current position cursor ld (tabcx1),a ; Save it rdbuf0: push ix ; Save start address buffer pop hl ; Get it in hl ld c,(hl) ; Get maximum line lenght inc hl ; Increment to line lenght position ld b,0 ; Clear line lenght counter push hl ; Save start line - 1 rdbuf1: push hl ; Save registers push bc rdbuf2: call getch ; Get character pop bc ; Restore registers pop hl and 07fh ; Mask character rdbuf3: cp ContH ; Test backspace jr nz,rdbuf4 ; Not then jump doback: ld a,b ; Test if deleting char from empty line or a jr z,rdbuf1 ; Yes then get next char pop hl ; Get start line push hl ; And save it again call delch ; Delete character jr rdbuf1 ; Get next character rdbuf4: cp ContP ; Test print enable/disable jr nz,rdbufC ; Not then jump ld a,(fContP) ; Complement print flag cpl ld (fContP),a rdbufc: cp ContX ; Test delete line jr nz,rdbufe ; Not then jump rdbufd: pop hl ; Get start line ld a,b ; Test if last character deleted or a jp z,RDBUF ; Yes start routine again push hl ; Save pointer call delch ; Delete last character line jr rdbufd ; Test last character deleted rdbufe: cp rubout ; Test delete last character jr nz,rdbuff ; Not then jump jr doback ; Part of delete key fix rdbuff: cp cr ; Test carriage return jr z,rdbufi ; Yes, then exit inc hl ; Increment pointer ld (hl),a ; And save character inc b ; Increment line counter rdbufg: push hl ; Save registers push bc call outch ; Echo character pop bc ; Restore registers pop hl cp ContC ; Test warm boot ld a,b ; Get line count jr nz,rdbufh ; No warm boot then jump cp 1 ; Test ContC is first character line jp z,RamLow+00000h ; Yes then execute warm boot rdbufh: cp c ; Test line length=maximum line length jp nz,rdbuf1 ; Not then get next character rdbufi: pop hl ; Get start line - 1 ld (hl),b ; Save line counter ld a,cr ; Load carriage return jp wrcon ; And echo it ;--------------------------- ; Error routines  ;--------------------------- SelErr: ld de,msel ; Load selected error message jr derror ; And display error ; ; File read only error ; FilRO: ld de,mfilro ; Load file R/O message ld a,0ffh ; Set file R/O message flag jr error ; And display error ; ; Read error message--b.h. ; rderr: ld de,mrderr jr derror ; ; Write error message--b.h. wrterr: ld de,mwrter jr derror ; ; Drive read only error ; rdonly: ld de,mro ; Load drive R/O message derror: xor a ; Set no file R/O message ; ; Display error message ; ; "Error message" error on d: ; Function nn ; File filename.typ ; error: ld c,a ; Save file R/O message flag push bc push de ; Save error message pointer call crout ; Display cr/lf pop de call mess ; Display error ld a,(defdrv) ; Get current drive add a,'A' ; Make ASCII ld (mdrive),a ; Save it ld de,mberr ; Load message " error on d:" call mess ; Display message ld de,mbfunc ; Load message "function " call mess ; Display message ld a,(funct) ; Get function number push af ; Save it ld bc,100 ; Display number / 100 call num ld c,10 ; Display number / 10 call num ld bc,101h ; Always display number / 1 call num pop af ; Get function number pop bc ; Get file R/O flag cp 15 ; Test if FCB used in command jr c,error3 ; Commands <16, don't show filename cp 24 jr c,error1 ; Commands 16..23 show file cp 30 jr z,error1 ; Command 30 show file cp 33 jr c,error3 ; Other commands 24..32 no file cp 37 jr c,error1 ; 33..36 show cp 40 jr nz,error3 ; 37 don't show error1: push ix ; Display "file =" sub 19 ; Test delete file function jr nz,error2 ; Not then jump or c ; Test file R/O flag jr z,error2 ; No file R/O then jump call caldir ; Get FCB from directory buffer ex (sp),hl ; Save it error2: ld de,mfile ; Get message " file =" call mess ; Display message pop hl ; Get pointer FCB ld b,8 ; Display first 8 characters call filenm ld a,'.' ; Load '.' push hl ; Save FCB pointer call wrcon ; Echo it pop hl ; Restore FCB pointer ld b,3 ; Display last 3 characters call filenm ; Absorb any pending characters: error3: call gConSt ; Test if character pending or a jr z,error4 ; No then jump call getch ; Get character jr error3 ; And test again ; ; Allow retry on read/write errors ; error4: ld a,(retflg) ; Allow retry? or a jr z,error5 ; No xor a ld (retflg),a ; Reset flag call getch cp ContC ; Control-c entered? ret nz ; No, retry error5: jp RamLow+00000h ; Do warm boot ; ; Display number ; num: ld d,-1 ; Load number -1 num1: inc d ; Increment number sub c ; Divide by c jr nc,num1 ; Not finished then loop add a,c ; Restore last value push af ; Save it ld a,d ; Test if "0" or b ; And if leading zero jr z,num2 ; Yes, then exit ld b,a ; Set no leading zero ld a,d ; Get number add a,'0' ; Make ASCII push bc ; Save registers call wrcon ; Echo number pop bc ; Restore registers num2: pop af ; Restore number ret ; And exit ; ; Display filename.typ ; filenm: inc hl ; Increment pointer FCB ld a,(hl) ; Get character from FCB and 07fh ; Mask it push hl ; Save registers push bc call wrcon ; Echo character pop bc ; Restore registers pop hl djnz filenm ; Repeat b times ret ; And exit ; ; Error messages ; Made more meaningful--b.h. ; msel: defm 'Illegal drive$' mfilro: defm 'File ' mro: defm 'R/O$' mrderr: db 'Read$' mwrter: db 'Write$' mberr: defm ' error on ' mdrive: defb 0 defb DrvSep defm '$' mbfunc: defm cr,lf,'Function', tab, '$' mfile: defm cr,lf,'File', tab, tab, '$' ; END Z80DCHAR.Z80 ; Z80DOS - Z80 Disk Operating System ; ; Version 1.0 - 05 Sept. 87 by Carson Wilson ; ; ------------------------------------------------------------------- ; ; Z80DDISK.Z80 - Return CP/M Version, Disk Functions ; ; ------------------------------------------------------------------- ; ; Return version number ; cmnd12: ld a,22h ; Set version number jr cmd25a ; And exit ; ; ----------------------------- ; ; Disk functions ; ; ----------------------------- ; ; Reset disk system ; cmnd13: ld hl,0 ; Load zero ld (login),hl ; All drives logged out ld (dskro),hl ; All drives read/write xor a ld (diff),a ; Disk changed flag ld hl,RamLow+00080h ; Set up DMA address ld (DMA),hl ; And save it call stDMA ; Do BIOS call xor a ; Set default drive = 'A' ld (defdrv),a ; Save it ld a,ResDsk ; call seldk ; Select drive 0=A, 1=B, etc. ld a,(subflg) ; Get submit flag jr cmd25a ; Exit ; ; Search for file ; cmnd17: call seldrv ; Select drive from FCB ld a,(ix+0) ; Get drive number from FCB sub '?' ; Test if '?' jr z,cmd17b ; If so all entries match ld a,(ix+14) ; Get system byte cp '?' ; Test if '?' jr z,cmd17a ; Yes, jump ld (ix+14),0 ; Load system byte with zero cmd17a: ld a,15 ; Test first 15 items in FCB cmd17b: call search ; Do search cmd17c: ld hl,(dirbuf) ; Copy directory buffer ld de,(DMA) ; To DMA address ld bc,128 ; Directory=128 bytes ldir ret ; Exit ; ; Search for next occurrence of file ; cmnd18: ld ix,(dcopy) ; Get last FCB used by search call seldrv ; Select drive from FCB call searcn ; Search next file match jr cmd17c ; And copy directory to DMA address ; ; Delete file ; cmnd19: call seldrv ; Select drive from FCB call delete ; Delete file cmd19a: ld a,(searex) ; Get exit byte 00=file found,0ffh not jr cmd25a ; And exit ; ; Rename file ; cmnd23: call seldrv ; Select drive from FCB call renam ; Rename file jr cmd19a ; And exit ; ; Return login vector ; cmnd24: ld hl,(login) ; Get login vector cmd24a: ld (pexit),hl ; Save it ret ; And exit ; ; Return current drive ; cmnd25: ld a,(defdrv) ; Get current drive cmd25a: jp exit ; And exit ; ; Return allocation vector ; cmnd27: ld hl,(alv) ; Get allocation vector jr cmd24a ; And exit ; ; Return disk R/O vector ; cmnd29: ld hl,(dskro) ; Get disk R/O vector jr cmd24a ; And exit ; ; Set file attributes ; cmnd30: call seldrv ; Select drive from FCB call cstat ; Change status jr cmd19a ; And exit ; ; Get Disk Parameter Block Address ; cmnd31: ld hl,(ixp) ; get drive table jr cmd24a ; And exit ; ; Set/get user code ; cmnd32: ld a,e ; Get user code inc a ; Test if 0ffh ld a,(user) ; Get old user code jr z,cmd25a ; If 0ffh then exit ld a,e ; Get new user code and 01fh ; Mask it ld (user),a ; Save it ret ; And exit ; ; Compute file size ; cmnd35: call seldrv ; Select drive from FCB call filsz ; Compute file size  jr cmd19a ; And exit ; ; Set random record count ; cmnd36: ld hl,32 ; Set pointer to next record call calrrc ; Calculate random record count ldrrc: ld (ix+33),d ; And save random record count ld (ix+34),c ld (ix+35),b ret ; And exit ; ; Reset individual disk drives ; cmnd37: ld a,e ; Get mask LSB cpl ; Complement it ld e,a ld a,d ; Get mask MSB cpl ; Complement it ld d,a ld hl,(login) ; Get login vector ld a,e ; Mask login vector and l ; LSB ld l,a ld a,d ; Mask login vector and h ; MSB ld h,a ld (login),hl ; Save login vector ex de,hl ; Use login vector as mask ld hl,(dskro) ; Get drive R/O vector ld a,e ; Mask drive R/O vector and l ; LSB ld l,a ld a,d ; Mask drive R/O vector and h ; LSB ld h,a ld (dskro),hl ; Save drive R/O vector ret ; And exit ; ; Select disk from FCB ; seldrv: ld a,0ffh ; Set disk select done flag ld (fldrv),a ld a,(defdrv) ; Get current drive ld (drive),a ; Save it in memory ld e,a ; Save it in register e ld a,(ix+0) ; Get drive from FCB ld (fcb0),a ; Save it cp '?' ; Test if '?' jr z,cmnd14 ; Yes, then select drive from register e and 01fh ; Mask drive ld a,e ; Test if zero jr z,seldr0 ; Select drive from register e ld a,(ix+0) ; Get drive from FCB dec a ; Decrement drive seldr0: call seldk ; Select drive ld a,(ix+0) ; Get drive from FCB and 0e0h ; Remove drive bits ld b,a ; Save register ld a,(user) ; Get user number or b ; Insert user number in FCB ld (ix+0),a ret ; And exit ; ; Select disk ; cmnd14: ld a,e ; Copy drive number ; seldk: and 0fh ; Mask drive number to 0-15 ld b,a ; Save counter ld de,(login) ; Get login vector or a ; Test drive 'a' jr z,seldk1 ; Yes then jump seldk0: rr d ; Shift login vector rr e ; Until bit 0 register e djnz seldk0 ; Is current drive seldk1: ld hl,defdrv ; Get pointer last drive bit 0,e ; Test if drive logged in jr z,seldk2 ; No, login drive cp (hl) ; Test same drive ret z ; Yes then exit seldk2: ld (hl),a ; Save new current drive push de ; Save drive logged in flag ld c,a ; Copy drive number call SelDsk ; Do BIOS select ld a,h ; Test if error or l jr z,seldk3 ; Yes, illegal drive number ld e,(hl) ; Get LSB translation vector inc hl ; Increment pointer ld d,(hl) ; Get MSB translation vector inc hl ; Increment pointer ld (trans),de ; Save translation vector ld (temp0),hl ; Save address temp0 ld de,6 add hl,de ; Point to dirbuf pointer ld de,dirbuf ; Load dirbuf pointer ld bc,8 ; Copy 8 bytes pointers to ; ..dirbuf, csv (wacd), allocation ldir ; ..vector pointer for this disk ld hl,(ixp) ; Get drive parameter address ld c,15 ; Copy 15 bytes ldir pop de ; Get drive logged in flag bit 0,e ; Test it ret nz ; Drive logged in so return ld hl,(login) ; Get login vector call sdrvb ; Set drive bit in login vector ld (login),hl ; Save login vector jr initdr ; And setup drive tables seldk3: ld hl,(StSel) ; Load error message address jp (hl) ; And display error ; ; Init drive ; Clear allocation vector bit buffer after drive reset ; initdr: ld de,(maxlen) ; Get length alv buffer-1 (bits) ld a,3 ; Divide by 8 initd0: srl d ; To get bytes rr e dec a jr nz,initd0 inc de ; Increment, so all bits are cleared ld hl,(alv) ; Get pointer alv buffer push hl initd1: ld (hl),0 ; Clear 8 bits inc hl ; Increment pointer dec de ; Decrement counter ld a,d ; Test if counter zero or e jr nz,initd1 ; Not then jump pop hl ; Get alv pointer ld de,(ndir0) ; Get first two bytes alv buffer ld (hl),e ; Save LSB inc hl ; Increment pointer ld (hl),d ; Save MSB ld hl,(temp0) ; Clear number of files xor a ; On this drive ld (hl),a ; Clear LSB inc hl ; Increment pointer ld (hl),a ; Clear MSB ld (subflg),a ; Clear submit flag (reset disk command) call setfct  ; Set file count initd2: ld a,0ffh ; Update directory checksum call rddir ; Read FCB's from directory call tstfct ; Test last FCB ret z ; Yes then exit call caldir ; Calculate entry point FCB ld a,(hl) ; Get first byte FCB cp 0e5h ; Test empty directory entry jr z,initd2 ; Yes then get next FCB cp 021h ; Test time stamp jr z,initd2 ; Yes then get next FCB ld a,(user) ; Get user number cp (hl) ; Test if user is same jr nz,initd3 ; No then jump inc hl ; Point to file name ld a,(hl) ; Get first char filename sub '$' ; Test if '$' jr nz,initd3 ; Not then jump dec a ; Load a with 0ffh ld (subflg),a ; Save it in subflg initd3: ld c,1 ; Set bit in alv buffer call fillbb ; Set bits from FCB in alv buffer call setlf ; Update last file count jr initd2 ; ; ; Set drive bit in HL ; sdrvb: ex de,hl ; Copy hl=>de ld hl,1 ; Get mask drive "a" ld a,(defdrv) ; Get current drive or a ; Test if drive "a" jr z,sdrvb1 ; Yes then done sdrvb0: add hl,hl ; Get next mask dec a ; Decrement drive counter jr nz,sdrvb0 ; And test if done sdrvb1: ld a,d ; Hl=hl or de or h ld h,a ld a,e or l ld l,a ret ; Exit ; ; Calculate sector/track directory ; stdir: ld hl,(filcnt) ; Get FCB counter directory srl h ; Divide by 4 rr l ; (4 FCB's / sector) srl h rr l ld (recdir),hl ; Save value (used by checksum) ex de,hl ; Copy it to de ld hl,0 ; Clear hl ; ; Calculate sector/track ; Entry: hl,de = sector number (128 byte sector) ; Exit: set track = hl,de / maxsec ; set sector = hl,de mod maxsec ; calst: ld bc,(maxsec) ; Get sectors/track ld a,17 ; Set up loop counter calst0: or a ; Test hl>=bc sbc hl,bc ccf jr c,calst1 ; Yes then jump add hl,bc ; No then restore hl or a ; And clear carry calst1: rl e ; Shift result in de rl d dec a ; Test last bit done jr z,calst2 ; Yes then exit rl l ; Shift next bit in hl rl h jr calst0 ; Continue calst2: push hl ; Save sector number ld hl,(nftrk) ; Get first track add hl,de ; Add track number ld b,h ; Copy it to bc ld c,l call SetTrk ; BIOS call set track pop bc ; Restore sector number ld de,(trans) ; Get translation table address call SecTrn ; BIOS call sector translation ld b,h ; Copy result to bc ld c,l jp SetSec ; BIOS call set sector ; ; Get disk map block number from FCB ; ; Exit: HL = address FCB ; DE = disk map ; BC = offset in disk map ; getdm: ld c,(ix+32) ; Get next record ld a,(nblock) ; Get number of blocks ld b,a ; Save it getdm0: srl c ; Shift next record djnz getdm0 ; Number of blocks times getdm1: cpl ; Complement number of blocks add a,9 ; Add 9 ld b,a ; B=8-number of blocks ld a,(nextnd) ; Get extend mask and (ix+12) ; Mask with extend rrca ; Rotate one right getdm2: rlca ; Rotate one left djnz getdm2 ; 8-number of blocks times getdm3: add a,c ; Add the two values to get entry FCB getdm4: push ix ; Get FCB address pop hl ld c,16 ; Add offset 16 to point to dm add hl,bc ld c,a ; Add entry FCB add hl,bc ld a,(maxlen+1) ; Test 8 bits/16 bits FCB entry or a jr nz,getdm5 ; 16 bits => jump ld e,(hl) ; Get 8 bit value ld d,0 ; Make MSB zero ret ; And exit getdm5: add hl,bc ; Add twice (16 bit values) ld e,(hl) ; Get LSB inc hl ; Increment pointer ld d,(hl) ; Get MSB dec hl ; Decrement pointer ret ; And exit ; ; Calculate sector number ; entry: de=block number from FCB ; calsec: ld hl,0 ; Clear MSB sector number ld a,(nblock) ; Get loop counter ld b,a ; Save it in b calsc0: sla e ; Shift l,d,e rl d rl l djnz calsc0 ; B times calsc1: ld a,(nmask) ; Get sector mask and (ix+32) ; And with next record or e ; Set up LSB sector number ld e,a ret ; And exit ; ; Calculate dirbuf entry point ; caldir: ld hl,(dirbuf) ; Get start address dirbuf ld a,(secpnt) ; Get sector pointer add a,l ; Add l=l+a ld l,a ret nc ; No carry exit inc h ; Increment h ret ; And exit ; ; Init file count ; setfct: ld hl,-1 ; Set up file count ld (filcnt),hl ; Save it ret ; And exit ; ; Test file count ; tstfct: ld hl,(filcnt) ; Test file count=0ffffh ld a,h ; Get MSB and l ; And LSB inc a ; Test if result=0ffh ret ; And exit ; ; Set last file ; setlf: call tstlf ; Test last file ret c ; No then exit inc de ; Increment last file ld (hl),d ; Save it in temp0 dec hl ld (hl),e ret ; And exit ; ; Test last file ; tstlf: ld hl,(temp0) ; Get pointer to last file ld de,(filcnt) ; Get file counter ld a,e ; Subtract de-(hl) sub (hl) inc hl ld a,d sbc a,(hl) ret ; Exit ; ; Get next FCB from drive ; Entry: A = 0 check checksum ; A = 0ffh update checksum ; rddir: ld c,a ; Save checksum flag ld hl,(filcnt) ; Get file counter inc hl ; Increment it ld (filcnt),hl ; And save it ld de,(nfiles) ; Get maximum number of files  or a ; Clear carry sbc hl,de ; Test if last file add hl,de jr z,rddir0 ; No jump jr nc,setfct ; Yes set file count to 0ffffh rddir0: ld a,l ; Get file count LSB add a,a ; *32 add a,a add a,a add a,a add a,a and 060h ; Mask it ld (secpnt),a ; Save it for later use ret nz ; Return if not first FCB sector push bc ; Save checksum flag call stdir ; Calculate sector/track directory call readdr ; Read sector directory pop bc ; Restore checksum flag ; ; Update/check checksum directory ; entry c=0 check checksum, c=0ffh update checksum ; chkdir: ld hl,(ncheck) ; Get number of checked records ld de,(recdir) ; Get current record or a ; Clear carry sbc hl,de ; Test current record ret z ; Exit if zero ret c ; Exit if greater then ncheck ld hl,(dirbuf) ; Get dirbuf ld b,128 ; Set up counter xor a ; Clear checksum chkdr0: add a,(hl) ; Add checksum inc hl ; Increment pointer djnz chkdr0 ; 128 times ld hl,(csv) ; Get pointer checksum directory add hl,de ; Add current record inc c ; Test checksum flag jr z,chkdr1 ; 0ffh=> update checksum cp (hl) ; Test checksum ret z ; Exit if ok ld a,true ld (diff),a ; Set disk changed flag jp setfn chkdr1: ld (hl),a ; Update checksum ret ; And exit ; ; Read sector from drive ; ; readr and writer modified to give separate error message--b.h. ; readr: call read ; BIOS call read sector ld hl,(rderr) jr write0 ; Test exit code ; ; Write sector on drive ; writer: call write ; BIOS call write sector ld hl,(wrterr) write0: or a ; Test exit code ret z ; Exit if ok ld (retflg),a ; Allow retry for read/write errors jp (hl) ; DOS error on d: write error ; ; Read directory from drive ; readdr: call DMAdir ; Set up DMA directory call readr ; Read record jr stDMA ; Set up DMA user ; ; Write directory on drive ; writdr: ld c,0ffh ; Update checksum directory call chkdir call DMAdir ; Set up DMA directory ld c,1 ; Write directory flag call writer ; Write record jr stDMA ; Set up DMA user ; ; Set DMA address command ; cmnd26: ld (DMA),de ; Save DMA address ; stDMA: ld bc,(DMA) ; Get DMA address jr DMAdr0 ; And do BIOS call ; ; Set DMA address directory ; DMAdir: ld bc,(dirbuf) ; Get DMA address directory DMAdr0: jp setDMA ; BIOS call set DMA ; ; Get bit from allocation vector buffer ; ; Entry: DE = block number ; Exit: A = bit in LSB ; B = bitnumber in a ; HL = pointer in alv buffer ; getbit: ld a,e ; Get bit number and 7 ; Mask it inc a ; Add 1 ld b,a ; Save it ld c,a ; Twice srl d ; Get byte number rr e ; De=de/8 srl d rr e srl d rr e ld hl,(alv) ; Get start address alv buffer add hl,de ; Add byte number ld a,(hl) ; Get 8 bits getbt0: rlca ; Get correct bit djnz getbt0 ld b,c ; Restore bit number ret ; And return to caller ; ; Set/reset bit in allocation vector buffer ; Entry DE = block number ; C = 0 reset bit, c=1 set bit ; setbit: push bc ; Save set/reset bit call getbit ; Get bit and 0feh ; Mask it pop de ; Get set/reset bit or e ; Set/reset bit setbt0: rrca ; Rotate bit in correct position djnz setbt0 ld (hl),a ; Save 8 bits ret ; And return to caller ; ; Fill bit buffer from FCB in dirbuf ; Entry: C = 0 reset bit ; C = 1 set bit ; fillbb: call caldir ; Get directory entry ld de,16 ; Get offset dm block add hl,de ; Add offset ld b,e ; Get block counter fillb0: ld e,(hl) ; Get LSB block number inc hl ; Increment pointer ld d,0 ; Reset MSB block number ld a,(maxlen+1) ; Test >256 blocks present or a jr z,fillb1 ; No then jump dec b ; Decrement block counter ld d,(hl) ; Get correct MSB inc hl ; Increment pointer fillb1: ld a,d ; Test block number or e jr z,fillb2 ; Zero then get next block push hl ; Save pointer push bc ; Save counter and set/reset bit ld hl,(maxlen) ; Get maximum lenght alv buffer or a ; Reset carry sbc hl,de ; Test de<=maxlen alv buffer call nc,setbit ; Yes then insert bit pop bc ; Get counter and set/reset bit pop hl ; Get pointer fillb2: djnz fillb0 ; Repeat for all dm entries ret ; And return to caller ; ; Set write protect disk ; cmnd28: setwpd: ld hl,(dskro) ; Get disk R/O vector call sdrvb ; Include drive bit ld (dskro),hl ; Save disk R/O bit setfn: ld de,(nfiles) ; Get maximum number of files-1 inc de ; Increment it ld hl,(temp0) ; Get pointer to disk parameter block ld (hl),e ; And save number of files inc hl ld (hl),d ret ; And return to caller ; ; Check file R/O bit ; chkfro: call caldir ; Get directory entry ld a,(searpu) ; Test if public file ld b,a ; Save it ld a,(searqu) ; Test if question mark used in file name or b ; A=public file or question mark used chkfr0: ld de,2 ; Offset to public file bit add hl,de ; Add offset or a ; Test question mark used jr z,chkfr1 ; No then don't test public file bit bit 7,(hl) ; Test public file jr nz,chkfr2 ; Yes then error chkfr1: ld e,7 ; Offset to file R/O bit add hl,de ; Add offset bit 7,(hl) ; Test file R/O jr nz,chkfr2 ; Yes then error or a ; Test question mark used ret z ; No then don't test system file bit inc hl ; Increment to system file bit 7,(hl) ; Test system file ret z ; No system file then ok chkfr2: ld hl,(sfilro) ; Get pointer to file R/O message jp (hl) ; Display message ; ; Check drive read only ; chkro: ld hl,(dskro) ; Get drive R/O vector call sdrvb ; Set drive bit sbc hl,de ; Test extra bit added ret nz ; Yes then drive not R/O ld hl,(stro) ; Get pointer to drive R/O message jp (hl) ; Display message ; ; Get free block from allocation vector buffer ; Entry: DE = old block number ; Exit: DE = new block number (0 if no free block) ; HL counts up ; DE counts down ; getfre: ld h,d ; Copy old block to hl ld l,e getfr0: ld a,d ; Test down counter is zero or e jr z,getfr1 ; Yes then jump dec de ; Decrememt down counter push hl ; Save up/down counter push de call getbit ; Get bit from alv buffer rra ; Test if zero jr nc,getfr3 ; Yes then found empty block pop de ; Get up/down counter pop hl getfr1: ld bc,(maxlen) ; Get maximum alv lenght-1 in bc or a ; Clear carry sbc hl,bc ; Test hl>=lenght alv-1 add hl,bc ; Restore hl (flags are not affected) jr nc,getfr2 ; End buffer then jump inc hl ; Increment up counter push de ; Save down/up counter push hl ex de,hl ; Save up counter in de call getbit ; Get bit from alv buffer rra ; Test if zero jr nc,getfr3 ; Yes then found empty block pop hl ; Get down/up counter pop de jr getfr0 ; And test next block getfr2: ld a,d ; Test if last block tested or e jr nz,getfr0 ; No then test next block ret ; Exit (de=0) getfr3: scf ; Set block number used rla ; Save bit call setbt0 ; Put bit in alv buffer pop de  ; Get correct counter pop hl ; Restore stack pointer ret ; Exit (de=block number) ; ; Search for file name ; Entry: A = number of bytes to search for ; search: ld (searnb),a ; Save number of bytes ld a,0ffh ; Set exit code to 0ffh (not found) ld (searex),a ld (dcopy),ix ; Copy FCB pointer to ram (search next) call setfct ; Initiate file counter ; ; Mod. 0.1 had a bug in which the directory of a changed disk would not be ; read. Adding call home forces a directory read--b.h. ; call home ; Call BIOS procedure ; ; Search next file name ; SearcN: xor a ; Clear accu, check checksum dir. ; Next 3 lines moved here from SEARC1: to clear flags ; when last file in dir. is a public file. - C.W. 2/87 ld (searqu),a ; Clear question mark detected flag ld (searpu),a ; Clear public file flag res 7,(ix+8) ; Reset public/system file flag ; call rddir ; Get FCB from directory call tstfct ; Test if past last entry jp z,searc8 ; Yes then jump ld de,(dcopy) ; Get FCB pointer ld a,(de) ; Get first byte cp 0e5h ; Test if searching empty directory jr z,searc1 ; Yes then jump push de ; Save FCB pointer call tstlf ; Test last file on this drive pop de ; Restore FCB pointer jr nc,searc8 ; Yes then jump searc1: call caldir ; Get entry in directory ld a,(hl) ; Get first byte directory entry cp 021h ; Test time stamp jr z,searcn ; Yes then get next directory entry ld a,(searnb) ; Get number of bytes to search for ld b,a ; Save it in counter xor a ; Clear accu ld c,a ; Clear counter searc2: ld a,b ; Test if counter is zero or a jr z,searc9 ; Yes then jump ld a,(de) ; Get byte from FCB xor '?' ; Test if question mark and 07fh ; Mask it (remove high bit) jr z,searc6 ; Yes then jump ld a,c ; Get FCB counter or a ; Test whether first byte jr nz,searc3 ; No ld a,(flags) ; Yes. get flag byte bit 0,a ; Test public file enable jr z,searc3 ; No inc hl ; Yes. get pointer to public bit inc hl bit 7,(hl) ; Test public bit directory dec hl ; Restore pointer dec hl jr z,searc3 ; No public file then jump ld a,(de) ; Get first byte FCB cp 0e5h ; Test if searching empty directory jr z,searc3 ; Yes then jump xor (hl) ; Test FCB=directory entry and 07fh ; Mask it jr z,searc5 ; Yes then jump and 0e0h ; Mask user number jr nz,searc3 ; Not the same then jump dec a ; A=0ffh ld (searpu),a ; Set public file found set 7,(ix+8) ; Set public/system file flag jr searc5 ; Jump found searc3: ld a,c ; Get FCB counter cp 13 ; Test if at user code jr z,searc5 ; Yes then no test cp 12 ; Test if at extend number ld a,(de) ; Get byte from FCB jr z,searc7 ; Jump if extent number xor (hl) ; Test byte FCB=byte directory entry and 07fh ; Mask it searc4: jr nz,searcn ; Not the same then get next entry searc5: inc de ; Increment pointer FCB inc hl ; Increment pointer directory entry inc c ; Increment counter dec b ; Decrement counter jr searc2 ; Test next byte searc6: dec a ; Set question mark found flag ld (searqu),a jr searc5 ; Jump found searc7: push bc ; Save counters xor (hl) ; Test extends ld b,a ; Save it ld a,(nextnd) ; Get extent mask cpl ; Complement it and 01fh ; Mask it and b ; Mask extents pop bc ; Restore counters jr searc4 ; And test result searc8: call setfct ; Error set file counter ld a,0ffh ; And set exit code ld (pexit),a ret ; Return to caller searc9: ld a,(searqu) ; Get question mark found flag ld b,a ; Save it ld a,(searpu) ; Get public file flag and b ; Test if public file and question mark jr nz,searc4 ; Yes then search for next entry call setlf ; Update last file count (empty FCB) ld a,(filcnt) ; Get file counter and 3 ; Mask it ld (pexit),a ; And set exit code xor a ; Clear exit code search ld (searex),a ret ; And return to caller ; ; Delete file ; delete: call chkro ; Check disk R/O  ld a,12 ; Number of bytes to search for call search ; Search file del0: call tstfct ; Test if file found ret z ; Not then exit call chkfro ; Check file R/O call caldir ; Get entry point directory ld (hl),0e5h ; Remove file ld c,0 ; Remove bits alv buffer call fillbb call wrFCB ; Write directory buffer on disk call searcn ; Search next entry jr del0 ; And test it ; ; Rename file ; renam: call chkro ; Check disk R/O ld a,12 ; Number of bytes to search for call search ; Search file renam0: call tstfct ; Test if file found ret z ; Not then exit call chkfro ; Check file R/O push ix ; Save FCB entry pop hl ; Get it in hl ld de,16 ; Offset to new name add hl,de ; Add offset ex de,hl ; Copy hl=>de call caldir ; Get directory entry ld b,11 ; Set up loop counter renam1: inc hl ; Increment directory pointer inc de ; Increment FCB pointer ld a,(de) ; Get character from FCB and 07fh ; Mask it cp '?' ; Test if question mark jr z,renam2 ; Yes then do not change char. on disk ld c,a ; Save it in c ld a,(hl) ; Get character from directory and 080h ; Mask status bit or c ; Or with new character ld (hl),a ; Save in directory renam2: djnz renam1 ; Loop until done call wrFCB ; And write directory on disk call searcn ; Search next file jr renam0 ; And test it ; ; Change status file ; cstat: call chkro ; Check disk R/O ld a,12 ; Number of bytes to search for call search ; Search file cstat0: call tstfct ; Test if file found ret z ; Not then exit push ix ; Save FCB entry pop de ; Get it in hl call caldir ; Get directory entry ld b,11 ; Set up loop counter cstat1: inc hl ; Increment directory pointer inc de ; Increment FCB pointer ld a,(de) ; Get status bit from FCB and 080h ; Mask it ld c,a ; Save it in c ld a,(hl) ; Get character from directory and 07fh ; Mask it or c ; Or with new status bit ld (hl),a ; Save in directory djnz cstat1 ; Loop until done call wrFCB ; And write directory to disk call searcn ; Search next file jr cstat0 ; And test it ; ; Compute file size ; filsz: ld bc,0 ; Reset file size lenght ld d,c call ldrrc ; Save it in FCB+33,34,35 ld a,12 ; Number of bytes to search for call search ; Search file filsz0: call tstfct ; Test if file found ret z ; Not then exit call caldir ; Get directory entry ex de,hl ; Copy to de ld hl,15 ; Offset to next record call calrrc ; Calculate random record count ld a,d ; Test LSB < (ix+33) sub (ix+33) ld a,c ; Test isb < (ix+34) sbc a,(ix+34) ld a,b ; Test MSB < (ix+35) sbc a,(ix+35) call nc,ldrrc ; Write new maximum call searcn ; Search next file jr filsz0 ; And test it ; ; Write FCB on disk ; wrFCB: call stdir ; Calculate sector/track directory jp writdr ; Write directory on disk ; ; Find file ; findf: ld a,15 ; Number of bytes to search for JP search ; Search file ; ; Open file command ; cmnd15: call seldrv ; Select drive from FCB ld (ix+14),0 ; Clear FCB+14 ; ; Open file ; openf: call findf ; Find file call tstfct ; Test file found ret z ; No then exit openf0: ld a,(ix+8) ; Get public/system file bit push af ; Save it ld a,(ix+12) ; Get extent number from FCB push af ; Save it call caldir ; Get directory entry push hl ; Save it ld e,8 ; Set access date/time call stime ; Zero flag set if stamps present pop hl ; Get directory entry push ix ; Save FCB entry pop de ; Get in in de ld bc,32 ; Number of bytes to move ldir ; Move directory to FCB call z,wrFCB ; Write directory to disk if stamps ; ..Present nowrit: set 7,(ix+14) ; Set FCB/file not modified ld b,(ix+12) ; Get extent number ld c,(ix+15) ; Get next record number pop af ; Get old extent number ld (ix+12),a ; Save it cp b ; Compare old and new extent number jr z,openf1 ; Same then jump ld c,0 ; Set next record count to 0 jr nc,openf1 ; Old extent >= new extent then jump ld c,80h ; Set next record count to maximum openf1: ld (ix+15),c ; Save next record count pop af ; Get public/system file bit rl (ix+8) ; Remove MSB from ix+8 rla ; Set new MSB in carry rr (ix+8) ; Save carry in ix+8 ret ; And return to caller ; ; Close file command ; cmnd16: call seldrv ; select drive from FCB ; ; Close file ; close: bit 7,(ix+14) ; test FCB/file modified ret nz ; not then no close required call chkro ; test disk R/O ld a,15 ; number of bytes to search for call search ; search file call tstfct ; test file present ret z ; no then exit call chkfro ; check file R/O call caldir ; get directory entry ld bc,16 ; offset to dm block add hl,bc ; add offset ex de,hl ; save hl in de push ix ; save FCB pointer pop hl ; get it in hl add hl,bc ; add offset ld a,(maxlen+1) ; test number of block >= 256 or a jr z,close0 ; no then jump dec b ; set flag close0: call copydm ; copy and test blocks ex de,hl ; exchange copy direction call copydm ; copy and test blocks ex de,hl ; exchange copy direction jr nz,close4 ; block not the same then error inc hl ; increment pointer FCB inc de ; increment pointer directory bit 0,b ; test number of block >= 256 jr z,close1 ; no then jump inc hl ; increment pointer FCB inc de ; increment pointer directory dec c ; decrement counter close1: dec c ; decrement counter jr nz,close0 ; not ready then jump ld hl,-20 ; add -20 to get extent number add hl,de ; hL contains pointer to extent number ld a,(ix+12) ; get extent number FCB cp (hl) ; compare with extent number directory jr c,close3 ; fCB < directory then jump ld (hl),a ; save extent number in directory inc hl ; get pointer to next record inc hl inc hl ld a,(ix+15) ; get next record FCB close2: ld (hl),a ; save next record in directory close3: ld e,4 ; Set last update date/time call stime ; Update time call caldir ; Get directory entry ld bc,11 ; Point to archive byte add hl,bc res 7,(hl) ; reset archive bit res 7,(ix+11) ; reset bit in FCB jp wrFCB ; write FCB on disk ; close4: ld a,0ffh ; flag error ld (pexit),a ret ; and return to caller ; ; Copy and test disk map ; ; Entry: HL = pointer to first FCB ; DE = pointer to second FCB ; B = 000h if less then 256 blocks ; 0ffh if more or equal to 256 blocks ; Exit: Z blocks are the same ; NZ blocks are not the same ; copydm: ld a,(hl) ; get byte first FCB bit 0,b ; test number of blocks >=256 jr z,copyd0 ; No then jump inc hl ; Increment pointer or (hl) ; Test byte =0 dec hl ; Decrement pointer copyd0: or a ; Test block number is zero jr nz,copyd1 ; No then compare blocks ld a,(de) ; Copy block from other FCB in empty location ld (hl),a bit 0,b ; Test number of blocks >=256 ret z ; No then exit inc hl ; Increment to MSB block numbers inc de ld a,(de) ; Copy block from other FCB in empty location ld (hl),a jr copyd2 ; Jump trick to save space copyd1: ld a,(de) ; Get block number first FCB sub (hl) ; Test if the same ret nz ; Not then return or b ; Test if >=256 blocks ret z ; No then return inc hl ; Increment to MSB block numbers inc de copyd2: ld a,(de) ; Get block number first FCB sub (hl) ; Test if the same dec hl ; Decrement block FCB pointers dec de ret ; And exit to caller ; ; Make file command ; cmnd22: call seldrv ; Select drive from FCB ld (ix+14),0 ; Clear FCB+14 ; ; Make file ; make: call chkro ; Check drive R/O ld a,(ix+0) ; Get first byte FCB push af ; Save it ld (ix+0),0e5h ; Set first byte to empty file ld a,1 ; Search for 1 byte call search ; Search empty file pop af ; Get first byte FCB ld (ix+0),a ; Restore it call tstfct ; Test empty file found ret z ; No then return error xor a  ; Clear FCB+13 ld (ix+13),a push ix ; Save FCB pointer pop hl ; Get it back in hl ld de,15 ; Prepare offset add hl,de ; Add it ld b,17 ; Set loop counter make0: ld (hl),a ; Clear FCB+15 up to FCB+31 inc hl ; Increment pointer djnz make0 ; And clear all bytes ld e,2 ; Set creation date call stime ; Update time in directory ld e,4 ; Set last update date/time call stime ; Update time in directory ld e,8 ; Set access date/time call stime res 7,(ix+8) ; Reset public/system file bit res 7,(ix+11) ; Reset archive bit if present call caldir ; Get directory entry push ix ; Save FCB entry pop de ; Get it in de ex de,hl ; Exchange FCB and directory entry ld bc,32 ; Number of bytes to move ldir ; Move bytes call wrFCB ; Write FCB on disk set 7,(ix+14) ; Set FCB/file not modified ret ; And return to caller ; ; Open next extent ; openex: bit 7,(ix+14) ; Test if FCB/file modified (write) jr nz,openx2 ; Not then jump call close ; Close current FCB ld a,(pexit) ; Get exit code inc a ; Test if error ret z ; Yes then exit call calnex ; Calculate next extent jr c,openx3 ; Error then jump jr nz,openx5 ; fCB present from close then jump openx0: ld a,15 ; Search first 15 bytes call search ; Search for file openx1: call tstfct ; Test if file found jr nz,openx5 ; Yes then jump ld a,(rdwr) ; Test read/write flag or a ; Test if read jr z,openx3 ; Yes then error call make ; Make new extent if write call tstfct ; Test if succesfull jr nz,openx6 ; Yes then exit jr openx3 ; No then error openx2: call calnex ; Calculate next extent jr c,openx3 ; Error then jump bit 7,(ix+10) ; Test system file bit jr z,openx0 ; No system file then jump call findf ; Search for file jr openx1 ; Use same routine openx3: set 7,(ix+14) ; Set FCB/file not modified ld a,0ffh ; Set exit code openx4: ld (pexit),a ret ; And return to caller openx5: call openf0 ; Open file openx6: xor a ; And clear exit code jr openx4 ; Use same routine ; ; Calculate next extent ; ; Exit: C overflow detected ; Z search next extent ; NZ next extent present (close) ; calnex: ld b,(ix+12) ; Get extent number ld c,(ix+14) ; Get FCB+14 bit 6,c ; Test error bit random record scf ; Set error flag ret nz ; Non zero then error exit inc b ; Increment extent number ld a,b ; Get extent number and 01fh ; Mask it ld b,a ; Save it in b jr nz,calnx0 ; Non zero then jump inc c ; Increment FCB+14 ld a,c ; Get it in a and 03fh ; Mask it ld c,a ; Save it in c scf ; Set error flag ret z ; And return if file overflow xor a ; Clear zero flag (not same extent) jr calnx1 ; And save extent number and FCB+14 calnx0: ld a,(nextnd) ; Get next extent mask and b ; Test if same extent (close) calnx1: ld (ix+12),b ; Save extent number ld (ix+14),c ; Save FCB+14 ret ; And return to caller ; ; Read random record command ; cmnd33: call seldrv ; Select drive from FCB ; ; Read random sector ; rdran: xor a ; Set read/write flag call ldFCB ; Load random record in FCB jr z,reads ; No error then read sector ret ; Return error ; ; Read sequential ; cmnd20: call seldrv ; Select drive from FCB ; ; Read sector ; reads: xor a ; Set read/write flag ld (rdwr),a ; Save it ld a,(ix+32) ; Get record counter cp 080h ; Test if last record this extent jr nc,reads1 ; Yes then open next extent cp (ix+15) ; Test if greater then current record jr c,reads2 ; No then get record reads0: ld a,1 ; Set end of file flag ld (pexit),a ; Save it ret ; And return to caller reads1: call openex ; ..To open next extent ld a,(pexit) ; Get exit code or a jr nz,reads0 ; Yes then end of file ld (ix+32),0 ; Clear record counter reads2: call getdm ; Get block number from dm in FCB ld a,d ; Test block number = 0 or e jr z,reads0 ; Yes then end file call calsec ; Calculate sector number (128 bytes) call calst ; Calculate sector/track number call readr ; Read data ld a,(funct) ; Get function number cp 20 ; Test if read sequential ret nz ; No then return inc (ix+32) ; Increment next record counter ret ; And return to caller ; ; ClrDsk - Reset Login Vector if disk change detected, and select drive ; from FCB. ; ; Instead of setting disks to read-only, "diff" flag is set by ; calls to wrFCB from Delete, Rename, Change Status, Open, Close, ; Select, and Make if the disk in use has changed. Next call to ; write sequential or random then resets all disks. Two ; consecutive calls to write between which a disk was changed may ; overwrite data, as is also the case with CP/M. ; clrdsk: ld a,(diff) ; Disk changed flag or a jr z,clrd0 xor a ld (diff),a ; Clear flag ld h,a ld l,a ld (login),hl ; Clear all drives ; ..(crude but effective) clrd0: call seldrv ; Select disk from FCB ret ; ; Write random record commands ; cmnd34: cmnd40: call clrdsk ; Reset disks if changed ; ..and select drive from FCB ; ; Write random sector ; wrran: ld a,0ffh ; Set read/write flag call ldFCB ; Load FCB from random record jr z,writes ; No error then write record ret ; Return error ; ; Write sequential ; cmnd21: call clrdsk ; Reset disks if changed ; ..and select drive from FCB ; ; Write sector ; writes: ld a,0ffh ; Set read/write flag ld (rdwr),a ; And save it call chkro ; Check disk R/O bit 7,(ix+9) ; Test if file R/O jp nz,chkfr2 ; Yes then file R/O message bit 7,(ix+8) ; Test if public or system file jp nz,chkfr2 ; Yes then file R/O message ld a,(ix+32) ; Get record count cp 080h ; Test if end this extent jr c,writs0 ; Yes then open next extent call openex ; Open next extent ld a,(pexit) ; Get error code or a jp nz,writs9 ; Error then directory full error ld (ix+32),0 ; Clear record counter writs0: call getdm ; Get block number from FCB ld a,d ; Test if block number = 0 or e jr nz,writs5 ; No then write sector push hl ; Save pointer to block number ld a,c ; Test first block number in extent or a jr z,writs1 ; Yes then jump dec a ; Decrement pointer to block number call getdm4 ; Get previous blocknumber writs1: call getfre ; Get nearest free block pop hl ; Get pointer to block number ld a,d ; Test if blocknumber = 0 or e jr z,writs8 ; Yes then disk full error res 7,(ix+14) ; Reset FCB/file modified ld (hl),e ; Save blocknumber ld a,(maxlen+1) ; Get number of blocks or a ; Test if <256 jr z,writs2 ; Yes then jump inc hl ; Increment to MSB block number ld (hl),d ; Save MSB block number writs2: ld c,2 ; Set write new block flag ld a,(nmask) ; Get sector mask and (ix+32) ; Mask with record counter jr z,writsx ; Zero then ok (at start new record) ld c,0 ; Else clear new block flag writsx: ld a,(funct) ; Get function number sub 40 ; Test if write rr with zero fill jr nz,writs6 ; No then jump push de ; Save blocknumber ld hl,(dirbuf) ; Use directory buffer for zero fill ld b,128 ; 128 bytes to clear writs3: ld (hl),a ; Clear directory buffer inc hl ; Increment pointer djnz writs3 ; Clear all bytes call calsec ; Calculate sector number (128 bytes) ld a,(nmask) ; Get sector mask ld b,a ; Copy it inc b ; Increment it to get number of writes cpl ; Complement sector mask and e ; Mask sector number ld e,a ; And save it ld c,2 ; Set write new block flag writs4: push hl ; Save registers push de push bc call calst ; Calculate sector/track call DMAdir ; Set DMA directory buffer pop bc ; Get write new block flag push bc ; Save it again call writer ; Write record on disk pop bc ; Restore registers pop de pop hl ld c,0 ; Clear write new block flag inc e ; Increment sector number djnz writs4 ; Write all blocks call stDMA ; Set user DMA address pop de ; Get block number writs5: ld c,0 ; Clear write new block flag writs6: res 7,(ix+14) ; Reset FCB/file modified flag push bc ; Save it call calsec ; Calculate sector number (128 bytes) call calst ; Calculate sector/track pop bc ; Get write new block flag call writer ; Write record on disk ld a,(ix+32) ; Get record counter cp (ix+15) ; Compare with next record jr c,writs7 ; If less then jump inc a ; Increment record count ld (ix+15),a ; Save it on next record position res 7,(ix+14) ; Reset FCB/file modified flag writs7: ld a,(funct) ; Get function number cp 21 ; Test write sequential ret nz ; Not then return inc (ix+32) ; Increment record count ret ; And return to caller writs8: ld a,2 ; Set disk full error ld (pexit),a ret ; And return to caller writs9: ld a,1 ; Set directory full flag ld (pexit),a ret ; And return to caller ; ; Load FCB for random read/write ; ; Exit: Z no error ; NZ error occured ; ldFCB: ld (rdwr),a ; Save read/write flag ld a,(ix+33) ; Get first byte random record ld d,a ; Save it in d res 7,d ; Reset MSB to get next record rla ; Shift MSB in carry ld a,(ix+34) ; Load next byte random record rla ; Shift carry push af ; Save it and 01fh ; Mask next extent ld c,a ; Save it in c pop af ; Get byte rla ; Shift 4 times rla rla rla and 0fh ; Mask it ld b,a ; Save FCB+14 ld a,(ix+35) ; Get next byte random record ld e,6 ; Set random record to large flag cp 4 ; Test random record to large jr nc,ldFCB8 ; Yes then error rlca ; Shift 4 times rlca rlca rlca add a,b ; Add byte ld b,a ; Save FCB+14 in b ld (ix+32),d ; Set next record count ld d,(ix+14) ; Get FCB+14 bit 6,d ; Test error random record jr nz,ldFCB0 ; Yes then jump ld a,c ; Get new extent number cp (ix+12) ; Compare with FCB jr nz,ldFCB0 ; Not equal then open next extent ld a,b ; Get new FCB+14 xor (ix+14) ; Compare with FCB+14 and 03fh ; Mask it jr z,ldFCB6 ; Equal then return ldFCB0: bit 7,d ; Test FCB modified (write) jr nz,ldFCB1 ; No then jump push de ; Save registers push bc call close ; Close extent pop bc ; Restore registers pop de ld e,3 ; Set close error ld a,(pexit) ; Get exit code inc a jr z,ldFCB7 ; Error then exit ldFCB1: ld (ix+12),c ; Save new extent number ld (ix+14),b ; Save new FCB+14 bit 7,d ; Test FCB modified (previous FCB) jr nz,ldFCB3 ; No then jump ldFCB2: ld a,15 ; Set number of bytes to search for call search ; Search next FCB jr ldFCB4 ; Jump ldFCB3: bit 7,(ix+10) ; Test if system file jr z,ldFCB2 ; No use search call findf ; Open file ldFCB4: ld a,(pexit) ; Get error code inc a jr nz,ldFCB5 ; No error then exit ld a,(rdwr) ; Get read/write flag ld e,4 ; Set read empty record inc a jr nz,ldFCB7 ; Read then error call make ; Make new FCB ld e,5 ; Set make error ld a,(pexit) ; Get error code inc a jr z,ldFCB7 ; Error then exit jr ldFCB6 ; No error exit (zero set) ldFCB5: call openf0 ; Open file ldFCB6: xor a ; Set zero flag and clear error code ld (pexit),a ret ; And return to caller ldFCB7: ld (ix+14),0c0h ; Set random record error ldFCB8: ld a,e ; Get error code ld (pexit),a ; And save it set 7,(ix+14) ; Set FCB/file not modified or a ; Clear zero flag ret ; And return to caller ; ; Calculate random record ; Entry: HL = offset in FCB ; DE = FCB pointer ; Exit: D = LSB random record ; C = ISB random record ; B = MSB random record ; calrrc: add hl,de ; Pointer to FCB+15 or FCB+32 ld a,(hl) ; Get byte ld hl,12 ; Offset to extent number add hl,de ; Get pointer to extent byte ld d,a ; Save first byte ld a,(hl) ; Get extent byte and 01fh ; Mask it rl d ; Shift MSB in carry adc a,0 ; Add carry rra ; Shift 1 time (16 bits) rr d ld c,a ; Save isb inc hl ; Increment to FCB+14 inc hl ld a,(hl) ; Get FCB+14 rrca ; Shift 4 times rrca rrca rrca push af ; Save it and 03h ; Mask MSB ld b,a ; Save it pop af ; Get LSB and 0f0h ; Mask it add a,c ; Add with isb ld c,a ; Save isb ret nc ; No carry then return inc b ; Increment MSB ret ; And return to caller ; END Z80DDISK.Z80  ; Clear zero flag ret ; And return to caller ; ; Calculate random record ; Entry: HL = offset in FCB ; DE = FCB pointer ; Exit: D = LSB random record ; C = ISB random record ; B = MSB random record ; calrrc: add hl,de ; Pointer to FCB+15 or FCB+32 ld a,(hl) ; Get byte ld hl,12 ; Offset to extent number add hl,de ; Get pointer to extent byte ld d,a ; Save first byte ld a,(hl) ; Get extent byte and 01fh ; Mask it rl d ; Shift MSB in carry adc a,0 ; Add carry rra ; Shift 1 time (16 bits) rr d ld c,a ; Save isb inc hl ; Increment to FCB+14 inc hl ld a,(hl) ; Ge; Z80DOS - Z80 Disk Operating System ; ; Version 1.0 - 05 Sept. 87 by Carson Wilson ; ; ------------------------------------------------------------------- ; ; Z80DHDR.LIB - Options and Standard Equates ; ; ------------------------------------------------------------------- ; ; Specify where in memory your BDOS begins and where your BIOS ends. ; The next two equates must be set for the particular system. ; You can use either kilobyte or page boundaries. ; ; The number for "MSize/MPages" is where your BIOS ENDS in memory. For ; standard 64k CP/M systems this is 64 kilobytes/256 pages. ; ; The number for "base" is where your CCP BEGINS in memory. This is ; computed using MSize and a value n which is the size in kilobytes/pages ; of your entire operating system (CCP+BDOS+BIOS). You should be able ; to get these values from the source code of your BIOS. ; ; 1 page = 256 bytes, so 4 pages = 1 kilobyte. ; ; Specify addresses using kilobyte boundaries: MSize equ 64 ; Standard 64k system size base equ (MSize-9)*1024 ; (MSize-n) where n is size of ; ..BIOS+DOS+CCP in kilobytes ; n varies with the length of ; ..your BIOS. ; Or, specify addresses using page boundaries: ;MPages equ 256 ; 256 page system size ;base equ (MPages-37)*256 ; (mpages-n) where n is size in ; ..pages of BIOS+DOS+CCP ; Standard system addresses - don't change the next three equates: RamLow equ 0000h ; Start address memory DOS equ base+800h ; Start address DOS BIOS equ DOS+00e00h ; Start address BIOS ; Time - give the offset in bytes of your BIOS time routine from your ; BIOS Boot routine. At least a minimal routine which points ; to a 4-byte BIOS time buffer is required for time stamping. ; See BIOSTIME.Z8D for an example BIOS implementation. ; If you don't have a BIOS time routine, leave the value for ; BIOStim set to 6, which calls the Console Status routine. BIOStim equ BIOS+0006 ; Offset of BIOS jump to BIOS ; ..get/set time routine, if  ; ..implemented. ; ResDsk - You can optionally define an alternate disk for disk reset. ; This is useful in floppy systems with a virtual drive feature. ; By defining a non-virtual drive (drive B in Morrow MD3) as the ; reset disk, you avoid having to switch back to disk A every time a ; disk reset is performed. However, this requires that you always ; have a diskette in drive B:. ; ResDsk equ 0 ; CP/M default is disk A for resets ; ..0=A, 1=B, 2=C, etc. ; ; Options - Bit 0: public file enable(1)/disable(0) ; Bit 1: delay 256 characters active(1)/disable(0) ; Options equ 00000011B ; Enable public file and delay ; ------------------------------------------------------------- ; ; The remaining equates should stay the same for all systems: ; ; ------------------------------------------------------------- false equ 0 true equ not false ; Standard BIOS function offsets: Boot equ BIOS+00000h ; BIOS cold boot WBoot equ BIOS+00003h ; BIOS warm boot ConSt equ BIOS+00006h ; BIOS console status ConIn equ BIOS+00009h ; BIOS console input ConOut equ BIOS+0000ch ; BIOS console output list equ BIOS+0000fh ; BIOS list output punch equ BIOS+00012h ; BIOS punch output reader equ BIOS+00015h ; BIOS reader input home equ BIOS+00018h ; BIOS home disk SelDsk equ BIOS+0001bh ; BIOS select disk SetTrk equ BIOS+0001eh ; BIOS select track SetSec equ BIOS+00021h ; BIOS select sector SetDMA equ BIOS+00024h ; BIOS set DMA address read equ BIOS+00027h ; BIOS read 128 bytes write equ BIOS+0002ah ; BIOS write 128 bytes ListSt equ BIOS+0002dh ; BIOS list status SecTrn equ BIOS+00030h ; BIOS sector translation ; Internal definitions: ContC equ 003h ; Key to generate warm boot ContH equ 008h ; Backspace ContS equ 013h ; Control-S tab equ 009h ; Tab lf equ 00ah ; Line feed cr equ 00dh ; Carriage return ContP equ 010h ; Set/reset print flag ContX equ 018h ; Delete line (backspaces) DrvSep equ 03ah ; Drive seperator (:) rubout equ 07fh ; Delete last char ; MaxCmd equ 40 ; Number of valid DOS commands ; END Z80DHDR.LIB sole output list equ BIOS+0000fh ; BIOS list output punch equ BIOS+00012h ; BIOS punch output reader equ BIOS+00015h ; BIOS reader input home equ BIOS+00018h ; BIOS home disk SelDsk equ BIOS+0001bh ; BIOS select disk SetTrk equ BIOS+0001eh ; BIOS select track SetSec equ BIOS+00021h ; BIOS select sector SetDMA equ BIOS+00024h ; BIOS set DMA address read equ BIOS+00027h ; BIOS read 128 bytes write equ BIOS+0002ah ; BIOS write 128 bytes ListSt equ BIOS+0002dh ; BIOS list status SecTrn equ BIOS+00030h ; BIOS sector translation ; Internal definitions: ContC equ 003h ; Key to generate warm boot ContH equ 008h ; Backspace ContS equ 013h ; Control-S tab equ 009h ; Tab lf equ 00ah ; Line feed cr equ 00dh ; Carriage return ContP equ 010h ; Set/reset print flag ContX equ 018h ; Delete line (backspaces) DrvSep equ 03ah ; Drive seperator (:){ The following was captured from the screen as an example } { of how to build a Z80DOS system from a CP/M system. } { Comments are in "{}" } { First run TELL.COM and write down the address for beginning of BDOS: } A>tell This is a utility that tells you where your CCP starts, what your BDOS entry add- ress is, and where your CBIOS jump table begins, as well as other useful info. Your CCP starts at: CD00H. Your BDOS entry address is: D506H. { <-- write down } Your CBIOS jump table begins at: E300H. { Detail left out } List status routine: F502H. Sector translate routine: F601H. { Next, edit and assemble Z80DOS.Z80 to a hex file: } A>z80asm z80dos/h Z80ASM Copyright (C) 1983-86 by SLR Systems Rel. 1.31 #SB1006 Z80DOS/H End of file Pass 1 0 Error(s) Detected. 3546 Absolute Bytes. 370 Symbols Detected. A>dir *.hex A: Z80DOS HEX { Use MOVCPM.COM to create an image of your operating system: } A>dir movcpm.* A: MOVCPM COM A>movcpm 64 * CONSTRUCTING 64K CP/M VERS. 2.2 READY FOR "SYSGEN" OR "SAVE 46 CPM64.COM" { <-- write down number after "SAVE" } A> A>save 46 cpm64.com A> A>dir cpm64.* A: CPM64 COM { Write down the number (46 here) given by your MOVCPM.COM. } { You will use it later to save the new image. } { Now use DDT.COM to patch CPM.COM with Z80DOS.HEX: } A>ddt cpm64.com DDT VERS 2.2 NEXT PC 2F00 0100 { First look for beginning of BDOS image. Should be near 1500: } -d1470 1470 DE 01 7A E3 CD A7 DC C3 86 E3 42 41 44 20 4C 4F ..z.......BAD LO 1480 41 44 00 43 4F 4D CD 66 E0 CD 5E DE 3A CE E3 D6 AD.COM.f..^.:... 1490 20 21 F0 E3 B6 C2 09 DE C3 82 DF 00 00 00 00 00 !.............. 14A0 00 00 00 00 00 00 00 00 00 00 00 00 00 24 24 24 .............$$$ 14B0 20 20 20 20 20 53 55 42 00 00 00 00 00 00 00 00 SUB........ 14C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 14D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 14E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 14F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1500 22 16 00 01 92 D0 C3 11 E4 00 00 99 E4 9E E4 A3 "............... 1510 E4 EB 22 43 E7 EB 7B 32 D6 F1 21 00 00 22 45 E7 .."C..{2..!.."E. 1520 39 22 0F E7 31 41 E7 AF 32 E0 F1 32 DE F1 21 74 9"..1A..2..2..!t { "$$$ SUB" denotes end of CCP and beginning of BDOS. C3 "jump" } { instruction at 1506 in this example is the actual start of BDOS. } { The numbers from 1500 to 1505 here are the CP/M serial number, } { which must be preserved. } { Calculate end of BDOS image by adding E00 to start of serial number, } { and write it down. } -h1500,e00 2300 0700 { I got 2300 hex as the start of BIOS, and end of BDOS. } { Take a look at end of BDOS: } -d2280 2280 3A E0 F1 B7 CA 91 F1 77 3A DF F1 32 D6 F1 CD 45 :......w:..2...E 2290 F0 2A 0F E7 F9 2A 45 E7 7D 44 C9 CD 51 F0 3E 02 .*...*E.}D..Q.>. 22A0 32 D5 F1 0E 00 CD 07 EF CC 03 EE C9 E5 00 00 00 2............... 22B0 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 22C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 22D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 22E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 22F0 00 00 CD 06 F2 B7 CA F2 F1 CD 09 F2 FE 03 C0 C7 ................ 2300 C3 CE FC C3 58 F2 C3 20 F3 C3 AA F3 C3 CB F4 C3 ....X.. ........ 2310 F2 F4 C3 52 F5 C3 4C F5 C3 76 F5 C3 98 F5 C3 B3 ...R..L..v...... 2320 F5 C3 B8 F5 C3 BD F5 C3 C2 F5 C3 CD F5 C3 02 F5 ................ 2330 C3 01 F6 C3 4B F6 C3 1F F6 C3 2C F6 C3 38 F6 31 ....K.....,..8.1 { The series of C3 "jump" instructions at 2300 indicate the } { start of BIOS. Now zero out the old BDOS from beginning } { of BDOS to just before BIOS (1506 to 22ff in this example) } -f1506,22ff,0 { Check your work: } -d1500,153f 1500 22 16 00 01 92 D0 00 00 00 00 00 00 00 00 00 00 "............... 1510 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1520 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 1530 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ { It's zeroed out except for the serial number } { Now load in Z80DOS } -iz80dos.hex { Calculate offset, using the beginning of the BDOS image in CPM.COM } { and the beginning of actual BDOS from TELL.COM. Write down the } { offset value (3100 here): } -h1506,e406 F90C 3100 { Now read in Z80DOS.HEX using the offset: } -r3100 NEXT PC 2F00 0000 { NEXT PC is the same as when CPM.COM read in, indicates } { Z80DOS.HEX successfully read in. Check our work: } -d1500 1500 22 16 00 01 92 D0 C3 14 E4 00 00 81 E6 97 E6 86 "............... 1510 E6 06 F2 03 79 32 7F F1 21 00 00 22 80 F1 AF 32 ....y2..!.."...2 1520 82 F1 32 83 F1 ED 73 A1 F1 31 E3 F1 DD E5 D5 DD ..2...s..1...... 1530 E1 21 30 F1 E5 79 FE 69 CA 12 F1 FE 68 CA 1F F1 .!0..y.i....h... 1540 FE 37 CA 28 F1 FE 36 CA DB F0 FE 29 D0 21 59 E4 .7.(..6....).!Y. 1550 06 00 09 09 7E 23 66 6F E9 03 F2 AB E4 BA E4 BD ....~#fo........ 1560 E4 C2 E4 C6 E4 CA E4 E4 E4 E9 E4 C2 E5 17 E6 EE ................ 1570 E4 94 E7 98 E7 9C E8 FC EC 4C ED BC E7 E3 E7 EF .........L...... 1580 E7 B5 EE 13 EF DB ED FA E7 02 E8 09 E8 9D EA 0F ................ 1590 E8 00 EB 14 E8 19 E8 21 E8 26 E8 AB EE 08 EF 34 .......!.&.....4 15A0 E8 3C E8 4C E8 B9 E4 B9 E4 08 EF CD 66 E5 CD A6 .<.L........f... 15B0 E5 38 03 CD 0E E5 32 80 F1 C9 7B 18 51 CD 15 F2 .8....2...{.Q... { Beginning of BDOS looks ok, check beginning of BIOS } -d22c0 22C0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 22D0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 22E0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 22F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 2300 C3 CE FC C3 58 F2 C3 20 F3 C3 AA F3 C3 CB F4 C3 ....X.. ........ 2310 F2 F4 C3 52 F5 C3 4C F5 C3 76 F5 C3 98 F5 C3 B3 ...R..L..v...... 2320 F5 C3 B8 F5 C3 BD F5 C3 C2 F5 C3 CD F5 C3 02 F5 ................ 2330 C3 01 F6 C3 4B F6 C3 1F F6 C3 2C F6 C3 38 F6 31 ....K.....,..8.1 2340 F6 F6 1D F7 37 F7 C5 F9 F3 F9 F4 F9 F5 F9 F7 F9 ....7........... 2350 AD F9 C3 E4 F2 C3 E0 F3 31 00 FC 11 00 FC D5 DD ........1....... 2360 E1 01 1F 00 D3 F6 2A 1B 00 ED B0 FD 21 1D F7 DD ......*.....!... 2370 36 02 02 DD 36 03 08 DD 36 0D 81 DD 36 06 DC CD 6...6...6...6... { BIOS wasn't overwritten, so quit DDT and save the new image, } { using the same number given by MOVCPM.COM above: } -g0 A> A>save 46 z80dos.com A> A>dir z8*.* A: Z80DOS COM { Now write the image to the system tracks of a junk disk } A>sysgen z80dos.com SYSGEN VER 2.0 MD1.2 DESTINATION DRIVE NAME (OR RETURN TO REBOOT)a DESTINATION ON A, THEN TYPE RETURN FUNCTION COMPLETE DESTINATION DRIVE NAME (OR RETURN TO REBOOT) A> { Test the new operating system } { Check reboot } A>^C A> { Reboot okay, try some carriage returns } A> A> A> { If carriage return crashes system, you may have overwritten } { the serial number at the beginning of BDOS. } { Try some commands } A>dir A: Z80DOS COM : SYSGEN COM A>zip ZIP? A> { If reboot, carriage return and commands work, CCP, BDOS, and BIOS are } { all at least operational. Test the new system for a couple of days } { before relying heavily on it. Use DU.COM's Map command on your new } { system disk to check the directory tracks. When you are satisfied that } { the new system is safe, use SYSGEN as above to install Z80DOS on your } { regular system disks. } em tracks of a junk disk } A>sysgen z80dos.com SYSGEN VER 2.0 MD1.2 DESTINATION DRIVE NAME (OR RETURN TO REBOOT)a DESTINATION ON A, THEN TYPE RETURN FUNCTION COMPLETE DESTINATION DRIVE NAME (OR RETURN TO REBOOT) A> { Test the new operating system } { Check reboot } A>^C A> { Reboot okay, try some carriage returns } A> A> A> { If carriage return cra ----- Z 8 0 D O S ----- CP/M Compatible Z-80 Disk Operating System Featuring Enhanced Integral File Date and Time Stamping Version 1.0 October 1, 1987 by Carson Wilson The author assumes no responsibility for losses resulting from the use or inability to use Z80DOS10.LBR. The parts of Z80DOS10.LBR created by Carson Wilson are hereby released to the public. CONTENTS 1. What is Z80DOS? 2. What is your purpose in releasing Z80DOS? 3. What are the differences between Z80DOS and CP/M? - CP/M function calls. - Modifications affecting several functions. - Additional function calls. 4. How does Z80DOS compare with some of the other BDOS replacements currently available? - Comparison of replacement disk operating systems for CP/M 5. How do I install Z80DOS on my system? - Installing DOS segment - Installing BIOS segment (optional) 6.  What do the programs in Z80DOS.LBR do? 7. Acknowledgements 1. What is Z80DOS? Z80DOS is a replacement for CP/M's Basic Disk Operating System, or BDOS, for computers having a Zilog Z80 microprocessor. The BDOS is the module of CP/M which allows for standardization of CP/M programs by supplying "system calls" which are the same across the many different CP/M hardware configurations. It acts as an interpreter between application programs and your system's Basic Input Output System, or BIOS. Z80DOS supports all of the standard CP/M 2.2 BDOS functions, and adds several new functions and enhancements. All programs designed to run under standard CP/M will also work under Z80DOS. Programs designed to run under Z80DOS can take advantage of enhancements to the BDOS to perform functions not available under standard CP/M. Z80DOS fits in the same area of memory used by CP/M's BDOS. This makes it simple to install Z80DOS in any working CP/M system. By coding the BDOS in Z80 assembler, it is possible to fit more functions within the standard 3.5 kilobytes of memory allowed by CP/M. 2. What is your purpose in releasing Z80DOS? Z80 Replacement Disk Operating System, or ZRDOS (tm), by Echelon, Inc., has become a standard among many Z80 computer users. ZRDOS offers many important advantages over standard CP/M's BDOS, and I applaud the new standard ZRDOS has created. ZRDOS is lacking in one crucial area, though: it does not implement date and time stamping of files. ZRDOS users must rely on DateStamper (tm) by PluPerfect Systems, a separate program which requires additional memory and files and considerable programming overhead to use. Z80DOS solves these problems by making date and time stamping an integral part of the operating system (as do several of the other BDOS replacements below), and by implementing two new BDOS functions exclusive to Z80DOS which allow programs that modify or copy files to maintain file stamps with very  little program overhead (as does no other current BDOS replacement). Z80DOS is a fully operational replacement BDOS for CP/M, and is the only BDOS I use on my own system. However, Z80DOS does lack some of the advanced features of ZRDOS (see table below). My purpose in releasing Z80DOS is not to supplant ZRDOS, but simply to generate interest in the ideas it contains. If you find the ideas used in Z80DOS worthwhile, please help by introducing others to Z80DOS.LBR. If widely accepted, the time stamping protocols of Z80DOS could form the basis for a file-stamping standard for Z80 computer users. 3. What are the differences between Z80DOS and CP/M? 3.1. CP/M function calls. Z80DOS makes the following modifications to the CP/M standard functions: - CP/M Function - --------------- Z80DOS Modification(s) ----------------- No. --- Name --- 2 Console out After 256 characters output, console status is checked. This makes it possible to exit a program by typing control-s followed by control-c. This feature is normally on, and may be turned off by setting bit 0 of address Z80DOS+19 to zero. 10 Read console Delete key is same as backspace. Control-u, control-r, and control-e are simply echoed to the screen. 15 Open file File access date and time are changed to reflect the current DOS date and time. If the f2 attribute bit is set, the file is available from all user areas on a drive (this is also the case with Search First and Search Next). 16 Close file If the file was written to, its update date and time are changed to the current DOS date and time, and its archive attribute (t3) is reset to zero. 19 Delete file To prevent accidental erasure, public files (see below) and system files can only be erased from their home user area by using unambiguous file names. 22 Create file File create date, modified date and time, and access date and time are set to current DOS date and time. 3.2. Modifications affecting several functions. When Z80DOS detects a changed disk, it resets the disk system rather than aborting with a read-only error message as CP/M does. Files may be made "public" (available from all user areas on a disk) by setting the high bit of the second character of their filename (f2 bit). Public files cannot be referenced by wildcards. This feature is normally on, and may be turned off by setting bit 1 of address Z80DOS+19 to zero. Disks of up to one gigabyte and files of up to 32 megabytes are possible under Z80DOS. Z80DOS provides the user with more information when an error occurs. The type of error, the function call which produced the error, the drive letter, and the filename (if any) associated with the function call are all displayed. 3.3 Additional function calls. Z80DOS adds the following non-standard functions to CP/M: --- Function ---- -------------------- Description ----------------------- No. --- Name --- 54 Get stamp Following a successful Open File, Search First, or Search Next call, retain the file's full 10 byte date and time stamp for future use, and return a pointer to the stamp in HL. If no stamps are present, store zeroes. 55 Use Stamp Use creation date and last modified date and time stored by Get Stamp instead of real time for the next Write, Make File or Close File call. 104 Set time Set the system time to the values pointed to by DE (BIOS dependent; see Z80DTIME.Z80 for format). 105 Get time Fill the five bytes pointed to by DE with the current date and time. 4. How does Z80DOS compare with some of the other BDOS replacements currently available? 4.1. Comparison of replacement disk operating systems for CP/M: Name | CP/M ZRDOS+ Z80DOS P2DOS21 DOS+25 SUPERDOS -------+------------------------------------------------------------------- Author | Digital Echelon, Carson H.A.J. C.B. Benjamin | Research, Inc. Wilson Ten Falconer Ho | Inc. Brugge | Deriv- | Unknown Unknown P2DOS, Unknown P2DOS P2DOS ation | SUPERDOS | Time | No No (*)C,U,A C,U C,U,A C,U stamps | | Disks | No Yes Yes No No Yes auto- | login | | Archive| No Yes Yes Yes Yes No | Public | No Public F2 F2 System F2 files | user attribute attribute files attribute | areas at A0: | Get/Use| No No Yes No No No stamps | | Get/Set| No No Yes Yes Yes Yes time | | Error | Cryptic Clear Legible, Legible, Legible, Legible, messgs.| give give give give | function function function function | & file & file & file & file | Return | No Yes No No Yes No current| DMA | | Wheel- | No Yes No No No No protect| files | | Set/res| No Yes No No No No warm | boot | | Source | No No Yes Yes $50 Yes code | | Approx.| $20 $60 free free free free price +----------------------------------------------------------------- (*) C = Create, U = Update, A = Last Access 5. How do I install Z80DOS on my system? 5.1. Installing BDOS segment To install Z80DOS in your CP/M system, you will need: 1. A Z80 assembler 2. Digital Research's MOVCPM.COM, SYSGEN.COM, and DDT.COM or the equivalents You must first set the options and addresses in Z80DHDR.LIB to match your particular system and tastes. See instructions in Z80DHDR.LIB. Then you will need to assemble Z80DOS to a hex file. Finally, you will use SYSGEN.COM to overwrite the sections of the system tracks on your system diskettes which presently contain CP/M's BDOS with Z80DOS. If you are familiar with the process of patching your operating system with hex files, the file Z80DOS.BLD should give you enough information to install Z80DOS. If you have never before altered your operating system, you may want to consult one or more of the following references for more information before proceeding: Conn, Richard. Z3INSTAL.LBR. 132 kilobyte public domain library file describing Conn's Z80 Command Processor Replacement (ZCPR). Available on Z-Nodes worldwide. Conn, Richard. ZCPR3: The Manual. (New York: New York Zoetrope, Inc., 1985). Johnson-Laird, Andy. The Programmer's CP/M Handbook. (Berkeley: Osborne/McGraw-Hill, 1983). Miller, Alan R. Mastering CP/M. (Berkeley: SYBEX, Inc., 1983) Waite, Mitchell, and Robert Lafore. Soul of CP/M (How to Use the Hidden Power of Your CP/M System). (Indianapolis: Howard W. Sams & Co., 1983). 5.2. Installing BIOS segment (optional) To implement time stamping under Z80DOS, you will need in addition: 1. Source code for your BIOS 2. A real-time clock or four bytes of reserved RAM memory To implement time stamping, you will need to modify and reassemble your BIOS and overwrite your system tracks with the new BIOS using the techniques described in Z80DOS.BLD and in the above references. The offset at which to load the hex image of your BIOS is the same number used to load the hex image of Z80DOS in Z80DOS.BLD. You needn't have a real-time clock in your computer to implement date stamping, just an area of memory to hold the current date and time. BIOS.TIM includes an example BIOS time inserts which use memory to hold the time. Finally, before Z80DOS will do date and time stamping, you must run INITDIR.COM (included) on your disks. This sets aside extra space in your disk directories for time/date stamps (See Z80DPROG.NOT). 6. What do the programs in Z80DOS.LBR do? INITDIR.COM initializes directories for time stamping by clearing the fourth entry in each directory sector and flagging its user area byte with 21 hex. This tells Z80DOS that this entry is to be used for holding the time and date stamps of the other three directory entries in the sector. See Z80DPROG.NOT for a complete description of the format used. For help using INITDIR.COM, type INITDIR. PPIP.COM is a public domain file copy utility which I have adapted for use with Z80DOS by enabling it to copy file date and time stamps along with files. PPIP has many useful options such as file archiving. For help with PPIP.COM, type PPIP or see PPIP.DOC, which is available in PPIPnn.LBR on many bulletin boards. SAVESTMP.COM is my own program which copies the creation date from one file to another. SAVESTMP is designed to bridge the gap between an advanced operating system which implements creation date file stamps and programs such as word processors which do not recognize time stamps. NewWord, for example, always erases the source file when modifications are performed, and in so doing loses the file's date of creation. To compensate for this, SAVESTMP can be used to save the original creation date by copying it to a zero-length file before editing and then restoring the creation date from the zero-length file after editing. This is best done using aliases under ZCPR, but could also be implemented with CP/M's SUBMIT.COM. Here is a simple sample ZCPR alias named MODIFY which implements this idea: MOD.IFY savestmp ROOT:DATEHOLD=$1;vde $1;savestmp $1=ROOT:DATEHOLD To edit a file, the user would type "MODIFY MYFILE". MYFILE's creation date would be copied to DATEHOLD (or any other file) prior to editing, then after editing, the creation date would be copied back to MYFILE. This ZCPR alias could be elaborated on with file existence checks, etc. Although I have tested SAVESTMP fully on my own system, it does make BIOS calls and writes directly to directory tracks, so please test SAVESTMP.COM on a dispensable diskette before using it extensively. My thanks to Rick Charnes, whose concern about lost creation dates inspired SAVESTMP.COM. For a brief help message, type SAVESTMP. TDIR.COM or "TIMEDIR.COM" is a directory utility which displays Z80DOS creation, modification, and last access stamps along with individual and total file size, number of files matched, and kilobytes free. TDIR will also display file attributes (such as read-only) using video display features when patched for your terminal. To patch TDIR, look for the ASCII labels "[VIDON>" and "[VIDOFF>" near the beginning of the file. After VIDON, you may patch in a string to turn a video attribute on (I use dim and reverse video on my system). A string to turn the attribute back off goes after VIDOFF. The strings may be up to 6 bytes long, but MUST be terminated with a dollar sign. TDIR is derived from DDIR, by H.A.J. Ten Brugge. For help with TDIR.COM, type "TDIR /". TELL.COM is a public domain utility which tells you information about your operating system. It is included for use in installing Z80DOS.COM. To use, just type TELL. TIME.COM sets or displays the date and time from the real-time clock or section of memory devoted to time storage on Z80DOS system. TIME.COM will only function once the proper BIOS routine has been installed. If you lack a real-time clock, you may wish to run TIME once each time you start your system, by using AUTO.COM on a CP/M system, or installing the TIME command in the STARTUP alias of a ZCPR system. For help, type TIME. TIME.COM was adapted from a program of the same name by C. B. Falconer. ZF10GD5.COM is for use on ZCPR systems only. This is the popular ZFILER program, modified by adding 31 bytes of code to copy Z80DOS date and time stamps when copying, moving, archiving, or unsqueezing files. 7. Acknowledgements The main body of Z80DOS is my adaptation of P2DOS, version 2.1, by H.A.J. Ten Brugge, Molenstraat 33, NL-7491 BD Delden, The Netherlands, all rights reserved. The date stamping format of Z80DOS and the programs TIME and INITDIR were adapted from DOS+, version 2.5, by C. B. Falconer, 680 Hartford Tpk, Hamden, CT 06517, all rights reserved. The method Z80DOS uses to automatically log in changed disks is an adaptation of the scheme presented by Benjamin Ho of Evanston, IL in his SUPERDOS.LBR. Please let me know what you think of Z80DOS. I will be happy to explain the goals and techniques of Z80DOS to programmers and developers or to assist anyone in adapting programs to Z80DOS. I can be reached thru messages to Carson Wilson at: Lillipute Z-Nodes, Chicago 312-664-1730 and 312-649-1730 8-1-No Parity 24 hours Best Wishes!  CONTENTSZ8D DATEDEMOZ80% DATEHL RELDATEHL Z80FUNCTIONZ8DPPIP-CPMCOMPPIP-Z3 COM! RCPCP LIB8!"#$%&'SAVESTMPCOM(SAVESTMPZ80%)*+,-TDIR-CPMCOM./0TDIR-Z3 COM12TELL COM 34TIME COM5Z80DCHARZ806789:;<=>?@ABCDEZ80DCHARZ80FZ80DDISKZ80GHIJKLMNOPQRSTUVZ80DDISKZ80WXYZ[\]^_`abcdefZ80DDISKZ80[ghijklmnopqrZ80DHDR LIB!stuvwZ80DOS BLD=xyz{|}~Z80DOS10$$$Z80DOS10$$$ This replacement for CP/M's Basic Disk Operating System (BDOS) provides two new function calls designed to allow programs which copy and modify files to easily preserve file date and time stamps while remaining downward compatible with CP/M. Also features automatic disk login, archiving, and public files. See CONTENTS.Z8D for short descriptions of all files. Source code, installation and programming notes, and useful utilities are included. October 1, 1987 by Carson Wilson. omatically log in changed disks is an adaptation of the scheme presented by Benjamin Ho of Evanston, IL in his SUPERDOS.LBR. Please let me know what you think of Z80DOS. I will be happy to explain the goals and techniques of Z80DOS to programmers and developers or to assist anyone in adapting programs to Z80DOS. I can be reached thru messages to Carson Wilson at: Lillipute Z-Nodes, Chicago 312-664-1730 and 312-649-1730 8-1-No Parity 24 hours Best Wishes! ; Z80DOS - Z80 Disk Operating System ; ; Version 1.0 - 05 Sept. 87 by Carson Wilson ; ; Main Module ; maclib z80dhdr ; Operating system addresses, options, ; ..and equates include z80dchar.z80 ; Jump table, character I/O routines, ; ..error routines include z80ddisk.z80 ; Return CP/M version, disk routines include z80dtime.z80 ; Time routines, DOS exit routines, ; ..RAM area ; END Z80DOS.Z80 "k>*z-;*9<ݖߍ˝˝˓&ǥy^vI@7yK727_ʲw'uEo]_f_^iP  EDA[.i` 4:Y:UWo=Zٳ23fhICp6u[1`ϔ~, PqdW5??3Za?ۜ4z,Бr)NU=N\LI(;km(雤ƽXtk*X3PY^ۦ!}2W=eZn12'j Ip?2d 2A?I?7y5poZ=CPCmkHZA- - Programming Notes for Z80DOS - October 1, 1987 by Carson Wilson CONTENTS 1. Format of date/time stamps under Z80DOS 2. Time stamping protocol of Z80DOS 3. Get Stamp and Use Stamp - General description - Z80DOS function 54 -- Get Stamp - Z80DOS function 55 -- Use Stamp 4. Using Get Stamp and Use Stamp - Programming example 1. Format of date/time stamps under Z80DOS Z80DOS uses 10 bytes to represent date of file creation, date and time of last modification, and date and time of last file access. This is the same format used by C. B. Falconer's DOS+25. The format is as follows: Byte Meaning ---- ------- 1 Low byte of date of creation 2 High " " " " " 3 Low byte of date of last modification 4 High " " " " " " 5 Hour " " " " " in BCD. 6 Minute " " "  " " " " 7 Low byte of date of last access 8 High " " " " " " 9 Hour " " " " " in BCD. 10 Minute " " " " " " " Following a successful Open File, Search First, or Search Next BDOS call (see below), a call to function 55 (Get Stamp) returns a pointer in the HL register to a 10-byte buffer in the above format containing the full date and time stamp of the file referenced in the Open or Search call. If the file had no time stamp, the buffer is filled with zeroes. A ten-byte date and time code of the above format is maintained for each file by Z80DOS. The code for each file is stored at the fourth entry of the directory sector containing the file's first directory extent. The fourth directory entry of each sector is flagged for date and time stamping by setting the user-number field of the directory entry to 21 hex (INITDIR.COM performs this function). For example, here is the directory sector for TIMEZCCP.EX, which was created on May 20, 1987 at 11:46 a.m.: 00 0054494D 4542494F 53455820 00000045 |.TIMEBIOSEX ...E| 10 7778797A 7B000000 00000000 00000000 |wxyz{...........| 20 0054494D 455A4343 50455820 0000000D |.TIMEZCCPEX ....| 30 7C000000 00000000 00000000 00000000 ||...............| 40 0054494D 455A4350 52434F4D 0000005C |.TIMEZCPRCOM...\| 50 144698A1 A2A50000 00000000 00000000 |.F.!"%..........| 60 2100630D 630D1307 --> 630D630D |!.c.c...c...c.c.| 70 1146630D 1146 <-- 620D2232 620D2232 |.Fc..Fb.b."2b."2| The fourth entry of the directory sector contains the stamps for the files described by the previous three entries, in order. The first byte of this entry is 21 hex. Starting at the third byte of the third directory entry are the three date and time stamps of the files described by the previous three directory entries. Between the arrows (added for clarity) are the date of creation, date and time !of modification, and date and time of access for TIMEZCCP.EX, respectively. In this example, the creation, modification, and access date stamps are all the same. The date stamp bytes are 63h and 0Dh, because December 31, 1977 was exactly 0D63 hex days before May 20, 1987. The time stamp bytes are 11h and 46h--the 24-hour time (11:53) in binary coded decimal. 2. Time stamping protocol of Z80DOS By selectively modifying the three fields of a file's stamp, Z80DOS maintains an active record of when various operations were last performed on the file. This is done by selectively updating fields of the stamp depending on which standard DOS function is being called by a program. The three standard DOS functions which affect the stamps of files under Z80DOS are Make File (function 22), Open File (function 15), and Close File (function 16). The fields of the time stamp affected are as follows: Create Last Modified Last Access Make file X X X Open file X Close file X All three fields are initialized when a file is created. Each time a file is opened for reading or writing, its Last Access field is updated, while the Create and Last Modified fields remain the same. Under Z80DOS, as with CP/M, the Close File call simply returns and has no effect if a file has not been modified. Therefore calls to function 16 will update the Last Modified field only if the file has been written to, and the Create and Last Access fields will remain the same. No other function calls affect a file's time and date stamp. This means that files can be renamed or have their attributes changed without affecting file stamps. In fact, even unerased files will retain their full time and date stamps. When the fields of the file's stamp are being updated, Z80DOS normally gets the values to use from the system clock (i.e., the current real time and date). Z80DOS' Use Stamp call instructs Z80DOS to access a dedicated internal buffer rather than the system clock for the next function call, allowing greater control over a file's time stamps for programs which copy files. 3. Get Stamp and Use Stamp 3.1 General Description The problem addressed by the Get Stamp and Use Stamp functions is that of preserving time and date stamps when files are copied. Under CP/M, files are normally copied by creating a destination file of the same name as the source file on another disk or user area, and then copying the contents of the source file to the destination file. As noted above, however, each time a file is created, all three fields of its time and date stamp are normally initialized to the current system time. The result is that each time a file is copied, its actual dates of creation and last modification are lost. The Get Stamp and Use Stamp functions of Z80DOS address this problem by allowing the calling program more control over the file stamping process. By selective use of these two functions, programs can copy the creation date and modification time and date of the source file along with its contents. The Get Stamp function is used following successful File Open, Search First, or Search Next calls to store the full time stamp of a file in an internal DOS buffer. This buffer remains intact until the next call to Get Stamp or until the next system cold or warm boot. The Use Stamp function is then used before functions affecting file stamping, and instructs the BDOS to substitute the values from its internal buffer during these functions. The buffer values are initialized to zeroes after a warm boot or if the previous Get Stamp call referenced a file in an unstamped directory, and are initialized to the values of the file referenced in the previous Get Stamp call otherwise. Because no buffering is required of the calling program, Get Stamp and Use Stamp can be implemented with little cost in terms of additional program code. It is not necessary for the program to store the file's date and time stamps, or even to know whether time stamps are present for the file. Because the Get Stamp and Use Stamp routines of Z80DOS are called as non- standard functions, programs which use them will remain downward compatible, i.e., compatible with operating systems which do not provide Get Stamp and Use Stamp. As far as I know, no operating system in use for the Z80 (except possibly CP/M 3) uses DOS calls 54 and 55. Therefore, programs can call these functions and still function properly under CP/M or ZRDOS. Please note that functions 54 and 55 needn't remain exclusive to Z80DOS. Since these are unused function numbers under CP/M and ZRDOS, future operating systems such as ZOS might implement functionally equivalent routines using the same calls. In this way, functions 54 and 55 may eventually become standard Get and Use Stamp calls for programs for the Z80 which recognize time and date stamping. 3.2. Z80DOS function 54 -- Get Stamp This function lo"ads the 10-byte date and time stamp of a file into a buffer in BDOS following a successful file open, file search, or file create call. The buffer holds these values until the next cold or warm boot, or until Get Stamp is called again. If no time stamps are present on the directory, the buffer is filled with zeroes. As an additional service, Get Stamp returns a pointer in HL to the first byte of the ten byte dedicated buffer. This allows programs such as directory programs to easily read a file's time stamp following a call to Search First, Search Next, or Open File (see DATEDEMO.Z80 for example). At present, Z80DOS only provides a valid date and time stamp for the first extent of multiple-extent files. Therefore, if a program is searching all extents of files (e.g., to get file sizes), measures must be taken to ensure that Get Stamp is called following searches for first extents only. Otherwise, possibly invalid time stamps from susequent extents will be stored. 3.3 Z80DOS function 55 -- Use Stamp The main purpose of Get Stamp is to load a file's date and time stamp for retrieval with Use Stamp. When Use Stamp is called before a file is created, written to, or closed, Z80DOS retrieves the appropriate values stored by Get Stamp and substitutes them for the system time for date and time stamping. For example, if Use Stamp is called immediately before a call to Make File (function 22), the new file is stamped with the creation date value stored earlier by Get Stamp. Z80DOS will automatically revert to the current system time following all BDOS calls except to Use Stamp. This allows the smallest amount of program overhead, since no "Reset Stamp" call is required following the Use Stamp call to set the BDOS back to the current time. However, Use Stamp must therefore be called IMMEDIATELY BEFORE Create, Write, or Close calls which are to use the stored stamp values. 4. Using Get Stamp and Use Stamp The following pseudo code describes the general pattern for file copy programs using these Get Stamp and Use Stamp: Open source File If file open successful then Get Stamp Use Stamp Create destination file Repeat Read Sequential from source file Use Stamp Write Sequential to destination file Until end of source file reached Use Stamp Close destination file Close source file Get Stamp need only be called once upon successfully opening the source file; from then on Use Stamp does all of the work. When called before creating the destination file, Use Stamp causes Z80DOS to initialize all three time/date fields of the destination file to those of the source file. Upon closing the destination file, Use Stamp is called again, this time causing Z80DOS to use the stored stamp of the source file in setting the destination file's last modified date and time. Otherwise, the current system time would be used. Use Stamp is also called before each write to the destination file. This is necessary only when copying files with over one extent, and is due to the way CP/M's Write Sequential function is implemented. Write Sequential calls to files of over one extent cause CP/M to Close a file's first extent before opening a new one for writing. If the first extent of a file is closed without first calling Use Stamp, Z80DOS will stamp the first extent with the current system time. Therefore, to ensure that the first extent of the copied file is marked with the proper stamp, it is necessary to call Use Stamp before each call to Write Sequential. Most word processors for CP/M erase the source file and rewrite it to disk each a file is modified. The Z80DOS environment will enable these programs to preserve the original file's time and date stamps as follows: Reset file replaced flag Search for file X If file X found then Get Stamp Set file replaced flag Delete file X If replaced = true then Use Stamp Create File X Repeat If replaced = true then Use Stamp Write Sequential to file X Until end of text If replaced = true then Use Stamp Close file X This procedure is nearly as simple as the file copy procedure, but does require that the calling program maintain a record of whether a file is being replaced or created anew. 4.1. Programming Example The following assembly language example illustrates the use of functions 54 and 55 to copy the date of creation from the source file to the destination file in a file copy program: GetStp equ 54 ; Store file's date and time stamp UseStp equ 55 ; Use stored stamp for write/close .... ; Open source file call initfcb2 ld c,15 ; Open source file call bdos inc a ; Check for error jp z,prfnf ; Branch if file not found ; ld c,54 ; Save source's stamp call bdos .... ; Create destination file ld c,55 ; Use stored stamp call bdos ; ..for destination file ; ld de,fcb1 ; #Point to destination FCB ld c,22 ; BDOS make-file function call bdos inc a ; Test for error (no directory space) .... For other programming examples, see files RCPCP.LIB, DATEDEMO.Z80, and SAVESTMP.Z80 in this library. ion from the source file to the destination file in a file copy program: GetStp equ 54 ; Store file's date and time stamp UseStp equ 55 ; Use stored stamp for write/close .... ; Open source file call initfcb2 ld c,15 ; Open source file call bdos inc a ; Check for error jp z,prfnf ; Branch if file not found ; ld c,54 ; Save source's stamp call bdos .... ; Create destination file ld c,55 ; Use stored stamp call bdos ; ..for destination file ; ld de,fcb1 ; ; Z80DOS - Z80 Disk Operating System ; ; Version 1.0 - 05 Sept. 87 by Carson Wilson ; ; ------------------------------------------------------------------- ; ; Z80DTIME.Z80 - Time Functions, Exit Routine, RAM Area ; ; ------------------------------------------------------------------- ; Time Routines ; ; Set file's time and date ; ; Entry: E = 2 - set creation date or get time stamp ; E = 4 - Set last update time/date ; E = 8 - Set last access time/date ; ; Exit: Z if time stamps present ; NZ if no time stamps stime: ld hl,(dirbuf) ; get directory entry ld bc,060h ; offset entry point time/date stamp add hl,bc ; add offset to time stamp ld a,(hl) ; get time stamp byte sub 021h ; time stamp present? ret nz ; no, return ld d,a ; yes, clear d add hl,de ; add entry (update/create/access) ld a,e ; set access time if E ge 8 and 8 ; jr z,stim1 ; E lt 8 srl e ; E ge 8, set to 4 stim1: ld b,e ; save # bytes to write ld a,(secpnt) ; get sector pointer rrca ; shift 2 times rrca ld e,a ; save it rrca ; shift 2 times rrca add a,e ; add it (a=0,10,20) ld e,a ; save in e add hl,de ; add offset ld a,(funct) cp 54 ; if set DOS time ret z ; ..just point to date stamp push hl ; save result ld c,0 ; time return date/time pointer in hl push bc ; save 2 or 4 byte count call time ; return pointer in hl pop bc pop de ; get pointer ld c,b ; set write 2 or 4 bytes ld b,0 ldir ; copy 2 or 4 bytes to directory buff. xor a ; set zero flag - time stamps present ret ; and return to caller ; ; Store time stamp following file find, open, or make. ; ; Exit: HL points to file's 10-byte time stamp ; stored in DOS for subsequent commands ; when DOS-time is on (command 55). ; GetStp: ld e,2 ; point to file's stamp call stime ; point to beginning of ; ..last file's stamp ld de,cdate ; point to DOS storage ld (pexit),de ; save for return ld bc,10 jr nz,GtStp1 ; no stamp found ldir ; save full stamp ret GtStp1: ld b,c GtStp2: ld a,0 ; else zero out storage ld (de),a inc de djnz GtStp2 ret ; return to calling program ; ; Time - get time for stamping from DOS storage or BIOS ; ; Entry: rdwr and dtime both = 0ffh, get time from DOS ; and reset dtime to 0 ; else get time from BIOS ; ; Format of time returned to pointer in HL: ; ; HL + 0 = low byte of date since Dec. 31, 1977 ; HL + 1 = high byte of date since Dec. 31, 1977 ; HL + 2 = hours (BCD) ; HL + 3 = minutes (BCD) ; HL + 4 = seconds (BCD) (not used in time stamp) ; time: ld a,(dtime) ; Use DOS time? or a jr z,btime ; no, get BIOS time ld a,(funct) cp 22 ; creating file? ld hl,cdate ; point to create date jr z,time2 ; yes ld hl,udate ; no, point to update date/time time2: xor a ld (dtime),a ; use DOS time for one call only ret ; ; Get BIOS time to address DE ; gettim: push de ; save address to put time ld c,0 ; get time address call btime ; execute p2bios call pop de ; restore address to put time ld bc,5 ; 5 bytes to move ldir ; store the time ret ; and return to caller ; ; Set BIOS time from address DE ; settim: ex de,hl ; get address time in hl ld c,0ffh ; set time address ; and fall through to p2bios call ; ; Get/Set time in BIOS clock interface ; ; Entry: C = 0: On return HL points to 5-byte time entry ; C <> 0: 5-byte entry pointed to by HL sets system time btime: ; get system time push hl ; save value in hl ld hl,(timead) ; get address time routine ex (sp),hl ; put address on stack and restore hl ret ; execute BIOS time routine ; ; Use stored time stamp for very next DOS call ; ..all other DOS calls cancel DOS time thru p2exit ; UseStp: or 0ffh ld (dtime),a ; Use DOS time pop hl ; clean up stack jr p2ext0 ; ---------------------------- ; DOS exit routines ; ---------------------------- $ p2exit: xor a ld (dtime),a ; turn off DOS time ld a,(fldrv) ; test drive select used flag or a jr z,p2ext0 ; no then exit ld a,(FCB0) ; get FCB byte 0 ld (ix+0),a ; save it ld a,(drive) ; get old drive number call seldk ; select disk p2ext0: push ix ; save ix pop de ; restore de pop ix ; restore ix ld sp,(spsave) ; get old sp ld hl,(pexit) ; get exit code ld a,(funct) ; get function code ld c,a ; restore c ld a,l ; copy function code ld b,h ret ; and return to caller ; -------------------- ; RAM area ; -------------------- tabcnt: defb 0 ; tab counter tabcx1: defb 0 ; temporary tab counter (used by rdbuf) fcontp: defb 0 ; list enable flag (control p) lastch: defb 0 ; last character delay: defb 0ffh ; delay counter ; trans: defw 0 ; translation vector temp0: defw 0 ; number of files on drive dirbuf: defw 0 ; directory buffer ixp: defw 0 ; disk parameter block csv: defw 0 ; check sum pointer alv: defw 0 ; allocation vector pointer ; maxsec: defw 0 ; maximum number of sectors/track nblock: defb 0 ; number of blocks nmask: defb 0 ; mask number of blocks nextnd: defb 0 ; extent mask maxlen: defw 0 ; maximum block number-1 nfiles: defw 0 ; maximum number of files-1 ndir0: defb 0 ; first two entries alv buffer ndir1: defb 0 ncheck: defw 0 ; number of checksum entries nftrk: defw 0 ; first track number ; dskro: defw 0 ; disk R/O vector login: defw 0 ; login vector DMA: defw 080h ; DMA address ; funct: defb 0 ; function number pexit: defw 0 ; exit code fldrv: defb 0 ; drive select used flag rdwr: defb 0 ; read/write flag ; FCB0: defb 0 ; FCB byte 0 user: defb 0 ; user number drive: defb 0 ; drive number defdrv: defb 0 ; default drive number recdir: defw 0 ; record directory (checksum) filcnt: defw 0 ; file counter secpnt: defb 0 ; sector pointer subflg: defb 0 ; submit flag (reset disk command) ; dcopy: defw 0 ; copy address FCB searex: defb 0 ; exit code search searnb: defb 0 ; search number of bytes searqu: defb 0 ; search question mark used searpu: defb 0 ; search public file ;next two flags added by b.h. diff: db 0 ; Disk changed flag retflg: db 0 ; allow retry on error when non-zero ; dtime db 0 ; Flag for UseStp cdate ds 2 ; Create date storage udate ds 4 ; Update date/time adate ds 4 ; Last access date/time ; spsave: defw 0 ; stack pointer location defs 64 DOSs: equ $ ; DOS internal 64 byte stack DOSstop equ $ ; if ((DOSstop-DOS) gt 3584) * BDOS over 3584 bytes - too large!! * endif ; END Z80DTIME.Z80 umber defdrv: defb 0 ; default drive number recdir: defw 0 ; record directory (checksum) filcnt: defw 0 ; file counter secpnt: defb 0 ; sector pointer subflg: defb 0 ; submit flag (reset disk command) ; dcopy: defw 0 ; copy address FCB NZ3ENVM}CONFIG =, GO cannot be used with ZFILER!*"*~0 &* . -~;~6#~6>2;s;-2 kD3( #+*&!+ͳ1(=,Shell Stk Full8:C;\48:C;g%2%'ʹ'+ !&ͼ' , E2 2(ë 92ͱ$W%'z%:]/ (!6-͇& :%ć&2%Ͱ"x*((\$+"(:;(ʹ'; !&2;=(v)+o(W%2;2;̓+͠&Gs+̓+:(2>#(2:( x08:8#s+W+Invalid Cmd: Hq͞;*;*;K;!:͔ oz%u1!6-͈,ͬ&͗%* ~!;~#~{;"!M( !~ !~ȸ#(###3 W+NOT Wheel!o#~#fo͠3 =,No Shell Stk>8=,SH STK too small2 =,No Cmd Bufd,=,Bad TCAPO=, Macro: ͠& # !oS;Gx9s+2x!+Y< *& 9>ͣ0 s+!+Ͷ(=, NOT Foundo!:6Y<5O6 4S;( 6 ' >#(Z6  (! >2% K;!:W+No User Function: q:oJ&ay 0>^9y@9>2(6 8(2 (! Ͱ, ,9 ( > 9 y =,File Name : K;!:*K<#Ͷ(=, Enter Macro Choice or RETURN - ͠&S;!a9*; ("($( w#6 w6'(1"(-#6#";[;($(w#Ͳ(6#";*;~̓+6 '("( ( (9>&G~(#x'("(IS;^;$DUHRgCm=w$(#wS;^;$P-F6NUT@$*!:;:;6G#6O#!;6$#w#w#̓͋6:#͟U6.#͟*K< ͧ͟*K<#ͧK;K;/ !+ͧ:;Aw#:;ͦ5:;͆:;*M<|~ #8 , 8   , , +.>-zz,z2;*K<"}<@2;2;@+Jump to Filename: ;&!;? ~ q#*K<"}<*G<"K<.+*6<*K< "K<:;(*}<9&(5*I<9&('!; D**6<9& :;((2;+l:; *}<"K<:;>9.+*6<"K<͙*+l.+͢*+*8<"K8*6<"K<͙*\ 8"K<*;%";*K< *8<9& ͙**6<"K<*K< "K<**6<*K<9& ͺ**8<"K<**K<"K<# *K< p5 *K< >#6':4<.+*K< ~#İ,#ͳ(~9,@+Tag/Untag (T/U)? ͦ&T#(Uo @+Match (filename.ext)?  <&*K<"}<*G<"K<*I<*K<9&(*! "K&oC;z!0{2%͗%͔ oj2 W+Cmd Line Err^%ͼ'ͺ 22;@+Login [DIR][:MASK] &o:(";'(78SC;Ͷ%>2;*;###~:( #\@4~ ( G:;̶%'W+Directory Entry Invalid9W+Directory Access DeniedW+Invalid Mask - Ignoredo: 2;:!2;(=,OPTIONS A. single replace query B. group replace query C. archive replace query D. verify query E. verify default F. suppress SYS files G. sort by file name H. set copied file attributes I. use dest file attributes J. archive destination 0,Option to Toggle (or RET): 9S;A8 0 o&~/wͼ'! :;2!!:;E#a !!6- $>N(>Y9:!/2!ͼ'E#DI<*K<#Ͷ(=, is Y5>K9oW+Size of ͠$#Y<*z<"? ^D0, >^0,>v0,-- Macros --0,# : Display Menu0,ESC : Lead-in Char0,70-9 : Direct Macros{2;:;_~G(#~(͛=, - Ͱ,͛, ͯ9 #F(#F#~#(92;2;ͶD(>25<>W2;2;s+o:; @+Print (Y/N)? ͦ&Yo2;!";W+Printing *K<#Ͷ(23<>25<>292;͠$6Y<5(9 XE:;aW+File NOT FoundW+Empty Fileɯ2y<2;H!w:< !6 (ͨ5a:923<4{ 2;:9(p:;:;G:5<<25<د25<=,  [sp=line cr=screen ^x=file ^c:abort ^z:eof]  ͠&.(> 9 >2; B:;=25<:;G:5<<25<:525<:; i9i9> ::3<D( ͠& !+ͷ!Z<:< !+ͷ! <:9 !+ͷ*;#";Y5!+ ~Ź#2;ͻ>._{  {ͼ9!;4~ ͼ9!;4!6-=, EOF [^x:next ^c:abort]  ͠& ( D2;2;? !";2&2;!";͠$ !Z< :92323<*;";-(:;A2;2;͕2!";2&!";"<͠$6Y<5(7W+Empty File skipped.6Z<:; '=2;@+Usq to DIR: :2;:Y&Ͷ%H!:<(!W+USQ error, File skipped.f@+USQ ͨ(=, to :;A9:;v9>:9! <ͥ(23<ͬ&K;!:!"A<*;";*;! w͇"͡"#";*A<#"A<*E<9& >23w͇"#*A<#"A<*;";-(:;A[<*<9&AW+ -- USQ Checksum Erroro&( ;'( 8/C;s+oW+Destination Dir Entry InvalidoW+Destination Dir Access DeniedoK;!: <<:;(  <:;:;(:<:9! <Ͷ(=, Exists. Erase (Y/N)? ͦ&oY 4! w <*;";!";ͬ& < 9<(e2+<=, Vfy <(=('W+Verify Read Erroro!~͇"#[;*;9& =, OK >2;ͬ&K;!:AW+ -- CRC Erroro[1< 9 !&<=! o~(8s+#Ͷ(=, is R/O. Erase (Y/N)? ͦ&s+oY(# ~w#=!";*; w#W+Move File2;2;! B:; s+o2;͠$o͞:;o*8<[6<9& "8<[I<9& 0[K<9& "K<**;"W<͢*> 9*W<";"82;:;*K< *I<9&( 2&SI<*M<+"M05̓+*K<#Ͷ(=, is R/O. Rename anyway (Y/N)? ͦ&Yos+@+Rename File to:  <&! < ~#?(f <<(W+File Exists! << 2&*K< < !& < <[K<*K<"}<*;"W<2;*G<"K<*K< "K<[I<9&( ~# +> >2;#6'*}<"K<*W<";:;v)(W+Group ArchivesW+All Tagged Files ARE Archived.oW+Group Copy>W+Group usQ/W+Group MoveW+Group PrintW+Group View*K<"}<*;"W<.+!";*G<"K<2;=2;( **K< "K<*I<9& P(fQ(hV(jM("A(0D(a͞:; *K< #6. ?:;  ? ?:; *K< >wY<>w  ͕ Ͷ *}<"K<.+*W<";+:G:<<08:D2M2Q2VDs+l :c<Q>!sM"S"v9&> sS""D))[;";* +{ozg8 ?&"E<| >*;z(6w#2<2<2<:<( =2<:2<==2<:<[(y/1>6 o6 g*;!yg)0>g>!or$s%# [;O*; ~W$^S;^*<"<%*G<"K<"I<";!"M<>'2h2|\< <2;*M<|=ƀo&3(: (  ~> 8#:\w[K< 2&> w#"K<:;#*M<#"M<\< "(*K<"I<";*M<+|(s!)9&0+"O<W*O<;"O<*M<[O<}o|g"Qw#&( @4~ ȷs+o*;p#6 ^#6V;~͠3] ( /'~#/'O~#~ (/!+>0/ 7? 3( ;3=8X30;/(;~ (-:((&o 3 @+Password: &( ͞' 7 ~ S;#/3 :; ʹ'*K<# !&>2;͠3 ~O怱w#͠3 !0/w#> 9d,2#Fͯ9͈,> 5(>9|A9}DMv9>:9/(~ (9# =,Noname"!?6-*K<#͉(>.9~8 >_Ͱ,9,#!Z<Ͷ(ɯ> O(>.9~9#s+*/0o";|!g";!D*;$|8}!o";*;9&(}(o";{:4<*;6-=, !:;G6-((6-=,!:;W+Working ... !6-:;G((!6-:;G((File: --> Page HELP ZFILERZFILER CMDZFILER ???????????noname +,~,###~ ~0I3-~*V#^#6-V#^#6-~#a,U,[,9?,Ͱ,?,,?,*/~!ڄ,V###~ʄ,͙.=*/~!ڬ,V#..~ʬ,͙.=*/~!,...~,͙.=*/~!-....~-͙.=*/~!2-.....~2-͙.*/~!f-~2.##.~f-k-:.Ͱ.=%-2.2.%ʐ-\ʈ-9t-9t-d/.D/.2).3...+->-r-R-i-I-9t->2.t->2.t-@.O-^.t-G@.9:.2.t-@.9-@.dw. w.09-@..@.dw. .:.V.Q.:.:.L.Q.O:.o.m.iak.m.0ڂ.z.z0.yʓ.z9~ʬ.#\¦.~#9Ù.zð.O.*4+~.>G. ... .~#\.#."4"/"/f3/~/'/#~+'/##= /200h/:0V//b//b/d//b//R/>-0v/#h/> v/ 3/8:x20y20;3G~Aڰ//20#-0/͆0/-0/͆0/#³/-0/X3O R0//20:0G:0O=f3000#0!0 /OG=~S;!P0=_.:,<>ȷ;ɯ~-00#͆0ڃ0Wyڃ0ڃ0ڃ0ڃ0ڃ0OV0y70 Ґ0?720m12020 m12020w3:00m1<1~1T1'1##0x20_m1y20_ m1'0=1:0_:0_ :0G:0O}20w3:0N1T1##x11y11=~$^1:0<=G#~+$k1:0Oͨ3ʥ1O~ʬ1X6yʠ1 w#”1Ê1>>͠32~#1¿1%2͠3~112͠3x11K+x2w+ 11͠3w#2>>>I3###~I3###w+3|g2"^#V|g2~72y2G~ʊ2#|2t22~;ʡ2ʡ2>;ʭ2~#ʴ2¡26t22s#r~#2=*4^#V*4~#fo~#fo~*4/F##x_*4.~*4$~#fo|*4*4R*4,~*4"~#fo*4-~*4^#V#~ѷ*4 ^#V#~ѷ*40~#_ͨ3XG*4^#V#~#Fѷ*4+~*4)~#fo~*4 ^#V8:| 4~ 48:$3G#~#$3O3"4225235$կM4~#:i4,4!4U4~S;Aڂ4A}4<225#~:4?š4235#~:4z4G~#:ʸ40z4 z4OxGÜ4x z42354~:4#~,4!4 >?4:25G:35O> 5~.5# 54)5445)5*5>?5# 545#!5> )5~S;!W5=_.:;,<>ɾ'͂5͂5d͂5 ͂5}0ͼ9}o|gڑ5 Ä5}o|gyž5y0ü9d5 5055 5_y5{y05{w#99Oy!67!68!6?8!6Ä8!6ø8!68>Í7>Í7>Í7>Í7>Í7>Í7>*9!(*9####*9!(*9####~‹7"9####$͊: 99w7͒7|7*96#6#!&s#rÍ7~‹7"9####$͊: 9798Á7~m7"9##^#Vz|72 9r+s+5|86#!&s#r͒7|8*9##6#6: 92 9~m7"9##^#V: 9r+s+5|86#!&s#r͹7|8r7~ʍ7"98~ʍ7"9#~8>9~898*96####ʆ7Í7*9Ä8w W:O:29>39O:29O:29,9_9O:O:> :> :d͎9 ͎909ژ9 Ð9_y¥9ʥ9{y09{> 9> 9O:9Gy :xy9*.9/*. 9O*. :O*.:Y X OGH`:Ẃ:Hv:Ẃ:r# x:͡:H ͪ: Hͪ:|:»:}:~# x»: ~+ x::HAz::+|:|g}o;7~#;2;;+~#;2;#;;O !J;F;#<;y  a{_F#";##y;###h;*;z;#~#fo*;}ʐ;$.Ɋ<