title fdisk ;++ ; ; FDISK equivalent, uses command line instead of stupid menus. ; ; By John Wilson , 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 ,<-> 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 ,<-> 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 ,create ;syn. for CREATE kw ,backup,bkphlp kw ,boot,bthelp kw ,change,chghlp kw ,clear,clrhlp kw ,cls,clshlp kw ,create,crthlp kw ,delete,delhlp kw ,help kw ,list,lsthlp kw ,wmbr,mbrhlp kw ,order,ordhlp kw ,quit,qhelp kw ,read,rdhelp kw ,restor,rsthlp kw ,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 ,52h ;CP/M-86 kw ,05h ;DOS 3.3 extended partition kw ,01h ;DOS 2.X FAT12 kw ,04h ;DOS 3.X FAT16 kw ,06h ;DOS 4.X large partition kw ,83h ;Linux ext2 file system kw ,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, 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 ,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) ; 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 . ; ;- 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 ,0 ;ZF=1 kw ,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, 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 ',cr,lf db 'Type "HELP" for help',cr,lf,'$' hlphdg db 'Commands (type HELP 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 ',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 ',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