# rmrd.S86 - DOS program to remove ramdisk # # Copyright (C) 1996-2003 Gero Kuhlmann # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. # # $Id: rmrd.S86,v 1.1 2003/03/09 00:43:09 gkminix Exp $ # #==================================================================== # #include "common.i86" #include "boot.i86" .file "boot.s86" .line 30 # #==================================================================== # # Define the text segment. This segment has to start at 0x0100, # but we cant use the .org directive. The assembler would then # write 0x0100 times 0 values. Instead we have to tell the linker # using a ldscript file that this starts at an absolute address. # \(.text) .global _start _start: jmp start # #==================================================================== # # When using our own boot sector, the BIOS disk parameter block (DPB) # for the first floppy drive gets stored into the resident ramdisk # driver. Therefore, unless no other program has modified the DPB pointer # within the interrupt table, it will still point into the resident # area. When we remove the resident ramdisk drive we have to make # sure that the DPB remains in memory. In that case we copy the DPB # into the following data area and then make this program resident. # It then only contains the DPB and nothing else, but keeps the DPB # from getting overwritten. # dpb: .space DPB_SIZE,0 # #==================================================================== # # Here the actual game starts. It is not as simple as calling the # resident driver to remove itself. First we have to check for # some error conditions: # # - the DOS version version has to be 3.2 or above # - the version of the ramdisk driver has to be 2.0 or above # - the ramdisk can only get removed if it is simulating the # first floppy disk drive # - at least one physical floppy disk drive has to be installed # in the system, because DOS allocates a driver entry for the # ramdisk, and we can't remove that but just change the para- # meter to fit the real drive # - the ramdisk should not be the current drive # start: push cs pop ds # Check for correct versions of DOS and ramdisk driver mov ax,0x3000 xor bx,bx int 0x21 # get DOS version xchg al,ah cmp ax,0x0302 # has to be at least 3.2 jb start1 cmp bh,0xFD # check for FreeDOS which doesnt jne start2 # support some functions needed start1: mov si,offset invdos mov al,1 jmp doerr start2: mov [dosver],ax # save DOS version xor ax,ax mov es,ax mov si,offset rdsig mov di,0xF1 * 4 # check ramdisk driver signature mov cx,4 repz cmpsb jnz start3 mov ax,0x9C00 int 0xF8 # check if a ramdisk driver is cmp ax,0x009D # installed jne start3 cmp bl,2 # check that we have ramdisk driver jae start4 # version 2.0 or higher start3: mov si,offset noramd mov al,1 jmp doerr # Next check that the ramdisk is simulating the first floppy disk drive, and # that there is a physical floppy drive installed in the system. Also check # that the ramdisk is not the current drive. start4: mov ax,0x9C03 int 0xF8 # get ramdisk ID or cl,cl # has to be drive A: (e.g. floppy) jz start5 mov si,offset invdrv mov al,2 jmp doerr start5: test ch,0x01 # check that there is at least one jnz start6 # physical floppy drive mov si,offset noflop mov al,2 jmp doerr start6: shr ch,6 inc ch # determine number of physical floppy mov [dsknum],ch # drives (max. number is 2) mov ah,0x19 int 0x21 # get current default drive from DOS or al,al # should not be A: jz start7 cmp byte ptr [dsknum],1 ja startH # if we have just one physical disk dec al # drive, it should also not be B: jnz startH start7: mov si,offset curdrv mov al,2 jmp doerr # Copy the DPB from the resident ramdisk driver into our own memory for # later reference. startH: mov ax,0x9C05 # get address of ramdisk DPB from int 0xF8 # driver mov word ptr [dpbptr + 0],dx mov word ptr [dpbptr + 2],bx mov si,ds mov es,si mov ds,bx xchg si,dx mov di,offset dpb mov cx,DPB_SIZE # copy DPB into our own memory rep movsb mov ds,dx # Now we can ask the resident driver to remove itself. If the driver turned # off the ramdisk and could remove itself from memory, and nobody moved the # DPB, we have to use our own copy of the DPB. mov ax,0x9C04 int 0xF8 # remove ramdisk or al,al jz start8 cmp al,2 # check for error code je start9 mov si,offset rderr mov al,3 jmp doerr start8: xor ax,ax mov es,ax # check if someone changed the mov eax,es:[DPB_VECT] # DPB already cmp eax,[dpbptr] jne start9 mov word ptr es:[DPB_VECT + 0],offset dpb mov word ptr es:[DPB_VECT + 2],cs inc byte ptr [resflg] # Since the ramdisk driver has now been uninstalled, we can ask the BIOS # about the parameters of the physical floppy drive, and setup the DOS # IOCTL parameter block. Note that some BIOS incorrectly return this # information for floppy disk drives. MS-DOS assumes 360kB drives in # that case, so we do the same. start9: xor di,di # ES:DI = 0000:0000 to prevent mov es,di # lockups with buggy BIOS mov ax,0x0800 xor dl,dl int 0x13 # get drive parameters jc startA or ch,ch # check for valid response jnz startB startA: mov bx,0x0001 mov cx,0x2708 # set parameters for 360kB floppy mov dx,0x0101 startB: movzx ax,dh # set number of heads inc ax mov [heads],ax mov al,ch mov ah,cl shr ah,6 # set number of cylinders inc ax mov [cylnum],ax movzx ax,cl and ax,0x003F # set number of sectors per track mov [sptrk],ax mov [trknum],ax xor bh,bh dec bx cmp bx,5 # determine disk drive ID jbe startC xor bx,bx startC: mov ax,bx shl ax,2 # multiply by 5 add bx,ax add bx,offset drvtab mov al,[bx + 0] # get device type mov [devtyp],al mov al,[bx + 1] # get media ID mov [medid],al mov al,[bx + 2] # get sectors per cluster mov [spclus],al xor ah,ah mov al,[bx + 3] # get number of sectors per FAT mov [spfat],ax mov al,[bx + 4] # get number of root dir entries mov [rootno],ax mov ax,[heads] mov cx,[sptrk] mul cx # compute total number of sectors (we mul word ptr [cylnum] # dont have to care for overflow here mov [secnum],ax # as that should never happen ;-)) cld mov bx,1 mov ax,ds mov es,ax mov di,offset trktab startD: mov ax,bx # setup track layout field stosw mov ax,SECT_SIZE stosw inc bx loop startD cmp byte ptr [devtyp],0 je startE # no changeline support for 360 kB mov ax,0x15FF xor dx,dx int 0x13 # get floppy DASD type jc startE cmp ah,0x02 # check for changeline support jne startE or word ptr [attrib],0x0002 # Call DOS with the new parameter table. Then set the number of sectors # per track in the DPB, and reset the disk system. startE: mov ax,0x440D mov bx,0x0001 mov cx,0x0840 mov dx,offset ioctl int 0x21 # call DOS with new parameters cmp byte ptr [dsknum],1 ja startI mov ax,0x440D # if we have just one physical drive mov bx,0x0002 # this is also mapped to B: by DOS, so mov cx,0x0840 # we have to set the new parameters mov dx,offset ioctl # for B: as well in this case int 0x21 startI: xor ax,ax mov es,ax les di,es:[DPB_VECT] # set number of sectors per track mov dx,[sptrk] # in DPB mov byte ptr es:[di + 4],dl xor dx,dx int 0x13 # reset the disk drive # Now we have reconfigured the disk driver to the new floppy parameters, # but DOS itself doesnt know about the new parameters yet. Therefore we # still have to manipulate the DOS disk parameter blocks. mov ah,0x52 int 0x21 # get the DOS list of lists les bx,es:[bx] # get pointer to first parameter block mov ax,es or ax,bx # check for valid pointer jz startL mov si,0x0017 cmp word ptr [dosver],0x0400 jb startK # set offset to "not accessed flag" inc si # depending on DOS version startK: cmp bx,0xFFFF # check for end of list je startL mov al,es:[bx + 0] # get logical drive number or al,al jz startN # check if its drive A: cmp byte ptr [dsknum],1 ja startM # if we have only one physical drive, dec al # we have to set the parameters for jnz startM # drive B: as well # mark drive as never accessed startN: mov byte ptr es:[bx + si],0x80 startM: les bx,es:[bx + si + 1] # get pointer to next parameter block jmp startK # If the system loaded nbramdrv with the NEWDRIVE switch, the ramdisk still # exists but has been set readonly. The BIOS ramdisk driver has been removed # by now, so we can now activate nbramdrv. startL: mov dx,offset rddev mov ax,0x3D02 # open ramdisk controlling device int 0x21 # for reading and writing jc startO # no ramdisk controller mov bx,ax push bx mov dx,offset rdcmd mov cx,offset rdend sub cx,dx mov ax,0x4403 # write command to start ramdisk int 0x21 # driver to ramdisk controller jnc startP mov dx,offset acterr mov ah,0x09 int 0x21 # print warning message mov dx,offset crlf mov ah,0x09 int 0x21 # print CR/LF startP: pop bx mov ah,0x3E # close file handle int 0x21 # Thats it, so we can print a message to the user and terminate. However, # we have to check if we have to stay resident. startO: mov dx,offset rdok mov ah,0x09 int 0x21 # print success message mov dx,offset crlf mov ah,0x09 int 0x21 # print CR/LF test byte ptr [resflg],0xFF jnz startF mov ax,0x4C00 int 0x21 # terminate program lret startF: mov ax,word ptr [0x002C] # get segment of environment or ax,ax jz startG mov es,ax # free environment memory block mov ax,0x4900 int 0x21 startG: mov dx,offset start add dx,0x000F # compute number of paragraphs to shr dx,4 # keep mov ax,0x3100 int 0x21 # terminate and stay resident lret # #==================================================================== # # Print an error message and exit the program # Input: SI - Offset to error message # AL - Error code # Output: none # Registers changed: all (routine doesnt return) # doerr: push ax push si mov dx,offset errmsg # print leading message mov ah,0x09 int 0x21 pop dx mov ah,0x09 # print error message int 0x21 mov dx,offset crlf mov ah,0x09 # print CR/LF int 0x21 pop ax mov ah,0x4C # terminate program with int 0x21 # error code in AL lret # #==================================================================== # # Data area # dpbptr: .long 0 # ramdisk DPB pointer resflg: .byte 0 # flag if we have to remain resident dsknum: .byte 0 # number of physical floppy drives dosver: .word 0 # DOS version # DOS parameter block for IOCTL function 0x40 ioctl: .byte 0x04 # special functions devtyp: .byte 0 # device type attrib: .word 0 # device attributes cylnum: .word 0 # number of cylinders .byte 0 # media type .word SECT_SIZE # number of bytes per sector spclus: .byte 0 # number of sectors per cluster .word 1 # number of reserved sectors .byte 2 # number of FATs rootno: .word 0 # number of root directory entries secnum: .word 0 # total number of sectors medid: .byte 0 # media ID spfat: .word 0 # number of sectors per FAT sptrk: .word 0 # number of sectors per track heads: .word 0 # number of heads .long 0 # number of hidden sectors .long 0 # total number of sectors .byte 0,0,0,0,0,0 # reserved trknum: .word 0 # number of tracks trktab: .space (64 * 4),0 # track table # Table with parameters for different floppy drive sizes # This table is organized as follows: # 0x00 - device type # 0x01 - media ID # 0x02 - sectors per cluster # 0x03 - number of sectors per FAT # 0x04 - number of root directory entries drvtab: .byte 0x00, 0xFD, 2, 2, 112 # 360 kB .byte 0x01, 0xF9, 1, 7, 224 # 1.2 MB .byte 0x02, 0xF9, 2, 3, 112 # 720 kB .byte 0x07, 0xF0, 1, 9, 224 # 1.44 MB .byte 0x09, 0xF0, 2, 9, 224 # unknown .byte 0x09, 0xF0, 2, 9, 224 # 2.88 MB # Strings needed to access nbramdrv rddev: .asciz "RAMCNTR0" # name of ramdisk controller device rdcmd: .ascii "RDS2" # command string to activate ramdisk rdend: # Text messages rdsig: .ascii "NetB" errmsg: .ascii "Unable to remove ramdisk: $" invdos: .ascii "Invalid DOS version$" noramd: .ascii "No or invalid ramdisk driver$" invdrv: .ascii "Ramdisk is not drive A:$" noflop: .ascii "No physical floppy drive installed$" curdrv: .ascii "Ramdisk is current default drive$" rderr: .ascii "Can't remove ramdisk$" acterr: .ascii "Warning: unable to activate nbramdrv$" rdok: .ascii "Ramdisk removed$" crlf: .byte 0x0D,0x0A .ascii "$" # #==================================================================== # .end