1 ; -*- fundamental -*- (asm-mode sucks)
2 ; $Id: memdisk.asm,v 1.29 2005/04/29 06:08:03 hpa Exp $
3 ; ****************************************************************************
7 ; A program to emulate an INT 13h disk BIOS from a "disk" in extended
10 ; Copyright (C) 2001-2004 H. Peter Anvin
12 ; This program is free software; you can redistribute it and/or modify
13 ; it under the terms of the GNU General Public License as published by
14 ; the Free Software Foundation, Inc., 53 Temple Place Ste 330,
15 ; Boston MA 02111-1307, USA; either version 2 of the License, or
16 ; (at your option) any later version; incorporated herein by reference.
18 ; ****************************************************************************
21 %include "../version.gen"
24 ; %define DEBUG_TRACERS ; Uncomment to get debugging tracers
38 %endif ; DEBUG_TRACERS
40 %define CONFIG_READONLY 0x01
41 %define CONFIG_RAW 0x02
42 %define CONFIG_BIGRAW 0x08 ; MUST be 8!
46 %define SECTORSIZE_LG2 9 ; log2(sector size)
47 %define SECTORSIZE (1 << SECTORSIZE_LG2)
49 ; Parameter registers definition; this is the definition
51 %define P_DS word [bp+34]
52 %define P_ES word [bp+32]
53 %define P_EAX dword [bp+28]
54 %define P_HAX word [bp+30]
55 %define P_AX word [bp+28]
56 %define P_AL byte [bp+28]
57 %define P_AH byte [bp+29]
58 %define P_ECX dword [bp+24]
59 %define P_HCX word [bp+26]
60 %define P_CX word [bp+24]
61 %define P_CL byte [bp+24]
62 %define P_CH byte [bp+25]
63 %define P_EDX dword [bp+20]
64 %define P_HDX word [bp+22]
65 %define P_DX word [bp+20]
66 %define P_DL byte [bp+20]
67 %define P_DH byte [bp+21]
68 %define P_EBX dword [bp+16]
69 %define P_HBX word [bp+18]
70 %define P_HBXL byte [bp+18]
71 %define P_BX word [bp+16]
72 %define P_BL byte [bp+16]
73 %define P_BH byte [bp+17]
74 %define P_EBP dword [bp+8]
75 %define P_BP word [bp+8]
76 %define P_ESI dword [bp+4]
77 %define P_SI word [bp+4]
78 %define P_EDI dword [bp]
79 %define P_DI word [bp]
82 ; These pointers are used by the installer and
83 ; must be first in the binary
84 Pointers: dw Int13Start
99 ; See if DL points to our class of device (FD, HD)
104 js .nomatch ; If SF=0, we have a class match here
105 jz .our_drive ; If ZF=1, we have an exact match
107 jb .nomatch ; Drive < Our drive
108 dec dl ; Drive > Our drive, adjust drive #
112 call far [cs:OldInt13]
116 cmp byte [cs:SavedAX+1],08h
118 cmp byte [cs:SavedAX+1],15h
120 test byte [bp+4],80h ; Hard disk?
128 mov ax,[bp+2] ; Flags
130 mov [bx+4],al ; Arithmetric flags
139 ; Set up standard entry frame
146 mov bp,sp ; Point BP to the entry stack frame
148 ; Note: AH == P_AH here
151 xor al,al ; AL = 0 is standard entry condition
153 shr di,7 ; Convert AH to an offset in DI
156 Done: ; Standard routine for return
163 mov [es:bx],ah ; Save status
167 ; This sets the low byte (the arithmetric flags) of the
168 ; FLAGS on stack to either 00h (no flags) or 01h (CF)
169 ; depending on if AH was zero or not.
170 setnz [bx+4] ; Set CF iff error
178 ; Reset affects multiple drives, so we need to pass it on
180 test dl,dl ; Always pass it on if we are resetting HD
181 js .pass_on ; Bit 7 set
182 ; Some BIOSes get very unhappy if we pass a reset floppy
183 ; command to them and don't actually have any floppies.
184 ; This is a bug, but we have to deal with it nontheless.
185 ; Therefore, if we are the *ONLY* floppy drive, and the
186 ; user didn't request HD reset, then just drop the command.
187 xor ax,ax ; Bottom of memory
189 ; BIOS equipment byte, top two bits + 1 == total # of floppies
190 test byte [es:0x410],0C0h
192 ; ... otherwise pass it to the BIOS
194 pop ax ; Drop return address
195 popad ; Restore all registers
198 lss esp,[cs:Stack] ; Restore the stack
199 and dl,80h ; Clear all but the type bit
200 jmp far [cs:OldInt13]
204 pop dx ; Drop return address
207 mov ah,01h ; Unsupported function
211 test byte [DriveNo],80h
212 mov bl,02h ; Type 02h = floppy with changeline
216 mov dx,[DiskSize] ; Return the disk size in sectors
221 mov P_AH,bl ; 02h floppy, 03h hard disk
222 pop ax ; Drop return address
223 xor ax,ax ; Success...
224 jmp short DoneWeird ; But don't stick it into P_AX
230 mov ah,[bx] ; Copy last status
242 movzx ax,P_AL ; AH = 0, AL = transfer count
249 test byte [ConfigFlags],CONFIG_READONLY
252 xchg esi,edi ; Opposite direction of a Read!
254 .readonly: mov ah,03h ; Write protected medium
257 ; Verify integrity; just bounds-check
260 call setup_regs ; Returns error if appropriate
261 ; And fall through to success
263 CheckIfReady: ; These are always-successful noop functions
269 xor ax,ax ; Always successful
274 mov dl,[DriveCnt] ; Cached data
276 test byte [DriveNo],80h
284 dec ax ; We report the highest #, not the count
294 ; Is this MEMDISK installation check?
305 ; MEMDISK installation check...
311 mov P_DI,MemDisk_Info
317 ; Set up registers as for a "Read", and compares against disk size
320 ; Convert a CHS address in P_CX/P_DH into an LBA in eax
322 ; CL[0:5] = sector (1-based) CL[7:6] = cyl[9:8]
325 movzx ebx,cl ; Sector number
327 dec ebx ; Sector number is 1-based
330 movzx edi,P_DH ; Head number
331 movzx eax,word [Heads]
335 xchg cl,ch ; Now (E)CX <- cylinder number
336 mul ecx ; eax <- Heads*cyl# (edx <- 0)
340 ; Now eax = LBA, edx = 0
343 ; setup_regs continues...
345 ; Note: edi[31:16] and ecx[31:16] = 0 already
346 mov di,P_BX ; Get linear address of target buffer
349 add edi,ecx ; EDI = address to fetch to
350 movzx ecx,P_AL ; Sector count
352 add eax,ecx ; LBA of final sector + 1
353 shl esi,SECTORSIZE_LG2 ; LBA -> byte offset
354 add esi,[DiskBuf] ; Get address in high memory
355 cmp eax,[DiskSize] ; Check the high mark against limit
357 shl ecx,SECTORSIZE_LG2-2 ; Convert count to 32-bit words
360 .overrun: pop ax ; Drop setup_regs return address
361 mov ax,0200h ; Missing address mark
365 cmp edx,534D4150h ; "SMAP"
367 cmp ecx,20 ; Need 20 bytes
376 add bx,12 ; Advance to next
377 mov eax,[bx-4] ; Type
378 and eax,eax ; Null type?
379 jz .renew ; If so advance to next
381 mov eax,[bx-12] ; Start addr (low)
383 mov ecx,[bx-8] ; Start addr (high)
385 mov eax,[bx] ; End addr (low)
386 mov ecx,[bx+4] ; End addr (high)
387 sub eax,[bx-12] ; Derive the length
389 mov [es:di+8],eax ; Length (low)
390 mov [es:di+12],ecx ; Length (high)
391 cmp dword [bx+8],-1 ; Type of next = end?
393 xor ebx,ebx ; Done with table
397 mov ecx,20 ; Bytes loaded
399 mov byte [bp+6], 02h ; Clear CF
404 mov byte [bp+6], 03h ; Set CF
421 jmp far [cs:OldInt15]
428 jmp short int15_success
435 jmp short int15_success
438 mov ax,[cs:MemInt1588]
439 jmp short int15_success
442 ; Routine to copy in/out of high memory
443 ; esi = linear source address
444 ; edi = linear target address
445 ; ecx = 32-bit word count
447 ; Assumes cs = ds = es
455 test byte [ConfigFlags],CONFIG_RAW
458 smsw ax ; Unprivileged!
464 ; We're in real mode, do it outselves
479 ; Test to see if A20 is enabled or not
496 push dx ; Save A20 status
499 mov ax,2401h ; Enable A20
505 ; DX = 16 for BIGRAW, 8 for RAW
506 ; 8 is selector for a 64K flat segment,
507 ; 16 is selector for a 4GB flat segment.
514 mov bx,16 ; Large flat segment
520 ; DX has the appropriate value to put in
521 ; the registers on return
534 mov ax,2400h ; Disable A20
552 push ecx ; Transfer size this cycle
556 mov [Mover_src1+2], al
561 mov [Mover_dst1+2], al
565 shl cx,1 ; Convert to 16-bit words
567 cli ; Some BIOSes enable interrupts on INT 15h
568 pop eax ; Transfer size this cycle
603 Int13Funcs dw Reset ; 00h - RESET
604 dw GetStatus ; 01h - GET STATUS
606 dw Write ; 03h - WRITE
607 dw Verify ; 04h - VERIFY
608 dw Invalid ; 05h - FORMAT TRACK
609 dw Invalid ; 06h - FORMAT TRACK AND SET BAD FLAGS
610 dw Invalid ; 07h - FORMAT DRIVE AT TRACK
611 dw GetParms ; 08h - GET PARAMETERS
612 dw InitWithParms ; 09h - INITIALIZE CONTROLLER WITH DRIVE PARAMETERS
615 dw Seek ; 0Ch - SEEK TO CYLINDER
616 dw Reset ; 0Dh - RESET HARD DISKS
619 dw CheckIfReady ; 10h - CHECK IF READY
620 dw Recalibrate ; 11h - RECALIBRATE
624 dw GetDriveType ; 15h - GET DRIVE TYPE
625 dw DetectChange ; 16h - DETECT DRIVE CHANGE
637 dw ReadMult ; 21h - READ MULTIPLE
638 dw WriteMult ; 22h - WRITE MULTIPLE
639 dw SetMode ; 23h - SET CONTROLLER FEATURES
640 dw SetMode ; 24h - SET MULTIPLE MODE
641 dw Invalid ; 25h - IDENTIFY DRIVE
669 dw EDDPresence ; 41h - EDD PRESENCE DETECT
670 dw EDDRead ; 42h - EDD READ
671 dw EDDWrite ; 43h - EDD WRITE
672 dw EDDVerify ; 44h - EDD VERIFY
673 dw Invalid ; 45h - EDD LOCK/UNLOCK MEDIA
674 dw Invalid ; 46h - EDD EJECT
675 dw EDDSeek ; 47h - EDD SEEK
676 dw EDDGetParms ; 48h - EDD GET PARAMETERS
680 Int13FuncsMax equ (Int13FuncsEnd-Int13Funcs) >> 1
683 Shaker dw ShakerEnd-$
684 dd 0 ; Pointer to self
687 Shaker_RMDS: dd 0x0000ffff ; 64K data segment
690 Shaker_DS: dd 0x0000ffff ; 4GB data segment
698 Mover dd 0, 0, 0, 0 ; Must be zero
699 dw 0ffffh ; 64 K segment size
700 Mover_src1: db 0, 0, 0 ; Low 24 bits of source addy
701 db 93h ; Access rights
702 db 00h ; Extended access rights
703 Mover_src2: db 0 ; High 8 bits of source addy
704 dw 0ffffh ; 64 K segment size
705 Mover_dst1: db 0, 0, 0 ; Low 24 bits of target addy
706 db 93h ; Access rights
707 db 00h ; Extended access rights
708 Mover_dst2: db 0 ; High 8 bits of source addy
709 Mover_dummy2: dd 0, 0, 0, 0 ; More space for the BIOS
712 MemDisk_Info equ $ ; Pointed to by installation check
713 MDI_Bytes dw 27 ; Total bytes in MDI structure
714 MDI_Version db VER_MINOR, VER_MAJOR ; MEMDISK version
716 PatchArea equ $ ; This gets filled in by the installer
718 DiskBuf dd 0 ; Linear address of high memory disk
719 DiskSize dd 0 ; Size of disk in blocks
720 CommandLine dw 0, 0 ; Far pointer to saved command line
722 OldInt13 dd 0 ; INT 13h in chain
723 OldInt15 dd 0 ; INT 15h in chain
725 OldDosMem dw 0 ; Old position of DOS mem end
726 BootLoaderID db 0 ; Boot loader ID from header
727 ; ---- MDI structure ends here ---
730 MemInt1588 dw 0 ; 1MB-65MB memory amount (1K)
732 Cylinders dw 0 ; Cylinder count
733 Heads dw 0 ; Head count
734 Sectors dd 0 ; Sector count (zero-extended)
736 Mem1MB dd 0 ; 1MB-16MB memory amount (1K)
737 Mem16MB dd 0 ; 16MB-4G memory amount (64K)
739 DriveNo db 0 ; Our drive number
740 DriveType db 0 ; Our drive type (floppies)
741 DriveCnt db 0 ; Drive count (from the BIOS)
743 ConfigFlags db 0 ; Bit 0 - readonly
745 MyStack dw 0 ; Offset of stack
746 StatusPtr dw 0 ; Where to save status (zeroseg ptr)
748 DPT times 16 db 0 ; BIOS parameter table pointer (floppies)
752 Stack dd 0 ; Saved SS:ESP on invocation
754 SavedAX dw 0 ; AX saved on invocation
756 alignb 4, db 0 ; We *MUST* end on a dword boundary
758 E820Table equ $ ; The installer loads the E820 table here
759 TotalSize equ $ ; End pointer