DOS 3.30 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

882 lines
20 KiB

;
; After the boot sector reads in msbio it jumps to this location. Msbio
; immediately jumps to initialization code in msinit.
;
EXTRN INIT:NEAR
Public START$
START$:
JMP INIT ; START$ patch by init to point to
; hdrive BPB
PATHSTART 001,BIO
;----------------------------------------------------------------------------
;
; Command Jump Tables
;
; These tables hold the entry points for the various service routines
; for the different drivers. The index in the table is the command code for
; that funcion plus two. For example the command code for Read (input) is 4,
; The 6th (4 plus 2) entry in the table DSKTBL is DSK$READ - the command to
; read a disk. Commands which do not exist for a device are filled with
; exit (e.g. MediaCheck for CONTBL). The first entry in the table is the
; largest command code implemented for that device. This value is used
; for error checking. If new command codes are added then the first entry
; in the table must be incremented.
;
; BEWARE - These tables overlap somewhat! -c.p.
;
;
; Disk:
;
ODD
DSKTBL LABEL BYTE
DB 24 ; This is the size of the table YUK!!!!
DW DSK$INIT ; Code 0: INIT
DW MEDIA$CHK ; code 1: Media Check
DW GET$BPB ; code 2: BUILD BPB
DW CMDERR ; code 3: IOCTL input
DW DSK$READ ; code 4: INPUT
DW BUS$EXIT ; code 5: NONDESTRUCITVE INPUT, NO WAIT
DW EXIT ; code 6: INPUT STATUS
DW EXIT ; code 7: INPUT FLUSH
DW DSK$WRIT ; code 8: OUTPUT
DW DSK$WRITV ; code 9: OUTPUT with verify
DW EXIT ; code 10: OUTPUT STATUS
DW EXIT ; code 11: OUTPUT FLUSH
DW CMDERR ; code 12: IOCTL output
Public TABLE_PATCH
TABLE_PATCH LABEL WORD ;ARR 2.42
DW DSK$OPEN ; code 13: DEVICE OPEN
DW DSK$CLOSE ; code 14: DEVICE CLOSE
DW DSK$REM ; code 15: REMOVABLE MEDIA
dw exit
dw exit
dw exit
DW GENERIC$IOCTL
dw exit
dw exit
dw exit
dw IOCTL$GETOWN
dw IOCTL$SETOWN
;
; Console:
;
ODD
CONTBL LABEL BYTE
DB 10
DW EXIT
DW EXIT
DW EXIT
DW CMDERR
DW CON$READ
DW CON$RDND
DW EXIT
DW CON$FLSH
DW CON$WRIT
DW CON$WRIT
DW EXIT
;
; Auxilary:
;
ODD
AUXTBL LABEL BYTE
DB 10
DW EXIT
DW EXIT
DW EXIT
DW CMDERR
DW AUX$READ
DW AUX$RDND
DW EXIT
DW AUX$FLSH
DW AUX$WRIT
DW AUX$WRIT
DW AUX$WRST
;
; Clock:
;
ODD
TIMTBL LABEL BYTE
DB 9
DW EXIT
DW EXIT
DW EXIT
DW CMDERR
DW TIM$READ
DW BUS$EXIT
DW EXIT
DW EXIT
DW TIM$WRIT
DW TIM$WRIT
;
; Printer:
;
ODD
PRNTBL LABEL BYTE
DB 24
DW EXIT ;INIT
DW EXIT
DW EXIT
DW CMDERR
DW EXIT$ZER ;INDICATE ZERO CHARS READ
DW BUS$EXIT
DW EXIT
DW EXIT
DW PRN$WRIT
DW PRN$WRIT
DW PRN$STAT
DW EXIT
DW EXIT
DW EXIT
DW EXIT
DW EXIT
DW PRN$TilBusy
DW EXIT
DW EXIT
DW PRN$GenIOCTL
dw exit
dw exit
dw exit
dw CMDERR
dw CMDERR
EVENB
Public Old13
OLD13 label DWORD
db '5986' ;Code for 3.30
Public Orig13
ORIG13 label DWORD
db '21',0,0 ;Code for 3.30
;
; PTRSAV - pointer save
;
; This variable holds the pointer to the Request Header passed by a
; program wishing to use a device driver. When the strategy routine is
; called it puts the address of the Request header in this variable and
; returns.
;
EVENB
PUBLIC PTRSAV
PTRSAV DD 0
;
; Buffer for the AUX device driver
;
;;Rev 3.30 Modification
PUBLIC AUXBUF
AUXBUF DB 0,0,0,0 ;SET OF 1 BYTE BUFFERS FOR COM 1,2,3, AND 4
EVENB
PUBLIC PREVOPER,NUMBER_OF_SEC
;;End of Modification
PrevOper DW ? ; Holds ROM DISK INT request (i.e. Register AX).
Number_Of_Sec DB ? ; Holds number of sectors to read on an ECC error
;;Rev 3.30 Modification
IF ($-CODE) GT 100H
%OUT VDISK BUFFER NOT CORRECTLY LOCATED
ELSE
ORG 100H
ENDIF
PUBLIC VDISK_AREA
VDISK_AREA DB 108 DUP(0) ;FOR USE BY VDISK
;;End of Modification
;
; AUXNUM holds the number of the printer or AUX device requested. For
; example if printer 2 was called (PRN2$IN) AUXNUM is set to be one; with
; line printer 3 AUXNUM is set to 2. With this set the printer device driver
; can tell which printer to command applies to.
;
; WARNING!!! These are addressed together in GETDX
;
EVENB
AUXNUM DB 0
DB 0
;
; Device Header for the CON Device Driver
;
EVENB
PUBLIC CONHeader
CONHeader LABEL WORD
DD AUXDEV2
DW 1000000000010011B ; Con in and con out + special
DW STRATEGY ; Strategy entry point
DW CON$IN ; interrupt entry point
DB 'CON ' ; device name
;
; Device Header for device "AUX"
;
EVENB
PUBLIC AUXDEV2
AUXDEV2 LABEL WORD ;HEADER FOR DEVICE "AUX"
DD PRNDEV2
DW 1000000000000000B ; attribute word, character device
DW STRATEGY ; device strategy routine
DW AUX0$IN ; device interrupt routine
DB 'AUX ' ; device name
;
; Device Header for device PRN
;
EVENB
PUBLIC PRNDEV2
PRNDEV2 LABEL WORD ;HEADER FOR DEVICE "PRN"
DD TIMDEV
DW CharDev + OutTilBusy + Dev320
DW STRATEGY
DW PRN0$IN
DB 'PRN '
;
; Device Header for device CLOCK$
;
EVENB
PUBLIC TIMDEV
TIMDEV LABEL WORD
DD DSKDEV
DW 1000000000001000B
DW STRATEGY
DW TIM$IN
DB 'CLOCK$ '
;
; Device Header for disk devices
;
; Device attribute bits:
; Bit 6 - DOS 3.2 Bit
;
EVENB
PUBLIC DSKDEV
DSKDEV LABEL WORD
DD COM1DEV
DW 0000100001000000B ; DOS 3.2
DW STRATEGY ; strategy routine
DW DSK$IN ; Interrupt entry point
;
; maximum number of drives
;
DRVMAX DB 4
Public DRVMAX
;
; Last drive accessed
;
PUBLIC STEP_DRV
STEP_DRV DB -2 ; ARR 2.20 LAST DRIVE ACCESSED
Public Phys_Drv
Phys_Drv DB 0 ; Used by setdrvie for getting
; BDS for logical drive, or physical
; drive. 0 => use logical
; 1 => use physical
Public fHave96
fHave96 DB 0 ; Flag to indicate presence of
; 96tpi support
Public Single
Single DB 0 ; Used to detect single drive systems
Public fHaveK09
fHaveK09 DB 0 ;Indicates if this is a K09 or not
; used by console driver.
Public NEW_ROM
NEW_ROM DB 0 ;Set to 1 if we have a ROM that can
; handle strange media layouts.
PUBLIC FSETOWNER
fSetOwner db ? ;=1 if we are setting the owner of a
;drive. (Examined by CheckSingle).
public Secrete_Code
Secrete_Code dw 'jk' ;Code for 3.30.
;
; Device Header for device "COM1"
;
EVENB
Public COM1DEV
COM1DEV LABEL WORD
DD LPT1DEV
DW 1000000000000000B ; attribute word, character device
DW STRATEGY ; device strategy routine
DW AUX0$IN ; device interrupt routine
DB 'COM1 ' ; device name
;
; Device Header for device LPT1
;
EVENB
Public LPT1DEV
LPT1DEV LABEL WORD
DD LPT2DEV
DW CharDev + OutTilBusy + Dev320
DW STRATEGY
DW PRN1$IN
DB 'LPT1 '
;
; Device Header for device LPT2
;
EVENB
Public Lpt2Dev
LPT2DEV LABEL WORD
DD LPT3DEV
DW CharDev + OutTilBusy + Dev320
DW STRATEGY
DW PRN2$IN
DB 'LPT2 '
;
; Device Header for device LPT3
;
EVENB
Public Lpt3Dev
LPT3DEV LABEL WORD
DD COM2DEV
DW CharDev + OutTilBusy + Dev320
DW STRATEGY
DW PRN3$IN
DB 'LPT3 '
;
; Device Header for device "COM2"
;
EVENB
Public Com2Dev
COM2DEV LABEL WORD
dd COM3DEV
DW 1000000000000000B ; attribute word, character device
DW STRATEGY ; device strategy routine
DW AUX1$IN ; device interrupt routine
DB 'COM2 ' ; device name
;;Rev 3.30 Modification
;
; Device header for device "COM3"
;
EVENB
PUBLIC COM3DEV
COM3DEV LABEL WORD
dd COM4DEV
dw 1000000000000000b ; character device attribute
dw STRATEGY
dw AUX2$IN ; com3 == aux2
db 'COM3 '
;
; Device header for device "COM4"
;
EVENB
PUBLIC COM4DEV
COM4DEV LABEL WORD
dw -1,CODE
dw 1000000000000000b ; character device attribute
dw STRATEGY
dw AUX3$IN ; com4 == aux3
db 'COM4 '
;;End of Modification
; Hard-wire the link to the next Int2f handler.
;;Rev 3.30 Modification
EVENB
PUBLIC Next2f_13
NEXT2F_13 LABEL WORD
EXTRN INT2F_DISK:FAR ;MSBIO2
DD INT2F_DISK
;
; Start of linked list of BDS's
;
EVENB
Public Start_BDS
START_BDS LABEL WORD
DD BDS1 ;START OF BDS LINKED LIST.
;;End of Modification
;
; Some floppy drives do not have changeline support. The result is a
; large amount of inefficiency in the code. A media-check always returns
; "I don`t know". This cause DOS to reread the FAT on every access and
; always discard any cached data.
; We get around this inefficiency by implementing a "Logical Door Latch".
; The following three items are used to do this. The logical door latch is
; based on the premise that it is not physically possible to change floppy
; disks in a drive in under two seconds (most people take about 10). The
; logical door latch is implemented by saving the time of the last successful
; disk operation (in the value TIM_DRV). When a new request is made the
; current time is compared to the saved time. If less than two seconds have
; passed then the value "No Change" is returned. If more than two seconds
; have passed the value "Don't Know" is returned.
; There is one complecation to this algorithm. Some programs change the
; value of the timer. In this unfortunate case we have an invalid timer.
; This possiblity is detected by counting the number of disk operations
; which occur without any time passing. If this count exceeds the value of
; "AccessMax" we assume the counter is invalid and always return "Don't
; Know". The variable "AccessCount" is used to keep track of the number
; of disk operation which occur without the time changing.
;
PUBLIC ACCESSCOUNT
AccessCount db 0 ; number of times media check called
PUBLIC TIM_DRV
TIM_DRV DB -1 ; time when last disk I/O was performed
PUBLIC FLAGBITS
FlagBits dw 0 ; Bits to set in flag field when doing
; a Set_Changed_DL
PUBLIC MEDBYT
MedByt DB ? ; hold media byte from floppy
EVENB
PUBLIC WRTVERIFY
WRTVERIFY LABEL WORD
PUBLIC RFLAG
RFLAG DB ROMRead ;2 for read, 3 for write
VERIFY DB 0 ;1 if verify after write
PUBLIC SECCNT
SECCNT DW 0
Public HARDNUM
HARDNUM DB 99 ;logical drive number of first hardfile
;
; Some of the older versions of the IBM rom-bios always assumed a seek would
; have to be made to read the diskette. Consequently a large head settle
; time was always used in the I/O operations. To get around this problem
; we need to continually adjust the head settle time. The following
; algorithm is used:
;
; Get the current head settle value.
; If it is 1, then
; set slow = 15
; else
; set slow = value
; ...
; if we are seeking and writing then
; use slow
; else
; use fast
; ...
; restore current head settle value
;
PUBLIC MOTORSTARTUP,SETTLECURRENT,SETTLESLOW
MotorStartup db ? ; value from table
SettleCurrent db ? ; value from table
SettleSlow db ? ; slow settle value
NextSpeed DB ? ; value of speed to be used
public save_head_sttl
Save_head_sttl db ? ;used by READ_SECTOR routine
Public EOT
EOT DB 9
;
; pointer to Disk Parameter Table
;
EVENB
PUBLIC DPT
DPT DD ?
;
; The following two sets of variables are used to hold values for
; disk I/O operations
; Keep the next two items contiguous - see IOCTL_Block for reason
PUBLIC CURSEC,CURHD,CURTRK,SPSAV
CURSEC DB 0 ; current sector
CURHD DB 0 ; current head
CURTRK DW 0 ; current track
SPSAV DW 0 ; save the stack pointer
;
; The following are used for IOCTL function calls
;
PUBLIC FORMT_EOT,HDNUM,TRKNUM,GAP_PATCH
FORMT_EOT DB 8 ; EOT used for format
HDNUM DB 0 ; Head number
TRKNUM DW 0 ; Track being manipulated
GAP_PATCH DB 50h ; Format gap patched into DPT
;
; Disk errors returned from the IBM rom
;
Public ERRIN
ERRIN LABEL BYTE
DB 80H ; no response
DB 40H ; seek failure
DB 10H ; bad CRC
DB 8 ; DMA overrun
DB 6 ; media change
DB 4 ; sector not found
DB 3 ; write attempt to write-protect disk
PUBLIC LSTERR
LSTERR DB 0 ; all other errors
;
; returned error code corresponding to above errors
;
Public ERROUT
ERROUT LABEL BYTE
DB 2 ; no response
DB 6 ; seek failure
DB 4 ; bad CRC
DB 4 ; DMA overrun
DB 15 ; invalid media change
DB 8 ; sector not found
DB 0 ; write attempt on write-protect disk
DB 12 ; general error
PUBLIC NUMERR
NUMERR = ERROUT-ERRIN
;-------------------------------------------------------------
;
; DiskSector is a 512 byte sector into which the boot sector
; is read. It is also used as read sector for DMA check for
; hard disk.
Public DiskSector
DiskSector db 11 dup(?) ; take care of 3 jump bytes plus OEM name.
PUBLIC BPB_IN_SECTOR
BPB_In_Sector dw ?
PUBLIC SECPERCLUSINSECTOR
SECPERCLUSINSECTOR DB ?
dw ?
db ?
dw ?
dw ?
PUBLIC MEDIABYTE
mediabyte db ?
dw ?
dw ?
dw ?
dw ?
db ?
db 512-($-DiskSector) dup (?)
;---------------------------------------------------------------------
;
; The "BDS"'s contain information for each drive in the system.
; There is one BDS for each logical drvie in the system. The BDS's
; are all linked together in a chain. The BDS contain various values
; important to the disk drive. Various values are updated whenever actions
; are performed. For example if a drive is read from the last time
; accessed fields are updated to the current time.
; Initial values:
; * Sectors/Alloc. unit in BPB initially set to -1 to signify that
; the BPB has not been filled.
; * Link is set to -1 to signify end of list.
; * number of cylinders in MaxParms initialized to -1 to indicate
; that the parameters have not been set.
;
; Start_BDS contains a pointer to the first BDS. It is through this
; pointer that routines find particular BDS (see SetDrive to see how
; this is done).
;
EVENB
BDS1 LABEL WORD
DD BDS2 ;LINK TO NEXT STRUCTURE
DB 0 ;ROM DISK INT Drive Number
DB 0 ;Logical Drive Letter
PUBLIC FDRIVE1
FDRIVE1 DW 512 ;Physical sector size in bytes
DB -1 ;Sectors/allocation unit
DW 1 ;Reserved sectors for DOS
DB 2 ;No. allocation tables
DW 64 ;Number directory entries
DW 9*40 ;Number sectors (at 512 bytes ea.)
DB 00000000B ;Media descriptor, initially 00H.
DW 2 ;Number of FAT sectors
DW 9 ;Sector limit
DW 1 ;Head limit
DW 0 ;Hidden sector count
DB 0 ; TRUE => Large fats
OPCNT1 DW 0 ;Open Ref. Count
VOLID1 DB "NO NAME ",0 ;Volume ID for this disk
DB 3 ;Form Factor
FLAGS1 DW 0020H ;Various Flags
; DB 9 dup (0) ;Reserved for future use
dw 40 ; number of cylinders
RecBPB1 DW 512 ;Physical sector size in bytes
DB 1 ;Sectors/allocation unit
DW 1 ;Reserved sectors for DOS
DB 2 ;No. allocation tables
DW 0E0H ;NUMBER DIRECTORY ENTRIES
DW 9*40 ;Number sectors (at 512 bytes ea.)
DB 0F0h ;Media descriptor, initially 00H.
DW 2 ;Number of FAT sectors
DW 9 ;Sector limit
DW 2 ;HEAD LIMIT
DW 0 ;Hidden sector count
DB 12 DUP (?)
TRACK1 DB -1 ;Last track accessed on this drive
TIM_LO1 DW -1 ;Keep these two contiguous (?)
TIM_HI1 DW -1
EVENB
BDS2 LABEL WORD
DD BDS3 ;LINK TO NEXT STRUCTURE
DB 0 ;INT 13 DRIVE NUMBER
DB 0 ;Logical Drive Letter
PUBLIC FDRIVE2
FDRIVE2 DW 512 ;Physical sector size in bytes
DB -1 ;Sectors/allocation unit
DW 1 ;Reserved sectors for DOS
DB 2 ;No. allocation tables
DW 64 ;Number directory entries
DW 9*40 ;Number sectors (at 512 bytes ea.)
DB 00000000B ;Media descriptor, initially 00H.
DW 2 ;Number of FAT sectors
DW 9 ;Sector limit
DW 1 ;Head limit
DW 0 ;Hidden sector count
DB 0 ; TRUE => Large fats
OPCNT2 DW 0 ;Open Ref. Count
VOLID2 DB "NO NAME ",0 ;Volume ID for this disk
DB 3 ;Form Factor
FLAGS2 DW 0020H ;Various Flags
; DB 9 dup (0) ;Reserved for future use
dw 40 ; number of cylinders
RecBPB2 DW 512 ;Physical sector size in bytes
DB 1 ;Sectors/allocation unit
DW 1 ;Reserved sectors for DOS
DB 2 ;No. allocation tables
DW 0E0H ;NUMBER DIRECTORY ENTRIES
DW 9*40 ;Number sectors (at 512 bytes ea.)
DB 0F0h ;Media descriptor, initially 00H.
DW 2 ;Number of FAT sectors
DW 9 ;Sector limit
DW 2 ;HEAD LIMIT
DW 0 ;Hidden sector count
DB 12 DUP (?)
TRACK2 DB -1 ;Last track accessed on this drive
TIM_LO2 DW -1 ;Keep these two contiguous (?)
TIM_HI2 DW -1
EVENB
BDS3 LABEL WORD
DD BDS4 ;LINK TO NEXT STRUCTURE
DB 0 ;INT 13 DRIVE NUMBER
DB 0 ;Logical Drive Letter
PUBLIC FDRIVE3
FDRIVE3 DW 512 ;Physical sector size in bytes
DB -1 ;Sectors/allocation unit
DW 1 ;Reserved sectors for DOS
DB 2 ;No. allocation tables
DW 64 ;Number directory entries
DW 9*40 ;Number sectors (at 512 bytes ea.)
DB 00000000B ;Media descriptor, initially 00H.
DW 2 ;Number of FAT sectors
DW 9 ;Sector limit
DW 1 ;Head limit
DW 0 ;Hidden sector count
DB 0 ; TRUE => Large fats
OPCNT3 DW 0 ;Open Ref. Count
VOLID3 DB "NO NAME ",0 ;Volume ID for this disk
DB 3 ;Form Factor
FLAGS3 DW 0020H ;Various Flags
; DB 9 dup (0) ;Reserved for future use
dw 40 ; number of cylinders
RecBPB3 DW 512 ;Physical sector size in bytes
DB 1 ;Sectors/allocation unit
DW 1 ;Reserved sectors for DOS
DB 2 ;No. allocation tables
DW 0E0H ;NUMBER DIRECTORY ENTRIES
DW 9*40 ;Number sectors (at 512 bytes ea.)
DB 0F0h ;Media descriptor, initially 00H.
DW 2 ;Number of FAT sectors
DW 9 ;Sector limit
DW 2 ;HEAD LIMIT
DW 0 ;Hidden sector count
DB 12 DUP (?)
TRACK3 DB -1 ;Last track accessed on this drive
TIM_LO3 DW -1 ;Keep these two contiguous (?)
TIM_HI3 DW -1
EVENB
BDS4 LABEL WORD
DW -1 ;Link to next structure
DW Code
DB 0 ;INT 13 DRIVE NUMBER
DB 0 ;Logical Drive Letter
PUBLIC FDRIVE4
FDRIVE4 DW 512 ;Physical sector size in bytes
DB -1 ;Sectors/allocation unit
DW 1 ;Reserved sectors for DOS
DB 2 ;No. allocation tables
DW 64 ;Number directory entries
DW 9*40 ;Number sectors (at 512 bytes ea.)
DB 00000000B ;Media descriptor, initially 00H.
DW 2 ;Number of FAT sectors
DW 9 ;Sector limit
DW 1 ;Head limit
DW 0 ;Hidden sector count
DB 0 ; TRUE => Large fats
OPCNT4 DW 0 ;Open Ref. Count
VOLID4 DB "NO NAME ",0 ;Volume ID for this disk
DB 3 ;Form Factor
FLAGS4 DW 0020H ;Various Flags
; DB 9 dup (0) ;Reserved for future use
dw 40 ; number of cylinders
;;Rev 3.30 Modification
RECBPB4 DW 512 ;BYTES PER SECTOR
DB 1 ;SECTORS/ALLOCATION UNIT
DW 1 ;RESERVED SECTORS FOR DOS
DB 2 ;NO. ALLOCATION TABLES
DW 0E0H ;NUMBER DIRECTORY ENTRIES
DW 9*40 ;NUMBER SECTORS (AT 512 BYTES EA.)
DB 0F0H ;MEDIA DESCRIPTOR, INITIALLY F0H.
DW 2 ;NUMBER OF FAT SECTORS
DW 9 ;SECTOR LIMIT
DW 2 ;HEAD LIMIT
DW 0 ;HIDDEN SECTOR COUNT
DB 12 DUP (?)
;;End of Modification
TRACK4 DB -1 ;Last track accessed on this drive
TIM_LO4 DW -1 ;Keep these two contiguous (?)
TIM_HI4 DW -1
bpbType struc
spf db ?
spt db ?
cdire db ?
csec dw ?
spa db ?
chead db ?
bpbType ends
PUBLIC SM92
sm92 bpbType <3,9,70H,2*9*80,2,2>
;
; ALTAH is a single character buffer used to handle special keys.
;
PUBLIC ALTAH
ALTAH DB 0 ;Special key handling
;
; The following variable can be modified via IOCTL sub-function 16. In this
; way, the wait can be set to suit the speed of the particular printer being
; used. One for each printer device.
;
PUBLIC PRINTDEV
PRINTDEV DB 0 ; Index into following array
EVENB
PUBLIC WAIT_COUNT
WAIT_COUNT DW 4 dup (50h) ; Array of Retry counts for printer
;
; DAYCNT is the number of days since 1-1-80.
; Each time the clock is read it is necessary to check if another day has
; passed. The ROM only returns the day rollover once so if it is missed
; the time will be off by a day.
;
EVENB
Public DAYCNT
DAYCNT DW 0
;
; The following variables and two routines (MSGOUT and MSGNUM) are used
; with the debug routines to print numbers and messages on the screen.
;
; The variable fTestBits controls the level of debugging in the system.
; See the comments and "equ's" in msmacro.inc for an explination of
; how to control the level of debugging. In a nutshell, setting
; fTestBits to fTestALL prints all the debugging messages. Setting
; it to fTestDisk prints all disk related messages, etc.
;
if test
Public NumBuf
NumBuf DB 5 dup (?)
Public Digits
Digits DB "0123456789ABCDEF"
Public fTestBits
FTESTBITS DW fTestDISK
endif
PATHEND 001,BIO