📄 first-dos.s
字号:
xor dx,dx mov cl,CON(0x10) xor ch,ch mul cx ; compute linear buffer address add ax,bx adc dx,CON(0) mov [di],ax ; set dst for read, src for write mov [di+2],dl mov [di+5],dh push es mov ax,cs mov es,ax mov si,CON(rd_gdt) mov cx,CON(SECT_SIZE/2) ; copy 512 bytes, e.g. 256 words mov ax,CON(0x8700) ; let the BIOS move the sector int 0x15 pop es pop ax pop dx pop cx pop di pop si ret;====================================================================;; Return a pointer to the disk drive status byte. This routine should; not change any flags!; Input: none; Output: ES:BX - pointer to disk drive status byte; Registers changed: BX, ESgetsts: mov bx,CON(BIOS_SEG) ; status byte is in BIOS data mov es,bx ; segment SEGCS mov bx,LOC(statof) ret;====================================================================;; Initialize the XMS interface. This is necessary to prevent the; ram disk from getting overwritten. The way this works is to; first allocate all of the available XMS, then resize the memory; block to end just above the ramdisk and lock it. Unfortunately; we have to do it this complicated because there is no way of; knowing how the XMS is going to allocate the available memory.; Another problem is that at least HIMEM.SYS requires up to 256; bytes of stack, and we cannot assume the caller of INT 13 to; provide that much so we have to change stacks.; Input: none; Output: none; Registers changed: AX, BXinixms: call setstk ; set new stack; First check that the XMS version number is correct. To support all; necessary functions it has to be version 2.0+. xor ah,ah call callxm ; get version number cmp ah,CON(0x02) jb inixm8 ; wrong XMS version; Determine how much memory we can allocate. mov ah,CON(0x08) xor bl,bl ; get amount of extended memory call callxm or bl,bl ; check for error jnz inixm8 mov bx,LOC(rdsize) ; get size of ramdisk add bx,BCON(65) ; care for a missing HMA cmp bx,ax ; check if enough memory for ram disk ja inixm8; Grab all of the extended memory. push bx mov dx,ax ; grab largest block - which is whole mov ah,CON(0x09) ; memory because there should be no call callxm ; other process using XMS pop bx or ax,ax ; check for error jz inixm8 mov LOC(xmshdl),dx ; save handle; Now resize the memory block so that it will contain the ramdisk image. mov ah,CON(0x0f) ; reallocate memory block call callxm or ax,ax ; check for error jnz inixm1inixm8: mov WLOC(xmshdl),CON(0) ; in case of error dont return handle JMP(inixm9); Now lock the memory block and check that the physical address of the; memory block is correct.inixm1: mov dx,LOC(xmshdl) mov ah,CON(0x0c) ; lock memory block call callxm add bx,CON(0x03ff) adc dx,BCON(0x0001) ; add 65kb - maximum difference sub bx,LOC(rdaddr+0) ; check that ramdisk address is below sbb dx,LOC(rdaddr+2) jc inixm8; Thats it. Restore all registers and swap the stack back to its; original state.inixm9: call rststk ; restore old stack ret;====================================================================;; Call XMS driver.; Input: AH - function code; other registers depending on function code; Output: depends on called function; Registers changed: depends on called functioncallxm: push ax push bp push ax mov bp,sp mov ax,[bp+6] mov [bp+4],ax ; make far return address from mov [bp+6],cs ; near call pop ax pop bp SEGCS push WLOC(xmsadr+2) ; push address of XMS driver SEGCS push WLOC(xmsadr+0) retf ; call XMS driver;====================================================================;; Set new stack; Input: none; Output: none; Registers changed: AX, BX, DS, SS, SPsetstk: cli pop bx ; get return address mov ax,sp SEGCS mov LOC(oldstk+0),ax SEGCS mov LOC(oldstk+2),ss ; save old stack pointer mov ax,cs mov ss,ax mov sp,CON(newtos - 2) ; change to new stack sti push cx push dx push si ; save all registers push di push es push ds mov ax,cs ; set DS to current segment mov ds,ax jmp bx ; return to caller;====================================================================;; Reset stack to old stack; Input: none; Output: none; Registers changed: all (reset to old state)rststk: pop bx ; get return address pop ds pop es pop di pop si ; restore all registers pop dx pop cx cli SEGCS mov ax,LOC(oldstk+0) ; restore old stack SEGCS mov ss,LOC(oldstk+2) mov sp,ax sti jmp bx ; return to caller;====================================================================;; Remove ramdisk from memory. This involves restoring all interrupt; vectors, freeing all used memory and restoring the DOS drive para-; meter table. Since we need to call the XMS drive, we have to switch; stacks like with inixms.; Input: none; Output: AL - non-zero if error; Registers changed: AXrmrd: push bx call setstk ; set new stack mov al,LOC(drvid) cmp al,CON(0x80) ; can only restore floppy drives jb rmrd1rmrd8: call rststk ; return with error pop bx mov al,CON(0x01) ret; First determine the address of the DOS disk parameter block for the; ramdisk and check that the open count is zero, i.e. no open file on; the device.rmrd1: push ds mov ax,CON(0x0803) int 0x2f ; get address of drive parameter mov ax,ds ; table from DOS mov es,ax pop dsrmrd2: SEGES mov al,[di+4] ; get physical unit number cmp al,LOC(drvid) ; is it our drive? je rmrd3 cmp di,CON(0xffff) ; error if we couldnt find the DPB je rmrd8 ; for the ramdisk SEGES les di,[di] ; get pointer to next entry JMP(rmrd2)rmrd3: mov LOC(dpb_addr+0),di mov LOC(dpb_addr+2),es SEGES mov ax,[di+0x20] ; get device open count or ax,ax jnz rmrd8; Next restore the interrupt vectors. Int 13h is special as it is; redirected by DOS. However, DOS provides a function to restore; that interrupt. Interrupt 2Fh doesnt have to get restored because; DOS does never call the old interrupt again. xor ax,ax mov es,ax mov ax,cs SEGES ; first check that nobody redirected cmp ax,LOC(IF8_INT+2) ; our own interrupts. In that case jne rmrd8 ; there is no chance of removing the SEGES ; ramdisk. mov ax,LOC(IF8_INT+0) cmp ax,CON(intF8) jne rmrd8 mov si,CON(if1sig) mov di,CON(IF1_INT) mov cx,CON(4) ; interrupt F1h contains a signature repz ; and no vector cmpsb jnz rmrd8 push ds les bx,LOC(old13h) ; get old interrupt vector 13h mov dx,bx mov ax,es ; save it into DS:DX and ES:BX mov ds,ax mov ah,CON(0x13) int 0x2f ; call DOS to restore vector mov ax,ds mov cx,cs cmp ax,cx ; check that its indeed our interrupt jne rmrd4 ; which we are replacing mov ax,es cmp ax,cx jne rmrd4 cmp bx,CON(int13) jne rmrd4 cmp dx,CON(int13) je rmrd5rmrd4: mov ah,CON(0x13) int 0x2f ; restore old interrupt pop ds ; someone redirected the interrupt#ifdef USE_AS86 jmp near rmrd8 ; already, cant restore#endif#ifdef USE_NASM jmp rmrd8 ; already, cant restore#endifrmrd5: pop ds ; restore the other interrupts cli xor ax,ax mov es,ax mov ax,LOC(oldF8h+0) SEGES mov LOC(IF8_INT+0),ax mov ax,LOC(oldF8h+2) SEGES mov LOC(IF8_INT+2),ax sti; OK, we can now setup the DOS drive parameter table to contain the; correct values for the physical floppy drive. If we couldnt create; a valid parameter table entry for this drive, simply mark the DOS; entry as invalid. This will cause "Not Ready" errors in DOS. This; doesnt work with DR-DOS 5.0! les di,LOC(dpb_addr) ; get address of DPB SEGES#ifdef USE_AS86 or [di+0x1f],BCON(80) ; mark drive as invalid#endif#ifdef USE_NASM or word [di+0x1f],BCON(80) ; mark drive as invalid#endif test BLOC(dpb_valid),CON(0xff) ; check if DPB valid jz rmrd6 cld ; got correct table entry mov cx,CON(dpb_end - dpb) mov si,CON(dpb) add di,BCON(4) rep movsb ; simply copy the DPB; Next remove the ramdisk image from extended memory using the XMS driver.rmrd6: mov dx,LOC(xmshdl) or dx,dx ; only free memory if we really jz rmrd7 ; assigned it with XMS push dx mov ah,CON(0x0d) call callxm ; unlock memory block pop dx or ax,ax ; dont free block if error jz rmrd7 mov ah,CON(0x0a) call callxm ; free memory block; Finally we can remove the memory for the ramdisk driver. We only; reset the owner field of the memory control block to 0 to indicate; it as free.rmrd7: mov dx,CON(start_resident) ; determine last usable segment mov cl,CON(4) ; from segment and offset of shr dx,cl ; the resident section mov ax,cs add dx,ax ; add offset to segment sub dx,BCON(2) mov es,dx mov di,CON(1) xor ax,ax ; set owner field to 0 stosw add di,BCON(5) mov cx,CON(4) ; clear owner name rep stosw; Thats it. Return to caller.rmrd9: call rststk ; restore old stack pop bx xor al,al ; return without error ret;====================================================================;; Variables for the resident section ALIGN(2)oldints:old13h: dd 0 ; old interrupt 13h vectorold2Fh: dd 0 ; old interrupt 2Fh vectoroldF1h: dd 0 ; old interrupt F1h vectoroldF8h: dd 0 ; old interrupt F8h vector; Disk parameters for ram diskstatof: dw BIOS_FDSTAT ; offset to BIOS disk status byterdaddr: dd 0 ; base address of ram diskrdsize: dw 0 ; size of ram disk in kbcylnum: dw 80 ; number of cylinderssecnumlo: dw 2400 ; number of sectors on disksecnumhi: dw 0secptk: dw 15 ; number of sectors per trackheads: db 1 ; number of headsdrvnum: db 1 ; number of disk drivesdrvid: db 0 ; ram disk drive idnohd: db 0 ; no-hard-disk flagsyscnf: dw 0 ; system configuration from BIOS ALIGN(2); Variables used to access the XMS interfacexmshdl: dw 0 ; XMS handle for ram diskxmsadr: dd 0 ; address of XMS driver interface; Variables used to redirect the stackoldstk: dd 0 ; old stack pointernewstk: SPACE(512) ; new stack for calling XMS drivernewtos: ; new top of stack; Signature to put into interrupt vector F1hif1sig: STRDECL('NetB') ALIGN(16) ; has to be paragraph aligned; Descriptor table to access ram disk using the BIOSrd_gdt: dw 0,0,0,0 dw 0,0,0,0rd_src: dw 0xffff ; lengthrd_srcb: db 0,0,0 ; base db 0x93 ; typebyte db 0 ; limit16 =0rd_srcbh: db 0 ; base24rd_dst: dw 0xffff ; lengthrd_dstb: db 0,0,0 ; base db 0x93 ; typebyte db 0 ; limit16 =0rd_dstbh: db 0 ; base24 dw 0,0,0,0 ; BIOS CS dw 0,0,0,0 ; BIOS DS; DOS disk parameter block. It contains the definitions for the; floppy disk drive which is redirected by the ramdisk, and used; for removing the ramdisk drive. Note that this DPB is only; valid for DOS 4.0 and higher.dpb_addr: dd 0 ; address of DPB in DOS data areadpb_valid: db 0 ; non-zero if DPB is validdpb:dpb_phys: db 0 ; BIOS ID of physical drivedpb_log: db 0 ; logical DOS drive IDdpb_bpb_low: dw 512 ; BIOS param block for lowest capacity db 0xff dw 1 db 2 dw 64 dw 360 db 0x00 dw 2 dw 9 dw 1 dd 0 dd 0dpb_fat: db 0 ; flag indicating 16-bit FATdpb_open: dw 0 ; device open countdpb_type: db 0x01 ; device typedpb_flags: dw DPB_F_DEFAULT ; flags describing drivedpb_cyls: dw 80 ; number of cylindersdpb_bpb_cur: dw 512 ; BIOS parameter block for current db 1 dw 1 db 2 dw 224 dw 2400 db 0xf9 dw 7 dw 15 dw 2 dd 0 dd 0dpb_rsvd: db 0, 0, 0, 0, 0, 0dpb_ltrack: db 0xff ; last accessed trackdpb_lacc: dd 0xffffffff ; time of last disk accessdpb_volname: STRDECL('NO NAME ') ; volume name db 0dpb_sernum: dd 0 ; volume serial numberdpb_fsname: STRDECL('FAT12 ') ; file system name db 0dpb_end:; Copy of bootp block from bootrom. This has to be last in the data area!btplen: dw 0 ; length of bootp blockbtpnew: ; bootp block has to be at the very end_end: SPACE(4096-(_end-_start))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -