MS-DOS/v2.0/source/SYS.ASM
2018-09-21 17:53:34 -07:00

587 lines
22 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

TITLE MS-DOS SYS Program
; SYS - Copies system programs IBMBIO.COM/IO.SYS and IBMDOS.COM/MSDOS.SYS
; 1.6 05/21/82 Added rev number message
; 1.61 06/04/82 Allow SYS to blank disk TimP at SCP
; 1.70 06/30/82 NON contiguous DOS allowed on 2.00 IBM. Allows SYS to
; 1.0 1.1 disks.
; 1.71 07/02/82 Put in CHDIRs to make sure everything done in root dir.
; 1.80 04/26/83 MZ make sys work in small machines; use full 2.0 system
; calls
; 1.81 07/22/83 ARR Added check in IBM version for valid FAT ID on
; destination because of IBM problem with SYSing to
; unformatted disks which are really formatted.
; Prints NoDest message for ridic IBM reasons, should
; have a better message.
FALSE EQU 0
TRUE EQU NOT FALSE
IBMJAPVER EQU FALSE
IBMVER EQU FALSE
MSVER EQU TRUE
.xlist
.xcref
INCLUDE DOSSYM.ASM
.cref
.list
DOSVER_LOW EQU 0136H ; Lowest acceptable DOS version number
DOSVER_HIGH EQU 020BH ; Highest acceptable DOS version
CODE SEGMENT WORD PUBLIC
CODE ENDS
CONST SEGMENT BYTE PUBLIC
CONST ENDS
DATA SEGMENT BYTE PUBLIC
DATA ENDS
DG GROUP CODE,DATA,CONST
DATA SEGMENT PUBLIC BYTE
EXTRN BADDRV:BYTE, BADDRVLen:WORD
EXTRN BADPARM:BYTE, BADPARMLen:WORD
EXTRN GETSYS:BYTE, GETSYSLen:WORD
EXTRN SYSDRV:BYTE
EXTRN NODEST:BYTE, NODESTLen:WORD
EXTRN BADSIZ:BYTE, BADSIZLen:WORD
EXTRN DONE:BYTE, DONELen:WORD
EXTRN BADVER:BYTE
IF IBMJAPVER
EXTRN BADDISK:BYTE, BADDISKLen:WORD
ENDIF
DEFALT DB 0
IF MSVER
BIOSName DB "A:\IO.SYS",0
DOSName DB "A:\MSDOS.SYS",0
ENDIF
IF IBMVER OR IBMJAPVER
BIOSName DB "A:\IBMBIO.COM",0
DOSName DB "A:\IBMDOS.COM",0
ENDIF
BIOSInFH DW ? ; file handle of source BIOS
BIOSLenLow DW 2 DUP (?) ; 32-bit length of BIOS
BIOSLenHigh DW 2 DUP (?) ; 32-bit length of BIOS
BIOSTime DW 2 DUP (?) ; place to store time of BIOS write
BIOSOutFH DW ? ; fh of BIOS destination
DOSInFH DW ? ; file handle of source DOS
DOSLenLow DW 2 DUP (?) ; 32-bit length of DOS
DOSLenHigh DW 2 DUP (?) ; 32-bit length of DOS
DOSTime DW 2 DUP (?) ; place to store time of DOS write
DOSOutFH DW ? ; fh of DOS destination
AllName DB "A:\*.*",0
cbBuf DW ? ; number of bytes in buffer
pDOS DW ? ; offset of beginning of DOS in buffer
pDOSEnd DW ? ; offset of end of DOS in buffer
IF IBMVER OR IBMJAPVER
BOOT DW 256 DUP (0)
IF IBMJAPVER
LLISTBUF DW 256 DUP (0)
ENDIF
ENDIF
IF IBMJAPVER
RELOC DW 1 DUP(?)
STARTSECTOR DW 1 DUP(?)
ENDIF
BUF LABEL BYTE ; beginning of area for file reads
DATA ENDS
CODE SEGMENT PUBLIC
ASSUME CS:DG,DS:DG,ES:DG,SS:DG
ORG 100H
Start:
JMP SHORT CheckVersion
IF IBMVER
DW OFFSET DG:BOOT
ENDIF
HEADER DB "Vers 1.81"
CheckVersion:
PUSH AX ; save drive letter validity
MOV AH,GET_VERSION
INT 21H ; get dos version
XCHG AH,AL ; Turn it around to AH.AL
CMP AX,DOSVER_LOW ; is it too low?
JB GOTBADDOS ; yes, error
CMP AX,DOSVER_HIGH ; too high?
JBE OKDOS ; yes, go check drive letter
GOTBADDOS:
MOV DX,OFFSET DG:BADVER ; message to dump
MOV AH,STD_CON_STRING_OUTPUT ; standard output device
INT 21H
INT 20H ; old style exit for compatability
OKDOS: POP AX ; get drive validity
JMP SHORT SYS ; go process
ERR0: MOV DX,OFFSET DG:BADPARM ; no drive letter
MOV CX,BadParmLen
JMP DisplayError
ERR1: MOV DX,OFFSET DG:BADDRV ; drive letter invalid
MOV CX,BadDrvLen
JMP DisplayError
ERR2: MOV AL,DEFALT ; get default drive number
ADD AL,'A'-1 ; turn into letter
MOV SYSDRV,AL ; place into middle of message
MOV DX,OFFSET DG:GETSYS
MOV CX,GETSYSLen ; length for output
MOV BX,stderr ; use stderr
MOV AH,Write ; Ask for system disk
INT 21H
CALL GetKeystroke ; wait for him to type simething
XOR AL,AL ; valid drive spec now...
SYS:
CMP DS:(BYTE PTR 5DH)," " ; Was file specified?
JNZ ERR0 ; yes, no files are allowed -> error
CMP AL,-1 ; Invalid drive spec?
JZ ERR1 ; yes, must have valid drive -> error
CMP DS:(BYTE PTR 5CH),0 ; No drive specified?
JZ ERR1 ; yes, cannot sys to default drive error
MOV AH,GET_DEFAULT_DRIVE ; Get default drive
INT 21H
INC AL ; turn from phys drive to logical drive
MOV DEFALT,AL ; save it for possible printing
CMP DS:(BYTE PTR 5CH),AL ; did he specify the default drive?
JZ ERR1 ; yes, default drive not allowed
IF IBMVER ; Check for "valid" destination
PUSH AX
MOV AL,BYTE PTR DS:[5Ch]
DEC AL
MOV BX,OFFSET DG:BUF ; Temp space
MOV DX,1 ; Sector 1 (first sec of FAT)
MOV CX,DX ; One sector
INT 25H ; Read Fat sector
POP AX ; Flags
POP AX ; Real AX
JC OKFAT ; Don't error here, let a CREATE or
; some other call to the dest
; generate a more useful INT 24H
; error
CMP BYTE PTR [BUF],0F8H
JAE OKFAT
JMP ERR3
OKFAT:
ENDIF
ADD AL,'A'-1 ; turn into letter
MOV BIOSName,AL ; twiddle source name
MOV DOSName,AL ; twiddle source name
CLD
MOV DX,OFFSET DG:BIOSName ; source name
MOV DI,OFFSET DG:BIOSInFH ; pointer to block of data
CALL OpenFile
JC Err2 ; not found, go and try again
MOV DX,OFFSET DG:DOSName ; source of DOS
MOV DI,OFFSET DG:DOSInFH ; pointer to block of data
CALL OpenFile ; Look for DOS
JC ERR2 ; not there, go ask for a system disk
MOV CX,SP ; get lowest available spot
SUB CX,0200h+(OFFSET DG:BUF); leave room for all sorts of things
MOV cbBuf,CX ; store length away
CALL FillMem ; load up memory with files
IF IBMJAPVER
CALL READ_BOOT ; need to copy boot sector too
ENDIF
MOV AL,DS:(BYTE PTR 5CH) ; get drive of destination
IF IBMJAPVER
CALL CHECK_TRAN ; check for bootable device
JZ DOSWRT ; ok to boot
MOV DX,OFFSET DG:BADDISK ; incorrect format to boot
MOV CX,BadDiskLen
JMP DisplayError ; go error and quit
DOSWRT:
ENDIF
ADD AL,'A'-1 ; convert to letter
MOV BIOSName,AL ; point names at destination drive
MOV DOSName,AL
MOV AllName,AL ; look for any files
MOV AH,Find_First ; look for files
MOV DX,OFFSET DG:AllName ; path of where to look
MOV CX,Attr_Hidden+Attr_System ; attributes to find
INT 21H
JC PutSys ; no files - go and copy
IF MSVER
MOV DL,DS:(BYTE PTR 5CH) ; get drive number
MOV AH,GET_DRIVE_FREESPACE ; get free space available
INT 21H
MUL CX ; Compute size of cluster (secsiz*secperclus)
XCHG CX,AX ; move it to correct spot
MOV DX,OFFSET DG:BIOSName ; who to open
MOV AX,BIOSLenLow+2 ; get low part of size
MOV BX,BIOSLenHigh+2 ; get high size
CALL CHKLEN ; open and snoop size
JNZ ERR4 ; Must fit exact so MSDOS is in right place
MOV DX,OFFSET DG:DOSName ; other guy to open
MOV AX,DOSLenLow+2 ; get low part of size
MOV BX,DOSLenHigh+2 ; get high size
CALL CHKLEN ; open and snoop second size
JA ERR4 ; Must be enough (or too much) space
ENDIF
IF IBMVER OR IBMJAPVER
MOV DX,OFFSET DG:BIOSName ; open BIOS
MOV CX,7 ; attributes
MOV AH,Find_First
INT 21H
JNC FindDos
Err3J: JMP Err3 ; not found, go and complain
FindDos:
MOV DX,OFFSET DG:DOSName ; open DOS
MOV AH,Find_First
INT 21H
JC Err3J ; Not found, go complain
ENDIF
PUTSYS:
MOV DX,OFFSET DG:BIOSName ; who to change mode
MOV CX,0 ; undo attributes
MOV AX,(ChMod SHL 8) + 1 ; set the attributes
INT 21h
MOV DX,OFFSET DG:DOSName ; who to change mode
MOV CX,0 ; undo attributes
MOV AX,(ChMod SHL 8) + 1 ; set the attributes
INT 21h
MOV DX,OFFSET DG:BIOSName ; destination of BIOS
MOV CX,7 ; fancy attributes
MOV AH,Creat ; make a new one
INT 21h
MOV BIOSOutFH,AX ; save handle
MOV DX,OFFSET DG:DOSName ; destination of DOS
MOV AH,Creat ; make a new one
INT 21h
MOV DOSOutFH,AX ; save handle
Copy:
CALL DumpMem ; flush out memory
MOV AX,DOSLenHigh ; more DOS?
OR AX,DOSLenLow ; more low dos
OR AX,BIOSLenHigh ; more high BIOS
OR AX,BIOSLenLow ; more low BIOS
JZ AllDone ; nope, all done
CALL FillMem ; reload world
JMP Copy
ERR4:
MOV DX,OFFSET DG:BADSIZ
MOV CX,BadSizLen
JMP DisplayError
AllDone:
MOV CX,BIOSTime ; get time and date
MOV DX,BIOSTime+2
MOV BX,BIOSOutFH ; where to stuff the time
MOV AX,(File_Times SHL 8) + 1
INT 21h
MOV AH,Close
INT 21h
MOV CX,DOSTime ; get time and date
MOV DX,DOSTime+2
MOV BX,DOSOutFH ; where to stuff the time
MOV AX,(File_Times SHL 8) + 1
INT 21h
MOV AH,Close
INT 21h
IF IBMVER OR IBMJAPVER
CALL PUTBOOT ; copy the boot sector also
ENDIF
MOV DX,OFFSET DG:DONE ; all finished message
MOV CX,DoneLen
XOR AL,AL ; ok error code
SERROR:
PUSH AX
MOV BX,stderr
MOV AH,Write ; convenient place to display message
INT 21H
POP AX
ErrorExit:
MOV AH,EXIT ; bye and return error code
INT 21h
DisplayError:
MOV AL,1
JMP SERROR
FillMem:
MOV CX,cbBuf ; get length of buffer
MOV BX,BIOSInFH ; get bios source handle
MOV DX,OFFSET DG:BUF ; point to beginning of buffer
PUSH CX ; save away total length
CMP BIOSLenHigh,0 ; > 64K to read?
JA UseCX ; use CX
CMP BIOSLenLow,CX ; more left to read?
JA UseCX ; use CX
MOV CX,BIOSLenLow ; move new
UseCX:
MOV AH,Read
INT 21h ; read in what we can
ADD DX,AX ; update pointer for DOS Read
MOV pDOS,DX ; point to beginning of DOS
SUB BIOSLenLow,AX ; decrement remaining
SBB BIOSLenHigh,0 ; do 32 bit
POP CX ; get original length
SUB CX,AX ; this much is left
MOV BX,DOSInFH ; get bios source handle
CMP DOSLenHigh,0 ; > 64K to read?
JA UseCXDOS ; use CX
CMP DOSLenLow,CX ; more left to read?
JA UseCXDOS ; use CX
MOV CX,DOSLenLow ; move new
UseCXDOS:
MOV AH,Read
INT 21h ; read in what we can
ADD DX,AX ; update pointer for DOS Read
MOV pDOSEnd,DX ; point to End of dos DOS
SUB DOSLenLow,AX ; decrement remaining
SBB DOSLenHigh,0 ; do 32 bit arithmetic
return
OpenFile:
MOV AX,(OPEN SHL 8) + 0 ; open for reading only
INT 21H ; Look for BIOS
retc ; not found, go and try again
STOSW ; stash away handle
MOV BX,AX ; get ready for seeks
MOV AX,(LSeek SHL 8) + 2 ; seek relative to eof
XOR CX,CX ; zero offset
XOR DX,DX ; zero offset
INT 21h ; get offsets
STOSW ; save low part of size
STOSW ; save low part of size
MOV AX,DX
STOSW ; save high part of size
STOSW ; save high part of size
XOR DX,DX ; zero offset
MOV AX,(LSeek SHL 8) + 0 ; seek relative to beginning
INT 21h
MOV AX,(File_Times SHL 8) + 0
INT 21h ; get last write times
MOV AX,CX
STOSW ; save time
MOV AX,DX
STOSW ; save date
return
ERR3:
MOV DX,OFFSET DG:NODEST
MOV CX,NoDestLen
JMP DisplayError
DumpMem:
MOV DX,OFFSET DG:BUF ; get offset of bios start
MOV CX,pDOS ; beginning of next guy
SUB CX,DX ; difference is length
JZ DumpDos ; no bios to move
MOV BX,BIOSOutFH ; where to output
MOV AH,Write
INT 21h ; wham
DumpDos:
MOV DX,pDOS ; beginning of dos
MOV CX,pDOSEnd ; end of dos
SUB CX,DX ; difference is length
retz ; if zero no write
MOV BX,DOSOutFH ; where to output
MOV AH,Write
INT 21h ; wham
ret
IF MSVER
CHKLEN:
; CX has size of cluster, DX has pointer to file name
; Returns with flags set on (size of file) - (size of hole)
PUSH AX ; old size low
PUSH BX ; old size high
PUSH CX ; old cluster size
MOV AH,Find_First
MOV CX,7 ; attributes to search for
INT 21H
JC ERR3 ; cannot find file, error
POP CX ; get cluster size back
MOV DX,DS:[80h+find_buf_size_h] ; get destination size high
MOV AX,DS:[80h+find_buf_size_l] ; get size low
ADD AX,CX ; add cluster size
ADC DX,0 ; 32 bit add
SUB AX,1 ; adding CLUSSIZE-1
SBB DX,0 ; 32 bit dec
DIV CX ; compute new cluster size
POP DX ; get old high
POP BX ; get old low
PUSH AX ; save away dividend
MOV AX,BX ; put into correct register
ADD AX,CX ; do the same as above (+CLUSSIZE-1)/CLUSSIZE
ADC DX,0 ; 32 bit add
SUB AX,1 ; adding CLUSSIZE-1
SBB DX,0 ; 32 bit dec
DIV CX ; compute old cluster size
POP DX ; get new size
CMP AX,DX ; is old >= new?
return
ENDIF
IF IBMJAPVER
PUTBOOT:
CALL READ_LLIST ; Get the list sector and set new boot sector
MOV AL,DS:(BYTE PTR 5CH)
DEC AL ; A=0
MOV CX,1
XOR DX,DX
MOV BX,OFFSET DG:BOOT
INT 26H ; Write out new boot sector
POPF
CALL WRITE_LLIST ; Make and write out new list sector
RET
ENDIF
IF IBMVER
PUTBOOT:
MOV AH,GET_DPB
MOV DL,BYTE PTR DS:[5Ch] ; Target drive
INT 21H
ASSUME DS:NOTHING
MOV AL,[BX+16H] ; Media byte
PUSH CS
POP DS
ASSUME DS:DG
CMP AL,0FEH
JB RET1
TEST AL,1
JZ GOTBOOT
MOV BX,OFFSET DG:BOOT
MOV WORD PTR [BX+17],112 ; Set number of dir entries
MOV WORD PTR [BX+19],2*8*40 ; Set number of sectors
INC BYTE PTR [BX+21] ; Media = ff
INC WORD PTR [BX+26] ; Number of heads = 2
GOTBOOT:
MOV AL,BYTE PTR DS:[5Ch]
DEC AL
MOV BX,OFFSET DG:BOOT ; Boot sector
XOR DX,DX ; Sector 0
MOV CX,DX
INC CX ; One sector
INT 26H ; Write out 8 sector boot sector
POP AX ; Flags
RET1: RET
ENDIF
IF IBMJAPVER
READ_BOOT:
MOV AL,[DEFALT]
DEC AL ; A=0
MOV CX,1
XOR DX,DX
MOV BX,OFFSET DG:BOOT
INT 25H
POPF
MOV AX,[BOOT+108H] ; Get old first sector of data
MOV [RELOC],AX
RET
READ_LLIST:
MOV AL,DS:(BYTE PTR 5CH)
DEC AL ; A=0
MOV CX,1
MOV DX,[STARTSECTOR]
MOV BX,OFFSET DG:LLISTBUF
INT 25H
POPF
RET
WRITE_LLIST:
MOV AX,[STARTSECTOR]
MOV DX,AX
SUB AX,[RELOC] ; True reloc factor
MOV CL,BYTE PTR [LLISTBUF+0CH] ; Number of entries needing reloc
XOR CH,CH
JCXZ NO_RELOCS
MOV BX,OFFSET DG:LLISTBUF + 10H
RELLOOP:
ADD WORD PTR [BX+2],AX
ADD BX,10H
LOOP RELLOOP
NO_RELOCS:
MOV AL,DS:(BYTE PTR 5CH)
DEC AL ; A=0
MOV CX,1
MOV BX,OFFSET DG:LLISTBUF
INT 26H
POPF
RET
CHECK_TRAN:
; All registers preserved. Returns zero if SYS OK, NZ if SYS FAIL
; AL is drive (1=A,...) AL=0 is not valid
PUSH BX
PUSH AX
PUSH DS
MOV DL,AL
MOV AH,GET_DPB
INT 21H
MOV AX,[BX.dpb_first_sector] ; Get new first sector of data
MOV BH,[BX.dpb_media]
POP DS
MOV [STARTSECTOR],AX
MOV [BOOT+108H],AX ; Set new start of data in boot
POP AX
PUSH AX
MOV BL,AL
INT 11H ; IBM EQUIP CALL
ROL AL,1
ROL AL,1
AND AL,3
JNZ NOT_SINGLE
INC AL
NOT_SINGLE:
INC AL ; AL is now MAX floppy #
CMP BL,AL
POP AX
JBE CHECK_FLOP ; Is a floppy
XOR BL,BL ; Is Hard file
POP BX
RET
CHECK_FLOP:
CMP BH,0FBH ; Only floppy that boots
POP BX
RET
ENDIF
GetKeystroke:
MOV AX,(Std_CON_Input_Flush SHL 8) + Std_CON_Input_No_Echo
INT 21H
MOV AX,(Std_CON_Input_Flush SHL 8) + 0
INT 21H
return
CODE ENDS
END START