	title	fdisk
;++
;
; FDISK equivalent, uses command line instead of stupid menus.
;
; By John Wilson <wilson@dbit.com>, Troy NY USA.
;
; This program may be freely distributed provided that proper credit is given
; to the author, including the message displayed when the program is run.
;
; 02/04/93	JMBW	Created.
; 05/28/94	JMBW	Sorts partition table by starting sector address.
; 02/11/97	JMBW	Create/delete partitions (finally!).
; 03/15/97	JMBW	MBR bootstrap prompts if no partitions are bootable.
; 03/21/98	JMBW	Clear entire MBR (to restore a disk to virgin state).
; 05/08/99	JMBW	CHANGE command.
; 10/26/99	JMBW	Fixed stupid bug in CNUM.
;
;--
	.radix	8
;
ptab=	01BEh	;start of partition table in master boot record
pmax=	4	;max # partitions
;
tab=	11
lf=	12
cr=	15
;
; Partition table entries look like this (at offset PTAB in MBR):
;
;	dw	dx,cx		;start (INT 13h form), DL=80h (Active) or 0
;	dw	dx,cx		;end (" " "), DL=system code
;	dd	psn		;physical sector # of start
;	dd	len		;# sectors in partition
;
; (length of entry=10h bytes)
;
kw	macro	text,addr,help	;;define keyword for TBLUK
kh=	0
ki=	0
	irpc	kc,text
ki=	ki+1
ifidn <kc>,<->
kh=	ki
endif
	endm ;; irpc
ife kh
	.err
	%out	No hyphen in string:  &text
	exitm
endif ;; ife kh
	db	kh-1,ki-1
	irpc	kc,text
ifdif <kc>,<->
	db	'&kc'
endif
	endm ;; irpc
	dw	&addr
ifnb <&help>
	dw	&help
else
	dw	0
endif
	endm
;
code	segment
	assume	cs:code
	org	100h
start:	cld			;DF=0
	; choose whichever buffer doesn't span a 64 KB boundary
	mov	bx,ds		;get DS
	mov	cl,4		;shift count
	sal	bx,cl		;low 16 bits of abs base of segment
	mov	ax,offset buf1	;try BUF1 first
	mov	dx,offset buf2	;(point at other buf)
	add	bx,ax
	add	bx,512d-1	;see if it wraps
	jnc	nowrap
	 xchg	ax,dx		;it does, so switch places
nowrap:	mov	ds:buf,ax	;save addr of buf that doesn't span 64K
	mov	ds:othbuf,dx	;save other buf to (for LDPTR)
	; fetch command line if non-blank
	mov	si,80h		;pt at JCL
	lodsb			;get length
	cbw			;AH=0
	mov	cx,ax		;copy
	jcxz	jcl4		;nothing, skip
jcl1:	; skim off leading white space
	lodsb			;get a char
	cmp	al,' '		;blank or ctrl char?
	ja	jcl3		;no, copy into buf
jcl2:	loop	jcl1		;do all chars
	jmp	short jcl4	;never found any non-blank char
jcl3:	dec	si		;unget char
	mov	di,offset kbbuf+2 ;point at buffer
	mov	ds:cmdlen,cx	;save length
	rep	movsb		;copy
jcl4:	; trap ^Cs
	mov	dx,offset mloop	;pt at ^C vector
	mov	ax,2523h	;func=set INT 23h vector
	int	21h
	; say hello
	test	ds:cmdlen,-1	;cmd line given?
	jnz	jcl5		;yes, shut up
	mov	dx,offset hello	;pt at msg
	mov	ah,09h		;func=print
	int	21h
jcl5:	; try to init
	mov	dx,80h		;default drive #, head=0
	mov	cx,1		;cyl=0, sec=1
	call	rhome		;read home block
	jc	mloop		;failed
	call	sort		;make sure it's sorted
	call	ldptr		;load up PPTRS
	mov	byte ptr ds:extprt,0 ;not extended partition
	test	ds:cmdlen,-1	;cmd line arg given?
	jnz	mloop		;yes, shut up
	call	list		;list partition table
;+
;
; Main loop.
;
;-
mloop:	cld			;in case we got here from ^C
	mov	ax,cs		;load CS into DS and ES
	mov	ds,ax
	mov	es,ax
	cli			;ints off
	mov	ss,ax		;;set SS=CS too
	mov	sp,offset pdl	;;reinit stack
	sti			;;ints back on
	mov	dx,offset prompt ;pt at prompt
	mov	cx,ds:cmdlen	;get length of JCL
	test	cx,cx		;anything?
	jnz	mlp2		;yes
	call	getwp		;get a word, prompt first
mlp1:	mov	ax,offset cmds	;pt at cmd table
	call	tbluk		;look up cmd
	jc	what
	call	ax		;process cmd
	jmp	short mloop	;loop
mlp2:	js	mlp3		;already handled first cmd, 2nd is quit
	mov	ds:cmdlen,-1	;first prompt, quit next time
	mov	si,offset kbbuf+2 ;point at saved JCL
	call	getw		;parse off cmd (always succeeds)
	jmp	short mlp1	;go handle it
mlp3:	jmp	quit
what:	; don't like keyword at DS:BX (length=DX)
	mov	di,offset obuf	;pt at buffer
	mov	al,'?'		;?
	stosb
	mov	si,bx		;point at keyword
	mov	cx,dx		;length
	rep	movsb		;copy
	stosb			;another ?
	call	pline		;flush
	jmp	short mloop	;loop
;
cmds	label	byte
	kw	<?->,help	;syn. for HELP
	kw	<A-DD>,create	;syn. for CREATE
	kw	<BA-CKUP>,backup,bkphlp
	kw	<BO-OT>,boot,bthelp
	kw	<CH-ANGE>,change,chghlp
	kw	<CLE-AR>,clear,clrhlp
	kw	<CLS->,cls,clshlp
	kw	<CR-EATE>,create,crthlp
	kw	<DELETE->,delete,delhlp
	kw	<H-ELP>,help
	kw	<L-IST>,list,lsthlp
	kw	<MBR->,wmbr,mbrhlp
	kw	<O-RDER>,order,ordhlp
	kw	<Q-UIT>,quit,qhelp
	kw	<REA-D>,read,rdhelp
	kw	<RES-TORE>,restor,rsthlp
	kw	<WRITE->,write,wrhelp
	db	0
;
quit:	call	chkdrt		;see if buf dirty
	int	20h
;+
;
; Back up the boot record to a DOS file (presumably on a floppy).
;
;-
backup:	push	si		;save
	call	getbuf		;make sure MBR is loaded
	pop	si		;restore
	mov	dx,offset bkpprm ;prompt
	call	getwp		;get filename
	xchg	dx,bx		;get ptr into DX
	add	bx,dx		;pt at end
	mov	byte ptr [bx],0	;mark end
	xor	cx,cx		;mode=0
	mov	ah,3Ch		;func=create
	int	21h
	jc	bkup1
	mov	dx,ds:buf	;pt at buf
	mov	cx,512d		;length
	mov	bx,ax		;handle
	mov	ah,40h		;func=write
	int	21h
	jc	bkup2		;write error
	mov	ah,3Eh		;func=close
	int	21h
	ret
bkup1:	mov	dx,offset crterr ;creation error
	jmp	short bkup3
bkup2:	mov	ah,3Eh		;func=close
	int	21h
	mov	dx,offset werr	;write error
bkup3:	mov	ah,09h		;func=print
	int	21h
	ret
;+
;
; BOOT n
;
; Set bootable partition (0=none).
;
;-
boot:	mov	dx,offset prtprm ;prompt
	call	getwp		;get partition #
	call	getn		;interpret as #
	jc	boot5
	cmp	al,pmax		;must be in [0,PMAX)
	ja	boot4		;error
	cbw			;AH=0
	dec	ax		;-1 (0:N-1, or -1 to clear boot flag)
	push	ax		;save
	call	getbuf		;get buf ptr
	pop	ax		;restore
	add	ax,ax		;*2
	js	boot1		;-1, skip
	mov	bx,ax		;copy
	mov	bx,ds:pptrs[bx]	;fetch ptr
	test	bx,bx		;exists?
	jz	boot4		;no, error
boot1:	; mark all partitions as non-bootable
	add	si,ptab		;pt at part table
	mov	cx,pmax		;# partition slots
boot2:	mov	byte ptr [si],0	;mark as inactive
	add	si,10h		;skip to next
	loop	boot2		;do them all
	test	ax,ax		;were we just clearing them all?
	js	boot3		;yes
	 mov	byte ptr [bx],80h ;mark as active
boot3:	mov	byte ptr ds:dirty,1 ;buf is dirty
	ret
boot4:	mov	dx,offset invprt ;invalid partition #
	mov	ah,09h		;func=print
	int	21h
	ret
boot5:	jmp	what
;+
;
; Change type of a partition.
;
; CHANGE n type
; N	partition number
; TYPE	type to change it to
;
;-
change:	push	si		;save
	call	getbuf		;make sure buf exists
	pop	si		;restore
	mov	dx,offset prtprm ;prompt
	call	getwp		;get partition #
	call	getn		;interpret as #
	jc	chg2
	test	al,al		;must be in [1,PMAX)
	jz	chg1		;error
	cmp	al,pmax
	ja	chg1
	cbw			;AH=0
	dec	ax		;-1 (0:N-1)
	push	ax		;save
	call	gettyp		;get partition type into AL
	push	ax		;save
	call	getbuf		;get buf ptr
	pop	ax		;restore type
	pop	bx		;rsetore part
	add	bx,bx		;*2
	mov	bx,ds:pptrs[bx]	;fetch ptr
	test	bx,bx		;exists?
	jz	chg1		;no, error
	test	byte ptr [bx+4],-1 ;defined?
	jz	chg1		;no, error
	mov	[bx+4],al	;save
	mov	byte ptr ds:dirty,1 ;buf is dirty
	ret
chg1:	mov	dx,offset invprt ;invalid partition #
	mov	ah,09h		;func=print
	int	21h
	ret
chg2:	jmp	what
;+
;
; Clear out entire MBR.
;
;-
clear:	mov	dx,offset clrcfm ;pt at string
	call	yesno		;see if they care
	jc	clr1		;default is no
	jnz	clr1		;they said no
	call	getbuf		;get buf addr
	mov	di,si		;point at it
	xor	ax,ax		;load 0
	mov	cx,512d/2	;init to all zeros
	rep	stosw
	mov	byte ptr ds:dirty,1 ;buf has changed
clr1:	ret
;+
;
; Clear screen.
;
;-
cls:	mov	ah,0Fh		;func=get video mode
	int	10h
	xor	ah,ah		;func=set video mode (to # returned)
	int	10h
	ret
;+
;
; CREATE type C[/H[/S]](start) C[/H[/S]](end)
; CREATE type [C[/H[/S]](start)] xxxMB
; CREATE type [C[/H[/S]](start)] xxx%
;
;-
create:	push	si		;save
	call	getbuf		;make sure buf exists
	pop	si		;restore
	call	gettyp		;get partition type into AL
	; AL=partition type
	push	ax		;save
	; search for first empty area to use as default
	mov	ax,ds:nsecs	;get abs sec addr of 2nd track of disk
	xor	dx,dx		;(skip track 0, reserved for MBR + viruses!)
	test	byte ptr ds:extprt,-1 ;in extended partition?
	jz	crt1		;no
	mov	ax,word ptr ds:sec ;get cyl+sec of current part table
	mov	dh,ds:head	;head
	call	unpchs		;unpack
	call	chsabs		;convert to abs sec #
	add	ax,ds:nsecs	;skip a track (as above)
	adc	dx,0
crt1:	call	fsize		;see what's available at begn of disk
	jnc	crt5		;well it's something
	mov	di,offset pptrs	;point at part list
crt2:	mov	bx,[di]		;get next part
	test	bx,bx		;exists?
	jz	crt3		;no
	mov	ax,[bx+6]	;get ending cyl+sec
	mov	dh,[bx+5]	;ending head
	call	unpchs		;unpack
	call	chsabs		;convert to abs sector #
	add	ax,1		;+1 to pt at area following partition
	adc	dx,0
	push	di		;save
	call	fsize		;anything?
	pop	di		;[restore]
	jnc	crt5		;something, OK
crt3:	inc	di		;+2
	inc	di
	cmp	di,offset pptrs+(pmax*2) ;done all?
	jb	crt2		;loop if not
crt4:	pop	ax		;flush stack
	mov	dx,offset nofree ;no free space
	mov	ah,09h		;func=print
	int	21h
	ret
crt5:	; there's at least 1 free area available, FREE1/FREE2 describe the 1st
	mov	dx,offset prtbeg ;prompt
	call	getwp		;get beginning of partition
	mov	ds:dhead,0	;default starting head=0
	mov	ds:dsec,1	;default starting sector=1 (begn of cyl)
	call	getchs		;try to parse as C/H/S
	jc	crt8		;failed, try MB or percentage w/default start
	call	chsabs		;convert to abs sector #
	call	fsize		;get size of free area at that addr
	jc	crt4		;nothing
	mov	dx,offset prtend ;prompt
	call	getwp		;get end of partition
	mov	ax,ds:nheads	;get # heads
	dec	ax		;# of last
	mov	ds:dhead,ax	;default ending head=last
	mov	ax,ds:nsecs	;get # secs
	mov	ds:dsec,ax	;default ending sec=last
	call	getchs		;try to parse as C/H/S
	jc	crt8		;failed, try it as MB or percentage w/our start
	call	chsabs		;convert to abs sector #
	add	ax,1		;pt at following sector
	adc	dx,0
	cmp	dx,ds:free2+2	;off end of free area?
	jb	crt6		;no, OK
	ja	crt4		;yes, bad
	cmp	ax,ds:free2	;check low word
	ja	crt4		;no good
crt6:	jmp	crt14
crt7:	jmp	what		;complain, flush stack
crt8:	; # MB or percentage
	call	getmbp		;try MB or percentage
	jc	crt7		;failed
	test	di,di		;MB?
	jnz	crt9		;no, percentage
	; # MB
	mov	dx,2048d	;# 512-byte sectors per megabyte
	mul	dx		;find # sectors
	jmp	short crt10
crt9:	; percentage
	mov	bx,ax		;copy
	mov	ax,ds:free2	;get ptr past end of free block
	mov	dx,ds:free2+2
	sub	ax,ds:free1	;find size
	sbb	dx,ds:free1+2
	mov	di,ax		;save low order
	mov	ax,dx		;get high order
	mul	bx		;find high order of size*BX
	mov	cx,dx		;save high result
	xchg	di,ax		;save low result, get low order
	mul	bx		;low order of size*BX
	add	dx,di		;sum up
	adc	cx,0		;CX:DX:AX is size*percentage
	mov	di,100d		;value to divide by
	mov	bx,ax		;save high order result
	mov	ax,dx		;copy middle order
	mov	dx,cx		;and high order
	div	di		;/100d
	xchg	ax,bx		;save result, get low order
	div	di		;/100d
	mov	dx,bx		;done, DX:AX=result
crt10:	; DX:AX=length, check to see if larger than free area
	add	ax,ds:free1	;add base
	adc	dx,ds:free1+2
	cmp	dx,ds:free2+2	;too big?
	ja	crt11		;yes
	jb	crt12		;no
	cmp	ax,ds:free2	;check low word
	jbe	crt12
crt11:	jmp	crt4
crt12:	; round to end of cylinder if possible
	sub	ax,1		;-1
	sbb	dx,0
	call	abschs		;convert to C/H/S
	inc	ax		;bump to next cyl
	xor	bx,bx		;head=0
	mov	dx,1		;sec=1
	call	chsabs		;back to abs sector
	cmp	dx,ds:free2+2	;too big now?
	ja	crt13		;yes
	jb	crt14		;no
	cmp	ax,ds:free2	;check low word
	jbe	crt14
crt13:	mov	ax,ds:free2	;stop where they say
	mov	dx,ds:free2+2
crt14:	; OK, DX:AX=ending abs sec addr +1 (checked)
	mov	ds:free2,ax	;save
	mov	ds:free2+2,dx
	call	issort		;see if already sorted
	pop	cx		;[catch partition type]
	sbb	ch,ch		;CH NZ if not sorted
	mov	si,ds:buf	;pt at buf
	add	si,ptab		;point at partition table
crt15:	test	byte ptr [si+4],-1 ;anything here?
	jnz	crt17		;yes
	; found a free slot, save our stuff
	mov	ax,ds:free1	;get addr of start
	mov	dx,ds:free1+2
	mov	[si+08h],ax	;save
	mov	[si+0Ah],dx
	call	abschs		;convert to C/H/S
	call	pckchs		;in BIOS format
	xor	dl,dl		;not bootable
	mov	[si],dx		;save
	mov	[si+2],ax
	mov	ax,ds:free2	;get addr of end +1
	mov	dx,ds:free2+2
	sub	ax,1		;-1
	sbb	dx,0
	call	abschs		;convert to C/H/S
	call	pckchs		;in BIOS format
	mov	dl,cl		;copy partition type
	mov	[si+4],dx	;save
	mov	[si+6],ax
	mov	ax,ds:free2	;get addr of end +1 again
	mov	dx,ds:free2+2
	sub	ax,ds:free1	;find length
	sbb	dx,ds:free1+2
	mov	[si+0Ch],ax	;save
	mov	[si+0Eh],dx
	mov	byte ptr ds:dirty,1 ;buffer is dirty
	test	ch,ch		;was it sorted?
	jnz	crt16		;no
	mov	bx,ds:buf	;pt at it
	call	dosort
crt16:	jmp	ldptr		;reload pointers, return
crt17:	add	si,10h		;bump to next slot
	lea	ax,[si+(-(ptab+(pmax*10h)))] ;would be here if done all
	cmp	ax,ds:buf	;done all?
	jne	crt15		;loop if not
	mov	dx,offset ptfull ;partition table full
	mov	ah,09h		;func=print
	int	21h
	ret
;+
;
; Find size (in sectors) of the free block starting at the specified addr.
;
; dx:ax	starting abs sector #
;
; dx:ax	returns size in sectors, or CF=1 if no free block @ that addr
; si,cx	preserved
; DS:FREE1 is dword abs sec addr of beginning of free area
; DS:FREE2 is dword abs sec addr of sector after end of free area
;
;-
fsize:	mov	ds:free1,ax	;save
	mov	ds:free1+2,dx
	mov	di,offset pptrs	;point at pointers
frsz1:	push	di		;save
	mov	di,[di]		;fetch ptr to next slot in cyl order
	test	di,di		;empty slot?
	jz	frsz5		;yes, skip it
	mov	ax,[di+2]	;get starting cyl+sec
	mov	dh,[di+1]	;starting head
	call	unpchs		;unpack
	call	chsabs		;convert to abs sector #
	cmp	ds:free1+2,dx	;does our area come before this one?
	jb	frsz2		;yes
	ja	frsz3		;no
	cmp	ds:free1,ax	;check low order
	jae	frsz3
frsz2:	; our area starts before this one
	pop	di		;flush stack
	jmp	short frsz6	;go compute size and return
frsz3:	; our area starts after begn of this area, check for overlap
	mov	ax,[di+6]	;get ending cyl+sec
	mov	dh,[di+5]	;ending head
	call	unpchs		;unpack
	call	chsabs		;convert to abs sector #
	cmp	ds:free1+2,dx	;does our area start before this one ends?
	jb	frsz4		;yes
	ja	frsz5		;no
	cmp	ds:free1,ax	;check low order
	ja	frsz5
frsz4:	pop	di		;flush stack
	stc			;error
	ret
frsz5:	pop	di		;restore
	inc	di		;+2
	inc	di
	cmp	di,offset pptrs+(pmax*2) ;done all?
	jb	frsz1		;loop if not
	; we're after all existing partitions, stop at end of disk or ext part
	mov	ax,ds:dsize	;fetch dev size
	mov	dx,ds:dsize+2
	test	byte ptr ds:extprt,-1 ;in extended patition?
	jz	frsz6		;no
	mov	ax,ds:ecyl2	;fetch C/H/S of last sector of ext part
	mov	bx,ds:ehead2
	mov	dx,ds:esec2
	call	chsabs		;convert to abs sec addr
	add	ax,1		;+1 to point at sec after end
	adc	dx,0
frsz6:	mov	ds:free2,ax	;save
	mov	ds:free2+2,dx
	sub	ax,ds:free1	;find size
	sbb	dx,ds:free1+2
	jnz	frz7		;skip, CF=0
	test	ax,ax		;anything?
	jnz	frz7
	stc			;size=0, failed
frz7:	ret
;
ptype	label	byte
	kw	<C-PM86>,52h	;CP/M-86
	kw	<DOSE-XTENDED>,05h ;DOS 3.3 extended partition
	kw	<DOSFAT12->,01h	;DOS 2.X FAT12
	kw	<DOSFAT16->,04h	;DOS 3.X FAT16
	kw	<DO-SLARGE>,06h	;DOS 4.X large partition
	kw	<L-INUX>,83h	;Linux ext2 file system
	kw	<LINUXS-WAP>,82h ;Linux swap area
	db	0
;+
;
; Parse partition type.
;
; si,cx	line ptr/ctr, updated on return
; al	returns type
;
;-
gettyp:	mov	dx,offset prttyp ;prompt
	call	getwp		;get type
	mov	ax,offset ptype	;pt at table
	call	tbluk		;in table?
	jnc	gtyp1		;yes
	call	geth		;try it as a hex #
	jnc	gtyp1		;OK
	jmp	what
gtyp1:	ret
;+
;
; Parse a C/H/S string.
;
; bx,dx	addr,len of string to parse (preserved iff CF=1)
; si,cx	preserved
; ax	returns cyl
; bx	returns head
; dx	returns sector
;
; If omitted, H defaults to DS:DHEAD and S to DS:DSEC.
;
; CF=1 if invalid format, BX/DX preserved in this case.
;
;-
getchs:	; parse cyl
	push	bx		;save in case we don't like it
	push	dx
	call	getn		;convert to binary
	jnc	gchs3		;got it, use defaults for head/sec
	cmp	di,dx		;all invalid?
	je	gchs9		;yes, error
	xchg	di,dx		;swap total length, unused length
	sub	di,dx		;find # used
	add	bx,di		;advance ptr
	cmp	byte ptr [bx],'/' ;slash?
	je	gchs1
	cmp	byte ptr [bx],':' ;colon is OK too
	jne	gchs9		;no, invalid
gchs1:	; parse head
	inc	bx		;eat separator
	dec	dx
	jz	gchs3		;nothing left, use defaults
	push	ax		;save cyl
	call	getn		;parse head
	jnc	gchs4		;got it, use default for sec
	cmp	di,dx		;all invalid?
	je	gchs8		;yes, error
	xchg	di,dx		;swap total length, unused length
	sub	di,dx		;find # used
	add	bx,di		;advance ptr
	cmp	byte ptr [bx],'/' ;slash?
	je	gchs2
	cmp	byte ptr [bx],':' ;colon is OK too
	jne	gchs8		;no, invalid
gchs2:	inc	bx		;eat separator
	dec	dx
	jz	gchs4		;nothing left, use defaults
	push	ax		;save head
	call	getn		;parse sector
	jc	gchs7		;error
	mov	dx,ax		;copy
	pop	bx		;catch head
	pop	ax		;cyl
	jmp	short gchs6
gchs3:	mov	bx,ds:dhead	;default head
	jmp	short gchs5	;go get default sector
gchs4:	mov	bx,ax		;copy head
	pop	ax		;restore cyl
gchs5:	mov	dx,ds:dsec	;default sec
gchs6:	; AX=cyl, BX=head, DX=sec
	add	sp,4		;flush stack (CF=0)
	ret
gchs7:	pop	ax		;flush stack
gchs8:	pop	ax
gchs9:	pop	dx		;restore BX:DX
	pop	bx
	stc			;invalid as C/H/S
	ret
;+
;
; Parse a size string, "nnnMB" or "nnn%".
;
; bx,dx	addr,len of string to parse (preserved)
; si,cx	preserved
; ax	returns # MB or percentage
; di	0 for MB, NZ for percentage
;
; CF=1 if invalid format.
;
;-
getmbp:	call	getn		;parse as number
	jnc	gmbp2		;shouldn't happen
	cmp	di,dx		;all invalid?
	je	gmbp2		;yes, no good
	push	ax		;save
	mov	ax,bx		;copy
	add	ax,dx		;bump to end
	sub	ax,di		;back to first non-digit
	xchg	ax,di		;swap
	cmp	ax,2		;2 non-digit chars?
	pop	ax		;[restore]
	jne	gmbp1		;no
	cmp	word ptr [di],"BM" ;MB?
	jne	gmbp2
	xor	di,di		;say MB, CF=0
	ret
gmbp1:	ja	gmbp2		;should be .LT.
	cmp	byte ptr [di],'%' ;%?
	jne	gmbp2
	cmp	ax,100d		;should be .LE. 100.
	ja	gmbp2		;no
	test	di,di		;known NZ, CF=0
	ret
gmbp2:	stc			;no good
	ret
;+
;
; DELETE n
;
; Delete a partition.
;
;-
delete:	mov	dx,offset prtprm ;prompt
	call	getwp		;get partition #
	call	getn		;interpret as #
	jc	del4
	dec	al		;-1
	cmp	al,pmax		;in range?
	jae	del3		;no
	cbw			;AH=0
	push	ax		;save
	call	getbuf		;make sure buf exists
	pop	bx		;restore
	add	bx,bx		;*2
	mov	bx,ds:pptrs[bx]	;look up partition
	test	bx,bx		;exists?
	jz	del3		;no
	push	bx		;save
	call	issort		;BTW, is it sorted?
	sbb	ax,ax		;AX NZ if not
	push	ax
	mov	dx,offset delcfm ;make sure they're sure
	call	yesno
	pop	ax		;[restore]
	pop	bx
	jc	del2		;no
	jnz	del2		;no
	mov	byte ptr [bx+4],0 ;shoot it out
	mov	byte ptr ds:dirty,1 ;buf is dirty
	test	ax,ax		;was it sorted before?
	jnz	del1		;no, leave it unsorted
	mov	bx,ds:buf	;point at block
	call	dosort		;sort it
del1:	jmp	ldptr		;recompute part tab pointers, return
del2:	ret
del3:	mov	dx,offset invprt ;invalid partition #
	mov	ah,09h		;func=print
	int	21h
	ret
del4:	jmp	what
;+
;
; Print help text.
;
;-
help:	call	getw		;get a word
	jc	help3		;missing, give a list
	mov	ax,offset cmds	;pt at main cmd table
	call	tbluk		;look up cmd
	jc	help1		;invalid
	test	di,di		;got anything?
	jz	help2		;no
	call	pcrlf		;print blank line
	mov	dx,di		;copy ptr
	mov	ah,09h		;func=print
	int	21h
	jmp	pcrlf		;another blank line, return
help1:	jmp	what
help2:	; the keyword they gave has a null help string, say no help available
	mov	di,offset obuf	;pt at buffer
	mov	si,offset nohelp ;pt at msg
	mov	cx,nohlpl	;length
	rep	movsb
	mov	si,bx		;pt at keyword
	mov	cx,dx		;length
	rep	movsb
	mov	al,'"'		;closing quote
	stosb
	jmp	pline		;flush, return
help3:	; they just typed "HELP", give a list
	mov	dx,offset hlphdg ;heading
	mov	ah,09h		;func=print
	int	21h
	mov	di,offset obuf	;pt at buffer
	mov	si,offset cmds	;pt at cmd list
help4:	; get next keyword
	lodsw			;get length,,length to match
	test	al,al		;end?
	jz	help8
	mov	al,ah		;copy length
	cbw			;AH=0
	mov	bx,ax		;copy
	mov	cx,ax		;twice
	test	word ptr [si+bx+2],-1 ;see if HELP ptr is 0
	jz	help7		;yes, skip this entry
	mov	ax,di		;get curr line ptr
	sub	ax,offset obuf	;find current column
	jz	help6		;at BOL, leave it alone
	mov	dx,ax		;save a copy
	add	al,16d		;bump to next multiple of 16
	and	al,not 15d
	add	al,bl		;find where we'll be afterwards
	cmp	al,80d		;at or off right marg?
	jb	help5		;no
	push	si		;save
	push	cx
	call	pline		;flush, reset DI
	pop	cx		;restore
	pop	si
	jmp	short help6
help5:	sub	al,bl		;find where DI needs to go again
	sub	al,dl		;find # blanks we need
	push	cx		;save
	mov	cx,ax		;load count
	mov	al,' '		;blank
	rep	stosb
	pop	cx		;restore
help6:	rep	movsb		;copy
help7:	add	si,cx		;skip keyword (or not, CX=0 if dropped through)
	add	si,4		;skip dispatch addr, help addr
	jmp	short help4	;do next
help8:	call	pline		;flush final line
	jmp	pline		;blank line, return
;+
;
; List current contents of partition table.
;
;-
list:	call	getbuf		;get buf addr (make sure it exists)
	mov	al,ds:drive	;get drive #
	add	al,'0'-80h	;convert to ASCII digit
	mov	ds:drvmsg,al	;save
	cmp	word ptr [si+1FEh],0AA55h ;valid?
	jne	list2		;no, say nothing there
	test	ds:pptrs,-1	;do we have even one partition?
	jz	list3		;no
	mov	dx,offset lsthdg ;list heading
	mov	ah,09h		;func=print
	int	21h
	test	byte ptr ds:extprt,-1 ;extended partition?
	jz	list1		;no
	mov	dx,offset extmsg ;different msg
	mov	ah,09h		;func=print
	int	21h
	mov	di,offset obuf	;pt at buf
	push	di		;save
	mov	al,ds:extprt	;get partition #
	xor	ah,ah		;0-extend
	cwd
	call	cnum		;convert
	mov	byte ptr [di],'$' ;mark end
	pop	dx		;point at it
	mov	ah,09h		;func=print
	int	21h
list1:	mov	dx,offset clsbrk ;closing bracket, <CRLF>
	mov	ah,09h		;func=print
	int	21h
	jmp	short list5
list2:	; no valid signature
	mov	dx,offset nosig	;say so
	jmp	short list4
list3:	; no partitions defined
	mov	dx,offset nopart ;say so
list4:	mov	ah,09h		;func=print
	int	21h
	call	pcrlf		;blank line
	jmp	short list10
list5:	mov	si,offset pptrs	;point at list of ptrs to part table entries
	mov	al,'1'		;partition #
	mov	di,offset obuf	;init ptr to buffer (PLINE restores)
list6:	mov	bx,[si]		;get an entry
	test	bx,bx		;exists?
	jz	list7
	 jmp	list12		;yes
list7:	inc	si		;+2
	inc	si
	cmp	si,offset pptrs+(pmax*2) ;done all possible slots?
	jb	list6		;loop if not
	call	pcrlf		;blank line
	mov	dx,offset ordhdg ;pt at string
	mov	ah,09h		;func=print
	int	21h
	mov	bx,offset obuf	;pt at output
	mov	ax,ds:buf	;pt at MBR
	add	ax,ptab		;index to table
list8:	mov	di,offset pptrs	;point at pointer list
	mov	dx,di		;copy
	mov	cx,pmax		;# entries
	repne	scasw		;look for this table entry
	je	list9		;found something
	 mov	di,dx		;as if we never moved
list9:	sub	di,dx		;find slot # *2
	shr	di,1		;*1
	mov	dx,di		;copy
	add	dl,'0'		;convert to digit
	mov	[bx],dl		;save
	inc	bx		;advance ptr
	add	ax,10h		;bump to next part table slot
	cmp	bx,offset obuf+pmax ;done all?
	jb	list8		;loop
	mov	di,bx		;copy
	call	pline		;flush line
list10:	; display disk parms
	mov	dx,offset dskprm ;heading
	mov	ah,09h		;func=print
	int	21h
	mov	di,offset obuf	;pt at buf
	mov	cx,ds:mcyse	;get max cyl/sec
	mov	dh,byte ptr ds:mhd ;and max head
	call	prchs		;convert
	sub	di,dx		;trim padding
	call	pline
	test	byte ptr ds:extprt,-1 ;in extended partition?
	jz	list11		;no
	mov	dx,offset extprm ;point at prompt
	mov	ah,09h		;func=print
	int	21h
	mov	di,offset obuf	;pt at buf
	mov	cx,ds:ecyse1	;get starting cyl/sec
	mov	dh,byte ptr ds:ehead1 ;and head
	call	prchs		;convert
	sub	di,dx		;trim padding
	mov	ax,"t "		;" t"
	stosw
	mov	ax," o"		;"o "
	stosw
	mov	cx,ds:ecyse2	;get ending cyl/sec
	mov	dh,byte ptr ds:ehead2 ;and head
	call	prchs		;convert
	sub	di,dx		;trim padding
	jmp	pline		;print, return
list11:	ret
list12:	; display contents of this partition (AL=#, BX=ptr, SI=list ptr)
	stosb			;save partition #
	push	ax		;save
	push	si
	mov	si,offset bttext ;point at " BOOT  "
	test	byte ptr [bx],-1 ;bootable?
	jnz	list13		;yes
	 mov	si,offset spaces ;spaces instead
list13:	mov	cx,7		;# chars
	rep	movsb		;copy
	; starting cylinder
	mov	si,bx		;copy
	mov	dh,[si+1]	;get starting head
	mov	cx,[si+2]	;cyl/sec
	call	prchs		;print cyl/head/sec
	; ending cylinder
	mov	dh,[si+5]	;get ending head
	mov	cx,[si+6]	;cyl/sec
	call	prchs		;print cyl/head/sec
	; size in MB
	mov	ax,[si+0Ch]	;get size in 512-byte sectors
	mov	dx,[si+0Eh]
	mov	cl,11d		;shift count to get MB
	shr	ax,cl		;make space
	ror	dx,cl
	mov	bx,dx		;copy
	and	bl,not 37	;isolate high 11
	xor	dx,bx		;and low 5
	or	ax,bx		;OR into low total
	push	di		;save starting posn
	call	cnum		;display # MB
	mov	al,' '		;space
	stosb
	mov	ax,"BM"		;MB
	stosw
	pop	cx		;catch starting position
	add	cx,7+3+1	;allow for 21-bit #, " MB", one blank
	sub	cx,di		;find # blanks needed to pad
	mov	al,' '		;load one
	rep	stosb		;save
	mov	bx,si		;restore
	; look up type
	mov	dh,[bx+4]	;get system type
	; convert to hex in case unknown code
	mov	al,dh		;copy
	xor	ah,ah		;AH=0
	mov	dl,10h		;divisor
	div	dl		;split digits (AL=high, AH=low)
	add	ax,"00"		;start converting both to hex
	cmp	al,'0'+10d	;>9?
	jb	list14
	 add	al,'A'-('9'+1)	;convert to A-F
list14:	cmp	ah,'0'+10d	;same for low digit
	jb	list15
	 add	ah,'A'-('9'+1)	;convert
list15:	mov	ds:unktyp,ax	;save
	mov	si,offset systab ;point at table
	xor	ah,ah		;AH=0
list16:	lodsb			;get one
	test	al,al		;default msg if end of table
	jz	list17
	cmp	al,dh		;is this it?
	je	list17
	lodsb			;no, get length
	add	si,ax		;skip
	jmp	short list16	;loop
list17:	lodsb			;get length
	mov	cx,ax
	rep	movsb		;copy
	call	pline		;flush
	pop	si		;restore list ptr
	pop	ax		;restore part #
	inc	ax		;+1
	jmp	list7		;do next
;+
;
; Print cyl/head/sector.
;
; es:di	buf ptr (updated on return)
; ch	low 8 bytes of cyl
; cl	high 2 bits of cyl (in bits 7:6), sector (in bits 5:0)
; dh	head
; si	preserved
; dx	returns # blanks added for padding (SUB DI,DX to remove)
;
;-
prchs:	push	di		;save
	push	si
	push	cx
	push	dx
	mov	al,ch		;get low 8 bits of cyl
	mov	ah,cl		;get high 2
	rol	ah,1		;into place
	rol	ah,1
	and	ah,3		;isolate
	xor	dx,dx		;0-extend
	call	cnum		;show starting cyl
	mov	al,'/'		;/
	stosb
	pop	ax		;catch head
	mov	al,ah		;copy
	xor	ah,ah		;0-extend
	cwd
	call	cnum		;show starting head
	mov	al,'/'		;/
	stosb
	pop	ax		;catch sector
	and	ax,77		;trim to 6 bits
	cwd			;0-extend
	call	cnum
	pop	si		;restore
	pop	cx		;catch starting position
	add	cx,12d		;allow for 1023/255/63 plus one blank
	sub	cx,di		;find # blanks needed to pad
	mov	dx,cx		;save in case they don't want them
	mov	al,' '		;load one
	rep	stosb		;save
	ret
;+
;
; Refresh master boot record, preserving existing partition table if any.
;
;-
wmbr:	call	getbuf		;make sure buf exists
	mov	di,si		;copy
	mov	cx,ptab		;# bytes to zero if no boot code
	cmp	word ptr ds:sec,1 ;cyl 0, sec 1?
	jne	wmbr1		;no
	cmp	byte ptr ds:head,0 ;head 0?
	jne	wmbr1		;no
	mov	si,offset mbr	;pt at code
	mov	cx,mbrlen
	rep	movsb
	mov	cx,ptab-mbrlen	;# bytes left to zero
wmbr1:	xor	al,al		;load 0
	rep	stosb		;clear out the rest of the way
	mov	word ptr [di+pmax*10h],0AA55h ;signature for last word
	mov	byte ptr ds:dirty,1 ;buf is dirty
	ret
;+
;
; ORDER nnnn
;
; Specify order for partitions (if stored out of order).
;
; nnnn is a 1- to PMAX-digit string listing the partitions by number (where
; #1 is the partition with the lowest starting disk address, #2 is second
; lowest, etc.).  0 means an empty slot.
;
;-
order:	push	si		;save
	call	getbuf		;make sure buf exists
	pop	si		;restore
	mov	dx,offset ordprm ;prompt string
	call	getwp		;get digit string
	mov	si,ds:buf	;pt at MBR
	mov	di,ds:othbuf	;scratch buf
	mov	cx,512d/2	;word count
	rep	movsw		;copy
	sub	di,512d-ptab	;point at table within buf
ord1:	mov	al,[bx]		;get a digit
	inc	bx		;bump ptr
	mov	cx,10h/2	;load up size of table entry in words
	sub	al,'0'		;convert to binary
	jz	ord2		;0, empty slot
	cmp	al,pmax		;valid partition #?
	ja	ord6		;no
	cbw			;AH=0
	add	ax,ax		;*2
	mov	si,ax		;copy
	mov	ax,-1		;load invalid address (would wrap)
	xchg	ax,ds:pptrs-2[si] ;get ptr, clear it out
	test	ax,ax		;exists?
	jz	ord6		;no
	cmp	ax,-1		;done it already?
	je	ord7
	mov	si,ax		;point at it
	rep	movsw		;copy, CX=0
ord2:	xor	ax,ax		;load 0
	rep	stosw		;clear out table entry (or not if CX=0)
	dec	dx		;done all digits?
	jnz	ord1		;loop if not
	mov	dx,offset notall ;assume we missed one
	mov	si,offset pptrs	;pt at table
	mov	cx,pmax		;# entries
ord3:	lodsw			;get a word
	inc	ax		;-1?
	jz	ord4		;yes, did it
	dec	ax		;0?  (empty to begin with)
	jnz	ord8		;no, guess we missed it
ord4:	loop	ord3		;cover all slots
	mov	si,ds:othbuf	;pt at fixed copy
	mov	di,ds:buf	;pt at where it belongs
	mov	cx,512d/2	;word count
	rep	movsw
	mov	byte ptr ds:dirty,1 ;buf is dirty
ord5:	jmp	ldptr		;recompute pointers
ord6:	mov	dx,offset invprt ;invalid partition #
	jmp	short ord8
ord7:	mov	dx,offset dupdig ;duplicate digit
ord8:	mov	ah,09h		;func=print
	int	21h
	jmp	short ord5	;reload pointers, return
;+
;
; Read home block from a new disk.
;
;-
read:	call	getw		;get a word
	jc	read3		;nothing, reload existing one
	mov	ax,offset extend ;pt at table
	call	tbluk		;match?
	jc	read4		;no
	; read extended drive partition
	call	getbuf		;make sure we have a boot record
	mov	si,offset pptrs	;point at pointers
	mov	cx,pmax		;# entries
read1:	lodsw			;get an entry
	test	ax,ax		;null?
	jz	read5
	mov	bx,ax		;copy
	cmp	byte ptr [bx+4],05h ;DOS EXTENDED PARTITION?
	jne	read5
	; found extended partition, save parameters if this is in MBR
	test	byte ptr ds:extprt,-1 ;in master boot record?
	jnz	read2		;no, already have ext part parms
	mov	ax,[bx+2]	;get starting cyl+sec
	mov	dh,[bx+1]	;head
	mov	ds:ecyse1,ax	;save cyl+sector in BIOS form (CX)
	mov	cx,bx		;save
	call	unpchs		;unpack
	mov	ds:ecyl1,ax	;save
	mov	ds:ehead1,bx
	mov	ds:esec1,dx
	mov	bx,cx		;restore ptr to table entry
	mov	ax,[bx+6]	;get ending cyl+sec
	mov	dh,[bx+5]	;head
	mov	ds:ecyse2,ax	;save cyl+sector in BIOS form (CX)
	mov	cx,bx		;save
	call	unpchs		;unpack
	mov	ds:ecyl2,ax	;save
	mov	ds:ehead2,bx
	mov	ds:esec2,dx
	mov	bx,cx		;restore ptr to table entry
read2:	mov	dh,[bx+1]	;load up start
	mov	cx,[bx+2]
	mov	dl,ds:drive
	mov	al,ds:extprt	;get existing extended partition #
	inc	ax		;+1
	jmp	short read8	;go read ext part
read3:	jmp	short read6
read4:	jmp	short read7
read5:	loop	read1		;check whole table
	mov	dx,offset noextp ;no extended partition
	mov	ah,09h		;func=print
	int	21h
	ret
read6:	call	getbuf		;make sure default exists
	mov	dx,word ptr ds:drive ;get head,,drive
	mov	cx,word ptr ds:sec ;cyl,,sec
	mov	al,ds:extprt	;keep existing extended partition #
	jmp	short read8	;go re-read it
read7:	call	getn		;not EXTENDED, must be drive #
	jc	read9
	mov	dl,al		;copy
	or	dl,80h		;OR in "HD" bit
	xor	dh,dh		;head=0
	mov	cx,1		;cyl=0, sec=1
	xor	al,al		;not extended partition
read8:	; read it, AL=ext partition # (or 0 for MBR), CX:DX=BIOS-style sec addr
	push	ax		;save
	push	cx
	push	dx
	call	chkdrt		;see if buffer is dirty
	pop	dx		;restore
	pop	cx
	call	rhome		;read the table
	jc	read10		;failed
	call	sort		;check to see if sorted
	call	ldptr		;load PPTRS
	pop	ax		;restore ext part #
	mov	ds:extprt,al	;save
	jmp	list		;display it
read9:	jmp	what
read10:	pop	ax		;flush stack
	ret
;
extend	label	byte
	kw	<E-XTENDED>,0	;read extended DOS partition
	db	0
;+
;
; Restore the boot record from a DOS file (presumably on a floppy).
;
;-
restor:	mov	dx,offset rstprm ;prompt
	call	getwp		;get filename
	xchg	dx,bx		;get ptr into DX
	add	bx,dx		;pt at end
	mov	byte ptr [bx],0	;mark end
	mov	ax,3D00h	;func=open /RONLY
	int	21h
	jc	rest2
	push	ax		;save handle
	call	chkdrt		;make sure buf is clean
	mov	dx,ds:buf	;pt at buf
	mov	cx,512d		;length
	pop	bx		;restore handle
	mov	ah,3Fh		;func=read
	int	21h
	jc	rest3
	cmp	ax,cx		;got it all?
	jne	rest3
	mov	ah,3Eh		;func=close
	int	21h
	cmp	byte ptr ds:drive,0 ;is there a drive specified?
	jnz	rest1
	mov	word ptr ds:drive,80h ;no, say drive 0, head 0
	mov	word ptr ds:sec,1 ;cyl 0, sec 1
rest1:	call	sort		;check to see if sorted
	call	ldptr		;load PPTRS
	call	list		;display it
	mov	dx,offset usrwrt ;pt at msg
	mov	ah,09h		;func=print
	int	21h
	ret
rest2:	mov	dx,offset opnerr ;creation error
	jmp	short rest4
rest3:	mov	ah,3Eh		;func=close
	int	21h
	mov	dx,offset rerr	;read error
rest4:	mov	ah,09h		;func=print
	int	21h
	ret
;+
;
; Write home block (optionally specify new drive #).
;
;-
write:	call	getw		;get drive #
	jc	write1		;use default
	call	getn		;convert number
	jc	write3
	mov	dl,al		;copy
	or	dl,80h		;OR in "HD" bit
	xor	dh,dh		;head=0
	mov	cx,1		;cyl=0, sec=1
	jmp	short write2
write1:	call	getbuf		;make sure default exists
	mov	dx,word ptr ds:drive ;get head,,drive
	mov	cx,word ptr ds:sec ;cyl,,sec
write2:	jmp	whome		;write it
write3:	jmp	what
;+
;
; See if buffer has been modified and offer to write it back out if so.
;
;-
chkdrt:	test	byte ptr ds:dirty,-1 ;dirty?
	jz	cdrt1		;no, never mind
	mov	al,ds:drive	;fetch drive #
	test	al,al		;does it exist?
	jz	cdrt1		;no
	add	al,'0'-80h	;convert to digit
	mov	ds:wrtdrv,al	;save
	mov	dx,offset wrtbuf ;pt at msg
	call	yesno		;ask whether to write to disk
	jc	cdrt1		;default=no
	jnz	cdrt1		;they said no
	mov	dx,word ptr ds:drive ;get head,,drive
	mov	cx,word ptr ds:sec ;cyl,,sec
	call	whome		;write it out
cdrt1:	mov	byte ptr ds:dirty,0 ;guess they don't want it
	ret
;+
;
; Load up pointers to each partition (so we can easily handle the partitions in
; order even if the table is out of order).
;
; On return:
; DS:PPTRS  contains a one-word ptr into the buffer at DS:BUF for each existent
;	partition, or 0 for nonexistent partitions
;
;-
ldptr:	mov	di,offset pptrs	;pt at table
	mov	cx,pmax		;# entries
	xor	ax,ax		;load 0
	rep	stosw		;into all slots
	mov	si,ds:buf	;pt at buf
	mov	di,ds:othbuf	;pt at other buf
	mov	bx,di		;save a ptr
	mov	cx,512d/2	;word count
	rep	movsw		;copy
	lea	ax,[bx+ptab]	;pt at partition table
	push	ax		;save
	call	dosort		;sort it
	pop	bx		;restore
	mov	cx,pmax		;# entries
	mov	di,offset pptrs	;pt at table again
ldptr1:	test	byte ptr [bx+4],-1 ;anything here?
	jz	ldptr3		;no
	; find it in real MBR
	push	cx		;save
	push	di
	mov	ax,ds:buf	;point at real MBR
	add	ax,ptab-10h	;index to one slot before part table
ldptr2:	add	ax,10h		;bump to next slot
	mov	si,bx		;point at this entry
	mov	di,ax		;current posn in real MBR
	mov	cx,10h/2	;word count
	repe	cmpsw		;match?
	jne	ldptr2		;keep looking if not (guaranteed to find it)
	pop	di		;restore
	pop	cx
	stosw			;save addr
ldptr3:	add	bx,10h		;skip to next entry
	loop	ldptr1		;check all entries
	ret
;+
;
; Make sure partition table entries are in sorted order (prompt user if not to
; see whether to sort).
;
;-
sort:	call	issort		;is it in order?
	jnc	sort1		;yes
	mov	dx,offset outord ;pt at string
	call	yesno		;see if they care
	jc	sort1		;default is no
	jnz	sort1		;they said no
	mov	byte ptr ds:dirty,1 ;buf will have changed
	mov	bx,ds:buf	;pt at buf
	jmp	short dosort	;sort, return
sort1:	ret
;+
;
; See if partition table is in sorted order.
;
; CF=1 if not.
;
;-
issort:	mov	bx,ds:buf	;pt at buf
	mov	si,ptab		;pt at first entry
	xor	di,di		;no prev entry
isort1:	cmp	byte ptr [bx+si+4],0 ;anything in this slot?
	jz	isort3		;skip comparison if not
	test	di,di		;is there a prev entry?
	jz	isort2		;no, don't compare
	call	cmpent		;compare prev, curr entries
	ja	isort4		;skip if not in order
isort2:	mov	di,si		;curr entry is now prev
isort3:	add	si,10h		;bump to next slot
	cmp	si,ptab+(pmax*10h) ;done all?  (CF=0 if so)
	jb	isort1		;loop if not
	ret			;CF=0 if so
isort4:	stc			;out of order
	ret
;+
;
; Sort partition table.
;
; bx	base of boot record
;
;-
dosort:	mov	si,ptab		;pt at first entry
	xor	di,di		;no prev entry
dsrt1:	test	di,di		;is there a prev entry?
	jz	dsrt5		;no
	call	cmpent		;compare prev, curr entries
	jbe	dsrt5		;in order
	push	si		;save
	push	di
dsrt2:	; exchange entries at [BX+SI] and [BX+DI]
	mov	cx,10h/2	;count
dsrt3:	mov	ax,[bx+si]	;first dword
	xchg	ax,[bx+di]
	mov	[bx+si],ax
	inc	si		;SI+2
	inc	si
	inc	di		;DI+2
	inc	di
	loop	dsrt3
	sub	di,10h		;restore DI
	mov	si,di		;SI pts at prev
	sub	di,10h		;back up
	cmp	di,ptab		;off beginning?
	jb	dsrt4		;yes, done with this element
	call	cmpent		;compare these two
	ja	dsrt2		;swap if wrong
dsrt4:	pop	di		;restore
	pop	si
dsrt5:	mov	di,si		;curr entry is now prev
	add	si,10h		;bump to next slot
	cmp	si,ptab+(pmax*10h) ;off end?
	jb	dsrt1		;loop if not
	ret
;
cmpent:	; compare entries at [BX+DI] and [BX+SI], set flags
 %out should use starting sector #
	mov	al,1		;1 more than 0
	cmp	[bx+si+4],al	;empty slot after?  (set flags)
	jb	cmpen1		;yes, DI comes first for sure
	cmp	al,[bx+di+4]	;empty slot before?  (set flags)
	ja	cmpen1		;yes, SI comes first for sure
	mov	al,[bx+di+2]	;get high 2 bits of cyl
	mov	ah,[bx+si+2]
	and	ax,300*101h	;isolate
	cmp	al,ah		;which comes first?
	jne	cmpen1
	mov	al,[bx+di+3]	;get low 8 bits of cyl
	cmp	al,[bx+si+3]	;see which comes first
	jne	cmpen1
	mov	al,[bx+di+1]	;cyls match, get surface
	cmp	al,[bx+si+1]	;see which comes first
	jne	cmpen1
	mov	al,[bx+di+2]	;surfaces match, get sector
	mov	ah,[bx+si+2]
	and	ax,77*101h	;isolate (low 6 bits)
	cmp	al,ah
cmpen1:	ret
;+
;
; Read home block.
;
; ch	cyl number, high 2 bits of CL are bits 9:8
; cl	sec number in low 6 bits
; dh	head #
; dl	drive #
;
; Return CF=1 on error (msg printed).
;
;-
rhome:	mov	word ptr ds:drive,dx ;save drive #, head
	mov	word ptr ds:sec,cx ;sector, cyl
	; get disk geometry
	push	es		;save
	mov	ah,08h		;func=get disk parms
	int	13h		;get claimed disk geometry (may lie if >528MB)
	pop	es		;[restore]
	jc	rhom2		;err (presumably nonexistent disk, say rd err)
	mov	byte ptr ds:mhd,dh ;save in BIOS form
	mov	ds:mcyse,cx
	mov	byte ptr ds:mhd+1,0 ;clear MSB
	mov	ax,cx		;copy
	call	unpchs		;unpack
	inc	ax		;find total # cyls
	mov	ds:ncyls,ax	;save
	inc	bx		;find total # heads (supposedly <= 16. but with
				;big disks the BIOS will multiply the # of
				;heads by some N and divide the # of cyls by
				;the same N to allow bigger disks)
	mov	ds:nheads,bx	;save heads
	mov	ds:nsecs,dx	;don't inc because sectors starts at 1 not 0
	dec	ax		;back again
	dec	bx
	call	chsabs		;find total # sectors on device
	add	ax,1		;+1 for first sector after end
	adc	dx,0
	mov	ds:dsize,ax	;save
	mov	ds:dsize+2,dx
	; read partition table
	mov	cx,5		;retry count
rhom1:	push	cx		;save
	mov	dx,word ptr ds:drive ;get head,,drive
	mov	cx,word ptr ds:sec ;cyl,,sec
	mov	bx,ds:buf	;addr
	mov	ax,0201h	;func=read, 1 sector
	int	13h		;do it
	pop	cx		;[restore]
	jnc	rhom3		;happy
	loop	rhom1		;loop
rhom2:	mov	dx,offset rerr	;read error
	mov	ah,09h		;func=print
	int	21h
	mov	byte ptr ds:drive,0 ;nothing in buf
	stc			;error
	ret
rhom3:	; success
	mov	byte ptr ds:dirty,0 ;as yet untouched
	mov	bx,ds:buf	;get buf addr
	cmp	word ptr [bx+1FEh],0AA55h ;valid?
	je	rhom5		;yes
	mov	dx,offset crtmbr ;pt at string
	call	yesno		;ask if we should create boot record
	jc	rhom4		;default=yes
	jne	rhom5
rhom4:	; create master boot record
	mov	di,ds:buf	;get buf addr
	mov	cx,(512d/2)-1	;init to zero, except last word
	xor	ax,ax		;value to write
	rep	stosw
	mov	ax,0AA55h	;signature for last word
	stosw
	cmp	word ptr ds:sec,1 ;cyl 0, sec 1?
	jne	rhom5		;no
	cmp	byte ptr ds:head,0 ;head 0?
	jne	rhom5		;no
	sub	di,512d		;point at beginning
	mov	si,offset mbr	;pt at code
	mov	cx,mbrlen
	rep	movsb
rhom5:	clc			;happy
	ret
;+
;
; Write home block.
;
; ch	cyl number, high 2 bits of CL are bits 9:8
; cl	sec number in low 6 bits
; dh	head #
; dl	drive #
;
; Return CF=1 on error (msg printed).
;
;-
whome:	mov	bx,5		;retry count
whom1:	push	bx		;save
	push	cx
	push	dx
	mov	bx,ds:buf	;addr
	mov	ax,0301h	;func=write, 1 sector
	int	13h		;do it
	pop	dx		;[restore]
	pop	cx
	pop	bx
	jnc	whom2		;happy
	dec	bx		;loop
	jnz	whom1
	mov	dx,offset werr	;read error
	mov	ah,09h		;func=print
	int	21h
	stc			;error return
	ret
whom2:	mov	byte ptr ds:dirty,0 ;no longer needs flushing
	mov	dx,offset reboot ;remind them to reboot
	mov	ah,09h		;func=print
	int	21h
	clc			;happy return
	ret
;+
;
; Return addr of block buf, if a home block has been read.
;
;-
getbuf:	mov	si,ds:buf	;get addr
	cmp	byte ptr ds:drive,0 ;is there anything in the buf?
	jnz	getbf1
	mov	dx,offset nodrv	;pt at msg
	mov	ah,09h		;func=print
	int	21h
	jmp	mloop		;restart (flush stack)
getbf1:	ret
;+
;
; Pack cyl/head/sector into BIOS format, truncating values to fit fields.
;
; ax	cyl
; bx	head
; dx	sector
;
; ax	returns cyl+sec
; dh	returns head
; si,cx	preserved
;
;-
pckchs:	xchg	al,ah		;cyl in LH
	and	al,3		;truncate to 10 bits total
	ror	al,1		;in high 2 bits of AL
	ror	al,1
	and	dl,77		;isolate sector
	or	al,dl		;combine
	mov	dh,bl		;get head
	ret
;+
;
; Unpack cyl/head/sector from BIOS format.
;
; ax	cyl+sec
; dh	head
;
; ax	returns cyl
; bx	returns head
; dx	returns sector
; si,cx,di preserved
;
;-
unpchs:	mov	dl,al		;copy sector out of the way
	xchg	al,ah		;low 8 bits of cyl in RH
	rol	ah,1		;get high 2 bits into 9:8
	rol	ah,1
	and	ah,3		;isolate
	mov	bl,dh		;get head
	xor	bh,bh		;zero-extend
	and	dx,77		;isolate sector
	ret
;+
;
; Convert cyl/head/sec to absolute sector addr.
;
; ax	cyl
; bx	head
; dx	sec
; others preserved
;
; dx:ax	returns abs sec addr (starting at 0)
;	(CYL*NHEADS+HEAD)*NSECS+SEC-1
;
;-
chsabs:	push	cx		;save
	mov	cx,dx		;copy sector out of the way
	mul	ds:nheads	;DX:AX=CYL*NHEADS
	add	ax,bx		;add HEAD
	adc	dx,0
	mov	bx,ax		;save low order of sum
	mov	ax,dx		;get high order
	mul	ds:nsecs	;high order of total *NSECS
	xchg	ax,bx		;save low word of result, get low word of sum
	mul	ds:nsecs	;low order of total *NSECS
	dec	cx		;sector -1 (sectors start at 1 not 0)
	add	ax,cx		;add it in
	adc	dx,bx		;handle carry, add in product with high order
	pop	cx		;restore
	ret
;+
;
; Convert absolute sector addr to cyl/head/sec.
;
; dx:ax	abs sec addr (starting at 0)
;
; ax	returns cyl
; bx	returns head
; dx	returns sec
; others preserved
;
;-
abschs:	; divide DX:AX by NSECS to yield 32-bit quotient, 16-bit remainder
	mov	bx,ax		;save low order
	mov	ax,dx		;get high order
	xor	dx,dx		;0-extend
	div	ds:nsecs	;MSW/NSECS
	xchg	ax,bx		;save result, get low order
	div	ds:nsecs	;rem'LSW/NSECS
	xchg	dx,bx		;BX=remainder (sector-1), DX:AX=quotient
	div	ds:nheads	;AX=cyl, DX=head
	xchg	bx,dx		;the right way around
	inc	dx		;sectors start at 1 not 0
	ret
;+
;
; Get a line from the terminal.
;
; dx	prompt string ($-terminated)
; si,cx	return addr, length of response
;
;-
gtlin:	mov	ah,09h		;func=print
	int	21h
	mov	dx,offset kbbuf	;point at keyboard buf
	mov	byte ptr ds:kbbuf,80d ;length
	mov	ah,0Ah		;func=buffered read
	int	21h
	mov	dl,lf		;echo LF
	mov	ah,02h		;func=CONOUT
	int	21h
	mov	si,offset kbbuf+1 ;pt at length read
	lodsb			;get it
	cbw			;AH=0
	mov	cx,ax		;copy
	ret
;+
;
; Put decimal number in DX:AX at ES:DI.
;
;-
cnum:	mov	bx,10d		;divisor
cnum1:	test	dx,dx		;see if number<10.
	jnz	cnum2
	cmp	ax,bx		;well?
	jb	cnum4
cnum2:	; DX:AX=dividend, BX=divisor (both unsigned)
	xor	cx,cx		;assume high order result=0 (no overflow)
	cmp	dx,bx		;number .lt. divisor*65536?
	jb	cnum3		;yes, a single DIV will do it
	; result won't fit in 16 bits (quo.lt.65536, rem.lt.BX)
	; do the division in two steps
	mov	cx,ax		;save low order
	mov	ax,dx		;copy high order
	xor	dx,dx		;0-extend
	div	bx		;do first 16-bit "digit" of long division
	xchg	ax,cx		;save high "digit" of result, get low order
cnum3:	div	bx		;(dx is remainder "carried" from first div)
	; CX:AX=quotient, DX=remainder
	push	dx		;save remainder
	mov	dx,cx		;copy
	call	cnum1		;recurse for other digits
	pop	ax		;restore
cnum4:	or	al,'0'		;convert to digit
	stosb			;store
	ret
;+
;
; Flush line in OBUF and reset DI.
;
;-
pline:	mov	ax,cr+(lf*400)	;<CRLF>
	stosw
	mov	dx,offset obuf	;addr
	sub	di,dx		;len
	mov	cx,di
	mov	di,dx		;(reset ptr)
	mov	bx,1		;STDOUT
	mov	ah,40h		;func=write
	int	21h
	ret
;+
;
; Print <CRLF>.
;
;-
pcrlf:	mov	dl,cr		;CR
	mov	ah,02h		;func=CONOUT
	int	21h
	mov	dl,lf		;LF
	mov	ah,02h		;func=CONOUT
	int	21h
	ret
;+
;
; Parse a number from the input line.
;
; bx,dx	addr, len of number (preserved)
; si,cx	preserved
; ax	returns number
;
; CF=1 in invalid digit found (AX=number so far, DI=# chars discarded).
;
;-
getn:	push	si		;save
	push	bx
	push	cx
	push	dx
	mov	si,bx		;pt at number
	mov	cx,dx
	xor	bx,bx		;init #
getn1:	lodsb			;get a digit
	sub	al,'0'		;convert to binary
	cmp	al,9d		;is it a digit?
	ja	getn3
	cbw			;AH=0
	xchg	ax,bx		;put in BX, get # so far
	mov	dx,10d		;multiplier
	mul	dx
	add	bx,ax		;add to new digit
	loop	getn1		;loop
	clc
getn2:	mov	ax,bx		;copy
	pop	dx		;restore
	pop	cx
	pop	bx
	pop	si
	ret
getn3:	mov	di,cx		;# unparsed digits
	stc			;no good
	jmp	short getn2
;+
;
; Parse a hex number from the input line.
;
; bx,dx	addr, len of number (preserved)
; si,cx	preserved
; ax	returns number
;
; CF=1 in invalid number.
;
;-
geth:	push	si		;save
	push	dx
	push	cx
	push	bx
	mov	si,bx		;pt at number
	mov	cx,dx
	xor	bx,bx		;init #
geth1:	lodsb			;get a digit
	sub	al,'0'		;convert to binary
	cmp	al,9d		;is it a digit?
	jbe	geth2		;yes
	sub	al,'A'-'0'	;see if A-F
	cmp	al,5
	ja	geth4		;no
	add	al,10d		;convert 0-5 to 0Ah-0Fh
geth2:	cbw			;AH=0
	xchg	ax,bx		;put in BX, get # so far
	mov	dx,10h		;multiplier
	mul	dx
	add	bx,ax		;add to new digit
	loop	geth1		;loop
	mov	ax,bx		;copy
	clc			;happy
geth3:	pop	bx		;restore
	pop	cx
	pop	dx
	pop	si
	ret
geth4:	stc			;no good
	jmp	short geth3
;+
;
; Parse a word from the input line.
;
; ds:si	current position
; cx	# chars left
;
; On return:
; si	points at posn after last char of word
; cx	updated
; bx	points at begn of word (converted to U.C.) if CF=0
; dx	length of word
;
;-
getw:	jcxz	getw2		;EOL already
getw1:	; look for beginning of word
	mov	bx,si		;in case word starts here
	lodsb			;get a char
	cmp	al,' '		;blank or ctrl?
	ja	getw4		;no
	loop	getw1		;loop
getw2:	stc			;no luck
	ret
getw3:	; look for end of word
	lodsb			;get a char
getw4:	cmp	al,' '		;blank or ctrl?
	jbe	getw6		;yes, end of word
	cmp	al,'a'		;lower case?
	jb	getw5
	cmp	al,'z'		;hm?
	ja	getw5
	and	al,not 40	;yes, convert
	mov	[si-1],al	;put back
getw5:	loop	getw3		;loop
	inc	si		;compensate for next inst
getw6:	dec	si		;unget
	mov	dx,si		;calc length
	sub	dx,bx		;CF=0
	ret
;+
;
; Get word, prompting (endlessly) for it if nothing left on line.
;
; Regs same as GETW except DS:DX points at prompt string CF is always 0.
;
;-
getwp:	push	dx		;save
getwp1:	call	getw		;try to get a word
	pop	ax		;[restore]
	jnc	getwp2		;got one
	mov	dx,ax		;copy
	push	ax		;save again
	call	gtlin		;get line
	jmp	short getwp1	;try again
getwp2:	ret
;+
;
; Look up a keyword in a table.
;
; ds:bx	keyword } from GETW
; dx	length  }
; cs:ax	table
;
; Returns CF=1 if not found, otherwise AX=number from table, and DI=contents
; of "help" field from table.
;
; This routine doesn't require that DS=CS, so it may be used to parse
; environment strings.
;
; si,cx preserved either way.
;
;-
tbluk:	push	cx		;save
	push	si
	push	ds
	mov	si,ax		;pt at table
	push	ds		;copy DS to ES
	pop	es
	push	cs		;and CS to DS
	pop	ds
	xor	ch,ch		;CH=0
tbluk1:	lodsw			;get length,,length to match
	test	al,al		;end?
	jz	tbluk4
	mov	cl,ah		;assume bad length
	cmp	al,dl		;is ours long enough?
	ja	tbluk2		;no
	sub	ah,dl		;too long?
	jc	tbluk2		;yes
	mov	cl,dl		;just right
	mov	di,bx		;point at keyword
	repe	cmpsb		;match?
	je	tbluk3
	add	cl,ah		;no, add extra length
tbluk2:	add	si,cx		;skip to end
	add	si,2+2		;skip jump addr, HELP addr
	jmp	short tbluk1	;loop
tbluk3:	; got it
	mov	cl,ah		;get extra length
	add	si,cx		;skip to end
	lodsw			;get dispatch addr
	mov	di,[si]		;get help addr
	stc			;makes CF=0 below
tbluk4:	; not found
	cmc			;CF=-CF
	pop	ds		;restore regs
	pop	si
	pop	cx
	ret
;
msgprm:	; missing parameter
	mov	dx,offset mparm	;pt at msg
	mov	ah,09h		;func=print
	int	21h
	jmp	mloop		;restart
;+
;
; Get an answer to a yes/no question.
;
; dx	prompt
;
; On return, CF=1 means they entered a blank line, otherwise ZF=1 means yes.
;
;-
yesno:	call	gtlin		;prompt, get response
	call	getw		;read first word
	jc	yesno1		;CF=1, default
	mov	ax,offset yntab	;pt at table
	call	tbluk		;look up their answer
	jc	yesno2
	test	ax,ax		;set ZF according to answer
yesno1:	ret
yesno2:	jmp	what		;invalid answer
;
yntab	label	byte
	kw	<Y-ES>,0	;ZF=1
	kw	<N-O>,1		;ZF=0
	db	0
;+
;
; Master boot record.
;
;-
mbr:	; set up stack
	cli			;ints off
	xor	ax,ax		;;load 0 into SS
	mov	ss,ax
	mov	sp,7A00h	;;stack base
	sti			;;(ints back on)
	cld			;DF=0
	mov	es,ax		;ES:DI points to free area after stack
	mov	di,sp
	; move us out of the way of the secondary boot block
	push	cs		;copy CS to DS
	pop	ds
	mov	si,7C00h	;source addr
	mov	cx,512d/2	;count
	rep	movsw
	mov	ds,ax		;point with DS
	db	0EAh		;JMP FAR
	dw	($+4-mbr)+7A00h,0 ;to the next instruction
	; look for first bootable partition
	mov	si,7A00h+ptab	;point at partition table
mbr1:	cmp	byte ptr [si],0	;is this one bootable?
	jnz	mbr2		;yes, do it
	add	si,10h		;advance to next
	cmp	si,7A00h+ptab+(pmax*10h) ;off end?
	jb	mbr1		;loop if not
	jmp	mbr8		;error, no bootable partition
mbr2:	; boot the partition pointed to by DS:SI
	; note:  floppy-only BIOS doesn't guarantee that SI/DI are preserved,
	; but IBM fixed disk BIOS and later do, and that's what we're using
	mov	di,5		;retry count
mbr3:	; try EDD BIOS first, in case partition starts after 8.4 GB mark
	mov	dl,[si]		;fetch drive #
	mov	bx,55AAh	;magic number
	mov	ah,41h		;func=EDD presence check
	int	13h
	jc	mbr4
	cmp	bx,0AA55h	;byte-swapped magic #?
	jne	mbr4
	test	cl,1		;OK to use packet structure?
	jz	mbr4
	mov	ax,[si+8d]	;fetch block addr
	mov	bx,[si+10d]
	push	si		;save
	mov	si,7000h	;point at packet buf (well below our stack)
	mov	[si+8d],ax	;starting block address
	mov	[si+10d],bx
	xor	ax,ax		;load 0
	mov	[si+12d],ax	;high dword of block address
	mov	[si+14d],ax
	mov	bx,7C00h	;ES:BX=buf addr (LILO cares)
	mov	[si+4],bx	;FAR buffer address
	mov	[si+6],ax
	mov	word ptr [si],16d ;packet size, reserved byte
	inc	ax		;=1
	mov	[si+2],ax	;block count, reserved byte
	mov	ah,42h		;func=extended read
	int	13h		;(DL still set from test above)
	pop	si		;[restore]
	jmp	short mbr5
mbr4:	; no EDD BIOS, use original BIOS calls
	mov	dx,[si]		;load regs
	mov	cx,[si+2]
	mov	bx,7C00h	;point at where it goes
	mov	ax,0201h	;func=read one sector
	int	13h		;do it
mbr5:	jnc	mbr6
	xor	ah,ah		;func=reset
	int	13h
	dec	di		;retry
	jnz	mbr3
if 1
	int	18h		;according to BIOS boot spec V1.01
else
	call	mbrmsg
	db	'?Boot block read error',0
endif
	jmp	short $		;spin until Ctrl-Alt-Del
mbr6:	cmp	word ptr [bx+1FEh],0AA55h ;bootable?
	jne	mbr7
	db	0E9h		;JMP NEAR
	dw	7C00h-($+2-mbr+7A00h) ;to begn of boot block
mbr7:	call	mbrmsg
	db	'?Invalid boot block',0
	jmp	short $		;spin until Ctrl-Alt-Del
mbr8:	; unable to find a partition to boot, prompt the user
	call	mbrmsg
	db	'?No bootable partition',cr,lf,0
mbr9:	call	mbrmsg		;print prompt
	db	'Partition to boot (1-',pmax+'0','): ',0
mbr10:	xor	ah,ah		;func=get char
	int	16h		;from KB BIOS
	test	al,al		;the one value that will crash us
	jnz	mbr11
	 mov	al,' '		;change to blank
mbr11:	mov	byte ptr ds:(bpart-mbr+7A00h),al ;save char
	push	ax		;save
	call	mbrmsg		;echo it, <CRLF>
bpart	db	?,cr,lf,0
	pop	ax		;restore char
	sub	al,'1'		;convert to 0:N-1
	cmp	al,pmax		;in range?
	jae	mbr9		;reprompt if not
	cbw			;AH=0
	mov	cl,4		;shift count for size of part table entry
	sal	ax,cl		;get offset into table
	add	ax,7A00h+ptab	;add base
	mov	si,ax		;copy
	test	byte ptr [si+4],-1 ;part exists?
	jz	mbr9		;no, reprompt
	mov	byte ptr [si],80h ;yes, mark it "active" in memory
	jmp	mbr2		;go boot it
;
mbrmsg:	; print in-line .ASCIZ message
	pop	si		;restore ptr
	lodsb			;get a char
	test	al,al		;end?
	jz	mbrm1		;yes
	mov	bx,7		;color=white on black
	mov	ah,0Eh		;func=write TTY
	push	si		;save
	int	10h
	jmp	short mbrmsg	;loop
mbrm1:	jmp	si		;return
;
mbrlen=	$-mbr
;
	.radix	16d
	irp	x,%mbrlen
	irp	y,%ptab
	%out	MBR length = &x, max = &y
	endm
	endm
	.radix	8
if mbrlen gt ptab
	.err	Boot code overlays partition table
endif
;
	subttl	pure data
;
hello	db	'FDISK V1.13  By John Wilson <wilson@dbit.com>',cr,lf
	db	'Type "HELP" for help',cr,lf,'$'
hlphdg	db	'Commands (type HELP <cmd> for information):',cr,lf,cr,lf,'$'
rerr	db	'?Read error',cr,lf,'$'
werr	db	'?Write error',cr,lf,'$'
crterr	db	'?File creation error',cr,lf,'$'
opnerr	db	'?File open error',cr,lf,'$'
nodrv	db	'?No partition table has been read',cr,lf,'$'
nosig	db	'?This disk has no valid master boot record',cr,lf,'$'
nopart	db	'%No partitions defined',cr,lf,'$'
noextp	db	'?No extended partition exists',cr,lf,'$'
nofree	db	'?Insufficient available space',cr,lf,'$'
ptfull	db	'?Partition table is full',cr,lf,'$'
ordprm	db	'Order in which to store partitions (4 digits 0-4)? $'
dupdig	db	'?Duplicate digit',cr,lf,'$'
notall	db	'?String does not cover all existing partitions',cr,lf,'$'
mparm	db	'?Missing parameter',cr,lf,'$'
invprt	db	'?Invalid partition number',cr,lf,'$'
usrwrt	db	'%Restored to memory, use WRITE to write to disk',cr,lf,'$'
reboot	db	'%Must reboot to make changes take effect',cr,lf,'$'
bttext	db	' BOOT  '		;bootable (active) partition
spaces	db	'       '		;non-bootable (inactive) partition
ordhdg	db	'Partition table order:  $'
dskprm	db	'Maximum C/H/S as reported by BIOS:  $'
extprm	db	'Extended partition is from $'
;
; Prompts:
prompt	db	'FDISK>$'
prttyp	db	'Partition type? $'
prtprm	db	'Partition number? $'
prtbeg	db	'Start of partition (or size only if using default)? $'
prtend	db	'End of partition, or size as "nnnMB" or "nnn%"? $'
crtmbr	db	'No valid boot record exists -- create? [YES] $'
outord	db	'Partition table out of order -- sort it? [NO] $'
clrcfm	db	'ALL CONTENTS WILL BE LOST -- really clear out partition '
	db	'table? [NO] $'
wrtbuf	db	'Partition table in memory has been modified -- '
	db	'write to drive '
wrtdrv	db	'n? [NO] $'
delcfm	db	'ALL CONTENTS WILL BE LOST -- really delete partition? [NO] $'
bkpprm	db	'Backup to what filename? $'
rstprm	db	'Restore from what filename? $'
;
; Help text:
nohelp	db	'%No help available for "'
nohlpl=	$-nohelp
bkphlp	db	'BACKUP <filename>',cr,lf
	db	'Writes the currently loaded partition table '
	db	'to a file for safekeeping.',cr,lf,'$'
bthelp	db	'BOOT n',cr,lf
	db	'Makes partition "n" bootable (meaningful on drive 0 only).'
	db	cr,lf,'$'
chghlp	db	'CHANGE n type',cr,lf
	db	"Changes partition N's type to TYPE.  See ? CREATE for types."
	db	cr,lf,'$'
clrhlp	db	'CLEAR',cr,lf
	db	'Clears out entire master boot record, deleting all '
	db	'partitions.',cr,lf
	db	'Use this to restore the drive to its virgin state.',cr,lf,'$'
clshlp	db	'CLS',cr,lf
	db	'Clears screen.',cr,lf,'$'
crthlp	db	'CREATE type [range]',cr,lf
	db	'Creates a partition of the specified type covering the '
	db	'specified range of the',cr,lf
	db	'disk.  The type may be one of:',cr,lf
	db	'  CPM86        CP/M-86 file system',cr,lf
	db	'  DOSEXTENDED  DOS 3.3 extended partition',cr,lf
	db	'  DOSFAT12     DOS 2.X FAT12',cr,lf
	db	'  DOSFAT16     DOS 3.X FAT16',cr,lf
	db	'  DOSLARGE     DOS 4.X large partition (the usual choice, '
	db	'DOS for short)',cr,lf
	db	'  LINUX        Linux ext2 file system',cr,lf
	db	'  LINUXSWAP    Linux swap area',cr,lf
	db	'  nn           two-digit hex number for any other type',cr,lf
	db	'The range starts with an optional cyl/head/sec (e.g. '
	db	'"100/0/1"), the beginning',cr,lf
	db	'of the first unused area on the disk is used if no start '
	db	'is given.  The range',cr,lf
	db	'ends either with a given cyl/head/sec, or the size may '
	db	'be given instead, either',cr,lf
	db	'a number of megabytes like "400MB" or a percentage of '
	db	'the free area like "50%".',cr,lf
	db	'Note that no white space is allowed between the number '
	db	'and the "MB" or "%".  In',cr,lf
	db	'cyl/head/sec values, the head and sector may be omitted '
	db	'and default to the',cr,lf
	db	'beginning of the cylinder for the start C/H/S, or the '
	db	'end of the cylinder for',cr,lf
	db	'the end C/H/S.  Examples:',cr,lf,cr,lf
	db	'CREATE DOS 100%        DOS large partition '
	db	'using the whole disk',cr,lf
	db	'CREATE LINUX 300 50MB  50MB Linux partition '
	db	'starting at cylinder 300',cr,lf
	db	'CREATE CPM 200/1 399   CP/M partition from cyl '
	db	'200 head 1 to end of cyl 399$'
delhlp	db	'DELETE n',cr,lf
	db	'Deletes partition "n" LOSING ALL DATA.',cr,lf,'$'
lsthlp	db	'LIST',cr,lf
	db	'Lists the currently loaded partition table.',cr,lf,'$'
mbrhlp	db	'MBR',cr,lf
	db	'Refreshes boot code in master boot record (MBR), '
	db	'preserving partition table.',cr,lf,'$'
ordhlp	db	'ORDER ',pmax dup('n'),cr,lf
	db	'Reorders the partition table so the entries will be in '
	db	'a specific order.',cr,lf
	db	'"',pmax dup('n'),'" is a string of digits in the range 0-'
	db	pmax+'0',';  each digit gives a partition',cr,lf
	db	'number (with 0 meaning an empty slot), and each '
	db	'existing partition must appear',cr,lf
	db	'once in the string.  The partitions table is reordered '
	db	'so that the partition',cr,lf
	db	'descriptors are in the same order as the digits in the '
	db	'string.  This does not',cr,lf
	db	'affect DOS, which assigns drive letters in the order '
	db	'that the partitions appear',cr,lf
	db	'on the disk, not the order that they appear in the '
	db	'partition table.',cr,lf,'$'
qhelp	db	'QUIT',cr,lf
	db	'Exits the program.',cr,lf,'$'
rdhelp	db	'READ n',cr,lf
	db	'Reads the master partition table from the specified hard '
	db	'disk (0 or 1).',cr,lf
	db	cr,lf
	db	'READ EXTENDED',cr,lf
	db	'Reads the DOS extended partition table (use this command '
	db	'multiple times to',cr,lf
	db	'follow the chain, and READ 0 or READ 1 to start over).',cr,lf
	db	'$'
rsthlp	db	'RESTORE <filename>',cr,lf
	db	'Restores a partition table into memory from a file.'
	db	cr,lf
	db	'(Use WRITE to write it on the disk.)',cr,lf,'$'
wrhelp	db	'WRITE [n]',cr,lf
	db	'Writes the currently loaded partition table back to the '
	db	'specified hard disk',cr,lf
	db	'(0 or 1, default=current).  You must reboot (Ctrl-Alt-Del) '
	db	'to make changes',cr,lf
	db	'take effect.',cr,lf
	db	'$'
;
	subttl	impure data
;
cmdlen	dw	0		;length of cmd line in JCL (0 => none)
;
drive	db	0		;curr unit # (80, 81) or 0 if nothing in buf
head	db	0		;BIOS-style head, sec, cyl of boot rec
sec	db	0		;(high 2 bits are bits 9:8 of cyl)
cyl	db	0		;(low 8 bits)
				;the above four words MUST be in this order
;
extprt	db	0		;extended partition # (starting at 1), or 0
				;for MBR (bumped by 1 on each READ EXT)
;
dirty	db	0		;NZ => partition table has been altered, but
				;not written to disk (ignored if DRIVE=0)
;
lsthdg	db	cr,lf
	db	'-No.-   -Start CHS- -End CHS-   -Size-     -Type-'
	db	'    [Drive '
drvmsg	db	'#$'
clsbrk	db	']',cr,lf,'$'	;end of "[Drive n]" msg in LIST heading
extmsg	db	', ext. part. $'
;
sys	macro	token,name
	local	a,b
a	db	token,b,name
b=	$-a-2
	endm
;
systab	label	byte
	sys	01h,'DOS PRIMARY FAT12'
	sys	02h,'XENIX ROOT DIRECTORY'
	sys	03h,'XENIX /USR PARTITION'
	sys	04h,'DOS PRIMARY FAT16'	 ;3.0 (up to 64 K sectors, usu. 32 MB)
	sys	05h,'DOS EXTENDED PARTITION'
	sys	06h,'DOS LARGE PARTITION'  ;4.0 (up to 2 GB)
	sys	07h,'OS/2 HPFS'
	sys	08h,'AIX FILE SYSTEM'
	sys	09h,'AIX BOOT PARTITION'
	sys	0Bh,'WIN95B FAT32'	;Windows 95 OSR2, up to 2 TB
	sys	0Ch,'WIN95B EXTENDED PARTITION' ;W95 OSR2 ext part, up to 2 TB
;;
;;	sys	0Eh,'VFAT'
;; (from DR-DOS FDISK documentation, not verified)
;; According to M$ KB article # Q69912, 0E = 06 with extended INT 13h support,
;; and 0F = 05 with extended INT 13h support
	sys	10h,'OPUS'
	sys	16h,'HIDDEN DOS LARGE'	;Partition Magic
	sys	17h,'HIDDEN HPFS'	;Partition Magic
	sys	40h,'VENIX 286'		;Venturecom, Cambridge MA
	sys	51h,'NOVELL'
	sys	52h,'CP/M-86'
	sys	63h,'SYSTEM V/386'
	sys	64h,'NOVELL'
	sys	75h,'PC/IX'
	sys	80h,'MINIX (V1.4A-)'
	sys	81h,'MINIX (V1.4B+)'
	sys	82h,'LINUX SWAP'
	sys	83h,'LINUX EXT2 NATIVE'
	sys	93h,'AMOEBA FILE SYSTEM'
	sys	94h,'AMOEBA BAD BLOCK TABLE'
	sys	0DBh,'CONCURRENT DOS'	;DRI
;	sys	0F2h,'DOS SECOND PARTITION'
;	sys	0FFh,'BAD TRACK TABLE'
	sys	00h,'UNKNOWN (nn)'
	org	$-3
unktyp	label	word		;(patch in file sys ID in hex)
	org	$+3
;
	subttl	pure storage
;
; Geometry of drive whose unit # is in DS:DRIVE:
ncyls	dw	1 dup(?)	;# cylinders 1-1024.
nheads	dw	1 dup(?)	;# heads 1-256.
nsecs	dw	1 dup(?)	;# sectors/track 1-64.
mcyse	dw	1 dup(?)	;max cyl+sector in BIOS form (CX)
mhd	dw	1 dup(?)	;max head
dsize	dw	2 dup(?)	;total disk size in sectors
;
ecyl1	dw	1 dup(?)	;starting cyl, head, sector of ext part
ehead1	dw	1 dup(?)	;(meaningful iff EXTPRT.GT.0)
esec1	dw	1 dup(?)
ecyse1	dw	1 dup(?)	;starting cyl+sector in BIOS form (CX)
;
ecyl2	dw	1 dup(?)	;ending cyl, head, sector of ext part
ehead2	dw	1 dup(?)	;(as above)
esec2	dw	1 dup(?)
ecyse2	dw	1 dup(?)	;ending cyl+sector in BIOS form (CX)
;
free1	dw	2 dup(?)	;abs sec addr of start of free area
free2	dw	2 dup(?)	;abs sec addr of end of free area
;
dhead	dw	1 dup(?)	;default head # for GETCHS
dsec	dw	1 dup(?)	;default sector # for GETCHS
;
pptrs	dw	pmax dup(?)	;pointers to partition table entries, or 0
				;(these are in sorted order even if the part
				;table isn't)
;
obuf	db	80d dup(?)	;line output buffer
;
kbbuf	db	82d dup(?)	;keyboard buffer
	db	1 dup(?)	;allow for adding NUL to filenames
;
buf	dw	1 dup(?)	;ptr to 512-byte buf not spanning 64KB boundary
othbuf	dw	1 dup(?)	;ptr to other buffer (used by LDPTR)
buf1	db	512d dup(?)	;disk buffer
buf2	db	512d dup(?)	;BUF2 used if BUF1 spans 64KB boundary
;
	dw	400h dup(?)	;stack
pdl	label	word		;end of stack
;
code	ends
	end	start
